Browse Source

任务管理详情和首页

tags/v1.0.0
吴迪 1 year ago
parent
commit
9c8a7cb248
34 changed files with 1528 additions and 189 deletions
  1. +14
    -0
      .env.localhost
  2. +5
    -4
      index.html
  3. +107
    -0
      package-lock.json
  4. +1
    -0
      package.json
  5. +7
    -0
      public/aliyun-upload-sdk/aliyun-upload-sdk-1.5.2.min.js
  6. +20
    -0
      public/aliyun-upload-sdk/lib/aliyun-oss-sdk-6.13.0.min.js
  7. +1
    -0
      public/aliyun-upload-sdk/lib/es6-promise.min.js
  8. +12
    -4
      src/api/equipment/index.js
  9. +47
    -15
      src/api/task/index.js
  10. +16
    -0
      src/utils/dictionary.js
  11. +24
    -0
      src/utils/echarts/README.md
  12. +247
    -0
      src/utils/echarts/index.js
  13. +67
    -0
      src/utils/http/interceptors.js
  14. +6
    -6
      src/utils/map/GMap.js
  15. BIN
      src/views/dashboard/assets/clients.png
  16. BIN
      src/views/dashboard/assets/fly.png
  17. BIN
      src/views/dashboard/assets/platform.png
  18. BIN
      src/views/dashboard/assets/task.png
  19. BIN
      src/views/dashboard/assets/total.png
  20. +52
    -0
      src/views/dashboard/components/ContComp.vue
  21. +121
    -0
      src/views/dashboard/components/FootComp.vue
  22. +101
    -0
      src/views/dashboard/components/HeadComp.vue
  23. +86
    -0
      src/views/dashboard/components/TabComp.vue
  24. +16
    -10
      src/views/dashboard/index.vue
  25. +219
    -61
      src/views/task-manage/components/DrawComp.vue
  26. +87
    -56
      src/views/task-manage/components/UserModal.vue
  27. +50
    -0
      src/views/task-manage/hook/basic.js
  28. +33
    -0
      src/views/task-manage/hook/equipment.js
  29. +28
    -0
      src/views/task-manage/hook/execution.js
  30. +39
    -0
      src/views/task-manage/hook/fly.js
  31. +11
    -0
      src/views/task-manage/hook/index.js
  32. +12
    -7
      src/views/task-manage/index.vue
  33. +89
    -0
      src/views/task-manage/tools/drawForm.js
  34. +10
    -26
      src/views/task-manage/tools/form.js

+ 14
- 0
.env.localhost View File

@@ -0,0 +1,14 @@
# 资源公共路径,需要以 /开头和结尾
VITE_PUBLIC_PATH = '/'

# 是否启用MOCK
VITE_APP_USE_MOCK = false

# proxy
VITE_PROXY = [["/api-local","http://127.0.0.1:8002"],["/api-mock","http://127.0.0.1:8003"]]

# base api
VITE_APP_GLOB_BASE_API = '/api-local'

# mock base api
VITE_APP_GLOB_BASE_API_MOCK = '/api-mock'

+ 5
- 4
index.html View File

@@ -9,18 +9,19 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/favicon.ico" />
<!-- IE需要es6-promise,目前支持到IE10 -->
<script src="./public/aliyun-upload-sdk/lib/es6-promise.min.js"></script>
<script src="./public/aliyun-upload-sdk/lib/aliyun-oss-sdk-6.13.0.min.js"></script>
<script src="./public/aliyun-upload-sdk/aliyun-upload-sdk-1.5.2.min.js"></script>
<title>
<%= title %>
</title>
<script type="text/javascript">
window._AMapSecurityConfig = {
// serviceHost:'http://127.0.0.1:8002/_AMapService',
// 例如 :serviceHost:'http://1.1.1.1:80/_AMapService',
securityJsCode: '935114b12a268130801d10a1d874418a',
securityJsCode: 'eb839debb422cd65cc598664067ee7d8'
}
</script>
<!-- <script type="text/javascript"
src="https://webapi.amap.com/maps?v=1.4.15&key=20535a30fa0f3aae9c7bbe4407270792"></script> -->
<style type="text/css">
.amap-logo {
display: none;

+ 107
- 0
package-lock.json View File

@@ -14,6 +14,7 @@
"@vicons/ionicons5": "^0.10.0",
"ali-oss": "^6.17.1",
"axios": "^0.26.1",
"chart-all": "^1.0.2",
"dayjs": "^1.11.2",
"mockjs": "^1.1.0",
"pinia": "^2.0.13",
@@ -2849,6 +2850,17 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chart-all": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/chart-all/-/chart-all-1.0.2.tgz",
"integrity": "sha512-gxkvz4rsnsr+nM5pnQt+4m6lKkie8JQYRhsyae+VlWMn3iR2iqhQo98AsYFUgKZAdm0Cvsh6ZvDlZnLx2VwLZg==",
"dependencies": {
"echarts": "^4.9.0",
"echarts-gl": "^1.1.2",
"echarts-liquidfill": "^2.0.6",
"zrender": "^4.3.1"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@@ -2903,6 +2915,11 @@
"integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==",
"dev": true
},
"node_modules/claygl": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz",
"integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ=="
},
"node_modules/clean-css": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz",
@@ -4103,6 +4120,40 @@
"node": ">=6.0.0"
}
},
"node_modules/echarts": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/echarts/-/echarts-4.9.0.tgz",
"integrity": "sha512-+ugizgtJ+KmsJyyDPxaw2Br5FqzuBnyOWwcxPKO6y0gc5caYcfnEUIlNStx02necw8jmKmTafmpHhGo4XDtEIA==",
"dependencies": {
"zrender": "4.3.2"
}
},
"node_modules/echarts-gl": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-1.1.2.tgz",
"integrity": "sha512-EVGx9RS2eMzaCgAMJSDCeLId4g8oFCFn78Fdh+0xIXASiZw/gPnJqr1vQgnQhmXhiUKixkIhIzfdc//qrct/Hg==",
"dependencies": {
"claygl": "^1.2.1",
"zrender": "^4.0.4"
},
"peerDependencies": {
"echarts": "^4.1.0"
}
},
"node_modules/echarts-liquidfill": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/echarts-liquidfill/-/echarts-liquidfill-2.0.6.tgz",
"integrity": "sha512-p+AH0O9/BtwXMQQyhjJbMZo+GwRAgWG/DCyK5r27PQzpS0UWrgXu57MyEFc0A8Ub3sRuqEu08BuxwHICBkSWSQ==",
"peerDependencies": {
"echarts": "^4.8.0",
"zrender": "^4.3.1"
}
},
"node_modules/echarts/node_modules/zrender": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/zrender/-/zrender-4.3.2.tgz",
"integrity": "sha512-bIusJLS8c4DkIcdiK+s13HiQ/zjQQVgpNohtd8d94Y2DnJqgM1yjh/jpDb8DoL6hd7r8Awagw8e3qK/oLaWr3g=="
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -12126,6 +12177,11 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
"dev": true
},
"node_modules/zrender": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/zrender/-/zrender-4.3.1.tgz",
"integrity": "sha512-CeH2TpJeCdG0TAGYoPSAcFX2ogdug1K7LIn9UO/q9HWqQ54gWhrMAlDP9AwWYMUDhrPe4VeazQ4DW3msD96nUQ=="
}
},
"dependencies": {
@@ -14376,6 +14432,17 @@
"integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==",
"dev": true
},
"chart-all": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/chart-all/-/chart-all-1.0.2.tgz",
"integrity": "sha512-gxkvz4rsnsr+nM5pnQt+4m6lKkie8JQYRhsyae+VlWMn3iR2iqhQo98AsYFUgKZAdm0Cvsh6ZvDlZnLx2VwLZg==",
"requires": {
"echarts": "^4.9.0",
"echarts-gl": "^1.1.2",
"echarts-liquidfill": "^2.0.6",
"zrender": "^4.3.1"
}
},
"chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@@ -14415,6 +14482,11 @@
"integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==",
"dev": true
},
"claygl": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/claygl/-/claygl-1.3.0.tgz",
"integrity": "sha512-+gGtJjT6SSHD2l2yC3MCubW/sCV40tZuSs5opdtn79vFSGUgp/lH139RNEQ6Jy078/L0aV8odCw8RSrUcMfLaQ=="
},
"clean-css": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz",
@@ -15325,6 +15397,36 @@
"integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==",
"dev": true
},
"echarts": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/echarts/-/echarts-4.9.0.tgz",
"integrity": "sha512-+ugizgtJ+KmsJyyDPxaw2Br5FqzuBnyOWwcxPKO6y0gc5caYcfnEUIlNStx02necw8jmKmTafmpHhGo4XDtEIA==",
"requires": {
"zrender": "4.3.2"
},
"dependencies": {
"zrender": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/zrender/-/zrender-4.3.2.tgz",
"integrity": "sha512-bIusJLS8c4DkIcdiK+s13HiQ/zjQQVgpNohtd8d94Y2DnJqgM1yjh/jpDb8DoL6hd7r8Awagw8e3qK/oLaWr3g=="
}
}
},
"echarts-gl": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/echarts-gl/-/echarts-gl-1.1.2.tgz",
"integrity": "sha512-EVGx9RS2eMzaCgAMJSDCeLId4g8oFCFn78Fdh+0xIXASiZw/gPnJqr1vQgnQhmXhiUKixkIhIzfdc//qrct/Hg==",
"requires": {
"claygl": "^1.2.1",
"zrender": "^4.0.4"
}
},
"echarts-liquidfill": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/echarts-liquidfill/-/echarts-liquidfill-2.0.6.tgz",
"integrity": "sha512-p+AH0O9/BtwXMQQyhjJbMZo+GwRAgWG/DCyK5r27PQzpS0UWrgXu57MyEFc0A8Ub3sRuqEu08BuxwHICBkSWSQ==",
"requires": {}
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -21255,6 +21357,11 @@
"dev": true
}
}
},
"zrender": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/zrender/-/zrender-4.3.1.tgz",
"integrity": "sha512-CeH2TpJeCdG0TAGYoPSAcFX2ogdug1K7LIn9UO/q9HWqQ54gWhrMAlDP9AwWYMUDhrPe4VeazQ4DW3msD96nUQ=="
}
}
}

