@@ -1,8 +1,8 @@ | |||
# title | |||
VITE_APP_TITLE = '河湖长眼' | |||
VITE_APP_TITLE = '智飞' | |||
# 端口号 | |||
VITE_PORT = 3000 | |||
VITE_PORT = 3050 | |||
VITE_SERVER = "/pilot/admin" |
@@ -5,7 +5,7 @@ VITE_PUBLIC_PATH = '/' | |||
VITE_APP_USE_MOCK = false | |||
# proxy | |||
VITE_PROXY = [["/api","http://192.168.11.11:7011/api"]] | |||
VITE_PROXY = [["/api","http://192.168.11.11:9055"]] | |||
# base api | |||
VITE_APP_GLOB_BASE_API = '/api' |
@@ -2,10 +2,10 @@ | |||
VITE_PUBLIC_PATH = '/' | |||
# 是否启用MOCK | |||
VITE_APP_USE_MOCK = true | |||
VITE_APP_USE_MOCK = false | |||
# proxy | |||
VITE_PROXY = [["/api-local","http://127.0.0.1:8002/api"],["/api-mock","http://127.0.0.1:8003"]] | |||
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' |
@@ -5,7 +5,7 @@ VITE_PUBLIC_PATH = '/' | |||
VITE_APP_USE_MOCK = false | |||
# proxy | |||
VITE_PROXY = [["/api","http://192.168.11.241:7011/api"]] | |||
VITE_PROXY = [["/api","http://192.168.11.11:9055"]] | |||
# base api | |||
VITE_APP_GLOB_BASE_API = '/api' |
@@ -2,4 +2,5 @@ node_modules | |||
.DS_Store | |||
dist | |||
dist-ssr | |||
*.local | |||
*.local | |||
.idea |
@@ -1,17 +1,42 @@ | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="UTF-8" /> | |||
<meta http-equiv="Expires" content="0" /> | |||
<meta http-equiv="Pragma" content="no-cache" /> | |||
<meta http-equiv="Cache-control" content="no-cache" /> | |||
<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" /> | |||
<title><%= title %></title> | |||
</head> | |||
<body> | |||
<div id="app"></div> | |||
<script type="module" src="/src/main.js"></script> | |||
</body> | |||
</html> | |||
<head> | |||
<meta charset="UTF-8" /> | |||
<meta http-equiv="Expires" content="0" /> | |||
<meta http-equiv="Pragma" content="no-cache" /> | |||
<meta http-equiv="Cache-control" content="no-cache" /> | |||
<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', | |||
securityJsCode: 'eb839debb422cd65cc598664067ee7d8' | |||
} | |||
</script> | |||
<style type="text/css"> | |||
.amap-logo { | |||
display: none; | |||
opacity: 0 !important; | |||
} | |||
.amap-copyright { | |||
opacity: 0; | |||
} | |||
</style> | |||
</head> | |||
<body> | |||
<div id="app"></div> | |||
<script type="module" src="/src/main.js"></script> | |||
</body> | |||
</html> |
@@ -8,14 +8,17 @@ | |||
"name": "vite_vue3", | |||
"version": "0.0.0", | |||
"dependencies": { | |||
"@amap/amap-jsapi-loader": "^1.0.1", | |||
"@tinymce/tinymce-vue": "^4.0.5", | |||
"@vicons/antd": "^0.10.0", | |||
"@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", | |||
"pinia-plugin-persist": "^1.0.0", | |||
"tinymce": "^5.10.2", | |||
"vue": "^3.2.16", | |||
"vue-router": "^4.0.14", | |||
@@ -59,6 +62,11 @@ | |||
"node": "8 || 10 || 12 || 14 || 16 || 17" | |||
} | |||
}, | |||
"node_modules/@amap/amap-jsapi-loader": { | |||
"version": "1.0.1", | |||
"resolved": "https://registry.npmjs.org/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz", | |||
"integrity": "sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw==" | |||
}, | |||
"node_modules/@ampproject/remapping": { | |||
"version": "2.1.2", | |||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", | |||
@@ -2842,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", | |||
@@ -2896,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", | |||
@@ -4096,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", | |||
@@ -8380,6 +8438,49 @@ | |||
} | |||
} | |||
}, | |||
"node_modules/pinia-plugin-persist": { | |||
"version": "1.0.0", | |||
"resolved": "https://registry.npmjs.org/pinia-plugin-persist/-/pinia-plugin-persist-1.0.0.tgz", | |||
"integrity": "sha512-M4hBBd8fz/GgNmUPaaUsC29y1M09lqbXrMAHcusVoU8xlQi1TqgkWnnhvMikZwr7Le/hVyMx8KUcumGGrR6GVw==", | |||
"dependencies": { | |||
"vue-demi": "^0.12.1" | |||
}, | |||
"peerDependencies": { | |||
"@vue/composition-api": "^1.0.0", | |||
"pinia": "^2.0.0", | |||
"vue": "^2.0.0 || >=3.0.0" | |||
}, | |||
"peerDependenciesMeta": { | |||
"@vue/composition-api": { | |||
"optional": true | |||
} | |||
} | |||
}, | |||
"node_modules/pinia-plugin-persist/node_modules/vue-demi": { | |||
"version": "0.12.5", | |||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz", | |||
"integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==", | |||
"hasInstallScript": true, | |||
"bin": { | |||
"vue-demi-fix": "bin/vue-demi-fix.js", | |||
"vue-demi-switch": "bin/vue-demi-switch.js" | |||
}, | |||
"engines": { | |||
"node": ">=12" | |||
}, | |||
"funding": { | |||
"url": "https://github.com/sponsors/antfu" | |||
}, | |||
"peerDependencies": { | |||
"@vue/composition-api": "^1.0.0-rc.1", | |||
"vue": "^3.0.0-0 || ^2.6.0" | |||
}, | |||
"peerDependenciesMeta": { | |||
"@vue/composition-api": { | |||
"optional": true | |||
} | |||
} | |||
}, | |||
"node_modules/pinia/node_modules/vue-demi": { | |||
"version": "0.13.1", | |||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.1.tgz", | |||
@@ -12076,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": { | |||
@@ -12090,6 +12196,11 @@ | |||
"js-message": "1.0.7" | |||
} | |||
}, | |||
"@amap/amap-jsapi-loader": { | |||
"version": "1.0.1", | |||
"resolved": "https://registry.npmjs.org/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz", | |||
"integrity": "sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw==" | |||
}, | |||
"@ampproject/remapping": { | |||
"version": "2.1.2", | |||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", | |||
@@ -14321,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", | |||
@@ -14360,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", | |||
@@ -15270,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", | |||
@@ -18456,6 +18613,22 @@ | |||
} | |||
} | |||
}, | |||
"pinia-plugin-persist": { | |||
"version": "1.0.0", | |||
"resolved": "https://registry.npmjs.org/pinia-plugin-persist/-/pinia-plugin-persist-1.0.0.tgz", | |||
"integrity": "sha512-M4hBBd8fz/GgNmUPaaUsC29y1M09lqbXrMAHcusVoU8xlQi1TqgkWnnhvMikZwr7Le/hVyMx8KUcumGGrR6GVw==", | |||
"requires": { | |||
"vue-demi": "^0.12.1" | |||
}, | |||
"dependencies": { | |||
"vue-demi": { | |||
"version": "0.12.5", | |||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz", | |||
"integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==", | |||
"requires": {} | |||
} | |||
} | |||
}, | |||
"platform": { | |||
"version": "1.3.6", | |||
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", | |||
@@ -21184,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==" | |||
} | |||
} | |||
} |
@@ -9,11 +9,13 @@ | |||
"serve": "vite preview" | |||
}, | |||
"dependencies": { | |||
"@amap/amap-jsapi-loader": "^1.0.1", | |||
"@tinymce/tinymce-vue": "^4.0.5", | |||
"@vicons/antd": "^0.10.0", | |||
"@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", |
@@ -0,0 +1,64 @@ | |||
import { defAxios as request } from '@/utils/http' | |||
/** | |||
* 任务总数数量统计 | |||
* @returns | |||
*/ | |||
export const statisticsTask = () => request({ | |||
url: '/index/statisticsTask', | |||
method: 'GET' | |||
}) | |||
/** | |||
* 任务总数 趋势 | |||
* @returns | |||
*/ | |||
export const pilotTaskDate = () => request({ | |||
url: '/index/pilotTaskDate', | |||
method: 'GET' | |||
}) | |||
/** | |||
* 飞手飞行时长 | |||
* @returns | |||
*/ | |||
export const pilotTaskTimeCount = () => request({ | |||
url: '/index/pilotTaskTimeCount', | |||
method: 'GET' | |||
}) | |||
/** | |||
* 任务状态 | |||
* @returns | |||
*/ | |||
export const taskStatusStatistics = () => request({ | |||
url: '/index/taskStatusStatistics', | |||
method: 'GET' | |||
}) | |||
/** | |||
* 飞手飞行任务数量 | |||
* @returns | |||
*/ | |||
export const pilotTaskStatistics = () => request({ | |||
url: '/index/pilotTaskStatistics', | |||
method: 'GET' | |||
}) | |||
/** | |||
* 平台任务数量 | |||
* @returns | |||
*/ | |||
export const platformTasks = () => request({ | |||
url: '/index/platformTasks', | |||
method: 'GET' | |||
}) | |||
/** | |||
* 最新任务列表 | |||
* @returns | |||
*/ | |||
export const newestTask = () => request({ | |||
url: '/index/newestTask', | |||
method: 'GET' | |||
}) |
@@ -0,0 +1,59 @@ | |||
import { defAxios as request } from '@/utils/http' | |||
/** | |||
* 获取云盒列表(分页) | |||
* @param {Object} | |||
* @returns | |||
*/ | |||
export function boxPage(params) { | |||
return request({ | |||
url: '/cloudBox/index', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
/** | |||
* 获取云盒列表 | |||
* params | |||
* @returns | |||
*/ | |||
export function boxAll() { | |||
return request({ | |||
url: '/cloudBox/getList', | |||
method: 'get' | |||
}) | |||
} | |||
/** | |||
* 创建盒子 | |||
* @returns | |||
*/ | |||
export function boxCreate(data) { | |||
return request({ | |||
url: '/cloudBox/add', | |||
method: 'post', | |||
data | |||
}) | |||
} | |||
/** | |||
* 编辑盒子 | |||
* @returns | |||
*/ | |||
export function boxUpdate(data) { | |||
return request({ | |||
url: '/cloudBox/edit', | |||
method: 'put', | |||
data | |||
}) | |||
} | |||
/** | |||
* 删除盒子 | |||
* @returns | |||
*/ | |||
export function boxDelete(ids) { | |||
return request({ | |||
url: `/cloudBox/delete/${ids}`, | |||
method: 'delete' | |||
}) | |||
} |
@@ -0,0 +1,28 @@ | |||
import { defAxios as request } from '@/utils/http' | |||
/** | |||
* 无人机列表 | |||
* @returns | |||
*/ | |||
export const getEquipment = () => request({ | |||
url: '/equipment/getList', | |||
method: 'GET' | |||
}) | |||
/** | |||
* 挂载设备列表 | |||
* @returns | |||
*/ | |||
export const getEquipmentMount = () => request({ | |||
url: '/equipmentMount/getList', | |||
method: 'GET' | |||
}) | |||
/** | |||
* 挂载盒子 | |||
* @returns | |||
*/ | |||
export const getCloud = () => request({ | |||
url: '/cloudBox/getList', | |||
method: 'GET' | |||
}) |
@@ -0,0 +1,59 @@ | |||
import { defAxios as request } from '@/utils/http' | |||
/** | |||
* 获取挂载设备列表(分页) | |||
* @param {Object} | |||
* @returns | |||
*/ | |||
export function mountPage(params) { | |||
return request({ | |||
url: '/equipmentMount/index', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
/** | |||
* 获取挂载设备 | |||
* params | |||
* @returns | |||
*/ | |||
export function mountAll() { | |||
return request({ | |||
url: '/equipmentMount/getList', | |||
method: 'get' | |||
}) | |||
} | |||
/** | |||
* 创建挂载设备 | |||
* @returns | |||
*/ | |||
export function mountCreate(data) { | |||
return request({ | |||
url: '/equipmentMount/add', | |||
method: 'post', | |||
data | |||
}) | |||
} | |||
/** | |||
* 编辑挂载设备 | |||
* @returns | |||
*/ | |||
export function mountUpdate(data) { | |||
return request({ | |||
url: '/equipmentMount/edit', | |||
method: 'put', | |||
data | |||
}) | |||
} | |||
/** | |||
* 删除挂载设备 | |||
* @returns | |||
*/ | |||
export function mountDelete(ids) { | |||
return request({ | |||
url: `/equipmentMount/delete/${ids}`, | |||
method: 'delete' | |||
}) | |||
} |
@@ -0,0 +1,59 @@ | |||
import { defAxios as request } from '@/utils/http' | |||
/** | |||
* 获取挂载设备列表(分页) | |||
* @param {Object} | |||
* @returns | |||
*/ | |||
export function uvaPage(params) { | |||
return request({ | |||
url: '/equipment/index', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
/** | |||
* 获取挂载设备 | |||
* params | |||
* @returns | |||
*/ | |||
export function uvaAll() { | |||
return request({ | |||
url: '/equipment/getList', | |||
method: 'get' | |||
}) | |||
} | |||
/** | |||
* 创建挂载设备 | |||
* @returns | |||
*/ | |||
export function uvaCreate(data) { | |||
return request({ | |||
url: '/equipment/add', | |||
method: 'post', | |||
data | |||
}) | |||
} | |||
/** | |||
* 编辑挂载设备 | |||
* @returns | |||
*/ | |||
export function uvaUpdate(data) { | |||
return request({ | |||
url: '/equipment/edit', | |||
method: 'put', | |||
data | |||
}) | |||
} | |||
/** | |||
* 删除挂载设备 | |||
* @returns | |||
*/ | |||
export function uvaDelete(ids) { | |||
return request({ | |||
url: `/equipment/delete/${ids}`, | |||
method: 'delete' | |||
}) | |||
} |
@@ -0,0 +1,47 @@ | |||
import { defAxios as request } from '@/utils/http' | |||
/** | |||
* 获取云盒列表(分页) | |||
* @param {Object} | |||
* @returns | |||
*/ | |||
export function bannerPage(params) { | |||
return request({ | |||
url: '/pilotAd/index', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
/** | |||
* 创建盒子 | |||
* @returns | |||
*/ | |||
export function bannerCreate(data) { | |||
return request({ | |||
url: '/pilotAd/add', | |||
method: 'post', | |||
data | |||
}) | |||
} | |||
/** | |||
* 编辑盒子 | |||
* @returns | |||
*/ | |||
export function bannerUpdate(data) { | |||
return request({ | |||
url: '/pilotAd/edit', | |||
method: 'put', | |||
data | |||
}) | |||
} | |||
/** | |||
* 删除盒子 | |||
* @returns | |||
*/ | |||
export function bannerDelete(ids) { | |||
return request({ | |||
url: `/pilotAd/delete/${ids}`, | |||
method: 'delete' | |||
}) | |||
} |
@@ -0,0 +1,47 @@ | |||
import { defAxios as request } from '@/utils/http' | |||
/** | |||
* 获取用户列表(分页) | |||
* @param {Object} | |||
* @returns | |||
*/ | |||
export function userPage(params) { | |||
return request({ | |||
url: '/user/index', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
/** | |||
* 创建用户 | |||
* @returns | |||
*/ | |||
export function userCreate(data) { | |||
return request({ | |||
url: '/user/add', | |||
method: 'post', | |||
data | |||
}) | |||
} | |||
/** | |||
* 编辑用户 | |||
* @returns | |||
*/ | |||
export function userUpdate(data) { | |||
return request({ | |||
url: '/user/edit', | |||
method: 'put', | |||
data | |||
}) | |||
} | |||
/** | |||
* 删除用户 | |||
* @returns | |||
*/ | |||
export function userDelete(ids) { | |||
return request({ | |||
url: `/delete/${ids}`, | |||
method: 'delete' | |||
}) | |||
} |
@@ -0,0 +1,117 @@ | |||
import { defAxios as request } from '@/utils/http' | |||
/** | |||
* 任务查询 | |||
* @returns | |||
*/ | |||
export const getTaskList = params => request({ | |||
url: '/task/getTaskList', | |||
method: 'GET', | |||
params | |||
}) | |||
/** | |||
* 任务详情 | |||
* @param {*} params id | |||
* @returns | |||
*/ | |||
export const getTaskInfo = params => request({ | |||
url: `/task/getInfo/${params}`, | |||
method: 'GET' | |||
}) | |||
/** | |||
* 获取飞手列表 | |||
* @returns | |||
*/ | |||
export const getTaskPilot = () => request({ | |||
url: '/task/getPilot', | |||
method: 'GET' | |||
}) | |||
/** | |||
* 添加任务 | |||
* @param {*} params | |||
* @returns | |||
*/ | |||
export const taskAdd = data => request({ | |||
url: '/task/add', | |||
method: 'POST', | |||
data | |||
}) | |||
/** | |||
* 编辑任务 | |||
* @param {*} params | |||
* @returns | |||
*/ | |||
export const taskEdit = data => request({ | |||
url: '/task/edit', | |||
method: 'PUT', | |||
data | |||
}) | |||
/** | |||
* 删除任务 | |||
* @param {*} params | |||
* @returns | |||
*/ | |||
export const taskDel = params => request({ | |||
url: `/task/delete/${params}`, | |||
method: 'DELETE' | |||
}) | |||
/** | |||
* 分配飞手 | |||
* @param {*} params | |||
* @returns | |||
*/ | |||
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 | |||
}) | |||
/** | |||
* 上传飞行文件 | |||
* @param {*} params | |||
* @returns | |||
*/ | |||
export const uploadFlightUrl = data => request({ | |||
url: '/task/uploadFlightUrl', | |||
method: 'PUT', | |||
data | |||
}) |
@@ -0,0 +1,249 @@ | |||
<template> | |||
<n-upload | |||
action="#" | |||
:max="data.limit" | |||
:file-list="data.fileList" | |||
:default-upload="false" | |||
class="upload" | |||
@before-upload="beforeUpload" | |||
@change="handleChange" | |||
> | |||
<n-button> | |||
<n-icon> | |||
<FileOutlined /> | |||
</n-icon> | |||
选择文件 | |||
</n-button> | |||
</n-upload> | |||
<n-button | |||
v-if="!autoUpload" | |||
type="primary" | |||
:disabled="data.fileList.length === 0" | |||
style="margin-top: 16px" | |||
class="btn" | |||
@click="handleUploadStart" | |||
> | |||
<n-icon> | |||
<UploadOutlined /> | |||
</n-icon> | |||
{{ text }} | |||
</n-button> | |||
</template> | |||
<script setup name="UploadVod"> | |||
import { FileOutlined, UploadOutlined } from '@vicons/antd' | |||
import { getAuth, refreshAuth } from '@/api/common/upload.js' | |||
import { reactive } from 'vue' | |||
const props = defineProps({ | |||
limit: { | |||
type: Number, | |||
default: 10 | |||
}, | |||
autoUpload: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
text: { | |||
type: String, | |||
default: '开始上传' | |||
} | |||
}) | |||
const emit = defineEmits(['uploadStatus']) | |||
const data = reactive({ | |||
limit: props.limit, | |||
timeout: '', // 请求超时时间 | |||
partSize: '', // 分片大小 | |||
parallel: '', // 分片数 | |||
retryCount: '', // 失败重试次数 | |||
retryDuration: '', // 失败重试间隔 | |||
region: 'cn-shanghai', // 配置项 | |||
userId: '224202346013657350', // 阿里云帐号ID | |||
uploader: null, // 上传实例 | |||
fileList: [], // 上传的文件列表 | |||
uploadFile: [], // 选中的文件列表 | |||
readyFile: {}, // 准备上传的文件列表 | |||
uploaderObject: {} // 上传的实例化对象列表 | |||
}) | |||
/** | |||
* @description: 校验文件是否合规 | |||
* @param {*} file | |||
* @return {*} | |||
*/ | |||
const beforeUpload = ({ file }) => { | |||
// if (!file.type.match('video.*')) { | |||
// $message.error('请选择正确的视频') | |||
// return false | |||
// } else { | |||
const hasSelect = data.fileList.some((item) => item.name === file.name) | |||
if (!hasSelect) { | |||
data.uploadFile.push(file.file) | |||
} else { | |||
$message.error('您已选择过该文件') | |||
return false | |||
} | |||
// } | |||
} | |||
/** | |||
* @description: 文件发生变化时,过滤不符合条件与重复的文件 | |||
* @param {Array} fileList | |||
* @return {*} | |||
*/ | |||
const handleChange = ({ fileList }) => { | |||
data.fileList = fileList | |||
/* 若未实例化过,则实例化上传对象 */ | |||
if (!data.uploader) { | |||
data.uploader = createUploader() | |||
} | |||
const status = fileList.length ? 'ready' : 'no-file' | |||
emit('uploadStatus', { status, list: data.readyFile }) | |||
handleUploadReady() // 准备上传文件 | |||
} | |||
/** | |||
* @description: 创建上传的实例化对象 | |||
* @param {*} | |||
* @return {*} | |||
*/ | |||
const createUploader = () => { | |||
const uploader = new AliyunUpload.Vod({ | |||
timeout: data.timeout || 60000, | |||
partSize: data.partSize || 1048576, | |||
parallel: data.parallel || 5, | |||
retryCount: data.retryCount || 3, | |||
retryDuration: data.retryDuration || 2, | |||
region: data.region, | |||
userId: data.userId, | |||
/* 添加文件成功 */ | |||
addFileSuccess: function(uploadInfo) { | |||
// console.log('addFileSuccess: ' + uploadInfo.file.name) | |||
}, | |||
// 开始上传 | |||
onUploadstarted: function(uploadInfo) { | |||
console.log('uploader', uploader) | |||
if (uploadInfo.videoId) { | |||
// 如果uploadInfo.videoId存在,调用刷新视频上传凭证接口 | |||
refreshAuth(uploadInfo.videoId).then(res => { | |||
uploader.setUploadAuthAndAddress(uploadInfo, res.data.uploadAuth, res.data.uploadAddress, res.data.videoId) | |||
}) | |||
} else { | |||
const params = { | |||
title: uploadInfo.file.name, | |||
fileName: uploadInfo.file.name | |||
} | |||
getAuth(params).then(res => { | |||
uploader.setUploadAuthAndAddress(uploadInfo, res.data.uploadAuth, res.data.uploadAddress, res.data.videoId) | |||
}) | |||
} | |||
}, | |||
// 文件上传成功 | |||
onUploadSucceed: function(uploadInfo) { | |||
const fileName = uploadInfo.file.name | |||
data.fileList.forEach((item) => { | |||
console.log(item) | |||
if (item.name === fileName) { | |||
data.readyFile[item.uid].url = uploadInfo.object | |||
} | |||
}) | |||
}, | |||
// 文件上传失败 | |||
onUploadFailed: function(uploadInfo, code, message) { | |||
console.log('onUploadFailed: file:' + uploadInfo.file.name + ',code:' + code + ', message:' + message) | |||
}, | |||
// 取消文件上传 | |||
onUploadCanceled: function(uploadInfo, code, message) { | |||
// console.log('Canceled file: ' + uploadInfo.file.name + ', code: ' + code + ', message:' + message) | |||
}, | |||
// 文件上传进度,单位:字节, 可以在这个函数中拿到上传进度并显示在页面上 | |||
onUploadProgress: function(uploadInfo, totalSize, progress) { | |||
// console.log("onUploadProgress:file:" + uploadInfo.file.name + ", fileSize:" + totalSize + ", percent:" + Math.ceil(progress * 100) + "%") | |||
const progressPercent = Math.ceil(progress * 100) | |||
const fileName = uploadInfo.file.name | |||
data.fileList.forEach((item) => { | |||
if (item.name === fileName) { | |||
item.status = progressPercent === 100 ? 'success' : 'uploading' | |||
item.percent = progressPercent | |||
} | |||
}) | |||
}, | |||
// 上传凭证超时 | |||
onUploadTokenExpired: function(uploadInfo) { | |||
console.log('onUploadTokenExpired', uploadInfo) | |||
refreshAuth(uploadInfo.videoId).then(res => { | |||
uploader.resumeUploadWithAuth(uploadInfo, res.data.uploadAuth, res.data.uploadAddress, res.data.videoId) | |||
}) | |||
}, | |||
// 全部文件上传结束 | |||
onUploadEnd: function(uploadInfo) { | |||
// console.log('onUploadEnd: uploaded all the files', uploadInfo) | |||
emit('uploadStatus', { status: 'success', list: data.readyFile }) | |||
} | |||
}) | |||
return uploader | |||
} | |||
/** | |||
* @description: 新建文件到实例对象中 | |||
* @param {*} | |||
* @return {*} | |||
*/ | |||
const handleUploadReady = () => { | |||
data.uploadFile.forEach((item, index) => { | |||
/* 是否已经新建过该文件 */ | |||
if (!Object.keys(data.readyFile).includes(item.uid)) { | |||
const obj = { | |||
name: item.name, | |||
url: '' | |||
} | |||
data.readyFile[item.uid] = obj | |||
data.uploader.addFile(item, null, null, null, '{"Vod":{}}') | |||
} | |||
}) | |||
if (props.autoUpload) { // 判断是否自动上传 | |||
handleUploadStart() | |||
} | |||
// handleUploadStart() | |||
} | |||
/** | |||
* @description: 开始上传文件 | |||
* @param {*} | |||
* @return {*} | |||
*/ | |||
const handleUploadStart = () => { | |||
if (data.uploader !== null) { | |||
data.uploader.startUpload() | |||
emit('uploadStatus', { status: 'uploading', list: data.readyFile }) | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.upload { | |||
list-style: none; | |||
padding: 10px 20px; | |||
} | |||
.upload .upload-item__name { | |||
margin-bottom: 5px; | |||
} | |||
.btn { | |||
width: 120px; | |||
margin-left: calc(50% - 60px); | |||
margin-top: 10px; | |||
margin-bottom: 30px; | |||
} | |||
</style> |
@@ -70,12 +70,12 @@ export default [ | |||
} | |||
}, | |||
{ | |||
path: 'equipment-manage', | |||
component: () => import('@/views/equipment-library/equipment-manage/index.vue'), | |||
path: 'mount-manage', | |||
component: () => import('@/views/equipment-library/mount-manage/index.vue'), | |||
name: 'EquipmentMount', | |||
title: '挂载设备', | |||
meta: { | |||
title: '盒子管理', | |||
title: '挂载设备', | |||
role: ['admin'] | |||
} | |||
}, |
@@ -28,3 +28,71 @@ export const MENU_STATUS = [ | |||
{ label: '在用', value: 1 }, | |||
{ label: '停用', value: 2 } | |||
] | |||
export const ROLE_TYPE = [ | |||
{ label: '管理员', value: 1 }, | |||
{ label: '飞手', value: 2 } | |||
] | |||
export const EQUIPMENT_TYPE = [ | |||
{ label: '高清相机', value: 1 }, | |||
{ label: '多光谱相机', value: 2 }, | |||
{ label: '空中喊话', value: 3 }, | |||
{ label: '大功率照明', value: 4 }, | |||
{ label: '双光热成像相机', value: 5 }, | |||
{ label: '抛投钩', value: 6 }, | |||
{ label: '抛投绳', value: 7 }, | |||
{ label: '无人机喷火枪', value: 8 }, | |||
{ label: '灭火弹抛投器', value: 9 }, | |||
{ label: '气体采集盒', value: 10 }, | |||
{ label: '无人机取水器', value: 11 }, | |||
{ label: '其他', value: 12 } | |||
] | |||
export const POWER_TYPE = [ | |||
{ label: '电动', value: 1 }, | |||
{ label: '油动', value: 2 }, | |||
{ label: '混合', value: 3 } | |||
] | |||
/** | |||
* 状态 | |||
*/ | |||
export const TASK_STATUS = [ | |||
{ | |||
label: '任务待分配', | |||
value: 5 | |||
}, | |||
{ | |||
label: '任务已分配', | |||
value: 10 | |||
}, | |||
{ | |||
label: '飞手已接单', | |||
value: 15 | |||
}, | |||
{ | |||
label: '任务飞行中', | |||
value: 20 | |||
}, | |||
{ | |||
label: '任务已完成', | |||
value: 25 | |||
} | |||
] | |||
// 拍摄方式 | |||
export const PHOTOGRAPHY_WAY = [ | |||
{ | |||
label: '普通巡检', | |||
value: '1' | |||
}, | |||
{ | |||
label: '正射影像', | |||
value: '2' | |||
}, | |||
{ | |||
label: '倾斜摄影', | |||
value: '3' | |||
} | |||
] |
@@ -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> | |||
``` |
@@ -0,0 +1,216 @@ | |||
import chartClass from 'chart-all' | |||
import * as echarts from 'echarts' | |||
export default { | |||
// 首页 - 任务数据 | |||
line1: (id, xAxis, YAxis) => { | |||
const { chart } = new chartClass(id) | |||
chart.setOption({ | |||
grid: { | |||
top: '15%', | |||
left: '5%', | |||
right: '7%', | |||
bottom: '5%', | |||
containLabel: true | |||
}, | |||
tooltip: { | |||
trigger: 'axis', | |||
axisPointer: { | |||
// 坐标轴指示器,坐标轴触发有效 | |||
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' | |||
} | |||
}, | |||
xAxis: [{ | |||
type: 'category', | |||
// boundaryGap: true, //坐标轴两边留白 | |||
data: xAxis, | |||
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: { | |||
type: 'dashed', | |||
color: 'rgba(137, 137, 137, 0.15)' | |||
} | |||
} | |||
}], | |||
series: [{ | |||
type: 'line', | |||
emphasis: { | |||
focus: 'series' | |||
}, | |||
name: '', | |||
smooth: true, // 平滑曲线 | |||
showSymbol: true, // 节点 | |||
symbolSize: 6, | |||
animationDuration: 2500, | |||
lineStyle: { | |||
width: 3 | |||
}, | |||
itemStyle: { | |||
color: 'rgba(32,204,255, 0.8)' | |||
}, | |||
areaStyle: { | |||
normal: { | |||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ | |||
{ | |||
offset: 0, | |||
color: 'rgba(32,204,255, 0.8)' | |||
}, | |||
{ | |||
offset: 1, | |||
color: 'rgba(60,212,191, 0.1)' | |||
} | |||
]) | |||
} | |||
}, | |||
data: YAxis | |||
}] | |||
}) | |||
}, | |||
// 首页 - 统计 | |||
line2: (id, xAxis, yAxis) => { | |||
const { chart } = new chartClass(id) | |||
chart.setOption({ | |||
grid: { | |||
top: '4%', | |||
left: '5%', | |||
right: '8%', | |||
bottom: '4%', | |||
containLabel: true | |||
}, | |||
legend: { | |||
show: false | |||
}, | |||
tooltip: { | |||
trigger: 'axis', | |||
axisPointer: { | |||
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' | |||
} | |||
}, | |||
yAxis: [{ | |||
type: 'category', | |||
data: xAxis, | |||
animation: true, | |||
offset: 5, | |||
axisLine: { | |||
show: false | |||
}, | |||
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 | |||
} | |||
} | |||
}], | |||
xAxis: [{ | |||
type: 'value', | |||
name: '', | |||
animation: true, | |||
axisLine: { | |||
// y轴 | |||
show: false | |||
}, | |||
axisTick: { | |||
// y轴刻度线 | |||
show: false | |||
}, | |||
splitLine: { | |||
lineStyle: { | |||
color: 'rgba(102, 102, 102, 0.1)' | |||
} | |||
} | |||
}], | |||
series: [{ | |||
barWidth: 22, | |||
type: 'bar', | |||
emphasis: { | |||
focus: 'series' | |||
}, | |||
itemStyle: { | |||
color: 'rgba(64, 134, 255, 1)', | |||
barBorderRadius: [10] | |||
}, | |||
data: yAxis | |||
}] | |||
}) | |||
} | |||
} |
@@ -13,7 +13,8 @@ export function setupInterceptor(service) { | |||
if (isWithoutToken(config)) { | |||
return config | |||
} | |||
const token = getToken() | |||
// const token = getToken() | |||
const token = 'token' | |||
if (token) { | |||
config.headers.Authorization = token | |||
return config | |||
@@ -63,4 +64,4 @@ export function setupInterceptor(service) { | |||
return Promise.reject(error) | |||
} | |||
) | |||
} | |||
} |
@@ -105,6 +105,11 @@ export function isExternal(path) { | |||
return /^(https?:|mailto:|tel:)/.test(path) | |||
} | |||
export function isPhone(phone) { | |||
const reg = /^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/ | |||
return reg.test(phone) | |||
} | |||
export const isServer = typeof window === 'undefined' | |||
export const isClient = !isServer |
@@ -0,0 +1,11 @@ | |||
import AMapLoader from '@amap/amap-jsapi-loader' | |||
export default AMapLoader.load({ | |||
'key': '709c024e3faa4a92473b9d847ca87702', | |||
'version': '2.0', | |||
'plugins': ['AMap.ToolBar', 'AMap.AutoComplete', 'AMap.PlaceSearch', 'AMap.Geocoder'], | |||
AMapUI: { | |||
version: '1.1', | |||
plugins: ['overlay/SimpleMarker'] | |||
} | |||
}) |
@@ -0,0 +1,49 @@ | |||
```js | |||
import GMap from '@/utils/map/GMap' | |||
GMap.then((AMap) => { | |||
let map = new AMap.Map('mapComp', { | |||
zoom: 13, | |||
center: [118.773319, 31.828123], | |||
}); | |||
// 根据经纬度 反查详细地址 | |||
const geocoder = new AMap.Geocoder() | |||
geocoder.getAddress([116.396574, 39.992706], function(status, result) { | |||
if (status === 'complete' && result.info === 'OK') { | |||
console.log('result', result); | |||
} | |||
}) | |||
// 添加缩放工具 | |||
const toolbar = new AMap.ToolBar(); | |||
map.addControl(toolbar); | |||
// 搜索 | |||
AMapUI.loadUI(['misc/PoiPicker'], function (PoiPicker) { | |||
const poiPicker = new PoiPicker({ | |||
input: 'pickerInput' | |||
}); | |||
poiPicker.on('poiPicked', ({ item }) => { | |||
let inp = document.getElementById('pickerInput'); | |||
inp.value = item.name; | |||
const marker = new AMap.Marker({ | |||
title: item.address | |||
}); | |||
marker.setMap(map); | |||
marker.setPosition(item.location); | |||
map.setCenter(marker.getPosition()); | |||
}) | |||
}); | |||
}).catch(e => console.log(e)) | |||
``` |
@@ -1,7 +1,10 @@ | |||
const common = { | |||
primaryColor: '#36ad6a', | |||
primaryColor: 'rgba(24, 144, 255, 1)', | |||
textColor: 'rgba(51, 51, 51, 1)', | |||
whiteColor: 'rgba(255, 255, 255, 1)' | |||
whiteColor: 'rgba(255, 255, 255, 1)', | |||
errorColor: '#ff3333', | |||
primaryColorHover: 'rgba(24, 144, 255, 0.8)', | |||
primaryColorPressed: 'rgba(24, 144, 255, 1)' | |||
} | |||
const themeOverrides = { |
@@ -0,0 +1,37 @@ | |||
<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' | |||
import { pilotTaskDate } from '@/api/dashboard/index' | |||
onMounted(() => { | |||
pilotTaskDate().then(({ data }) => { | |||
const xAxis = data.map(it => it.checkTime) | |||
const YAxis = data.map(it => it.pilotTaskSum) | |||
myChart.line1('chart01', xAxis, YAxis) | |||
}) | |||
}) | |||
</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> |
@@ -0,0 +1,97 @@ | |||
<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' | |||
import { pilotTaskTimeCount, taskStatusStatistics, pilotTaskStatistics, platformTasks } from '@/api/dashboard/index' | |||
onMounted(() => { | |||
// 飞手飞行时长 | |||
pilotTaskTimeCount().then(({ data }) => { | |||
const xAxis = data.map(it => it.flightHandName) | |||
const YAxis = data.map(it => it.flightTime) | |||
myChart.line2('chart02', xAxis, YAxis) | |||
}) | |||
// 任务状态统计 | |||
taskStatusStatistics().then(({ data }) => { | |||
const xAxis = data.map(it => it.statusType) | |||
const YAxis = data.map(it => it.pilotTaskSum) | |||
myChart.line2('chart03', xAxis, YAxis) | |||
}) | |||
// 飞手飞行任务数量 | |||
pilotTaskStatistics().then(({ data }) => { | |||
const xAxis = data.map(it => it.flightHandName) | |||
const YAxis = data.map(it => it.pilotTaskSum) | |||
myChart.line2('chart04', xAxis, YAxis) | |||
}) | |||
// 平台任务数量 | |||
platformTasks().then(({ data }) => { | |||
const xAxis = data.map(it => it.platformName) | |||
const YAxis = data.map(it => it.pilotTaskSum) | |||
myChart.line2('chart05', xAxis, YAxis) | |||
}) | |||
}) | |||
</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; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
white-space: nowrap; | |||
} | |||
#chart02, | |||
#chart03, | |||
#chart04, | |||
#chart05 { | |||
width: 100%; | |||
height: 195px; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,111 @@ | |||
<template> | |||
<div class="HeadComp"> | |||
<div v-for="(it, i) in headList" :key="'head' + 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">{{ it.value }} <span v-if="i !== headList.length -1" class="span">个</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' | |||
import { statisticsTask } from '@/api/dashboard/index' | |||
const headList = reactive([ | |||
{ | |||
name: '总条数', | |||
value: null, | |||
img: total | |||
}, | |||
{}, | |||
{ | |||
name: '待执行任务', | |||
value: null, | |||
img: task | |||
}, | |||
{}, | |||
{ | |||
name: '接入平台数量', | |||
value: null, | |||
img: platform | |||
}, | |||
{}, | |||
{ | |||
name: '服务客户数量', | |||
value: null, | |||
img: clients | |||
}, | |||
{}, | |||
{ | |||
name: '累计飞行时长', | |||
value: null, | |||
img: fly | |||
} | |||
]) | |||
statisticsTask().then(({ data }) => { | |||
headList[0].value = data.allTaskNumber | |||
headList[2].value = data.performTask | |||
headList[4].value = data.platformNumber | |||
headList[6].value = data.customerNumber | |||
headList[8].value = data.flightTimeCount | |||
}) | |||
</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; | |||
font-weight: bold; | |||
margin-right: 10px; | |||
color: rgba(0, 0, 0, 1); | |||
.span { | |||
font-weight: 400; | |||
} | |||
} | |||
} | |||
} | |||
.border { | |||
border: 1px solid rgba(207, 207, 207, 0.65); | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,74 @@ | |||
<template> | |||
<div class="TabComp"> | |||
<div class="title"> | |||
<div class="title-left">最新任务</div> | |||
<div class="title-right" @click="$router.push('/task/task-manage')">查看更多</div> | |||
</div> | |||
<n-space vertical> | |||
<n-table striped style="table-layout:fixed;"> | |||
<thead> | |||
<tr> | |||
<th class="one">序号</th> | |||
<th style="text-align: center;">任务名称</th> | |||
<th style="text-align: center;">执行时间</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<tr v-for="(it, i) in tabList" :key="'list' + it.name"> | |||
<td class="one">{{ i + 1 }}</td> | |||
<td class="hid">{{ it.taskName }}</td> | |||
<td class="hid">{{ it.taskStartTime }}</td> | |||
</tr> | |||
</tbody> | |||
</n-table> | |||
</n-space> | |||
</div> | |||
</template> | |||
<script setup name="tabComp"> | |||
import { ref } from 'vue' | |||
import { newestTask } from '@/api/dashboard/index' | |||
const tabList = ref([]) | |||
newestTask().then(({ data }) => { | |||
tabList.value = data | |||
}) | |||
</script> | |||
<style lang="scss" scoped> | |||
.TabComp { | |||
height: 500px; | |||
.title { | |||
display: flex; | |||
justify-content: space-between; | |||
align-items: center; | |||
margin-bottom: 10px; | |||
&-left { | |||
font-size: 16px; | |||
font-weight: bold; | |||
} | |||
&-right { | |||
font-size: 12px; | |||
color: rgba(36, 158, 255, 1); | |||
cursor: pointer; | |||
} | |||
} | |||
.one { | |||
width: 45px; | |||
text-align: center; | |||
} | |||
.hid { | |||
text-align: center; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
white-space: nowrap; | |||
} | |||
} | |||
</style> |
@@ -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> |
@@ -0,0 +1,134 @@ | |||
<template> | |||
<Modal | |||
:options="getModalOptions" | |||
:on-positive-click="handleConfirm" | |||
:on-negative-click="handleClose" | |||
:on-close="handleClose" | |||
> | |||
<template #Context> | |||
<n-form | |||
ref="formRef" | |||
:model="BoxForm" | |||
:rules="BoxRules" | |||
: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"> | |||
<n-input v-if="item.type === 'input'" v-model:value="BoxForm[item.key]" v-bind="item.props" /> | |||
</n-form-item> | |||
</template> | |||
</n-form> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import { form } from '../tools/form.js' | |||
import { defineComponent, ref, reactive, computed, toRefs } from 'vue' | |||
import { boxCreate, boxUpdate } from '@/api/equipment/box.js' | |||
import Modal from '@/components/Modal/index.vue' | |||
export default defineComponent({ | |||
name: 'BoxModal', | |||
components: { Modal }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => {} | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { | |||
'create': '新建盒子信息', | |||
'preview': '盒子详情', | |||
'update': '编辑盒子信息' | |||
} | |||
const { BoxForm, BoxRules } = form | |||
const formRef = ref() | |||
const data = reactive({ | |||
BoxForm: { | |||
...BoxForm, | |||
...props.data | |||
}, | |||
BoxRules: { | |||
...BoxRules | |||
}, | |||
disabled: props.type === 'preview' | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
title: MODAL_TYPE[props.type], | |||
show: props.visible, | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
const getFormOptions = computed(() => { | |||
return { | |||
...form.formItem | |||
} | |||
}) | |||
function handleConfirm() { | |||
formRef.value?.validate((errors) => { | |||
if (!errors) { | |||
const params = { ...data.BoxForm } | |||
if (params.id) { | |||
/* 编辑 */ | |||
boxUpdate(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} else { | |||
/* 新增 */ | |||
boxCreate(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} else { | |||
$message.error('请完善必填信息') | |||
} | |||
}) | |||
} | |||
/* 关闭弹窗 */ | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
return { | |||
...toRefs(data), | |||
formRef, | |||
getModalOptions, | |||
getFormOptions, | |||
handleConfirm, | |||
handleClose | |||
} | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
</style> |
@@ -1,17 +1,97 @@ | |||
<template> | |||
<div> | |||
盒子管理 | |||
<n-card> | |||
<headSearch :info="search" @search="handleSearch" @reset="handleSearch" /> | |||
<data-table | |||
ref="tableRef" | |||
:columns="columns" | |||
:request="loadDataTable" | |||
:row-key="(row) => row.id" | |||
size="large" | |||
@update:checked-row-keys="handleCheck" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
<n-popconfirm | |||
negative-text="取消" | |||
positive-text="确认" | |||
@positive-click="handleDelete" | |||
> | |||
<template #trigger> | |||
<n-button type="primary"> 删除 </n-button> | |||
</template> | |||
确认要删除选中数据吗? | |||
</n-popconfirm> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
</div> | |||
<!-- 新增、编辑弹窗 --> | |||
<BoxModal v-if="modalShow" v-model:visible="modalShow" :data="rowData" :type="modalType" @reload="handleSearch" /> | |||
</template> | |||
<script> | |||
import search from './tools/search.js' | |||
import table from './tools/table.js' | |||
import headSearch from '@/components/Search/index.vue' | |||
import dataTable from '@/components/DataTable/index.vue' | |||
import BoxModal from './components/BoxModal.vue' | |||
import { ref, unref, toRefs, reactive, onUnmounted } from 'vue' | |||
import { boxPage } from '@/api/equipment/box.js' | |||
export default { | |||
name: 'BoxManage', | |||
components: { dataTable, BoxModal, headSearch }, | |||
setup() { | |||
const data = reactive({ | |||
...toRefs(table), | |||
search | |||
}) | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
...unref(data.searchParams), | |||
...res | |||
} | |||
return await boxPage(_params) | |||
} | |||
// 新增 | |||
function handleModal() { | |||
data.rowData = null | |||
data.modalType = 'create' | |||
data.modalShow = true | |||
} | |||
const selectedIds = ref([]) | |||
function handleCheck(rowKeys) { | |||
selectedIds.value = rowKeys | |||
} | |||
function handleDelete() { | |||
if (selectedIds.value.length) { | |||
data.deleteData(selectedIds.value) | |||
} else { | |||
$message.warning('请至少选中一条数据') | |||
} | |||
} | |||
onUnmounted(() => { | |||
data.searchParams = null | |||
}) | |||
return { | |||
...toRefs(data), | |||
loadDataTable, | |||
handleModal, | |||
handleDelete, | |||
handleCheck | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-button + .n-button { | |||
margin-left: 10px; | |||
} | |||
</style> |
@@ -0,0 +1,19 @@ | |||
import { reactive } from 'vue' | |||
export const form = reactive({ | |||
BoxForm: { | |||
boxSn: null, | |||
boxName: null, | |||
streamCode: null | |||
}, | |||
BoxRules: { | |||
boxSn: [{ required: true, message: '请输入设备编号', trigger: 'blur' }], | |||
boxName: [{ required: true, message: '请输入盒子名称', type: 'string', trigger: 'blur' }], | |||
streamCode: [{ required: true, message: '请输入通道编码', trigger: 'blur' }] | |||
}, | |||
formItem: [ | |||
{ type: 'input', key: 'boxSn', label: '设备编号', props: { maxlength: '20', placeholder: '请输入设备编号', clearable: true }}, | |||
{ type: 'input', key: 'boxName', label: '盒子名称', props: { maxlength: '20', placeholder: '请输入盒子名称', clearable: true }}, | |||
{ type: 'input', key: 'streamCode', label: '通道编码', props: { maxlength: '20', placeholder: '请输入通道编码', clearable: true }} | |||
] | |||
}) |
@@ -0,0 +1,14 @@ | |||
import { reactive } from 'vue' | |||
const data = reactive([ | |||
{ | |||
label: '盒子名称', | |||
key: 'boxName', | |||
props: { | |||
placeholder: '请输入盒子名称' | |||
} | |||
} | |||
]) | |||
export default data | |||
@@ -0,0 +1,117 @@ | |||
import { h, ref, reactive } from 'vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { boxDelete } from '@/api/equipment/box.js' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
tableRef.value.reFetch({ searchParams }) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
// 删除接口 | |||
function deleteData(data) { | |||
boxDelete(data) | |||
.then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}) | |||
.catch((e) => { | |||
console.log(e) | |||
}) | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
modalType: 'create', | |||
modalShow: false, | |||
configModalShow: false, | |||
handleSearch, | |||
deleteData, | |||
columns: [ | |||
{ type: 'selection' }, | |||
{ | |||
title: '设备编号', | |||
key: 'boxSn', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '盒子名称', | |||
key: 'boxName', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '通道编码', | |||
key: 'streamCode', | |||
align: 'center', | |||
Minwidth: 160 | |||
}, | |||
{ | |||
title: '创建时间', | |||
key: 'createTime', | |||
align: 'center', | |||
Minwidth: 160 | |||
}, | |||
{ | |||
title: '创建人', | |||
key: 'createUser', | |||
align: 'center', | |||
width: 100 | |||
}, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '编辑', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '删除', | |||
type: 'popconfirm', | |||
auth: 'basic_list', | |||
tip: '确定删除这条数据吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, [row.id]) | |||
}, | |||
ButtonProps: { | |||
text: true, | |||
type: 'primary' | |||
} | |||
} | |||
], | |||
align: 'center' | |||
}) | |||
} | |||
} | |||
] | |||
}) | |||
export default data |
@@ -1,17 +0,0 @@ | |||
<template> | |||
<div> | |||
挂载设备 | |||
</div> | |||
</template> | |||
<script> | |||
export default { | |||
name: 'EquipmentManage', | |||
setup() { | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
</style> |
@@ -0,0 +1,135 @@ | |||
<template> | |||
<Modal | |||
:options="getModalOptions" | |||
:on-positive-click="handleConfirm" | |||
:on-negative-click="handleClose" | |||
:on-close="handleClose" | |||
> | |||
<template #Context> | |||
<n-form | |||
ref="formRef" | |||
:model="mountForm" | |||
:rules="mountRules" | |||
: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"> | |||
<n-input v-if="item.type === 'input'" v-model:value="mountForm[item.key]" v-bind="item.props" /> | |||
<n-select v-if="item.type === 'select'" v-model:value="mountForm[item.key]" v-bind="item.props" /> | |||
</n-form-item> | |||
</template> | |||
</n-form> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import { form } from '../tools/form.js' | |||
import { defineComponent, ref, reactive, computed, toRefs } from 'vue' | |||
import { mountCreate, mountUpdate } from '@/api/equipment/mount.js' | |||
import Modal from '@/components/Modal/index.vue' | |||
export default defineComponent({ | |||
name: 'MountModal', | |||
components: { Modal }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => {} | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { | |||
'create': '新建挂载', | |||
'preview': '挂载详情', | |||
'update': '编辑挂载信息' | |||
} | |||
const { mountForm, mountRules } = form | |||
const formRef = ref() | |||
const data = reactive({ | |||
mountForm: { | |||
...mountForm, | |||
...props.data | |||
}, | |||
mountRules: { | |||
...mountRules | |||
}, | |||
disabled: props.type === 'preview' | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
title: MODAL_TYPE[props.type], | |||
show: props.visible, | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
const getFormOptions = computed(() => { | |||
return { | |||
...form.formItem | |||
} | |||
}) | |||
function handleConfirm() { | |||
formRef.value?.validate((errors) => { | |||
if (!errors) { | |||
const params = { ...data.mountForm } | |||
if (params.id) { | |||
/* 编辑 */ | |||
mountUpdate(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} else { | |||
/* 新增 */ | |||
mountCreate(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} else { | |||
$message.error('请完善必填信息') | |||
} | |||
}) | |||
} | |||
/* 关闭弹窗 */ | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
return { | |||
...toRefs(data), | |||
formRef, | |||
getModalOptions, | |||
getFormOptions, | |||
handleConfirm, | |||
handleClose | |||
} | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
</style> |
@@ -0,0 +1,97 @@ | |||
<template> | |||
<div> | |||
<n-card> | |||
<headSearch :info="search" @search="handleSearch" @reset="handleSearch" /> | |||
<data-table | |||
ref="tableRef" | |||
:columns="columns" | |||
:request="loadDataTable" | |||
:row-key="(row) => row.id" | |||
size="large" | |||
@update:checked-row-keys="handleCheck" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
<n-popconfirm | |||
negative-text="取消" | |||
positive-text="确认" | |||
@positive-click="handleDelete" | |||
> | |||
<template #trigger> | |||
<n-button type="primary"> 删除 </n-button> | |||
</template> | |||
确认要删除选中数据吗? | |||
</n-popconfirm> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
</div> | |||
<!-- 新增、编辑弹窗 --> | |||
<MountModal v-if="modalShow" v-model:visible="modalShow" :data="rowData" :type="modalType" @reload="handleSearch" /> | |||
</template> | |||
<script> | |||
import search from './tools/search.js' | |||
import table from './tools/table.js' | |||
import headSearch from '@/components/Search/index.vue' | |||
import dataTable from '@/components/DataTable/index.vue' | |||
import MountModal from './components/MountModal.vue' | |||
import { ref, unref, toRefs, reactive, onUnmounted } from 'vue' | |||
import { mountPage } from '@/api/equipment/mount.js' | |||
export default { | |||
name: 'BoxManage', | |||
components: { dataTable, MountModal, headSearch }, | |||
setup() { | |||
const data = reactive({ | |||
...toRefs(table), | |||
search | |||
}) | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
...unref(data.searchParams), | |||
...res | |||
} | |||
return await mountPage(_params) | |||
} | |||
// 新增 | |||
function handleModal() { | |||
data.rowData = null | |||
data.modalType = 'create' | |||
data.modalShow = true | |||
} | |||
const selectedIds = ref([]) | |||
function handleCheck(rowKeys) { | |||
selectedIds.value = rowKeys | |||
} | |||
function handleDelete() { | |||
if (selectedIds.value.length) { | |||
data.deleteData(selectedIds.value) | |||
} else { | |||
$message.warning('请至少选中一条数据') | |||
} | |||
} | |||
onUnmounted(() => { | |||
data.searchParams = null | |||
}) | |||
return { | |||
...toRefs(data), | |||
loadDataTable, | |||
handleModal, | |||
handleDelete, | |||
handleCheck | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-button + .n-button { | |||
margin-left: 10px; | |||
} | |||
</style> |
@@ -0,0 +1,21 @@ | |||
import { reactive } from 'vue' | |||
import { EQUIPMENT_TYPE } from '@/utils/dictionary.js' | |||
export const form = reactive({ | |||
mountForm: { | |||
code: null, | |||
name: null, | |||
type: null | |||
}, | |||
mountRules: { | |||
boxSn: [{ required: true, message: '请输入设备编号', trigger: 'blur' }], | |||
name: [{ required: true, message: '请输入盒子名称', type: 'string', trigger: 'blur' }], | |||
type: [{ required: true, type: 'number', message: '请输入通道编码', trigger: 'blur' }] | |||
}, | |||
formItem: [ | |||
{ type: 'input', key: 'code', label: '设备编号', props: { maxlength: '20', placeholder: '请输入设备编号', clearable: true }}, | |||
{ type: 'input', key: 'name', label: '设备名称', props: { maxlength: '20', placeholder: '请输入盒子名称', clearable: true }}, | |||
{ type: 'select', key: 'type', label: '设备类型', props: { options: EQUIPMENT_TYPE, clearable: true }}, | |||
{ type: 'input', key: 'note', label: '备注', props: { type: 'textarea', autosize: { maxlength: '200', minRows: 3, maxRows: 3 }}} | |||
] | |||
}) |
@@ -0,0 +1,14 @@ | |||
import { reactive } from 'vue' | |||
const data = reactive([ | |||
{ | |||
label: '设备名称', | |||
key: 'name', | |||
props: { | |||
placeholder: '请输入设备名称' | |||
} | |||
} | |||
]) | |||
export default data | |||
@@ -0,0 +1,124 @@ | |||
import { h, ref, reactive } from 'vue' | |||
import TableTags from '@/components/DataTable/tools/Tags.vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { mountDelete } from '@/api/equipment/mount.js' | |||
import { EQUIPMENT_TYPE } from '@/utils/dictionary.js' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
tableRef.value.reFetch({ searchParams }) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
// 删除接口 | |||
function deleteData(data) { | |||
mountDelete(data) | |||
.then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}) | |||
.catch((e) => { | |||
console.log(e) | |||
}) | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
modalType: 'create', | |||
modalShow: false, | |||
configModalShow: false, | |||
handleSearch, | |||
deleteData, | |||
columns: [ | |||
{ type: 'selection' }, | |||
{ | |||
title: '设备编号', | |||
key: 'code', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '设备名称', | |||
key: 'name', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '设备类型', | |||
key: 'type', | |||
align: 'center', | |||
render(row) { | |||
return h(TableTags, { | |||
data: row.type, | |||
filters: EQUIPMENT_TYPE | |||
}) | |||
} | |||
}, | |||
{ | |||
title: '创建时间', | |||
key: 'createTime', | |||
align: 'center', | |||
Minwidth: 160 | |||
}, | |||
{ | |||
title: '创建人', | |||
key: 'createUser', | |||
align: 'center', | |||
width: 100 | |||
}, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '编辑', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '删除', | |||
type: 'popconfirm', | |||
auth: 'basic_list', | |||
tip: '确定删除这条数据吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, [row.id]) | |||
}, | |||
ButtonProps: { | |||
text: true, | |||
type: 'primary' | |||
} | |||
} | |||
], | |||
align: 'center' | |||
}) | |||
} | |||
} | |||
] | |||
}) | |||
export default data |
@@ -0,0 +1,144 @@ | |||
<template> | |||
<Modal | |||
:options="getModalOptions" | |||
:on-positive-click="handleConfirm" | |||
:on-negative-click="handleClose" | |||
:on-close="handleClose" | |||
> | |||
<template #Context> | |||
<n-form | |||
ref="formRef" | |||
:model="uvaForm" | |||
:rules="uvaRules" | |||
: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"> | |||
<n-input v-if="item.type === 'input'" v-model:value="uvaForm[item.key]" v-bind="item.props" /> | |||
<n-select v-if="item.type === 'select'" v-model:value="uvaForm[item.key]" v-bind="item.props" /> | |||
<n-input-number v-if="item.type === 'number'" v-model:value="uvaForm[item.key]" v-bind="item.props"> | |||
<template v-if="item.suffix" #suffix> | |||
{{ item.suffix }} | |||
</template> | |||
</n-input-number> | |||
</n-form-item> | |||
</template> | |||
</n-form> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import { form } from '../tools/form.js' | |||
import { defineComponent, ref, reactive, computed, toRefs } from 'vue' | |||
import { uvaCreate, uvaUpdate } from '@/api/equipment/uva.js' | |||
import Modal from '@/components/Modal/index.vue' | |||
export default defineComponent({ | |||
name: 'UvaModal', | |||
components: { Modal }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => {} | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { | |||
'create': '新建盒子信息', | |||
'preview': '盒子详情', | |||
'update': '编辑盒子信息' | |||
} | |||
const { uvaForm, uvaRules } = form | |||
const formRef = ref() | |||
const data = reactive({ | |||
uvaForm: { | |||
...uvaForm, | |||
...props.data | |||
}, | |||
uvaRules: { | |||
...uvaRules | |||
}, | |||
disabled: props.type === 'preview' | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
title: MODAL_TYPE[props.type], | |||
show: props.visible, | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
const getFormOptions = computed(() => { | |||
return { | |||
...form.formItem | |||
} | |||
}) | |||
function handleConfirm() { | |||
formRef.value?.validate((errors) => { | |||
if (!errors) { | |||
const params = { ...data.uvaForm } | |||
if (params.id) { | |||
/* 编辑 */ | |||
uvaUpdate(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} else { | |||
/* 新增 */ | |||
uvaCreate(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} else { | |||
$message.error('请完善必填信息') | |||
} | |||
}) | |||
} | |||
/* 关闭弹窗 */ | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
return { | |||
...toRefs(data), | |||
formRef, | |||
getModalOptions, | |||
getFormOptions, | |||
handleConfirm, | |||
handleClose | |||
} | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-input-number{ | |||
width: 100%; | |||
} | |||
</style> |
@@ -1,17 +1,97 @@ | |||
<template> | |||
<div> | |||
无人机管理 | |||
<n-card> | |||
<headSearch :info="search" @search="handleSearch" @reset="handleSearch" /> | |||
<data-table | |||
ref="tableRef" | |||
:columns="columns" | |||
:request="loadDataTable" | |||
:row-key="(row) => row.id" | |||
size="large" | |||
@update:checked-row-keys="handleCheck" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
<n-popconfirm | |||
negative-text="取消" | |||
positive-text="确认" | |||
@positive-click="handleDelete" | |||
> | |||
<template #trigger> | |||
<n-button type="primary"> 删除 </n-button> | |||
</template> | |||
确认要删除选中数据吗? | |||
</n-popconfirm> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
</div> | |||
<!-- 新增、编辑弹窗 --> | |||
<UVAModal v-if="modalShow" v-model:visible="modalShow" :data="rowData" :type="modalType" @reload="handleSearch" /> | |||
</template> | |||
<script> | |||
import search from './tools/search.js' | |||
import table from './tools/table.js' | |||
import headSearch from '@/components/Search/index.vue' | |||
import dataTable from '@/components/DataTable/index.vue' | |||
import UVAModal from './components/UVAModal.vue' | |||
import { ref, unref, toRefs, reactive, onUnmounted } from 'vue' | |||
import { uvaPage } from '@/api/equipment/uva.js' | |||
export default { | |||
name: 'UVAManage', | |||
name: 'BoxManage', | |||
components: { dataTable, UVAModal, headSearch }, | |||
setup() { | |||
const data = reactive({ | |||
...toRefs(table), | |||
search | |||
}) | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
...unref(data.searchParams), | |||
...res | |||
} | |||
return await uvaPage(_params) | |||
} | |||
// 新增 | |||
function handleModal() { | |||
data.rowData = null | |||
data.modalType = 'create' | |||
data.modalShow = true | |||
} | |||
const selectedIds = ref([]) | |||
function handleCheck(rowKeys) { | |||
selectedIds.value = rowKeys | |||
} | |||
function handleDelete() { | |||
if (selectedIds.value.length) { | |||
data.deleteData(selectedIds.value) | |||
} else { | |||
$message.warning('请至少选中一条数据') | |||
} | |||
} | |||
onUnmounted(() => { | |||
data.searchParams = null | |||
}) | |||
return { | |||
...toRefs(data), | |||
loadDataTable, | |||
handleModal, | |||
handleDelete, | |||
handleCheck | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-button + .n-button { | |||
margin-left: 10px; | |||
} | |||
</style> |
@@ -0,0 +1,26 @@ | |||
import { reactive } from 'vue' | |||
import { POWER_TYPE } from '@/utils/dictionary.js' | |||
export const form = reactive({ | |||
uvaForm: { | |||
code: null, | |||
name: null, | |||
powerType: null, | |||
accuracy: null, | |||
manufacturer: null | |||
}, | |||
uvaRules: { | |||
code: [{ required: true, message: '请输入设备编号', trigger: 'blur' }], | |||
name: [{ required: true, message: '请输入盒子名称', type: 'string', trigger: 'blur' }], | |||
powerType: [{ required: true, type: 'number', message: '请选择动力类型', trigger: 'blur' }], | |||
accuracy: [{ required: true, type: 'number', message: '请输入定位精度', trigger: 'blur' }], | |||
manufacturer: [{ required: true, message: '请输入生产厂家', trigger: 'blur' }] | |||
}, | |||
formItem: [ | |||
{ type: 'input', key: 'code', label: '设备编号', props: { maxlength: '20', placeholder: '请输入设备编号', clearable: true }}, | |||
{ type: 'input', key: 'name', label: '设备名称', props: { maxlength: '20', placeholder: '请输入盒子名称', clearable: true }}, | |||
{ type: 'select', key: 'powerType', label: '动力类型', props: { options: POWER_TYPE, clearable: true }}, | |||
{ type: 'number', key: 'accuracy', label: '定位精度', props: { min: 0, placeholder: '请输入通道编码', showButton: false, clearable: true }, suffix: '米' }, | |||
{ type: 'input', key: 'manufacturer', label: '生产厂家', props: { maxlength: '20', placeholder: '请输入生产厂家', clearable: true }} | |||
] | |||
}) |
@@ -0,0 +1,14 @@ | |||
import { reactive } from 'vue' | |||
const data = reactive([ | |||
{ | |||
label: '设备名称', | |||
key: 'name', | |||
props: { | |||
placeholder: '请输入设备名称' | |||
} | |||
} | |||
]) | |||
export default data | |||
@@ -0,0 +1,122 @@ | |||
import { h, ref, reactive } from 'vue' | |||
import TableTags from '@/components/DataTable/tools/Tags.vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { uvaDelete } from '@/api/equipment/uva.js' | |||
import { POWER_TYPE } from '@/utils/dictionary.js' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
tableRef.value.reFetch({ searchParams }) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
// 删除接口 | |||
function deleteData(data) { | |||
uvaDelete(data) | |||
.then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}) | |||
.catch((e) => { | |||
console.log(e) | |||
}) | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
modalType: 'create', | |||
modalShow: false, | |||
configModalShow: false, | |||
handleSearch, | |||
deleteData, | |||
columns: [ | |||
{ type: 'selection' }, | |||
{ | |||
title: '设备编号', | |||
key: 'code', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '设备名称', | |||
key: 'name', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '动力类型', | |||
key: 'powerType', | |||
align: 'center', | |||
render(row) { | |||
return h(TableTags, { | |||
data: row.powerType, | |||
filters: POWER_TYPE | |||
}) | |||
} | |||
}, | |||
{ | |||
title: '定位精度', | |||
key: 'accuracy', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '生产厂家', | |||
key: 'manufacturer', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '编辑', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '删除', | |||
type: 'popconfirm', | |||
auth: 'basic_list', | |||
tip: '确定删除这条数据吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, [row.id]) | |||
}, | |||
ButtonProps: { | |||
text: true, | |||
type: 'primary' | |||
} | |||
} | |||
], | |||
align: 'center' | |||
}) | |||
} | |||
} | |||
] | |||
}) | |||
export default data |
@@ -0,0 +1,158 @@ | |||
<template> | |||
<Modal | |||
:options="getModalOptions" | |||
:on-positive-click="handleConfirm" | |||
:on-negative-click="handleClose" | |||
:on-close="handleClose" | |||
> | |||
<template #Context> | |||
<n-form | |||
ref="formRef" | |||
:model="form" | |||
:rules="rules" | |||
:label-width="80" | |||
label-placement="left" | |||
require-mark-placement="left" | |||
> | |||
<n-form-item label="横幅图片" path="cover"> | |||
<UploadOss ref="ossRefs" :default-list="initUpload" @upload-status="handleUploadStatus" /> | |||
</n-form-item> | |||
<!-- <n-form-item label="排序" path="sort"> | |||
<n-input-number v-model:value="form.sort" clearable /> | |||
</n-form-item> --> | |||
</n-form> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import { defineComponent, ref, reactive, computed, toRefs, watch } from 'vue' | |||
import Modal from '@/components/Modal/index.vue' | |||
import UploadOss from '@/components/UploadOss/index.vue' | |||
import { bannerCreate, bannerUpdate } from '@/api/system/banner.js' | |||
export default defineComponent({ | |||
name: 'BannerModal', | |||
components: { Modal, UploadOss }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => null | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { | |||
'create': '新建横幅', | |||
'preview': '横幅详情', | |||
'update': '编辑横幅' | |||
} | |||
const data = reactive({ | |||
form: { ...props.data }, | |||
initUpload: '', | |||
rules: { | |||
cover: [{ required: true, message: '请选择横幅图片', type: 'string', trigger: 'blur' }] | |||
} | |||
}) | |||
watch(() => props.visible, (value) => { | |||
if (value) { | |||
if (props.data) { | |||
data.initUpload = props.data.cover | |||
} | |||
} else { | |||
data.initUpload = '' | |||
} | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
show: props.visible, | |||
title: MODAL_TYPE[props.type], | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
function handleUploadStatus(status) { | |||
data.form.cover = status | |||
} | |||
const formRef = ref() | |||
const ossRefs = ref() | |||
function handleConfirm() { | |||
if (props.type !== 'preview') { | |||
formRef.value?.validate((errors) => { | |||
if (!errors) { | |||
const uploads = ossRefs.value.map((item, index) => { | |||
return item.startUpload() | |||
}) | |||
Promise.all(uploads) | |||
.then(response => { | |||
const isError = response.map((item) => { | |||
return item.includes('error') | |||
}) | |||
if (!isError.includes(true)) { | |||
const imageStr = response.join() | |||
const params = { | |||
...data.form, | |||
cover: imageStr[0] | |||
} | |||
if (params.id) { | |||
/* 编辑 */ | |||
bannerUpdate(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} else { | |||
/* 新增 */ | |||
bannerCreate(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} else { | |||
$message.error('图片上传失败,请稍后重试') | |||
} | |||
}) | |||
handleClose() | |||
} else { | |||
$message.error('请完善必填信息') | |||
} | |||
}) | |||
} else { | |||
handleClose() | |||
} | |||
} | |||
/* 关闭弹窗 */ | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
return { | |||
...toRefs(data), | |||
getModalOptions, | |||
handleUploadStatus, | |||
handleConfirm, | |||
handleClose | |||
} | |||
} | |||
}) | |||
</script> |
@@ -1,14 +1,60 @@ | |||
<template> | |||
<div> | |||
移动端横幅管理 | |||
<n-card> | |||
<!-- <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" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
</div> | |||
<!-- 新增、编辑弹窗 --> | |||
<BannerModal v-if="modalShow" v-model:visible="modalShow" :type="modalType" :data="rowData" @reload="handleSearch" /> | |||
</template> | |||
<script> | |||
import { unref, toRefs, reactive } from 'vue' | |||
// import headSearch from '@/components/Search/index.vue' | |||
import table from './tool/table.js' | |||
import dataTable from '@/components/DataTable/index.vue' | |||
import BannerModal from './BannerModal.vue' | |||
import { bannerPage } from '@/api/system/banner.js' | |||
export default { | |||
name: 'BannerManage', | |||
components: { dataTable, BannerModal }, | |||
setup() { | |||
const data = reactive({ | |||
...toRefs(table) | |||
}) | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
...unref(data.searchParams), | |||
...res | |||
} | |||
return await bannerPage(_params) | |||
} | |||
// 新增 | |||
function handleModal() { | |||
data.rowData = null | |||
data.modalType = 'create' | |||
data.modalShow = true | |||
} | |||
return { | |||
...toRefs(data), | |||
loadDataTable, | |||
handleModal | |||
} | |||
} | |||
} | |||
@@ -0,0 +1,19 @@ | |||
import { reactive } from 'vue' | |||
export const search = reactive({ | |||
search: [ | |||
{ | |||
label: '用户账号', | |||
key: 'username', | |||
props: { | |||
placeholder: '请输入用户账号' | |||
} | |||
}, | |||
{ | |||
label: '用户姓名', | |||
key: 'realname', | |||
props: { | |||
placeholder: '请输入用户姓名' | |||
} | |||
} | |||
] | |||
}) |
@@ -0,0 +1,133 @@ | |||
import { h, ref, reactive } from 'vue' | |||
import TableImage from '@/components/DataTable/tools/Image.vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { bannerDelete } from '@/api/system/banner.js' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
tableRef.value.reFetch({ searchParams }) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
/** | |||
* @description: 删除用户接口 | |||
* @param {Array} ids 用户id集合 | |||
* @return {*} | |||
*/ | |||
function deleteData(ids) { | |||
bannerDelete(ids).then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
modalType: 'create', | |||
modalShow: false, | |||
handleSearch, | |||
deleteData, | |||
columns: [ | |||
{ | |||
title: '序号', | |||
key: 'key', | |||
render: (_, index) => { | |||
return `${index + 1}` | |||
}, | |||
align: 'center' | |||
}, | |||
{ | |||
title: '横幅图片', | |||
key: 'cover', | |||
align: 'center', | |||
render(row) { | |||
return h(TableImage, { | |||
images: { | |||
width: 36, | |||
height: 36, | |||
src: row.cover | |||
} | |||
}) | |||
} | |||
}, | |||
{ | |||
title: '上传人', | |||
key: 'updateUser', | |||
align: 'center', | |||
width: 160 | |||
}, | |||
{ | |||
title: '上传时间', | |||
key: 'updateTime', | |||
align: 'center', | |||
width: 160 | |||
}, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '详情', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'preview') | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '修改', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '删除', | |||
type: 'popconfirm', | |||
auth: 'basic_list', | |||
tip: '确定删除这条数据吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, [row.id]) | |||
}, | |||
ButtonProps: { | |||
text: true, | |||
type: 'error' | |||
} | |||
} | |||
], | |||
align: 'center' | |||
}) | |||
} | |||
} | |||
] | |||
}) | |||
export default data |
@@ -0,0 +1,283 @@ | |||
<template> | |||
<!-- 状态 --> | |||
<div class="head"> | |||
<n-steps size="small" :current="current" :status="'process'"> | |||
<n-step v-for="(it, i) in TASK_STATUS" :key="i + it.label" :title="it.label" /> | |||
</n-steps> | |||
</div> | |||
<!-- 基本信息 --> | |||
<div class="cont"> | |||
<n-descriptions label-placement="left" label-align="right" :column="4" title="任务基本信息"> | |||
<n-descriptions-item v-for="(it, i) in basicInfo" :key="i + it.label" :label="it.label"> | |||
{{ it.value }} | |||
</n-descriptions-item> | |||
</n-descriptions> | |||
</div> | |||
<!-- 飞行信息 - 待分配飞手:状态为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="飞行信息"> | |||
<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 v-if="current == 2 && isAdmin" class="cont"> | |||
<n-descriptions label-placement="left" label-align="right" :column="4" title="设备/影响基本信息"> | |||
<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> | |||
<UploadVod @upload-status="vodStatus" /> | |||
</div> | |||
</template> | |||
<script setup name="DrawComp"> | |||
import { ref, defineProps, reactive, defineEmits } from 'vue' | |||
import { TASK_STATUS } from '@/utils/dictionary' | |||
import { distributionPilot, pilotOrder, pilotStart, pilotEnd, uploadFlightUrl } from '@/api/task/index.js' | |||
import { form, getPilotList, getEquipment, getEquipmentMount, getCloudMount } from '../tools/drawForm' | |||
import { basic, fly, equipment, execution } from '../hook/index' | |||
import UploadVod from '@/components/UploadVod/index.vue' | |||
getPilotList() // 获取飞手列表 | |||
getEquipment() // 无人机列表 | |||
getEquipmentMount() // 挂载设备列表 | |||
getCloudMount() // 挂载盒子列表 | |||
const props = defineProps({ | |||
detail: { | |||
type: Object, | |||
required: true | |||
} | |||
}) | |||
const { detail: { data }} = props // 传来的详情数据 | |||
const current = data.pilotStatus / 5 // 当前状态 | |||
const isAdmin = Math.random() > 0.5 // 模拟是否管理员 | |||
console.log('随即模拟是否管理员:', isAdmin) | |||
const formRef = ref() // 表格refs - 分配飞手 | |||
const formRefOrder = ref() // 表格refs - 飞手接单 | |||
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 vodStatus = ({ status, list }) => { | |||
console.log(status, list) | |||
const videoUrl = [] | |||
for (var key in list) { | |||
videoUrl.push(list[key].url) | |||
} | |||
const videoList = videoUrl.join(',') | |||
console.log('videoList', videoList) | |||
return | |||
if (status === 'success') { | |||
// 视频上传 | |||
uploadFlightUrl({ | |||
id: data.id, | |||
videoUrl: videoList | |||
}).then(({ code }) => { | |||
console.log(code) | |||
}) | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.head { | |||
margin: 20px; | |||
padding: 50px 10px 50px 30px; | |||
background-color: #fff; | |||
} | |||
.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) { | |||
font-weight: bold; | |||
margin: 10px; | |||
padding: 20px 0; | |||
border-bottom: 1px solid rgba(240, 242, 245, 0.8); | |||
} | |||
:deep(.n-descriptions-table) { | |||
margin: 10px; | |||
padding: 10px; | |||
} | |||
:deep(.n-drawer-body-content-wrapper) { | |||
background-color: rgba(240, 242, 245, 1); | |||
} | |||
: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> |
@@ -0,0 +1,73 @@ | |||
<template> | |||
<div class="mapComp" id="mapComp"></div> | |||
<input id="pickerInput" type="text" placeholder="输入关键字选取地点" /> | |||
</template> | |||
<script setup name="mapComp"> | |||
import GMap from '@/utils/map/GMap' | |||
const emit = defineEmits(['mapEmit']) | |||
GMap.then((AMap) => { | |||
let map = new AMap.Map('mapComp', { | |||
zoom: 13, | |||
center: [118.773319, 31.828123], | |||
}); | |||
// 添加缩放工具 | |||
const toolbar = new AMap.ToolBar(); | |||
map.addControl(toolbar); | |||
// 搜索 | |||
AMapUI.loadUI(['misc/PoiPicker'], function (PoiPicker) { | |||
const poiPicker = new PoiPicker({ | |||
input: 'pickerInput' | |||
}); | |||
poiPicker.on('poiPicked', ({ item }) => { | |||
let inp = document.getElementById('pickerInput'); | |||
inp.value = item.name; | |||
const marker = new AMap.Marker({ | |||
title: item.address | |||
}); | |||
marker.setMap(map); | |||
marker.setPosition(item.location); | |||
map.setCenter(marker.getPosition()); | |||
emit('mapEmit', item); | |||
}) | |||
}); | |||
}).catch(e => console.log(e)) | |||
</script> | |||
<style lang="scss" scoped> | |||
.mapComp { | |||
width: 100%; | |||
height: 400px; | |||
} | |||
#pickerInput { | |||
position: absolute; | |||
z-index: 9999; | |||
top: 30px; | |||
left: 30px; | |||
width: 200px; | |||
padding: 5px 5px; | |||
} | |||
.amap_lib_placeSearch .poibox.highlight { | |||
background-color: #CAE1FF; | |||
} | |||
.amap_lib_placeSearch .poi-more { | |||
display: none !important; | |||
} | |||
</style> |
@@ -0,0 +1,325 @@ | |||
<template> | |||
<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" | |||
> | |||
<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" | |||
/> | |||
<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" /> | |||
<n-input v-if="item.type === 'inputStart'" v-model:value="userForm[item.key]" disabled v-bind="item.props"> | |||
<template #suffix> | |||
<n-button quaternary type="info" @click="showStart = true">去标记 | |||
<n-icon size="20" color="rgba(42, 130, 228, 1)"> | |||
<LocationSharp /> | |||
</n-icon> | |||
</n-button> | |||
</template> | |||
</n-input> | |||
<n-input v-if="item.type === 'inputEnd'" v-model:value="userForm[item.key]" disabled v-bind="item.props"> | |||
<template #suffix> | |||
<n-button quaternary type="info" @click="showEnd = true">去标记 | |||
<n-icon size="20" color="rgba(42, 130, 228, 1)"> | |||
<LocationSharp /> | |||
</n-icon> | |||
</n-button> | |||
</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" | |||
:is-date-disabled="isDateDisabled" | |||
/> | |||
<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> | |||
</n-space> | |||
</n-radio-group> | |||
</n-form-item> | |||
</template> | |||
</n-form> | |||
</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" /> | |||
</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" /> | |||
</n-modal> | |||
</template> | |||
<script> | |||
import { form, getPilotList, getEquipment, getEquipmentMount } from '../tools/form.js' | |||
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' | |||
import { LocationSharp } from '@vicons/ionicons5' | |||
// import AMapLoader from '@amap/amap-jsapi-loader' | |||
import MapComp from './MapComp.vue' | |||
import GMap from '@/utils/map/GMap' | |||
import dayjs from 'dayjs' | |||
export default defineComponent({ | |||
name: 'UserModal', | |||
components: { Modal, UploadOss, MapComp, LocationSharp }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => null | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { | |||
'create': '新建任务', | |||
'preview': '任务详情', | |||
'update': '编辑任务' | |||
} | |||
getPilotList() | |||
getEquipment() | |||
getEquipmentMount() | |||
const { formItem, userForm, userRules } = form | |||
const formRef = ref() | |||
const ossRefs = ref([]) | |||
const data = reactive({ | |||
userForm: { | |||
...userForm, | |||
...props.data | |||
}, | |||
userRules: { | |||
...userRules | |||
}, | |||
disabled: props.type === 'preview' | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
show: props.visible, | |||
title: MODAL_TYPE[props.type], | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
const getFormOptions = computed(() => { | |||
return { | |||
...formItem | |||
} | |||
}) | |||
function handleUploadStatus(status) { | |||
data.userForm.imageStatus = status | |||
} | |||
let geocoder = null | |||
// 地图 | |||
GMap.then((AMap) => { | |||
let Gmap = null | |||
// 坐标求地址 | |||
geocoder = new AMap.Geocoder() | |||
if (props.type === 'update') { // 打开编辑 | |||
Gmap = new AMap.Map('mapContainer', { | |||
zoom: 13, | |||
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) | |||
// 起点赋值 | |||
geocoder.getAddress([data.userForm.startLongitude, data.userForm.startLatitude], (status, result) => { | |||
if (status === 'complete' && result.info === 'OK') { | |||
data.userForm.patrolLocation = result.regeocode.formattedAddress | |||
} | |||
}) | |||
// 终点赋值 | |||
geocoder.getAddress([data.userForm.endLongitude, data.userForm.endLatitude], (status, result) => { | |||
if (status === 'complete' && result.info === 'OK') { | |||
data.userForm.endValue = result.regeocode.formattedAddress | |||
} | |||
}) | |||
} else { | |||
Gmap = new AMap.Map('mapContainer', { | |||
zoom: 13, | |||
center: [118.773319, 31.828123] | |||
}) | |||
} | |||
// 添加缩放工具 | |||
const toolbar = new AMap.ToolBar() | |||
Gmap.addControl(toolbar) | |||
}).catch(e => console.log(e)) | |||
// 地图辅助参数 | |||
const showStart = ref(false) | |||
const showEnd = ref(false) | |||
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) { | |||
if (status === 'complete' && result.info === 'OK') { | |||
startVal.value = result.regeocode.formattedAddress | |||
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) { | |||
if (status === 'complete' && result.info === 'OK') { | |||
endVal.value = result.regeocode.formattedAddress | |||
data.userForm.endLongitude = String(lng) | |||
data.userForm.endLatitude = String(lat) | |||
} | |||
}) | |||
} | |||
function handleConfirm() { | |||
formRef.value?.validate(async(errors) => { | |||
if (!errors) { | |||
const params = { | |||
...data.userForm, | |||
taskStartTime: String(data.userForm.taskStartTime) | |||
} | |||
if (params.taskCode) { | |||
/* 编辑 */ | |||
taskEdit(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} else { | |||
/* 新增 */ | |||
taskAdd(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} else { | |||
$message.error('请完善必填信息') | |||
} | |||
}) | |||
} | |||
/* 关闭弹窗 */ | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
const isDateDisabled = number => { | |||
return number < dayjs().startOf('day') | |||
} | |||
return { | |||
...toRefs(data), | |||
formRef, | |||
ossRefs, | |||
getModalOptions, | |||
getFormOptions, | |||
handleUploadStatus, | |||
handleConfirm, | |||
handleClose, | |||
showStart, | |||
showEnd, | |||
startVal, | |||
endVal, | |||
startHandle, | |||
endHandle, | |||
isDateDisabled | |||
} | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
.map { | |||
width: 100%; | |||
height: 155px; | |||
margin-bottom: 10px; | |||
} | |||
:deep(.n-form-item-blank) { | |||
width: 100%; | |||
} | |||
:deep(.__button-1jezgko-hlmmi) { | |||
padding-right: 0; | |||
} | |||
</style> |
@@ -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 | |||
} | |||
} | |||
}) | |||
} |
@@ -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 | |||
} | |||
} | |||
}) | |||
} |
@@ -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 | |||
} | |||
} | |||
}) | |||
} |
@@ -0,0 +1,41 @@ | |||
import { customRef } from 'vue' | |||
const status = ['任务待分配', '任务已分配', '飞手已接单', '任务飞行中', '任务已完成'] // 摄影方式 | |||
export default function(data) { | |||
const list = [ | |||
{ | |||
label: '分配人', | |||
value: data.distributeUserName | |||
}, | |||
{ | |||
label: '分配飞手时间', | |||
value: data.distributeTime | |||
}, | |||
{ | |||
label: '飞手', | |||
value: data.flightHandName | |||
}, | |||
{ | |||
label: '飞手接单时间', | |||
value: data.ordersTime | |||
}, | |||
{ | |||
label: '飞手接单状态', | |||
value: status[(data.pilotStatus / 5) - 1] | |||
}, | |||
{ | |||
label: '飞行开始时间', | |||
value: data.flightStartTime, | |||
current: 4 | |||
} | |||
] | |||
return customRef((track, trigger) => { | |||
return { | |||
get() { | |||
track() | |||
return list | |||
} | |||
} | |||
}) | |||
} |
@@ -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 | |||
} |
@@ -1,17 +1,110 @@ | |||
<template> | |||
<div> | |||
任务管理 | |||
<n-card> | |||
<!-- 搜索 --> | |||
<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" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
</div> | |||
<!-- 新增、编辑弹窗 --> | |||
<UserModal v-if="modalShow" v-model:visible="modalShow" :type="modalType" :data="rowData" @reload="handleSearch" /> | |||
<!-- 详情 - 抽屉 --> | |||
<n-drawer v-model:show="showDraw" :width="'calc(100vw - 210px)'" :placement="'right'" resizable> | |||
<n-drawer-content closable> | |||
<draw-comp :detail="detail" @close="showDraw = false" /> | |||
</n-drawer-content> | |||
</n-drawer> | |||
</template> | |||
<script> | |||
import { search } from './tools/search.js' | |||
import table from './tools/table.js' | |||
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 { 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 _params = { | |||
...unref(data.searchParams), | |||
...res | |||
} | |||
return await getTaskList(_params) | |||
} | |||
// 新增 | |||
function handleModal() { | |||
data.rowData = null | |||
data.modalType = 'create' | |||
data.modalShow = true | |||
} | |||
// 选择表格数据 | |||
const selectedIds = ref([]) | |||
function handleCheck(rowKeys) { | |||
selectedIds.value = rowKeys | |||
} | |||
// 批量删除 | |||
// function deleteComplex() { | |||
// if (selectedIds.value.length) { | |||
// data.deleteData(selectedIds.value) | |||
// } else { | |||
// $message.warning('请至少选中一条数据') | |||
// } | |||
// } | |||
onUnmounted(() => { | |||
data.searchParams = null | |||
}) | |||
return { | |||
...toRefs(data), | |||
loadDataTable, | |||
handleModal, | |||
selectedIds, | |||
// deleteComplex, | |||
handleCheck | |||
} | |||
}, | |||
methods: { | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-button+.n-button { | |||
margin-left: 10px; | |||
} | |||
:deep(.n-drawer-body-content-wrapper) { | |||
background-color: rgba(240, 242, 245, 0.8); | |||
} | |||
</style> |
@@ -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 | |||
})) | |||
} |
@@ -0,0 +1,85 @@ | |||
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 equipmentMountList = ref([]) | |||
// 飞手列表 | |||
const pilotList = ref() | |||
export const form = reactive({ | |||
userForm: { | |||
inspectionType: null, | |||
taskName: '', | |||
patrolLocation: '', | |||
endValue: '', | |||
taskStartTime: null, | |||
flightHandId: '', | |||
equipmentId: '', | |||
equipmentMountId: '', | |||
photographyWay: null, | |||
remark: '' | |||
}, | |||
userRules: { | |||
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' }] | |||
}, | |||
formItem: [ | |||
{ 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' }, | |||
{ type: 'inputStart', key: 'patrolLocation', label: '任务起点', props: { maxlength: '20', placeholder: '请选择任务起点', clearable: true }}, | |||
{ type: 'inputEnd', key: 'endValue', label: '任务终点', props: { maxlength: '20', placeholder: '请选择任务终点', clearable: true }}, | |||
{ type: 'datetime', key: 'taskStartTime', label: '飞行时间', props: { maxlength: '20', placeholder: '请选择飞行时间', clearable: true }}, | |||
{ 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: PHOTOGRAPHY_WAY, maxlength: '20', placeholder: '请选择拍摄方式', clearable: true }}, | |||
{ type: 'input', key: 'remark', label: '备注', props: { type: 'textarea', autosize: { maxlength: '200', minRows: 3, maxRows: 3 }}} | |||
] | |||
}) | |||
// 获取飞手列表 | |||
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 | |||
})) | |||
} | |||
@@ -0,0 +1,46 @@ | |||
import { ref, reactive, computed } from 'vue' | |||
import { TASK_STATUS } from '@/utils/dictionary' | |||
export const search = reactive({ | |||
search: [ | |||
{ | |||
label: '任务状态', | |||
type: 'select', | |||
key: 'status', | |||
props: { | |||
placeholder: '请输入任务状态', | |||
options: TASK_STATUS | |||
} | |||
}, | |||
{ | |||
label: '平台名称', | |||
key: 'platformName', | |||
props: { | |||
placeholder: '请输入平台名称' | |||
} | |||
}, | |||
{ | |||
label: '租户名称', | |||
key: 'tenantName', | |||
props: { | |||
placeholder: '请输入租户名称' | |||
} | |||
}, | |||
{ | |||
label: '选择日期', | |||
type: 'date', | |||
key: 'taskStartTime', | |||
props: { | |||
placeholder: '请选择日期' | |||
} | |||
}, | |||
{ | |||
label: '任务名称', | |||
key: 'taskName', | |||
props: { | |||
placeholder: '请选择任务名称' | |||
} | |||
} | |||
] | |||
}) | |||
@@ -0,0 +1,154 @@ | |||
import { h, ref, reactive } from 'vue' | |||
import TableImage from '@/components/DataTable/tools/Image.vue' | |||
import TableTags from '@/components/DataTable/tools/Tags.vue' | |||
import TableSwitch from '@/components/DataTable/tools/Switch.vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { resetPassword, deleteUser, setUserStatus } from '@/api/system/user/index.js' | |||
import { getTaskInfo, taskDel } from '@/api/task' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
tableRef.value.reFetch({ searchParams }) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
async function getRowData(row, type) { | |||
data.detail = await getTaskInfo(row.id) // 根据id获取详情 | |||
data.rowData = row | |||
data.modalType = type | |||
data.showDraw = true | |||
} | |||
/** | |||
* @description: 编辑 | |||
* @return {*} | |||
*/ | |||
function editHandle(row, type) { | |||
getTaskInfo(row.id).then(res => { | |||
data.rowData = res.data | |||
data.modalType = type | |||
data.modalShow = true | |||
}) | |||
} | |||
/** | |||
* @description: 删除用户接口 | |||
* @param {Array} ids 用户id集合 | |||
* @return {*} | |||
*/ | |||
function deleteData(ids) { | |||
taskDel(ids).then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
modalType: 'create', | |||
modalShow: false, | |||
showDraw: false, | |||
detail: {}, | |||
handleSearch, | |||
deleteData, | |||
columns: [ | |||
{ | |||
title: '任务编码', | |||
key: 'taskCode', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '任务名称', | |||
key: 'taskName', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '租户名称', | |||
key: 'tenantName', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '期望执行时间', | |||
key: 'taskStartTime', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '巡逻地点', | |||
key: 'patrolLocation', | |||
align: 'center', | |||
}, | |||
{ | |||
title: '任务状态', | |||
key: 'status', | |||
align: 'center', | |||
}, | |||
{ | |||
title: '飞手姓名', | |||
key: 'flightHandName', | |||
align: 'center', | |||
}, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '详情', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row) | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '编辑', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: editHandle.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '删除', | |||
type: 'popconfirm', | |||
auth: 'basic_list', | |||
tip: '确定删除这条数据吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, row.id) | |||
}, | |||
ButtonProps: { | |||
text: true, | |||
type: 'primary' | |||
} | |||
} | |||
], | |||
align: 'center' | |||
}) | |||
} | |||
} | |||
] | |||
}) | |||
export default data |
@@ -0,0 +1,140 @@ | |||
<template> | |||
<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" | |||
> | |||
<template v-for="(item,index) in getFormOptions" :key="index"> | |||
<n-form-item :label="item.label" :path="item.key"> | |||
<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" /> | |||
</n-form-item> | |||
</template> | |||
</n-form> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import { form } from '../tools/form.js' | |||
import { defineComponent, ref, reactive, computed, toRefs } from 'vue' | |||
import { userCreate, userUpdate } from '@/api/system/user.js' | |||
import Modal from '@/components/Modal/index.vue' | |||
export default defineComponent({ | |||
name: 'UserModal', | |||
components: { Modal }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => {} | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { | |||
'create': '新建盒子信息', | |||
'preview': '盒子详情', | |||
'update': '编辑盒子信息' | |||
} | |||
const { userForm, userRules } = form | |||
const formRef = ref() | |||
const data = reactive({ | |||
userForm: { | |||
...userForm, | |||
...props.data | |||
}, | |||
userRules: { | |||
...userRules | |||
}, | |||
disabled: props.type === 'preview' | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
title: MODAL_TYPE[props.type], | |||
show: props.visible, | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
const getFormOptions = computed(() => { | |||
const formOptions = [ | |||
...form.formItem | |||
] | |||
if (props.type !== 'create') { | |||
formOptions.splice(4, 1) | |||
} | |||
return formOptions | |||
}) | |||
function handleConfirm() { | |||
formRef.value?.validate((errors) => { | |||
if (!errors) { | |||
const params = { ...data.userForm } | |||
if (params.id) { | |||
/* 编辑 */ | |||
delete params['password'] | |||
userUpdate(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} else { | |||
/* 新增 */ | |||
userCreate(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} else { | |||
$message.error('请完善必填信息') | |||
} | |||
}) | |||
} | |||
/* 关闭弹窗 */ | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
return { | |||
...toRefs(data), | |||
formRef, | |||
getModalOptions, | |||
getFormOptions, | |||
handleConfirm, | |||
handleClose | |||
} | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
</style> |
@@ -1,17 +1,71 @@ | |||
<template> | |||
<div> | |||
人员管理 | |||
<n-card> | |||
<headSearch :info="search" @search="handleSearch" @reset="handleSearch" /> | |||
<data-table | |||
ref="tableRef" | |||
:columns="columns" | |||
:request="loadDataTable" | |||
:row-key="(row) => row.id" | |||
size="large" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
</div> | |||
<!-- 新增、编辑弹窗 --> | |||
<UserModal v-if="modalShow" v-model:visible="modalShow" :data="rowData" :type="modalType" @reload="handleSearch" /> | |||
</template> | |||
<script> | |||
import search from './tools/search.js' | |||
import table from './tools/table.js' | |||
import headSearch from '@/components/Search/index.vue' | |||
import dataTable from '@/components/DataTable/index.vue' | |||
import UserModal from './components/UserModal.vue' | |||
import { unref, toRefs, reactive, onUnmounted } from 'vue' | |||
import { userPage } from '@/api/system/user.js' | |||
export default { | |||
name: 'UserManage', | |||
components: { dataTable, UserModal, headSearch }, | |||
setup() { | |||
const data = reactive({ | |||
...toRefs(table), | |||
search | |||
}) | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
...unref(data.searchParams), | |||
...res | |||
} | |||
return await userPage(_params) | |||
} | |||
// 新增 | |||
function handleModal() { | |||
data.rowData = null | |||
data.modalType = 'create' | |||
data.modalShow = true | |||
} | |||
onUnmounted(() => { | |||
data.searchParams = null | |||
}) | |||
return { | |||
...toRefs(data), | |||
loadDataTable, | |||
handleModal | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-button + .n-button { | |||
margin-left: 10px; | |||
} | |||
</style> |
@@ -0,0 +1,30 @@ | |||
import { reactive } from 'vue' | |||
import { ROLE_TYPE } from '@/utils/dictionary.js' | |||
import { isPhone } from '@/utils/is.js' | |||
export const form = reactive({ | |||
userForm: { | |||
realname: null, | |||
mobile: null, | |||
type: null, | |||
username: null, | |||
password: null | |||
}, | |||
userRules: { | |||
realname: [{ required: true, message: '请输入姓名', trigger: 'blur' }], | |||
mobile: [ | |||
// { required: true, message: '请输入联系电话', type: 'string', trigger: 'blur' }, | |||
{ required: true, message: '请输入正确的联系电话', pattern: /^(0|86|17951)?(13[0-9]|15[012356789]|166|17[3678]|18[0-9]|14[57])[0-9]{8}$/, trigger: 'blur' } | |||
], | |||
type: [{ required: true, type: 'number', message: '请选择身份', trigger: 'blur' }], | |||
username: [{ required: true, message: '请输入帐号', trigger: 'blur' }], | |||
password: [{ required: true, message: '请输入初始密码', trigger: 'blur' }] | |||
}, | |||
formItem: [ | |||
{ type: 'input', key: 'realname', label: '姓名', props: { maxlength: '20', placeholder: '请输入姓名', clearable: true }}, | |||
{ type: 'input', key: 'mobile', label: '联系电话', props: { maxlength: '20', placeholder: '请输入联系电话', clearable: true }}, | |||
{ type: 'select', key: 'type', label: '身份选择', props: { options: ROLE_TYPE, clearable: true }}, | |||
{ type: 'input', key: 'username', label: '帐号', props: { maxlength: '20', placeholder: '请输入帐号', clearable: true }}, | |||
{ type: 'input', key: 'password', label: '初始密码', props: { maxlength: '20', placeholder: '请输入初始密码', clearable: true }} | |||
] | |||
}) |
@@ -0,0 +1,14 @@ | |||
import { reactive } from 'vue' | |||
const data = reactive([ | |||
{ | |||
label: '设备名称', | |||
key: 'name', | |||
props: { | |||
placeholder: '请输入设备名称' | |||
} | |||
} | |||
]) | |||
export default data | |||
@@ -0,0 +1,132 @@ | |||
import { h, ref, reactive } from 'vue' | |||
import TableTags from '@/components/DataTable/tools/Tags.vue' | |||
import TableSwitch from '@/components/DataTable/tools/Switch.vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { ROLE_TYPE } from '@/utils/dictionary.js' | |||
import { userUpdate } from '@/api/system/user.js' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
tableRef.value.reFetch({ searchParams }) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
// 设置状态 | |||
function changeStatus(row) { | |||
userUpdate({ id: row.data.id, status: row.value }).then(res => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
modalType: 'create', | |||
modalShow: false, | |||
configModalShow: false, | |||
handleSearch, | |||
columns: [ | |||
{ | |||
title: '序号', | |||
key: 'key', | |||
render: (_, index) => { | |||
return `${index + 1}` | |||
}, | |||
align: 'center', | |||
width: 100 | |||
}, | |||
{ | |||
title: '角色', | |||
key: 'type', | |||
align: 'center', | |||
render(row) { | |||
return h(TableTags, { | |||
data: row.type, | |||
filters: ROLE_TYPE | |||
}) | |||
} | |||
}, | |||
{ | |||
title: '姓名', | |||
key: 'realname', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '手机号', | |||
key: 'mobile', | |||
align: 'center', | |||
Minwidth: 160 | |||
}, | |||
{ | |||
title: '任务次数', | |||
key: 'taskCount', | |||
align: 'center', | |||
Minwidth: 160 | |||
}, | |||
{ | |||
title: '上次登录时间', | |||
key: 'loginTime', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '状态', | |||
key: 'status', | |||
align: 'center', | |||
width: 100, | |||
render(row) { | |||
return h(TableSwitch, { | |||
data: { id: row.id, status: row.status }, | |||
rowKey: 'status', | |||
checkedValue: 1, | |||
uncheckedValue: 2, | |||
onChange: changeStatus.bind(row) | |||
}) | |||
} | |||
}, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '编辑', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
} | |||
], | |||
align: 'center' | |||
}) | |||
} | |||
} | |||
] | |||
}) | |||
export default data |