+ 1
- 0
package.json View File

@@ -15,6 +15,7 @@
"@vicons/ionicons5": "^0.10.0",
"ali-oss": "^6.17.1",
"axios": "^0.26.1",
"chart-all": "^1.0.2",
"dayjs": "^1.11.2",
"mockjs": "^1.1.0",
"pinia": "^2.0.13",

+ 7
- 0
public/aliyun-upload-sdk/aliyun-upload-sdk-1.5.2.min.js
File diff suppressed because it is too large
View File


+ 20
- 0
public/aliyun-upload-sdk/lib/aliyun-oss-sdk-6.13.0.min.js
File diff suppressed because it is too large
View File


+ 1
- 0
public/aliyun-upload-sdk/lib/es6-promise.min.js
File diff suppressed because it is too large
View File


+ 12
- 4
src/api/equipment/index.js View File

@@ -2,19 +2,27 @@ import { defAxios as request } from '@/utils/http'

/**
* 无人机列表
* @returns
* @returns
*/
export const getEquipment = () => request({
url: '/equipment/getList',
method: 'GET'
})


/**
* 挂载设备列表
* @returns
* @returns
*/
export const getEquipmentMount = () => request({
url: '/equipmentMount/getList',
method: 'GET'
})
})

/**
* 挂载盒子
* @returns
*/
export const getCloud = () => request({
url: '/cloudBox/getList',
method: 'GET'
})

+ 47
- 15
src/api/task/index.js View File

@@ -2,9 +2,9 @@ import { defAxios as request } from '@/utils/http'

/**
* 任务查询
* @returns
* @returns
*/
export const getTaskList = params => request({
export const getTaskList = params => request({
url: '/task/getTaskList',
method: 'GET',
params
@@ -13,17 +13,16 @@ import { defAxios as request } from '@/utils/http'
/**
* 任务详情
* @param {*} params id
* @returns
* @returns
*/
export const getTaskInfo = params => request({
export const getTaskInfo = params => request({
url: `/task/getInfo/${params}`,
method: 'GET'
})


/**
* 获取飞手列表
* @returns
* @returns
*/
export const getTaskPilot = () => request({
url: '/task/getPilot',
@@ -33,9 +32,9 @@ export const getTaskPilot = () => request({
/**
* 添加任务
* @param {*} params
* @returns
* @returns
*/
export const taskAdd = data => request({
export const taskAdd = data => request({
url: '/task/add',
method: 'POST',
data
@@ -44,9 +43,9 @@ export const getTaskPilot = () => request({
/**
* 编辑任务
* @param {*} params
* @returns
* @returns
*/
export const taskEdit = data => request({
export const taskEdit = data => request({
url: '/task/edit',
method: 'PUT',
data
@@ -55,9 +54,9 @@ export const getTaskPilot = () => request({
/**
* 删除任务
* @param {*} params
* @returns
* @returns
*/
export const taskDel = params => request({
export const taskDel = params => request({
url: `/task/delete/${params}`,
method: 'DELETE'
})
@@ -65,10 +64,43 @@ export const getTaskPilot = () => request({
/**
* 分配飞手
* @param {*} params
* @returns
* @returns
*/
export const distributionPilot = data => request({
export const distributionPilot = data => request({
url: '/task/distributionPilot',
method: 'PUT',
data
})
})

/**
* 飞手接单
* @param {*} params
* @returns
*/
export const pilotOrder = data => request({
url: '/task/pilotOrder',
method: 'PUT',
data
})

/**
* 开始飞行
* @param {*} params
* @returns
*/
export const pilotStart = data => request({
url: '/task/pilotStart',
method: 'PUT',
data
})

/**
* 结束飞行
* @param {*} params
* @returns
*/
export const pilotEnd = data => request({
url: '/task/pilotEnd',
method: 'PUT',
data
})

+ 16
- 0
src/utils/dictionary.js View File

@@ -80,3 +80,19 @@ export const TASK_STATUS = [
value: 25
}
]

// 拍摄方式
export const PHOTOGRAPHY_WAY = [
{
label: '普通巡检',
value: '1'
},
{
label: '正射影像',
value: '2'
},
{
label: '倾斜摄影',
value: '3'
}
]

+ 24
- 0
src/utils/echarts/README.md View File

@@ -0,0 +1,24 @@

## use

```js
<template>
<div id="testLine" style="width: 50%; height: 300px;"></div>
</template>

<script>
import { onMounted } from "vue";
import myChart from "@/utils/echarts";
export default {
name: "about",
setup() {
onMounted(() => {
myChart.line1('testLine', (e) => {
console.log('点我了', e)
});
})
}
}
</script>

```

+ 247
- 0
src/utils/echarts/index.js View File

@@ -0,0 +1,247 @@
import chartClass from 'chart-all'
import * as echarts from 'echarts'

export default {
// 首页 - 任务数据
line1: (id, data) => {
const { chart } = new chartClass(id)
chart.setOption({
grid: {
top: '20%',
left: '5%',
right: '7%',
bottom: '5%',
containLabel: true
},
tooltip: {
trigger: 'axis',
axisPointer: {
// 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
xAxis: [{
type: 'category',
// boundaryGap: true, //坐标轴两边留白
data: data.eventNameTrend,
animation: true,
boundaryGap: false, // 贴边
// offset: 5,
axisLine: {
// x轴
show: true,
lineStyle: {
color: 'rgba(137, 137, 137, 0.6)'
}
},
axisTick: {
// x轴刻度线
show: false
},
axisLabel: {
// x轴刻度文案
show: true
},
splitLine: {
// 网格
show: false
}
}],
yAxis: [{
type: 'value',
animation: true,
min: 0,
minInterval: 1,
axisLine: {
// y轴
show: false
},
axisTick: {
// y轴刻度线
show: false
},
axisLabel: {
// y轴刻度文案
show: true
},
splitLine: {
// 网格
show: true,
lineStyle: {
color: 'rgba(137, 137, 137, 0.6)'
}
}
}],
series: [{
type: 'line',
emphasis: {
focus: 'series'
},
name: '',
smooth: true, // 平滑曲线
showSymbol: true, // 节点
symbolSize: 6,
animationDuration: 2500,
lineStyle: {
width: 2
},
itemStyle: {
color: 'rgba(255,235,123, 0.8)'
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(255,235,123, 0.8)'
},
{
offset: 1,
color: 'rgba(60,212,191, 0.8)'
}
])
}
},
data: data.valueNameTrend
}]
})
},

line2: (id, data) => {
const { chart } = new chartClass(id)
chart.setOption({
grid: {
top: '4%',
left: '5%',
right: '8%',
bottom: '4%',
containLabel: true
},
legend: {
show: false,
data: [],
right: '7%',
itemWidth: 6,
itemHeight: 6,
textStyle: {
color: '#fff',
fontSize: '12'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
// 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
yAxis: [{
type: 'category',
data: data.tatil,
animation: true,
offset: 5,
axisLine: {
// x轴
show: false
// lineStyle: {
// color: 'rgba(255, 255, 255, 0.3)'
// }

},
axisTick: {
// x轴刻度线
show: false
},
axisLabel: {
// x轴刻度文案
show: true,
showMaxLabel: true,
rotate: 0, // 倾斜
interval: 0,
formatter: function(params) {
var newParamsName = '' // 最终拼接成的字符串
var paramsNameNumber = params.length // 实际标签的个数
var provideNumber = 7 // 每行能显示的字的个数
var rowNumber = Math.ceil(paramsNameNumber / provideNumber) // 换行的话,需要显示几行,向上取整
/**
* 判断标签的个数是否大于规定的个数, 如果大于,则进行换行处理 如果不大于,即等于或小于,就返回原标签
*/
// 条件等同于rowNumber>1
if (paramsNameNumber > provideNumber) {
/** 循环每一行,p表示行 */
for (var p = 0; p < rowNumber; p++) {
var tempStr = '' // 表示每一次截取的字符串
var start = p * provideNumber // 开始截取的位置
var end = start + provideNumber // 结束截取的位置
// 此处特殊处理最后一行的索引值
if (p == rowNumber - 1) {
// 最后一次不换行
tempStr = params.substring(start, paramsNameNumber)
} else {
// 每一次拼接字符串并换行
tempStr = params.substring(start, end) + '\n'
}
newParamsName += tempStr // 最终拼成的字符串
}
} else {
// 将旧标签的值赋给新标签
newParamsName = params
}
// 将最终的字符串返回
return newParamsName
}
},
splitLine: {
// 网格
show: false
}
}],
xAxis: [{
type: 'value',
name: '',
animation: true,
axisLine: {
// y轴
show: false
},
axisTick: {
// y轴刻度线
show: false
},
axisLabel: {
// y轴刻度文案
show: false

},
splitLine: {
// 网格
show: false,
lineStyle: {
color: 'rgba(102, 102, 102, 0.6)'
}
}
}],
series: [{
barWidth: 22,
type: 'bar',
emphasis: {
focus: 'series'
},
itemStyle: {
// color: "#9AC1FD"
color: '#4CD964',
opacity: 0.5
},
showBackground: true,
backgroundStyle: {
// color: 'rgba(180, 180, 180, 0.2)'
color: 'rgba(225, 106, 53, 0.2)'
},
data: data.data
}
]
}
)
}

}

+ 67
- 0
src/utils/http/interceptors.js View File

@@ -0,0 +1,67 @@
import { router } from '@/router'
import { getToken, removeToken } from '@/utils/token'
import { isWithoutToken } from './help'

export function setupInterceptor(service) {
service.interceptors.request.use(
async(config) => {
// 防止缓存,给get请求加上时间戳
if (config.method === 'get') {
config.params = { ...config.params, t: new Date().getTime() }
}
// 处理不需要token的请求
if (isWithoutToken(config)) {
return config
}
// const token = getToken()
const token = 'token'
if (token) {
config.headers.Authorization = token
return config
}
/**
* * 未登录或者token过期的情况下
* * 跳转登录页重新登录,携带当前路由及参数,登录成功会回到原来的页面
*/
const { currentRoute } = router
router.replace({
path: '/login',
query: { ...currentRoute.query, redirect: currentRoute.path }
})
return Promise.reject({ code: '-1', message: '未登录' })
},
(error) => Promise.reject(error)
)

service.interceptors.response.use(
(response) => {
const { method } = response?.config
const { code } = response?.data
const { currentRoute } = router
switch (code) {
case 0:
if (method !== 'get') {
$message.success(response.data.msg)
}
break
case -1:
$message.error(response.data.msg)
break
case 401:
// 未登录(可能是token过期或者无效了)
removeToken()
router.replace({
path: '/login',
query: { ...currentRoute.query, redirect: currentRoute.path }
})
break
default:
break
}
return response?.data
},
(error) => {
return Promise.reject(error)
}
)
}

+ 6
- 6
src/utils/map/GMap.js View File

@@ -1,11 +1,11 @@
import AMapLoader from '@amap/amap-jsapi-loader';
import AMapLoader from '@amap/amap-jsapi-loader'

export default AMapLoader.load({
"key": "20535a30fa0f3aae9c7bbe4407270792",
"version": "2.0",
"plugins": ['AMap.ToolBar', 'AMap.AutoComplete', "AMap.PlaceSearch", 'AMap.Geocoder'],
'key': '709c024e3faa4a92473b9d847ca87702',
'version': '2.0',
'plugins': ['AMap.ToolBar', 'AMap.AutoComplete', 'AMap.PlaceSearch', 'AMap.Geocoder'],
AMapUI: {
version: '1.1',
plugins: ['overlay/SimpleMarker'],
plugins: ['overlay/SimpleMarker']
}
})
})

BIN
src/views/dashboard/assets/clients.png View File

Before After
Width: 40  |  Height: 40  |  Size: 1.1KB

BIN
src/views/dashboard/assets/fly.png View File

Before After
Width: 40  |  Height: 40  |  Size: 1.3KB

BIN
src/views/dashboard/assets/platform.png View File

Before After
Width: 40  |  Height: 40  |  Size: 1018B

BIN
src/views/dashboard/assets/task.png View File

Before After
Width: 40  |  Height: 40  |  Size: 1.2KB

BIN
src/views/dashboard/assets/total.png View File

Before After
Width: 40  |  Height: 40  |  Size: 1.0KB

+ 52
- 0
src/views/dashboard/components/ContComp.vue View File

@@ -0,0 +1,52 @@
<template>
<div class="ContComp">
<div class="title">任务数据</div>
<div id="chart01" />
</div>
</template>

<script setup name="ContComp">
import { onMounted } from 'vue'
import myChart from '@/utils/echarts'

onMounted(() => {
myChart.line1('chart01', {
'eventNameTrend': [
'2022-09-20',
'2022-09-21',
'2022-09-22',
'2022-09-23',
'2022-09-26',
'2022-09-27',
'2022-09-28'
],
'valueNameTrend': [
'0.00',
'0.00',
'0.00',
'4.00',
'1.00',
'0.00',
'1.00'
]
}
)
})

</script>
<style scoped lang='scss'>
.ContComp {
margin-top: 20px;

.title {
font-size: 16px;
font-weight: 600;
color: rgba(0, 0, 0, 1);
}

#chart01 {
width: 100%;
height: 300px;
}
}
</style>

+ 121
- 0
src/views/dashboard/components/FootComp.vue View File

@@ -0,0 +1,121 @@
<template>
<div class="FootComp">
<n-grid x-gap="12" :cols="3">
<n-gi>
<div class="test">
<div class="comp">
<div class="title">飞手飞行时长</div>
<div id="chart02" />
</div>
<div class="comp" style="margin-top: 20px;">
<div class="title">飞手飞行时长</div>
<div id="chart03" />
</div>
</div>
</n-gi>
<n-gi>
<div class="test">
<div class="comp">
<div class="title">飞手飞行时长</div>
<div id="chart04" />
</div>
<div class="comp" style="margin-top: 20px;">
<div class="title">飞手飞行时长</div>
<div id="chart05" />
</div>
</div>
</n-gi>

<n-gi>
<tab-comp class="comp" />
</n-gi>
</n-grid>
</div>
</template>

<script setup name="FootComp">
import TabComp from './TabComp.vue'
import { onMounted } from 'vue'
import myChart from '@/utils/echarts'

onMounted(() => {
myChart.line2('chart02', {
'data': [
'242.0',
'285.5',
'476.0'
],
'tatil': [
'长椅',
'路灯',
'消防栓'
]
}
)
myChart.line2('chart03', {
'data': [
'242.0',
'285.5',
'476.0'
],
'tatil': [
'长椅',
'路灯',
'消防栓'
]
}
)
myChart.line2('chart04', {
'data': [
'242.0',
'285.5',
'476.0'
],
'tatil': [
'长椅',
'路灯',
'消防栓'
]
}
)
myChart.line2('chart05', {
'data': [
'242.0',
'285.5',
'476.0'
],
'tatil': [
'长椅',
'路灯',
'消防栓'
]
}
)
})

</script>
<style scoped lang='scss'>
.FootComp {
margin-top: 20px;

.comp {
padding: 10px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.25);
}

.title {
font-size: 16px;
font-weight: bold;
}

#chart02,
#chart03,
#chart04,
#chart05 {
width: 100%;
height: 195px;
}
}
</style>

+ 101
- 0
src/views/dashboard/components/HeadComp.vue View File

@@ -0,0 +1,101 @@
<template>
<div class="HeadComp">

<div v-for="(it, i) in headList" :key="i + it.name" class="box" :class="{border: i % 2 != 0}">
<template v-if="i % 2 == 0">
<img :src="it.img" class="img">
<div class="item">
<div class="name">{{ it.name }}</div>
<div class="num"><span class="span">{{ it.value }}</span> 个</div>
</div>
</template>
</div>

</div>
</template>

<script setup name="HeadComp">
import { reactive, ref } from 'vue'
import total from '../assets/total.png'
import task from '../assets/task.png'
import platform from '../assets/platform.png'
import clients from '../assets/clients.png'
import fly from '../assets/fly.png'

const headList = reactive([
{
name: '总条数',
value: 373,
img: total
},
{},
{
name: '待执行任务',
value: 23,
img: task
},
{},
{
name: '接入平台数量',
value: 7,
img: platform
},
{},
{
name: '服务客户数量',
value: 27,
img: clients
},
{},
{
name: '累计飞行时长',
value: 884,
img: fly
}
])

</script>
<style scoped lang='scss'>
.HeadComp {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
font-size: 14px;

height: 60px;

.box {
display: flex;
align-items: center;

.img {
width: 24px;
height: 24px;
}

.item {
margin-left: 20px;

.name {
margin-bottom: 2px;
color: rgba(0, 0, 0, 0.75);
}

.num {
text-align: end;
margin-top: 2px;

.span {
font-weight: bold;
margin-right: 10px;
color: rgba(0, 0, 0, 1);
}
}
}
}

.border {
border: 1px solid rgba(207, 207, 207, 0.65);
}
}
</style>

+ 86
- 0
src/views/dashboard/components/TabComp.vue View File

@@ -0,0 +1,86 @@
<template>
<div class="TabComp">
<div class="title">
<div class="title-left">最新任务</div>
<div class="title-right">查看更多</div>
</div>
<n-space vertical>
<n-table striped>
<thead>
<tr>
<th>序号</th>
<th>任务名称</th>
<th>执行时间</th>
</tr>
</thead>
<tbody>
<tr v-for="(it, i) in tabList" :key="i + it.name">
<td>{{ i + 1 }}</td>
<td>{{ it.name }}</td>
<td>{{ it.time }}</td>
</tr>
</tbody>
</n-table>
</n-space>
</div>
</template>

<script setup name="tabComp">
import { reactive } from 'vue'

const tabList = reactive([
{
name: '宁杭高速巡检',
time: '202-08-19'
},
{
name: '宁杭高速巡检',
time: '202-08-19'
},
{
name: '宁杭高速巡检',
time: '202-08-19'
},
{
name: '宁杭高速巡检',
time: '202-08-19'
},
{
name: '宁杭高速巡检',
time: '202-08-19'
},
{
name: '宁杭高速巡检',
time: '202-08-19'
},
{
name: '宁杭高速巡检',
time: '202-08-19'
},
{
name: '宁杭高速巡检',
time: '202-08-19'
}
])

</script>

<style lang="scss" scoped>
.TabComp {
height: 500px;
.title{
display: flex;
justify-content: space-between;
margin-bottom: 10px;
&-left {
font-size: 16px;
font-weight: bold;
}
&-right {
font-size: 12px;
color: rgba(36, 158, 255, 1);
cursor: pointer;
}
}
}
</style>

+ 16
- 10
src/views/dashboard/index.vue View File

@@ -1,17 +1,23 @@
<template>
<div>
主页
</div>
</template>
<n-card class="wrapper">

<script>
export default {
name: 'HomePage',
setup() {
<head-comp />

}
}
<cont-comp />

<foot-comp />

</n-card>
</template>

<script setup name="dashboard">
import HeadComp from './components/HeadComp.vue'
import ContComp from './components/ContComp.vue'
import FootComp from './components/FootComp.vue'

</script>
<style scoped lang='scss'>
.wrapper {
background: rgba(240, 242, 245, 1);
}
</style>

+ 219
- 61
src/views/task-manage/components/DrawComp.vue View File

@@ -1,91 +1,226 @@
<template>

<!-- 状态 -->
<div class="head">
<n-steps size="small" :current="current" :status="'process'">
<n-step :title="it.label" v-for="(it, i) in TASK_STATUS" :key="i + it.label" />
<n-step v-for="(it, i) in TASK_STATUS" :key="i + it.label" :title="it.label" />
</n-steps>
</div>

<div class="basic">
<!-- 基本信息 -->
<div class="cont">
<n-descriptions label-placement="left" label-align="right" :column="4" title="任务基本信息">
<n-descriptions-item :label="it.label" v-for="(it, i) in basicInfo" :key="i + it.label">
{{ it.value}}
<n-descriptions-item v-for="(it, i) in basicInfo" :key="i + it.label" :label="it.label">
{{ it.value }}
</n-descriptions-item>
</n-descriptions>
</div>

<div class="basic">
<!-- 飞行信息 - 待分配飞手:状态为5 -->
<div v-if="current === 1 && isAdmin" class="cont">
<div class="title">飞行信息</div>
<n-form
ref="formRef"
:model="form.userForm"
:rules="form.userRules"
:label-width="85"
label-placement="left"
require-mark-placement="left"
>
<template v-for="it in form.formItem" :key="it.key">
<n-form-item v-if="it?.isLive !== data.isLive" :label="it.label" :path="it.key">
<n-select v-model:value="form.userForm[it.key]" v-bind="it.props" />
</n-form-item>
</template>
</n-form>
<n-button type="info" class="btn" @click="submitClick">提交</n-button>
</div>

<!-- 飞行信息 - 状态不为5 -->
<div v-if="current !== 1" class="cont">
<n-descriptions label-placement="left" label-align="right" :column="4" title="飞行信息">
<n-descriptions-item :label="it.label" v-for="(it, i) in basicInfo" :key="i + it.label">
{{ it.value}}
</n-descriptions-item>
<template v-for="(it, i) in flyInfo" :key="i + it.label">
<n-descriptions-item v-if="current !== it?.current" :label="it.label">
{{ it.value }}
</n-descriptions-item>
</template>
</n-descriptions>
</div>

<div class="basic">
<!-- 设备/影响基本信息 - 飞手接单: 管理员 -->
<div v-if="current == 2 && isAdmin" class="cont">
<n-descriptions label-placement="left" label-align="right" :column="4" title="设备/影响基本信息">
<n-descriptions-item :label="it.label" v-for="(it, i) in basicInfo" :key="i + it.label">
{{ it.value}}
</n-descriptions-item>
<template v-for="(it, i) in equipmentInfo" :key="i + it.label">
<n-descriptions-item v-if="it?.isLive !== data.isLive" :label="it.label">
{{ it.value }}
</n-descriptions-item>
</template>
</n-descriptions>
</div>

<!-- 飞手接单: 飞手 -->
<div v-if="current === 2 && !isAdmin" class="cont">
<div class="title">飞行信息{{ '-isAdmin:' + isAdmin + data.isLive }}</div>
<n-form
ref="formRefOrder"
:model="form.userForm"
:rules="form.userRulesOrder"
:label-width="85"
label-placement="left"
require-mark-placement="left"
>
<template v-for="it in form.formItemOrder" :key="it.key">
<n-form-item v-if="it?.isLive !== data.isLive" :label="it.label" :path="it.key">
<n-select v-model:value="form.userForm[it.key]" v-bind="it.props" />
</n-form-item>
</template>
</n-form>
<n-button type="info" class="btn" @click="flyClick">接单</n-button>
</div>

<!-- 设备/影响基本信息 - 已接单 -->
<div v-if="current === 3" class="cont">
<n-descriptions label-placement="left" label-align="right" :column="4" title="设备/影响基本信息">
<template v-for="(it, i) in executionInfo" :key="i + it.label">
<n-descriptions-item :label="it.label">
{{ it.value }}
</n-descriptions-item>
</template>
</n-descriptions>
<n-button v-if="isAdmin" type="info" class="btn" @click="startFly">开始飞行</n-button>
</div>

<!-- 设备/影响基本信息 - 执行中: 管理员 -->
<div v-if="current === 4" class="cont">
<n-descriptions label-placement="left" label-align="right" :column="4" title="设备/影响基本信息">
<template v-for="(it, i) in executionInfo" :key="i + it.label">
<n-descriptions-item :label="it.label">
{{ it.value }}
</n-descriptions-item>
</template>
</n-descriptions>
<n-button v-if="isAdmin" type="Error" class="btn" @click="endFly">结束飞行</n-button>
</div>

<div v-if="current === 5" class="cont">
<div class="title">飞行文件</div>

<UploadOss :ref="uploadoss" :limit="2" :default-list="ossList" @upload-status="handleUploadStatus" />

<UploadVod @upload-status="vodStatus" />

<!-- <n-button v-if="data.photographyWay == 1" type="info" class="btn">上传文件</n-button>
<n-button v-if="data.photographyWay == 2" type="info" class="btn">上传文件</n-button>
<n-button v-if="data.photographyWay == 3" type="info" class="btn">上传文件</n-button> -->

</div>

</template>

<script setup name="DrawComp">
import { ref, computed, reactive, onMounted } from 'vue'
import { ref, defineProps, reactive, defineEmits } from 'vue'
import { TASK_STATUS } from '@/utils/dictionary'
import { distributionPilot, pilotOrder, pilotStart, pilotEnd } from '@/api/task/index.js'
import { form, getPilotList, getEquipment, getEquipmentMount, getCloudMount } from '../tools/drawForm'
import { basic, fly, equipment, execution } from '../hook/index'
import UploadOss from '@/components/UploadOss/index.vue'
import UploadVod from '@/components/UploadVod/index.vue'

getPilotList() // 获取飞手列表
getEquipment() // 无人机列表
getEquipmentMount() // 挂载设备列表
getCloudMount() // 挂载盒子列表

const props = defineProps({
detail: Object
detail: {
type: Object,
required: true
}
})

const { detail: {data}} = props;

let basicInfo = reactive([
{
label: "平台",
value: data.platformName
},
{
label: "客户名称",
value: data.tenantName
},
{
label: "任务编号",
value: data.taskCode
},

{
label: "巡逻地点",
value: data.patrolLocation
},
{
label: "任务发起人",
value: data.sponsorName
},

{
label: "发起人联系方式",
value: data.sponsorPhone
},

{
label: "任务发起时间",
value: data.createTime
},
{
label: "期望执行时间",
value: data.taskStartTime
},
{
label: "备注",
value: data.remark
},
])

const current = ref(2)
const { detail: { data }} = props // 传来的详情数据

const current = data.pilotStatus / 5 // 当前状态

const isAdmin = Math.random() > 0.5 // 模拟是否管理员

const formRef = ref() // 表格refs - 分配飞手
const formRefOrder = ref() // 表格refs - 飞手接单
const uploadoss = ref([]) // 上传refs
const ossList = ref('https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg')

const basicInfo = basic(data) // 基础信息
const flyInfo = fly(data) // 飞行信息
const equipmentInfo = equipment(data) // 设备/影响基本信息
const executionInfo = execution(data) // 执行中信息

const emit = defineEmits(['close'])

// 分配飞手
const submitClick = () => {
formRef.value?.validate().then(() => {
const { userForm } = form
distributionPilot({
...userForm,
id: data.id
}).then(({ code }) => {
if (code === 0) {
emit('close')
}
})
}).catch(e => {
$message.error('请完善必填信息')
})
}
// 飞手接单
const flyClick = () => {
formRefOrder.value?.validate().then(() => {
const { userForm } = form
pilotOrder({
...userForm,
flightHandId: data.flightHandId,
id: data.id
}).then(({ code }) => {
if (code === 0) {
emit('close')
}
})
}).catch(e => {
$message.error('请完善必填信息')
})
}

// 开始飞行
const startFly = () => {
pilotStart({ id: data.id }).then(({ code, msg }) => {
if (code === 0) {
emit('close')
} else {
$message.error(msg)
}
})
}

// 结束飞行
const endFly = () => {
pilotEnd({ id: data.id }).then(({ code, msg }) => {
if (code === 0) {
emit('close')
} else {
$message.error(msg)
}
})
}

// 图片上传状态
const handleUploadStatus = status => {
console.log(status)
}

// 视频上传状态
const vodStatus = status => {
console.log(status)
}

</script>

@@ -96,9 +231,21 @@ const current = ref(2)
background-color: #fff;
}

.basic {
.cont {
margin: 20px;
background-color: #fff;
.title {
margin: 20px;
padding: 20px;
font-size: 18px;
font-weight: bold;
}
.btn {
width: 70px;
margin-left: calc(50% - 30px);
margin-top: 10px;
margin-bottom: 30px;
}
}

:deep(.n-descriptions-header) {
@@ -116,4 +263,15 @@ const current = ref(2)
:deep(.n-drawer-body-content-wrapper) {
background-color: rgba(240, 242, 245, 1);
}
</style>
:deep(.n-form) {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
padding: 0 20px;
}
:deep(.n-form-item) {
width: 230px;
margin-right: 15px;
}

</style>

+ 87
- 56
src/views/task-manage/components/UserModal.vue View File

@@ -1,20 +1,35 @@
<template>
<Modal :options="getModalOptions" :on-positive-click="handleConfirm" :on-negative-click="handleClose"
:on-close="handleClose">
<Modal
:options="getModalOptions"
:on-positive-click="handleConfirm"
:on-negative-click="handleClose"
:on-close="handleClose"
>
<template #Context>
<n-form ref="formRef" :model="userForm" :rules="userRules" :label-width="80" label-placement="left"
require-mark-placement="left" :disabled="disabled">
<n-form
ref="formRef"
:model="userForm"
:rules="userRules"
:label-width="80"
label-placement="left"
require-mark-placement="left"
:disabled="disabled"
>
<template v-for="(item,index) in getFormOptions" :key="index">
<n-form-item :label="item.label" :path="item.key">
<UploadOss v-if="item.type === 'oss'" :ref="el=>{ossRefs[item.refIndex] = el}"
:default-list="userForm[item.file]" @upload-status="handleUploadStatus" />
<UploadOss
v-if="item.type === 'oss'"
:ref="el=>{ossRefs[item.refIndex] = el}"
:default-list="userForm[item.file]"
@upload-status="handleUploadStatus"
/>
<n-input v-if="item.type === 'input'" v-model:value="userForm[item.key]" v-bind="item.props" />
<n-select v-if="item.type === 'select'" v-model:value="userForm[item.key]" v-bind="item.props" />
<div v-if="item.type === 'map'" id="mapContainer" class="map"></div>
<div v-if="item.type === 'map'" id="mapContainer" class="map" />

<n-input v-if="item.type === 'inputStart'" disabled v-model:value="userForm[item.key]" v-bind="item.props">
<n-input v-if="item.type === 'inputStart'" v-model:value="userForm[item.key]" disabled v-bind="item.props">
<template #suffix>
<n-button @click="showStart = true" quaternary type="info">去标记
<n-button quaternary type="info" @click="showStart = true">去标记
<n-icon size="20" color="rgba(42, 130, 228, 1)">
<LocationSharp />
</n-icon>
@@ -22,9 +37,9 @@
</template>
</n-input>

<n-input v-if="item.type === 'inputEnd'" disabled v-model:value="userForm[item.key]" v-bind="item.props">
<n-input v-if="item.type === 'inputEnd'" v-model:value="userForm[item.key]" disabled v-bind="item.props">
<template #suffix>
<n-button @click="showEnd = true" quaternary type="info">去标记
<n-button quaternary type="info" @click="showEnd = true">去标记
<n-icon size="20" color="rgba(42, 130, 228, 1)">
<LocationSharp />
</n-icon>
@@ -32,12 +47,17 @@
</template>
</n-input>

<n-date-picker v-if="item.type === 'datetime'" v-model:formatted-value="userForm[item.key]"
v-bind="item.props" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" />
<n-date-picker
v-if="item.type === 'datetime'"
v-model:formatted-value="userForm[item.key]"
v-bind="item.props"
type="datetime"
value-format="yyyy-MM-dd HH:mm:ss"
/>
<n-radio-group v-if="item.type === 'radio'" v-model:value="userForm[item.key]" :name="item.key">
<n-space>
<n-radio v-for="(cItem,cIndex) in item.options" :key="`${item.key}_${cIndex}`" :value="cItem.value"> {{
cItem.label }}</n-radio>
cItem.label }}</n-radio>
</n-space>
</n-radio-group>
</n-form-item>
@@ -46,22 +66,35 @@
</template>
</Modal>


<n-modal v-model:show="showStart" :mask-closable="false" preset="dialog" title="" positive-text="确定"
@positive-click="userForm.patrolLocation = startVal" @negative-click="showStart = false">
<map-comp @mapEmit="startHandle"></map-comp>
<n-modal
v-model:show="showStart"
:mask-closable="false"
preset="dialog"
title=""
positive-text="确定"
@positive-click="userForm.patrolLocation = startVal"
@negative-click="showStart = false"
>
<map-comp @mapEmit="startHandle" />
</n-modal>

<n-modal v-model:show="showEnd" :mask-closable="false" preset="dialog" title="" positive-text="确定"
@positive-click="userForm.endValue = endVal" @negative-click="showEnd = false">
<map-comp @mapEmit="endHandle"></map-comp>
<n-modal
v-model:show="showEnd"
:mask-closable="false"
preset="dialog"
title=""
positive-text="确定"
@positive-click="userForm.endValue = endVal"
@negative-click="showEnd = false"
>
<map-comp @mapEmit="endHandle" />
</n-modal>

</template>

<script>
import { form, getPilotList, getEquipment, getEquipmentMount } from '../tools/form.js'
import { defineComponent, ref, reactive, computed, toRefs, unref } from 'vue'
import { defineComponent, ref, reactive, computed, toRefs } from 'vue'
import Modal from '@/components/Modal/index.vue'
import UploadOss from '@/components/UploadOss/index.vue'
import { taskAdd, taskEdit } from '@/api/task'
@@ -97,9 +130,9 @@ export default defineComponent({
'preview': '任务详情',
'update': '编辑任务'
}
getPilotList();
getEquipment();
getEquipmentMount();
getPilotList()
getEquipment()
getEquipmentMount()
const { formItem, userForm, userRules } = form
const formRef = ref()
const ossRefs = ref([])
@@ -132,29 +165,29 @@ export default defineComponent({
data.userForm.imageStatus = status
}

let geocoder = null;
let geocoder = null
// 地图
GMap.then((AMap) => {
let Gmap = null;
let Gmap = null
// 坐标求地址
geocoder = new AMap.Geocoder()

if(props.type == 'update') { // 打开编辑
if (props.type === 'update') { // 打开编辑
Gmap = new AMap.Map('mapContainer', {
zoom: 13,
center: [data.userForm.startLongitude, data.userForm.startLatitude],
});
center: [data.userForm.startLongitude, data.userForm.startLatitude]
})

var marker = new AMap.Marker({
position: new AMap.LngLat(data.userForm.startLongitude, data.userForm.startLatitude),
icon: '//vdata.amap.com/icons/b18/1/2.png',
title: '起点'
});
})

// 将创建的点标记添加到已有的地图实例:
Gmap.add(marker);
Gmap.add(marker)
// 起点赋值
geocoder.getAddress([data.userForm.startLongitude, data.userForm.startLatitude], (status, result) => {
if (status === 'complete' && result.info === 'OK') {
@@ -171,53 +204,51 @@ export default defineComponent({
} else {
Gmap = new AMap.Map('mapContainer', {
zoom: 13,
center: [118.773319, 31.828123],
});
center: [118.773319, 31.828123]
})
}

// 添加缩放工具
const toolbar = new AMap.ToolBar();
Gmap.addControl(toolbar);

const toolbar = new AMap.ToolBar()
Gmap.addControl(toolbar)
}).catch(e => console.log(e))

// 地图辅助参数
let showStart = ref(false);
let showEnd = ref(false);
const showStart = ref(false)
const showEnd = ref(false)

const startVal = ref(null);
const endVal = ref(null);
const startVal = ref(null)
const endVal = ref(null)

// 起点地图 确定事件
const startHandle = e => {
const { location: { lat } } = e;
const { location: { lng } } = e;
geocoder.getAddress([lng, lat], function (status, result) {
const { location: { lat }} = e
const { location: { lng }} = e
geocoder.getAddress([lng, lat], function(status, result) {
if (status === 'complete' && result.info === 'OK') {
startVal.value = result.regeocode.formattedAddress
data.userForm.startLongitude = String(lng);
data.userForm.startLatitude = String(lat);
data.userForm.startLongitude = String(lng)
data.userForm.startLatitude = String(lat)
}
})
}
// 终点地图 确定事件
const endHandle = e => {
const { location: { lat } } = e;
const { location: { lng } } = e;
geocoder.getAddress([lng, lat], function (status, result) {
const { location: { lat }} = e
const { location: { lng }} = e
geocoder.getAddress([lng, lat], function(status, result) {
if (status === 'complete' && result.info === 'OK') {
endVal.value = result.regeocode.formattedAddress
data.userForm.endLongitude = String(lng);
data.userForm.endLatitude = String(lat);
data.userForm.endLongitude = String(lng)
data.userForm.endLatitude = String(lat)
}
})
}

function handleConfirm() {

formRef.value?.validate(async (errors) => {
formRef.value?.validate(async(errors) => {
if (!errors) {
let params = {
const params = {
...data.userForm,
taskStartTime: String(data.userForm.taskStartTime)
}

+ 50
- 0
src/views/task-manage/hook/basic.js View File

@@ -0,0 +1,50 @@
import { customRef } from 'vue'

export default function(data) {
const list = [
{
label: '平台',
value: data.platformName
},
{
label: '客户名称',
value: data.tenantName
},
{
label: '任务编号',
value: data.taskCode
},
{
label: '巡逻地点',
value: data.patrolLocation
},
{
label: '任务发起人',
value: data.sponsorName
},
{
label: '发起人联系方式',
value: data.sponsorPhone
},
{
label: '任务发起时间',
value: data.createTime
},
{
label: '期望执行时间',
value: data.taskStartTime
},
{
label: '备注',
value: data.remark
}
]
return customRef((track, trigger) => {
return {
get() {
track()
return list
}
}
})
}

+ 33
- 0
src/views/task-manage/hook/equipment.js View File

@@ -0,0 +1,33 @@
import { customRef } from 'vue'

const photographyWay = ['普通巡检', '正射影像', '倾斜摄影'] // 摄影方式

export default function(data) {
const list = [
{
label: '执飞无人机',
value: data.equipmentName
},
{
label: '摄影方式',
value: photographyWay[data.photographyWay - 1]
},
{
label: '挂载设备',
value: data.equipmentMountName
},
{
label: '盒子名称',
value: data.cloudBoxName,
isLive: 1
}
]
return customRef((track, trigger) => {
return {
get() {
track()
return list
}
}
})
}

+ 28
- 0
src/views/task-manage/hook/execution.js View File

@@ -0,0 +1,28 @@
import { customRef } from 'vue'

const photographyWay = ['普通巡检', '正射影像', '倾斜摄影'] // 摄影方式

export default function(data) {
const list = [
{
label: '执飞无人机',
value: data.equipmentName
},
{
label: '摄影方式',
value: photographyWay[data.photographyWay - 1]
},
{
label: '挂载设备',
value: data.equipmentMountName
}
]
return customRef((track, trigger) => {
return {
get() {
track()
return list
}
}
})
}

+ 39
- 0
src/views/task-manage/hook/fly.js View File

@@ -0,0 +1,39 @@
import { customRef } from 'vue'

export default function(data) {
const list = [
{
label: '分配人',
value: data.distributeUserName
},
{
label: '分配飞手时间',
value: data.distributeTime
},
{
label: '飞手',
value: data.flightHandName
},
{
label: '飞手接单时间',
value: data.ordersTime
},
{
label: '飞手接单状态',
value: '状态待确定?'
},
{
label: '飞行开始时间',
value: '暂不知道取值',
current: 4
}
]
return customRef((track, trigger) => {
return {
get() {
track()
return list
}
}
})
}

+ 11
- 0
src/views/task-manage/hook/index.js View File

@@ -0,0 +1,11 @@
import basic from './basic.js'
import fly from './fly.js'
import equipment from './equipment.js'
import execution from './execution.js'

export {
basic,
fly,
equipment,
execution
}

+ 12
- 7
src/views/task-manage/index.vue View File

@@ -5,8 +5,14 @@
<headSearch :info="search" @search="handleSearch" @reset="handleSearch" />

<!-- 表格 -->
<data-table ref="tableRef" :columns="columns" :row-key="(row) => row.id" :request="loadDataTable" size="large"
@update:checked-row-keys="handleCheck">
<data-table
ref="tableRef"
:columns="columns"
:row-key="(row) => row.id"
:request="loadDataTable"
size="large"
@update:checked-row-keys="handleCheck"
>
<template #tableTitle>
<n-button type="primary" @click="handleModal"> 新建 </n-button>
</template>
@@ -20,7 +26,7 @@
<!-- 详情 - 抽屉 -->
<n-drawer v-model:show="showDraw" :width="'calc(100vw - 210px)'" :placement="'right'" resizable>
<n-drawer-content closable>
<draw-comp :detail="detail" />
<draw-comp :detail="detail" @close="showDraw = false" />
</n-drawer-content>
</n-drawer>

@@ -33,20 +39,19 @@ import headSearch from '@/components/Search/index.vue'
import dataTable from '@/components/DataTable/index.vue'
import DrawComp from './components/DrawComp.vue'
import UserModal from './components/UserModal.vue'
import { getTaskList } from '@/api/task/index';
import { getTaskList } from '@/api/task/index'
import { unref, ref, toRefs, reactive, onUnmounted } from 'vue'

export default {
name: 'TaskManage',
components: { dataTable, UserModal, headSearch, DrawComp },
setup() {

const data = reactive({
...toRefs(table),
...toRefs(search)
})

const loadDataTable = async (res) => {
const loadDataTable = async(res) => {
const _params = {
...unref(data.searchParams),
...res
@@ -102,4 +107,4 @@ export default {
:deep(.n-drawer-body-content-wrapper) {
background-color: rgba(240, 242, 245, 0.8);
}
</style>
</style>

+ 89
- 0
src/views/task-manage/tools/drawForm.js View File

@@ -0,0 +1,89 @@
import { ref, reactive } from 'vue'
import { getTaskPilot } from '@/api/task/index.js'
import { getEquipment as equipment, getEquipmentMount as equipmentMount, getCloud } from '@/api/equipment'
import { PHOTOGRAPHY_WAY } from '@/utils/dictionary'

// 无人机列表
const equipmentList = ref([])

// 挂载设备列表
const equipmentMountList = ref([])

// 飞手列表
const pilotList = ref([])

// 挂载盒子
const cloudList = ref([])

export const form = reactive({
userForm: {
flightHandId: null,
equipmentId: null,
equipmentMountId: null,
cloudBoxId: null,
photographyWay: null
},
userRules: {
flightHandId: [{ required: true, message: '请选择飞手', trigger: 'blur' }]
},
formItem: [
{ type: 'select', key: 'flightHandId', label: '飞手', props: { options: pilotList, placeholder: '请选择飞手', clearable: true }},
{ type: 'select', key: 'equipmentId', label: '执飞无人机', props: { options: equipmentList, placeholder: '请选择执飞无人机', clearable: true }},
{ type: 'select', key: 'equipmentMountId', label: '挂载设备', props: { options: equipmentMountList, placeholder: '请选择挂载设备', clearable: true }},
{ type: 'select', key: 'cloudBoxId', label: '挂载盒子', isLive: 1, props: { options: cloudList, placeholder: '请选择盒子', clearable: true }},
{ type: 'select', key: 'photographyWay', label: '拍摄方式', props: { options: PHOTOGRAPHY_WAY, placeholder: '请选择拍摄方式', clearable: true }}
],
// 飞手接单 - 校验
userRulesOrder: {
equipmentId: [{ required: true, message: '请选择执飞无人机', trigger: 'blur' }],
photographyWay: [{ required: true, message: '请选择摄影方式', trigger: 'blur' }],
equipmentMountId: [{ required: true, message: '请选择挂载设备', trigger: 'blur' }]
},
// 飞手接单 - 表单
formItemOrder: [
{ type: 'select', key: 'equipmentId', label: '执飞无人机', props: { options: equipmentList, placeholder: '请选择执飞无人机', clearable: true }},
{ type: 'select', key: 'photographyWay', label: '摄影方式', props: { options: PHOTOGRAPHY_WAY, placeholder: '请选择拍摄方式', clearable: true }},
{ type: 'select', key: 'equipmentMountId', label: '挂载设备', props: { options: equipmentMountList, placeholder: '请选择挂载设备', clearable: true }},
{ type: 'select', key: 'cloudBoxId', label: '盒子名称', isLive: 0, props: { options: cloudList, placeholder: '请选择盒子', clearable: true }}
]
})

// 获取飞手列表
export const getPilotList = async function() {
const { data } = await getTaskPilot()
pilotList.value = data.map(it => ({
...it,
label: it.username,
value: it.id
}))
}

// 无人机列表
export const getEquipment = async function() {
const { data } = await equipment()
equipmentList.value = data.map(it => ({
...it,
label: it.manufacturer,
value: it.id
}))
}

// 挂载设备列表
export const getEquipmentMount = async function() {
const { data } = await equipmentMount()
equipmentMountList.value = data.map(it => ({
...it,
label: it.name,
value: it.id
}))
}

// 挂载盒子列表
export const getCloudMount = async function() {
const { data } = await getCloud()
cloudList.value = data.map(it => ({
...it,
label: it.boxName,
value: it.id
}))
}

+ 10
- 26
src/views/task-manage/tools/form.js View File

@@ -1,32 +1,16 @@
import { ref, reactive, computed } from 'vue'
import { ref, reactive } from 'vue'
import { getTaskPilot } from '@/api/task/index.js'
import { getEquipment as equipment, getEquipmentMount as equipmentMount } from '@/api/equipment'
import { PHOTOGRAPHY_WAY } from '@/utils/dictionary'

// 无人机列表
const equipmentList = ref([]);
const equipmentList = ref([])

// 挂载设备列表
const equipmentMountList = ref([]);
const equipmentMountList = ref([])

// 飞手列表
const pilotList = ref();

// 拍摄方式
const photographyWay = [
{
label: '普通巡检',
value: 1
},
{
label: '正射影像',
value: 2
},
{
label: '倾斜摄影',
value: 3
},
]
const pilotList = ref()

export const form = reactive({
userForm: {
@@ -42,15 +26,15 @@ export const form = reactive({
remark: ''
},
userRules: {
inspectionType: [{ required: true, message: '请选择巡检类型', type: 'number',trigger: ["blur", "change"] }],
inspectionType: [{ required: true, message: '请选择巡检类型', type: 'number', trigger: ['blur', 'change'] }],
taskName: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
patrolLocation: [{ required: true, message: '请输入任务起点', trigger: 'blur' }],
endValue: [{ required: true, message: '请输入任务终点', trigger: 'blur' }],
taskStartTime: [{ required: true, message: '请输入飞行时间', trigger: 'blur' }],
flightHandId: [{ required: true, message: '请选择飞手', trigger: 'blur' }],
flightHandId: [{ required: true, message: '请选择飞手', trigger: 'blur' }]
},
formItem: [
{ type: 'select', key: 'inspectionType', label: '巡检类型', props: { options: [{label: '自营计划', value: 1}], maxlength: '20', placeholder: '请输入巡检类型', clearable: true } },
{ type: 'select', key: 'inspectionType', label: '巡检类型', props: { options: [{ label: '自营计划', value: 1 }], maxlength: '20', placeholder: '请输入巡检类型', clearable: true }},
{ type: 'input', key: 'taskName', label: '任务名称', props: { maxlength: '20', placeholder: '请输入任务名称', clearable: true }},

{ type: 'map' },
@@ -62,9 +46,9 @@ export const form = reactive({
{ type: 'select', key: 'flightHandId', label: '飞手', props: { options: pilotList, placeholder: '请选择飞手', clearable: true }},
{ type: 'select', key: 'equipmentId', label: '执飞无人机', props: { options: equipmentList, maxlength: '20', placeholder: '请选择执飞无人机', clearable: true }},
{ type: 'select', key: 'equipmentMountId', label: '挂载设备', props: { options: equipmentMountList, maxlength: '20', placeholder: '请选择挂载设备', clearable: true }},
{ type: 'select', key: 'photographyWay', label: '拍摄方式', props: { options: photographyWay, maxlength: '20', placeholder: '请选择拍摄方式', clearable: true }},
{ type: 'select', key: 'photographyWay', label: '拍摄方式', props: { options: PHOTOGRAPHY_WAY, maxlength: '20', placeholder: '请选择拍摄方式', clearable: true }},

{ type: 'input', key: 'remark', label: '备注', props: { type: 'textarea', autosize: { maxlength: '200', minRows: 3, maxRows: 3 }}},
{ type: 'input', key: 'remark', label: '备注', props: { type: 'textarea', autosize: { maxlength: '200', minRows: 3, maxRows: 3 }}}

]
})

Loading…
Cancel
Save