吉安感知网项目-前端
chenyao
2026-01-17 40c2702c3da24d9d9fda6bd63894c3206779f812
Merge remote-tracking branch 'origin/master'
160 files modified
2 files renamed
1 files copied
80 files added
87 files deleted
58299 ■■■■ changed files
AGENTS.md 2 ●●● patch | view | raw | blame | history
applications/drone-command/env/.env.development 2 ●●● patch | view | raw | blame | history
applications/drone-command/package.json 12 ●●●● patch | view | raw | blame | history
applications/drone-command/public/fonts/PangMen.TTF patch | view | raw | blame | history
applications/drone-command/public/fonts/font.css 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/api/dataCockpit/index.js 9 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/assets/geojson/ja.geojson 1 ●●●● patch | view | raw | blame | history
applications/drone-command/src/assets/images/basicManage/chartBg.svg 132 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/assets/images/basicManage/chartBg2.svg 132 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/device-fz.svg 66 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/device-wrj.svg 49 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/device-zc.svg 76 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/device-zhd.svg 63 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/legend/command-post.png patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/legend/drone.png patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/legend/equipment.png patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/map/command-post.png patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/map/equipment.png patch | view | raw | blame | history
applications/drone-command/src/assets/images/dataCockpit/title-bg.png patch | view | raw | blame | history
applications/drone-command/src/assets/images/defaultava.png patch | view | raw | blame | history
applications/drone-command/src/components/PlaybackVideo/PlaybackVideo.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/components/PlaybackVideo/components/MapContainer.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/components/commonDialog.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/common-cesium-map.vue 135 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/device-map-container.vue 189 ●●●● patch | view | raw | blame | history
applications/drone-command/src/components/map-container/mapContainer.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/page/index/top/index.vue 25 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/page/index/top/top-search.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/store/modules/user.js 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/styles/common.scss 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/styles/common/cockpit.scss 198 ●●●● patch | view | raw | blame | history
applications/drone-command/src/styles/element-ui.scss 36 ●●●● patch | view | raw | blame | history
applications/drone-command/src/utils/cesium/adminBoundary.js 88 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/utils/cesium/publicCesium.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/aICrudTemplate/aICrudTemplateApi.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/aICrudTemplate/index.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/algorithmMange/algorithmMange.vue 16 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/api.txt 343 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/areaStatistics/FormDiaLog.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/areaStatistics/index.vue 18 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/defenseZone/FormDiaLog.vue 56 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/defenseZone/defenseZoneApi.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/defenseZone/index.vue 22 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue 70 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/partition/index.vue 18 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/partition/partitionApi.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue 40 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/precinctInfo/index.vue 22 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/precinctInfo/precinctInfoApi.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue 59 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneConfig/index.vue 30 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneConfig/sceneConfigApi.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/authority/role.vue 883 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceScrap/FormDiaLog.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceScrap/index.vue 100 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/DeviceChart1.vue 263 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/DeviceChart2.vue 156 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/DeviceChart3.vue 145 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/DeviceScrapDiaLog.vue 10 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/DeviceTrackDiaLog.vue 12 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/FormDiaLog.vue 38 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js 27 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/fwDeviceScrapApi.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/fwDeviceTrackApi.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/deviceStock/index.vue 105 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/maintainRecord/FormDiaLog.vue 18 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/maintainRecord/MaintenanceDiaLog.vue 32 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/maintainRecord/fwDeviceMaintainRecord.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/maintainRecord/index.vue 34 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/basicManage/maintainRecord/maintainRecordApi.js 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCenter/components/dataCenterMap.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCenter/dataCenter.vue 6 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/CenterContainer.vue 17 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue 91 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/HistoryWarning.vue 8 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue 8 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/LegendBar.vue 108 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/RealWarning.vue 22 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/components/RightContainer.vue 10 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/dataCockpit/index.vue 38 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/FormDiaLog.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/countermeasureEvaluationApi.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/index.vue 24 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue 226 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue 67 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/FormDiaLog.vue 42 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue 103 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/FormDiaLog.vue 30 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/index.vue 49 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/taskScheduleApi.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/device/airport.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/device/components/firmwareManage.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/DeviceJobDetails.vue 6 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/DeviceJobDetailsMap.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/JobRelatedEvents.vue 6 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/TaskAlgorithmBusiness.vue 6 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/TaskIntermediateContent/AddTask.vue 12 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskTable.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/job/components/executeNestDetails.vue 6 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/layerManagement/components/folderFile.vue 6 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/layerManagement/components/leftList.vue 22 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/layerManagement/components/rightEdit.vue 10 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/layerManagement/index.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/license/license.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/monitor/log/flightLog.vue 14 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionDept.vue 8 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionDept/FormDiaLog.vue 305 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionDept/index.vue 224 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionRole.vue 8 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionRole/FormDiaLog.vue 153 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionRole/RolePermissionDialog.vue 170 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionRole/index.vue 189 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionUser.vue 8 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionUser/FormDiaLog.vue 421 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/permissionManage/permissionUser/index.vue 202 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/alarmRecords/fwDroneAlarmRecord.js 5 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/alarmRecords/index.vue 41 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/historyTracks/TrajectoryDiaLog.vue 50 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/historyTracks/flyTrajectory.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/historyTracks/fwDroneFlightRecord.js 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/historyTracks/index.vue 24 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/recordManage/index.vue 12 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/resource/components/spotDetails.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/resource/patchManagement.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/system/menu.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/system/param.vue 4 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/system/userinfo.vue 16 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/tickets/component/AddEditDetails.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/tickets/orderLog.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/tickets/orderLogCopy.vue 10 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/tickets/ticket-copy.vue 8 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/tickets/ticket.vue 10 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/tickets/ticketComponent/RecheckDialog.vue 2 ●●● patch | view | raw | blame | history
applications/mobile-web-view/package.json 1 ●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue 63 ●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/hooks/useAreaBoundary.js 2 ●●● patch | view | raw | blame | history
applications/task-work-order/env/.env.development 12 ●●●●● patch | view | raw | blame | history
applications/task-work-order/package.json 1 ●●●● patch | view | raw | blame | history
applications/task-work-order/public/fonts/PangMen.TTF patch | view | raw | blame | history
applications/task-work-order/public/fonts/font.css 8 ●●●●● patch | view | raw | blame | history
applications/task-work-order/public/img/bg/mainLogo.png patch | view | raw | blame | history
applications/task-work-order/src/api/dataCockpit/index.js 54 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/assets/images/aiNowFly/Frame.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/aiNowFly/fly.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/aiNowFly/flying.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/aiNowFly/full-charge.svg 13 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/assets/images/aiNowFly/low-battery.svg 6 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/assets/images/aiNowFly/medium-battery.svg 6 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/assets/images/aiNowFly/starting.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/dataCockpit/equipment.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/dataCockpit/fzsb.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/dataCockpit/layerControl.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/dataCockpit/left-btn.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/dataCockpit/map/equipment.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/dataCockpit/right-btn.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/dataCockpit/title-bg.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/dataCockpit/warning.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/dataCockpit/zcsb.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/no-data-bg.png patch | view | raw | blame | history
applications/task-work-order/src/assets/images/pageLayout/collapse.png patch | view | raw | blame | history
applications/task-work-order/src/components/PlaybackVideo/PlaybackVideo.vue 2 ●●● patch | view | raw | blame | history
applications/task-work-order/src/components/PlaybackVideo/components/MapContainer.vue 2 ●●● patch | view | raw | blame | history
applications/task-work-order/src/components/basic-container/main.vue 25 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/components/commonDialog.vue 2 ●●● patch | view | raw | blame | history
applications/task-work-order/src/components/map-container/device-map-container.vue 437 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/components/map-container/mapContainer-copy.vue 10 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/components/map-container/mapContainer.vue 4 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/config/website.js 20 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/page/index/index.vue 112 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/page/index/logo.vue 27 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/page/index/sidebar/index.vue 19 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/page/index/tags.vue 17 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/page/index/top/index.vue 61 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/page/index/top/top-search.vue 4 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/router/views/index.js 11 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/store/modules/user.js 4 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/styles/common.scss 91 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/styles/common/cockpit.scss 1118 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/styles/element-ui.scss 839 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/styles/sidebar.scss 75 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/styles/tags.scss 58 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/styles/top.scss 20 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/DrawPolygon.js 301 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/Material/index.js 18 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/common.js 8 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/compressImage.js 55 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/compressImage2.js 46 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/createRouteLine.js 923 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/eventBus.js 5 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/frustum/CesiumVideoFrustum.js 712 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/frustum/CoordinateTranslate.js 229 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/kmz.js 10 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/mapUtil.js 207 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/publicCesium.js 168 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/use-kmz-tsa.js 675 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/utils/cesium/useBoundary.js 210 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/RoutePlan/PlanarAirLine/planarRouteUtils.js 1018 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/RoutePlan/PointAirLine/pointWayLineUtils.js 613 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/algorithmMange/algorithmMange.vue 322 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/algorithmRepository/algorithmRepository.vue 381 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/authority/apiscope.vue 645 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/authority/datascope.vue 737 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/authority/role.vue 440 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/base/region.vue 457 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/basicManage/maintainRecord/FormDiaLog.vue 289 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/basicManage/maintainRecord/MaintenanceDiaLog.vue 177 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/basicManage/maintainRecord/fwDeviceMaintainRecord.js 19 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/basicManage/maintainRecord/index.vue 236 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/basicManage/maintainRecord/maintainRecordApi.js 46 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCenter/components/dataCenterMap.vue 341 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCenter/components/searchData.vue 499 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCenter/dataCenter.vue 1225 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/components/CenterContainer.vue 237 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/components/DeviceHistoryDialog.vue 208 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/components/EmptyState.vue 59 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/components/EquipmentWarning.vue 101 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/components/HistoryWarning.vue 123 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/components/LeftContainer.vue 219 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/components/RealWarning.vue 109 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/components/RightContainer.vue 288 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/components/templateComponents/EquipmentTemplate.vue 171 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/components/templateComponents/HistoryTemplate.vue 149 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue 213 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealTemplate.vue 245 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/components/templateComponents/TitleTemplate.vue 43 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/dataCockpit/index.vue 96 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/desk/notice.vue 307 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/device/addDevice.vue 688 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/device/airport.vue 1687 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/device/components/DeviceSettingBox.vue 433 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/device/components/DeviceSettingPopover.vue 99 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/device/components/DockControlPanel.vue 552 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/device/components/devicePerShare.vue 301 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/device/components/firmwareManage.vue 444 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/device/components/use-device-setting.js 72 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/device/components/use-dock-control.js 70 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/device/devicePerMenu.vue 307 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/device/fly.vue 537 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/device/index.vue 97 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/flow/deploy.vue 160 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/flow/follow.vue 199 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/flow/manager.vue 369 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/flow/model.vue 548 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/CancelTaskDialog.vue 152 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/DeviceJobDetails.vue 806 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/DeviceJobDetailsMap.vue 364 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/FileTypePlay.vue 158 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/JobRelatedEvents.vue 392 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/SearchBox.vue 590 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/TaskAlgorithmBusiness.vue 131 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/TaskIntermediateContent/AddTask.vue 414 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue 382 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/TaskIntermediateContent/TaskTable.vue 252 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/TaskTop/TaskEvent.vue 155 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/TaskTop/TaskIndustry.vue 117 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/TaskTop/TaskTime.vue 161 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/TaskTop/TaskTop.vue 47 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/TaskTop/TaskTotal.vue 157 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/components/executeNestDetails.vue 225 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/const/drc.js 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/jobinfo.vue 269 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/jobserver.vue 213 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/job/jobstatistics.vue 14 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/layerManagement/components/folderFile.vue 316 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/layerManagement/components/leftList.vue 588 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/layerManagement/components/nationalSpatialPlanning.vue 49 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/layerManagement/components/rightEdit.vue 471 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/layerManagement/index.vue 602 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/license/license.vue 360 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/license/upload.vue 112 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/monitor/log/api.vue 218 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/monitor/log/error.vue 173 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/monitor/log/flightLog.vue 529 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/monitor/log/usual.vue 164 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/odm/task.vue 492 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/appInnovation/index.vue 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/index.vue 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/evaluate/index.vue 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/index.vue 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/clueEvents/index.vue 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionReport/index.vue 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/operatingIncome/index.vue 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/orderView/orderManage/orderManage/index.vue 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/report/reportlist.vue 199 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/resource/components/spotDetails.vue 6 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/resource/patchManagement.vue 2 ●●● patch | view | raw | blame | history
applications/task-work-order/src/views/system/menu.vue 4 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/system/param.vue 4 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/system/user/FormDiaLog.vue 451 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/system/user/index.vue 504 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/system/userinfo.vue 16 ●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/test.vue 9 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/tickets/component/AddEditDetails.vue 622 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/tickets/component/SearchBox.vue 313 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/tickets/orderLog.vue 528 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/tickets/orderLogCopy.vue 1666 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/tickets/ticket-copy.vue 4072 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/tickets/ticket.vue 2358 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/tickets/ticketComponent/CreateTicketDialog.vue 713 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/tickets/ticketComponent/DispatchDialog.vue 244 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/tickets/ticketComponent/RecheckDialog.vue 117 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/tickets/ticketComponent/TicketDetailDialog.vue 1441 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/tool/code.vue 659 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/tool/datasource.vue 375 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/tool/model.vue 393 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/wel/components/backlog.vue 434 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/wel/components/calendarBox.vue 398 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/wel/components/flightStatistics.vue 524 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/wel/components/flyratio.vue 357 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/wel/components/proportionStatic.vue 395 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/wel/components/statistics.vue 498 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/wel/components/taskOutcome.vue 315 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/wel/dashboard.vue 196 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/wel/index.vue 212 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/views/workspace/index.vue 303 ●●●●● patch | view | raw | blame | history
detect_duplicates.js 174 ●●●●● patch | view | raw | blame | history
packages/utils/date/index.js 12 ●●●●● patch | view | raw | blame | history
pnpm-lock.yaml 84 ●●●●● patch | view | raw | blame | history
uniapps/work-app/README.md 2 ●●● patch | view | raw | blame | history
uniapps/work-app/package.json 2 ●●● patch | view | raw | blame | history
uniapps/work-app/src/hooks/useGlobalWS.js 102 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/manifest.json 2 ●●● patch | view | raw | blame | history
uniapps/work-app/src/pages.json 9 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/pages/login/index.vue 53 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/userDetail/infos/index.vue 42 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/userDetail/password/index.vue 46 ●●●● patch | view | raw | blame | history
uniapps/work-app/src/subPackages/workDetail/index.vue 366 ●●●●● patch | view | raw | blame | history
uniapps/work-app/src/utils/websocket.js 6 ●●●●● patch | view | raw | blame | history
AGENTS.md
@@ -21,7 +21,7 @@
- 使用 Prettier 统一格式化(`.prettierrc.json`)。
- 使用 Tab 缩进(`tabWidth: 2`, `useTabs: true`),`semi: false`,`singleQuote: true`。
- SCSS 使用 `tabWidth: 4` 且双引号。
- 文件与目录保持 kebab-case,遵循现有应用与包的命名风格。
- 文件与目录保持 kebabCase,遵循现有应用与包的命名风格。
## 测试指南
- 当前未配置测试框架(`pnpm test` 会直接退出)。
applications/drone-command/env/.env.development
@@ -2,7 +2,7 @@
 # @Author       : yuan
 # @Date         : 2026-01-07 14:58:30
 # @LastEditors  : yuan
 # @LastEditTime : 2026-01-15 10:25:10
 # @LastEditTime : 2026-01-17 10:15:51
 # @FilePath     : \applications\drone-command\env\.env.development
 # @Description  : 
 # Copyright 2026 OBKoro1, All Rights Reserved. 
applications/drone-command/package.json
@@ -11,11 +11,6 @@
  },
  "dependencies": {
    "@amap/amap-jsapi-loader": "catalog:",
    "@ztzf/apis": "workspace:*",
    "@ztzf/components": "workspace:*",
    "@ztzf/constants": "workspace:*",
    "@ztzf/hooks": "workspace:*",
    "@ztzf/utils": "workspace:*",
    "@dvgis/dc-sdk": "catalog:",
    "@element-plus/icons-vue": "catalog:",
    "@saber/nf-design-base-elp": "catalog:",
@@ -23,6 +18,11 @@
    "@saber/nf-form-elp": "catalog:",
    "@smallwei/avue": "catalog:",
    "@turf/turf": "catalog:",
    "@ztzf/apis": "workspace:*",
    "@ztzf/components": "workspace:*",
    "@ztzf/constants": "workspace:*",
    "@ztzf/hooks": "workspace:*",
    "@ztzf/utils": "workspace:*",
    "animate.css": "catalog:",
    "avue-plugin-ueditor": "catalog:",
    "axios": "catalog:",
@@ -33,6 +33,7 @@
    "decimal.js": "catalog:",
    "disable-devtool": "catalog:",
    "echarts": "catalog:",
    "echarts-gl": "^2.0.9",
    "element-plus": "catalog:",
    "eventemitter3": "catalog:",
    "highlight.js": "catalog:",
@@ -72,7 +73,6 @@
    "@vitejs/plugin-vue": "catalog:",
    "@vue/compiler-sfc": "catalog:",
    "postcss-pxtorem": "catalog:",
    "prettier": "catalog:",
    "sass": "catalog:",
    "terser": "catalog:",
    "unplugin-auto-import": "catalog:",
applications/drone-command/public/fonts/PangMen.TTF
Binary files differ
applications/drone-command/public/fonts/font.css
@@ -46,7 +46,7 @@
@font-face {
  font-family: "PangMen";
  src: url("PANGMENZHENGDAOBIAOTITIMIANFEIBAN-2.TTF") format("truetype");
  src: url("PangMen.TTF") format("truetype");
  font-weight: normal;
  font-style: normal;
  font-display: swap;
applications/drone-command/src/api/dataCockpit/index.js
@@ -52,3 +52,12 @@
        method: 'get'
    })
}
// 区域列表
export const areaDivideListApi = (params) => {
    return request({
        url: '/drone-fw/area/fwAreaDivide/list',
        method: 'get',
        params
    })
}
applications/drone-command/src/assets/geojson/ja.geojson
New file
@@ -0,0 +1 @@
{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"adcode":360802,"name":"吉州区","center":[114.987331,27.112367],"centroid":[114.940577,27.150739],"childrenNum":0,"level":"district","parent":{"adcode":360800},"subFeatureIndex":0,"acroutes":[100000,360000,360800]},"geometry":{"type":"MultiPolygon","coordinates":[[[[115.068571,27.160774],[115.078507,27.16655],[115.091301,27.175422],[115.099557,27.177109],[115.108801,27.181853],[115.11483,27.189672],[115.11107,27.193204],[115.110839,27.202214],[115.101007,27.203801],[115.093675,27.200666],[115.093696,27.203603],[115.086133,27.20767],[115.081322,27.20519],[115.083339,27.202035],[115.079033,27.200011],[115.078024,27.203384],[115.07147,27.20271],[115.064894,27.204972],[115.05876,27.214754],[115.049643,27.213583],[115.043865,27.215785],[115.040735,27.222829],[115.04458,27.22634],[115.039496,27.229971],[115.03855,27.237886],[115.034223,27.242726],[115.034706,27.249033],[115.028845,27.25066],[115.025757,27.256094],[115.022941,27.266388],[115.024454,27.27194],[115.021975,27.279892],[115.022983,27.284195],[115.016975,27.28937],[115.011639,27.29056],[115.00185,27.287962],[114.994224,27.282529],[114.990568,27.27777],[114.980484,27.273685],[114.983951,27.26254],[114.98143,27.251076],[114.980947,27.24338],[114.973951,27.243182],[114.969035,27.248994],[114.961997,27.240484],[114.958195,27.237906],[114.949183,27.236338],[114.941095,27.231558],[114.933826,27.222849],[114.932628,27.214873],[114.92141,27.208186],[114.896369,27.204436],[114.889667,27.206321],[114.87763,27.202055],[114.867924,27.202154],[114.860592,27.205924],[114.854143,27.205111],[114.847714,27.202114],[114.83158,27.188382],[114.829606,27.178955],[114.807253,27.166788],[114.803661,27.162461],[114.802842,27.153587],[114.806665,27.146381],[114.804879,27.139909],[114.80656,27.133734],[114.809711,27.13018],[114.810698,27.119398],[114.817064,27.110282],[114.817589,27.102934],[114.820551,27.099737],[114.833576,27.092467],[114.836538,27.082655],[114.839668,27.077331],[114.839038,27.067139],[114.826475,27.061596],[114.822211,27.053589],[114.820614,27.044827],[114.821602,27.039282],[114.834942,27.035507],[114.843198,27.035606],[114.85534,27.031393],[114.86721,27.036003],[114.875697,27.037434],[114.874941,27.044032],[114.877987,27.04737],[114.886033,27.047946],[114.891915,27.051026],[114.902713,27.052437],[114.908259,27.057484],[114.913259,27.065053],[114.924792,27.061239],[114.935296,27.060583],[114.943279,27.064398],[114.950926,27.065192],[114.958195,27.059252],[114.95767,27.05341],[114.952082,27.051841],[114.953321,27.044827],[114.959372,27.0365],[114.9633,27.035984],[114.970086,27.040375],[114.978552,27.0519],[114.985463,27.05657],[114.993152,27.058219],[114.991514,27.076198],[114.989203,27.088256],[114.990883,27.097929],[114.995568,27.106549],[115.000925,27.112109],[115.009223,27.116697],[115.025693,27.140643],[115.030798,27.144376],[115.045252,27.148009],[115.051428,27.152118],[115.068571,27.160774]]]]}},{"type":"Feature","properties":{"adcode":360803,"name":"青原区","center":[115.016306,27.105879],"centroid":[115.224044,26.894879],"childrenNum":0,"level":"district","parent":{"adcode":360800},"subFeatureIndex":1,"acroutes":[100000,360000,360800]},"geometry":{"type":"MultiPolygon","coordinates":[[[[115.068571,27.160774],[115.051428,27.152118],[115.045252,27.148009],[115.030798,27.144376],[115.025693,27.140643],[115.009223,27.116697],[115.000925,27.112109],[114.995568,27.106549],[114.990883,27.097929],[114.989203,27.088256],[114.991514,27.076198],[114.993152,27.058219],[115.003782,27.04431],[115.012395,27.036719],[115.018908,27.033003],[115.038739,27.027597],[115.04647,27.024238],[115.057794,27.014857],[115.060882,27.010802],[115.064096,26.996768],[115.053571,26.987981],[115.042248,26.968953],[115.036765,26.965811],[115.047038,26.961198],[115.050693,26.953005],[115.05563,26.95028],[115.060651,26.941072],[115.075356,26.937392],[115.083885,26.928581],[115.085776,26.920246],[115.094726,26.920186],[115.094221,26.914337],[115.091196,26.91008],[115.093654,26.902758],[115.091007,26.895615],[115.085083,26.89086],[115.085335,26.88489],[115.090314,26.880851],[115.096049,26.870522],[115.101511,26.863477],[115.103255,26.853266],[115.112057,26.850141],[115.127813,26.852629],[115.132729,26.850101],[115.138128,26.841203],[115.138926,26.836206],[115.14985,26.834594],[115.156678,26.831806],[115.1613,26.834434],[115.163148,26.830353],[115.173337,26.827526],[115.183001,26.82655],[115.187938,26.821434],[115.197832,26.816316],[115.203525,26.815759],[115.208294,26.805205],[115.208714,26.799251],[115.213819,26.789294],[115.219828,26.784036],[115.237831,26.780052],[115.262684,26.766129],[115.276339,26.757822],[115.286822,26.752861],[115.294175,26.740887],[115.304279,26.736265],[115.301695,26.727836],[115.302536,26.722475],[115.307452,26.715321],[115.317115,26.71291],[115.321401,26.70434],[115.319048,26.701709],[115.320729,26.696287],[115.324699,26.692958],[115.323481,26.690547],[115.328901,26.688613],[115.332955,26.679961],[115.33493,26.672266],[115.336317,26.673801],[115.347325,26.673542],[115.350665,26.670751],[115.350455,26.666684],[115.357808,26.661919],[115.357493,26.656256],[115.363816,26.656396],[115.368543,26.652687],[115.375454,26.655279],[115.390664,26.655997],[115.406294,26.6616],[115.41079,26.659067],[115.418289,26.663255],[115.427827,26.671289],[115.426693,26.675277],[115.438772,26.683829],[115.449507,26.687816],[115.455431,26.684447],[115.461104,26.678885],[115.462721,26.672525],[115.477406,26.671628],[115.475893,26.675137],[115.488645,26.681835],[115.487237,26.68955],[115.495388,26.698799],[115.497279,26.704439],[115.502363,26.71004],[115.508749,26.710398],[115.511396,26.713946],[115.50564,26.724548],[115.499527,26.729689],[115.499128,26.734551],[115.493855,26.739712],[115.492006,26.744792],[115.486544,26.749096],[115.474822,26.750729],[115.469927,26.753837],[115.472028,26.75824],[115.46957,26.760153],[115.452742,26.759694],[115.449507,26.769097],[115.444549,26.774156],[115.433835,26.778698],[115.432407,26.787322],[115.429403,26.795049],[115.424109,26.799689],[115.41268,26.795049],[115.401441,26.795268],[115.39745,26.791863],[115.390727,26.793894],[115.390454,26.802178],[115.38203,26.809049],[115.379761,26.815122],[115.3885,26.820418],[115.383752,26.829019],[115.386273,26.834972],[115.377135,26.834534],[115.3664,26.835589],[115.354614,26.827088],[115.344867,26.834952],[115.342262,26.839292],[115.345686,26.845204],[115.345644,26.851793],[115.341232,26.86073],[115.332955,26.868791],[115.324783,26.865387],[115.317493,26.864929],[115.313502,26.861407],[115.291423,26.853266],[115.285813,26.85663],[115.281423,26.863855],[115.272936,26.869169],[115.266444,26.869368],[115.261654,26.873826],[115.262978,26.878622],[115.252558,26.891636],[115.24802,26.91571],[115.244953,26.919927],[115.232516,26.926969],[115.221739,26.928859],[115.217244,26.931684],[115.215542,26.938068],[115.207307,26.946999],[115.215668,26.95034],[115.216298,26.957002],[115.219323,26.961476],[115.229827,26.963266],[115.233504,26.965533],[115.23239,26.970604],[115.223399,26.976986],[115.22176,26.984402],[115.228798,26.986848],[115.231907,26.997742],[115.232684,27.005773],[115.225857,27.007821],[115.22197,27.013744],[115.226046,27.023562],[115.232054,27.027219],[115.238315,27.02716],[115.245205,27.036699],[115.241529,27.04425],[115.218693,27.066742],[115.217412,27.073795],[115.219092,27.080748],[115.214765,27.088236],[115.208483,27.092944],[115.204471,27.092884],[115.200773,27.088733],[115.195164,27.086508],[115.187328,27.088594],[115.180123,27.094215],[115.170375,27.09489],[115.166888,27.09346],[115.159934,27.085376],[115.15214,27.085296],[115.13775,27.098485],[115.135733,27.103709],[115.131636,27.106549],[115.122666,27.104027],[115.108318,27.094334],[115.113822,27.08766],[115.113948,27.079933],[115.115943,27.073695],[115.105482,27.070894],[115.095692,27.076079],[115.088444,27.073556],[115.08689,27.069186],[115.081491,27.07008],[115.077373,27.074113],[115.08107,27.078999],[115.089684,27.079854],[115.09231,27.084224],[115.093801,27.092447],[115.0867,27.097651],[115.083297,27.106787],[115.079201,27.109369],[115.071071,27.111176],[115.070587,27.120609],[115.077541,27.144078],[115.068571,27.160774]]]]}},{"type":"Feature","properties":{"adcode":360821,"name":"吉安县","center":[114.905117,27.040042],"centroid":[114.743834,27.137422],"childrenNum":0,"level":"district","parent":{"adcode":360800},"subFeatureIndex":2,"acroutes":[100000,360000,360800]},"geometry":{"type":"MultiPolygon","coordinates":[[[[114.880949,27.592395],[114.872798,27.589172],[114.865277,27.591901],[114.858365,27.585692],[114.848429,27.582647],[114.847126,27.587571],[114.835761,27.590655],[114.82677,27.590734],[114.82011,27.599474],[114.819228,27.60669],[114.811014,27.610466],[114.808367,27.614895],[114.801959,27.617208],[114.799564,27.615369],[114.793892,27.617129],[114.792401,27.623731],[114.787023,27.628594],[114.781624,27.628159],[114.778283,27.620153],[114.773851,27.618513],[114.772464,27.613788],[114.766141,27.61191],[114.759712,27.605465],[114.754817,27.607521],[114.745364,27.604061],[114.741015,27.592455],[114.737045,27.589449],[114.742402,27.580214],[114.742906,27.570069],[114.74549,27.565243],[114.744334,27.549182],[114.748767,27.54396],[114.747822,27.537669],[114.736814,27.533396],[114.733347,27.530666],[114.733284,27.52103],[114.729377,27.516203],[114.72215,27.5127],[114.722213,27.509554],[114.733452,27.502529],[114.737948,27.494633],[114.735385,27.486874],[114.740553,27.486479],[114.749418,27.48155],[114.747024,27.474286],[114.75257,27.466902],[114.7532,27.462824],[114.759733,27.456588],[114.762737,27.450787],[114.767548,27.449718],[114.781561,27.436809],[114.781287,27.42867],[114.784081,27.42164],[114.784292,27.402151],[114.790279,27.390405],[114.789922,27.386621],[114.782716,27.380817],[114.787905,27.368692],[114.797758,27.361598],[114.797842,27.357259],[114.791497,27.350164],[114.785258,27.339225],[114.785867,27.335123],[114.790237,27.328859],[114.789544,27.32331],[114.786077,27.320852],[114.784313,27.315024],[114.771141,27.312169],[114.761267,27.303526],[114.758683,27.297697],[114.747024,27.286892],[114.745805,27.277255],[114.748599,27.271682],[114.746729,27.268192],[114.739566,27.262044],[114.734482,27.259783],[114.733789,27.253972],[114.729503,27.251493],[114.715028,27.246415],[114.713537,27.239869],[114.704798,27.238005],[114.700764,27.22761],[114.692172,27.22511],[114.689126,27.222174],[114.681038,27.225765],[114.679273,27.222829],[114.682319,27.218781],[114.680807,27.214496],[114.675744,27.215607],[114.671185,27.213782],[114.666647,27.215131],[114.662299,27.211976],[114.653937,27.210389],[114.646396,27.215746],[114.642362,27.221361],[114.637047,27.223146],[114.632005,27.217948],[114.626186,27.21771],[114.618602,27.212928],[114.623245,27.203007],[114.622552,27.196975],[114.618371,27.190625],[114.625661,27.175125],[114.630556,27.1705],[114.631942,27.1652],[114.631921,27.1546],[114.637194,27.14793],[114.629757,27.137447],[114.636165,27.129803],[114.634589,27.126824],[114.628182,27.125514],[114.62835,27.118524],[114.617951,27.111593],[114.611375,27.110223],[114.599968,27.102319],[114.589317,27.106946],[114.590115,27.10925],[114.578099,27.118822],[114.561839,27.096737],[114.556125,27.090242],[114.546041,27.090838],[114.540327,27.087382],[114.529508,27.084303],[114.524045,27.079754],[114.513037,27.07749],[114.510621,27.075761],[114.494403,27.071689],[114.49058,27.069285],[114.483752,27.071888],[114.474992,27.073179],[114.469971,27.072007],[114.462744,27.067159],[114.455119,27.069007],[114.452409,27.057384],[114.447451,27.049476],[114.447976,27.04578],[114.45306,27.036083],[114.45474,27.029882],[114.471463,27.021297],[114.475475,27.014837],[114.476547,27.009689],[114.481462,27.006847],[114.478164,27.004819],[114.469236,26.990963],[114.457429,26.980068],[114.455665,26.968734],[114.447934,26.961596],[114.436863,26.962212],[114.426296,26.959826],[114.41909,26.959547],[114.418985,26.956624],[114.428249,26.951454],[114.431254,26.947456],[114.430854,26.940813],[114.433732,26.936716],[114.432346,26.928939],[114.424027,26.925676],[114.420813,26.919629],[114.420455,26.911492],[114.42201,26.906777],[114.430371,26.899973],[114.430518,26.891735],[114.433522,26.882363],[114.439909,26.876413],[114.445875,26.864253],[114.446106,26.855774],[114.451253,26.854082],[114.45766,26.847772],[114.472156,26.844945],[114.479887,26.839989],[114.486609,26.842557],[114.49413,26.849782],[114.500895,26.850758],[114.508983,26.846777],[114.517344,26.849743],[114.531713,26.849086],[114.537911,26.843751],[114.543814,26.841163],[114.550936,26.841402],[114.563162,26.837759],[114.568393,26.833499],[114.587174,26.830811],[114.591691,26.828302],[114.613539,26.819781],[114.617237,26.820956],[114.620367,26.826909],[114.631837,26.83543],[114.635114,26.844866],[114.641648,26.850579],[114.643875,26.854938],[114.640618,26.86075],[114.641669,26.865367],[114.64858,26.867159],[114.650009,26.870283],[114.648748,26.877786],[114.651059,26.886144],[114.656584,26.889586],[114.675765,26.889925],[114.684252,26.885109],[114.694945,26.889288],[114.699945,26.901783],[114.706541,26.909742],[114.713348,26.915252],[114.720953,26.91943],[114.725953,26.917202],[114.734986,26.916983],[114.7432,26.922951],[114.749061,26.92134],[114.763893,26.923647],[114.767149,26.928163],[114.77946,26.925637],[114.784271,26.917142],[114.78822,26.918614],[114.788115,26.924682],[114.794753,26.931803],[114.802905,26.935085],[114.80261,26.939441],[114.794732,26.947715],[114.811266,26.95728],[114.814144,26.963564],[114.822841,26.962292],[114.829354,26.964757],[114.837652,26.962292],[114.849983,26.956644],[114.849815,26.949266],[114.858534,26.947217],[114.862525,26.936597],[114.868806,26.936716],[114.874184,26.931962],[114.873575,26.92496],[114.866811,26.923309],[114.862084,26.906658],[114.863134,26.903574],[114.870361,26.903176],[114.875718,26.907673],[114.881894,26.916923],[114.895697,26.914596],[114.901978,26.911353],[114.910024,26.917381],[114.916221,26.917759],[114.923469,26.912686],[114.931788,26.915213],[114.934078,26.919927],[114.94267,26.925776],[114.93578,26.931445],[114.936641,26.936119],[114.943048,26.938387],[114.949603,26.944393],[114.957082,26.944771],[114.964035,26.941052],[114.968405,26.940813],[114.973804,26.945427],[114.979182,26.943757],[114.987039,26.938526],[114.992837,26.9396],[115.005085,26.950519],[115.014286,26.95392],[115.036765,26.965811],[115.042248,26.968953],[115.053571,26.987981],[115.064096,26.996768],[115.060882,27.010802],[115.057794,27.014857],[115.04647,27.024238],[115.038739,27.027597],[115.018908,27.033003],[115.012395,27.036719],[115.003782,27.04431],[114.993152,27.058219],[114.985463,27.05657],[114.978552,27.0519],[114.970086,27.040375],[114.9633,27.035984],[114.959372,27.0365],[114.953321,27.044827],[114.952082,27.051841],[114.95767,27.05341],[114.958195,27.059252],[114.950926,27.065192],[114.943279,27.064398],[114.935296,27.060583],[114.924792,27.061239],[114.913259,27.065053],[114.908259,27.057484],[114.902713,27.052437],[114.891915,27.051026],[114.886033,27.047946],[114.877987,27.04737],[114.874941,27.044032],[114.875697,27.037434],[114.86721,27.036003],[114.85534,27.031393],[114.843198,27.035606],[114.834942,27.035507],[114.821602,27.039282],[114.820614,27.044827],[114.822211,27.053589],[114.826475,27.061596],[114.839038,27.067139],[114.839668,27.077331],[114.836538,27.082655],[114.833576,27.092467],[114.820551,27.099737],[114.817589,27.102934],[114.817064,27.110282],[114.810698,27.119398],[114.809711,27.13018],[114.80656,27.133734],[114.804879,27.139909],[114.806665,27.146381],[114.802842,27.153587],[114.803661,27.162461],[114.807253,27.166788],[114.829606,27.178955],[114.83158,27.188382],[114.847714,27.202114],[114.854143,27.205111],[114.860592,27.205924],[114.867924,27.202154],[114.87763,27.202055],[114.889667,27.206321],[114.896369,27.204436],[114.92141,27.208186],[114.932628,27.214873],[114.933826,27.222849],[114.941095,27.231558],[114.949183,27.236338],[114.958195,27.237906],[114.961997,27.240484],[114.969035,27.248994],[114.967775,27.25661],[114.964666,27.261747],[114.958363,27.265178],[114.955191,27.27549],[114.958531,27.296746],[114.950863,27.302971],[114.95433,27.308205],[114.972943,27.313279],[114.975085,27.318077],[114.971031,27.319999],[114.958993,27.32099],[114.9483,27.320416],[114.928826,27.314528],[114.925696,27.314885],[114.927566,27.326798],[114.934477,27.334112],[114.941893,27.354643],[114.943006,27.361063],[114.937901,27.380103],[114.932418,27.38468],[114.929708,27.398229],[114.927166,27.416035],[114.917398,27.419204],[114.915843,27.421878],[114.922818,27.425521],[114.920927,27.430096],[114.923784,27.436967],[114.920822,27.439759],[114.907881,27.443204],[114.900213,27.446887],[114.904688,27.451777],[114.904121,27.456014],[114.900129,27.455955],[114.881642,27.446154],[114.875445,27.445263],[114.870361,27.447342],[114.870298,27.455856],[114.875256,27.466487],[114.874268,27.475632],[114.868806,27.47868],[114.871117,27.484757],[114.881201,27.480798],[114.884394,27.483252],[114.883827,27.489546],[114.880571,27.494197],[114.874289,27.498175],[114.872567,27.502984],[114.874941,27.505121],[114.877084,27.517845],[114.87992,27.524157],[114.877315,27.53666],[114.887945,27.542021],[114.889016,27.546433],[114.89431,27.553831],[114.891369,27.564017],[114.890592,27.574301],[114.882756,27.584664],[114.880949,27.592395]]]]}},{"type":"Feature","properties":{"adcode":360822,"name":"吉水县","center":[115.134569,27.213445],"centroid":[115.246722,27.194906],"childrenNum":0,"level":"district","parent":{"adcode":360800},"subFeatureIndex":3,"acroutes":[100000,360000,360800]},"geometry":{"type":"MultiPolygon","coordinates":[[[[115.36035,27.412332],[115.347451,27.420174],[115.342493,27.425898],[115.3343,27.429165],[115.331401,27.441363],[115.321695,27.449085],[115.318922,27.456628],[115.310162,27.46326],[115.301464,27.464111],[115.290036,27.467575],[115.287788,27.472841],[115.288376,27.479531],[115.28136,27.498927],[115.281003,27.506249],[115.284112,27.512661],[115.280477,27.516579],[115.265394,27.511612],[115.260877,27.51553],[115.254491,27.525759],[115.252705,27.525739],[115.242201,27.513709],[115.231634,27.507219],[115.218987,27.49774],[115.212937,27.495365],[115.196383,27.499719],[115.184702,27.498353],[115.181488,27.503716],[115.170102,27.512957],[115.168736,27.517746],[115.164031,27.521703],[115.154493,27.515629],[115.148737,27.516301],[115.136216,27.510741],[115.145544,27.50051],[115.147855,27.496355],[115.143317,27.485172],[115.12754,27.470624],[115.121784,27.459954],[115.11775,27.45641],[115.106742,27.453124],[115.095986,27.455361],[115.091448,27.460073],[115.088801,27.467536],[115.077898,27.468427],[115.072709,27.465893],[115.06439,27.46712],[115.057478,27.465695],[115.049664,27.470525],[115.042479,27.478126],[115.036996,27.481907],[115.026765,27.485825],[115.008425,27.488873],[115.004623,27.487112],[114.999602,27.488398],[114.993068,27.495009],[114.992375,27.504745],[114.987102,27.516381],[114.979455,27.519626],[114.976073,27.525838],[114.977606,27.53116],[114.987123,27.539667],[114.983804,27.545384],[114.974938,27.546967],[114.972796,27.55031],[114.959897,27.552011],[114.953825,27.55393],[114.949414,27.559112],[114.941872,27.563364],[114.937376,27.561683],[114.930402,27.552268],[114.911705,27.54669],[114.905822,27.540755],[114.896159,27.542041],[114.891873,27.540379],[114.887945,27.542021],[114.877315,27.53666],[114.87992,27.524157],[114.877084,27.517845],[114.874941,27.505121],[114.872567,27.502984],[114.874289,27.498175],[114.880571,27.494197],[114.883827,27.489546],[114.884394,27.483252],[114.881201,27.480798],[114.871117,27.484757],[114.868806,27.47868],[114.874268,27.475632],[114.875256,27.466487],[114.870298,27.455856],[114.870361,27.447342],[114.875445,27.445263],[114.881642,27.446154],[114.900129,27.455955],[114.904121,27.456014],[114.904688,27.451777],[114.900213,27.446887],[114.907881,27.443204],[114.920822,27.439759],[114.923784,27.436967],[114.920927,27.430096],[114.922818,27.425521],[114.915843,27.421878],[114.917398,27.419204],[114.927166,27.416035],[114.929708,27.398229],[114.932418,27.38468],[114.937901,27.380103],[114.943006,27.361063],[114.941893,27.354643],[114.934477,27.334112],[114.927566,27.326798],[114.925696,27.314885],[114.928826,27.314528],[114.9483,27.320416],[114.958993,27.32099],[114.971031,27.319999],[114.975085,27.318077],[114.972943,27.313279],[114.95433,27.308205],[114.950863,27.302971],[114.958531,27.296746],[114.955191,27.27549],[114.958363,27.265178],[114.964666,27.261747],[114.967775,27.25661],[114.969035,27.248994],[114.973951,27.243182],[114.980947,27.24338],[114.98143,27.251076],[114.983951,27.26254],[114.980484,27.273685],[114.990568,27.27777],[114.994224,27.282529],[115.00185,27.287962],[115.011639,27.29056],[115.016975,27.28937],[115.022983,27.284195],[115.021975,27.279892],[115.024454,27.27194],[115.022941,27.266388],[115.025757,27.256094],[115.028845,27.25066],[115.034706,27.249033],[115.034223,27.242726],[115.03855,27.237886],[115.039496,27.229971],[115.04458,27.22634],[115.040735,27.222829],[115.043865,27.215785],[115.049643,27.213583],[115.05876,27.214754],[115.064894,27.204972],[115.07147,27.20271],[115.078024,27.203384],[115.079033,27.200011],[115.083339,27.202035],[115.081322,27.20519],[115.086133,27.20767],[115.093696,27.203603],[115.093675,27.200666],[115.101007,27.203801],[115.110839,27.202214],[115.11107,27.193204],[115.11483,27.189672],[115.108801,27.181853],[115.099557,27.177109],[115.091301,27.175422],[115.078507,27.16655],[115.068571,27.160774],[115.077541,27.144078],[115.070587,27.120609],[115.071071,27.111176],[115.079201,27.109369],[115.083297,27.106787],[115.0867,27.097651],[115.093801,27.092447],[115.09231,27.084224],[115.089684,27.079854],[115.08107,27.078999],[115.077373,27.074113],[115.081491,27.07008],[115.08689,27.069186],[115.088444,27.073556],[115.095692,27.076079],[115.105482,27.070894],[115.115943,27.073695],[115.113948,27.079933],[115.113822,27.08766],[115.108318,27.094334],[115.122666,27.104027],[115.131636,27.106549],[115.135733,27.103709],[115.13775,27.098485],[115.15214,27.085296],[115.159934,27.085376],[115.166888,27.09346],[115.170375,27.09489],[115.180123,27.094215],[115.187328,27.088594],[115.195164,27.086508],[115.200773,27.088733],[115.204471,27.092884],[115.208483,27.092944],[115.214765,27.088236],[115.219092,27.080748],[115.217412,27.073795],[115.218693,27.066742],[115.241529,27.04425],[115.245205,27.036699],[115.238315,27.02716],[115.232054,27.027219],[115.226046,27.023562],[115.22197,27.013744],[115.225857,27.007821],[115.232684,27.005773],[115.231907,26.997742],[115.228798,26.986848],[115.22176,26.984402],[115.223399,26.976986],[115.23239,26.970604],[115.233504,26.965533],[115.229827,26.963266],[115.219323,26.961476],[115.216298,26.957002],[115.215668,26.95034],[115.207307,26.946999],[115.215542,26.938068],[115.217244,26.931684],[115.221739,26.928859],[115.232516,26.926969],[115.244953,26.919927],[115.24802,26.91571],[115.252558,26.891636],[115.262978,26.878622],[115.261654,26.873826],[115.266444,26.869368],[115.272936,26.869169],[115.281423,26.863855],[115.285813,26.85663],[115.291423,26.853266],[115.313502,26.861407],[115.317493,26.864929],[115.324783,26.865387],[115.332955,26.868791],[115.341232,26.86073],[115.345644,26.851793],[115.345686,26.845204],[115.342262,26.839292],[115.344867,26.834952],[115.354614,26.827088],[115.3664,26.835589],[115.377135,26.834534],[115.386273,26.834972],[115.386799,26.846617],[115.377618,26.864392],[115.382954,26.867855],[115.388794,26.878025],[115.386504,26.886064],[115.39266,26.88891],[115.412239,26.885806],[115.432638,26.887696],[115.437911,26.885268],[115.44749,26.884472],[115.458057,26.886761],[115.460557,26.896053],[115.454549,26.904847],[115.454822,26.910498],[115.458856,26.914994],[115.465431,26.908588],[115.470809,26.907374],[115.47646,26.899814],[115.473939,26.896391],[115.484569,26.887318],[115.48646,26.883438],[115.491334,26.880851],[115.496775,26.873229],[115.513833,26.866343],[115.52356,26.867517],[115.535934,26.874303],[115.539064,26.882582],[115.547131,26.887756],[115.552404,26.888591],[115.561017,26.884373],[115.572025,26.887975],[115.57755,26.895735],[115.576941,26.903872],[115.56963,26.903116],[115.562488,26.906956],[115.568874,26.916625],[115.567635,26.922414],[115.57316,26.925875],[115.583643,26.925875],[115.59902,26.936318],[115.610239,26.939938],[115.617676,26.949465],[115.622738,26.951951],[115.626625,26.957559],[115.627675,26.96261],[115.625364,26.968655],[115.625827,26.976231],[115.624041,26.983289],[115.619671,26.990983],[115.610344,26.997364],[115.60381,27.006131],[115.60673,27.011041],[115.605974,27.018315],[115.608201,27.032168],[115.610785,27.036003],[115.607718,27.037275],[115.607571,27.042601],[115.603054,27.044886],[115.605827,27.049675],[115.598726,27.051503],[115.595764,27.05496],[115.578097,27.064676],[115.575849,27.0701],[115.565408,27.072464],[115.55982,27.075066],[115.559421,27.079079],[115.548055,27.089627],[115.542677,27.085138],[115.537173,27.083092],[115.529505,27.084502],[115.526984,27.088713],[115.530115,27.107999],[115.5294,27.113202],[115.525199,27.118643],[115.520052,27.116975],[115.51751,27.108376],[115.511733,27.10637],[115.502594,27.113301],[115.503077,27.117769],[115.49772,27.116637],[115.502615,27.120907],[115.494737,27.132423],[115.490515,27.14521],[115.488918,27.153528],[115.480641,27.149716],[115.47541,27.149438],[115.468687,27.144118],[115.462763,27.145031],[115.457616,27.150689],[115.447511,27.151205],[115.446062,27.148783],[115.427953,27.150014],[115.423751,27.146818],[115.420243,27.15319],[115.42331,27.153171],[115.42644,27.159642],[115.413563,27.160297],[115.404004,27.166093],[115.400811,27.162838],[115.393668,27.161528],[115.37329,27.151086],[115.366358,27.160992],[115.360539,27.162441],[115.355119,27.16788],[115.34993,27.188918],[115.353648,27.200547],[115.346463,27.205051],[115.34369,27.20388],[115.330371,27.210607],[115.32806,27.213524],[115.326674,27.221341],[115.324321,27.224614],[115.324825,27.238104],[115.316863,27.239572],[115.314216,27.244551],[115.313943,27.2531],[115.315393,27.258653],[115.311023,27.267062],[115.311527,27.270909],[115.309006,27.279317],[115.300246,27.287209],[115.302725,27.297816],[115.289028,27.301781],[115.284154,27.306876],[115.28825,27.313299],[115.285919,27.318691],[115.286213,27.334905],[115.280351,27.339126],[115.27949,27.35276],[115.280225,27.365303],[115.287032,27.370296],[115.296359,27.37204],[115.311065,27.379806],[115.317619,27.380401],[115.326317,27.377033],[115.328964,27.377667],[115.338964,27.38678],[115.347094,27.390028],[115.344657,27.396149],[115.351232,27.40643],[115.36035,27.412332]]]]}},{"type":"Feature","properties":{"adcode":360823,"name":"峡江县","center":[115.319331,27.580862],"centroid":[115.208061,27.583725],"childrenNum":0,"level":"district","parent":{"adcode":360800},"subFeatureIndex":4,"acroutes":[100000,360000,360800]},"geometry":{"type":"MultiPolygon","coordinates":[[[[115.252936,27.761878],[115.240079,27.760753],[115.225626,27.75479],[115.203778,27.75019],[115.198105,27.750605],[115.191152,27.743003],[115.17256,27.733287],[115.165816,27.73311],[115.158842,27.724183],[115.160039,27.717211],[115.154682,27.713755],[115.13922,27.70935],[115.135859,27.710773],[115.13546,27.71891],[115.139031,27.726356],[115.135985,27.734334],[115.129094,27.741482],[115.123128,27.742134],[115.11859,27.738698],[115.11899,27.730996],[115.116889,27.727817],[115.111217,27.726553],[115.110271,27.718574],[115.102835,27.71095],[115.09962,27.712471],[115.098213,27.708679],[115.08941,27.704649],[115.082898,27.704057],[115.079117,27.716974],[115.069432,27.721517],[115.058529,27.732576],[115.055294,27.731569],[115.054621,27.723393],[115.046218,27.720905],[115.029769,27.712866],[115.024937,27.711977],[115.023068,27.708363],[115.02666,27.700363],[115.024475,27.695365],[115.015589,27.689201],[115.017206,27.677189],[115.015169,27.668199],[115.019937,27.665136],[115.019223,27.654367],[115.009959,27.650632],[114.997648,27.655493],[114.992522,27.659426],[114.986997,27.668081],[114.980316,27.674344],[114.975989,27.67472],[114.966031,27.664465],[114.960758,27.661125],[114.962838,27.65822],[114.968195,27.657785],[114.971325,27.651224],[114.954078,27.639406],[114.9437,27.63986],[114.926095,27.630966],[114.926368,27.624146],[114.921242,27.622703],[114.913154,27.615231],[114.90828,27.613274],[114.899142,27.61272],[114.89139,27.609992],[114.887482,27.606512],[114.88744,27.601352],[114.882525,27.597675],[114.880949,27.592395],[114.882756,27.584664],[114.890592,27.574301],[114.891369,27.564017],[114.89431,27.553831],[114.889016,27.546433],[114.887945,27.542021],[114.891873,27.540379],[114.896159,27.542041],[114.905822,27.540755],[114.911705,27.54669],[114.930402,27.552268],[114.937376,27.561683],[114.941872,27.563364],[114.949414,27.559112],[114.953825,27.55393],[114.959897,27.552011],[114.972796,27.55031],[114.974938,27.546967],[114.983804,27.545384],[114.987123,27.539667],[114.977606,27.53116],[114.976073,27.525838],[114.979455,27.519626],[114.987102,27.516381],[114.992375,27.504745],[114.993068,27.495009],[114.999602,27.488398],[115.004623,27.487112],[115.008425,27.488873],[115.026765,27.485825],[115.036996,27.481907],[115.042479,27.478126],[115.049664,27.470525],[115.057478,27.465695],[115.06439,27.46712],[115.072709,27.465893],[115.077898,27.468427],[115.088801,27.467536],[115.091448,27.460073],[115.095986,27.455361],[115.106742,27.453124],[115.11775,27.45641],[115.121784,27.459954],[115.12754,27.470624],[115.143317,27.485172],[115.147855,27.496355],[115.145544,27.50051],[115.136216,27.510741],[115.148737,27.516301],[115.154493,27.515629],[115.164031,27.521703],[115.168736,27.517746],[115.170102,27.512957],[115.181488,27.503716],[115.184702,27.498353],[115.196383,27.499719],[115.212937,27.495365],[115.218987,27.49774],[115.231634,27.507219],[115.242201,27.513709],[115.252705,27.525739],[115.254491,27.525759],[115.260877,27.51553],[115.265394,27.511612],[115.280477,27.516579],[115.284112,27.512661],[115.281003,27.506249],[115.28136,27.498927],[115.288376,27.479531],[115.287788,27.472841],[115.290036,27.467575],[115.301464,27.464111],[115.310162,27.46326],[115.318922,27.456628],[115.321695,27.449085],[115.331401,27.441363],[115.3343,27.429165],[115.342493,27.425898],[115.347451,27.420174],[115.36035,27.412332],[115.368522,27.416669],[115.377954,27.423818],[115.381883,27.433759],[115.391588,27.435561],[115.398941,27.43372],[115.40747,27.440076],[115.425327,27.437324],[115.438646,27.447263],[115.442764,27.452866],[115.452616,27.460409],[115.456587,27.458231],[115.463835,27.459756],[115.469213,27.464962],[115.481775,27.470822],[115.486061,27.474365],[115.491313,27.465734],[115.497594,27.468763],[115.501292,27.474365],[115.514422,27.475315],[115.525325,27.479967],[115.528644,27.485331],[115.535051,27.491347],[115.538161,27.499145],[115.532699,27.501658],[115.530346,27.506328],[115.536522,27.517152],[115.535493,27.519566],[115.528917,27.521525],[115.524779,27.51921],[115.510346,27.519368],[115.505682,27.527263],[115.503266,27.536087],[115.495725,27.543505],[115.488393,27.547066],[115.490725,27.553593],[115.480347,27.573906],[115.484653,27.573688],[115.485935,27.578039],[115.483456,27.582133],[115.476649,27.586404],[115.468834,27.586345],[115.462574,27.589192],[115.45936,27.586977],[115.450978,27.58763],[115.443415,27.592435],[115.439844,27.597714],[115.442995,27.604021],[115.442406,27.60762],[115.427449,27.613748],[115.413857,27.614974],[115.404025,27.633061],[115.391609,27.636263],[115.380601,27.634583],[115.371736,27.630749],[115.366652,27.626103],[115.361505,27.625313],[115.354909,27.631005],[115.349804,27.626598],[115.345518,27.625886],[115.341989,27.628732],[115.336001,27.629563],[115.327829,27.626993],[115.321401,27.629563],[115.319216,27.63312],[115.312746,27.651481],[115.313418,27.661797],[115.321884,27.669325],[115.326023,27.674601],[115.338438,27.685547],[115.342409,27.691947],[115.3143,27.703365],[115.30449,27.714901],[115.299112,27.719858],[115.28262,27.728232],[115.269175,27.732043],[115.260919,27.732438],[115.256087,27.735973],[115.254533,27.740317],[115.252936,27.761878]]]]}},{"type":"Feature","properties":{"adcode":360824,"name":"新干县","center":[115.399294,27.755758],"centroid":[115.501718,27.732716],"childrenNum":0,"level":"district","parent":{"adcode":360800},"subFeatureIndex":5,"acroutes":[100000,360000,360800]},"geometry":{"type":"MultiPolygon","coordinates":[[[[115.593306,27.518458],[115.591941,27.523702],[115.604819,27.533416],[115.603558,27.539746],[115.606289,27.543208],[115.606919,27.561228],[115.60902,27.567953],[115.607025,27.575765],[115.611436,27.582152],[115.621982,27.593246],[115.6236,27.597556],[115.629797,27.604476],[115.63465,27.612068],[115.634608,27.616259],[115.64299,27.625313],[115.652002,27.636955],[115.663389,27.644979],[115.663557,27.649545],[115.673073,27.650829],[115.681918,27.657054],[115.694733,27.677367],[115.707211,27.689814],[115.712421,27.692362],[115.724837,27.708876],[115.729816,27.72128],[115.73511,27.729614],[115.730551,27.748413],[115.728093,27.751138],[115.73032,27.757969],[115.725383,27.762628],[115.722883,27.761937],[115.710425,27.766616],[115.697716,27.769716],[115.695426,27.771966],[115.690489,27.784461],[115.690426,27.790442],[115.686792,27.798573],[115.677569,27.811638],[115.677254,27.816492],[115.673872,27.821307],[115.661561,27.824109],[115.654166,27.828351],[115.645406,27.82103],[115.636688,27.824089],[115.630133,27.821839],[115.613348,27.827956],[115.604272,27.835335],[115.591164,27.841806],[115.586038,27.852104],[115.581185,27.85382],[115.578223,27.863307],[115.571857,27.860112],[115.560933,27.861098],[115.551627,27.865319],[115.54356,27.870546],[115.53169,27.884785],[115.524232,27.885396],[115.510577,27.896754],[115.510388,27.900244],[115.516186,27.906711],[115.513833,27.915642],[115.521249,27.922601],[115.529484,27.925144],[115.53711,27.940263],[115.527447,27.949053],[115.510892,27.948344],[115.500745,27.953409],[115.485494,27.957291],[115.475368,27.952207],[115.472448,27.94724],[115.464465,27.946432],[115.464297,27.941525],[115.472343,27.938824],[115.474549,27.935828],[115.471229,27.926209],[115.466103,27.922404],[115.45999,27.921596],[115.451587,27.92408],[115.450389,27.932339],[115.439339,27.935947],[115.443982,27.955104],[115.439423,27.954946],[115.439255,27.959853],[115.428457,27.960306],[115.426546,27.95534],[115.429634,27.944264],[115.425264,27.943693],[115.413374,27.954749],[115.393605,27.945565],[115.393605,27.935927],[115.404361,27.931768],[115.408185,27.928673],[115.405643,27.913001],[115.402681,27.908762],[115.391252,27.90336],[115.382618,27.89636],[115.382492,27.893402],[115.392198,27.883621],[115.401861,27.881708],[115.407029,27.87875],[115.408248,27.87522],[115.40371,27.870506],[115.392534,27.869639],[115.388668,27.85885],[115.382597,27.864727],[115.380328,27.86315],[115.371421,27.863623],[115.360539,27.861552],[115.362471,27.852597],[115.360665,27.84585],[115.352997,27.837071],[115.350812,27.830896],[115.331632,27.816235],[115.324342,27.812032],[115.319237,27.798218],[115.308901,27.787363],[115.302305,27.78379],[115.296443,27.776625],[115.280204,27.767386],[115.262222,27.764543],[115.252936,27.761878],[115.254533,27.740317],[115.256087,27.735973],[115.260919,27.732438],[115.269175,27.732043],[115.28262,27.728232],[115.299112,27.719858],[115.30449,27.714901],[115.3143,27.703365],[115.342409,27.691947],[115.338438,27.685547],[115.326023,27.674601],[115.321884,27.669325],[115.313418,27.661797],[115.312746,27.651481],[115.319216,27.63312],[115.321401,27.629563],[115.327829,27.626993],[115.336001,27.629563],[115.341989,27.628732],[115.345518,27.625886],[115.349804,27.626598],[115.354909,27.631005],[115.361505,27.625313],[115.366652,27.626103],[115.371736,27.630749],[115.380601,27.634583],[115.391609,27.636263],[115.404025,27.633061],[115.413857,27.614974],[115.427449,27.613748],[115.442406,27.60762],[115.442995,27.604021],[115.439844,27.597714],[115.443415,27.592435],[115.450978,27.58763],[115.45936,27.586977],[115.462574,27.589192],[115.468834,27.586345],[115.476649,27.586404],[115.483456,27.582133],[115.485935,27.578039],[115.484653,27.573688],[115.480347,27.573906],[115.490725,27.553593],[115.488393,27.547066],[115.495725,27.543505],[115.503266,27.536087],[115.505682,27.527263],[115.510346,27.519368],[115.524779,27.51921],[115.528917,27.521525],[115.535493,27.519566],[115.536522,27.517152],[115.530346,27.506328],[115.532699,27.501658],[115.538161,27.499145],[115.559105,27.507397],[115.570639,27.506724],[115.577529,27.511454],[115.582382,27.512225],[115.593306,27.518458]]]]}},{"type":"Feature","properties":{"adcode":360825,"name":"永丰县","center":[115.435559,27.321087],"centroid":[115.586677,27.091665],"childrenNum":0,"level":"district","parent":{"adcode":360800},"subFeatureIndex":6,"acroutes":[100000,360000,360800]},"geometry":{"type":"MultiPolygon","coordinates":[[[[115.593306,27.518458],[115.582382,27.512225],[115.577529,27.511454],[115.570639,27.506724],[115.559105,27.507397],[115.538161,27.499145],[115.535051,27.491347],[115.528644,27.485331],[115.525325,27.479967],[115.514422,27.475315],[115.501292,27.474365],[115.497594,27.468763],[115.491313,27.465734],[115.486061,27.474365],[115.481775,27.470822],[115.469213,27.464962],[115.463835,27.459756],[115.456587,27.458231],[115.452616,27.460409],[115.442764,27.452866],[115.438646,27.447263],[115.425327,27.437324],[115.40747,27.440076],[115.398941,27.43372],[115.391588,27.435561],[115.381883,27.433759],[115.377954,27.423818],[115.368522,27.416669],[115.36035,27.412332],[115.351232,27.40643],[115.344657,27.396149],[115.347094,27.390028],[115.338964,27.38678],[115.328964,27.377667],[115.326317,27.377033],[115.317619,27.380401],[115.311065,27.379806],[115.296359,27.37204],[115.287032,27.370296],[115.280225,27.365303],[115.27949,27.35276],[115.280351,27.339126],[115.286213,27.334905],[115.285919,27.318691],[115.28825,27.313299],[115.284154,27.306876],[115.289028,27.301781],[115.302725,27.297816],[115.300246,27.287209],[115.309006,27.279317],[115.311527,27.270909],[115.311023,27.267062],[115.315393,27.258653],[115.313943,27.2531],[115.314216,27.244551],[115.316863,27.239572],[115.324825,27.238104],[115.324321,27.224614],[115.326674,27.221341],[115.32806,27.213524],[115.330371,27.210607],[115.34369,27.20388],[115.346463,27.205051],[115.353648,27.200547],[115.34993,27.188918],[115.355119,27.16788],[115.360539,27.162441],[115.366358,27.160992],[115.37329,27.151086],[115.393668,27.161528],[115.400811,27.162838],[115.404004,27.166093],[115.413563,27.160297],[115.42644,27.159642],[115.42331,27.153171],[115.420243,27.15319],[115.423751,27.146818],[115.427953,27.150014],[115.446062,27.148783],[115.447511,27.151205],[115.457616,27.150689],[115.462763,27.145031],[115.468687,27.144118],[115.47541,27.149438],[115.480641,27.149716],[115.488918,27.153528],[115.490515,27.14521],[115.494737,27.132423],[115.502615,27.120907],[115.49772,27.116637],[115.503077,27.117769],[115.502594,27.113301],[115.511733,27.10637],[115.51751,27.108376],[115.520052,27.116975],[115.525199,27.118643],[115.5294,27.113202],[115.530115,27.107999],[115.526984,27.088713],[115.529505,27.084502],[115.537173,27.083092],[115.542677,27.085138],[115.548055,27.089627],[115.559421,27.079079],[115.55982,27.075066],[115.565408,27.072464],[115.575849,27.0701],[115.578097,27.064676],[115.595764,27.05496],[115.598726,27.051503],[115.605827,27.049675],[115.603054,27.044886],[115.607571,27.042601],[115.607718,27.037275],[115.610785,27.036003],[115.608201,27.032168],[115.605974,27.018315],[115.60673,27.011041],[115.60381,27.006131],[115.610344,26.997364],[115.619671,26.990983],[115.624041,26.983289],[115.625827,26.976231],[115.625364,26.968655],[115.627675,26.96261],[115.626625,26.957559],[115.622738,26.951951],[115.617676,26.949465],[115.610239,26.939938],[115.59902,26.936318],[115.583643,26.925875],[115.57316,26.925875],[115.567635,26.922414],[115.568874,26.916625],[115.562488,26.906956],[115.56963,26.903116],[115.576941,26.903872],[115.57755,26.895735],[115.572025,26.887975],[115.561017,26.884373],[115.552404,26.888591],[115.547131,26.887756],[115.539064,26.882582],[115.535934,26.874303],[115.52356,26.867517],[115.513833,26.866343],[115.496775,26.873229],[115.491334,26.880851],[115.48646,26.883438],[115.484569,26.887318],[115.473939,26.896391],[115.47646,26.899814],[115.470809,26.907374],[115.465431,26.908588],[115.458856,26.914994],[115.454822,26.910498],[115.454549,26.904847],[115.460557,26.896053],[115.458057,26.886761],[115.44749,26.884472],[115.437911,26.885268],[115.432638,26.887696],[115.412239,26.885806],[115.39266,26.88891],[115.386504,26.886064],[115.388794,26.878025],[115.382954,26.867855],[115.377618,26.864392],[115.386799,26.846617],[115.386273,26.834972],[115.383752,26.829019],[115.3885,26.820418],[115.379761,26.815122],[115.38203,26.809049],[115.390454,26.802178],[115.390727,26.793894],[115.39745,26.791863],[115.401441,26.795268],[115.41268,26.795049],[115.424109,26.799689],[115.429403,26.795049],[115.432407,26.787322],[115.433835,26.778698],[115.444549,26.774156],[115.449507,26.769097],[115.452742,26.759694],[115.46957,26.760153],[115.472028,26.75824],[115.469927,26.753837],[115.474822,26.750729],[115.486544,26.749096],[115.492006,26.744792],[115.493855,26.739712],[115.499128,26.734551],[115.499527,26.729689],[115.50564,26.724548],[115.511396,26.713946],[115.508749,26.710398],[115.502363,26.71004],[115.497279,26.704439],[115.495388,26.698799],[115.487237,26.68955],[115.488645,26.681835],[115.49709,26.677071],[115.501859,26.677489],[115.508182,26.68656],[115.514001,26.687457],[115.520304,26.693756],[115.526732,26.69541],[115.534736,26.687696],[115.535556,26.677589],[115.534652,26.671987],[115.537425,26.656435],[115.54253,26.651411],[115.554652,26.646306],[115.565072,26.649835],[115.570156,26.647363],[115.586164,26.650673],[115.591857,26.650533],[115.592382,26.660663],[115.602886,26.660264],[115.607067,26.657951],[115.615092,26.664212],[115.618789,26.662218],[115.643977,26.661161],[115.649965,26.653584],[115.657822,26.651211],[115.659565,26.647961],[115.665721,26.648659],[115.670321,26.641839],[115.674796,26.64491],[115.67362,26.649437],[115.675762,26.652846],[115.682989,26.654182],[115.687695,26.659546],[115.687779,26.666265],[115.698808,26.672685],[115.698451,26.679044],[115.704417,26.685344],[115.715131,26.68941],[115.714291,26.692281],[115.701728,26.700752],[115.704312,26.705815],[115.708661,26.708306],[115.716833,26.708645],[115.721518,26.705516],[115.729354,26.70721],[115.731559,26.711295],[115.72948,26.720981],[115.728072,26.74545],[115.724669,26.749574],[115.724501,26.758639],[115.71803,26.759396],[115.712484,26.754833],[115.705342,26.754236],[115.693157,26.760232],[115.687443,26.765073],[115.685195,26.764097],[115.678073,26.767583],[115.675048,26.772782],[115.676876,26.776706],[115.69114,26.780829],[115.688472,26.786744],[115.688682,26.792719],[115.696014,26.796682],[115.695825,26.800426],[115.687401,26.803214],[115.687191,26.80877],[115.691119,26.815162],[115.688073,26.823066],[115.694039,26.830492],[115.705321,26.841302],[115.705531,26.849723],[115.708136,26.852728],[115.718072,26.850599],[115.721791,26.84821],[115.728933,26.839351],[115.740467,26.834076],[115.748828,26.832623],[115.753072,26.827884],[115.757021,26.817272],[115.765025,26.811737],[115.785424,26.80411],[115.787651,26.808252],[115.789856,26.8245],[115.79347,26.828283],[115.798911,26.83971],[115.804646,26.855376],[115.807062,26.857864],[115.823196,26.858859],[115.826032,26.861327],[115.827335,26.873089],[115.832839,26.883139],[115.835486,26.897506],[115.837901,26.899714],[115.84641,26.900311],[115.86454,26.909682],[115.870086,26.91561],[115.874287,26.916923],[115.88248,26.924642],[115.893236,26.938367],[115.90145,26.945547],[115.904244,26.951076],[115.91145,26.955053],[115.920463,26.956803],[115.925526,26.95388],[115.928929,26.955272],[115.935357,26.965056],[115.930925,26.971976],[115.911345,26.985118],[115.900673,26.982553],[115.894749,26.979273],[115.888215,26.982633],[115.883447,26.989949],[115.887417,26.997981],[115.885379,26.999869],[115.874728,26.997106],[115.866199,27.000823],[115.86101,27.005873],[115.859876,27.013267],[115.862859,27.021436],[115.856409,27.027319],[115.85351,27.035884],[115.852607,27.056013],[115.845653,27.056272],[115.848111,27.06102],[115.84809,27.068351],[115.842964,27.07304],[115.825843,27.08182],[115.825045,27.088892],[115.821599,27.09497],[115.818994,27.107065],[115.814163,27.110481],[115.798911,27.112447],[115.792398,27.115267],[115.782756,27.113738],[115.773659,27.110243],[115.755656,27.110302],[115.741622,27.104245],[115.727232,27.100531],[115.718408,27.100352],[115.715593,27.110203],[115.711686,27.114572],[115.698493,27.12043],[115.695741,27.127182],[115.687842,27.129942],[115.679418,27.138201],[115.676288,27.144793],[115.675216,27.154501],[115.670721,27.164009],[115.665216,27.169091],[115.666981,27.179094],[115.662674,27.182845],[115.659502,27.191676],[115.654502,27.193324],[115.649524,27.18989],[115.639713,27.188402],[115.638368,27.197173],[115.630049,27.213305],[115.629881,27.221202],[115.627591,27.228046],[115.628095,27.23612],[115.625869,27.243718],[115.627948,27.24707],[115.641835,27.254924],[115.645049,27.260815],[115.649797,27.260021],[115.656855,27.264325],[115.670805,27.265753],[115.675804,27.263909],[115.682632,27.267359],[115.689249,27.268291],[115.695258,27.27202],[115.692632,27.278861],[115.693472,27.285325],[115.686434,27.289965],[115.68425,27.294723],[115.685069,27.301742],[115.681372,27.314092],[115.681918,27.324122],[115.677254,27.332506],[115.676729,27.336867],[115.679859,27.346875],[115.682737,27.34848],[115.683367,27.356307],[115.689712,27.365284],[115.696833,27.365303],[115.701224,27.369365],[115.702001,27.384323],[115.696119,27.401161],[115.704333,27.411124],[115.712589,27.415283],[115.719816,27.425086],[115.720467,27.430967],[115.724732,27.439363],[115.713283,27.452272],[115.702716,27.455242],[115.695636,27.451817],[115.683829,27.448115],[115.675951,27.451203],[115.66841,27.446669],[115.655196,27.449303],[115.651057,27.451302],[115.638915,27.464448],[115.630721,27.469119],[115.627087,27.479551],[115.619944,27.484084],[115.618978,27.488992],[115.614987,27.495306],[115.610239,27.498809],[115.608747,27.50522],[115.60463,27.511533],[115.593306,27.518458]]]]}},{"type":"Feature","properties":{"adcode":360826,"name":"泰和县","center":[114.901393,26.790164],"centroid":[114.934187,26.737471],"childrenNum":0,"level":"district","parent":{"adcode":360800},"subFeatureIndex":7,"acroutes":[100000,360000,360800]},"geometry":{"type":"MultiPolygon","coordinates":[[[[114.434489,26.697842],[114.448585,26.702606],[114.456421,26.702526],[114.469572,26.697025],[114.475727,26.688434],[114.481147,26.687756],[114.486063,26.682792],[114.488626,26.674938],[114.499718,26.675915],[114.498752,26.685623],[114.492071,26.700911],[114.498731,26.703144],[114.508374,26.711176],[114.517008,26.707828],[114.515747,26.699596],[114.520285,26.697324],[114.521483,26.693377],[114.527848,26.68963],[114.533898,26.693696],[114.541797,26.690527],[114.547175,26.683789],[114.550557,26.683709],[114.55728,26.699217],[114.558667,26.706731],[114.57041,26.711275],[114.573708,26.71572],[114.580893,26.716776],[114.587027,26.725405],[114.59583,26.72939],[114.598687,26.725106],[114.61043,26.730665],[114.614632,26.72939],[114.615241,26.720582],[114.618266,26.714325],[114.624484,26.711455],[114.6294,26.70422],[114.637299,26.70434],[114.652152,26.699975],[114.644442,26.694593],[114.642908,26.685424],[114.640471,26.681277],[114.653013,26.677968],[114.656185,26.671947],[114.66858,26.658928],[114.674651,26.657153],[114.684063,26.662178],[114.687298,26.658469],[114.69005,26.659765],[114.69713,26.655339],[114.69818,26.666764],[114.702928,26.669814],[114.718747,26.685942],[114.724482,26.690447],[114.723285,26.702087],[114.729314,26.704838],[114.73736,26.711634],[114.744061,26.709342],[114.745679,26.698181],[114.754271,26.703941],[114.758389,26.708505],[114.766288,26.709103],[114.76841,26.707469],[114.766876,26.691842],[114.770195,26.685743],[114.77904,26.683629],[114.78238,26.68028],[114.784985,26.669475],[114.78927,26.666066],[114.788556,26.662058],[114.802884,26.655179],[114.812379,26.657074],[114.821013,26.668458],[114.829942,26.678646],[114.831349,26.684327],[114.8299,26.699417],[114.839836,26.700314],[114.847819,26.702645],[114.853471,26.69828],[114.86305,26.697025],[114.866811,26.694852],[114.8645,26.685663],[114.867336,26.682354],[114.873743,26.68662],[114.873911,26.693836],[114.878869,26.692799],[114.879373,26.68335],[114.890339,26.674898],[114.890655,26.670532],[114.887293,26.660922],[114.879646,26.65825],[114.87305,26.657971],[114.866643,26.649357],[114.878491,26.638489],[114.880739,26.633523],[114.882167,26.622992],[114.911494,26.608132],[114.918385,26.600372],[114.913511,26.594627],[114.912104,26.586826],[114.920297,26.583953],[114.920402,26.578386],[114.923658,26.571163],[114.92475,26.561585],[114.923574,26.556975],[114.929036,26.548992],[114.940548,26.552165],[114.940843,26.548114],[114.948363,26.542006],[114.956746,26.539711],[114.965233,26.531547],[114.981241,26.539511],[114.987732,26.532046],[114.987669,26.523403],[114.990211,26.521806],[114.998236,26.523203],[115.00624,26.518751],[115.011765,26.511125],[115.017353,26.508869],[115.028383,26.498367],[115.02813,26.49619],[115.020694,26.488503],[115.014349,26.487444],[115.014328,26.482872],[115.017521,26.480236],[115.020063,26.472946],[115.028614,26.465177],[115.034916,26.456708],[115.039223,26.454851],[115.042227,26.449697],[115.045945,26.449757],[115.047794,26.454471],[115.051785,26.454351],[115.0621,26.447979],[115.073592,26.457248],[115.075902,26.46274],[115.081701,26.467814],[115.087163,26.47722],[115.096154,26.487724],[115.114935,26.503977],[115.126973,26.507671],[115.131973,26.511405],[115.141657,26.513102],[115.165879,26.522764],[115.185123,26.520668],[115.192601,26.522804],[115.195521,26.51927],[115.205248,26.518172],[115.217307,26.513541],[115.226928,26.517074],[115.230899,26.521566],[115.230794,26.527415],[115.226676,26.531667],[115.223987,26.527215],[115.218714,26.532066],[115.235352,26.558891],[115.239617,26.563381],[115.251403,26.572241],[115.25615,26.573478],[115.257768,26.582397],[115.269112,26.603325],[115.27886,26.606297],[115.283923,26.611184],[115.286003,26.620399],[115.292767,26.62742],[115.302074,26.632566],[115.298502,26.647064],[115.299637,26.65486],[115.309573,26.656156],[115.317346,26.661719],[115.32888,26.662896],[115.33493,26.672266],[115.332955,26.679961],[115.328901,26.688613],[115.323481,26.690547],[115.324699,26.692958],[115.320729,26.696287],[115.319048,26.701709],[115.321401,26.70434],[115.317115,26.71291],[115.307452,26.715321],[115.302536,26.722475],[115.301695,26.727836],[115.304279,26.736265],[115.294175,26.740887],[115.286822,26.752861],[115.276339,26.757822],[115.262684,26.766129],[115.237831,26.780052],[115.219828,26.784036],[115.213819,26.789294],[115.208714,26.799251],[115.208294,26.805205],[115.203525,26.815759],[115.197832,26.816316],[115.187938,26.821434],[115.183001,26.82655],[115.173337,26.827526],[115.163148,26.830353],[115.1613,26.834434],[115.156678,26.831806],[115.14985,26.834594],[115.138926,26.836206],[115.138128,26.841203],[115.132729,26.850101],[115.127813,26.852629],[115.112057,26.850141],[115.103255,26.853266],[115.101511,26.863477],[115.096049,26.870522],[115.090314,26.880851],[115.085335,26.88489],[115.085083,26.89086],[115.091007,26.895615],[115.093654,26.902758],[115.091196,26.91008],[115.094221,26.914337],[115.094726,26.920186],[115.085776,26.920246],[115.083885,26.928581],[115.075356,26.937392],[115.060651,26.941072],[115.05563,26.95028],[115.050693,26.953005],[115.047038,26.961198],[115.036765,26.965811],[115.014286,26.95392],[115.005085,26.950519],[114.992837,26.9396],[114.987039,26.938526],[114.979182,26.943757],[114.973804,26.945427],[114.968405,26.940813],[114.964035,26.941052],[114.957082,26.944771],[114.949603,26.944393],[114.943048,26.938387],[114.936641,26.936119],[114.93578,26.931445],[114.94267,26.925776],[114.934078,26.919927],[114.931788,26.915213],[114.923469,26.912686],[114.916221,26.917759],[114.910024,26.917381],[114.901978,26.911353],[114.895697,26.914596],[114.881894,26.916923],[114.875718,26.907673],[114.870361,26.903176],[114.863134,26.903574],[114.862084,26.906658],[114.866811,26.923309],[114.873575,26.92496],[114.874184,26.931962],[114.868806,26.936716],[114.862525,26.936597],[114.858534,26.947217],[114.849815,26.949266],[114.849983,26.956644],[114.837652,26.962292],[114.829354,26.964757],[114.822841,26.962292],[114.814144,26.963564],[114.811266,26.95728],[114.794732,26.947715],[114.80261,26.939441],[114.802905,26.935085],[114.794753,26.931803],[114.788115,26.924682],[114.78822,26.918614],[114.784271,26.917142],[114.77946,26.925637],[114.767149,26.928163],[114.763893,26.923647],[114.749061,26.92134],[114.7432,26.922951],[114.734986,26.916983],[114.725953,26.917202],[114.720953,26.91943],[114.713348,26.915252],[114.706541,26.909742],[114.699945,26.901783],[114.694945,26.889288],[114.684252,26.885109],[114.675765,26.889925],[114.656584,26.889586],[114.651059,26.886144],[114.648748,26.877786],[114.650009,26.870283],[114.64858,26.867159],[114.641669,26.865367],[114.640618,26.86075],[114.643875,26.854938],[114.641648,26.850579],[114.635114,26.844866],[114.631837,26.83543],[114.620367,26.826909],[114.617237,26.820956],[114.613539,26.819781],[114.591691,26.828302],[114.587174,26.830811],[114.568393,26.833499],[114.563162,26.837759],[114.550936,26.841402],[114.543814,26.841163],[114.537911,26.843751],[114.531713,26.849086],[114.517344,26.849743],[114.508983,26.846777],[114.500895,26.850758],[114.49413,26.849782],[114.486609,26.842557],[114.479887,26.839989],[114.48371,26.828681],[114.481105,26.822847],[114.468648,26.822688],[114.457976,26.816356],[114.453501,26.812254],[114.451589,26.813589],[114.446064,26.80636],[114.439426,26.802039],[114.436421,26.805046],[114.431127,26.803533],[114.426716,26.79943],[114.427073,26.794691],[114.43264,26.790827],[114.430434,26.784354],[114.433459,26.778658],[114.437304,26.776706],[114.435518,26.771905],[114.438417,26.771089],[114.449677,26.77563],[114.460328,26.768459],[114.469152,26.766348],[114.47995,26.759157],[114.48142,26.751945],[114.476505,26.748379],[114.480517,26.743198],[114.479635,26.739592],[114.475244,26.736982],[114.474593,26.742521],[114.467408,26.743995],[114.46537,26.75071],[114.458312,26.753598],[114.461064,26.744812],[114.466043,26.740967],[114.467555,26.735428],[114.466022,26.730048],[114.462891,26.729609],[114.459845,26.73485],[114.456715,26.728653],[114.451778,26.729171],[114.449425,26.732857],[114.444236,26.730347],[114.446547,26.718609],[114.438753,26.719088],[114.439762,26.712132],[114.431758,26.714325],[114.427619,26.71841],[114.42577,26.716557],[114.427745,26.709621],[114.431043,26.706751],[114.434489,26.697842]]]]}},{"type":"Feature","properties":{"adcode":360827,"name":"遂川县","center":[114.51689,26.323705],"centroid":[114.364445,26.338219],"childrenNum":0,"level":"district","parent":{"adcode":360800},"subFeatureIndex":8,"acroutes":[100000,360000,360800]},"geometry":{"type":"MultiPolygon","coordinates":[[[[114.088656,26.411478],[114.085442,26.406263],[114.077543,26.406183],[114.076472,26.404165],[114.071682,26.40984],[114.062523,26.406103],[114.057649,26.398789],[114.049897,26.391115],[114.042985,26.390255],[114.042313,26.385878],[114.036284,26.377324],[114.03057,26.376624],[114.031872,26.369289],[114.039666,26.361413],[114.037523,26.354476],[114.039771,26.345679],[114.047586,26.337381],[114.038511,26.329903],[114.035632,26.323404],[114.038868,26.312125],[114.041788,26.309545],[114.033931,26.304225],[114.025738,26.296805],[114.021599,26.288544],[114.021221,26.283043],[114.025402,26.270239],[114.029309,26.266378],[114.016767,26.259255],[114.011263,26.258495],[114.002713,26.251352],[113.989121,26.24669],[113.978911,26.237745],[113.980928,26.231881],[113.980193,26.226457],[113.971537,26.219332],[113.973533,26.208263],[113.970067,26.202118],[113.961517,26.197113],[113.949479,26.192669],[113.946118,26.184521],[113.95179,26.177873],[113.95116,26.169584],[113.944626,26.1661],[113.9445,26.163757],[113.955445,26.156087],[113.957609,26.151882],[113.962672,26.15084],[113.966495,26.154125],[113.972168,26.154745],[113.985466,26.16638],[113.992104,26.167822],[114.003848,26.176552],[114.005822,26.180677],[114.0087,26.179555],[114.013385,26.18408],[114.02454,26.179876],[114.027902,26.180036],[114.036368,26.188565],[114.042103,26.18346],[114.040947,26.177393],[114.055359,26.181337],[114.058972,26.172527],[114.059119,26.16616],[114.070401,26.171126],[114.074686,26.171767],[114.080947,26.168022],[114.088593,26.168423],[114.094034,26.176392],[114.100358,26.178414],[114.099644,26.182398],[114.102816,26.187824],[114.116807,26.191788],[114.128151,26.196993],[114.136155,26.198475],[114.141933,26.203379],[114.153235,26.2044],[114.165503,26.209885],[114.181301,26.214508],[114.183738,26.201077],[114.186616,26.197153],[114.192267,26.196733],[114.211616,26.202939],[114.216784,26.203139],[114.22212,26.188865],[114.230565,26.185161],[114.232204,26.179936],[114.218485,26.168523],[114.21941,26.163957],[114.225376,26.159672],[114.235691,26.160072],[114.237266,26.152162],[114.225313,26.147716],[114.214977,26.137462],[114.204809,26.133115],[114.188906,26.121297],[114.182457,26.121237],[114.171071,26.12324],[114.162415,26.120275],[114.156701,26.115808],[114.129013,26.105971],[114.108761,26.100282],[114.10748,26.095152],[114.11895,26.089903],[114.12021,26.084212],[114.116597,26.081567],[114.114937,26.076357],[114.109412,26.073651],[114.106912,26.069463],[114.100043,26.071828],[114.085589,26.066336],[114.081745,26.067819],[114.069854,26.078541],[114.05788,26.075976],[114.052502,26.078561],[114.043805,26.076677],[114.046052,26.070846],[114.037355,26.063691],[114.035212,26.059001],[114.036599,26.046794],[114.03141,26.040961],[114.028091,26.031179],[114.018847,26.025325],[114.008469,26.015661],[114.007167,26.012413],[114.009057,26.004192],[114.017061,25.996511],[114.017587,25.992922],[114.024015,25.992922],[114.028217,25.98139],[114.021074,25.975233],[114.020717,25.972184],[114.02828,25.976055],[114.039015,25.986805],[114.054519,25.990876],[114.06956,25.989673],[114.075632,25.986665],[114.082816,25.99222],[114.096387,25.99212],[114.105064,25.990716],[114.11082,25.996993],[114.122269,26.004573],[114.129013,26.001685],[114.133613,26.002387],[114.150861,26.011631],[114.151554,26.018508],[114.161302,26.025365],[114.165104,26.025024],[114.178192,26.028572],[114.183171,26.027169],[114.190104,26.029314],[114.201931,26.025806],[114.215565,26.020132],[114.221363,26.018869],[114.231069,26.026688],[114.239031,26.02771],[114.242518,26.025645],[114.257707,26.026187],[114.269808,26.029575],[114.277392,26.035529],[114.281782,26.03188],[114.293042,26.034907],[114.294513,26.039698],[114.291992,26.048538],[114.294618,26.052908],[114.300752,26.055393],[114.308546,26.061747],[114.313273,26.067579],[114.333483,26.076497],[114.335899,26.081607],[114.344575,26.08836],[114.356949,26.090844],[114.364827,26.102205],[114.368482,26.110199],[114.375057,26.116509],[114.383524,26.116249],[114.391465,26.112182],[114.401821,26.113685],[114.405708,26.109498],[114.412073,26.107154],[114.422283,26.108876],[114.426317,26.107835],[114.424321,26.09926],[114.42785,26.090143],[114.428712,26.082048],[114.431001,26.085074],[114.44056,26.103988],[114.445035,26.110459],[114.456547,26.114366],[114.456673,26.125924],[114.452808,26.142869],[114.455791,26.145874],[114.4664,26.144932],[114.480706,26.152162],[114.486084,26.161795],[114.495433,26.169864],[114.502323,26.171466],[114.505369,26.176912],[114.513773,26.185362],[114.516273,26.191548],[114.520033,26.192729],[114.522806,26.207503],[114.518457,26.213247],[114.523856,26.213668],[114.527365,26.218191],[114.532911,26.220513],[114.5381,26.215149],[114.545411,26.214128],[114.54999,26.219112],[114.552238,26.226317],[114.565305,26.228919],[114.576313,26.235563],[114.583246,26.235743],[114.587342,26.232301],[114.594527,26.232201],[114.598645,26.224696],[114.60751,26.223175],[114.610031,26.21613],[114.606544,26.206602],[114.60646,26.201577],[114.614506,26.200016],[114.619379,26.202038],[114.632614,26.203399],[114.636018,26.202338],[114.641795,26.191348],[114.644568,26.191107],[114.653979,26.198495],[114.668958,26.194351],[114.670702,26.20494],[114.676689,26.208724],[114.678517,26.21683],[114.686227,26.218532],[114.687865,26.225176],[114.684903,26.228639],[114.687361,26.234823],[114.687151,26.239806],[114.69484,26.243968],[114.702004,26.245129],[114.71047,26.251372],[114.711604,26.267158],[114.719944,26.275561],[114.722402,26.279982],[114.730679,26.284043],[114.732297,26.288604],[114.728999,26.292624],[114.72986,26.295685],[114.736835,26.300905],[114.738767,26.307585],[114.742255,26.308085],[114.748116,26.303945],[114.749061,26.314385],[114.762422,26.326364],[114.764754,26.332822],[114.758473,26.3424],[114.765952,26.365531],[114.76425,26.371427],[114.77154,26.378783],[114.771099,26.385819],[114.768725,26.388677],[114.756057,26.395332],[114.745406,26.394093],[114.735091,26.389596],[114.727528,26.389436],[114.716457,26.38302],[114.705365,26.384999],[114.700176,26.389376],[114.689126,26.391814],[114.6865,26.400468],[114.679546,26.404105],[114.670681,26.404604],[114.665135,26.408821],[114.653244,26.408321],[114.651501,26.412617],[114.654274,26.42007],[114.659841,26.425844],[114.667214,26.429121],[114.668496,26.433976],[114.667046,26.4478],[114.657824,26.450536],[114.654295,26.454371],[114.656837,26.460563],[114.655072,26.464957],[114.644253,26.467993],[114.633056,26.486107],[114.623728,26.489561],[114.61398,26.482712],[114.603876,26.478658],[114.600304,26.487484],[114.591418,26.480515],[114.584401,26.47728],[114.578519,26.471948],[114.565557,26.478878],[114.564864,26.483031],[114.560809,26.484968],[114.556839,26.491039],[114.548688,26.489801],[114.540285,26.490679],[114.538919,26.493774],[114.543436,26.502779],[114.53768,26.514319],[114.53144,26.517993],[114.526629,26.523423],[114.521146,26.523862],[114.51873,26.528154],[114.521714,26.537096],[114.520789,26.54027],[114.526041,26.546876],[114.531167,26.545339],[114.538478,26.552225],[114.539612,26.558432],[114.543394,26.565177],[114.559591,26.572799],[114.561524,26.575992],[114.563162,26.585928],[114.566523,26.589978],[114.563982,26.59742],[114.568834,26.602347],[114.568561,26.607374],[114.573225,26.616131],[114.57041,26.62036],[114.571523,26.625186],[114.568834,26.629434],[114.56812,26.635876],[114.571902,26.643295],[114.550284,26.649437],[114.544108,26.657652],[114.52747,26.670751],[114.522134,26.672286],[114.521735,26.678407],[114.517764,26.681676],[114.513794,26.692699],[114.515747,26.699596],[114.517008,26.707828],[114.508374,26.711176],[114.498731,26.703144],[114.492071,26.700911],[114.498752,26.685623],[114.499718,26.675915],[114.488626,26.674938],[114.486063,26.682792],[114.481147,26.687756],[114.475727,26.688434],[114.469572,26.697025],[114.456421,26.702526],[114.448585,26.702606],[114.434489,26.697842],[114.420981,26.690806],[114.404342,26.687437],[114.390372,26.692759],[114.380814,26.692181],[114.372347,26.695211],[114.363083,26.693895],[114.352915,26.681556],[114.340667,26.673861],[114.333798,26.67101],[114.326487,26.670392],[114.326256,26.673722],[114.31947,26.683789],[114.313714,26.681516],[114.306277,26.681457],[114.295584,26.688733],[114.283148,26.683769],[114.279177,26.676931],[114.274534,26.673981],[114.274808,26.664112],[114.265186,26.643235],[114.278841,26.624688],[114.281068,26.619422],[114.286782,26.614795],[114.286908,26.609927],[114.290395,26.606656],[114.284975,26.597739],[114.286803,26.59315],[114.278274,26.582077],[114.255018,26.57687],[114.248884,26.568389],[114.239997,26.562343],[114.2288,26.560986],[114.224115,26.558352],[114.221679,26.550489],[114.222414,26.538713],[114.227813,26.537316],[114.233107,26.538793],[114.243737,26.536358],[114.266993,26.526796],[114.267497,26.522105],[114.280333,26.520887],[114.287118,26.511025],[114.290774,26.510546],[114.294576,26.506333],[114.29632,26.498307],[114.307139,26.497149],[114.307874,26.492716],[114.297979,26.479756],[114.28674,26.478957],[114.279871,26.476621],[114.286404,26.4632],[114.273358,26.459545],[114.269577,26.450097],[114.255312,26.453433],[114.254157,26.449797],[114.258863,26.446561],[114.260921,26.437991],[114.26985,26.43873],[114.273631,26.436813],[114.279366,26.43923],[114.292013,26.436533],[114.295563,26.429261],[114.302979,26.428841],[114.309786,26.423627],[114.309303,26.414096],[114.304072,26.412337],[114.299366,26.404984],[114.28632,26.401567],[114.276488,26.393313],[114.275837,26.388317],[114.271341,26.385599],[114.270249,26.380762],[114.266299,26.377204],[114.258001,26.381481],[114.249262,26.380722],[114.251237,26.373846],[114.248905,26.367949],[114.239535,26.368289],[114.235271,26.365211],[114.226111,26.364191],[114.223233,26.368129],[114.211091,26.372647],[114.200881,26.368529],[114.192667,26.373106],[114.188108,26.381841],[114.17857,26.382381],[114.168003,26.386998],[114.166617,26.393373],[114.167394,26.402466],[114.165987,26.408701],[114.162436,26.412397],[114.160398,26.419591],[114.156533,26.422907],[114.148991,26.422987],[114.145063,26.426184],[114.125882,26.424386],[114.118677,26.422048],[114.107522,26.421189],[114.094959,26.413436],[114.088656,26.411478]]]]}},{"type":"Feature","properties":{"adcode":360828,"name":"万安县","center":[114.784694,26.462085],"centroid":[114.818047,26.439294],"childrenNum":0,"level":"district","parent":{"adcode":360800},"subFeatureIndex":9,"acroutes":[100000,360000,360800]},"geometry":{"type":"MultiPolygon","coordinates":[[[[114.668958,26.194351],[114.667193,26.185101],[114.677046,26.181497],[114.685617,26.181958],[114.698327,26.169344],[114.714797,26.162555],[114.718516,26.162355],[114.71881,26.16644],[114.726079,26.174129],[114.728179,26.1842],[114.739755,26.185942],[114.753305,26.180316],[114.757759,26.181558],[114.761834,26.177173],[114.766015,26.176132],[114.765742,26.182378],[114.772569,26.192108],[114.777401,26.192289],[114.782317,26.187324],[114.78906,26.188865],[114.798535,26.18342],[114.817442,26.182599],[114.820593,26.18382],[114.825782,26.190307],[114.834984,26.189426],[114.837715,26.19383],[114.842757,26.192809],[114.847504,26.198535],[114.851559,26.199195],[114.862189,26.19337],[114.860466,26.185101],[114.866201,26.186883],[114.871243,26.184681],[114.878974,26.193089],[114.884142,26.195652],[114.889772,26.203679],[114.902923,26.202078],[114.905612,26.205621],[114.904604,26.209564],[114.908995,26.211786],[114.91912,26.208724],[114.930507,26.208263],[114.935822,26.205181],[114.939036,26.199656],[114.945086,26.201657],[114.962397,26.197754],[114.974203,26.201437],[114.976682,26.197914],[114.984686,26.195932],[114.997312,26.198114],[115.005043,26.19357],[115.008551,26.189025],[115.01082,26.17461],[115.009791,26.170185],[115.013845,26.168923],[115.021555,26.175471],[115.028446,26.160493],[115.030966,26.158831],[115.039013,26.160032],[115.046428,26.157249],[115.054138,26.147996],[115.058802,26.147316],[115.063823,26.14343],[115.078297,26.144532],[115.08418,26.149418],[115.090587,26.150159],[115.09523,26.154205],[115.098234,26.160773],[115.099116,26.167862],[115.086406,26.172407],[115.087247,26.178514],[115.084011,26.180156],[115.081596,26.187023],[115.070482,26.188124],[115.065188,26.19325],[115.046008,26.195972],[115.04647,26.200056],[115.051449,26.203379],[115.051344,26.207503],[115.047206,26.211746],[115.051554,26.219632],[115.064033,26.229299],[115.070356,26.2303],[115.078591,26.24735],[115.079159,26.256274],[115.086995,26.260596],[115.091133,26.266938],[115.078717,26.281162],[115.071344,26.286664],[115.069873,26.296005],[115.070861,26.303405],[115.074432,26.308245],[115.072835,26.314845],[115.067415,26.319705],[115.067478,26.323964],[115.06063,26.327603],[115.059222,26.331623],[115.050252,26.339761],[115.045378,26.34094],[115.033403,26.355055],[115.026534,26.359453],[115.034433,26.364191],[115.03708,26.367689],[115.035147,26.379263],[115.031681,26.382541],[115.032752,26.392834],[115.043487,26.39799],[115.043781,26.403205],[115.038466,26.406742],[115.037794,26.410059],[115.041723,26.415395],[115.044643,26.426264],[115.054768,26.431159],[115.058613,26.436773],[115.059012,26.442626],[115.0621,26.447979],[115.051785,26.454351],[115.047794,26.454471],[115.045945,26.449757],[115.042227,26.449697],[115.039223,26.454851],[115.034916,26.456708],[115.028614,26.465177],[115.020063,26.472946],[115.017521,26.480236],[115.014328,26.482872],[115.014349,26.487444],[115.020694,26.488503],[115.02813,26.49619],[115.028383,26.498367],[115.017353,26.508869],[115.011765,26.511125],[115.00624,26.518751],[114.998236,26.523203],[114.990211,26.521806],[114.987669,26.523403],[114.987732,26.532046],[114.981241,26.539511],[114.965233,26.531547],[114.956746,26.539711],[114.948363,26.542006],[114.940843,26.548114],[114.940548,26.552165],[114.929036,26.548992],[114.923574,26.556975],[114.92475,26.561585],[114.923658,26.571163],[114.920402,26.578386],[114.920297,26.583953],[114.912104,26.586826],[114.913511,26.594627],[114.918385,26.600372],[114.911494,26.608132],[114.882167,26.622992],[114.880739,26.633523],[114.878491,26.638489],[114.866643,26.649357],[114.87305,26.657971],[114.879646,26.65825],[114.887293,26.660922],[114.890655,26.670532],[114.890339,26.674898],[114.879373,26.68335],[114.878869,26.692799],[114.873911,26.693836],[114.873743,26.68662],[114.867336,26.682354],[114.8645,26.685663],[114.866811,26.694852],[114.86305,26.697025],[114.853471,26.69828],[114.847819,26.702645],[114.839836,26.700314],[114.8299,26.699417],[114.831349,26.684327],[114.829942,26.678646],[114.821013,26.668458],[114.812379,26.657074],[114.802884,26.655179],[114.788556,26.662058],[114.78927,26.666066],[114.784985,26.669475],[114.78238,26.68028],[114.77904,26.683629],[114.770195,26.685743],[114.766876,26.691842],[114.76841,26.707469],[114.766288,26.709103],[114.758389,26.708505],[114.754271,26.703941],[114.745679,26.698181],[114.744061,26.709342],[114.73736,26.711634],[114.729314,26.704838],[114.723285,26.702087],[114.724482,26.690447],[114.718747,26.685942],[114.702928,26.669814],[114.69818,26.666764],[114.69713,26.655339],[114.69005,26.659765],[114.687298,26.658469],[114.684063,26.662178],[114.674651,26.657153],[114.66858,26.658928],[114.656185,26.671947],[114.653013,26.677968],[114.640471,26.681277],[114.642908,26.685424],[114.644442,26.694593],[114.652152,26.699975],[114.637299,26.70434],[114.6294,26.70422],[114.624484,26.711455],[114.618266,26.714325],[114.615241,26.720582],[114.614632,26.72939],[114.61043,26.730665],[114.598687,26.725106],[114.59583,26.72939],[114.587027,26.725405],[114.580893,26.716776],[114.573708,26.71572],[114.57041,26.711275],[114.558667,26.706731],[114.55728,26.699217],[114.550557,26.683709],[114.547175,26.683789],[114.541797,26.690527],[114.533898,26.693696],[114.527848,26.68963],[114.521483,26.693377],[114.520285,26.697324],[114.515747,26.699596],[114.513794,26.692699],[114.517764,26.681676],[114.521735,26.678407],[114.522134,26.672286],[114.52747,26.670751],[114.544108,26.657652],[114.550284,26.649437],[114.571902,26.643295],[114.56812,26.635876],[114.568834,26.629434],[114.571523,26.625186],[114.57041,26.62036],[114.573225,26.616131],[114.568561,26.607374],[114.568834,26.602347],[114.563982,26.59742],[114.566523,26.589978],[114.563162,26.585928],[114.561524,26.575992],[114.559591,26.572799],[114.543394,26.565177],[114.539612,26.558432],[114.538478,26.552225],[114.531167,26.545339],[114.526041,26.546876],[114.520789,26.54027],[114.521714,26.537096],[114.51873,26.528154],[114.521146,26.523862],[114.526629,26.523423],[114.53144,26.517993],[114.53768,26.514319],[114.543436,26.502779],[114.538919,26.493774],[114.540285,26.490679],[114.548688,26.489801],[114.556839,26.491039],[114.560809,26.484968],[114.564864,26.483031],[114.565557,26.478878],[114.578519,26.471948],[114.584401,26.47728],[114.591418,26.480515],[114.600304,26.487484],[114.603876,26.478658],[114.61398,26.482712],[114.623728,26.489561],[114.633056,26.486107],[114.644253,26.467993],[114.655072,26.464957],[114.656837,26.460563],[114.654295,26.454371],[114.657824,26.450536],[114.667046,26.4478],[114.668496,26.433976],[114.667214,26.429121],[114.659841,26.425844],[114.654274,26.42007],[114.651501,26.412617],[114.653244,26.408321],[114.665135,26.408821],[114.670681,26.404604],[114.679546,26.404105],[114.6865,26.400468],[114.689126,26.391814],[114.700176,26.389376],[114.705365,26.384999],[114.716457,26.38302],[114.727528,26.389436],[114.735091,26.389596],[114.745406,26.394093],[114.756057,26.395332],[114.768725,26.388677],[114.771099,26.385819],[114.77154,26.378783],[114.76425,26.371427],[114.765952,26.365531],[114.758473,26.3424],[114.764754,26.332822],[114.762422,26.326364],[114.749061,26.314385],[114.748116,26.303945],[114.742255,26.308085],[114.738767,26.307585],[114.736835,26.300905],[114.72986,26.295685],[114.728999,26.292624],[114.732297,26.288604],[114.730679,26.284043],[114.722402,26.279982],[114.719944,26.275561],[114.711604,26.267158],[114.71047,26.251372],[114.702004,26.245129],[114.69484,26.243968],[114.687151,26.239806],[114.687361,26.234823],[114.684903,26.228639],[114.687865,26.225176],[114.686227,26.218532],[114.678517,26.21683],[114.676689,26.208724],[114.670702,26.20494],[114.668958,26.194351]]]]}},{"type":"Feature","properties":{"adcode":360829,"name":"安福县","center":[114.61384,27.382746],"centroid":[114.449024,27.35538],"childrenNum":0,"level":"district","parent":{"adcode":360800},"subFeatureIndex":10,"acroutes":[100000,360000,360800]},"geometry":{"type":"MultiPolygon","coordinates":[[[[114.737045,27.589449],[114.73257,27.594116],[114.730469,27.600186],[114.717549,27.597319],[114.710911,27.597319],[114.700596,27.593938],[114.692466,27.594511],[114.678034,27.590497],[114.670828,27.585059],[114.667761,27.576477],[114.667004,27.567696],[114.660639,27.563938],[114.659652,27.559349],[114.655492,27.554187],[114.651837,27.542298],[114.634736,27.530231],[114.623497,27.535236],[114.6194,27.542833],[114.620115,27.550152],[114.605997,27.551833],[114.601817,27.549143],[114.593666,27.555611],[114.594653,27.561189],[114.590704,27.564828],[114.584359,27.563305],[114.572994,27.565026],[114.554045,27.561703],[114.546566,27.559468],[114.540138,27.559943],[114.533268,27.56734],[114.534949,27.576674],[114.534928,27.586107],[114.529949,27.591189],[114.530915,27.601174],[114.524634,27.606097],[114.517596,27.607442],[114.505916,27.599553],[114.502806,27.595539],[114.503752,27.585534],[114.499823,27.580214],[114.493794,27.580926],[114.483899,27.575745],[114.471925,27.564353],[114.467156,27.563325],[114.464593,27.557866],[114.462177,27.561861],[114.456442,27.562138],[114.453585,27.567201],[114.449509,27.568348],[114.440959,27.564096],[114.429468,27.561881],[114.424699,27.559152],[114.421107,27.550785],[114.416947,27.548174],[114.405645,27.550547],[114.386948,27.549459],[114.385141,27.555967],[114.378902,27.557727],[114.375141,27.55573],[114.372789,27.54665],[114.3651,27.546749],[114.358608,27.553297],[114.355016,27.560022],[114.34905,27.564057],[114.334764,27.557332],[114.333525,27.559547],[114.308756,27.575745],[114.299996,27.578177],[114.283967,27.578988],[114.265018,27.569713],[114.261384,27.563285],[114.253548,27.562158],[114.247602,27.557352],[114.240376,27.558182],[114.233065,27.552525],[114.22983,27.552446],[114.226111,27.547679],[114.226237,27.539272],[114.222939,27.530369],[114.21899,27.525621],[114.208402,27.521327],[114.202204,27.520378],[114.198129,27.505161],[114.204998,27.49681],[114.20254,27.494554],[114.202729,27.487547],[114.19607,27.480204],[114.195398,27.473375],[114.186763,27.471178],[114.181553,27.466764],[114.178633,27.45736],[114.177898,27.450134],[114.173486,27.444808],[114.161785,27.439165],[114.155924,27.439224],[114.148193,27.432334],[114.140777,27.430532],[114.133319,27.431878],[114.129517,27.429601],[114.122143,27.429482],[114.112858,27.436848],[114.097501,27.434136],[114.089959,27.430076],[114.080821,27.428254],[114.070653,27.435779],[114.059539,27.430749],[114.053699,27.433502],[114.046914,27.432492],[114.041326,27.434215],[114.029981,27.426215],[114.030654,27.417026],[114.026935,27.405697],[114.023049,27.402844],[114.026284,27.396149],[114.021788,27.391633],[114.023532,27.387354],[114.031956,27.379668],[114.038679,27.381352],[114.049519,27.375765],[114.047901,27.371901],[114.051262,27.362926],[114.054561,27.360013],[114.066997,27.356506],[114.07435,27.353216],[114.071325,27.348995],[114.071094,27.340632],[114.074098,27.335915],[114.080652,27.335123],[114.082669,27.332566],[114.095001,27.331951],[114.100631,27.329414],[114.106282,27.319464],[114.116744,27.309751],[114.120736,27.30985],[114.136512,27.317304],[114.142248,27.318592],[114.144559,27.314271],[114.138865,27.303348],[114.140588,27.295536],[114.144832,27.289311],[114.147857,27.276382],[114.148487,27.265277],[114.157836,27.253159],[114.157625,27.242845],[114.142542,27.231101],[114.127815,27.221936],[114.139664,27.207889],[114.144685,27.204376],[114.143025,27.199277],[114.14563,27.194772],[114.16002,27.192986],[114.171701,27.200269],[114.184936,27.199039],[114.191469,27.207849],[114.203276,27.215448],[114.210754,27.215944],[114.22338,27.207631],[114.226784,27.210805],[114.233695,27.210329],[114.248674,27.205011],[114.263841,27.204059],[114.277097,27.204873],[114.279997,27.207571],[114.282412,27.218345],[114.286383,27.219615],[114.294639,27.208662],[114.302874,27.204575],[114.30924,27.204396],[114.312958,27.211143],[114.325752,27.208047],[114.332369,27.20015],[114.337432,27.186973],[114.337957,27.178717],[114.341781,27.168555],[114.350835,27.154957],[114.361381,27.149677],[114.367558,27.140544],[114.378377,27.129981],[114.384742,27.126784],[114.392179,27.126328],[114.399448,27.121522],[114.397263,27.116339],[114.399343,27.104384],[114.404658,27.098227],[114.412935,27.09491],[114.414699,27.088852],[114.427115,27.066643],[114.431001,27.066086],[114.441589,27.069186],[114.455119,27.069007],[114.462744,27.067159],[114.469971,27.072007],[114.474992,27.073179],[114.483752,27.071888],[114.49058,27.069285],[114.494403,27.071689],[114.510621,27.075761],[114.513037,27.07749],[114.524045,27.079754],[114.529508,27.084303],[114.540327,27.087382],[114.546041,27.090838],[114.556125,27.090242],[114.561839,27.096737],[114.578099,27.118822],[114.590115,27.10925],[114.589317,27.106946],[114.599968,27.102319],[114.611375,27.110223],[114.617951,27.111593],[114.62835,27.118524],[114.628182,27.125514],[114.634589,27.126824],[114.636165,27.129803],[114.629757,27.137447],[114.637194,27.14793],[114.631921,27.1546],[114.631942,27.1652],[114.630556,27.1705],[114.625661,27.175125],[114.618371,27.190625],[114.622552,27.196975],[114.623245,27.203007],[114.618602,27.212928],[114.626186,27.21771],[114.632005,27.217948],[114.637047,27.223146],[114.642362,27.221361],[114.646396,27.215746],[114.653937,27.210389],[114.662299,27.211976],[114.666647,27.215131],[114.671185,27.213782],[114.675744,27.215607],[114.680807,27.214496],[114.682319,27.218781],[114.679273,27.222829],[114.681038,27.225765],[114.689126,27.222174],[114.692172,27.22511],[114.700764,27.22761],[114.704798,27.238005],[114.713537,27.239869],[114.715028,27.246415],[114.729503,27.251493],[114.733789,27.253972],[114.734482,27.259783],[114.739566,27.262044],[114.746729,27.268192],[114.748599,27.271682],[114.745805,27.277255],[114.747024,27.286892],[114.758683,27.297697],[114.761267,27.303526],[114.771141,27.312169],[114.784313,27.315024],[114.786077,27.320852],[114.789544,27.32331],[114.790237,27.328859],[114.785867,27.335123],[114.785258,27.339225],[114.791497,27.350164],[114.797842,27.357259],[114.797758,27.361598],[114.787905,27.368692],[114.782716,27.380817],[114.789922,27.386621],[114.790279,27.390405],[114.784292,27.402151],[114.784081,27.42164],[114.781287,27.42867],[114.781561,27.436809],[114.767548,27.449718],[114.762737,27.450787],[114.759733,27.456588],[114.7532,27.462824],[114.75257,27.466902],[114.747024,27.474286],[114.749418,27.48155],[114.740553,27.486479],[114.735385,27.486874],[114.737948,27.494633],[114.733452,27.502529],[114.722213,27.509554],[114.72215,27.5127],[114.729377,27.516203],[114.733284,27.52103],[114.733347,27.530666],[114.736814,27.533396],[114.747822,27.537669],[114.748767,27.54396],[114.744334,27.549182],[114.74549,27.565243],[114.742906,27.570069],[114.742402,27.580214],[114.737045,27.589449]]]]}},{"type":"Feature","properties":{"adcode":360830,"name":"永新县","center":[114.242534,26.944721],"centroid":[114.18396,26.968921],"childrenNum":0,"level":"district","parent":{"adcode":360800},"subFeatureIndex":11,"acroutes":[100000,360000,360800]},"geometry":{"type":"MultiPolygon","coordinates":[[[[114.455119,27.069007],[114.441589,27.069186],[114.431001,27.066086],[114.427115,27.066643],[114.414699,27.088852],[114.412935,27.09491],[114.404658,27.098227],[114.399343,27.104384],[114.397263,27.116339],[114.399448,27.121522],[114.392179,27.126328],[114.384742,27.126784],[114.378377,27.129981],[114.367558,27.140544],[114.361381,27.149677],[114.350835,27.154957],[114.341781,27.168555],[114.337957,27.178717],[114.337432,27.186973],[114.332369,27.20015],[114.325752,27.208047],[114.312958,27.211143],[114.30924,27.204396],[114.302874,27.204575],[114.294639,27.208662],[114.286383,27.219615],[114.282412,27.218345],[114.279997,27.207571],[114.277097,27.204873],[114.263841,27.204059],[114.248674,27.205011],[114.233695,27.210329],[114.226784,27.210805],[114.22338,27.207631],[114.210754,27.215944],[114.203276,27.215448],[114.191469,27.207849],[114.184936,27.199039],[114.171701,27.200269],[114.16002,27.192986],[114.14563,27.194772],[114.143025,27.199277],[114.144685,27.204376],[114.139664,27.207889],[114.127815,27.221936],[114.122815,27.218266],[114.114433,27.223483],[114.109706,27.221738],[114.095379,27.209],[114.094476,27.205091],[114.097921,27.199634],[114.097312,27.19636],[114.091303,27.190922],[114.085988,27.168932],[114.078951,27.162679],[114.076703,27.155771],[114.073216,27.153508],[114.058426,27.157855],[114.049372,27.158213],[114.037418,27.150113],[114.013427,27.122595],[114.00912,27.113142],[114.003974,27.094751],[113.999163,27.091593],[113.995843,27.085495],[113.994856,27.076735],[114.001095,27.068709],[113.997713,27.063961],[113.997734,27.057762],[113.993638,27.053232],[113.98786,27.051245],[113.981705,27.044369],[113.979373,27.036202],[113.975424,27.033718],[113.966579,27.033639],[113.959836,27.021416],[113.959269,27.009053],[113.963113,27.001837],[113.961201,26.999034],[113.948324,26.998557],[113.946853,26.994502],[113.939479,26.990486],[113.936706,26.984899],[113.932715,26.981977],[113.921412,26.960064],[113.921265,26.954516],[113.926854,26.949166],[113.92637,26.947158],[113.917022,26.93771],[113.910635,26.934249],[113.90219,26.925835],[113.901035,26.917878],[113.903031,26.910637],[113.896938,26.901326],[113.891392,26.897844],[113.890174,26.889905],[113.891308,26.884552],[113.888619,26.880055],[113.891161,26.874423],[113.892085,26.860531],[113.886413,26.861188],[113.877443,26.859476],[113.86801,26.847991],[113.859019,26.833797],[113.857948,26.826829],[113.835763,26.80636],[113.834587,26.80409],[113.836772,26.796144],[113.842528,26.789572],[113.861372,26.807456],[113.868998,26.805962],[113.873262,26.798992],[113.884102,26.795547],[113.886875,26.798395],[113.89698,26.796483],[113.909606,26.79957],[113.926643,26.807197],[113.930677,26.804508],[113.934206,26.7972],[113.93824,26.794571],[113.942021,26.786844],[113.951937,26.779614],[113.963638,26.778259],[113.969962,26.784055],[113.975991,26.78547],[113.991096,26.784255],[113.994646,26.781327],[113.996495,26.774555],[114.005864,26.774734],[114.017398,26.780789],[114.020801,26.788357],[114.030927,26.791544],[114.036116,26.789672],[114.044036,26.794969],[114.045464,26.811657],[114.049792,26.813549],[114.050086,26.806898],[114.055338,26.802298],[114.063699,26.801521],[114.074455,26.792878],[114.083131,26.793058],[114.089833,26.7971],[114.097963,26.793376],[114.108236,26.784514],[114.117521,26.782801],[114.125378,26.777582],[114.123278,26.766687],[114.135903,26.760153],[114.13979,26.756268],[114.146911,26.754993],[114.153802,26.747801],[114.160903,26.746287],[114.170713,26.748677],[114.176932,26.746585],[114.177625,26.743258],[114.173444,26.737619],[114.192625,26.722595],[114.203024,26.720781],[114.211385,26.733734],[114.221153,26.738138],[114.222246,26.741246],[114.217351,26.758977],[114.223926,26.768619],[114.232771,26.771268],[114.236048,26.776805],[114.253022,26.77822],[114.257896,26.775829],[114.266551,26.776268],[114.27298,26.76838],[114.277139,26.771427],[114.286047,26.773997],[114.291887,26.782303],[114.296719,26.784573],[114.307517,26.783],[114.312412,26.78545],[114.32321,26.787382],[114.338399,26.793456],[114.350856,26.79722],[114.362222,26.797857],[114.365352,26.79943],[114.367894,26.809068],[114.376087,26.815719],[114.385309,26.817372],[114.393628,26.826092],[114.404069,26.830293],[114.409615,26.830154],[114.41367,26.83324],[114.421422,26.828541],[114.426212,26.830572],[114.436064,26.827287],[114.443333,26.82004],[114.446925,26.821035],[114.452661,26.817232],[114.457976,26.816356],[114.468648,26.822688],[114.481105,26.822847],[114.48371,26.828681],[114.479887,26.839989],[114.472156,26.844945],[114.45766,26.847772],[114.451253,26.854082],[114.446106,26.855774],[114.445875,26.864253],[114.439909,26.876413],[114.433522,26.882363],[114.430518,26.891735],[114.430371,26.899973],[114.42201,26.906777],[114.420455,26.911492],[114.420813,26.919629],[114.424027,26.925676],[114.432346,26.928939],[114.433732,26.936716],[114.430854,26.940813],[114.431254,26.947456],[114.428249,26.951454],[114.418985,26.956624],[114.41909,26.959547],[114.426296,26.959826],[114.436863,26.962212],[114.447934,26.961596],[114.455665,26.968734],[114.457429,26.980068],[114.469236,26.990963],[114.478164,27.004819],[114.481462,27.006847],[114.476547,27.009689],[114.475475,27.014837],[114.471463,27.021297],[114.45474,27.029882],[114.45306,27.036083],[114.447976,27.04578],[114.447451,27.049476],[114.452409,27.057384],[114.455119,27.069007]]]]}},{"type":"Feature","properties":{"adcode":360881,"name":"井冈山市","center":[114.284421,26.745919],"centroid":[114.14755,26.64284],"childrenNum":0,"level":"district","parent":{"adcode":360800},"subFeatureIndex":12,"acroutes":[100000,360000,360800]},"geometry":{"type":"MultiPolygon","coordinates":[[[[114.457976,26.816356],[114.452661,26.817232],[114.446925,26.821035],[114.443333,26.82004],[114.436064,26.827287],[114.426212,26.830572],[114.421422,26.828541],[114.41367,26.83324],[114.409615,26.830154],[114.404069,26.830293],[114.393628,26.826092],[114.385309,26.817372],[114.376087,26.815719],[114.367894,26.809068],[114.365352,26.79943],[114.362222,26.797857],[114.350856,26.79722],[114.338399,26.793456],[114.32321,26.787382],[114.312412,26.78545],[114.307517,26.783],[114.296719,26.784573],[114.291887,26.782303],[114.286047,26.773997],[114.277139,26.771427],[114.27298,26.76838],[114.266551,26.776268],[114.257896,26.775829],[114.253022,26.77822],[114.236048,26.776805],[114.232771,26.771268],[114.223926,26.768619],[114.217351,26.758977],[114.222246,26.741246],[114.221153,26.738138],[114.211385,26.733734],[114.203024,26.720781],[114.192625,26.722595],[114.173444,26.737619],[114.177625,26.743258],[114.176932,26.746585],[114.170713,26.748677],[114.160903,26.746287],[114.153802,26.747801],[114.146911,26.754993],[114.13979,26.756268],[114.135903,26.760153],[114.123278,26.766687],[114.125378,26.777582],[114.117521,26.782801],[114.108236,26.784514],[114.097963,26.793376],[114.089833,26.7971],[114.083131,26.793058],[114.074455,26.792878],[114.063699,26.801521],[114.055338,26.802298],[114.050086,26.806898],[114.049792,26.813549],[114.045464,26.811657],[114.044036,26.794969],[114.036116,26.789672],[114.030927,26.791544],[114.020801,26.788357],[114.017398,26.780789],[114.005864,26.774734],[113.996495,26.774555],[113.994646,26.781327],[113.991096,26.784255],[113.975991,26.78547],[113.969962,26.784055],[113.963638,26.778259],[113.951937,26.779614],[113.942021,26.786844],[113.93824,26.794571],[113.934206,26.7972],[113.930677,26.804508],[113.926643,26.807197],[113.909606,26.79957],[113.89698,26.796483],[113.886875,26.798395],[113.884102,26.795547],[113.873262,26.798992],[113.868998,26.805962],[113.861372,26.807456],[113.842528,26.789572],[113.845931,26.788158],[113.848704,26.777343],[113.853536,26.769416],[113.852422,26.76332],[113.855448,26.757204],[113.852864,26.750012],[113.85675,26.735627],[113.855364,26.727298],[113.856939,26.722575],[113.856456,26.714723],[113.861393,26.705954],[113.862653,26.68949],[113.861267,26.684766],[113.861792,26.670093],[113.860489,26.664092],[113.86801,26.652986],[113.87717,26.656455],[113.881476,26.654342],[113.884922,26.635796],[113.898094,26.63091],[113.90135,26.627899],[113.90364,26.618804],[113.906455,26.615672],[113.915467,26.613957],[113.924543,26.615273],[113.934773,26.613019],[113.953155,26.613717],[113.960004,26.618106],[113.970256,26.61663],[113.973449,26.612461],[113.976894,26.616211],[113.984415,26.614575],[113.99639,26.615592],[114.01286,26.602088],[114.017692,26.599614],[114.015423,26.593829],[114.019751,26.587444],[114.033091,26.585609],[114.042565,26.579923],[114.060884,26.580641],[114.069665,26.575832],[114.078867,26.575134],[114.084686,26.577768],[114.095169,26.577189],[114.10811,26.575034],[114.108635,26.569607],[114.105904,26.559669],[114.096282,26.548273],[114.097522,26.541228],[114.086283,26.525738],[114.087501,26.521187],[114.083993,26.513621],[114.084203,26.505016],[114.078089,26.491578],[114.072228,26.487405],[114.072711,26.479896],[114.077375,26.480335],[114.086766,26.478179],[114.098068,26.48415],[114.110127,26.482991],[114.10958,26.480216],[114.09727,26.466935],[114.09603,26.461282],[114.090547,26.45573],[114.090232,26.446242],[114.086409,26.439589],[114.088951,26.434815],[114.085757,26.423447],[114.089056,26.418052],[114.088656,26.411478],[114.094959,26.413436],[114.107522,26.421189],[114.118677,26.422048],[114.125882,26.424386],[114.145063,26.426184],[114.148991,26.422987],[114.156533,26.422907],[114.160398,26.419591],[114.162436,26.412397],[114.165987,26.408701],[114.167394,26.402466],[114.166617,26.393373],[114.168003,26.386998],[114.17857,26.382381],[114.188108,26.381841],[114.192667,26.373106],[114.200881,26.368529],[114.211091,26.372647],[114.223233,26.368129],[114.226111,26.364191],[114.235271,26.365211],[114.239535,26.368289],[114.248905,26.367949],[114.251237,26.373846],[114.249262,26.380722],[114.258001,26.381481],[114.266299,26.377204],[114.270249,26.380762],[114.271341,26.385599],[114.275837,26.388317],[114.276488,26.393313],[114.28632,26.401567],[114.299366,26.404984],[114.304072,26.412337],[114.309303,26.414096],[114.309786,26.423627],[114.302979,26.428841],[114.295563,26.429261],[114.292013,26.436533],[114.279366,26.43923],[114.273631,26.436813],[114.26985,26.43873],[114.260921,26.437991],[114.258863,26.446561],[114.254157,26.449797],[114.255312,26.453433],[114.269577,26.450097],[114.273358,26.459545],[114.286404,26.4632],[114.279871,26.476621],[114.28674,26.478957],[114.297979,26.479756],[114.307874,26.492716],[114.307139,26.497149],[114.29632,26.498307],[114.294576,26.506333],[114.290774,26.510546],[114.287118,26.511025],[114.280333,26.520887],[114.267497,26.522105],[114.266993,26.526796],[114.243737,26.536358],[114.233107,26.538793],[114.227813,26.537316],[114.222414,26.538713],[114.221679,26.550489],[114.224115,26.558352],[114.2288,26.560986],[114.239997,26.562343],[114.248884,26.568389],[114.255018,26.57687],[114.278274,26.582077],[114.286803,26.59315],[114.284975,26.597739],[114.290395,26.606656],[114.286908,26.609927],[114.286782,26.614795],[114.281068,26.619422],[114.278841,26.624688],[114.265186,26.643235],[114.274808,26.664112],[114.274534,26.673981],[114.279177,26.676931],[114.283148,26.683769],[114.295584,26.688733],[114.306277,26.681457],[114.313714,26.681516],[114.31947,26.683789],[114.326256,26.673722],[114.326487,26.670392],[114.333798,26.67101],[114.340667,26.673861],[114.352915,26.681556],[114.363083,26.693895],[114.372347,26.695211],[114.380814,26.692181],[114.390372,26.692759],[114.404342,26.687437],[114.420981,26.690806],[114.434489,26.697842],[114.431043,26.706751],[114.427745,26.709621],[114.42577,26.716557],[114.427619,26.71841],[114.431758,26.714325],[114.439762,26.712132],[114.438753,26.719088],[114.446547,26.718609],[114.444236,26.730347],[114.449425,26.732857],[114.451778,26.729171],[114.456715,26.728653],[114.459845,26.73485],[114.462891,26.729609],[114.466022,26.730048],[114.467555,26.735428],[114.466043,26.740967],[114.461064,26.744812],[114.458312,26.753598],[114.46537,26.75071],[114.467408,26.743995],[114.474593,26.742521],[114.475244,26.736982],[114.479635,26.739592],[114.480517,26.743198],[114.476505,26.748379],[114.48142,26.751945],[114.47995,26.759157],[114.469152,26.766348],[114.460328,26.768459],[114.449677,26.77563],[114.438417,26.771089],[114.435518,26.771905],[114.437304,26.776706],[114.433459,26.778658],[114.430434,26.784354],[114.43264,26.790827],[114.427073,26.794691],[114.426716,26.79943],[114.431127,26.803533],[114.436421,26.805046],[114.439426,26.802039],[114.446064,26.80636],[114.451589,26.813589],[114.453501,26.812254],[114.457976,26.816356]]]]}}]}
applications/drone-command/src/assets/images/basicManage/chartBg.svg
New file
@@ -0,0 +1,132 @@
<svg width="365" height="34" viewBox="0 0 365 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 2136636843">
<g id="Group 1321318807">
<g id="Vector 482" filter="url(#filter0_f_62_4305)">
<path d="M365 15.5H167.292L150.51 31.5H12.5863L7.34204 26.5002" stroke="url(#paint0_linear_62_4305)" stroke-width="1.6"/>
</g>
<g id="Rectangle 346241148" filter="url(#filter1_f_62_4305)">
<path d="M24.1237 14.5H145.79L139.497 23.5H17.8306L24.1237 14.5Z" fill="url(#paint1_linear_62_4305)"/>
</g>
<g id="Vector 485" filter="url(#filter2_f_62_4305)">
<path d="M6.29321 8H8.39091V27H6.4756H6.29321V8Z" fill="url(#paint2_linear_62_4305)"/>
</g>
<g id="Vector 483" filter="url(#filter3_ddf_62_4305)">
<path d="M8.39078 34L1.04883 27V30.5L4.71981 34H8.39078Z" fill="white"/>
</g>
<g id="Vector 484" opacity="0.3" filter="url(#filter4_f_62_4305)">
<path d="M0 0H31.4655V2H2.24754H0V0Z" fill="url(#paint3_linear_62_4305)"/>
</g>
<g id="Union" filter="url(#filter5_dd_62_4305)">
<path d="M365 16H348.218V14H365V16Z" fill="white"/>
</g>
<path id="Union_2" d="M296.825 24H293.678V21H296.825V24Z" fill="#686B8D"/>
<path id="Union_3" d="M316.753 24H313.606V21H316.753V24Z" fill="#686B8D"/>
</g>
<g id="Group 1321318808">
<g id="Rectangle 346241141" filter="url(#filter6_f_62_4305)">
<path d="M174.109 20H182.5L173.06 30H164.669L174.109 20Z" fill="#0041FF"/>
</g>
<g id="Rectangle 346241142" filter="url(#filter7_f_62_4305)">
<path d="M188.793 20H197.184L187.744 30H179.353L188.793 20Z" fill="#0041FF" fill-opacity="0.7"/>
</g>
<g id="Rectangle 346241143" filter="url(#filter8_f_62_4305)">
<path d="M203.477 20H211.868L202.428 30H194.037L203.477 20Z" fill="#0041FF" fill-opacity="0.3"/>
</g>
<g id="Rectangle 346241144" filter="url(#filter9_f_62_4305)">
<path d="M218.161 20H226.552L217.112 30H208.721L218.161 20Z" fill="#0041FF" fill-opacity="0.16"/>
</g>
</g>
</g>
<defs>
<filter id="filter0_f_62_4305" x="5.79004" y="13.7" width="360.21" height="19.6001" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
</filter>
<filter id="filter1_f_62_4305" x="16.8306" y="13.5" width="129.96" height="11" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
</filter>
<filter id="filter2_f_62_4305" x="5.29321" y="7" width="4.09766" height="21" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
</filter>
<filter id="filter3_ddf_62_4305" x="-0.951172" y="25" width="11.342" height="11" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_62_4305"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_62_4305" result="effect2_dropShadow_62_4305"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_62_4305" result="shape"/>
<feGaussianBlur stdDeviation="0.05" result="effect3_foregroundBlur_62_4305"/>
</filter>
<filter id="filter4_f_62_4305" x="-0.1" y="-0.1" width="31.6656" height="2.2" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.05" result="effect1_foregroundBlur_62_4305"/>
</filter>
<filter id="filter5_dd_62_4305" x="342.218" y="8" width="28.7815" height="14" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="3"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_62_4305"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="3"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_62_4305" result="effect2_dropShadow_62_4305"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_62_4305" result="shape"/>
</filter>
<filter id="filter6_f_62_4305" x="163.669" y="19" width="19.8306" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
</filter>
<filter id="filter7_f_62_4305" x="178.353" y="19" width="19.8306" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
</filter>
<filter id="filter8_f_62_4305" x="193.037" y="19" width="19.8306" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
</filter>
<filter id="filter9_f_62_4305" x="207.721" y="19" width="19.8306" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_62_4305"/>
</filter>
<linearGradient id="paint0_linear_62_4305" x1="17.8305" y1="23.5" x2="428.456" y2="23.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#89A1FF"/>
<stop offset="0.159605" stop-color="#D9DFFF"/>
<stop offset="1" stop-color="#3C4986" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint1_linear_62_4305" x1="17.8306" y1="18.5" x2="139.497" y2="18.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#002AA7"/>
<stop offset="1" stop-color="#0041FF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint2_linear_62_4305" x1="7.34206" y1="8" x2="7.34206" y2="27" gradientUnits="userSpaceOnUse">
<stop stop-color="#4B598C" stop-opacity="0"/>
<stop offset="1" stop-color="#8299F2"/>
</linearGradient>
<linearGradient id="paint3_linear_62_4305" x1="31.4655" y1="1" x2="0" y2="1" gradientUnits="userSpaceOnUse">
<stop stop-color="#999999" stop-opacity="0"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
</defs>
</svg>
applications/drone-command/src/assets/images/basicManage/chartBg2.svg
New file
@@ -0,0 +1,132 @@
<svg width="618" height="34" viewBox="0 0 618 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 2136636843">
<g id="Group 1321318807">
<g id="Vector 482" filter="url(#filter0_f_63_4945)">
<path d="M617.49 15.5H162.658L146.342 31.5H12.2377L7.13867 26.5002" stroke="url(#paint0_linear_63_4945)" stroke-width="1.6"/>
</g>
<g id="Rectangle 346241148" filter="url(#filter1_f_63_4945)">
<path d="M23.4555 14.5H141.753L135.634 23.5H17.3367L23.4555 14.5Z" fill="url(#paint1_linear_63_4945)"/>
</g>
<g id="Vector 485" filter="url(#filter2_f_63_4945)">
<path d="M6.1189 8H8.1585V27H6.29623H6.1189V8Z" fill="url(#paint2_linear_63_4945)"/>
</g>
<g id="Vector 483" filter="url(#filter3_ddf_63_4945)">
<path d="M8.15839 34L1.01978 27V30.5L4.58908 34H8.15839Z" fill="white"/>
</g>
<g id="Vector 484" opacity="0.3" filter="url(#filter4_f_63_4945)">
<path d="M0 0H30.5941V2H2.18529H0V0Z" fill="url(#paint3_linear_63_4945)"/>
</g>
<g id="Union" filter="url(#filter5_dd_63_4945)">
<path d="M618 16H601.683V14H618V16Z" fill="white"/>
</g>
<path id="Union_2" d="M543.555 24H540.495V21H543.555V24Z" fill="#686B8D"/>
<path id="Union_3" d="M562.931 24H559.871V21H562.931V24Z" fill="#686B8D"/>
</g>
<g id="Group 1321318808">
<g id="Rectangle 346241141" filter="url(#filter6_f_63_4945)">
<path d="M169.287 20H177.446L168.267 30H160.109L169.287 20Z" fill="#0041FF"/>
</g>
<g id="Rectangle 346241142" filter="url(#filter7_f_63_4945)">
<path d="M183.564 20H191.723L182.545 30H174.386L183.564 20Z" fill="#0041FF" fill-opacity="0.7"/>
</g>
<g id="Rectangle 346241143" filter="url(#filter8_f_63_4945)">
<path d="M197.842 20H206L196.822 30H188.663L197.842 20Z" fill="#0041FF" fill-opacity="0.3"/>
</g>
<g id="Rectangle 346241144" filter="url(#filter9_f_63_4945)">
<path d="M212.119 20H220.277L211.099 30H202.941L212.119 20Z" fill="#0041FF" fill-opacity="0.16"/>
</g>
</g>
</g>
<defs>
<filter id="filter0_f_63_4945" x="5.57861" y="13.7" width="612.912" height="19.6001" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
</filter>
<filter id="filter1_f_63_4945" x="16.3367" y="13.5" width="126.416" height="11" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
</filter>
<filter id="filter2_f_63_4945" x="5.1189" y="7" width="4.03955" height="21" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
</filter>
<filter id="filter3_ddf_63_4945" x="-0.980225" y="25" width="11.1387" height="11" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_63_4945"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_63_4945" result="effect2_dropShadow_63_4945"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_63_4945" result="shape"/>
<feGaussianBlur stdDeviation="0.05" result="effect3_foregroundBlur_63_4945"/>
</filter>
<filter id="filter4_f_63_4945" x="-0.1" y="-0.1" width="30.794" height="2.2" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.05" result="effect1_foregroundBlur_63_4945"/>
</filter>
<filter id="filter5_dd_63_4945" x="595.683" y="8" width="28.3169" height="14" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="3"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_63_4945"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="3"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.4 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_63_4945" result="effect2_dropShadow_63_4945"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_63_4945" result="shape"/>
</filter>
<filter id="filter6_f_63_4945" x="159.109" y="19" width="19.3367" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
</filter>
<filter id="filter7_f_63_4945" x="173.386" y="19" width="19.3367" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
</filter>
<filter id="filter8_f_63_4945" x="187.663" y="19" width="19.3367" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
</filter>
<filter id="filter9_f_63_4945" x="201.941" y="19" width="19.3367" height="12" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="0.5" result="effect1_foregroundBlur_63_4945"/>
</filter>
<linearGradient id="paint0_linear_63_4945" x1="17.3367" y1="23.5" x2="618" y2="24.02" gradientUnits="userSpaceOnUse">
<stop stop-color="#89A1FF"/>
<stop offset="0.716346" stop-color="#D9DFFF"/>
<stop offset="1" stop-color="#3C4986" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint1_linear_63_4945" x1="17.3367" y1="18.5" x2="135.634" y2="18.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#002AA7"/>
<stop offset="1" stop-color="#0041FF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint2_linear_63_4945" x1="7.1387" y1="8" x2="7.1387" y2="27" gradientUnits="userSpaceOnUse">
<stop stop-color="#4B598C" stop-opacity="0"/>
<stop offset="1" stop-color="#8299F2"/>
</linearGradient>
<linearGradient id="paint3_linear_63_4945" x1="30.5941" y1="1" x2="0" y2="1" gradientUnits="userSpaceOnUse">
<stop stop-color="#999999" stop-opacity="0"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
</defs>
</svg>
applications/drone-command/src/assets/images/dataCockpit/device-fz.svg
New file
@@ -0,0 +1,66 @@
<svg width="30" height="42" viewBox="0 0 30 42" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 2136637024">
<g id="Group 2136636871">
<g id="Ellipse 115">
<g filter="url(#filter0_i_201_2895)">
<circle cx="15.0204" cy="14.9795" r="14.9795" fill="#082389"/>
<circle cx="15.0204" cy="14.9795" r="14.9795" fill="url(#paint0_linear_201_2895)" style="mix-blend-mode:overlay"/>
<circle cx="15.0204" cy="14.9795" r="14.9795" fill="url(#paint1_linear_201_2895)" style="mix-blend-mode:overlay"/>
</g>
<circle cx="15.0204" cy="14.9795" r="14.4795" stroke="white" style="mix-blend-mode:overlay"/>
</g>
<g id="Frame">
<path id="Vector" d="M22.3395 20.5666C22.3395 21.2157 21.8137 21.7416 21.1644 21.7416H9.414C8.76486 21.7416 8.23898 21.2157 8.23898 20.5666V17.3352C8.23898 16.686 8.76488 16.1601 9.41401 16.1601H14.611V13.0757C14.611 12.6703 14.94 12.3412 15.3453 12.3412C15.7507 12.3412 16.0797 12.6703 16.0797 13.0757V16.1601H21.1644C21.8135 16.1601 22.3394 16.686 22.3394 17.3353L22.3395 20.5666ZM20.8707 18.9509C20.8707 18.5455 20.5417 18.2165 20.1362 18.2165C19.7309 18.2165 19.4019 18.5455 19.4019 18.9509C19.4019 19.3562 19.7309 19.6853 20.1362 19.6853C20.5417 19.6853 20.8707 19.3562 20.8707 18.9509ZM18.2268 18.9509C18.2268 18.5455 17.8978 18.2165 17.4925 18.2165C17.0871 18.2165 16.758 18.5455 16.758 18.9509C16.758 19.3562 17.0871 19.6853 17.4924 19.6853C17.8978 19.6853 18.2268 19.3562 18.2268 18.9509ZM15.3454 9.99116C17.0492 9.99116 18.4298 11.3719 18.4298 13.0757C18.4298 13.4811 18.1008 13.8101 17.6954 13.8101C17.29 13.8101 16.9611 13.4811 16.9611 13.0757C16.9611 12.1826 16.2384 11.46 15.3453 11.46C14.4523 11.46 13.7297 12.1826 13.7297 13.0757C13.7297 13.4811 13.4006 13.8101 12.9953 13.8101C12.5899 13.8101 12.2609 13.4811 12.2609 13.0757C12.2608 11.3719 13.6415 9.99116 15.3454 9.99116ZM15.3454 7.64111C18.3476 7.64111 20.7799 10.0735 20.7799 13.0757C20.7799 13.4811 20.451 13.8101 20.0455 13.8101C19.6401 13.8101 19.3111 13.4811 19.3111 13.0757C19.3111 10.8842 17.5368 9.10997 15.3454 9.10997C13.154 9.10997 11.3796 10.8842 11.3796 13.0757C11.3796 13.4811 11.0505 13.8101 10.6452 13.8101C10.2398 13.8101 9.91081 13.4811 9.91081 13.0757C9.91081 10.0735 12.3429 7.64111 15.3454 7.64111Z" fill="url(#paint2_linear_201_2895)"/>
</g>
</g>
<g id="Group 2136637016">
<g id="&#232;&#183;&#175;&#229;&#190;&#132;">
<g filter="url(#filter1_i_201_2895)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.959 36C12.4796 36 10.4713 36.8438 10.4713 37.875C10.4713 38.9062 12.4796 39.75 14.959 39.75C17.4385 39.75 19.4468 38.9062 19.4468 37.875C19.4468 36.8438 17.4385 36 14.959 36Z" fill="#0A3BFF" fill-opacity="0.588235"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.959 36C12.4796 36 10.4713 36.8438 10.4713 37.875C10.4713 38.9062 12.4796 39.75 14.959 39.75C17.4385 39.75 19.4468 38.9062 19.4468 37.875C19.4468 36.8438 17.4385 36 14.959 36Z" stroke="#518EFF" stroke-width="0.4"/>
</g>
<path id="&#232;&#183;&#175;&#229;&#190;&#132;_2" opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M14.9591 33.75C6.6942 33.75 0 35.598 0 37.875C0 40.152 6.6942 42 14.9591 42C23.224 42 29.9182 40.152 29.9182 37.875C29.9182 35.598 23.224 33.75 14.9591 33.75V33.75Z" stroke="#518EFF" stroke-width="0.6"/>
<path id="&#232;&#183;&#175;&#229;&#190;&#132;_3" fill-rule="evenodd" clip-rule="evenodd" d="M14.9591 35.25C9.58694 35.25 5.23572 36.4225 5.23572 37.875C5.23572 39.3275 9.58694 40.5 14.9591 40.5C20.3313 40.5 24.6825 39.3275 24.6825 37.875C24.6825 36.4225 20.3313 35.25 14.9591 35.25V35.25Z" stroke="#518EFF" stroke-width="0.6"/>
<path id="&#232;&#183;&#175;&#229;&#190;&#132;_4" fill-rule="evenodd" clip-rule="evenodd" d="M14.2111 31.5V37.5H15.707V31.5H14.2111Z" fill="url(#paint3_linear_201_2895)"/>
</g>
</g>
<defs>
<filter id="filter0_i_201_2895" x="0.0408936" y="0" width="29.9591" height="29.9591" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="4.5"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="overlay" in2="shape" result="effect1_innerShadow_201_2895"/>
</filter>
<filter id="filter1_i_201_2895" x="10.2714" y="35.8" width="9.37537" height="5.1499" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="0.5"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.005478 0 0 0 0 0.0928216 0 0 0 0 0.379808 0 0 0 0.239216 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_201_2895"/>
</filter>
<linearGradient id="paint0_linear_201_2895" x1="15.0204" y1="0" x2="15.0204" y2="29.9591" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint1_linear_201_2895" x1="15.0204" y1="0" x2="15.0204" y2="29.9591" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint2_linear_201_2895" x1="15.2892" y1="7.64111" x2="15.2892" y2="21.7416" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#A2F2FB"/>
</linearGradient>
<linearGradient id="paint3_linear_201_2895" x1="17.2029" y1="37.5" x2="17.2029" y2="31.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#0A3BFF" stop-opacity="0.01"/>
<stop offset="1" stop-color="#0A3BFF"/>
</linearGradient>
</defs>
</svg>
applications/drone-command/src/assets/images/dataCockpit/device-wrj.svg
New file
@@ -0,0 +1,49 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 2136637030">
<g id="Ellipse 120">
<g filter="url(#filter0_i_205_3058)">
<circle cx="15" cy="15" r="15" fill="#38100A"/>
<circle cx="15" cy="15" r="15" fill="url(#paint0_linear_205_3058)" style="mix-blend-mode:overlay"/>
<circle cx="15" cy="15" r="15" fill="url(#paint1_linear_205_3058)" style="mix-blend-mode:overlay"/>
</g>
<circle cx="15" cy="15" r="14.5" stroke="white" style="mix-blend-mode:overlay"/>
</g>
<g id="Frame" clip-path="url(#clip0_205_3058)">
<g id="Group 1321318248">
<path id="Vector" d="M24.5173 11.5642C24.5173 11.3706 22.2846 11.2128 21.1002 11.1927V11.0749C21.1002 10.9977 21.0731 10.9237 21.0248 10.8691C20.9766 10.8145 20.9111 10.7838 20.8429 10.7838C20.7747 10.7838 20.7092 10.8145 20.661 10.8691C20.6128 10.9237 20.5856 10.9977 20.5856 11.0749V11.1927C19.4013 11.2128 17.1686 11.3706 17.1686 11.5642C17.1686 11.7585 19.4013 11.9163 20.5856 11.9365V13.1597H17.7198C17.5046 12.786 17.1361 12.5313 16.7065 12.5313H13.735C13.3054 12.5313 12.937 12.786 12.7217 13.1597H9.85593V11.9364C11.0403 11.9163 13.273 11.7585 13.273 11.5642C13.273 11.3706 11.0403 11.2127 9.85593 11.1926V11.0749C9.85593 10.9977 9.82883 10.9237 9.78058 10.8691C9.73233 10.8145 9.66689 10.7838 9.59866 10.7838C9.53043 10.7838 9.46499 10.8145 9.41674 10.8691C9.3685 10.9237 9.34139 10.9977 9.34139 11.0749V11.1926C8.15702 11.2127 5.92432 11.3706 5.92432 11.5642C5.92432 11.7585 8.15702 11.9163 9.34139 11.9365V13.1597H8.47017C8.14381 13.1597 8.07472 13.6794 8.38594 13.7889L11.8782 15.2562L10.5511 18.2921C10.4722 18.4763 10.4543 18.6865 10.5008 18.8844C10.5473 19.0823 10.6648 19.1999 10.8044 19.3702C11.1129 19.7465 11.6502 19.9449 11.6502 19.9449C11.8782 20.104 12.068 20.0383 12.1534 19.8333C12.2251 19.6612 12.1546 19.4577 12.0381 19.3702L11.4005 18.8852C11.3548 18.8495 11.3219 18.7965 11.3076 18.7358C11.2934 18.675 11.2988 18.6105 11.3228 18.5539L12.6151 15.5043L12.6835 15.5282L12.9112 16.9647C12.92 17.0479 12.9471 17.1272 12.99 17.1955C13.0328 17.2638 13.0901 17.3188 13.1567 17.3556L15.0273 18.386C15.0875 18.4194 15.1537 18.4368 15.2207 18.4368C15.2878 18.4368 15.354 18.4194 15.4142 18.386L17.2849 17.3555C17.3514 17.3187 17.4087 17.2637 17.4516 17.1954C17.4944 17.1271 17.5215 17.0478 17.5303 16.9646L17.7579 15.5281L17.8264 15.5042L19.1187 18.5538C19.1429 18.6103 19.1484 18.6747 19.1342 18.7354C19.1199 18.7961 19.0868 18.849 19.041 18.8844L18.3593 19.4159C18.237 19.5113 18.1613 19.7578 18.2497 19.9449C18.3642 20.1871 18.8003 20.0514 18.9369 19.9449L19.727 19.4159C19.8764 19.3003 19.9841 19.128 20.0307 18.9302C20.0773 18.7324 20.0597 18.5222 19.981 18.3378L18.6581 15.2225L22.0555 13.7889C22.3668 13.6794 22.2977 13.1597 21.9713 13.1597H21.1001V11.9364C22.2845 11.9163 24.5173 11.7585 24.5173 11.5642ZM15.2207 17.1954C14.5733 17.1954 14.0587 16.6417 14.0587 16.0131C14.0587 15.432 14.579 14.851 15.2208 14.851C15.8626 14.851 16.3964 15.3714 16.3828 16.0131C16.3695 16.6416 15.8681 17.1954 15.2207 17.1954Z" fill="url(#paint2_linear_205_3058)"/>
<path id="Vector_2" d="M14.6398 16.013C14.6398 16.0893 14.6548 16.1649 14.684 16.2354C14.7132 16.3059 14.756 16.3699 14.8099 16.4239C14.8639 16.4778 14.9279 16.5206 14.9984 16.5498C15.0689 16.579 15.1445 16.5941 15.2208 16.5941C15.2971 16.5941 15.3727 16.579 15.4432 16.5498C15.5137 16.5206 15.5777 16.4778 15.6317 16.4239C15.6856 16.3699 15.7284 16.3059 15.7576 16.2354C15.7868 16.1649 15.8018 16.0893 15.8018 16.013C15.8018 15.9367 15.7868 15.8612 15.7576 15.7907C15.7284 15.7202 15.6856 15.6561 15.6317 15.6022C15.5777 15.5482 15.5137 15.5054 15.4432 15.4762C15.3727 15.447 15.2971 15.432 15.2208 15.432C15.1445 15.432 15.0689 15.447 14.9984 15.4762C14.9279 15.5054 14.8639 15.5482 14.8099 15.6022C14.756 15.6561 14.7132 15.7202 14.684 15.7907C14.6548 15.8612 14.6398 15.9367 14.6398 16.013Z" fill="url(#paint3_linear_205_3058)"/>
</g>
</g>
</g>
<defs>
<filter id="filter0_i_205_3058" x="0" y="0" width="30" height="30" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="4.5"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="overlay" in2="shape" result="effect1_innerShadow_205_3058"/>
</filter>
<linearGradient id="paint0_linear_205_3058" x1="15" y1="0" x2="15" y2="30" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint1_linear_205_3058" x1="15" y1="0" x2="15" y2="30" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint2_linear_205_3058" x1="15.2208" y1="20.0803" x2="15.2208" y2="10.7838" gradientUnits="userSpaceOnUse">
<stop stop-color="#FCFEFF"/>
<stop offset="1" stop-color="#FBB6A5"/>
</linearGradient>
<linearGradient id="paint3_linear_205_3058" x1="15.2208" y1="16.5941" x2="15.2208" y2="15.432" gradientUnits="userSpaceOnUse">
<stop stop-color="#FCFEFF"/>
<stop offset="1" stop-color="#FBB6A5"/>
</linearGradient>
<clipPath id="clip0_205_3058">
<rect width="18.75" height="18.75" fill="white" transform="matrix(-1 0 0 1 24.6997 5.44312)"/>
</clipPath>
</defs>
</svg>
applications/drone-command/src/assets/images/dataCockpit/device-zc.svg
New file
@@ -0,0 +1,76 @@
<svg width="30" height="42" viewBox="0 0 30 42" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 2136637029">
<g id="Group 2136636871">
<g id="Ellipse 115">
<g filter="url(#filter0_i_205_3029)">
<circle cx="15.0204" cy="14.9795" r="14.9795" fill="#002624"/>
<circle cx="15.0204" cy="14.9795" r="14.9795" fill="url(#paint0_linear_205_3029)" style="mix-blend-mode:overlay"/>
<circle cx="15.0204" cy="14.9795" r="14.9795" fill="url(#paint1_linear_205_3029)" style="mix-blend-mode:overlay"/>
</g>
<circle cx="15.0204" cy="14.9795" r="14.4795" stroke="white" style="mix-blend-mode:overlay"/>
</g>
<g id="Frame">
<g id="Frame_2" clip-path="url(#clip0_205_3029)">
<g id="Frame_3" clip-path="url(#clip1_205_3029)">
<path id="Vector" d="M14.9991 16.3575L10.7982 21.959H19.2L14.9991 16.3575ZM20.1788 9.3562L19.1438 10.2984C20.0832 11.3285 20.6029 12.673 20.6007 14.0671C20.6027 15.461 20.083 16.805 19.1438 17.8349L20.1797 18.7762C21.3533 17.4889 22.0027 15.8091 22.0004 14.0671C22.0004 12.2531 21.3104 10.5993 20.1788 9.3562ZM9.81942 9.3562C8.64503 10.6438 7.9952 12.3244 7.99786 14.0671C7.99525 15.809 8.64435 17.4887 9.81755 18.7762L10.8544 17.834C9.91548 16.8043 9.39579 15.4606 9.39755 14.0671C9.39555 12.6733 9.91527 11.3292 10.8544 10.2993L9.81849 9.3562H9.81942ZM18.1069 11.2406L17.071 12.1828C17.5397 12.6984 17.8004 13.3696 17.7994 14.0662C17.7994 14.7918 17.5238 15.4528 17.071 15.9506L18.1079 16.8928C18.812 16.1203 19.2016 15.1124 19.2 14.0671C19.2 12.9778 18.7857 11.9859 18.1079 11.2406H18.1069ZM11.8904 11.2406C11.186 12.0133 10.7964 13.0216 10.7982 14.0671C10.7965 15.1121 11.1861 16.1198 11.8904 16.8918L12.9263 15.9506C12.4575 15.435 12.1979 14.7637 12.1988 14.0662C12.1988 13.3415 12.4744 12.6806 12.9263 12.1837L11.8904 11.2415V11.2406ZM14.9991 12.6675C14.8122 12.6626 14.6262 12.6953 14.4521 12.7634C14.278 12.8316 14.1193 12.934 13.9854 13.0645C13.8515 13.195 13.745 13.351 13.6724 13.5233C13.5997 13.6955 13.5623 13.8806 13.5623 14.0676C13.5623 14.2546 13.5997 14.4397 13.6724 14.612C13.745 14.7842 13.8515 14.9402 13.9854 15.0707C14.1193 15.2012 14.278 15.3036 14.4521 15.3718C14.6262 15.44 14.8122 15.4726 14.9991 15.4678C15.3642 15.4583 15.7111 15.3067 15.966 15.0451C16.2209 14.7836 16.3635 14.4328 16.3635 14.0676C16.3635 13.7024 16.2209 13.3517 15.966 13.0901C15.7111 12.8286 15.3642 12.6769 14.9991 12.6675Z" fill="url(#paint2_linear_205_3029)"/>
</g>
</g>
</g>
</g>
<g id="Group 2136637016">
<g id="&#232;&#183;&#175;&#229;&#190;&#132;">
<g filter="url(#filter1_i_205_3029)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.959 36C12.4796 36 10.4713 36.8438 10.4713 37.875C10.4713 38.9062 12.4796 39.75 14.959 39.75C17.4385 39.75 19.4468 38.9062 19.4468 37.875C19.4468 36.8438 17.4385 36 14.959 36Z" fill="#159DAD" fill-opacity="0.588235"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.959 36C12.4796 36 10.4713 36.8438 10.4713 37.875C10.4713 38.9062 12.4796 39.75 14.959 39.75C17.4385 39.75 19.4468 38.9062 19.4468 37.875C19.4468 36.8438 17.4385 36 14.959 36Z" stroke="#01DEF7" stroke-width="0.4"/>
</g>
<path id="&#232;&#183;&#175;&#229;&#190;&#132;_2" opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M14.9591 33.75C6.6942 33.75 0 35.598 0 37.875C0 40.152 6.6942 42 14.9591 42C23.224 42 29.9182 40.152 29.9182 37.875C29.9182 35.598 23.224 33.75 14.9591 33.75V33.75Z" stroke="#01DEF7" stroke-width="0.6"/>
<path id="&#232;&#183;&#175;&#229;&#190;&#132;_3" fill-rule="evenodd" clip-rule="evenodd" d="M14.9591 35.25C9.58694 35.25 5.23572 36.4225 5.23572 37.875C5.23572 39.3275 9.58694 40.5 14.9591 40.5C20.3313 40.5 24.6825 39.3275 24.6825 37.875C24.6825 36.4225 20.3313 35.25 14.9591 35.25V35.25Z" stroke="#01DEF7" stroke-width="0.6"/>
<path id="&#232;&#183;&#175;&#229;&#190;&#132;_4" fill-rule="evenodd" clip-rule="evenodd" d="M14.2111 31.5V37.5H15.707V31.5H14.2111Z" fill="url(#paint3_linear_205_3029)"/>
</g>
</g>
<defs>
<filter id="filter0_i_205_3029" x="0.0408936" y="0" width="29.9591" height="29.9591" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="4.5"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="overlay" in2="shape" result="effect1_innerShadow_205_3029"/>
</filter>
<filter id="filter1_i_205_3029" x="10.2714" y="35.8" width="9.37537" height="5.1499" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="0.5"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.005478 0 0 0 0 0.0928216 0 0 0 0 0.379808 0 0 0 0.239216 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_205_3029"/>
</filter>
<linearGradient id="paint0_linear_205_3029" x1="15.0204" y1="0" x2="15.0204" y2="29.9591" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint1_linear_205_3029" x1="15.0204" y1="0" x2="15.0204" y2="29.9591" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint2_linear_205_3029" x1="14.9991" y1="21.959" x2="14.9991" y2="9.3562" gradientUnits="userSpaceOnUse">
<stop stop-color="#ABF3FB"/>
<stop offset="1" stop-color="#FCFEFF"/>
</linearGradient>
<linearGradient id="paint3_linear_205_3029" x1="17.2029" y1="37.5" x2="17.2029" y2="31.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#01DEF7" stop-opacity="0.01"/>
<stop offset="1" stop-color="#01DEF7"/>
</linearGradient>
<clipPath id="clip0_205_3029">
<rect width="15" height="15" fill="white" transform="translate(7.5 8.25)"/>
</clipPath>
<clipPath id="clip1_205_3029">
<rect width="15" height="15" fill="white" transform="matrix(-1 0 0 1 22.5 8.25)"/>
</clipPath>
</defs>
</svg>
applications/drone-command/src/assets/images/dataCockpit/device-zhd.svg
New file
@@ -0,0 +1,63 @@
<svg width="40" height="56" viewBox="0 0 40 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 2136637022">
<g id="Group 2136637020">
<path id="&#232;&#183;&#175;&#229;&#190;&#132;" opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M20 45.7333C8.95 45.7333 0 48.033 0 50.8666C0 53.7002 8.95 55.9999 20 55.9999C31.05 55.9999 40 53.7002 40 50.8666C40 48.033 31.05 45.7333 20 45.7333V45.7333Z" stroke="#1EF9FD" stroke-width="0.6"/>
<path id="&#232;&#183;&#175;&#229;&#190;&#132;_2" fill-rule="evenodd" clip-rule="evenodd" d="M20 48C12.8175 48 7 49.34 7 51C7 52.66 12.8175 54 20 54C27.1825 54 33 52.66 33 51C33 49.34 27.1825 48 20 48V48Z" stroke="#1EF9FD" stroke-width="0.6"/>
<path id="&#232;&#183;&#175;&#229;&#190;&#132;_3" fill-rule="evenodd" clip-rule="evenodd" d="M19 42V50H21V42H19Z" fill="url(#paint0_linear_201_2906)"/>
<g id="&#232;&#183;&#175;&#229;&#190;&#132;_4">
<g filter="url(#filter0_i_201_2906)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 49C16.685 49 14 49.9 14 51C14 52.1 16.685 53 20 53C23.315 53 26 52.1 26 51C26 49.9 23.315 49 20 49Z" fill="#3DB4C2" fill-opacity="0.588235"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 49C16.685 49 14 49.9 14 51C14 52.1 16.685 53 20 53C23.315 53 26 52.1 26 51C26 49.9 23.315 49 20 49Z" stroke="#02E3FC" stroke-width="0.4"/>
</g>
</g>
<g id="Frame 2136637015">
<g id="Group 2136637019">
<g id="Group 2136637018">
<g id="Group 2136637017">
<path id="&#229;&#189;&#162;&#231;&#138;&#182;_1" fill-rule="evenodd" clip-rule="evenodd" d="M21.1865 37.8053C20.8879 37.9788 20.5662 38.0656 20.2213 38.0656C19.8764 38.0656 19.5547 37.9788 19.2562 37.8053L5.40778 29.76C5.10909 29.5861 4.87354 29.349 4.70118 29.0486C4.52881 28.7482 4.44263 28.4247 4.44263 28.078V11.9873C4.44256 11.6406 4.52875 11.317 4.70114 11.0166C4.87351 10.7163 5.10906 10.4792 5.40778 10.3056L19.2562 2.26025C19.5547 2.08674 19.8764 2 20.2213 2C20.5662 2 20.8879 2.08674 21.1865 2.26025L35.0349 10.3056C35.3335 10.4792 35.569 10.7161 35.7414 11.0165C35.9138 11.3169 36 11.6405 36 11.9873V28.0793C35.9997 28.4258 35.9134 28.7491 35.741 29.0493C35.5687 29.3494 35.3333 29.5863 35.0349 29.76L21.1865 37.8053Z" fill="url(#paint1_linear_201_2906)" stroke="url(#paint2_linear_201_2906)" stroke-width="3"/>
<path id="&#229;&#189;&#162;&#231;&#138;&#182;_1_&#230;&#139;&#183;&#232;&#180;&#157;_2" opacity="0.3" fill-rule="evenodd" clip-rule="evenodd" d="M21.3504 35.512C21.0899 35.6631 20.8091 35.7387 20.5082 35.7387C20.2072 35.7387 19.9264 35.6631 19.666 35.512L7.57987 28.5045C7.31925 28.3533 7.11374 28.1469 6.96331 27.8854C6.81289 27.6239 6.73767 27.3422 6.73767 27.0403V13.0253C6.7377 12.7234 6.81291 12.4417 6.96334 12.1801C7.11377 11.9186 7.31928 11.7123 7.57987 11.5611L19.666 4.55336C19.9265 4.4023 20.2072 4.32678 20.5082 4.32678C20.8091 4.32678 21.0898 4.4023 21.3504 4.55336L33.4364 11.5611C33.6971 11.7123 33.9026 11.9187 34.053 12.1802C34.2034 12.4417 34.2787 12.7234 34.2787 13.0253V27.0403C34.2786 27.3422 34.2034 27.6239 34.053 27.8854C33.9026 28.147 33.697 28.3533 33.4364 28.5045L21.3504 35.512Z" fill="url(#paint3_linear_201_2906)"/>
</g>
</g>
<g id="Group 2136637018_2">
<g id="Group 2136637017_2">
<path id="Vector" d="M23.3913 19.6115C23.7642 19.9799 24.3236 19.9799 24.6966 19.6115L27.4937 16.8484C27.8667 16.48 27.8667 15.9273 27.4937 15.5589L24.6966 12.7958C24.3236 12.4274 23.7642 12.4274 23.3913 12.7958L20.5941 15.5589C20.2212 15.9273 20.2212 16.48 20.5941 16.8484L23.3913 19.6115ZM18.7294 12.7432H13.6146C13.1085 12.7432 12.6823 13.1643 12.6823 13.6642V18.7431C12.6823 19.243 13.1085 19.6641 13.6146 19.6641H18.756C19.2622 19.6641 19.6884 19.243 19.6884 18.7431V13.6642C19.6618 13.1643 19.2355 12.7432 18.7294 12.7432ZM18.7294 20.5851H13.6146C13.1085 20.5851 12.6823 21.0062 12.6823 21.5061V26.585C12.6823 27.085 13.1085 27.506 13.6146 27.506H18.756C19.2622 27.506 19.6884 27.085 19.6884 26.585V21.5061C19.6618 21.0062 19.2355 20.5851 18.7294 20.5851ZM26.6679 20.5851H21.5265C21.0204 20.5851 20.5941 21.0062 20.5941 21.5061V26.585C20.5941 27.085 21.0204 27.506 21.5265 27.506H26.6679C27.1741 27.506 27.6003 27.085 27.6003 26.585V21.5061C27.6003 21.0062 27.1741 20.5851 26.6679 20.5851Z" fill="url(#paint4_linear_201_2906)"/>
</g>
</g>
</g>
</g>
</g>
<defs>
<filter id="filter0_i_201_2906" x="13.8" y="48.8" width="12.3999" height="5.3999" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="0.5"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.005478 0 0 0 0 0.0928216 0 0 0 0 0.379808 0 0 0 0.239216 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_201_2906"/>
</filter>
<linearGradient id="paint0_linear_201_2906" x1="23" y1="50" x2="23" y2="42" gradientUnits="userSpaceOnUse">
<stop stop-color="#02E3FC" stop-opacity="0.01"/>
<stop offset="1" stop-color="#01F7F7"/>
</linearGradient>
<linearGradient id="paint1_linear_201_2906" x1="35.9969" y1="38.0655" x2="35.9969" y2="2.00703" gradientUnits="userSpaceOnUse">
<stop stop-color="#08393C"/>
<stop offset="0.994" stop-color="#007874"/>
<stop offset="1" stop-color="#007874"/>
</linearGradient>
<linearGradient id="paint2_linear_201_2906" x1="35.9969" y1="38.0655" x2="35.9969" y2="2.00703" gradientUnits="userSpaceOnUse">
<stop stop-color="#1A6B6C"/>
<stop offset="1" stop-color="#C3FFFE"/>
</linearGradient>
<linearGradient id="paint3_linear_201_2906" x1="34.2787" y1="35.7411" x2="34.2787" y2="4.32907" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C656E" stop-opacity="0.01"/>
<stop offset="1"/>
</linearGradient>
<linearGradient id="paint4_linear_201_2906" x1="20.2278" y1="12.5195" x2="20.2278" y2="27.506" gradientUnits="userSpaceOnUse">
<stop stop-color="#4AB8B5"/>
<stop offset="1" stop-color="#DAFAFB"/>
</linearGradient>
</defs>
</svg>
applications/drone-command/src/assets/images/dataCockpit/legend/command-post.png
applications/drone-command/src/assets/images/dataCockpit/legend/drone.png
applications/drone-command/src/assets/images/dataCockpit/legend/equipment.png
applications/drone-command/src/assets/images/dataCockpit/map/command-post.png
applications/drone-command/src/assets/images/dataCockpit/map/equipment.png

applications/drone-command/src/assets/images/dataCockpit/title-bg.png

applications/drone-command/src/assets/images/defaultava.png

applications/drone-command/src/components/PlaybackVideo/PlaybackVideo.vue
@@ -1,6 +1,6 @@
<template>
    <el-dialog
        class="ztzf-dialog-video playback-dialog"
        class="command-dialog-video playback-dialog"
        v-model="isShow"
        append-to-body
        :close-on-click-modal="false"
applications/drone-command/src/components/PlaybackVideo/components/MapContainer.vue
@@ -1,5 +1,5 @@
<template>
    <div class="ztzf-cesium map-box" id="map">
    <div class="command-cesium map-box" id="map">
        <PlanarRouteLineList
            :curRouteLineData="curRouteLineData"
            @routeLineListClick="routeLineListClick"
applications/drone-command/src/components/commonDialog.vue
@@ -1,5 +1,5 @@
<template>
  <el-dialog class="ztzf-dialog-mange" append-to-body v-model="props.isShowView" :title="props.title"
  <el-dialog class="command-dialog-mange" append-to-body v-model="props.isShowView" :title="props.title"
             :width="props.width" :close-on-click-modal="false" :destroy-on-close="true" @close="cancel">
    <slot name="default" />
    <template #footer v-if="props.addEditView !== 'view'">
applications/drone-command/src/components/map-container/common-cesium-map.vue
New file
@@ -0,0 +1,135 @@
<template>
    <div :id="mapId" class="common-cesium-map"></div>
</template>
<script setup>
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import { loadJaAdminBoundary, removeJaAdminBoundary } from '@/utils/cesium/adminBoundary'
const props = defineProps({
    active: {
        type: Boolean,
        default: true,
    },
    domId: {
        type: String,
        default: '',
    },
    flatMode: {
        type: Boolean,
        default: false,
    },
    terrain: {
        type: Boolean,
        default: true,
    },
    layerMode: {
        type: Number,
        default: 4,
    },
    boundary: {
        type: Boolean,
        default: false,
    },
    contour: {
        type: Boolean,
        default: false,
    },
    showAdminBoundary: {
        type: Boolean,
        default: true,
    },
    zoomToBoundary: {
        type: Boolean,
        default: true,
    },
    enableStageEmit: {
        type: Boolean,
        default: false,
    },
    clusterHeight: {
        type: Number,
        default: 100000,
    },
    detailHeight: {
        type: Number,
        default: 10000,
    },
})
const emit = defineEmits(['ready', 'height-change', 'stage-change'])
const mapId = props.domId || `common-cesium-map-${Math.random().toString(36).slice(2, 10)}`
const viewInstance = ref(null)
const viewer = ref(null)
let adminBoundarySources = null
let initialized = false
let removeCameraListener = null
const initMap = async () => {
    if (initialized) return
    initialized = true
    viewInstance.value = new PublicCesium({
        dom: mapId,
        flatMode: props.flatMode,
        terrain: props.terrain,
        layerMode: props.layerMode,
        boundary: props.boundary,
        contour: props.contour,
    })
    viewer.value = viewInstance.value.getViewer()
    if (props.showAdminBoundary) {
        adminBoundarySources = await loadJaAdminBoundary(viewer.value, {
            zoomTo: props.zoomToBoundary,
        })
    }
    if (props.enableStageEmit) {
        let lastStage = ''
        const onCameraChanged = () => {
            const height = viewer.value?.camera?.positionCartographic?.height
            if (height == null) return
            emit('height-change', height)
            let stage = 'mid'
            if (height >= props.clusterHeight) stage = 'cluster'
            if (height <= props.detailHeight) stage = 'detail'
            if (stage !== lastStage) {
                lastStage = stage
                emit('stage-change', stage)
            }
        }
        viewer.value?.camera?.changed?.addEventListener(onCameraChanged)
        removeCameraListener = () => viewer.value?.camera?.changed?.removeEventListener(onCameraChanged)
    }
    emit('ready', { viewer: viewer.value, publicCesium: viewInstance.value })
}
watch(
    () => props.active,
    active => {
        if (active) initMap()
    }
)
onMounted(() => {
    if (props.active) initMap()
})
onBeforeUnmount(() => {
    if (removeCameraListener) removeCameraListener()
    removeJaAdminBoundary(viewer.value, adminBoundarySources)
    viewInstance.value?.viewerDestroy?.()
})
const getMap = () => ({ viewer: viewer.value, publicCesium: viewInstance.value })
const getViewer = () => viewer.value
const getPublicCesium = () => viewInstance.value
defineExpose({ getMap, getViewer, getPublicCesium })
</script>
<style scoped lang="scss">
.common-cesium-map {
    width: 100%;
    height: 100%;
}
</style>
applications/drone-command/src/components/map-container/device-map-container.vue
@@ -1,5 +1,18 @@
<template>
    <div class="ztzf-cesium map-container" :id="props.containerId"></div>
    <CommonCesiumMap
        ref="mapRef"
        class="command-cesium map-container"
        :dom-id="props.containerId"
        :active="true"
        :flat-mode="false"
        :terrain="false"
        :layer-mode="4"
        :contour="false"
        :boundary="false"
        :show-admin-boundary="true"
        :zoom-to-boundary="true"
        @ready="handleMapReady"
    />
    <div v-if="props.showLayerControl" class="layer-control-root" :class="{ collapsed: props.leftCollapsed }">
        <div class="layer-control-wrap" ref="layerWrapRef">
            <div class="layer-control" @click="toggleLayerPanel">
@@ -25,7 +38,10 @@
<script setup>
import * as Cesium from 'cesium'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
import { geomAnalysis } from '@ztzf/utils'
import { fwDefenseZonePageApi } from '@/views/areaManage/defenseZone/defenseZoneApi'
import { fwAreaDividePageApi } from '@/views/areaManage/partition/partitionApi'
import layerControlIcon from '@/assets/images/dataCockpit/layerControl.png'
import equipmentIcon from '@/assets/images/dataCockpit/map/equipment.png'
@@ -48,9 +64,13 @@
    },
})
let viewInstance = null
const DEFAULT_ZONE_PAGE_SIZE = 999
const mapRef = ref(null)
let viewer = null
const deviceEntityIds = new Set()
let defenseZoneSource = null
let partitionSource = null
const showLayerPanel = ref(false)
const layerWrapRef = ref(null)
const layerTreeProps = {
@@ -105,10 +125,19 @@
    deviceEntityIds.clear()
}
const clearDefenseZoneEntities = () => {
    if (!defenseZoneSource) return
    defenseZoneSource.entities.removeAll()
}
const clearPartitionEntities = () => {
    if (!partitionSource) return
    partitionSource.entities.removeAll()
}
const RING_STYLES = [
    { inner: 0, outer: 5000, gradient: ['#FF361C', '#360B00'] },
    { inner: 5000, outer: 8000, gradient: ['#FFC609', '#583300'] },
    { inner: 8000, outer: 10000, gradient: ['#2AEDBF', '#012B11'] },
    { inner: 0, outer: 2000, gradient: ['#FF361C', '#360B00'] }
]
const MATERIAL_TYPE = 'RadialGradientMaterial'
@@ -228,12 +257,110 @@
            position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
            billboard: {
                image: equipmentIcon,
                width: 40.34,
                height: 40.34,
                width: 40,
                height: 56,
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            },
        })
    })
}
const getDefenseZonePositions = geom => {
    const points = geomAnalysis(geom)
    if (points.length < 3) return []
    const first = points[0]
    const last = points[points.length - 1]
    const list =
        first && last && first.longitude === last.longitude && first.latitude === last.latitude
            ? points.slice(0, -1)
            : points
    return list.map(item => Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude))
}
const renderZonePolygons = ({ zones, source, idPrefix, lineColor, fillGradient }) => {
    if (!viewer || !source) return
    registerRadialGradientMaterial()
    const borderColor = Cesium.Color.fromCssColorString(lineColor)
    const fillColorStart = Cesium.Color.fromCssColorString(fillGradient[0]).withAlpha(0.34)
    const fillColorEnd = Cesium.Color.fromCssColorString(fillGradient[1]).withAlpha(0.34)
    const fillMaterial = new RadialGradientMaterialProperty(fillColorStart, fillColorEnd)
    zones.forEach((zone, index) => {
        if (!zone?.geom) return
        const positions = getDefenseZonePositions(zone.geom)
        if (!positions.length) return
        const entityId = `${idPrefix}-${zone.id ?? index}`
        const linePositions = positions.length > 1 ? [...positions, positions[0]] : positions
        source.entities.add({
            id: entityId,
            polygon: {
                hierarchy: new Cesium.PolygonHierarchy(positions),
                material: fillMaterial,
                outline: true,
                outlineColor: borderColor,
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            },
            polyline: {
                positions: linePositions,
                clampToGround: true,
                width: 2,
                material: borderColor,
            },
        })
    })
}
const renderDefenseZones = zones => {
    if (!viewer) return
    if (!defenseZoneSource) {
        defenseZoneSource = new Cesium.CustomDataSource('defenseZoneSource')
        viewer.dataSources.add(defenseZoneSource)
    }
    clearDefenseZoneEntities()
    renderZonePolygons({
        zones,
        source: defenseZoneSource,
        idPrefix: 'defense-zone',
        lineColor: '#19D266',
        fillGradient: ['#2AEDBF', '#012B11'],
    })
}
const renderPartitions = zones => {
    if (!viewer) return
    if (!partitionSource) {
        partitionSource = new Cesium.CustomDataSource('partitionSource')
        viewer.dataSources.add(partitionSource)
    }
    clearPartitionEntities()
    renderZonePolygons({
        zones,
        source: partitionSource,
        idPrefix: 'partition-zone',
        lineColor: '#FFCD2A',
        fillGradient: ['#FFC609', '#583300'],
    })
}
const loadDefenseZones = async () => {
    if (!viewer) return
    try {
        const res = await fwDefenseZonePageApi({ current: 1, size: DEFAULT_ZONE_PAGE_SIZE })
        renderDefenseZones(res?.data?.data?.records ?? [])
    } catch (error) {
        renderDefenseZones([])
    }
}
const loadPartitions = async () => {
    if (!viewer) return
    try {
        const res = await fwAreaDividePageApi({ current: 1, size: DEFAULT_ZONE_PAGE_SIZE })
        renderPartitions(res?.data?.data?.records ?? [])
    } catch (error) {
        renderPartitions([])
    }
}
watch(
    () => props.onlineDevices,
@@ -255,6 +382,13 @@
    showLayerPanel.value = !showLayerPanel.value
}
const handleMapReady = ({ viewer: mapViewer }) => {
    viewer = mapViewer
    renderDeviceEntities(props.onlineDevices)
    loadDefenseZones()
    loadPartitions()
}
const handleClickOutside = event => {
    if (!showLayerPanel.value) return
    const target = event.target
@@ -264,26 +398,15 @@
onMounted(() => {
    document.addEventListener('click', handleClickOutside)
    viewInstance = new PublicCesium({
        dom: props.containerId,
        flatMode: false,
        terrain: false,
        layerMode: 4,
        contour: false,
    })
    viewer = viewInstance.getViewer()
    renderDeviceEntities(props.onlineDevices)
    const map = mapRef.value?.getMap()
    if (map?.viewer) handleMapReady(map)
})
onBeforeUnmount(() => {
    document.removeEventListener('click', handleClickOutside)
    clearDeviceEntities()
    if (viewInstance) {
        viewInstance?.viewerDestroy()
        viewInstance = null
    }
    clearDefenseZoneEntities()
    clearPartitionEntities()
    viewer = null
})
</script>
@@ -299,14 +422,14 @@
.layer-control-root {
    position: absolute;
    left: 411px;
    left: 337px;
    bottom: 22px;
    z-index: 9;
    transition: transform 0.3s ease-in-out;
    pointer-events: none;
    &.collapsed {
        transform: translateX(-401px);
        transform: translateX(-317px);
    }
}
@@ -318,8 +441,8 @@
}
.layer-control {
    width: 34px;
    height: 34px;
    width: 46px;
    height: 46px;
    cursor: pointer;
    img {
@@ -333,7 +456,7 @@
    display: flex;
    flex-direction: column;
    position: absolute;
    left: 46px;
    left: 66px;
    bottom: 0;
    width: 160px;
    max-height: 442px;
@@ -370,6 +493,7 @@
            line-height: 30px !important;
            .el-tree-node__content {
                padding-left: 0 !important;
                display: flex;
                align-items: center;
                height: 40px !important;
@@ -381,6 +505,12 @@
            .el-tree-node__children {
                .el-tree-node__content {
                    border: none;
                }
            }
            &:focus {
                .el-tree-node__content {
                    background: transparent !important;
                }
            }
        }
@@ -412,7 +542,8 @@
    display: none;
}
.layer-panel :deep(.el-tree-node__content:hover) {
.layer-panel :deep(.el-tree-node__content:hover),
.layer-panel :deep(.el-tree-node__content:focus) {
    background: transparent !important;
}
applications/drone-command/src/components/map-container/mapContainer.vue
@@ -10,7 +10,7 @@
-->
<template>
  <div class="map-container">
    <div class="viewer-container ztzf-cesium" id="viewer-container">
    <div class="viewer-container command-cesium" id="viewer-container">
      <div class="content">
        <slot name="content"></slot>
      </div>
applications/drone-command/src/page/index/top/index.vue
@@ -8,13 +8,12 @@
    <div class="top-bar__right">
      <div class="top-user">
        <div class="icon-box">
          <img class="gateway" @click="jumpMH" src="@/assets/images/mh.png" alt="进入门户" title="进入门户">
         <img class="gateway" @click="jumpMH" src="@/assets/images/mh.png" alt="进入门户" title="进入门户">
        </div>
        <el-dropdown>
        <el-dropdown popper-class="command-custom-dropdown">
          <span class="el-dropdown-link">
            <!-- <img v-if="userInfo.avatar" class="top-bar__img top-bar__user__img" :src="userInfo.avatar" alt="" /> -->
            <img class="top-bar__img" :src="defaultAva" alt="" />
            <img class="top-bar__img" :src="userInfo.avatar" alt="" />
          </span>
          <template #dropdown>
@@ -22,9 +21,9 @@
              <!--              <el-dropdown-item>
                <router-link to="/">{{ $t('navbar.dashboard') }}</router-link>
              </el-dropdown-item>-->
              <el-dropdown-item>
              <!-- <el-dropdown-item>
                <router-link to="/info/index">{{ $t('navbar.userinfo') }}</router-link>
              </el-dropdown-item>
              </el-dropdown-item> -->
              <el-dropdown-item @click="logout" divided>{{ $t('navbar.logOut') }}
              </el-dropdown-item>
            </el-dropdown-menu>
@@ -37,8 +36,6 @@
</template>
<script>
import defaultAva from '@/assets/images/defaultava.png'
import logo from '@/assets/images/topContainer/logo.png'
import { mapGetters } from 'vuex'
@@ -70,7 +67,6 @@
  data () {
    return {
      logoUrl: logo,
      defaultAva: defaultAva
    }
  },
  filters: {},
@@ -190,13 +186,10 @@
.top-bar__img {
  margin-top: 5px;
  width: 16px;
  height: 21px;
}
.top-bar__user__img {
  width: 24px;
  height: 24px;
  width: 20px;
  height: 20px;
  border: 2px solid #383874;
  border-radius: 50%;
}
.el-dropdown-link {
applications/drone-command/src/page/index/top/top-search.vue
@@ -1,6 +1,6 @@
<template>
  <el-autocomplete
    class="top-search ztzf-top-search"
    class="top-search command-top-search"
    popper-class="my-autocomplete"
    v-model="value"
    :fetch-suggestions="querySearch"
@@ -88,7 +88,7 @@
<style lang="scss">
.ztzf-top-search{
.command-top-search{
  display: flex !important;
  width: 150px;
  align-items: center;
applications/drone-command/src/store/modules/user.js
@@ -8,6 +8,7 @@
import { setStore, getStore } from '@/utils/store';
import { validatenull } from '@/utils/validate';
import { deepClone } from '@/utils/util';
import defaultAva from '@/assets/images/defaultava.png'
import {
  loginByUsername,
  loginBySocial,
@@ -335,6 +336,9 @@
      if (validatenull(userInfo.user_id) && validatenull(userInfo.account)) {
        state.userInfo = { user_name: 'unauth', role_name: 'unauth' };
      } else {
        if (validatenull(userInfo.avatar)) {
          userInfo.avatar = defaultAva;
        }
        state.userInfo = userInfo;
      }
      setStore({ name: 'userInfo', content: state.userInfo });
applications/drone-command/src/styles/common.scss
@@ -157,7 +157,7 @@
  bottom: 0;
}
.ztzf-cesium {
.command-cesium {
  .cesium-viewer {
    height: 100%;
    overflow: hidden;
@@ -226,7 +226,7 @@
  }
}
.ztzf-cesium {
.command-cesium {
  .cesium-viewer {
    height: 100%;
    overflow: hidden;
applications/drone-command/src/styles/common/cockpit.scss
@@ -1,5 +1,44 @@
/* 下拉菜单整体样式 */
.command-custom-dropdown {
    background: #1A1A2A !important;
    border-radius: 8px;
    border: none !important;
    .el-popper__arrow::before {
        background: #1A1A2A !important;
        border: 1px solid #1A1A2A !important;
    }
    /* 菜单项样式 */
    .el-dropdown-menu {
        background: transparent !important;
        .el-dropdown-menu__item {
            color: #fff !important;
            /* 文字颜色 */
            a {
                color: #fff !important;
                /* 文字颜色 */
            }
            &:hover {
                background: none !important;
            }
            &.is-disabled {
                color: #8f9bb3 !important;
            }
        }
        .el-dropdown-menu__item--divided {
            display: none;
        }
    }
}
// 数据驾驶舱专属
.ztzf-data-cockpit-search-input {
.command-data-cockpit-search-input {
    .el-input__wrapper {
        padding: 0 8px !important;
        background: #161B2C;
@@ -22,7 +61,7 @@
    }
}
.ztzf-data-cockpit-date-picker {
.command-data-cockpit-date-picker {
    box-shadow: none !important;
    background: #161B2C;
    border: none;
@@ -51,7 +90,7 @@
    }
}
.ztzf-data-cockpit-date-picker-popper {
.command-data-cockpit-date-picker-popper {
    background: #161B2C !important;
    border: none !important;
    border-radius: 0px 0px 8px 8px;
@@ -258,9 +297,9 @@
    }
}
.ztzf-data-cockpit-select {
.command-data-cockpit-select {
    .el-select__input {
        color: #8ac3fd !important;
        color: #ffffff !important;
    }
    .el-select__wrapper {
@@ -298,7 +337,7 @@
    }
}
.ztzf-data-cockpit-select-popper.el-select__popper {
.command-data-cockpit-select-popper.el-select__popper {
    border: none;
    background: #2E2E48 !important;
@@ -344,8 +383,41 @@
    }
}
.command-data-cockpit-cascader {
    .el-input__wrapper {
        --el-input-text-color: #161B2C !important;
    }
}
.command-data-cockpit-cascader-popper.el-cascader__dropdown {
    border: none;
    background: #2E2E48 !important;
    .el-cascader-panel {
        .el-cascader-menu {
            color: #fff;
            .el-cascader-node:hover,
            .el-cascader-node:focus {
                background: transparent !important;
            }
            .el-cascader-node.is-selectable.in-active-path {
                color: #fff;
            }
        }
    }
    .el-popper__arrow::before {
        background: #2E2E48 !important;
        border: 1px solid #2E2E48 !important;
    }
}
// 树形选择
.ztzf-data-cockpit-tree-select-popper {
.command-data-cockpit-tree-select-popper {
    background: #2E2E48 !important;
    border: none !important;
@@ -423,7 +495,7 @@
    }
}
.ztzf-data-cockpit-dialog {
.command-data-cockpit-dialog {
    padding: 20px;
    display: flex;
    flex-direction: column;
@@ -464,7 +536,7 @@
    }
}
.el-form.ztzf-dialog-history-search {
.el-form.command-dialog-history-search {
    .el-form-item {
        margin-left: 20px;
    }
@@ -478,7 +550,7 @@
        text-transform: none;
    }
    .ztzf-data-cockpit-search-input {
    .command-data-cockpit-search-input {
        .el-input__wrapper {
            background: #2E2E48;
        }
@@ -490,11 +562,11 @@
        }
    }
    .ztzf-data-cockpit-date-picker {
    .command-data-cockpit-date-picker {
        background: #2E2E48;
    }
    .ztzf-data-cockpit-date-picker-popper {
    .command-data-cockpit-date-picker-popper {
        background: #2E2E48 !important;
        .el-picker-panel {
@@ -557,7 +629,7 @@
    }
}
.ztzf-page-history-search {
.command-page-history-search {
    .el-form-item__label {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 500;
@@ -567,13 +639,13 @@
        text-transform: none;
    }
    .ztzf-data-cockpit-select {
    .command-data-cockpit-select {
        .el-select__wrapper {
            background: #1C1C31;
        }
    }
    .ztzf-data-cockpit-search-input {
    .command-data-cockpit-search-input {
        .el-input__wrapper {
            background: #1C1C31;
        }
@@ -585,11 +657,11 @@
        }
    }
    .ztzf-data-cockpit-date-picker {
    .command-data-cockpit-date-picker {
        background: #1C1C31;
    }
    .ztzf-data-cockpit-date-picker-popper {
    .command-data-cockpit-date-picker-popper {
        background: #1C1C31 !important;
        .el-picker-panel {
@@ -643,8 +715,8 @@
    }
}
.ztzf-dialog-history-search,
.ztzf-page-history-search {
.command-dialog-history-search,
.command-page-history-search {
    display: flex;
    .el-form-item {
@@ -699,20 +771,22 @@
    }
}
.ztzf-table-toolbar {
.command-table-toolbar {
    margin-top: 30px;
    .el-button {
        min-width: 88px;
        border: none;
    }
    .el-button.is-disabled, .el-button.is-disabled:hover {
    .el-button.is-disabled,
    .el-button.is-disabled:hover {
        background: #1A2652;
        border: none;
    }
}
.ztzf-table-container {
.command-table-container {
    margin-top: 20px;
    height: 0;
@@ -720,19 +794,19 @@
    display: flex;
    flex-direction: column;
    .ztzf-table-content {
    .command-table-content {
        height: 0;
        flex: 1;
        display: flex;
        flex-direction: column;
    }
    .ztzf-table-content-bg {
    .command-table-content-bg {
        background: #1A1A2A;
    }
}
.ztzf-data-cockpit-table {
.command-data-cockpit-table {
    height: 0;
    flex: 1;
    background: transparent !important;
@@ -750,8 +824,7 @@
    th.el-table__cell {
        height: 40px;
        border-bottom: 1px solid #323241;
        background: transparent !important;
        border-bottom: 1px solid #323241 !important;
        .cell {
            padding: 0;
@@ -769,8 +842,7 @@
    td.el-table__cell {
        padding: 9px 0;
        height: 56px;
        border-bottom: 1px solid #323241;
        background: transparent !important;
        border-bottom: 1px solid #323241 !important;
        .cell {
            padding: 0;
@@ -802,9 +874,18 @@
            }
        }
    }
    th.el-table__cell:not(.fixed-column),
    td.el-table__cell:not(.fixed-column) {
        background: transparent !important;
    }
    .fixed-column {
        background-color: #1A1A2A !important;
    }
}
.ztzf-table-pagination {
.command-table-pagination {
    padding-top: 30px;
    padding-bottom: 10px;
    display: flex;
@@ -873,7 +954,7 @@
    }
}
.ztzf-page-view-dialog {
.command-page-view-dialog {
    padding: 30px;
    display: flex;
    flex-direction: column;
@@ -907,10 +988,10 @@
            padding: 0 10px;
            .detail-title {
                margin-bottom: 30px;
                margin-bottom: 20px;
                font-family: Source Han Sans CN, Source Han Sans CN;
                font-weight: 500;
                font-size: 18px;
                font-size: 16px;
                color: #FFFFFF;
                text-align: left;
                font-style: normal;
@@ -950,10 +1031,10 @@
            }
            .ztzf-table-container {
            .command-table-container {
                height: 360px;
                .ztzf-table-pagination {
                .command-table-pagination {
                    justify-content: center;
                }
            }
@@ -981,9 +1062,22 @@
                    display: flex;
                    align-items: center;
                    .el-input {
                    .el-input,
                    .el-input-number {
                        width: 0;
                        flex: 1;
                    }
                    .el-cascader {
                        width: 0;
                        flex: 1;
                        display: flex;
                        align-items: center;
                        border: none;
                        .el-input__wrapper {
                            box-shadow: none !important;
                        }
                    }
                    .el-textarea {
@@ -1003,11 +1097,11 @@
                        box-shadow: none;
                    }
                    .ztzf-data-cockpit-date-picker {
                    .command-data-cockpit-date-picker {
                        background: #2E2E48;
                    }
                    .ztzf-data-cockpit-date-picker-popper {
                    .command-data-cockpit-date-picker-popper {
                        background: #2E2E48 !important;
                        .el-picker-panel {
@@ -1045,6 +1139,7 @@
                .el-input {
                    .el-input__wrapper {
                        background: #2E2E48 !important;
                        box-shadow: none;
                        .el-input__inner {
                            color: #ffffff;
@@ -1071,7 +1166,7 @@
    }
}
.ztzf-page-view-message-box {
.command-page-view-message-box {
    background: #1A1A2A;
    border-radius: 6px;
    border: 1px solid #2E2E46;
@@ -1109,18 +1204,18 @@
    }
}
.ztzf-message-box-confirm {
.command-message-box-confirm {
    background: #284FE3 !important;
    border-color: #284FE3 !important;
}
.ztzf-message-box-cancel {
.command-message-box-cancel {
    background: #2B2B4C !important;
    border-color: #2B2B4C !important;
    color: #FFFFFF !important;
}
.ztzf-page-map-view-dialog {
.command-page-map-view-dialog {
    padding: 30px;
    display: flex;
    flex-direction: column;
@@ -1190,10 +1285,10 @@
                    flex-direction: column;
                    .detail-title {
                        margin-bottom: 30px;
                        margin-bottom: 20px;
                        font-family: Source Han Sans CN, Source Han Sans CN;
                        font-weight: 500;
                        font-size: 18px;
                        font-size: 16px;
                        color: #FFFFFF;
                        text-align: left;
                        font-style: normal;
@@ -1207,7 +1302,7 @@
                            align-items: center;
                            .label {
                                width: 112px;
                                width: 96px;
                                font-family: Source Han Sans CN, Source Han Sans CN;
                                font-weight: 500;
                                font-size: 14px;
@@ -1234,10 +1329,11 @@
                    }
                    .ztzf-table-container {
                        height: 360px;
                    .command-table-container {
                        margin-top: 0;
                        margin-bottom: 20px;
                        .ztzf-table-pagination {
                        .command-table-pagination {
                            justify-content: center;
                        }
                    }
@@ -1298,11 +1394,11 @@
                                color: #fff;
                            }
                            .ztzf-data-cockpit-date-picker {
                            .command-data-cockpit-date-picker {
                                background: #2E2E48;
                            }
                            .ztzf-data-cockpit-date-picker-popper {
                            .command-data-cockpit-date-picker-popper {
                                background: #2E2E48 !important;
                                .el-picker-panel {
@@ -1422,7 +1518,7 @@
                        margin-left: 16px;
                        width: 96px;
                        height: 36px;
                        &:first-child {
                            margin-left: 0;
                        }
applications/drone-command/src/styles/element-ui.scss
@@ -110,7 +110,7 @@
    }
}
.ztzf-drawer-body-basic-container {
.command-drawer-body-basic-container {
    .el-drawer__header {
        margin-bottom: 0;
    }
@@ -123,7 +123,7 @@
}
.ztzf-el-card__body {
.command-el-card__body {
    .el-card__body {
        .avue-form {
@@ -132,7 +132,7 @@
    }
}
.ztzf-public-general-avue-crud {
.command-public-general-avue-crud {
    .el-table {
        tr {
            min-height: 30px !important;
@@ -155,7 +155,7 @@
    }
}
.ztzf-layer-input {
.command-layer-input {
    .el-input__wrapper {
        padding: 0 8px !important;
        // background: #2D2D2D;
@@ -179,18 +179,18 @@
    }
}
.ztzf-button {
.command-button {
    background-color: transparent !important;
    color: #fff !important;
}
.ztzf-el-tree {
.command-el-tree {
    background-color: transparent !important;
    --el-tree-node-hover-bg-color: transparent !important;
    color: #fff !important;
}
.ztzf-layer-search {
.command-layer-search {
    color: #fff !important;
    background-color: transparent !important;
@@ -226,7 +226,7 @@
    }
}
.ztzf-select-popper.el-select__popper {
.command-select-popper.el-select__popper {
    border: #2D2D2D !important;
    .el-select-dropdown {
@@ -277,7 +277,7 @@
}
//时间日期选择器
.ztzf-layer-date-picker {
.command-layer-date-picker {
    box-shadow: none !important;
    background: transparent;
    border: 1px solid #fff;
@@ -315,7 +315,7 @@
}
// 日期下拉框
.ztzf-layer-date-picker-popper {
.command-layer-date-picker-popper {
    background: #012350 !important;
    border: 1px solid !important;
    border-radius: 0px 0px 8px 8px;
@@ -517,7 +517,7 @@
    }
}
.ztzf-layer-time-picker-popper {
.command-layer-time-picker-popper {
    background: rgba(0, 0, 0, 0.8) !important;
    border-radius: 0px 0px 8px 8px;
    border: 1px solid !important;
@@ -573,7 +573,7 @@
    }
}
.ztzf-custom-qrcode-popover {
.command-custom-qrcode-popover {
    //background: url("@/assets/images/home/eventOverviewDetail/qrCodeBg.png") no-repeat center center !important;
    //background-size: cover !important;
    border: none !important;
@@ -617,7 +617,7 @@
    }
}
.ztzf-dialog-video {
.command-dialog-video {
    background: #0f1929;
    box-shadow: inset 0px -50px 50px 0px rgba(27, 148, 255, 0.13);
    border-radius: 20px 0px 0px 0px;
@@ -670,7 +670,7 @@
}
//======以下针对表格功能 增删查改样式和弹窗=========
.ztzf-dialog-mange {
.command-dialog-mange {
    .el-dialog__body {
        padding: 2rem 2rem 4rem;
        flex: 1;
@@ -693,7 +693,7 @@
    }
}
.ztzf-search-mange {
.command-search-mange {
    height: 40px;
    .search-contain {
@@ -702,7 +702,7 @@
    }
}
.ztzf-form-mange {
.command-form-mange {
    .el-form-item {
        width: 300px;
@@ -716,7 +716,7 @@
    }
}
.ztzf-form-search {
.command-form-search {
    .el-form-item {
        width: 100%;
@@ -731,7 +731,7 @@
    }
}
.ztzf-table-mange {
.command-table-mange {
    th.el-table__cell {
        color: rgba(0, 0, 0, .85);
        background-color: #fafafa;
applications/drone-command/src/utils/cesium/adminBoundary.js
New file
@@ -0,0 +1,88 @@
import * as Cesium from 'cesium'
import jaGeojsonRaw from '@/assets/geojson/ja.geojson?raw'
const DEFAULT_OPTIONS = {
    strokeColor: '#00F9EC',
    strokeWidth: 2,
    fillColor: '#FFFFFF',
    fillAlpha: 0.05,
    labelFont: '14px Source Han Sans CN',
    labelFill: '#FFFFFF',
    labelOutline: '#18305C',
    labelOutlineAlpha: 1,
    zoomTo: true,
}
export const loadJaAdminBoundary = async (viewer, options = {}) => {
    if (!viewer) return null
    const config = { ...DEFAULT_OPTIONS, ...options }
    const geojson = JSON.parse(jaGeojsonRaw)
    const boundarySource = await Cesium.GeoJsonDataSource.load(geojson, {
        stroke: Cesium.Color.fromCssColorString(config.strokeColor),
        strokeWidth: config.strokeWidth,
        fill: Cesium.Color.fromCssColorString(config.fillColor).withAlpha(config.fillAlpha),
        clampToGround: true,
    })
    viewer.dataSources.add(boundarySource)
    const lineSource = new Cesium.CustomDataSource('jaBoundaryLineSource')
    boundarySource.entities.values.forEach(entity => {
        const polygon = entity.polygon
        if (!polygon) return
        const hierarchy = polygon.hierarchy?.getValue?.(viewer.clock.currentTime)
        const positions = hierarchy?.positions
        if (!positions?.length) return
        lineSource.entities.add({
            polyline: {
                positions,
                clampToGround: true,
                width: config.strokeWidth,
                material: Cesium.Color.fromCssColorString(config.strokeColor),
            },
        })
    })
    viewer.dataSources.add(lineSource)
    const labelSource = new Cesium.CustomDataSource('jaBoundaryLabelSource')
    geojson.features?.forEach(feature => {
        const name = feature?.properties?.name
        const point = feature?.properties?.centroid || feature?.properties?.center
        if (!name || !Array.isArray(point) || point.length < 2) return
        labelSource.entities.add({
            position: Cesium.Cartesian3.fromDegrees(point[0], point[1]),
            label: {
                text: name,
                font: config.labelFont,
                fillColor: Cesium.Color.fromCssColorString(config.labelFill),
                outlineColor: Cesium.Color.fromCssColorString(config.labelOutline).withAlpha(
                    config.labelOutlineAlpha
                ),
                outlineWidth: 2,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
                verticalOrigin: Cesium.VerticalOrigin.CENTER,
                horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                disableDepthTestDistance: Number.POSITIVE_INFINITY,
            },
        })
    })
    viewer.dataSources.add(labelSource)
    if (config.zoomTo) {
        await viewer.flyTo(boundarySource, {
            duration: 0,
            offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-75), 0),
        })
    }
    return { boundarySource, lineSource, labelSource }
}
export const removeJaAdminBoundary = (viewer, sources) => {
    if (!viewer || !sources) return
    const { boundarySource, lineSource, labelSource } = sources
    if (boundarySource) viewer.dataSources.remove(boundarySource)
    if (lineSource) viewer.dataSources.remove(lineSource)
    if (labelSource) viewer.dataSources.remove(labelSource)
}
applications/drone-command/src/utils/cesium/publicCesium.js
@@ -100,7 +100,7 @@
            dockOptions = {},
            boundaryChange,
            terrainLoadCallback,
            boundaryColor = '#7FFFD4',
            boundaryColor = '#00F9EC',
            dockRangeType = 1,
            useDockHeight = false
        } = options
applications/drone-command/src/views/aICrudTemplate/aICrudTemplateApi.js
@@ -5,7 +5,7 @@
    return request({
        url: `/drone-fw/device/fwDevice/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
applications/drone-command/src/views/aICrudTemplate/index.vue
@@ -35,8 +35,8 @@
                <el-col :span="4">
                    <el-form-item label="时间">
                        <el-date-picker
                            popper-class="ztzf-date-picker-popper"
                            class="ztzf-date-picker"
                            popper-class="command-date-picker-popper"
                            class="command-date-picker"
                            v-model="dateRange"
                            type="daterange"
                            range-separator="至"
applications/drone-command/src/views/algorithmMange/algorithmMange.vue
@@ -1,6 +1,6 @@
<template>
  <div class="algorithmMange">
    <div class="ztzf-form-search">
    <div class="command-form-search">
      <el-form :model="params" inline>
        <el-row :gutter="24">
          <el-col :span="4">
@@ -10,7 +10,7 @@
          </el-col>
          <el-col :span="4">
          <el-form-item label="算法类型:">
            <el-select class="ztzf-select" :teleported="false" v-model="params.algTypeCode" placeholder="请选择算法类型" clearable>
            <el-select class="command-select" :teleported="false" v-model="params.algTypeCode" placeholder="请选择算法类型" clearable>
              <el-option v-for="item in sfTypes" :key="item.id" :label="item.dictValue" :value="item.dictKey" ></el-option>
            </el-select>
          </el-form-item>
@@ -23,7 +23,7 @@
      </el-form>
    </div>
    <div class="mange-table">
            <el-table border :data="tableList" class="ztzf-table-mange">
            <el-table border :data="tableList" class="command-table-mange">
                <el-table-column label="序号" type="index" width="60"></el-table-column>
        <el-table-column prop="alg_name" label="算法名称" align="center" show-overflow-tooltip></el-table-column>
        <el-table-column prop="alg_type" label="算法类型" align="center"></el-table-column>
@@ -69,7 +69,7 @@
            </el-table>
        </div>
        <div class="pagination" v-if="tableList.length > 0">
            <el-pagination class="ztzf-pagination" popper-class="custom-pagination-dropdown" background
            <el-pagination class="command-pagination" popper-class="custom-pagination-dropdown" background
                :page-sizes="[10, 20, 30, 40, 50, 100]" :size="params.size" v-model:current-page="params.current"
                v-model:page-size="params.size" layout="total, sizes, prev, pager, next, jumper" :total="total"
                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
@@ -105,12 +105,12 @@
  </commonDialog>
  <commonDialog :isShowView="isShowEditView" title="编辑"  addEditView="edit"  @submit="submit(ruleFormRef)" @cancel="isShowEditView = false">
    <template #default>
      <el-form class="ztzf-form-mange" ref="ruleFormRef" :model="editParams" :rules="rules" inline>
      <el-form class="command-form-mange" ref="ruleFormRef" :model="editParams" :rules="rules" inline>
        <el-form-item label="算法名称">
          <el-input v-model="editParams.model_name" disabled />
        </el-form-item>
        <el-form-item label="算法类型">
          <el-select class="ztzf-select" :teleported="false" v-model="editParams.alg_type" disabled>
          <el-select class="command-select" :teleported="false" v-model="editParams.alg_type" disabled>
            <el-option v-for="item in sfTypes" :key="item.id" :label="item.dictValue" :value="item.dictKey" ></el-option>
          </el-select>
        </el-form-item>
@@ -121,7 +121,7 @@
          <el-input v-model="editParams.pass_rate" placeholder="请输入最高准确率" clearable />
        </el-form-item>
        <el-form-item label="事件生成类型" prop="event_type">
          <el-select class="ztzf-select" v-model="editParams.event_type" placeholder="请选择" clearable>
          <el-select class="command-select" v-model="editParams.event_type" placeholder="请选择" clearable>
            <el-option label="常规类" value="0"></el-option>
            <el-option label="紧急类" value="1"></el-option>
          </el-select>
@@ -313,7 +313,7 @@
      background: center center no-repeat none !important;
      color: #8eb8ea !important;
    }
    :deep(.ztzf-select){
    :deep(.command-select){
      .el-select__selection {
        width: 200px;
      }
applications/drone-command/src/views/api.txt
@@ -1,9 +1,9 @@
## 分页
## 列表
**接口地址**:`/device/fwDevice/page`
**接口地址**:`/drone-fw/area/fwAreaDivide/list`
**请求方式**:`GET`
@@ -15,7 +15,7 @@
**响应数据类型**:`*/*`
**接口描述**:<p>传入fwDevice</p>
**接口描述**:<p>传入filterSelected或sceneId</p>
@@ -27,12 +27,8 @@
| 参数名称 | 参数说明 | 请求类型    | 是否必须 | 数据类型 | schema |
| -------- | -------- | ----- | -------- | -------- | ------ |
|belongDept|所属部门|query|false|integer(int64)||
|current|当前页|query|false|integer(int32)||
|deviceName|设备名称|query|false|string||
|deviceType|设备类型(察打一体/便捷侦测箱/反制枪)|query|false|string||
|effectiveRangeKmIsNotNull|有效范围是否为空:0-不处理 1-不为空 2-为空|query|false|integer(int32)||
|size|每页的数量|query|false|integer(int32)||
|filterSelected|是否过滤已被选择的数据(1过滤 0不过滤)|query|false|integer(int32)||
|sceneId|场景id|query|false|integer(int64)||
**响应状态**:
@@ -40,7 +36,7 @@
| 状态码 | 说明 | schema |
| -------- | -------- | ----- | 
|200|OK|R«IPage«FwDeviceVO»»|
|200|OK|R«List«FwAreaDivide对象»»|
|401|Unauthorized||
|403|Forbidden||
|404|Not Found||
@@ -52,152 +48,32 @@
| 参数名称 | 参数说明 | 类型 | schema |
| -------- | -------- | ----- |----- | 
|code|状态码|integer(int32)|integer(int32)|
|data|承载数据|IPage«FwDeviceVO»|IPage«FwDeviceVO»|
|&emsp;&emsp;current||integer(int64)||
|&emsp;&emsp;pages||integer(int64)||
|&emsp;&emsp;records||array|FwDeviceVO|
|&emsp;&emsp;&emsp;&emsp;azimuth|方位角 0-360°|integer||
|&emsp;&emsp;&emsp;&emsp;batteryPct|电量百分比 0-100|integer||
|&emsp;&emsp;&emsp;&emsp;belongDept|所属部门id|integer||
|&emsp;&emsp;&emsp;&emsp;belongDeptName|所属部门名称|string||
|&emsp;&emsp;&emsp;&emsp;charger|负责人|string||
|&emsp;&emsp;&emsp;&emsp;contactPhone|联系电话|string||
|&emsp;&emsp;&emsp;&emsp;createTime|创建时间|string||
|&emsp;&emsp;&emsp;&emsp;detectTargetCnt|侦测目标数量|integer||
|&emsp;&emsp;&emsp;&emsp;deviceAtt|设备属性(无线电/光电/雷达)|string||
|&emsp;&emsp;&emsp;&emsp;deviceModel|型号|string||
|&emsp;&emsp;&emsp;&emsp;deviceName|设备名称|string||
|&emsp;&emsp;&emsp;&emsp;deviceSpecification|规格|string||
|&emsp;&emsp;&emsp;&emsp;deviceType|设备类型(察打一体/便捷侦测箱/反制枪)|string||
|&emsp;&emsp;&emsp;&emsp;effectiveRangeKm|有效范围 km|number||
|&emsp;&emsp;&emsp;&emsp;elevation|俯仰角 -90-+90°|integer||
|&emsp;&emsp;&emsp;&emsp;id|主键|integer||
|&emsp;&emsp;&emsp;&emsp;manufacturer|生产厂商|string||
|&emsp;&emsp;&emsp;&emsp;purpose|用途|string||
|&emsp;&emsp;&emsp;&emsp;remark|备注|string||
|&emsp;&emsp;&emsp;&emsp;source|来源|string||
|&emsp;&emsp;&emsp;&emsp;status|运行状态(0:在线/1:离线/2:故障/3:报废)|integer||
|&emsp;&emsp;&emsp;&emsp;workMode|工作模式:1.侦测中/2.信号干扰中/3.诱导驱离中/4.待机|string||
|&emsp;&emsp;size||integer(int64)||
|&emsp;&emsp;total||integer(int64)||
|msg|返回消息|string||
|success|是否成功|boolean||
**响应示例**:
```javascript
{
    "code": 0,
    "data": {
        "current": 0,
        "pages": 0,
        "records": [
            {
                "azimuth": 0,
                "batteryPct": 0,
                "belongDept": 0,
                "belongDeptName": "",
                "charger": "",
                "contactPhone": "",
                "createTime": "",
                "detectTargetCnt": 0,
                "deviceAtt": "",
                "deviceModel": "",
                "deviceName": "",
                "deviceSpecification": "",
                "deviceType": "",
                "effectiveRangeKm": 0,
                "elevation": 0,
                "id": 0,
                "manufacturer": "",
                "purpose": "",
                "remark": "",
                "source": "",
                "status": 0,
                "workMode": ""
            }
        ],
        "size": 0,
        "total": 0
    },
    "msg": "",
    "success": true
}
```
## 详情
**接口地址**:`/device/fwDevice/detail`
**请求方式**:`GET`
**请求数据类型**:`application/x-www-form-urlencoded`
**响应数据类型**:`*/*`
**接口描述**:<p>传入fwDevice</p>
**请求参数**:
**请求参数**:
| 参数名称 | 参数说明 | 请求类型    | 是否必须 | 数据类型 | schema |
| -------- | -------- | ----- | -------- | -------- | ------ |
|id|主键|query|true|integer(int64)||
**响应状态**:
| 状态码 | 说明 | schema |
| -------- | -------- | ----- |
|200|OK|R«FwDeviceVO»|
|401|Unauthorized||
|403|Forbidden||
|404|Not Found||
**响应参数**:
| 参数名称 | 参数说明 | 类型 | schema |
| -------- | -------- | ----- |----- |
|code|状态码|integer(int32)|integer(int32)|
|data|承载数据|FwDeviceVO|FwDeviceVO|
|&emsp;&emsp;azimuth|方位角 0-360°|integer(int32)||
|&emsp;&emsp;batteryPct|电量百分比 0-100|integer(int32)||
|&emsp;&emsp;belongDept|所属部门id|integer(int64)||
|&emsp;&emsp;belongDeptName|所属部门名称|string||
|&emsp;&emsp;charger|负责人|string||
|&emsp;&emsp;contactPhone|联系电话|string||
|data|承载数据|array|FwAreaDivide对象|
|&emsp;&emsp;areaCode|区域编码|string||
|&emsp;&emsp;areaName|区域名称|string||
|&emsp;&emsp;areaSize|区域面积(km²)|number||
|&emsp;&emsp;areaType|区域类型:报警区/重点管制区|string||
|&emsp;&emsp;controlLevel|管控级别:日常/重点/严密|string||
|&emsp;&emsp;createDept|创建部门|integer(int64)||
|&emsp;&emsp;createTime|创建时间|string(date-time)||
|&emsp;&emsp;detectTargetCnt|侦测目标数量|integer(int32)||
|&emsp;&emsp;deviceAtt|设备属性(无线电/光电/雷达)|string||
|&emsp;&emsp;deviceModel|型号|string||
|&emsp;&emsp;deviceName|设备名称|string||
|&emsp;&emsp;deviceSpecification|规格|string||
|&emsp;&emsp;deviceType|设备类型(察打一体/便捷侦测箱/反制枪)|string||
|&emsp;&emsp;effectiveRangeKm|有效范围 km|number||
|&emsp;&emsp;elevation|俯仰角 -90-+90°|integer(int32)||
|&emsp;&emsp;id|主键|integer(int64)||
|&emsp;&emsp;manufacturer|生产厂商|string||
|&emsp;&emsp;purpose|用途|string||
|&emsp;&emsp;remark|备注|string||
|&emsp;&emsp;source|来源|string||
|&emsp;&emsp;status|运行状态(0:在线/1:离线/2:故障/3:报废)|integer(int32)||
|&emsp;&emsp;workMode|工作模式:1.侦测中/2.信号干扰中/3.诱导驱离中/4.待机|string||
|&emsp;&emsp;createUser|创建人|integer(int64)||
|&emsp;&emsp;deviceIds|关联设备ID,逗号分隔|string||
|&emsp;&emsp;flyDateEnd|可飞行日期时间-结束|string(date-time)||
|&emsp;&emsp;flyDateStart|可飞行日期时间-开始|string(date-time)||
|&emsp;&emsp;geom|经纬度面(存储地理面数据)|string||
|&emsp;&emsp;id|主键id|integer(int64)||
|&emsp;&emsp;isDeleted|是否已删除|integer(int32)||
|&emsp;&emsp;latitude|区域中心纬度|number(double)||
|&emsp;&emsp;longitude|区域中心经度|number(double)||
|&emsp;&emsp;policeStationContactPerson|派出所联系人|string||
|&emsp;&emsp;policeStationContactPhone|派出所联系电话|string||
|&emsp;&emsp;policeStationId|关联派出所id|integer(int64)||
|&emsp;&emsp;policeStationName|派出所名称|string||
|&emsp;&emsp;responseMechanism|响应机制内容|string||
|&emsp;&emsp;status|业务状态|integer(int32)||
|&emsp;&emsp;triggerCondition|触发条件|string||
|&emsp;&emsp;updateTime|更新时间|string(date-time)||
|&emsp;&emsp;updateUser|更新人|integer(int64)||
|msg|返回消息|string||
|success|是否成功|boolean||
@@ -206,134 +82,35 @@
```javascript
{
    "code": 0,
    "data": {
        "azimuth": 0,
        "batteryPct": 0,
        "belongDept": 0,
        "belongDeptName": "",
        "charger": "",
        "contactPhone": "",
        "createTime": "",
        "detectTargetCnt": 0,
        "deviceAtt": "",
        "deviceModel": "",
        "deviceName": "",
        "deviceSpecification": "",
        "deviceType": "",
        "effectiveRangeKm": 0,
        "elevation": 0,
        "id": 0,
        "manufacturer": "",
        "purpose": "",
        "remark": "",
        "source": "",
        "status": 0,
        "workMode": ""
    },
    "msg": "",
    "success": true
}
```
## 新增或修改
**接口地址**:`/device/fwDevice/submit`
**请求方式**:`POST`
**请求数据类型**:`application/json`
**响应数据类型**:`*/*`
**接口描述**:<p>传入fwDevice</p>
**请求示例**:
```javascript
{
  "belongDept": 0,
  "charger": "",
  "contactPhone": "",
  "deviceAtt": "",
  "deviceModel": "",
  "deviceName": "",
  "deviceSpecification": "",
  "deviceStatusList": [],
  "deviceType": "",
  "effectiveRangeKm": 0,
  "effectiveRangeKmIsNotNull": 0,
  "id": 0,
  "manufacturer": "",
  "purpose": "",
  "source": ""
}
```
**请求参数**:
**请求参数**:
| 参数名称 | 参数说明 | 请求类型    | 是否必须 | 数据类型 | schema |
| -------- | -------- | ----- | -------- | -------- | ------ |
|fwDevice|fwDevice|body|true|FwDeviceDTO|FwDeviceDTO|
|&emsp;&emsp;belongDept|所属部门||false|integer(int64)||
|&emsp;&emsp;charger|负责人||false|string||
|&emsp;&emsp;contactPhone|联系电话||false|string||
|&emsp;&emsp;deviceAtt|设备属性(无线电/光电/雷达)||false|string||
|&emsp;&emsp;deviceModel|型号||false|string||
|&emsp;&emsp;deviceName|设备名称||true|string||
|&emsp;&emsp;deviceSpecification|规格||false|string||
|&emsp;&emsp;deviceStatusList|设备状态,多个状态用逗号隔开||false|array|string|
|&emsp;&emsp;deviceType|设备类型(察打一体/便捷侦测箱/反制枪)||false|string||
|&emsp;&emsp;effectiveRangeKm|有效范围 km||false|number||
|&emsp;&emsp;effectiveRangeKmIsNotNull|是否有范围 0不过滤 1有 2没有||false|integer(int32)||
|&emsp;&emsp;id|主键||false|integer(int64)||
|&emsp;&emsp;manufacturer|生产厂商||false|string||
|&emsp;&emsp;purpose|用途||false|string||
|&emsp;&emsp;source|来源||false|string||
**响应状态**:
| 状态码 | 说明 | schema |
| -------- | -------- | ----- |
|200|OK|R|
|201|Created||
|401|Unauthorized||
|403|Forbidden||
|404|Not Found||
**响应参数**:
| 参数名称 | 参数说明 | 类型 | schema |
| -------- | -------- | ----- |----- |
|code|状态码|integer(int32)|integer(int32)|
|data|承载数据|object||
|msg|返回消息|string||
|success|是否成功|boolean||
**响应示例**:
```javascript
{
    "code": 0,
    "data": {},
    "data": [
        {
            "areaCode": "",
            "areaName": "",
            "areaSize": 0,
            "areaType": "",
            "controlLevel": "",
            "createDept": 0,
            "createTime": "",
            "createUser": 0,
            "deviceIds": "",
            "flyDateEnd": "",
            "flyDateStart": "",
            "geom": "",
            "id": 0,
            "isDeleted": 0,
            "latitude": 0,
            "longitude": 0,
            "policeStationContactPerson": "",
            "policeStationContactPhone": "",
            "policeStationId": 0,
            "policeStationName": "",
            "responseMechanism": "",
            "status": 0,
            "triggerCondition": "",
            "updateTime": "",
            "updateUser": 0
        }
    ],
    "msg": "",
    "success": true
}
applications/drone-command/src/views/areaManage/areaStatistics/FormDiaLog.vue
@@ -1,5 +1,5 @@
<template>
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]"
    <el-dialog class="command-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]"
        :close-on-click-modal="false" width="520px">
        <div class="detail-container">
            <el-row>
applications/drone-command/src/views/areaManage/areaStatistics/index.vue
@@ -1,13 +1,13 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="区域名称" prop="areaName">
                <el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.areaName" placeholder="请输入"
                <el-input class="command-data-cockpit-search-input" v-model="searchParams.areaName" placeholder="请输入"
                    clearable @clear="handleSearch" />
            </el-form-item>
            <el-form-item label="区域类型" prop="areaType">
                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.areaType" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.areaType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
@@ -15,7 +15,7 @@
            </el-form-item>
            <el-form-item label="场景类型" prop="sceneType">
                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.sceneType" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.sceneType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
@@ -28,9 +28,9 @@
            </el-form-item>
        </el-form>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list">
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="areaName" show-overflow-tooltip label="区域名称" />
                    <el-table-column prop="areaType" show-overflow-tooltip label="区域类型">
@@ -57,8 +57,8 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
                <el-pagination popper-class="ztzf-data-cockpit-select-popper"
            <div class="command-table-pagination">
                <el-pagination popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
            </div>
applications/drone-command/src/views/areaManage/defenseZone/FormDiaLog.vue
@@ -1,8 +1,16 @@
<template>
    <el-dialog class="ztzf-page-map-view-dialog" v-model="visible" :show-close="false" :close-on-click-modal="false">
    <el-dialog class="command-page-map-view-dialog" v-model="visible" :show-close="false" :close-on-click-modal="false">
        <div class="dialog-container">
            <div class="left-container">
                <div class="leftMap ztzf-cesium" id="mapContainer"></div>
                <CommonCesiumMap
                    ref="mapRef"
                    class="leftMap command-cesium"
                    :active="visible"
                    :flat-mode="false"
                    :terrain="true"
                    :layer-mode="4"
                    :boundary="false"
                />
            </div>
            <div class="right-container">
@@ -24,9 +32,9 @@
                    </el-row>
                    <div class="detail-title">关联场景</div>
                    <div class="ztzf-table-container">
                        <div class="ztzf-table-content">
                            <el-table class="ztzf-data-cockpit-table" :data="sceneList" row-key="id">
                    <div class="command-table-container">
                        <div class="command-table-content">
                            <el-table class="command-data-cockpit-table" :data="sceneList" row-key="id">
                                <el-table-column prop="sceneName" label="场景名称" />
                                <el-table-column prop="sceneType" label="场景类型">
                                    <template v-slot="{ row }">
@@ -41,7 +49,7 @@
                <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly"
                    label-width="96px">
                    <el-form-item label="防区名称" prop="zoneName">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.zoneName" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.zoneName" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                    <el-form-item label="防区面积" prop="zoneArea">
@@ -54,12 +62,12 @@
                                关联场景
                            </div>
                            <el-input class="ztzf-data-cockpit-search-input" v-model="searchName" placeholder="请输入"
                            <el-input class="command-data-cockpit-search-input" v-model="searchName" placeholder="请输入"
                                clearable></el-input>
                        </div>
                        <el-form-item prop="defenseSceneIds" label-width="0">
                            <el-table class="ztzf-data-cockpit-table" ref="sceneTableRef"
                            <el-table class="command-data-cockpit-table" ref="sceneTableRef"
                                :data="sceneList.filter(item => item.sceneName.includes(searchName))" row-key="id"
                                @selection-change="handleSceneSelectionChange">
                                <el-table-column type="selection" width="55" :reserve-selection="true" />
@@ -92,7 +100,7 @@
import { fwDefenseZoneDetailApi, fwDefenseZoneSubmitApi } from './defenseZoneApi'
import { fwDefenseSceneListApi } from '@/views/areaManage/sceneConfig/sceneConfigApi'
import { fieldRules, geomAnalysis, getDictLabel } from '@ztzf/utils'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
import { DrawPolygon } from '@/utils/cesium/DrawPolygon'
import { cartesian3Convert } from '@/utils/cesium/mapUtil'
import * as turf from '@turf/turf'
@@ -119,6 +127,7 @@
const sceneList = ref([]) // 关联场景列表
const selectedSceneRows = ref([]) // 选中场景
const dictObj = inject('dictObj')
const mapRef = ref(null)
let viewer
let drawPolygonExample
let pointList = []
@@ -209,14 +218,9 @@
// 初始化地图实例
function initMap () {
    const publicCesiumInstance = new PublicCesium({
        dom: 'mapContainer',
        flatMode: false,
        terrain: true,
        layerMode: 4,
        boundary: false,
    })
    viewer = publicCesiumInstance.getViewer()
    if (viewer) return
    const map = mapRef.value?.getMap()
    viewer = map?.viewer || null
}
// 绘制完成回调
@@ -253,6 +257,22 @@
        noClosedList.map(item => ({ lng: item.longitude, lat: item.latitude }))
    )
    drawPolygonExample.subscribe('getPolygonPositions', drawFinished)
    if (noClosedList.length) {
        const result = noClosedList.map(item => [item.longitude, item.latitude]).flat()
        const tempEntity = viewer.entities?.add({
            position: Cesium.Cartesian3.fromDegrees(result[0], result[1]),
            polyline: {
                positions: Cesium.Cartesian3.fromDegreesArray(result),
                clampToGround: true,
                width: 1,
                material: Cesium.Color.TRANSPARENT,
            },
        })
        if (tempEntity) {
            viewer.flyTo(tempEntity, { duration: 0 })
            viewer.entities.remove(tempEntity)
        }
    }
}
// 查看面
@@ -270,7 +290,7 @@
            material: Cesium.Color.RED,
        },
    })
    viewer.flyTo(mian)
    viewer.flyTo(mian, { duration: 0 })
}
// 打开弹框
applications/drone-command/src/views/areaManage/defenseZone/defenseZoneApi.js
@@ -5,7 +5,7 @@
    return request({
        url: `/drone-fw/area/fwDefenseZone/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
applications/drone-command/src/views/areaManage/defenseZone/index.vue
@@ -1,8 +1,8 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="防区名称" prop="zoneName">
                <el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.zoneName" placeholder="请输入"
                <el-input class="command-data-cockpit-search-input" v-model="searchParams.zoneName" placeholder="请输入"
                    clearable @clear="handleSearch" />
            </el-form-item>
@@ -12,14 +12,14 @@
            </el-form-item>
        </el-form>
        <div class="ztzf-table-toolbar">
        <div class="command-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="openForm('add')">新增</el-button>
            <el-button :icon="Delete" color="#1A2652" type="primary" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="zoneName" show-overflow-tooltip label="防区名称" />
@@ -37,8 +37,8 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
                <el-pagination popper-class="ztzf-data-cockpit-select-popper"
            <div class="command-table-pagination">
                <el-pagination popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
            </div>
@@ -103,9 +103,9 @@
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'ztzf-page-view-message-box',
        confirmButtonClass: 'ztzf-message-box-confirm',
        cancelButtonClass: 'ztzf-message-box-cancel',
        customClass: 'command-page-view-message-box',
        confirmButtonClass: 'command-message-box-confirm',
        cancelButtonClass: 'command-message-box-cancel',
    })
    const ids = row ? row.id : selectedIds.value.join(',')
    await fwDefenseZoneRemoveApi({ ids })
applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
@@ -1,9 +1,17 @@
<template>
    <el-dialog class="ztzf-page-map-view-dialog" v-model="visible" :show-close="false"
    <el-dialog class="command-page-map-view-dialog" v-model="visible" :show-close="false"
        :close-on-click-modal="false">
        <div class="dialog-container">
            <div class="left-container">
                <div class="leftMap ztzf-cesium" id="mapContainer"></div>
                <CommonCesiumMap
                    ref="mapRef"
                    class="leftMap command-cesium"
                    :active="visible"
                    :flat-mode="false"
                    :terrain="true"
                    :layer-mode="4"
                    :boundary="false"
                />
            </div>
            <div class="right-container">
@@ -53,9 +61,9 @@
                    </el-row>
                    <div class="detail-title">关联设备</div>
                    <div class="ztzf-table-container">
                        <div class="ztzf-table-content">
                            <el-table class="ztzf-data-cockpit-table" ref="deviceTableRef" :data="deviceOptions"
                    <div class="command-table-container">
                        <div class="command-table-content">
                            <el-table class="command-data-cockpit-table" ref="deviceTableRef" :data="deviceOptions"
                                row-key="id">
                                <el-table-column prop="deviceName" label="设备名称" />
                                <el-table-column prop="deviceType" label="类型">
@@ -71,7 +79,7 @@
                <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly"
                    label-width="96px">
                    <el-form-item label="区域名称" prop="areaName">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.areaName" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.areaName" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                    <el-form-item label="区域位置" prop="longitude">
@@ -81,35 +89,35 @@
                        <div class="val">{{ formData.areaSize || '-' }}k㎡</div>
                    </el-form-item>
                    <el-form-item label="区域类型" prop="areaType">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                            v-model="formData.areaType" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.areaType" :key="item.dictKey" :label="item.dictValue"
                                :value="item.dictKey" />
                        </el-select>
                    </el-form-item>
                    <el-form-item label="响应机制" prop="responseMechanism">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.responseMechanism"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.responseMechanism"
                            maxlength="200" placeholder="请输入" clearable />
                    </el-form-item>
                    <el-form-item label="触发条件" prop="triggerCondition">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.triggerCondition"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.triggerCondition"
                            maxlength="200" placeholder="请输入" clearable />
                    </el-form-item>
                    <el-form-item label="管控级别" prop="controlLevel">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                            v-model="formData.controlLevel" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.controlLevel" :key="item.dictKey" :label="item.dictValue"
                                :value="item.dictKey" />
                        </el-select>
                    </el-form-item>
                    <el-form-item label="可飞行时段" prop="flyDateStart">
                        <el-date-picker class="ztzf-data-cockpit-date-picker"
                            popper-class="ztzf-data-cockpit-date-picker-popper" v-model="flyDateRange"
                        <el-date-picker class="command-data-cockpit-date-picker"
                            popper-class="command-data-cockpit-date-picker-popper" v-model="flyDateRange"
                            type="datetimerange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间"
                            value-format="YYYY-MM-DD HH:mm:ss" clearable />
                    </el-form-item>
                    <el-form-item label="关联派出所" prop="policeStationId">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                            v-model="formData.policeStationId" placeholder="请选择" clearable>
                            <el-option v-for="item in policeStationOptions" :key="item.id" :label="item.stationName"
                                :value="item.id" />
@@ -122,12 +130,12 @@
                                关联设备
                            </div>
                            <el-input class="ztzf-data-cockpit-search-input" v-model="searchName" placeholder="请输入"
                            <el-input class="command-data-cockpit-search-input" v-model="searchName" placeholder="请输入"
                                clearable></el-input>
                        </div>
                        <el-form-item prop="deviceIds" label-width="0">
                            <el-table class="ztzf-data-cockpit-table" ref="deviceTableRef"
                            <el-table class="command-data-cockpit-table" ref="deviceTableRef"
                                :data="deviceOptions.filter(item => item.deviceName.includes(searchName))" row-key="id"
                                @selection-change="handleDeviceSelectionChange">
                                <el-table-column type="selection" width="55" :reserve-selection="true" />
@@ -159,7 +167,7 @@
import { ElMessage } from 'element-plus'
import { fwAreaDivideDetailApi, fwAreaDivideSubmitApi } from './partitionApi'
import { fieldRules, geomAnalysis, getDictLabel } from '@ztzf/utils'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
import { DrawPolygon } from '@/utils/cesium/DrawPolygon'
import { cartesian3Convert } from '@/utils/cesium/mapUtil'
import * as turf from '@turf/turf'
@@ -197,6 +205,7 @@
const policeStationOptions = ref([]) // 关联派出所
const deviceOptions = ref([]) // 关联设备
const dictObj = inject('dictObj')
const mapRef = ref(null)
let viewer
let drawPolygonExample
let pointList = []
@@ -265,14 +274,9 @@
// 初始化地图实例
function initMap () {
    const publicCesiumInstance = new PublicCesium({
        dom: 'mapContainer',
        flatMode: false,
        terrain: true,
        layerMode: 4,
        boundary: false,
    })
    viewer = publicCesiumInstance.getViewer()
    if (viewer) return
    const map = mapRef.value?.getMap()
    viewer = map?.viewer || null
}
// 绘制完成回调
@@ -311,6 +315,22 @@
        pointList.map(item => ({ lng: item.longitude, lat: item.latitude }))
    )
    drawPolygonExample.subscribe('getPolygonPositions', drawFinished)
    if (pointList1.length) {
        const result = pointList1.map(item => [item.longitude, item.latitude]).flat()
        const tempEntity = viewer.entities?.add({
            position: Cesium.Cartesian3.fromDegrees(result[0], result[1]),
            polyline: {
                positions: Cesium.Cartesian3.fromDegreesArray(result),
                clampToGround: true,
                width: 1,
                material: Cesium.Color.TRANSPARENT,
            },
        })
        if (tempEntity) {
            viewer.flyTo(tempEntity, { duration: 0 })
            viewer.entities.remove(tempEntity)
        }
    }
}
// 查看面
@@ -328,7 +348,7 @@
            material: Cesium.Color.RED,
        },
    })
    viewer.flyTo(mian)
    viewer.flyTo(mian, { duration: 0 })
}
// 获取派出所列表
applications/drone-command/src/views/areaManage/partition/index.vue
@@ -1,13 +1,13 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="区域名称" prop="areaName">
                <el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.areaName" placeholder="请输入"
                <el-input class="command-data-cockpit-search-input" v-model="searchParams.areaName" placeholder="请输入"
                    clearable @clear="handleSearch" />
            </el-form-item>
            <el-form-item label="区域类型" prop="areaType">
                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.areaType" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.areaType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
@@ -20,14 +20,14 @@
            </el-form-item>
        </el-form>
        <div class="ztzf-table-toolbar">
        <div class="command-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="openForm('add')">新增</el-button>
            <el-button :icon="Delete" color="#1A2652" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="areaName" show-overflow-tooltip width="150" label="区域名称" />
@@ -65,8 +65,8 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
                <el-pagination popper-class="ztzf-data-cockpit-select-popper"
            <div class="command-table-pagination">
                <el-pagination popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
            </div>
applications/drone-command/src/views/areaManage/partition/partitionApi.js
@@ -5,7 +5,7 @@
    return request({
        url: `/drone-fw/area/fwAreaDivide/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
// 查list
applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
@@ -1,5 +1,5 @@
<template>
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]">
    <el-dialog class="command-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]">
        <div class="detail-container" v-if="readonly">
            <div class="detail-title">派出所详情</div>
            <el-row>
@@ -26,19 +26,19 @@
            <el-row>
                <el-col :span="12">
                    <el-form-item label="派出所名称" prop="stationName">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.stationName" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.stationName" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="联系人" prop="contactPerson">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.contactPerson" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.contactPerson" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="联系方式" prop="contactPhone">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.contactPhone" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.contactPhone" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
@@ -59,11 +59,19 @@
            </el-button>
        </template>
        <el-dialog class="ztzf-page-map-view-dialog" v-model="visible1" :show-close="false"
        <el-dialog class="command-page-map-view-dialog" v-model="visible1" :show-close="false"
            :close-on-click-modal="false" destroy-on-close @closed="positionClosed">
            <div class="dialog-container">
                <div class="left-container">
                    <div class="leftMap ztzf-cesium" id="mapContainer"></div>
                    <CommonCesiumMap
                        ref="mapRef"
                        class="leftMap command-cesium"
                        :active="visible1"
                        :flat-mode="false"
                        :terrain="true"
                        :layer-mode="4"
                        :boundary="false"
                    />
                </div>
            </div>
@@ -80,7 +88,7 @@
import { ElMessage } from 'element-plus'
import { fwPoliceStationDetailApi, fwPoliceStationSubmitApi } from './precinctInfoApi'
import { fieldRules } from '@ztzf/utils'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
import * as Cesium from 'cesium'
import axios from 'axios'
@@ -103,6 +111,7 @@
const readonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
const tempLocation = ref({ longitude: null, latitude: null })
const mapRef = ref(null)
const rules = {
    address: fieldRules(true, 50),
@@ -186,6 +195,7 @@
let viewer
let redPointEntity
let leftClickBound = false
function setMapPoint (longitude, latitude) {
    if (!viewer || longitude == null || latitude == null) return
    if (!redPointEntity) {
@@ -225,15 +235,12 @@
async function selectLocation () {
    visible1.value = true
    await nextTick()
    const publicCesiumInstance = new PublicCesium({
        dom: 'mapContainer',
        flatMode: false,
        terrain: true,
        layerMode: 4,
        boundary: false,
    })
    publicCesiumInstance.addLeftClickEvent(null, LeftClickEvent)
    viewer = publicCesiumInstance.getViewer()
    const map = mapRef.value?.getMap()
    viewer = map?.viewer || null
    if (viewer && !leftClickBound) {
        map.publicCesium?.addLeftClickEvent?.(null, LeftClickEvent)
        leftClickBound = true
    }
    if (formData.value.longitude != null && formData.value.latitude != null) {
        tempLocation.value = { longitude: formData.value.longitude, latitude: formData.value.latitude }
        setMapPoint(formData.value.longitude, formData.value.latitude)
@@ -264,6 +271,7 @@
    }
    visible1.value = false
}
// 打开弹框
async function open ({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
applications/drone-command/src/views/areaManage/precinctInfo/index.vue
@@ -1,8 +1,8 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="派出所名称" prop="stationName">
                <el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.stationName" placeholder="请输入"
                <el-input class="command-data-cockpit-search-input" v-model="searchParams.stationName" placeholder="请输入"
                    clearable @clear="handleSearch" />
            </el-form-item>
@@ -12,14 +12,14 @@
            </el-form-item>
        </el-form>
        <div class="ztzf-table-toolbar">
        <div class="command-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="openForm('add')">新增</el-button>
            <el-button :icon="Delete" color="#1A2652" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="stationName" show-overflow-tooltip label="派出所名称" />
@@ -37,8 +37,8 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
                <el-pagination popper-class="ztzf-data-cockpit-select-popper"
            <div class="command-table-pagination">
                <el-pagination popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
            </div>
@@ -99,9 +99,9 @@
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'ztzf-page-view-message-box',
        confirmButtonClass: 'ztzf-message-box-confirm',
        cancelButtonClass: 'ztzf-message-box-cancel',
        customClass: 'command-page-view-message-box',
        confirmButtonClass: 'command-message-box-confirm',
        cancelButtonClass: 'command-message-box-cancel',
    })
    const ids = row ? row.id : selectedIds.value.join(',')
    await fwPoliceStationRemoveApi({ ids })
applications/drone-command/src/views/areaManage/precinctInfo/precinctInfoApi.js
@@ -5,7 +5,7 @@
    return request({
        url: `/drone-fw/area/fwPoliceStation/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue
@@ -1,8 +1,16 @@
<template>
    <el-dialog class="ztzf-page-map-view-dialog" v-model="visible" :show-close="false" :close-on-click-modal="false">
    <el-dialog class="command-page-map-view-dialog" v-model="visible" :show-close="false" :close-on-click-modal="false">
        <div class="dialog-container">
            <div class="left-container">
                <div class="leftMap ztzf-cesium" id="mapContainer"></div>
                <CommonCesiumMap
                    ref="mapRef"
                    class="leftMap command-cesium"
                    :active="visible"
                    :flat-mode="false"
                    :terrain="true"
                    :layer-mode="4"
                    :boundary="false"
                />
            </div>
            <div class="right-container">
                <div class="header">
@@ -43,9 +51,9 @@
                    </el-row>
                    <div class="detail-title">关联区域</div>
                    <div class="ztzf-table-container">
                        <div class="ztzf-table-content">
                            <el-table class="ztzf-data-cockpit-table" ref="areaTableRef" :data="areaList" row-key="id">
                    <div class="command-table-container">
                        <div class="command-table-content">
                            <el-table class="command-data-cockpit-table" ref="areaTableRef" :data="areaList" row-key="id">
                                <el-table-column prop="areaName" label="区域名称" />
                                <el-table-column prop="areaType" label="区域类型">
                                    <template v-slot="{ row }">
@@ -60,29 +68,29 @@
                <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly"
                    label-width="96px">
                    <el-form-item label="场景名称" prop="sceneName">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.sceneName" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.sceneName" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                    <el-form-item label="指挥点位置" prop="longitude">
                        <div class="val">{{ formatLocation(formData) }}</div>
                    </el-form-item>
                    <el-form-item label="场景类型" prop="sceneType">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                            v-model="formData.sceneType" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.sceneType" :key="item.dictKey" :label="item.dictValue"
                                :value="item.dictKey" />
                        </el-select>
                    </el-form-item>
                    <el-form-item label="负责人" prop="defenseLeader">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.defenseLeader" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.defenseLeader" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                    <el-form-item label="负责人电话" prop="leaderPhone">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.leaderPhone" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.leaderPhone" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                    <el-form-item label="设备模式" prop="deviceMode">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                            v-model="formData.deviceMode" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.deviceMode" :key="item.dictKey" :label="item.dictValue"
                                :value="item.dictKey" />
@@ -98,12 +106,12 @@
                                关联区域
                            </div>
                            <el-input class="ztzf-data-cockpit-search-input" v-model="searchName" placeholder="请输入"
                            <el-input class="command-data-cockpit-search-input" v-model="searchName" placeholder="请输入"
                                clearable></el-input>
                        </div>
                        <el-form-item prop="areaDivideIds" label-width="0">
                            <el-table class="ztzf-data-cockpit-table" ref="areaTableRef"
                            <el-table class="command-data-cockpit-table" ref="areaTableRef"
                                :data="areaList.filter(item => item.areaName.includes(searchName))" row-key="id"
                                @selection-change="handleAreaSelectionChange">
                                <el-table-column type="selection" width="55" :reserve-selection="true" />
@@ -135,7 +143,7 @@
import { ElMessage } from 'element-plus'
import { fwDefenseSceneDetailApi, fwDefenseSceneSubmitApi } from './sceneConfigApi'
import { fieldRules, geomAnalysis, getDictLabel } from '@ztzf/utils'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
import * as Cesium from 'cesium'
import { fwAreaDivideListApi } from '../partition/partitionApi'
import { DrawPolygon } from '@/utils/cesium/DrawPolygon'
@@ -166,8 +174,10 @@
const areaList = ref([]) // 关联区域列表
const selectedAreaRows = ref([]) //选中列表
const dictObj = inject('dictObj')
const mapRef = ref(null)
let viewer
let redPointEntity
let leftClickBound = false
const rules = {
    sceneName: fieldRules(true, 50),
@@ -285,19 +295,18 @@
let geometricSource
// 初始化地图实例
function initMap () {
    const publicCesiumInstance = new PublicCesium({
        dom: 'mapContainer',
        flatMode: false,
        terrain: true,
        layerMode: 4,
        boundary: false,
    })
    if (!readonly.value) {
        publicCesiumInstance.addLeftClickEvent(null, LeftClickEvent)
    if (viewer) return
    const map = mapRef.value?.getMap()
    if (!map?.viewer) return
    viewer = map.viewer
    if (!readonly.value && !leftClickBound) {
        map.publicCesium?.addLeftClickEvent?.(null, LeftClickEvent)
        leftClickBound = true
    }
    viewer = publicCesiumInstance.getViewer()
    geometricSource = new Cesium.CustomDataSource('geometricSource')
    viewer.dataSources.add(geometricSource)
    if (!geometricSource) {
        geometricSource = new Cesium.CustomDataSource('geometricSource')
        viewer.dataSources.add(geometricSource)
    }
}
function LeftClickEvent (click) {
applications/drone-command/src/views/areaManage/sceneConfig/index.vue
@@ -1,9 +1,9 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="场景名称" prop="sceneName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.sceneName"
                    placeholder="请输入"
                    clearable
@@ -13,8 +13,8 @@
            <el-form-item label="场景类型" prop="sceneType">
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.sceneType"
                    placeholder="请选择"
                    clearable
@@ -31,8 +31,8 @@
            <el-form-item label="场景状态" prop="status">
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.status"
                    placeholder="请选择"
                    clearable
@@ -53,14 +53,14 @@
            </el-form-item>
        </el-form>
        <div class="ztzf-table-toolbar">
        <div class="command-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="openForm('add')">新增</el-button>
            <el-button :icon="Delete" color="#1A2652" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
@@ -90,9 +90,9 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
            <div class="command-table-pagination">
                <el-pagination
                    popper-class="ztzf-data-cockpit-select-popper"
                    popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
@@ -162,9 +162,9 @@
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'ztzf-page-view-message-box',
        confirmButtonClass: 'ztzf-message-box-confirm',
        cancelButtonClass: 'ztzf-message-box-cancel',
        customClass: 'command-page-view-message-box',
        confirmButtonClass: 'command-message-box-confirm',
        cancelButtonClass: 'command-message-box-cancel',
    })
    const ids = row ? row.id : selectedIds.value.join(',')
    await fwDefenseSceneRemoveApi({ ids })
applications/drone-command/src/views/areaManage/sceneConfig/sceneConfigApi.js
@@ -5,7 +5,7 @@
    return request({
        url: `/drone-fw/area/fwDefenseScene/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
applications/drone-command/src/views/authority/role.vue
@@ -1,48 +1,101 @@
<template>
  <basic-container>
    <avue-crud :option="option" :table-loading="loading" :data="data" ref="crud" v-model="form"
      :permission="permissionList" :before-open="beforeOpen" @row-del="rowDel" @row-update="rowUpdate"
      @row-save="rowSave" @search-change="searchChange" @search-reset="searchReset" @selection-change="selectionChange"
      @current-change="currentChange" @size-change="sizeChange" @refresh-change="refreshChange" @on-load="onLoad">
      <template #menu-left>
        <el-button type="danger" icon="el-icon-delete" v-if="permission.role_delete" plain @click="handleDelete">删 除
        </el-button>
        <el-button icon="el-icon-setting" v-if="userInfo.role_name.includes('admin')" @click="handleRole" plain>权限设置
        </el-button>
        <!--  -->
      </template>
    </avue-crud>
    <el-dialog title="角色权限配置" append-to-body v-model="box" width="620px">
      <el-tabs type="border-card">
        <el-tab-pane label="后台管理">
          <el-tree :data="menuGrantList" show-checkbox node-key="id" ref="treeMenu" :default-checked-keys="menuTreeObj"
            :props="props">
          </el-tree>
        </el-tab-pane>
        <el-tab-pane label="移动app">
          <el-tree :data="menuGrantListApp" show-checkbox node-key="id" ref="treeMenuApp" :default-checked-keys="menuTreeObjApp"
                   :props="props">
          </el-tree>
        </el-tab-pane>
    <basic-container>
        <avue-crud
            :option="option"
            :table-loading="loading"
            :data="data"
            ref="crud"
            v-model="form"
            :permission="permissionList"
            :before-open="beforeOpen"
            @row-del="rowDel"
            @row-update="rowUpdate"
            @row-save="rowSave"
            @search-change="searchChange"
            @search-reset="searchReset"
            @selection-change="selectionChange"
            @current-change="currentChange"
            @size-change="sizeChange"
            @refresh-change="refreshChange"
            @on-load="onLoad"
        >
            <template #menu-left>
                <el-button type="danger" icon="el-icon-delete" v-if="permission.role_delete" plain @click="handleDelete">
                    删 除
                </el-button>
                <el-button icon="el-icon-setting" v-if="userInfo.role_name.includes('admin')" @click="handleRole" plain>
                    权限设置
                </el-button>
                <!--  -->
            </template>
        </avue-crud>
        <el-dialog title="角色权限配置" append-to-body v-model="box" width="620px">
            <el-tabs type="border-card">
                <el-tab-pane label="后台管理">
                    <el-tree
                        :data="menuGrantList"
                        show-checkbox
                        node-key="id"
                        ref="treeMenu"
                        :default-checked-keys="menuTreeObj"
                        :props="props"
                    ></el-tree>
                </el-tab-pane>
                <el-tab-pane label="移动app">
                    <el-tree
                        :data="menuGrantListApp"
                        show-checkbox
                        node-key="id"
                        ref="treeMenuApp"
                        :default-checked-keys="menuTreeObjApp"
                        :props="props"
                    ></el-tree>
                </el-tab-pane>
        <el-tab-pane label="新大屏菜单">
          <el-tree :data="menuGrantListNew" show-checkbox node-key="id" ref="treeMenuWebNew" :default-checked-keys="menuTreeObjNew"
                   :props="props">
          </el-tree>
        </el-tab-pane>
                <el-tab-pane label="新大屏菜单">
                    <el-tree
                        :data="menuGrantListNew"
                        show-checkbox
                        node-key="id"
                        ref="treeMenuWebNew"
                        :default-checked-keys="menuTreeObjNew"
                        :props="props"
                    ></el-tree>
                </el-tab-pane>
        <el-tab-pane label="无人机管控">
          <el-tree :data="menuGrantListDroneControl" show-checkbox node-key="id" ref="droneControl" :default-checked-keys="droneControlMenuTreeObj"
                   :props="props">
          </el-tree>
        </el-tab-pane>
                <el-tab-pane label="无人机管控">
                    <el-tree
                        :data="menuGrantListDroneControl"
                        show-checkbox
                        node-key="id"
                        ref="droneControl"
                        :default-checked-keys="droneControlMenuTreeObj"
                        :props="props"
                    ></el-tree>
                </el-tab-pane>
        <el-tab-pane label="大屏菜单">
          <el-tree :data="menuGrantListOld" show-checkbox node-key="id" ref="treeMenuWebOld" :default-checked-keys="menuTreeObjOld"
                   :props="props">
          </el-tree>
        </el-tab-pane>
        <!-- <el-tab-pane label="数据权限">
                <el-tab-pane label="任务工单">
                    <el-tree
                        :data="menuGrantListWork"
                        show-checkbox
                        node-key="id"
                        ref="workRef"
                        :default-checked-keys="workTreeObj"
                        :props="props"
                    ></el-tree>
                </el-tab-pane>
                <el-tab-pane label="大屏菜单">
                    <el-tree
                        :data="menuGrantListOld"
                        show-checkbox
                        node-key="id"
                        ref="treeMenuWebOld"
                        :default-checked-keys="menuTreeObjOld"
                        :props="props"
                    ></el-tree>
                </el-tab-pane>
                <!-- <el-tab-pane label="数据权限">
          <el-tree
            :data="dataScopeGrantList"
            show-checkbox
@@ -64,394 +117,392 @@
          >
          </el-tree>
        </el-tab-pane> -->
      </el-tabs>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="box = false">取 消</el-button>
          <el-button type="primary" @click="submit">确 定</el-button>
        </span>
      </template>
    </el-dialog>
  </basic-container>
            </el-tabs>
            <template #footer>
                <span class="dialog-footer">
                    <el-button @click="box = false">取 消</el-button>
                    <el-button type="primary" @click="submit">确 定</el-button>
                </span>
            </template>
        </el-dialog>
    </basic-container>
</template>
<script>
import {
  add,
  getList,
  getRole,
  getRoleTreeById,
  grant,
  grantTree,
  remove,
  update,
} from '@/api/system/role'
import { add, getList, getRole, getRoleTreeById, grant, grantTree, remove, update } from '@/api/system/role'
import { mapGetters } from 'vuex'
import website from '@/config/website'
export default {
  data () {
    return {
      form: {},
      box: false,
      props: {
        label: 'title',
        value: 'key',
      },
      menuGrantList: [],
      menuGrantListApp: [],
      menuGrantListOld: [],
      menuGrantListNew: [],
      menuGrantListDroneControl: [],
      dataScopeGrantList: [],
      apiScopeGrantList: [],
      apiGrantList: [],
      menuTreeObj: [],
      menuTreeObjApp: [],
      menuTreeObjOld: [],
      menuTreeObjNew: [],
      droneControlMenuTreeObj: [],
      dataScopeTreeObj: [],
      apiScopeTreeObj: [],
      selectionList: [],
      query: {},
      loading: true,
      page: {
        pageSize: 10,
        currentPage: 1,
        total: 0,
      },
      option: {
        emptyBtnText: '重置',
        emptyBtnIcon: 'el-icon-refresh',
        tip: false,
        simplePage: true,
        searchShow: true,
        searchGutter: 30,
        searchMenuPosition: 'left',
        searchMenuSpan: 4,
        tree: true,
        border: true,
        index: true,
        selection: true,
        viewBtn: true,
        dialogWidth: 900,
        dialogClickModal: false,
    data() {
        return {
            form: {},
            box: false,
            props: {
                label: 'title',
                value: 'key',
            },
            menuGrantList: [],
            menuGrantListApp: [],
            menuGrantListOld: [],
            menuGrantListNew: [],
            menuGrantListDroneControl: [],
            menuGrantListWork: [],
            dataScopeGrantList: [],
            apiScopeGrantList: [],
            apiGrantList: [],
            menuTreeObj: [],
            menuTreeObjApp: [],
            menuTreeObjOld: [],
            menuTreeObjNew: [],
            droneControlMenuTreeObj: [],
            workTreeObj: [],
            dataScopeTreeObj: [],
            apiScopeTreeObj: [],
            selectionList: [],
            query: {},
            loading: true,
            page: {
                pageSize: 10,
                currentPage: 1,
                total: 0,
            },
            option: {
                emptyBtnText: '重置',
                emptyBtnIcon: 'el-icon-refresh',
                tip: false,
                simplePage: true,
                searchShow: true,
                searchGutter: 30,
                searchMenuPosition: 'left',
                searchMenuSpan: 4,
                tree: true,
                border: true,
                index: true,
                selection: true,
                viewBtn: true,
                dialogWidth: 900,
                dialogClickModal: false,
        height: 'auto',
        calcHeight: 20,
                height: 'auto',
                calcHeight: 20,
        column: [
          {
            label: '角色名称',
            prop: 'roleName',
            search: true,
            searchSpan: 4,
            span: 24,
            rules: [
              {
                required: true,
                message: '请输入角色名称',
                trigger: 'blur',
              },
            ],
          },
          // {
          //   label: '所属租户',
          //   prop: 'tenantId',
          //   type: 'tree',
          //   dicUrl: '/blade-system/tenant/select',
          //   addDisplay: false,
          //   editDisplay: false,
          //   viewDisplay: website.tenantMode,
          //   span: 24,
          //   props: {
          //     label: 'tenantName',
          //     value: 'tenantId',
          //   },
          //   hide: !website.tenantMode,
          //   search: website.tenantMode,
          //   rules: [
          //     {
          //       required: true,
          //       message: '请输入所属租户',
          //       trigger: 'click',
          //     },
          //   ],
          // },
          {
            label: '角色别名',
            prop: 'roleAlias',
            search: true,
            searchSpan: 4,
            span: 24,
            rules: [
              {
                required: true,
                message: '请输入角色别名',
                trigger: 'blur',
              },
            ],
          },
          {
            label: '上级角色',
            prop: 'parentId',
            dicData: [],
            type: 'tree',
            hide: true,
            span: 24,
            props: {
              label: 'title',
            },
            rules: [
              {
                required: false,
                message: '请选择上级角色',
                trigger: 'click',
              },
            ],
          },
          {
            label: '角色排序',
            prop: 'sort',
            type: 'number',
            span: 24,
            rules: [
              {
                required: true,
                message: '请输入角色排序',
                trigger: 'blur',
              },
            ],
          },
        ],
      },
      data: [],
    }
  },
  computed: {
    ...mapGetters(['userInfo', 'permission']),
    permissionList () {
      return {
        addBtn: this.validData(this.permission.role_add, false),
        viewBtn: this.validData(this.permission.role_view, false),
        delBtn: this.validData(this.permission.role_delete, false),
        editBtn: this.validData(this.permission.role_edit, false),
      }
    },
    ids () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id)
      })
      return ids.join(',')
    },
    idsArray () {
      let ids = []
      this.selectionList.forEach(ele => {
        ids.push(ele.id)
      })
      return ids
    },
  },
  methods: {
    initData (roleId) {
      getRoleTreeById(roleId).then(res => {
        const column = this.findObject(this.option.column, 'parentId')
        column.dicData = res.data.data
      })
    },
    submit () {
      const menuList = this.$refs.treeMenu.getCheckedKeys()
      const menuListApp = this.$refs.treeMenuApp.getCheckedKeys()
      const menuListWebNew = this.$refs.treeMenuWebNew.getCheckedKeys()
      const menuListDroneControl = this.$refs.droneControl.getCheckedKeys()
      const menuListWebOld = this.$refs.treeMenuWebOld.getCheckedKeys()
                column: [
                    {
                        label: '角色名称',
                        prop: 'roleName',
                        search: true,
                        searchSpan: 4,
                        span: 24,
                        rules: [
                            {
                                required: true,
                                message: '请输入角色名称',
                                trigger: 'blur',
                            },
                        ],
                    },
                    // {
                    //   label: '所属租户',
                    //   prop: 'tenantId',
                    //   type: 'tree',
                    //   dicUrl: '/blade-system/tenant/select',
                    //   addDisplay: false,
                    //   editDisplay: false,
                    //   viewDisplay: website.tenantMode,
                    //   span: 24,
                    //   props: {
                    //     label: 'tenantName',
                    //     value: 'tenantId',
                    //   },
                    //   hide: !website.tenantMode,
                    //   search: website.tenantMode,
                    //   rules: [
                    //     {
                    //       required: true,
                    //       message: '请输入所属租户',
                    //       trigger: 'click',
                    //     },
                    //   ],
                    // },
                    {
                        label: '角色别名',
                        prop: 'roleAlias',
                        search: true,
                        searchSpan: 4,
                        span: 24,
                        rules: [
                            {
                                required: true,
                                message: '请输入角色别名',
                                trigger: 'blur',
                            },
                        ],
                    },
                    {
                        label: '上级角色',
                        prop: 'parentId',
                        dicData: [],
                        type: 'tree',
                        hide: true,
                        span: 24,
                        props: {
                            label: 'title',
                        },
                        rules: [
                            {
                                required: false,
                                message: '请选择上级角色',
                                trigger: 'click',
                            },
                        ],
                    },
                    {
                        label: '角色排序',
                        prop: 'sort',
                        type: 'number',
                        span: 24,
                        rules: [
                            {
                                required: true,
                                message: '请输入角色排序',
                                trigger: 'blur',
                            },
                        ],
                    },
                ],
            },
            data: [],
        }
    },
    computed: {
        ...mapGetters(['userInfo', 'permission']),
        permissionList() {
            return {
                addBtn: this.validData(this.permission.role_add, false),
                viewBtn: this.validData(this.permission.role_view, false),
                delBtn: this.validData(this.permission.role_delete, false),
                editBtn: this.validData(this.permission.role_edit, false),
            }
        },
        ids() {
            let ids = []
            this.selectionList.forEach(ele => {
                ids.push(ele.id)
            })
            return ids.join(',')
        },
        idsArray() {
            let ids = []
            this.selectionList.forEach(ele => {
                ids.push(ele.id)
            })
            return ids
        },
    },
    methods: {
        initData(roleId) {
            getRoleTreeById(roleId).then(res => {
                const column = this.findObject(this.option.column, 'parentId')
                column.dicData = res.data.data
            })
        },
        submit() {
            const menuList = this.$refs.treeMenu.getCheckedKeys()
            const menuListApp = this.$refs.treeMenuApp.getCheckedKeys()
            const menuListWebNew = this.$refs.treeMenuWebNew.getCheckedKeys()
            const menuListWork = this.$refs.workRef.getCheckedKeys()
            const menuListDroneControl = this.$refs.droneControl.getCheckedKeys()
            const menuListWebOld = this.$refs.treeMenuWebOld.getCheckedKeys()
            // const dataScopeList = this.$refs.treeDataScope.getCheckedKeys();
            const dataScopeList = []
            // const apiScopeList = this.$refs.treeApiScope.getCheckedKeys();
            const apiScopeList = []
            grant(
                this.idsArray,
                [...menuList, ...menuListApp, ...menuListWebNew, ...menuListDroneControl, ...menuListWebOld, ...menuListWork],
                dataScopeList,
                apiScopeList
            ).then(() => {
                this.box = false
                this.$message({
                    type: 'success',
                    message: '操作成功!',
                })
                this.onLoad(this.page)
            })
        },
        rowSave(row, done, loading) {
            add(row).then(
                () => {
                    this.onLoad(this.page)
                    this.$message({
                        type: 'success',
                        message: '操作成功!',
                    })
                    done()
                },
                error => {
                    window.console.log(error)
                    loading()
                }
            )
        },
        rowUpdate(row, index, done, loading) {
            update(row).then(
                () => {
                    this.onLoad(this.page)
                    this.$message({
                        type: 'success',
                        message: '操作成功!',
                    })
                    done()
                },
                error => {
                    window.console.log(error)
                    loading()
                }
            )
        },
        rowDel(row) {
            this.$confirm('确定将选择数据删除?', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning',
            })
                .then(() => {
                    return remove(row.id)
                })
                .then(() => {
                    this.onLoad(this.page)
                    this.$message({
                        type: 'success',
                        message: '操作成功!',
                    })
                })
        },
      // const dataScopeList = this.$refs.treeDataScope.getCheckedKeys();
      const dataScopeList = []
      // const apiScopeList = this.$refs.treeApiScope.getCheckedKeys();
      const apiScopeList = []
      grant(this.idsArray, [
        ...menuList,
        ...menuListApp,
        ...menuListWebNew,
        ...menuListDroneControl,
        ...menuListWebOld,
      ], dataScopeList, apiScopeList).then(() => {
        this.box = false
        this.$message({
          type: 'success',
          message: '操作成功!',
        })
        this.onLoad(this.page)
      })
    },
    rowSave (row, done, loading) {
      add(row).then(
        () => {
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          })
          done()
        },
        error => {
          window.console.log(error)
          loading()
        }
      )
    },
    rowUpdate (row, index, done, loading) {
      update(row).then(
        () => {
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          })
          done()
        },
        error => {
          window.console.log(error)
          loading()
        }
      )
    },
    rowDel (row) {
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(row.id)
        })
        .then(() => {
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          })
        })
    },
        searchReset() {
            this.query = {}
            this.onLoad(this.page)
        },
        searchChange(params, done) {
            this.query = params
            this.page.currentPage = 1
            this.onLoad(this.page, params)
            done()
        },
        selectionChange(list) {
            this.selectionList = list
        },
        selectionClear() {
            this.selectionList = []
            this.$refs.crud.toggleSelection()
        },
        beforeOpen(done, type) {
            if (['add', 'edit'].includes(type)) {
                this.initData(this.form.id)
            }
            done()
        },
        handleRole() {
            if (this.selectionList.length !== 1) {
                this.$message.warning('只能选择一条数据')
                return
            }
            this.menuTreeObj = []
            this.dataScopeTreeObj = []
            this.apiScopeTreeObj = []
            grantTree({ sysType: 1 }).then(res => {
                this.menuGrantList = res.data.data.menu
                this.dataScopeGrantList = res.data.data.dataScope
                this.apiScopeGrantList = res.data.data.apiScope
                getRole(this.ids).then(res => {
                    this.menuTreeObj = res.data.data.menu
                    this.dataScopeTreeObj = res.data.data.dataScope
                    this.apiScopeTreeObj = res.data.data.apiScope
                    this.box = true
                })
            })
            grantTree({ sysType: 3 }).then(res => {
                this.menuGrantListApp = res.data.data.menu
                getRole(this.ids).then(res => {
                    this.menuTreeObjApp = res.data.data.menu
                    this.box = true
                })
            })
            grantTree({ sysType: 2 }).then(res => {
                this.menuGrantListOld = res.data.data.menu
                getRole(this.ids).then(res => {
                    this.menuTreeObjOld = res.data.data.menu
                    this.box = true
                })
            })
    searchReset () {
      this.query = {}
      this.onLoad(this.page)
    },
    searchChange (params, done) {
      this.query = params
      this.page.currentPage = 1
      this.onLoad(this.page, params)
      done()
    },
    selectionChange (list) {
      this.selectionList = list
    },
    selectionClear () {
      this.selectionList = []
      this.$refs.crud.toggleSelection()
    },
    beforeOpen (done, type) {
      if (['add', 'edit'].includes(type)) {
        this.initData(this.form.id)
      }
      done()
    },
    handleRole () {
      if (this.selectionList.length !== 1) {
        this.$message.warning('只能选择一条数据')
        return
      }
      this.menuTreeObj = []
      this.dataScopeTreeObj = []
      this.apiScopeTreeObj = []
      grantTree({sysType: 1}).then(res => {
        this.menuGrantList = res.data.data.menu
        this.dataScopeGrantList = res.data.data.dataScope
        this.apiScopeGrantList = res.data.data.apiScope
        getRole(this.ids).then(res => {
          this.menuTreeObj = res.data.data.menu
          this.dataScopeTreeObj = res.data.data.dataScope
          this.apiScopeTreeObj = res.data.data.apiScope
          this.box = true
        })
      })
      grantTree({sysType: 3}).then(res => {
        this.menuGrantListApp = res.data.data.menu
        getRole(this.ids).then(res => {
          this.menuTreeObjApp = res.data.data.menu
          this.box = true
        })
      })
      grantTree({sysType: 2}).then(res => {
        this.menuGrantListOld = res.data.data.menu
        getRole(this.ids).then(res => {
          this.menuTreeObjOld = res.data.data.menu
          this.box = true
        })
      })
      grantTree({sysType: 4}).then(res => {
        this.menuGrantListNew = res.data.data.menu
        getRole(this.ids).then(res => {
          this.menuTreeObjNew = res.data.data.menu
          this.box = true
        })
      })
            grantTree({ sysType: 4 }).then(res => {
                this.menuGrantListNew = res.data.data.menu
                getRole(this.ids).then(res => {
                    this.menuTreeObjNew = res.data.data.menu
                    this.box = true
                })
            })
      grantTree({sysType: 5}).then(res => {
        this.menuGrantListDroneControl = res.data.data.menu
        getRole(this.ids).then(res => {
          this.droneControlMenuTreeObj = res.data.data.menu
          this.box = true
        })
      })
    },
    handleDelete () {
      if (this.selectionList.length === 0) {
        this.$message.warning('请选择至少一条数据')
        return
      }
      this.$confirm('确定将选择数据删除?', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          return remove(this.ids)
        })
        .then(() => {
          this.onLoad(this.page)
          this.$message({
            type: 'success',
            message: '操作成功!',
          })
          this.$refs.crud.toggleSelection()
        })
    },
    currentChange (currentPage) {
      this.page.currentPage = currentPage
    },
    sizeChange (pageSize) {
      this.page.pageSize = pageSize
    },
    refreshChange () {
      this.onLoad(this.page, this.query)
    },
    onLoad (page, params = {}) {
      this.loading = true
      getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
        this.data = res.data.data
        this.loading = false
        this.selectionClear()
      })
    },
  },
            grantTree({ sysType: 5 }).then(res => {
                this.menuGrantListDroneControl = res.data.data.menu
                getRole(this.ids).then(res => {
                    this.droneControlMenuTreeObj = res.data.data.menu
                    this.box = true
                })
            })
            grantTree({ sysType: 6 }).then(res => {
                this.menuGrantListWork = res.data.data.menu
                getRole(this.ids).then(res => {
                    this.workTreeObj = res.data.data.menu
                    this.box = true
                })
            })
        },
        handleDelete() {
            if (this.selectionList.length === 0) {
                this.$message.warning('请选择至少一条数据')
                return
            }
            this.$confirm('确定将选择数据删除?', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning',
            })
                .then(() => {
                    return remove(this.ids)
                })
                .then(() => {
                    this.onLoad(this.page)
                    this.$message({
                        type: 'success',
                        message: '操作成功!',
                    })
                    this.$refs.crud.toggleSelection()
                })
        },
        currentChange(currentPage) {
            this.page.currentPage = currentPage
        },
        sizeChange(pageSize) {
            this.page.pageSize = pageSize
        },
        refreshChange() {
            this.onLoad(this.page, this.query)
        },
        onLoad(page, params = {}) {
            this.loading = true
            getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
                this.data = res.data.data
                this.loading = false
                this.selectionClear()
            })
        },
    },
}
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/basicManage/deviceScrap/FormDiaLog.vue
@@ -1,5 +1,5 @@
<template>
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed"
    <el-dialog class="command-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed"
        destroy-on-close>
        <div class="detail-container" v-if="dialogReadonly">
            <el-row>
applications/drone-command/src/views/basicManage/deviceScrap/index.vue
@@ -1,23 +1,46 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
            <el-form-item label="名称" prop="deviceName">
                <el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.deviceName" placeholder="请输入"
                    clearable @clear="handleSearch" />
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="设备名称" prop="deviceName">
                <el-input
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.deviceName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item label="类型" prop="deviceType">
                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                    v-model="searchParams.deviceType" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.deviceType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
            <el-form-item label="设备类型" prop="deviceType">
                <el-select
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.deviceType"
                    placeholder="请选择"
                    clearable
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.deviceType"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
                    />
                </el-select>
            </el-form-item>
            <el-form-item label="部门" prop="belongDept">
                <el-tree-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-tree-select-popper"
                    v-model="searchParams.belongDept" :data="deptTree" :props="treeProps" node-key="id" check-strictly
                    clearable @change="handleSearch" />
            <el-form-item label="所属部门" prop="belongDept">
                <el-tree-select
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-tree-select-popper"
                    v-model="searchParams.belongDept"
                    :data="deptTree"
                    :props="treeProps"
                    node-key="id"
                    check-strictly
                    clearable
                    @change="handleSearch"
                />
            </el-form-item>
            <el-form-item class="history-search-actions">
@@ -26,10 +49,9 @@
            </el-form-item>
        </el-form>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list">
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="deviceName" show-overflow-tooltip label="设备名称" />
                    <el-table-column prop="deviceType" show-overflow-tooltip label="设备类型">
@@ -41,11 +63,15 @@
                    <el-table-column prop="manufacturer" show-overflow-tooltip label="生产厂商" />
                    <el-table-column prop="createTime" show-overflow-tooltip label="入库时间" />
                    <el-table-column prop="source" show-overflow-tooltip label="来源" />
                    <el-table-column prop="belongDeptName" show-overflow-tooltip label="所属部门" />
                    <el-table-column prop="charger" show-overflow-tooltip label="负责人" />
                    <el-table-column prop="scrapTime" show-overflow-tooltip label="报废时间" />
                    <el-table-column prop="scrapTime" show-overflow-tooltip label="报废时间">
                        <template v-slot="{ row }">
                            {{dayjs(row.scrapTime).format('YYYY-MM-DD')}}
                        </template>
                    </el-table-column>
                    <el-table-column prop="scrapReason" show-overflow-tooltip label="报废原因" />
                    <el-table-column prop="disposeWay" show-overflow-tooltip label="处置方式" />
                    <el-table-column prop="maintainReminder" show-overflow-tooltip label="维护提醒" />
                    <el-table-column label="操作" class-name="operation-btns">
                        <template v-slot="{ row }">
                            <el-link @click="handleView(row)">查看</el-link>
@@ -54,10 +80,15 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
                <el-pagination popper-class="ztzf-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
            <div class="command-table-pagination">
                <el-pagination
                    popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
                    :total="total"
                    @change="getList"
                />
            </div>
        </div>
@@ -74,6 +105,7 @@
import FormDiaLog from './FormDiaLog.vue'
import MaintenanceDiaLog from '@/views/basicManage/maintainRecord/MaintenanceDiaLog.vue'
import { fwDeviceScrapPageApi } from '@/views/basicManage/deviceStock/fwDeviceScrapApi'
import dayjs from 'dayjs'
const initSearchParams = () => ({
    deviceName: '', // 设备名称
@@ -86,7 +118,6 @@
const total = ref(0) // 总条数
const loading = ref(true) // 列表加载中
const list = ref([]) // 列表数据
const selectedIds = ref([]) // 勾选的ID列表
const queryParamsRef = ref(null) // 查询表单实例
const dialogRef = ref(null) // 弹框实例
const maintenanceDialogRef = ref(null) // 弹框实例
@@ -108,7 +139,7 @@
provide('planCycleOptions', planCycleOptions)
// 获取列表
async function getList () {
async function getList() {
    loading.value = true
    try {
        const res = await fwDeviceScrapPageApi(searchParams.value)
@@ -120,48 +151,43 @@
}
// 查询
function handleSearch () {
function handleSearch() {
    searchParams.value.current = 1
    getList()
}
// 重置查询
function resetForm () {
function resetForm() {
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    getList()
}
// 查看
function handleView (row) {
function handleView(row) {
    dialogRef.value?.open({ mode: 'view', row: { ...row } })
}
// 勾选值设置
function handleSelectionChange (rows) {
    selectedIds.value = rows.map(item => item.id)
}
// 获取字典
function getDictList () {
function getDictList() {
    getDictionaryByCode('deviceType,deviceAtt').then(res => {
        dictObj.value = res.data.data
    })
}
// 获取部门树
function getDeptTreeFun () {
function getDeptTreeFun() {
    getDeptTree().then(res => {
        deptTree.value = res.data.data
    })
}
// 新增
function handleAdd () {
function handleAdd() {
    dialogRef.value?.open({ mode: 'add' })
}
function getPlanCycleLabel (row) {
function getPlanCycleLabel(row) {
    const item = planCycleOptions.find(option => option.value === row.planCycleType)
    return item?.label + '-' + row.planCycleValue.join(',')
}
applications/drone-command/src/views/basicManage/deviceStock/DeviceChart1.vue
New file
@@ -0,0 +1,263 @@
<template>
    <div class="deviceChart">
        <div class="titleBg">
            <span>设备类型分析</span>
        </div>
        <div class="chartBox" ref="chartRef"></div>
    </div>
</template>
<script setup>
import 'echarts-gl'
import { statisticalDeviceTypeApi } from '@/views/basicManage/deviceStock/fwDevice'
import useEchartsResize from '@/hooks/useEchartsResize'
const chartRef = ref(null)
let { chart } = useEchartsResize(chartRef)
const list = ref([])
const COLORS = ['#2D74EA', '#1BBDCA', '#E3A023', '#FFD900', '#AA4CD3', '#00ADEF']
const getParametricEquation = (startRatio, endRatio, isSelected, isHovered, k, h) => {
    const midRatio = (startRatio + endRatio) / 2
    const startRadian = startRatio * Math.PI * 2
    const endRadian = endRatio * Math.PI * 2
    const midRadian = midRatio * Math.PI * 2
    const kValue = typeof k !== 'undefined' ? k : 1 / 3
    const offsetX = isSelected ? Math.sin(midRadian) * 0.1 : 0
    const offsetY = isSelected ? Math.cos(midRadian) * 0.1 : 0
    const hoverRate = isHovered ? 1.05 : 1
    return {
        u: {
            min: -Math.PI,
            max: Math.PI * 3,
            step: Math.PI / 32,
        },
        v: {
            min: 0,
            max: Math.PI * 2,
            step: Math.PI / 20,
        },
        x: (u, v) => {
            if (u < startRadian) return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * kValue) * hoverRate
            if (u > endRadian) return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * kValue) * hoverRate
            return offsetX + Math.cos(u) * (1 + Math.cos(v) * kValue) * hoverRate
        },
        y: (u, v) => {
            if (u < startRadian) return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * kValue) * hoverRate
            if (u > endRadian) return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * kValue) * hoverRate
            return offsetY + Math.sin(u) * (1 + Math.cos(v) * kValue) * hoverRate
        },
        z: (u, v) => {
            if (u < -Math.PI * 0.5) return Math.sin(u)
            if (u > Math.PI * 2.5) return Math.sin(u) * h * 0.1
            return Math.sin(v) > 0 ? 1 * h * 0.1 : -1
        },
    }
}
const getPie3D = (pieData, internalDiameterRatio) => {
    const series = []
    let sumValue = 0
    let startValue = 0
    let endValue = 0
    const k =
        typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3
    for (let i = 0; i < pieData.length; i++) {
        sumValue += pieData[i].value
        const seriesItem = {
            name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
            type: 'surface',
            parametric: true,
            wireframe: {
                show: false,
            },
            pieData: pieData[i],
            pieStatus: {
                selected: false,
                hovered: false,
                k: 1 / 10,
            },
        }
        if (typeof pieData[i].itemStyle !== 'undefined') {
            const itemStyle = {}
            if (typeof pieData[i].itemStyle.color !== 'undefined') itemStyle.color = pieData[i].itemStyle.color
            if (typeof pieData[i].itemStyle.opacity !== 'undefined') itemStyle.opacity = pieData[i].itemStyle.opacity
            seriesItem.itemStyle = itemStyle
        }
        series.push(seriesItem)
    }
    for (let i = 0; i < series.length; i++) {
        endValue = startValue + series[i].pieData.value
        series[i].pieData.startRatio = startValue / sumValue
        series[i].pieData.endRatio = endValue / sumValue
        series[i].parametricEquation = getParametricEquation(
            series[i].pieData.startRatio,
            series[i].pieData.endRatio,
            false,
            false,
            k,
            series[i].pieData.value
        )
        startValue = endValue
    }
    return series
}
const buildOption = () => {
    const legendRich = {
        percent: {
            fontWeight: 700,
        },
    }
    const optionsData = list.value.map((item, index) => ({
        name: item.deviceTypeValue,
        value: item.deviceCount,
        itemStyle: {
            color: COLORS[index % COLORS.length],
        },
    }))
    list.value.forEach((item, index) => {
        legendRich[`count${index}`] = {
            color: COLORS[index % COLORS.length],
        }
    })
    const series = getPie3D(optionsData, 0.6)
    series.push({
        name: 'pie2d',
        type: 'pie',
        label: {
            show: false,
        },
        labelLine: {
            show: false,
        },
        startAngle: 300,
        clockwise: false,
        radius: ['42%', '54%'],
        center: ['30%', '50%'],
        data: optionsData,
        itemStyle: {
            opacity: 0,
        },
    })
    return {
        graphic: {
            type: 'text',
            left: '22%',
            top: '35%',
            style: {
                text: '设备类型',
                fill: '#FFFFFF',
                font: '600 14px Arial',
                textAlign: 'center',
                textVerticalAlign: 'middle',
            },
        },
        legend: {
            right: 24,
            top: 'center',
            orient: 'vertical',
            textStyle: {
                color: '#FFFFFF',
                fontSize: 12,
                rich: legendRich,
            },
            itemWidth: 8,
            itemHeight: 8,
            icon: 'rect',
            formatter: name => {
                const itemIndex = list.value.findIndex(v => v.deviceTypeValue === name)
                const item = list.value[itemIndex]
                if (!item) return name
                const percent = item.totalDevices ? Math.round((item.deviceCount / item.totalDevices) * 100) : 0
                return `${name}   {count${itemIndex}|${item.deviceCount}}   {percent|${percent}%}`
            },
        },
        animation: true,
        tooltip: {
            show: true,
            trigger: 'item',
            confine: true,
            backgroundColor: 'rgba(0,0,0,0.7)',
            borderColor: '#0070FF',
            borderWidth: 1,
            textStyle: {
                color: '#FFFFFF',
                fontSize: 14, // 也可以顺便调整字号
            },
            formatter: params => {
                if (params.seriesName !== 'mouseoutSeries' && params.seriesName !== 'pie2d') {
                    return `${params.seriesName}:${series[params.seriesIndex].pieData.value}`
                }
            },
        },
        title: {
            x: 'center',
            top: '20',
            textStyle: {
                color: '#fff',
                fontSize: 22,
            },
        },
        backgroundColor: 'transparent',
        labelLine: {
            show: false,
        },
        label: {
            show: false,
        },
        xAxis3D: {
            min: -1,
            max: 1,
        },
        yAxis3D: {
            min: -1,
            max: 1,
        },
        zAxis3D: {
            min: -1,
            max: 1,
        },
        grid3D: {
            show: false,
            boxHeight: 26,
            left: '0%',
            top: -20,
            width: '60%',
            viewControl: {
                distance: 210,
                alpha: 55,
                beta: 70,
                autoRotate: false,
                rotateSensitivity: 1,
                zoomSensitivity: 0,
                panSensitivity: 0,
            },
        },
        series,
    }
}
onMounted(() => {
    statisticalDeviceTypeApi().then(res => {
        list.value = Array.isArray(res?.data?.data) ? res.data.data : []
        chart.value.setOption(buildOption())
    })
})
</script>
<style scoped lang="scss">
.deviceChart {
    width: 385px;
}
.titleBg {
    width: 365px;
    height: 34px;
    background: url('@/assets/images/basicManage/chartBg.svg') no-repeat center / 100% 100%;
}
</style>
applications/drone-command/src/views/basicManage/deviceStock/DeviceChart2.vue
New file
@@ -0,0 +1,156 @@
<template>
    <div class="deviceChart">
        <div class="titleBg">
            <span>设备类型分析</span>
        </div>
        <div class="chartBox" ref="chartRef"></div>
    </div>
</template>
<script setup>
import * as echarts from 'echarts'
import useEchartsResize from '@/hooks/useEchartsResize'
import { statisticalDeviceOutApi } from '@/views/basicManage/deviceStock/fwDevice'
const chartRef = ref(null)
let { chart } = useEchartsResize(chartRef)
const list = ref([])
function buildOption() {
    const names = list.value.map(item => item.type)
    const values = list.value.map(item => item.count)
    const hasData = values.length > 0
    const maxValue = values.length ? Math.max(...values) : 0
    const stepMax = maxValue === 0 ? 10 : Math.ceil(maxValue / 5) * 5
    const shadowData = values.map(() => stepMax)
    return {
        graphic: hasData
            ? []
            : [
                    {
                        type: 'text',
                        left: 'center',
                        top: 'middle',
                        style: {
                            text: '暂无数据',
                            fill: '#FFFFFF',
                            font: '14px Arial',
                            textAlign: 'center',
                            textVerticalAlign: 'middle',
                        },
                    },
              ],
        grid: {
            top: 15,
            left: 10,
            right: 10,
            bottom: 10,
            containLabel: true,
        },
        tooltip: {
            trigger: 'axis',
            backgroundColor: 'rgba(0,0,0,0.7)',
            borderColor: '#0070FF',
            borderWidth: 1,
            textStyle: {
                color: '#FFFFFF',
                fontSize: 14, // 也可以顺便调整字号
            },
            axisPointer: {
                type: 'shadow',
            },
            formatter: params => {
                const target = Array.isArray(params) ? params.find(item => item.seriesIndex === 1) : null
                return `${target?.name ?? ''}:${target?.value ?? 0}`
            },
        },
        xAxis: {
            type: 'category',
            data: names,
            axisLine: {
                show: false,
            },
            axisTick: {
                show: false,
            },
            axisLabel: {
                color: '#FFFFFF',
                fontSize: 12,
                interval: 0,
            },
        },
        yAxis: {
            type: 'value',
            axisLabel: {
                color: '#FFFFFF',
                fontSize: 12,
            },
            axisLine: {
                show: false,
            },
            axisTick: {
                show: false,
            },
            splitLine: {
                show: false,
            },
        },
        series: [
            {
                type: 'bar',
                data: shadowData,
                barWidth: 20,
                itemStyle: {
                    color: 'rgba(255,255,255,0.08)',
                },
                z: 0,
            },
            {
                type: 'bar',
                data: values,
                barWidth: 10,
                barGap: '-75%',
                showBackground: false,
                itemStyle: {
                    color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
                        { offset: 1, color: '#0783FA' },
                        { offset: 0, color: 'rgba(0,85,255,0)' },
                    ]),
                },
                z: 1,
            },
            {
                type: 'pictorialBar',
                data: values,
                symbol: 'rect',
                symbolPosition: 'end',
                symbolSize: [10, 3],
                symbolOffset: [2.5, -2],
                itemStyle: {
                    color: '#6BB7FF',
                },
                z: 3,
            },
        ],
    }
}
onMounted(() => {
    statisticalDeviceOutApi().then(res => {
        list.value = Array.isArray(res?.data?.data) ? res.data.data : []
        chart.value.setOption(buildOption())
    })
})
</script>
<style scoped lang="scss">
.deviceChart {
    width: 638px;
}
.titleBg {
    width: 618px;
    height: 34px;
    background: url('@/assets/images/basicManage/chartBg2.svg') no-repeat center / 100% 100%;
}
</style>
applications/drone-command/src/views/basicManage/deviceStock/DeviceChart3.vue
New file
@@ -0,0 +1,145 @@
<template>
    <div class="deviceChart">
        <div class="titleBg">
            <span>设备生产厂商统计</span>
        </div>
        <div class="chartBox" ref="chartRef"></div>
    </div>
</template>
<script setup>
import * as echarts from 'echarts'
import useEchartsResize from '@/hooks/useEchartsResize'
import { statisticalDeviceManufacturerApi, statisticalDeviceOutApi } from '@/views/basicManage/deviceStock/fwDevice'
const chartRef = ref(null)
let { chart } = useEchartsResize(chartRef)
const list = ref([])
function buildOption() {
    const names = list.value.map(item => item.type)
    const values = list.value.map(item => item.count)
    const hasData = values.length > 0
    return {
        graphic: hasData
            ? []
            : [
                    {
                        type: 'text',
                        left: 'center',
                        top: 'middle',
                        style: {
                            text: '暂无数据',
                            fill: '#FFFFFF',
                            font: '14px Arial',
                            textAlign: 'center',
                            textVerticalAlign: 'middle',
                        },
                    },
              ],
        grid: {
            top: 15,
            left: 10,
            right: 10,
            bottom: 10,
            containLabel: true,
        },
        tooltip: {
            trigger: 'axis',
            backgroundColor: 'rgba(0,0,0,0.7)',
            borderColor: '#0070FF',
            borderWidth: 1,
            textStyle: {
                color: '#FFFFFF',
                fontSize: 14, // 也可以顺便调整字号
            },
            axisPointer: {
                type: 'shadow',
            },
            formatter: params => {
                const target = Array.isArray(params) ? params.find(item => item.seriesIndex === 1) : null
                return `${target?.name ?? ''}:${target?.value ?? 0}`
            },
        },
        xAxis: {
            type: 'category',
            data: names,
            axisLine: {
                show: false,
            },
            axisTick: {
                show: false,
            },
            axisLabel: {
                color: '#FFFFFF',
                fontSize: 12,
                interval: 0,
            },
        },
        yAxis: {
            type: 'value',
            axisLabel: {
                color: '#FFFFFF',
                fontSize: 12,
            },
            axisLine: {
                show: false,
            },
            axisTick: {
                show: false,
            },
            splitLine: {
                show: false,
            },
        },
        series: [
            {
                type: 'bar',
                data: values,
                barWidth: 10,
                itemStyle: {
                    color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
                        { offset: 1, color: '#0783FA' },
                        { offset: 0, color: 'rgba(0,85,255,0)' },
                    ]),
                },
                z: 1,
            },
            {
                type: 'pictorialBar',
                itemStyle: {
                    color: '#0B0B17',
                },
                symbolRepeat: 'fixed',
                symbolMargin: 3,
                symbol: 'rect',
                symbolClip: false,
                symbolPosition: 'end',
                symbolSize: [10, 3],
                data: values,
                z: 2,
            },
        ],
    }
}
onMounted(() => {
    statisticalDeviceManufacturerApi().then(res => {
        list.value = Array.isArray(res?.data?.data) ? res.data.data : []
        chart.value.setOption(buildOption())
    })
})
</script>
<style scoped lang="scss">
.deviceChart {
    width: 638px;
}
.titleBg {
    width: 618px;
    height: 34px;
    background: url('@/assets/images/basicManage/chartBg2.svg') no-repeat center / 100% 100%;
}
</style>
applications/drone-command/src/views/basicManage/deviceStock/DeviceScrapDiaLog.vue
@@ -1,22 +1,22 @@
<template>
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
    <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
        <el-form class="dialog-form" ref="formRef" :model="formData" :rules="rules" label-width="100px">
            <el-row>
                <el-col :span="24">
                    <el-form-item label="报废原因" prop="scrapReason">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.scrapReason" maxlength="200" type="textarea" placeholder="请输入" clearable />
                        <el-input class="command-data-cockpit-search-input" v-model="formData.scrapReason" maxlength="200" type="textarea" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="24">
                    <el-form-item label="处置方式" prop="disposeWay">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.disposeWay" maxlength="200" type="textarea" placeholder="请输入" clearable />
                        <el-input class="command-data-cockpit-search-input" v-model="formData.disposeWay" maxlength="200" type="textarea" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="报废时间" prop="scrapTime">
                        <el-date-picker class="ztzf-data-cockpit-date-picker" popper-class="ztzf-data-cockpit-date-picker-popper"
                        <el-date-picker class="command-data-cockpit-date-picker" popper-class="command-data-cockpit-date-picker-popper"
                            v-model="formData.scrapTime"
                            type="date"
                            type="datetime"
                            placeholder="选择日期"
                            value-format="YYYY-MM-DD HH:mm:ss"
                            clearable
applications/drone-command/src/views/basicManage/deviceStock/DeviceTrackDiaLog.vue
@@ -1,27 +1,27 @@
<template>
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
    <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
        <el-form class="dialog-form" ref="formRef" :model="formData" :rules="rules" label-width="100px">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="负责人" prop="charger">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper" v-model="formData.charger" placeholder="请选择" clearable>
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper" v-model="formData.charger" placeholder="请选择" clearable>
                            <el-option v-for="item in userList" :key="item.id" :label="item.name" :value="item.name" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="联系电话" prop="contactPhone">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.contactPhone" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="command-data-cockpit-search-input" v-model="formData.contactPhone" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="出库去向" prop="outTarget">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.outTarget" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="command-data-cockpit-search-input" v-model="formData.outTarget" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="出库时间" prop="outTime">
                        <el-date-picker class="ztzf-data-cockpit-date-picker" popper-class="ztzf-data-cockpit-date-picker-popper"
                        <el-date-picker class="command-data-cockpit-date-picker" popper-class="command-data-cockpit-date-picker-popper"
                            v-model="formData.outTime"
                            type="date"
                            placeholder="选择日期"
@@ -32,7 +32,7 @@
                </el-col>
                <el-col :span="12">
                    <el-form-item label="用途" prop="purpose">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.purpose" maxlength="50" placeholder="请输入" clearable />
                        <el-input class="command-data-cockpit-search-input" v-model="formData.purpose" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
            </el-row>
applications/drone-command/src/views/basicManage/deviceStock/FormDiaLog.vue
@@ -1,5 +1,5 @@
<template>
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed"
    <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed"
        destroy-on-close>
        <div class="detail-container" v-if="dialogReadonly">
            <div class="detail-title">设备详情</div>
@@ -50,10 +50,10 @@
                </el-col>
            </el-row>
            <div class="detail-title">出库去向详情</div>
            <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
                <div class="ztzf-table-content">
                    <el-table class="ztzf-data-cockpit-table" :data="list">
            <div class="detail-title">设备跟踪流转</div>
            <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
                <div class="command-table-content">
                    <el-table class="command-data-cockpit-table" :data="list">
                        <el-table-column type="index" width="60" label="序号" />
                        <el-table-column prop="charger" label="用户" />
                        <el-table-column prop="contactPhone" label="电话" />
@@ -63,8 +63,8 @@
                    </el-table>
                </div>
                <div class="ztzf-table-pagination">
                    <el-pagination popper-class="ztzf-data-cockpit-select-popper"
                <div class="command-table-pagination">
                    <el-pagination popper-class="command-data-cockpit-select-popper"
                        v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
                        layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
                </div>
@@ -75,13 +75,13 @@
            <el-row>
                <el-col :span="12">
                    <el-form-item label="设备名称" prop="deviceName">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.deviceName" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.deviceName" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="设备类型" prop="deviceType">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                            v-model="formData.deviceType" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.deviceType" :key="item.dictKey" :label="item.dictValue"
                                :value="item.dictKey" />
@@ -90,7 +90,7 @@
                </el-col>
                <el-col :span="12">
                    <el-form-item label="设备属性" prop="deviceAtt">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                            v-model="formData.deviceAtt" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.deviceAtt" :key="item.dictKey" :label="item.dictValue"
                                :value="item.dictKey" />
@@ -99,45 +99,45 @@
                </el-col>
                <el-col :span="12">
                    <el-form-item label="型号" prop="deviceModel">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.deviceModel" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.deviceModel" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="规格" prop="deviceSpecification">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.deviceSpecification"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.deviceSpecification"
                            maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="生产厂商" prop="manufacturer">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.manufacturer" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.manufacturer" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="来源" prop="source">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.source" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.source" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="用途" prop="purpose">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.purpose" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.purpose" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="所属部门" prop="belongDept">
                        <el-tree-select class="ztzf-data-cockpit-select"
                            popper-class="ztzf-data-cockpit-tree-select-popper" v-model="formData.belongDept"
                        <el-tree-select class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-tree-select-popper" v-model="formData.belongDept"
                            node-key="id" :data="deptTree" :props="treeProps" check-strictly @change="belongDeptChange"
                            clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="负责人" prop="charger">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                            v-model="formData.charger" placeholder="请选择" :disabled="!formData.belongDept" clearable>
                            <el-option v-for="item in userList" :key="item.id" :label="item.name" :value="item.name" />
                        </el-select>
@@ -145,7 +145,7 @@
                </el-col>
                <el-col :span="12">
                    <el-form-item label="联系电话" prop="contactPhone">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.contactPhone" maxlength="50"
                        <el-input class="command-data-cockpit-search-input" v-model="formData.contactPhone" maxlength="50"
                            placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js
@@ -5,7 +5,7 @@
    return request({
        url: `/drone-fw/device/fwDevice/page`,
        method: 'get',
        params,
        params:{descs:'update_time',...params},
    })
}
@@ -53,3 +53,28 @@
        responseType: 'blob',
    })
}
//设备类型统计
export const statisticalDeviceTypeApi = params => {
    return request({
        url: `/drone-fw/device/fwDevice/statisticalDeviceType`,
        method: 'get',
        params,
    })
}
//设备出库去向统计
export const statisticalDeviceOutApi = params => {
    return request({
        url: `/drone-fw/device/fwDevice/statisticalDeviceOut`,
        method: 'get',
        params,
    })
}
//设备生产厂商统计
export const statisticalDeviceManufacturerApi = params => {
    return request({
        url: `/drone-fw/device/fwDevice/statisticalDeviceManufacturer`,
        method: 'get',
        params,
    })
}
applications/drone-command/src/views/basicManage/deviceStock/fwDeviceScrapApi.js
@@ -14,7 +14,7 @@
    return request({
        url: `/drone-fw/device/fwDeviceScrap/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
applications/drone-command/src/views/basicManage/deviceStock/fwDeviceTrackApi.js
@@ -14,7 +14,7 @@
    return request({
        url: `/drone-fw/device/fwDeviceTrack/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
applications/drone-command/src/views/basicManage/deviceStock/index.vue
@@ -1,9 +1,15 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
            <el-form-item label="名称" prop="deviceName">
        <div class="chart">
            <DeviceChart1 />
            <DeviceChart2 />
            <DeviceChart3 />
        </div>
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="设备名称" prop="deviceName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.deviceName"
                    placeholder="请输入"
                    clearable
@@ -11,10 +17,10 @@
                />
            </el-form-item>
            <el-form-item label="类型" prop="deviceType">
            <el-form-item label="设备类型" prop="deviceType">
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.deviceType"
                    placeholder="请选择"
                    clearable
@@ -29,10 +35,10 @@
                </el-select>
            </el-form-item>
            <el-form-item label="部门" prop="belongDept">
            <el-form-item label="所属部门" prop="belongDept">
                <el-tree-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-tree-select-popper"
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-tree-select-popper"
                    v-model="searchParams.belongDept"
                    :data="deptTree"
                    :props="treeProps"
@@ -42,40 +48,24 @@
                    @change="handleSearch"
                />
            </el-form-item>
            <el-form-item label="时间">
                <el-date-picker
                    popper-class="ztzf-data-cockpit-date-picker-popper"
                    class="ztzf-data-cockpit-date-picker"
                    v-model="dateRange"
                    type="daterange"
                    range-separator="至"
                    start-placeholder="开始日期"
                    end-placeholder="结束日期"
                    value-format="YYYY-MM-DD HH:mm:ss"
                    @change="handleSearch"
                />
            </el-form-item>
            <el-form-item class="history-search-actions">
                <el-button :icon="RefreshRight" @click="resetForm"></el-button>
                <el-button class="search-btn" :icon="Search" @click="handleSearch"></el-button>
            </el-form-item>
        </el-form>
        <div class="ztzf-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="handleAdd">新增</el-button>
            <el-button :icon="Download" color="#1A2652" type="primary" @click="exportFile" :loading="exportLoading">导出</el-button>
        <div class="command-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="handleAdd">设备建档</el-button>
            <!--            <el-button type="danger" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>-->
        </div>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="deviceName" show-overflow-tooltip width="130" label="设备名称" />
                    <el-table-column prop="deviceType" show-overflow-tooltip width="130" label="类型">
                    <el-table-column prop="deviceType" show-overflow-tooltip width="130" label="设备类型">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.deviceType, dictObj.deviceType) }}
                        </template>
@@ -85,7 +75,7 @@
                    <el-table-column prop="manufacturer" show-overflow-tooltip width="130" label="生产厂商" />
                    <el-table-column prop="createTime" show-overflow-tooltip width="96" label="入库时间">
                        <template v-slot="{ row }">
                            {{ formatDateToSlash(row.createTime) }}
                            {{ dayjs(row.createTime).format('YYYY-MM-DD') }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="source" show-overflow-tooltip width="112" label="来源" />
@@ -109,9 +99,9 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
            <div class="command-table-pagination">
                <el-pagination
                    popper-class="ztzf-data-cockpit-select-popper"
                    popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
@@ -138,6 +128,10 @@
import { blobDownload, dateRangeFormat, getDictLabel, formatDateToSlash } from '@ztzf/utils'
import DeviceTrackDiaLog from '@/views/basicManage/deviceStock/DeviceTrackDiaLog.vue'
import DeviceScrapDiaLog from '@/views/basicManage/deviceStock/DeviceScrapDiaLog.vue'
import DeviceChart1 from '@/views/basicManage/deviceStock/DeviceChart1.vue'
import DeviceChart2 from '@/views/basicManage/deviceStock/DeviceChart2.vue'
import DeviceChart3 from '@/views/basicManage/deviceStock/DeviceChart3.vue'
import dayjs from 'dayjs'
const initSearchParams = () => ({
    deviceName: '', // 设备名称
@@ -267,4 +261,45 @@
    getDeptTreeFun()
})
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.chart {
    margin-bottom: 30px;
    width: 100%;
    height: 234px;
    display: flex;
    :deep() {
        * {
            box-sizing: border-box;
        }
        .deviceChart {
            margin-left: 20px;
            height: 100%;
            background: rgba(26, 26, 42, 0.3);
            border-radius: 10px 10px 10px 10px;
            position: relative;
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            align-items: center;
            padding: 10px;
            &:first-child {
                margin-left: 0;
            }
            .titleBg {
                font-weight: 400;
                font-size: 18px;
                color: #ffffff;
                font-family: PangMen, serif;
                padding-left: 22px;
            }
            .chartBox {
                width: 100%;
                height: 0;
                flex-grow: 1;
            }
        }
    }
}
</style>
applications/drone-command/src/views/basicManage/maintainRecord/FormDiaLog.vue
@@ -1,5 +1,5 @@
<template>
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed"
    <el-dialog class="command-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed"
        destroy-on-close>
        <div class="detail-container" v-if="dialogReadonly">
            <div class="detail-title">设备详情</div>
@@ -47,9 +47,9 @@
            </el-row>
            <div class="detail-title">维护详情</div>
            <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
                <div class="ztzf-table-content">
                    <el-table class="ztzf-data-cockpit-table" :data="list">
            <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
                <div class="command-table-content">
                    <el-table class="command-data-cockpit-table" :data="list">
                        <el-table-column type="index" width="60" label="序号" />
                        <el-table-column prop="maintainTime" label="维护时间" />
                        <el-table-column prop="maintainContent" label="维护内容" />
@@ -57,8 +57,8 @@
                    </el-table>
                </div>
                <div class="ztzf-table-pagination">
                    <el-pagination popper-class="ztzf-data-cockpit-select-popper"
                <div class="command-table-pagination">
                    <el-pagination popper-class="command-data-cockpit-select-popper"
                        v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
                        layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
                </div>
@@ -70,7 +70,7 @@
            <el-row>
                <el-col :span="12">
                    <el-form-item label="选择设备" prop="deviceId">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                            v-model="formData.deviceId" placeholder="请选择" filterable clearable>
                            <el-option v-for="item in deviceList" :key="item.id" :label="item.deviceName"
                                :value="item.id" />
@@ -79,7 +79,7 @@
                </el-col>
                <el-col :span="12">
                    <el-form-item label="维护计划周期" prop="planCycleType">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                            v-model="formData.planCycleType" placeholder="请选择" clearable
                            @change="formData.planCycleValue = []">
                            <el-option v-for="item in planCycleOptions" :key="item.value" :label="item.label"
@@ -89,7 +89,7 @@
                </el-col>
                <el-col :span="12">
                    <el-form-item label="维护计划时间" prop="planCycleValue">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                            v-model="formData.planCycleValue" placeholder="请选择" clearable multiple
                            :disabled="!formData.planCycleType">
                            <el-option v-for="item in planCycleValueOptions" :key="item.value" :label="item.value"
applications/drone-command/src/views/basicManage/maintainRecord/MaintenanceDiaLog.vue
@@ -1,6 +1,11 @@
<template>
    <el-dialog class="ztzf-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed"
        destroy-on-close>
    <el-dialog
        class="command-page-view-dialog"
        v-model="visible"
        :title="titleEnum[dialogMode]"
        @closed="handleClosed"
        destroy-on-close
    >
        <div class="detail-container" v-if="dialogReadonly">
            <div class="detail-title">设备详情</div>
            <el-row>
@@ -14,13 +19,20 @@
                </el-col>
            </el-row>
        </div>
        <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules"
            :disabled="dialogReadonly" label-width="110px">
        <el-form
            class="dialog-form"
            v-else
            ref="formRef"
            :model="formData"
            :rules="rules"
            :disabled="dialogReadonly"
            label-width="110px"
        >
            <el-row>
                <el-col :span="24">
                    <el-form-item label="维护内容" prop="maintainContent">
                        <el-input
                            class="ztzf-data-cockpit-search-input"
                            class="command-data-cockpit-search-input"
                            v-model="formData.maintainContent"
                            maxlength="200"
                            type="textarea"
@@ -32,7 +44,7 @@
                <el-col :span="24">
                    <el-form-item label="更换部件" prop="replacePart">
                        <el-input
                            class="ztzf-data-cockpit-search-input"
                            class="command-data-cockpit-search-input"
                            v-model="formData.replacePart"
                            maxlength="200"
                            type="textarea"
@@ -44,10 +56,10 @@
                <el-col :span="12">
                    <el-form-item label="实际维护时间" prop="maintainTime">
                        <el-date-picker
                            class="ztzf-data-cockpit-date-picker"
                            popper-class="ztzf-data-cockpit-date-picker-popper"
                            class="command-data-cockpit-date-picker"
                            popper-class="command-data-cockpit-date-picker-popper"
                            v-model="formData.maintainTime"
                            type="date"
                            type="datetime"
                            placeholder="选择日期"
                            value-format="YYYY-MM-DD HH:mm:ss"
                            clearable
@@ -102,7 +114,7 @@
const deviceList = ref([]) // 设备列表
const dialogReadonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '维护计划' })
const titleEnum = ref({ edit: '维护', view: '查看', add: '维护计划' })
const planCycleLabel = computed(() => {
    const item = planCycleOptions.find(option => option.value === formData.value.planCycleType)
applications/drone-command/src/views/basicManage/maintainRecord/fwDeviceMaintainRecord.js
@@ -5,7 +5,7 @@
    return request({
        url: `/drone-fw/device/fwDeviceMaintainRecord/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
applications/drone-command/src/views/basicManage/maintainRecord/index.vue
@@ -1,21 +1,21 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
            <el-form-item label="名称" prop="deviceName">
                <el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.deviceName" placeholder="请输入"
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="设备名称" prop="deviceName">
                <el-input class="command-data-cockpit-search-input" v-model="searchParams.deviceName" placeholder="请输入"
                    clearable @clear="handleSearch" />
            </el-form-item>
            <el-form-item label="类型" prop="deviceType">
                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
            <el-form-item label="设备类型" prop="deviceType">
                <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.deviceType" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in dictObj.deviceType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
                </el-select>
            </el-form-item>
            <el-form-item label="部门" prop="belongDept">
                <el-tree-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-tree-select-popper"
            <el-form-item label="所属部门" prop="belongDept">
                <el-tree-select class="command-data-cockpit-select" popper-class="command-data-cockpit-tree-select-popper"
                    v-model="searchParams.belongDept" :data="deptTree" :props="treeProps" node-key="id" check-strictly
                    clearable @change="handleSearch" />
            </el-form-item>
@@ -26,16 +26,15 @@
            </el-form-item>
        </el-form>
        <div class="ztzf-table-toolbar">
        <div class="command-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="handleAdd">维护计划</el-button>
            <el-button :icon="Delete" color="#1A2652" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="deviceName" show-overflow-tooltip label="设备名称" />
                    <el-table-column prop="deviceType" show-overflow-tooltip label="设备类型">
                        <template v-slot="{ row }">
@@ -44,6 +43,7 @@
                    </el-table-column>
                    <el-table-column prop="deviceModel" show-overflow-tooltip label="型号" />
                    <el-table-column prop="manufacturer" show-overflow-tooltip label="生产厂商" />
                    <el-table-column prop="belongDeptName" show-overflow-tooltip label="所属部门" />
                    <el-table-column prop="charger" show-overflow-tooltip label="负责人" />
                    <el-table-column prop="maintainReminder" show-overflow-tooltip label="维护提醒" />
                    <el-table-column prop="planCycleType" show-overflow-tooltip label="维护计划">
@@ -61,8 +61,8 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
                <el-pagination popper-class="ztzf-data-cockpit-select-popper"
            <div class="command-table-pagination">
                <el-pagination popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
            </div>
@@ -158,9 +158,9 @@
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'ztzf-page-view-message-box',
        confirmButtonClass: 'ztzf-message-box-confirm',
        cancelButtonClass: 'ztzf-message-box-cancel',
        customClass: 'command-page-view-message-box',
        confirmButtonClass: 'command-message-box-confirm',
        cancelButtonClass: 'command-message-box-cancel',
    })
    const ids = row ? row.id : selectedIds.value.join(',')
    await fwDeviceMaintainPlanRemoveApi({ ids })
applications/drone-command/src/views/basicManage/maintainRecord/maintainRecordApi.js
@@ -5,7 +5,7 @@
    return request({
        url: `/drone-fw/device/fwDeviceMaintainPlan/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
@@ -41,6 +41,6 @@
    return request({
        url: `/drone-fw/device/fwDevice/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
applications/drone-command/src/views/dataCenter/components/dataCenterMap.vue
@@ -1,12 +1,12 @@
<template>
  <el-dialog modal-class="mapDialog"  v-model="isShow" width="80%" class="ztzf-dialog-mange">
  <el-dialog modal-class="mapDialog"  v-model="isShow" width="80%" class="command-dialog-mange">
     <template #header="{ titleId, titleClass }">
          <div class="my-header">
            <h4 :id="titleId" :class="titleClass">定位</h4>
          </div>
        </template>
    <div class="mapBox">
      <div v-if="isShow" id="dataCenterMap" class="ztzf-cesium"></div>
      <div v-if="isShow" id="dataCenterMap" class="command-cesium"></div>
    </div>
  </el-dialog>
</template>
applications/drone-command/src/views/dataCenter/dataCenter.vue
@@ -209,7 +209,7 @@
      <!-- 查看弹框 -->
      <el-dialog
        v-model="dialogVisible"
        class="ztzf-dialog-mange"
        class="command-dialog-mange"
        width="60%"
        append-to-body
        @close="dialogClose"
@@ -237,7 +237,7 @@
            <div
              v-else-if="dialogDetailList?.resultType === 4"
              id="detaildataCenterMap"
              class="ztzf-cesium"
              class="command-cesium"
            ></div>
            <img v-else :src="getSmallImg(dialogDetailList?.link)" alt="" />
          </div>
@@ -318,7 +318,7 @@
      <!-- 视频预览 -->
      <el-dialog
        :title="currentVideoTitle"
        class="ztzf-dialog-mange"
        class="command-dialog-mange"
        modal-class="videoDialog"
        append-to-body
        width="54%"
applications/drone-command/src/views/dataCockpit/components/CenterContainer.vue
@@ -153,15 +153,30 @@
    .left-box,
    .right-box {
        position: relative;
        padding: 17px 20px;
        display: flex;
        align-items: center;
        height: 100px;
        background: #05050F;
        background: rgba(0,0,0,0.3);
        box-sizing: border-box;
        border-radius: 16px 16px 16px 16px;
        pointer-events: auto;
        &::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: -1;
            background: rgba(17, 23, 34, 0.56);
              backdrop-filter: blur(2px);
            border-radius: 16px 16px 16px 16px;
        }
        .logo {
            img {
                width: 40px;
applications/drone-command/src/views/dataCockpit/components/DeviceHistoryDialog.vue
@@ -1,30 +1,29 @@
<template>
    <el-dialog class="ztzf-data-cockpit-dialog" append-to-body v-model="visibleModel" :title="dialogTitle"
    <el-dialog class="command-data-cockpit-dialog" append-to-body v-model="visibleModel" :title="dialogTitle"
        :close-on-click-modal="false" :destroy-on-close="true">
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-dialog-history-search">
        <el-form ref="queryParamsRef" :model="searchParams" class="command-dialog-history-search">
            <el-form-item label="关键字" prop="keyword">
                <el-input class="ztzf-data-cockpit-search-input" v-model="searchParams.keyword" placeholder="名称/序列号"
                <el-input class="command-data-cockpit-search-input" v-model="searchParams.keyword" placeholder="名称/序列号"
                    clearable @clear="handleSearch" />
            </el-form-item>
            <el-form-item prop="dateRange">
                <el-date-picker class="ztzf-data-cockpit-date-picker"
                    popper-class="ztzf-data-cockpit-tree-select-popper" v-model="searchParams.dateRange"
                <el-date-picker class="command-data-cockpit-date-picker"
                    popper-class="command-data-cockpit-tree-select-popper" v-model="searchParams.dateRange"
                    type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"
                    value-format="YYYY/MM/DD" @change="handleSearch" />
            </el-form-item>
            <el-form-item label="区域" prop="area">
                <el-tree-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-tree-select-popper"
                    v-model="searchParams.area" :data="areaTree" :props="areaTreeProps" node-key="value" check-strictly
                    clearable placeholder="请选择" @change="handleSearch" />
                <el-tree-select class="command-data-cockpit-select" popper-class="command-data-cockpit-tree-select-popper"
                    v-model="searchParams.area" :data="areaTree" :props="areaTreeProps" node-key="value" check-strictly filterable :filter-node-method="filterAreaNode" clearable placeholder="请选择" @change="handleSearch" />
            </el-form-item>
            <el-form-item label="无人机类型" prop="droneType">
                <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.droneType" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in droneTypeOptions" :key="item.value" :label="item.label"
                        :value="item.value" />
                    <el-option v-for="item in dictObj.droneType" :key="item.dictKey" :label="item.dictValue"
                        :value="item.dictKey" />
                </el-select>
            </el-form-item>
@@ -34,9 +33,9 @@
            </el-form-item>
        </el-form>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content">
                <el-table class="ztzf-data-cockpit-table" :data="list">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content">
                <el-table class="command-data-cockpit-table" :data="list">
                    <el-table-column type="index" width="60" label="序号" />
                    <el-table-column prop="droneName" label="无人机名称" min-width="120" />
                    <el-table-column prop="droneSerialNo" label="无人机序列号" min-width="140" />
@@ -62,8 +61,8 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
                <el-pagination popper-class="ztzf-data-cockpit-select-popper"
            <div class="command-table-pagination">
                <el-pagination popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
            </div>
@@ -74,7 +73,9 @@
<script setup>
import { Search, RefreshRight } from '@element-plus/icons-vue'
import { computed, ref, watch } from 'vue'
import { alarmLogApi } from '@/api/dataCockpit'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { alarmLogApi, areaDivideListApi } from '@/api/dataCockpit'
import { getDictLabel } from '@ztzf/utils'
const props = defineProps({
    modelValue: {
@@ -111,29 +112,41 @@
const loading = ref(true)
const list = ref([])
const total = ref(0)
const areaTree = ref([
    {
        label: '古村区',
        value: '古村区'
    },
    {
        label: '新区',
        value: '新区'
    },
    {
        label: '产业园',
        value: '产业园'
    }
])
const dictObj = ref({
    droneType: []
})
const areaTree = ref([])
const areaTreeProps = {
    label: 'label',
    children: 'children'
}
const droneTypeOptions = [
    { label: '微型机', value: '1' },
    { label: '植保机', value: '2' }
]
const fetchDictList = async () => {
    const res = await getDictionaryByCode('droneType')
    dictObj.value = res?.data?.data ?? { droneType: [] }
}
const filterAreaNode = (value, data) => {
    if (!value) return true
    return (data?.label || '').includes(value)
}
const fetchAreaTree = async () => {
    try {
        const res = await areaDivideListApi()
        const list = res?.data?.data ?? []
        areaTree.value = list
            .filter((item) => item?.areaName)
            .map((item) => ({
                label: item.areaName,
                value: item.areaName
            }))
    } catch (error) {
        areaTree.value = []
    }
}
const getList = async () => {
    if (!props.device?.id) {
@@ -188,9 +201,7 @@
}
const getDroneTypeText = (value) => {
    if (value === 1 || value === '1') return '微型机'
    if (value === 2 || value === '2') return '植保机'
    return value || '-'
    return getDictLabel(value, dictObj.value.droneType) || value || '-'
}
watch(
@@ -198,7 +209,11 @@
    (visible) => {
        if (visible) {
            searchParams.value.current = 1
            fetchDictList()
            fetchAreaTree()
            getList()
        } else {
            searchParams.value = initSearchParams()
        }
    }
)
applications/drone-command/src/views/dataCockpit/components/HistoryWarning.vue
@@ -8,14 +8,14 @@
        <!-- 搜索区(设计稿红框) -->
        <div class="search-box">
            <!-- 关键字 -->
            <el-input v-model="query.keyword" class="ztzf-data-cockpit-search-input" placeholder="请输入数据名称" clearable
            <el-input v-model="query.keyword" class="command-data-cockpit-search-input" placeholder="请输入数据名称" clearable
                @keyup.enter="onSearch">
            </el-input>
            <!-- 日期范围 -->
            <el-date-picker v-model="query.dateRange" 
                  class="ztzf-data-cockpit-date-picker"
                 popper-class="ztzf-data-cockpit-date-picker-popper"
                  class="command-data-cockpit-date-picker"
                 popper-class="command-data-cockpit-date-picker-popper"
                type="daterange" 
                range-separator="~"
                start-placeholder="创建开始日期" end-placeholder="结束日期" format="YYYY年MM月DD日" value-format="YYYY-MM-DD"
@@ -109,7 +109,7 @@
        flex-direction: column;
        gap: 10px;
        ::v-deep(.ztzf-data-cockpit-date-picker) {
        ::v-deep(.command-data-cockpit-date-picker) {
            width: 100% !important;
        }
    }
applications/drone-command/src/views/dataCockpit/components/LeftContainer.vue
@@ -107,14 +107,14 @@
    transition: transform 0.3s ease-in-out;
    &.collapsed {
        transform: translateX(-100%);
        transform: translateX(calc(-100% - 17px - 32px));
    }
}
.collapse-btn {
    position: absolute;
    top: 50%;
    left: 357px;
    left: 300px;
    cursor: pointer;
    display: flex;
    align-items: center;
@@ -124,8 +124,8 @@
    transform: translateY(-50%);
    .arrow {
        width: 24px;
        height: 24px;
        width: 32px;
        height: 32px;
        background: url('@/assets/images/dataCockpit/left-btn.png') center / 100% 100% no-repeat;
    }
applications/drone-command/src/views/dataCockpit/components/LegendBar.vue
New file
@@ -0,0 +1,108 @@
<!--
 * @Author       : yuan
 * @Date         : 2026-01-17 14:51:39
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-17 15:13:50
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\LegendBar.vue
 * @Description  :
 * Copyright 2026 OBKoro1, All Rights Reserved.
 * 2026-01-17 14:51:39
-->
<template>
    <div class="legend-bar">
        <div class="legend-title" data-text="图例">图例</div>
        <div class="legend-item">
            <span class="legend-color defense"></span>
            <span class="legend-text" data-text="防区">防区</span>
        </div>
        <div class="legend-item">
            <span class="legend-color partition"></span>
            <span class="legend-text" data-text="区域">区域</span>
        </div>
        <div class="legend-item">
            <img class="legend-icon drone" :src="droneIcon" alt="无人机" />
            <span class="legend-text" data-text="无人机">无人机</span>
        </div>
        <div class="legend-item">
            <img class="legend-icon" :src="commandPostIcon" alt="指挥点" />
            <span class="legend-text" data-text="指挥点">指挥点</span>
        </div>
        <div class="legend-item">
            <img class="legend-icon" :src="equipmentIcon" alt="设备" />
            <span class="legend-text" data-text="设备">设备</span>
        </div>
    </div>
</template>
<script setup>
import droneIcon from '@/assets/images/dataCockpit/legend/drone.png'
import commandPostIcon from '@/assets/images/dataCockpit/legend/command-post.png'
import equipmentIcon from '@/assets/images/dataCockpit/legend/equipment.png'
</script>
<style scoped lang="scss">
.legend-bar {
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 10px 16px;
    font-family: Source Han Sans CN, Source Han Sans CN;
    font-size: 14px;
    color: #FFFFFF;
    text-align: center;
    font-style: normal;
    text-transform: none;
}
.legend-title {
    font-weight: 800;
}
.legend-item {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
}
.legend-title,
.legend-text {
    position: relative;
    white-space: nowrap;
    text-shadow:
    -1px -1px 0 black,
     1px -1px 0 black,
    -1px  1px 0 black,
     1px  1px 0 black;
}
.legend-color {
    width: 30px;
    height: 30px;
    border-radius: 0;
}
.legend-color.defense {
    background: radial-gradient(50% 50% at 50% 50%, #2aedbf 0%, #012b11 100%);
    border: 1px solid #19d266;
}
.legend-color.partition {
    background: radial-gradient(50% 50% at 50% 50%, #ffc609 0%, #583300 100%);
    border: 1px solid #ffcd2a;
}
.legend-icon {
    width: 30px;
    height: 42px;
    display: block;
}
.legend-icon.drone {
    width: 30px;
    height: 30px;
    display: block;
}
</style>
applications/drone-command/src/views/dataCockpit/components/RealWarning.vue
@@ -44,30 +44,16 @@
    counter: '诱导驱离'
}
const buildPayload = (item) => ({
    alarmRecordId: item.id,
    areaCode: item.areaCode,
    counterEffect: '',
    coverRadiusM: item.coverRadiusM,
    deployLatitude: item.latitude,
    deployLongitude: item.longitude,
    deviceCode: item.deviceCode,
const buildPayload = (type, item) => ({
    counterWay: type === 'signal' ? 1 : type === 'counter' ? 2 : '',
    deviceId: item.deviceId,
    deviceModel: item.deviceModel,
    deviceName: item.deviceName,
    deviceType: item.deviceType,
    droneDeviceCode: item.droneSerialNo,
    droneName: item.droneName,
    droneType: item.droneType,
    findTime: item.alarmTime,
    id: item.id,
    workMode: ''
    droneDeviceCode: item.droneDeviceCode ?? item.droneSerialNo
})
const handleAction = async (type, item) => {
    const actionText = actionTextMap[type] || '操作'
    try {
        const res = await interferenceAndExpulsionApi(buildPayload(item))
        const res = await interferenceAndExpulsionApi(buildPayload(type, item))
        if (res?.data?.success) {
            ElMessage({ type: 'success', message: `${actionText}成功` })
        } else {
applications/drone-command/src/views/dataCockpit/components/RightContainer.vue
@@ -2,7 +2,7 @@
 * @Author       : yuan
 * @Date         : 2026-01-07 15:17:54
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-09 16:54:31
 * @LastEditTime : 2026-01-16 17:23:06
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\RightContainer.vue
 * @Description  : 
 * Copyright 2026 OBKoro1, All Rights Reserved. 
@@ -259,14 +259,14 @@
    position: relative;
    &.collapsed {
        transform: translateX(100%);
        transform: translateX(calc(100% + 17px + 32px));
    }
}
.collapse-btn {
    position: absolute;
    top: 50%;
    right: 357px;
    right: 300px;
    cursor: pointer;
    display: flex;
    align-items: center;
@@ -276,8 +276,8 @@
    transform: translateY(-50%);
    .arrow {
        width: 24px;
        height: 24px;
        width: 32px;
        height: 32px;
        background: url('@/assets/images/dataCockpit/right-btn.png') center / 100% 100% no-repeat;
    }
applications/drone-command/src/views/dataCockpit/index.vue
@@ -2,7 +2,7 @@
 * @Author       : yuan
 * @Date         : 2026-01-06 16:35:50
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-09 16:34:39
 * @LastEditTime : 2026-01-16 17:17:24
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\index.vue
 * @Description  : 
 * Copyright 2026 OBKoro1, All Rights Reserved. 
@@ -18,6 +18,7 @@
    <LeftContainer class="left-container" v-model:activeId="leftActiveId" v-model:collapsed="leftCollapsed" />  
    <RightContainer class="right-container" v-model:collapsed="rightCollapsed" v-model:onlineDevices="onlineDevices" />  
    <CenterContainer @select-warning="onSelectWarning" @select-equipment="onSelectEquipment" />  
    <LegendBar class="legend-container" />
  </div>
</template>
@@ -26,6 +27,7 @@
  import LeftContainer from './components/LeftContainer.vue';
  import RightContainer from './components/RightContainer.vue';
  import CenterContainer from './components/CenterContainer.vue';
  import LegendBar from './components/LegendBar.vue';
  const leftActiveId = ref(1);
  const leftCollapsed = ref(false);
@@ -53,44 +55,44 @@
  .left-container,
  .right-container {
    position: absolute;
    top: 96px;
    bottom: 0;
    width: 401px;
    bottom: 20px;
    width: 300px;
    z-index: 9;
    backdrop-filter: blur(2px);
    border-radius: 10px 10px 10px 10px;
    ::v-deep(.wrapper) {
      display: flex;
      flex-direction: column;
      position: absolute;
      top: 17px;
      bottom: 20px;
      top: 0;
      bottom: 0;
      padding: 20px;
      width: 300px;
      background: rgba(0,0,0,0.3);
      backdrop-filter: blur(16px);
      border-radius: 10px 10px 10px 10px;
      box-sizing: border-box;
    }
  }
  .left-container {
    top: 96px;
    left: 0;
    top: 113px;
    left: 17px;
    background: linear-gradient( 270deg, rgba(17,23,34,0) 0%, rgba(17,23,34,0.56) 50%, rgba(17,23,34,0.96) 100%);
    ::v-deep(.wrapper) {
      left: 17px;
    }
  }
  .right-container {
    top: 54px;
    right: 0;
    top: 75px;
    right: 17px;
    background: linear-gradient( 270deg, rgba(17,23,34,0.96) 0.16%, rgba(17,23,34,0.56) 57.57%, rgba(4,30,37,0) 100%);
  }
    ::v-deep(.wrapper) {
      top: 21px;
      right: 17px;
    }
  .legend-container {
    position: absolute;
    left: 50%;
    bottom: 22px;
    transform: translateX(-50%);
    z-index: 9;
  }
</style>
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/FormDiaLog.vue
@@ -1,6 +1,6 @@
<template>
    <el-dialog
        class="ztzf-page-view-dialog"
        class="command-page-view-dialog"
        v-model="visible"
        :title="titleEnum[dialogMode]"
        :close-on-click-modal="false"
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/countermeasureEvaluationApi.js
@@ -5,7 +5,7 @@
    return request({
        url: `/drone-fw/detection/fwEffectEval/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/index.vue
@@ -1,9 +1,9 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="设备名称" prop="deviceName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.deviceName"
                    placeholder="请输入"
                    clearable
@@ -13,8 +13,8 @@
            <el-form-item label="设备类型" prop="deviceType">
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.deviceType"
                    placeholder="请选择"
                    clearable
@@ -31,8 +31,8 @@
            <el-form-item label="反制效果" prop="counterEffect">
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.counterEffect"
                    placeholder="请选择"
                    clearable
@@ -53,9 +53,9 @@
            </el-form-item>
        </el-form>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list">
                    <el-table-column type="index" width="64" show-overflow-tooltip label="序号" />
                    <el-table-column prop="droneName" show-overflow-tooltip width="130" label="无人机名称" />
                    <el-table-column prop="droneType" show-overflow-tooltip width="130" label="无人机类型" />
@@ -91,7 +91,7 @@
                            {{ getWorkModeLabel(row.workMode) }}
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" class-name="operation-btns" width="100">
                    <el-table-column fixed="right" label="操作" class-name="operation-btns fixed-column" width="100">
                        <template v-slot="{ row }">
                            <el-link @click="openForm('view', row)" type="primary">查看</el-link>
                        </template>
@@ -99,9 +99,9 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
            <div class="command-table-pagination">
                <el-pagination
                    popper-class="ztzf-data-cockpit-select-popper"
                    popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue
@@ -1,9 +1,23 @@
<template>
    <el-dialog v-model="visible" class="ztzf-page-map-view-dialog" :show-close="false" :close-on-click-modal="false"
        destroy-on-close @closed="handleClosed">
    <el-dialog
        v-model="visible"
        class="command-page-map-view-dialog"
        :show-close="false"
        :close-on-click-modal="false"
        destroy-on-close
        @closed="handleClosed"
    >
        <div class="dialog-container">
            <div class="left-container">
                <div class="leftMap ztzf-cesium" id="detectionRangeMap"></div>
                <CommonCesiumMap
                    ref="mapRef"
                    class="leftMap command-cesium"
                    :active="visible"
                    :flat-mode="false"
                    :terrain="true"
                    :layer-mode="4"
                    :boundary="false"
                />
            </div>
            <div class="right-container">
@@ -32,52 +46,90 @@
                            <div class="val">{{ formData.deviceModel || '-' }}</div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">覆盖范围</div>
                            <div class="label">覆盖范围(m)</div>
                            <div class="val">{{ formatRange(formData.effectiveRangeKm) }}</div>
                        </el-col>
                    </el-row>
                </div>
                <el-form v-else ref="formRef" class="dialog-form" :model="formData" :rules="rules" label-width="96px">
                <el-form v-else ref="formRef" class="dialog-form" :model="formData" :rules="rules" label-width="110px">
                    <el-form-item label="侦测设备" prop="deviceId">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                            v-model="formData.deviceId" placeholder="请选择" clearable @change="handleDeviceChange">
                            <el-option v-for="item in deviceOptions" :key="item.value" :label="item.label"
                                :value="item.value" />
                        <el-select
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-select-popper"
                            v-model="formData.deviceId"
                            placeholder="请选择"
                            clearable
                            @change="handleDeviceChange"
                        >
                            <el-option v-for="item in deviceOptions" :key="item.value" :label="item.label" :value="item.value" />
                        </el-select>
                    </el-form-item>
                    <el-form-item label="设备类型" prop="deviceType">
                        <el-select class="ztzf-data-cockpit-select" popper-class="ztzf-data-cockpit-select-popper"
                            v-model="formData.deviceType" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.deviceType" :key="item.dictKey" :label="item.dictValue"
                                :value="item.dictKey" />
                        <el-select
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-select-popper"
                            v-model="formData.deviceType"
                            placeholder="请选择"
                            clearable
                        >
                            <el-option
                                v-for="item in dictObj.deviceType"
                                :key="item.dictKey"
                                :label="item.dictValue"
                                :value="item.dictKey"
                            />
                        </el-select>
                    </el-form-item>
                    <el-form-item label="设备位置" prop="deployLocation">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.deployLocation"
                            placeholder="请选择位置">
                        <el-input
                            class="command-data-cockpit-search-input"
                            v-model="formData.deployLocation"
                            disabled
                            placeholder="请选择位置"
                        >
                            <template #suffix>
                                <span class="suffix-action">地图选点</span>
                            </template>
                        </el-input>
                    </el-form-item>
                    <el-form-item label="设备编号" prop="deviceSn">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.deviceSn" placeholder="请输入"
                            clearable />
                        <el-input
                            class="command-data-cockpit-search-input"
                            v-model="formData.deviceSn"
                            placeholder="请输入"
                            clearable
                        />
                    </el-form-item>
                    <el-form-item label="设备型号" prop="deviceModel">
                        <el-input class="ztzf-data-cockpit-search-input" v-model="formData.deviceModel"
                            placeholder="请输入" clearable />
                        <el-input
                            class="command-data-cockpit-search-input"
                            v-model="formData.deviceModel"
                            placeholder="请输入"
                            clearable
                        />
                    </el-form-item>
                    <el-form-item label="覆盖范围" prop="effectiveRangeKm">
                        <el-input-number class="ztzf-data-cockpit-search-input" v-model="formData.effectiveRangeKm" :min="0" :precision="2" :controls="false"
                            placeholder="请输入" />
                    <el-form-item label="覆盖范围(m)" prop="effectiveRangeKm">
                        <el-input-number
                            class="command-data-cockpit-search-input"
                            v-model="formData.effectiveRangeKm"
                            :min="0"
                            :precision="2"
                            :controls="false"
                            placeholder="请输入"
                        />
                    </el-form-item>
                </el-form>
                <div class="footer">
                    <el-button color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
                    <el-button color="#284FE3" v-if="!dialogReadonly" type="primary" :loading="submitting"
                        :disabled="submitting" @click="handleSubmit">
                    <el-button
                        color="#284FE3"
                        v-if="!dialogReadonly"
                        type="primary"
                        :loading="submitting"
                        :disabled="submitting"
                        @click="handleSubmit"
                    >
                        保存
                    </el-button>
                </div>
@@ -87,15 +139,16 @@
</template>
<script setup>
import { computed, inject, nextTick, ref } from 'vue'
import { computed, inject, nextTick, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import {
    detectionRangeDetailApi,
    detectionRangePageApi,
    detectionRangeSubmitApi,
} from '@/api/detectionCountermeasure/detectionRange'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
import * as Cesium from 'cesium'
import deviceZcImg from '@/assets/images/dataCockpit/device-zc.svg'
const initForm = () => ({
    deviceId: '', // 侦测设备ID
@@ -111,13 +164,15 @@
const emit = defineEmits(['success'])
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = ref(false) // 弹框显隐
const visible = defineModel() // 弹框显隐
const dialogMode = ref('add') // 弹框模式
const submitting = ref(false) // 提交中
const deviceOptions = ref([])
const dialogReadonly = computed(() => dialogMode.value === 'view')
const mapRef = ref(null)
let viewer
let redPointEntity
let rangeCircleEntity
const formTitle = computed(() => {
    if (dialogMode.value === 'edit') {
        return '编辑侦测范围'
@@ -135,7 +190,7 @@
    effectiveRangeKm: [{ required: true, message: '请输入覆盖范围', trigger: ['blur', 'change'] }],
}
function handleDeviceChange (value) {
function handleDeviceChange(value) {
    const target = deviceOptions.value.find(item => item.value === value)
    const raw = target?.raw
    if (!raw) return
@@ -148,24 +203,24 @@
        raw.deployLocation ?? raw.address ?? raw.location ?? raw.deployAddress ?? formData.value.deployLocation
}
function getDeviceTypeLabel (value) {
function getDeviceTypeLabel(value) {
    const list = dictObj?.deviceType ?? []
    const target = list.find(item => item.dictKey === value)
    return target?.dictValue ?? value ?? '-'
}
function formatRange (value) {
function formatRange(value) {
    if (value == null || value === '') return '-'
    return `${value}km`
    return `${value}`
}
// 关闭弹框
function handleCancel () {
function handleCancel() {
    visible.value = false
}
// 提交新增/编辑
async function handleSubmit () {
async function handleSubmit() {
    const isValid = await formRef.value?.validate().catch(() => false)
    if (!isValid) return
    submitting.value = true
@@ -186,13 +241,13 @@
}
// 加载详情
async function loadDetail (id) {
async function loadDetail(id) {
    if (!id) return null
    const res = await detectionRangeDetailApi({ id })
    return res?.data?.data ?? null
}
async function loadDeviceOptions () {
async function loadDeviceOptions() {
    const res = await detectionRangePageApi({
        current: 1,
        size: 200,
@@ -207,34 +262,38 @@
}
// 关闭后重置
function handleClosed () {
function handleClosed() {
    formData.value = initForm()
    clearRangeCircle()
    viewer = null
    redPointEntity = null
    rangeCircleEntity = null
}
function setMapPoint (longitude, latitude) {
function setMapPoint(longitude, latitude) {
    if (!viewer || longitude == null || latitude == null) return
    if (!redPointEntity) {
        redPointEntity = viewer.entities.add({
            position: Cesium.Cartesian3.fromDegrees(longitude, latitude),
            point: {
                color: Cesium.Color.RED,
                pixelSize: 10,
            billboard: {
                image: deviceZcImg, // 这里替换为你的图片路径或变量 deviceZcImg
                width: 30, // 可选:设置图片宽度
                height: 42, // 可选:设置图片高度
                verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 设置图片底部对齐坐标点
            },
            label: {
                text: `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`,
                font: '14px',
                font: '16px',
                fillColor: Cesium.Color.WHITE,
                backgroundColor: Cesium.Color.BLACK.withAlpha(0.45),
                backgroundPadding: new Cesium.Cartesian2(5, 5),
                pixelOffset: new Cesium.Cartesian2(0, -20),
                showBackground: true,
                style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                verticalOrigin: Cesium.VerticalOrigin.CENTER,
                outlineWidth: 1,
                outlineColor: Cesium.Color.WHITE,
                pixelOffset: new Cesium.Cartesian2(0, -60),
                eyeOffset: new Cesium.Cartesian3(0, 0, -20),
            },
        })
@@ -242,24 +301,61 @@
        redPointEntity.position = Cesium.Cartesian3.fromDegrees(longitude, latitude)
        redPointEntity.label.text = `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`
    }
    updateRangeCircle(longitude, latitude, formData.value.effectiveRangeKm)
}
function initMap () {
    if (viewer) return
    const publicCesiumInstance = new PublicCesium({
        dom: 'detectionRangeMap',
        flatMode: false,
        terrain: true,
        layerMode: 4,
        boundary: false,
    })
    if (!dialogReadonly.value) {
        publicCesiumInstance.addLeftClickEvent(null, handleMapClick)
function parseDeployLocation(value) {
    if (!value) return null
    const [longitude, latitude] = value.split(',').map(item => Number(item.trim()))
    if (Number.isNaN(longitude) || Number.isNaN(latitude)) return null
    return { longitude, latitude }
}
function clearRangeCircle() {
    if (viewer && rangeCircleEntity) {
        viewer.entities.remove(rangeCircleEntity)
    }
    viewer = publicCesiumInstance.getViewer()
}
function handleMapClick (click) {
function updateRangeCircle(longitude, latitude, range) {
    if (!viewer || longitude == null || latitude == null) return
    const rangeMeters = Number(range)
    if (!Number.isFinite(rangeMeters) || rangeMeters <= 0) {
        clearRangeCircle()
        rangeCircleEntity = null
        return
    }
    const position = Cesium.Cartesian3.fromDegrees(longitude, latitude)
    if (!rangeCircleEntity) {
        rangeCircleEntity = viewer.entities.add({
            position,
            ellipse: {
                semiMajorAxis: rangeMeters,
                semiMinorAxis: rangeMeters,
                material: Cesium.Color.fromCssColorString('#6b9ca8').withAlpha(0.6),
                outline: true,
                outlineColor: Cesium.Color.fromCssColorString('#6b9ca8').withAlpha(0.6),
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            },
        })
        return
    }
    rangeCircleEntity.position = position
    rangeCircleEntity.ellipse.semiMajorAxis = rangeMeters
    rangeCircleEntity.ellipse.semiMinorAxis = rangeMeters
}
function initMap() {
    if (viewer) return
    const map = mapRef.value?.getMap()
    if (!map?.viewer) return
    viewer = map.viewer
    if (!dialogReadonly.value) {
        map.publicCesium?.addLeftClickEvent?.(null, handleMapClick)
    }
}
function handleMapClick(click) {
    const pos = click.position
    const cartesian3 = viewer.camera.pickEllipsoid(pos, viewer.scene.globe.ellipsoid)
    if (!cartesian3) return
@@ -271,9 +367,8 @@
}
// 打开弹框
async function open ({ mode, row } = {}) {
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    visible.value = true
    await nextTick()
    initMap()
    if (dialogMode.value === 'add') {
@@ -286,6 +381,7 @@
        const detail = await loadDetail(row?.id)
        formData.value = detail ?? row
    }
    formData.value.deployLocation = `${formData.value.longitude}, ${formData.value.latitude}`
    formData.value.deviceId = formData.value.id ?? formData.value.deviceId
    await loadDeviceOptions()
    const currentName = formData.value.deviceName
@@ -295,14 +391,24 @@
    }
    const location = formData.value.deployLocation
    if (location) {
        const [longitude, latitude] = location.split(',').map(item => Number(item.trim()))
        if (!Number.isNaN(longitude) && !Number.isNaN(latitude)) {
            setMapPoint(longitude, latitude)
        const parsed = parseDeployLocation(location)
        if (parsed) {
            setMapPoint(parsed.longitude, parsed.latitude)
            viewer.flyTo(redPointEntity, { duration: 0 })
        }
    }
}
defineExpose({ open })
watch(
    () => formData.value.effectiveRangeKm,
    value => {
        const parsed = parseDeployLocation(formData.value.deployLocation)
        if (!parsed) return
        updateRangeCircle(parsed.longitude, parsed.latitude, value)
    }
)
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue
@@ -1,9 +1,9 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
            <el-form-item label="名称" prop="deviceName">
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="设备名称" prop="deviceName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.deviceName"
                    placeholder="请输入"
                    clearable
@@ -11,10 +11,10 @@
                />
            </el-form-item>
            <el-form-item label="类型" prop="deviceType">
            <el-form-item label="设备类型" prop="deviceType">
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.deviceType"
                    placeholder="请选择"
                    clearable
@@ -35,13 +35,13 @@
            </el-form-item>
        </el-form>
        <div class="ztzf-table-toolbar">
        <div class="command-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="handleAdd">新增</el-button>
        </div>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list">
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="deviceName" show-overflow-tooltip label="设备名称" />
                    <el-table-column prop="deviceType" show-overflow-tooltip label="设备类型">
@@ -55,7 +55,7 @@
                        </template>
                    </el-table-column>
                    <el-table-column prop="deviceModel" show-overflow-tooltip label="型号" />
                    <el-table-column show-overflow-tooltip label="覆盖范围">
                    <el-table-column show-overflow-tooltip label="覆盖范围(m)">
                        <template v-slot="{ row }">
                            {{ formatRange(row) }}
                        </template>
@@ -75,9 +75,9 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
            <div class="command-table-pagination">
                <el-pagination
                    popper-class="ztzf-data-cockpit-select-popper"
                    popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
@@ -87,7 +87,7 @@
            </div>
        </div>
        <DetectionRangeDialog ref="dialogRef" @success="getList" />
        <DetectionRangeDialog ref="dialogRef" @success="getList" v-model="visible" v-if="visible" />
    </basic-container>
</template>
<script setup>
@@ -115,7 +115,7 @@
const dictObj = ref({
    deviceType: [], // 设备类型
})
const visible = ref(false)
provide('dictObj', dictObj)
// 获取列表
@@ -145,26 +145,35 @@
// 新增
function handleAdd() {
    dialogRef.value?.open({ mode: 'add' })
    visible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode: 'add' })
    })
}
// 查看
function handleView(row) {
    dialogRef.value?.open({ mode: 'view', row: { ...row } })
    visible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode: 'view', row: { ...row } })
    })
}
// 编辑
function handleEdit(row) {
    dialogRef.value?.open({ mode: 'edit', row: { ...row } })
    visible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode: 'edit', row: { ...row } })
    })
}
// 删除
async function handleDelete(row) {
    await ElMessageBox.confirm('确认删除该条数据吗?', '提示', {
        type: 'warning',
        customClass: 'ztzf-page-view-message-box',
        confirmButtonClass: 'ztzf-message-box-confirm',
        cancelButtonClass: 'ztzf-message-box-cancel',
        customClass: 'command-page-view-message-box',
        confirmButtonClass: 'command-message-box-confirm',
        cancelButtonClass: 'command-message-box-cancel',
    })
    await detectionRangeSubmitApi({ id: row.id, effectiveRangeKm: 0 })
    ElMessage.success('删除成功')
@@ -179,20 +188,8 @@
}
function formatDeployLocation(row) {
    if (!row) return '-'
    const location = row.deployLocation ?? row.address ?? row.location ?? row.deployAddress
    if (Array.isArray(location) && location.length) {
        return location.filter(value => value !== null && value !== undefined).join(',')
    }
    if (typeof location === 'string' && location.trim()) {
        return location
    }
    const longitude = row.deployLongitude ?? row.longitude
    const latitude = row.deployLatitude ?? row.latitude
    if (longitude != null || latitude != null) {
        return `${longitude ?? '-'},${latitude ?? '-'}`
    }
    return '-'
    if (!row.longitude || !row.latitude) return ''
    return `${row.longitude}, ${row.latitude}`
}
function formatRange(row) {
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/FormDiaLog.vue
@@ -1,6 +1,6 @@
<template>
    <el-dialog
        class="ztzf-page-view-dialog"
        class="command-page-view-dialog"
        v-model="visible"
        :title="titleEnum[dialogMode]"
        :close-on-click-modal="false"
@@ -45,12 +45,13 @@
            </el-row>
        </div>
        <el-form v-else ref="formRef" :model="formData" :rules="rules" label-width="120px" class="dialog-form">
        <el-form v-else ref="formRef" :model="formData" :rules="rules" label-width="120" class="dialog-form">
            <el-row>
                <el-col :span="24"><h3 style="color: #ffffff">工作模式</h3></el-col>
                <el-col :span="12">
                    <el-form-item label="起始扇区" prop="startSector">
                        <el-input
                            class="ztzf-data-cockpit-search-input"
                            class="command-data-cockpit-search-input"
                            v-model="formData.startSector"
                            maxlength="50"
                            placeholder="请输入"
@@ -61,7 +62,7 @@
                <el-col :span="12">
                    <el-form-item label="终止扇区" prop="endSector">
                        <el-input
                            class="ztzf-data-cockpit-search-input"
                            class="command-data-cockpit-search-input"
                            v-model="formData.endSector"
                            maxlength="50"
                            placeholder="请输入"
@@ -72,8 +73,8 @@
                <el-col :span="12">
                    <el-form-item label="搜索模式" prop="searchMode">
                        <el-select
                            class="ztzf-data-cockpit-select"
                            popper-class="ztzf-data-cockpit-select-popper"
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-select-popper"
                            v-model="formData.searchMode"
                            placeholder="请选择"
                            clearable
@@ -85,7 +86,7 @@
                <el-col :span="12">
                    <el-form-item label="雷达频点" prop="radarFreq">
                        <el-input
                            class="ztzf-data-cockpit-search-input"
                            class="command-data-cockpit-search-input"
                            v-model="formData.radarFreq"
                            maxlength="50"
                            placeholder="请输入"
@@ -93,11 +94,12 @@
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="24"><h3 style="color: #ffffff">工作模式</h3></el-col>
                <el-col :span="12">
                    <el-form-item label="伺服模式" prop="servoMode">
                        <el-select
                            class="ztzf-data-cockpit-select"
                            popper-class="ztzf-data-cockpit-select-popper"
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-select-popper"
                            v-model="formData.servoMode"
                            placeholder="请选择"
                            clearable
@@ -109,8 +111,8 @@
                <el-col :span="12">
                    <el-form-item label="伺服数据模式" prop="servoDataMode">
                        <el-select
                            class="ztzf-data-cockpit-select"
                            popper-class="ztzf-data-cockpit-select-popper"
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-select-popper"
                            v-model="formData.servoDataMode"
                            placeholder="请选择"
                            clearable
@@ -127,8 +129,8 @@
                <el-col :span="12">
                    <el-form-item label="轴选择" prop="axisSelect">
                        <el-select
                            class="ztzf-data-cockpit-select"
                            popper-class="ztzf-data-cockpit-select-popper"
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-select-popper"
                            v-model="formData.axisSelect"
                            placeholder="请选择"
                            clearable
@@ -140,7 +142,7 @@
                <el-col :span="12">
                    <el-form-item label="伺服转速度" prop="servoTurnSpeed">
                        <el-input
                            class="ztzf-data-cockpit-search-input"
                            class="command-data-cockpit-search-input"
                            v-model="formData.servoTurnSpeed"
                            maxlength="50"
                            placeholder="请输入"
@@ -169,7 +171,7 @@
<script setup>
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { fwDeviceConfigSubmitApi } from './deviceAppConfigApi'
import { fwDeviceConfigDetailApi, fwDeviceConfigSubmitApi } from './deviceAppConfigApi'
import { fieldRules } from '@ztzf/utils'
const configKeys = [
@@ -204,7 +206,7 @@
const dialogMode = ref('view') // 弹框模式
const submitting = ref(false) // 提交中
const readonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看' })
const titleEnum = ref({ edit: '配置接口', view: '查看' })
const searchModeOptions = [
    { label: '0', value: '0' },
@@ -267,10 +269,18 @@
    }
}
function loadingDetails() {
    fwDeviceConfigDetailApi({ deviceId: formData.value.deviceId }).then(res => {
        const configJson = res.data.data ? JSON.parse(res.data.data.configJson) : {}
        formData.value = { ...formData.value, ...configJson }
    })
}
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'view'
    formData.value = { ...initForm(), deviceId: row.id }
    loadingDetails()
}
defineExpose({ open })
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue
@@ -1,9 +1,9 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="设备名称" prop="deviceName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.deviceName"
                    placeholder="请输入"
                    clearable
@@ -13,8 +13,8 @@
            <el-form-item label="设备类型" prop="deviceType">
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.deviceType"
                    placeholder="请选择"
                    clearable
@@ -29,15 +29,33 @@
                </el-select>
            </el-form-item>
            <el-form-item label="设备状态" prop="deviceStatus">
                <el-select
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.deviceStatus"
                    placeholder="请选择"
                    clearable
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.deviceStatus"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
                    />
                </el-select>
            </el-form-item>
            <el-form-item class="history-search-actions">
                <el-button :icon="RefreshRight" @click="resetForm"></el-button>
                <el-button class="search-btn" :icon="Search" @click="handleSearch"></el-button>
            </el-form-item>
        </el-form>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list">
                    <el-table-column type="index" width="64" show-overflow-tooltip label="序号" />
                    <el-table-column prop="deviceType" show-overflow-tooltip label="设备类型">
                        <template v-slot="{ row }">
@@ -47,7 +65,16 @@
                    <el-table-column prop="deviceModel" show-overflow-tooltip label="设备型号" />
                    <el-table-column prop="deviceName" show-overflow-tooltip label="设备名称" />
                    <el-table-column prop="deviceSn" show-overflow-tooltip label="设备编码" />
                    <el-table-column prop="deployLocation" show-overflow-tooltip label="部署位置" />
                    <el-table-column prop="deviceStatus" show-overflow-tooltip label="设备状态">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.status, dictObj.deviceStatus) }}
                        </template>
                    </el-table-column>
                    <el-table-column show-overflow-tooltip label="部署位置">
                        <template v-slot="{ row }">
                            {{ formatDeployLocation(row) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="manufacturer" show-overflow-tooltip label="厂商" />
                    <el-table-column prop="contactPhone" show-overflow-tooltip label="联系方式" />
                    <el-table-column prop="sceneName" show-overflow-tooltip label="所属场景" />
@@ -56,12 +83,18 @@
                            <el-link @click="openForm('edit', row)" type="warning">配置接口</el-link>
                        </template>
                    </el-table-column>
                    <!--                    <el-table-column label="操作" class-name="operation-btns">
                        <template v-slot="{ row }">
                            <el-link @click="handleEdit(row)">编辑</el-link>
                            <el-link @click="handleDelete(row)" type="danger">删除</el-link>
                        </template>
                    </el-table-column>-->
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
            <div class="command-table-pagination">
                <el-pagination
                    popper-class="ztzf-data-cockpit-select-popper"
                    popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
@@ -71,22 +104,27 @@
            </div>
        </div>
        <FormDiaLog v-if="dialogVisible" v-model="dialogVisible" ref="dialogRef" @success="getList" />
        <ConfigFormDiaLog v-if="dialogVisible" v-model="dialogVisible" ref="configDialogRef" @success="getList" />
        <DeviceFormDiaLog ref="deviceFormRef" @success="getList" />
    </basic-container>
</template>
<script setup>
import { RefreshRight, Search } from '@element-plus/icons-vue'
import { nextTick, onMounted, provide, ref } from 'vue'
import FormDiaLog from './FormDiaLog.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import ConfigFormDiaLog from './FormDiaLog.vue'
import DeviceFormDiaLog from '@/views/basicManage/deviceStock/FormDiaLog.vue'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { getDictLabel } from '@ztzf/utils'
import { fwDevicePageApi } from '@/views/basicManage/deviceStock/fwDevice'
import { fwDevicePageApi, fwDeviceRemoveApi } from '@/views/basicManage/deviceStock/fwDevice'
import { getDeptTree } from '@/api/system/dept'
// 初始化查询参数
const initSearchParams = () => ({
    deviceName: '', // 设备名称
    deviceType: '', // 设备类型
    deviceStatus: '', // 设备状态
    current: 1, // 当前页
    size: 10, // 每页大小
})
@@ -96,13 +134,18 @@
const loading = ref(true) // 列表加载中
const list = ref([]) // 列表数据
const queryParamsRef = ref(null) // 查询表单实例
const dialogRef = ref(null) // 弹框实例
const configDialogRef = ref(null) // 弹框实例
const deviceFormRef = ref(null) // 设备表单弹框实例
const dialogVisible = ref(false)
const deptTree = ref([])
const dictObj = ref({
    deviceType: [], //设备类型
    deviceStatus: [], //设备状态
    deviceAtt: [], //设备属性
})
provide('dictObj', dictObj)
provide('deptTree', deptTree)
// 获取列表
async function getList() {
@@ -131,8 +174,14 @@
// 获取字典
function getDictList() {
    getDictionaryByCode('deviceType').then(res => {
    getDictionaryByCode('deviceType,deviceStatus,deviceAtt').then(res => {
        dictObj.value = res.data.data
    })
}
function getDeptTreeFun() {
    getDeptTree().then(res => {
        deptTree.value = res.data.data
    })
}
@@ -140,13 +189,35 @@
function openForm(mode, row) {
    dialogVisible.value = true
    nextTick(() => {
        dialogRef.value?.open({ mode, row })
        configDialogRef.value?.open({ mode, row })
    })
}
function handleEdit(row) {
    deviceFormRef.value?.open({ mode: 'edit', row })
}
async function handleDelete(row) {
    await ElMessageBox.confirm('确认删除该设备吗?', '提示', {
        type: 'warning',
        customClass: 'command-page-view-message-box',
        confirmButtonClass: 'command-message-box-confirm',
        cancelButtonClass: 'command-message-box-cancel',
    })
    await fwDeviceRemoveApi({ ids: row.id })
    ElMessage.success('删除成功')
    getList()
}
function formatDeployLocation(row) {
    if (!row.longitude || !row.latitude) return ''
    return `${row.longitude}, ${row.latitude}`
}
onMounted(() => {
    getList()
    getDictList()
    getDeptTreeFun()
})
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/FormDiaLog.vue
@@ -1,6 +1,6 @@
<template>
    <el-dialog
        class="ztzf-page-view-dialog"
        class="command-page-view-dialog"
        v-model="visible"
        :title="titleEnum[dialogMode]"
        destroy-on-close
@@ -65,8 +65,8 @@
                <el-col :span="12">
                    <el-form-item label="设备" prop="deviceId">
                        <el-select
                            class="ztzf-data-cockpit-select"
                            popper-class="ztzf-data-cockpit-select-popper"
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-select-popper"
                            v-model="formData.deviceId"
                            placeholder="请选择"
                            clearable
@@ -79,7 +79,7 @@
                <el-col :span="12">
                    <el-form-item label="设备编码" prop="deviceSn">
                        <el-input
                            class="ztzf-data-cockpit-search-input"
                            class="command-data-cockpit-search-input"
                            v-model="formData.deviceSn"
                            disabled
                            placeholder="选择设备后自动带出"
@@ -89,8 +89,8 @@
                <el-col :span="12">
                    <el-form-item label="场景" prop="defenseSceneId">
                        <el-select
                            class="ztzf-data-cockpit-select"
                            popper-class="ztzf-data-cockpit-select-popper"
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-select-popper"
                            v-model="formData.defenseSceneId"
                            placeholder="请选择"
                            clearable
@@ -103,8 +103,8 @@
                <el-col :span="12">
                    <el-form-item label="区域" prop="areaDivideId">
                        <el-select
                            class="ztzf-data-cockpit-select"
                            popper-class="ztzf-data-cockpit-select-popper"
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-select-popper"
                            v-model="formData.areaDivideId"
                            placeholder="请先选择场景"
                            clearable
@@ -118,7 +118,7 @@
                <el-col :span="12">
                    <el-form-item label="调度人员" prop="dispatchUser">
                        <el-input
                            class="ztzf-data-cockpit-search-input"
                            class="command-data-cockpit-search-input"
                            v-model="formData.dispatchUser"
                            maxlength="50"
                            placeholder="请输入"
@@ -129,8 +129,8 @@
                <el-col :span="12">
                    <el-form-item label="调度日期" prop="dispatchDateRange">
                        <el-date-picker
                            class="ztzf-data-cockpit-date-picker"
                            popper-class="ztzf-data-cockpit-date-picker-popper"
                            class="command-data-cockpit-date-picker"
                            popper-class="command-data-cockpit-date-picker-popper"
                            v-model="formData.dispatchDateRange"
                            type="datetimerange"
                            range-separator="至"
@@ -153,7 +153,7 @@
                <el-col :span="24">
                    <el-form-item label="调度内容" prop="dispatchContent">
                        <el-input
                            class="ztzf-data-cockpit-search-input"
                            class="command-data-cockpit-search-input"
                            v-model="formData.dispatchContent"
                            type="textarea"
                            :rows="3"
@@ -166,7 +166,7 @@
                <el-col :span="24">
                    <el-form-item label="调度结果" prop="dispatchResult">
                        <el-input
                            class="ztzf-data-cockpit-search-input"
                            class="command-data-cockpit-search-input"
                            v-model="formData.dispatchResult"
                            type="textarea"
                            :rows="3"
@@ -179,7 +179,7 @@
                <el-col :span="24">
                    <el-form-item label="处理措施" prop="disposeMeasure">
                        <el-input
                            class="ztzf-data-cockpit-search-input"
                            class="command-data-cockpit-search-input"
                            v-model="formData.disposeMeasure"
                            type="textarea"
                            :rows="3"
@@ -207,7 +207,7 @@
        </template>
        <el-dialog v-model="visible1" title="选择位置" destroy-on-close @closed="positionClosed">
            <div class="ztzf-cesium" id="mapContainer"></div>
            <div class="command-cesium" id="mapContainer"></div>
            <template #footer>
                <el-button @click="handleMapCancel">取消</el-button>
                <el-button type="primary" @click="handleMapConfirm">确定</el-button>
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/index.vue
@@ -1,10 +1,10 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="设备名称" prop="deviceId">
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.deviceId"
                    placeholder="请选择"
                    clearable
@@ -15,18 +15,18 @@
                </el-select>
            </el-form-item>
            <el-form-item label="设备类型" prop="deviceType">
            <el-form-item label="设备状态" prop="status">
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    v-model="searchParams.deviceType"
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.status"
                    placeholder="请选择"
                    clearable
                    @clear="handleSearch"
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.deviceType"
                        v-for="item in dictObj.deviceStatus"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
@@ -34,9 +34,9 @@
                </el-select>
            </el-form-item>
            <el-form-item label="设备编号" prop="deviceSn">
            <el-form-item label="设备编码" prop="deviceSn">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.deviceSn"
                    placeholder="请输入"
                    clearable
@@ -50,23 +50,23 @@
            </el-form-item>
        </el-form>
        <div class="ztzf-table-toolbar">
        <div class="command-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="openForm('add')">新增</el-button>
            <el-button :icon="Delete" color="#1A2652" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="deviceName" show-overflow-tooltip width="140" label="设备名称" />
                    <el-table-column prop="deviceSn" show-overflow-tooltip width="140" label="设备编码" />
                    <!--                    <el-table-column prop="deviceStatus" show-overflow-tooltip width="100" label="设备状态">
                            <template v-slot="{ row }">
                                {{ getDictLabel(row.deviceStatus, dictObj.deviceStatus) }}
                            </template>
                        </el-table-column>-->
                    <el-table-column prop="status" show-overflow-tooltip width="100" label="设备状态">
                        <template v-slot="{ row }">
                            {{ formatDeployLocation(row) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="areaName" show-overflow-tooltip width="120" label="区域" />
                    <el-table-column prop="defenseZoneName" show-overflow-tooltip width="120" label="场景" />
                    <el-table-column show-overflow-tooltip label="调度位置">
@@ -90,9 +90,9 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
            <div class="command-table-pagination">
                <el-pagination
                    popper-class="ztzf-data-cockpit-select-popper"
                    popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
@@ -127,7 +127,7 @@
// 初始化查询参数
const initSearchParams = () => ({
    deviceId: '', // 设备名称
    deviceType: '', // 设备类型
    status: '', // 设备状态
    deviceSn: '', // 设备编号
    current: 1, // 当前页
    size: 10, // 每页大小
@@ -157,7 +157,10 @@
    }
    return row.dispatchStartTime || row.dispatchEndTime || ''
}
function formatDeployLocation(row) {
    if (!row.longitude || !row.latitude) return ''
    return `${row.longitude}, ${row.latitude}`
}
// 获取列表
async function getList() {
    loading.value = true
applications/drone-command/src/views/detectionCountermeasure/taskSchedule/taskScheduleApi.js
@@ -5,7 +5,7 @@
    return request({
        url: `/drone-fw/detection/fwTaskSchedule/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
applications/drone-command/src/views/device/airport.vue
@@ -142,7 +142,7 @@
      <el-input v-model="operate_password" disabled></el-input>
    </el-dialog>
    <el-dialog title="注销" class="ztzf-dialog-mange zx-cancel" append-to-body v-model="cancenOperate" width="560px">
    <el-dialog title="注销" class="command-dialog-mange zx-cancel" append-to-body v-model="cancenOperate" width="560px">
      <div class="txt-contain">
        <img :src="warnSvg" />注销
        <span class="name">
@@ -204,7 +204,7 @@
      </template>
    </el-dialog>
    <el-drawer class="ztzf-drawer-body-basic-container ztzf-el-card__body" title="机场授权管理" size="60%" append-to-body
    <el-drawer class="command-drawer-body-basic-container command-el-card__body" title="机场授权管理" size="60%" append-to-body
      v-model="devicePerShareVisible" :direction="'rtl'">
      <DevicePerShare ref="devicePerShare" />
    </el-drawer>
applications/drone-command/src/views/device/components/firmwareManage.vue
@@ -25,10 +25,10 @@
        </el-button>
      </template>
    </avue-crud>
    <el-dialog title="固件上传" class="ztzf-dialog-mange" append-to-body v-model="firmwareUploadBox" width="40%" height="60%">
    <el-dialog title="固件上传" class="command-dialog-mange" append-to-body v-model="firmwareUploadBox" width="40%" height="60%">
      <el-form
        ref="form"
        class="ztzf-form-search"
        class="command-form-search"
        :model="importForm"
        v-loading="loadingForm"
        style="width: 80%; margin-left: 10%"
applications/drone-command/src/views/job/components/DeviceJobDetails.vue
@@ -1,6 +1,6 @@
<!-- 历史任务详情 -->
<template>
  <el-dialog class="ztzf-dialog" append-to-body modal-class="detailsOfHistoricalTasks" v-model="isShow" title="巡检任务详情"
  <el-dialog class="command-dialog" append-to-body modal-class="detailsOfHistoricalTasks" v-model="isShow" title="巡检任务详情"
    :width="pxToRem(1800)" :close-on-click-modal="false" :destroy-on-close="true">
    <div class="content">
      <div class="contentLeft">
@@ -23,7 +23,7 @@
              回放直播
            </el-button>
            <el-popover placement="bottom-start" popper-class="ztzf-custom-popover bottom">
            <el-popover placement="bottom-start" popper-class="command-custom-popover bottom">
              <template #reference>
                <el-button class="playback-video-btn" v-show="videoData.length > 1" type="primary" size="small">
                  回放直播
@@ -180,7 +180,7 @@
      </div>
    </div>
  </el-dialog>
  <el-dialog class="ztzf-dialog" append-to-body modal-class="detailsOfHistoricalTasks" v-model="VideoShow"
  <el-dialog class="command-dialog" append-to-body modal-class="detailsOfHistoricalTasks" v-model="VideoShow"
    :width="pxToRem(1600)" :close-on-click-modal="false" :destroy-on-close="true">
    <div class="fullscreen">
      <video ref="fullscreenVideo" class="fullscreen-video" :src="currentVideoUrl"
applications/drone-command/src/views/job/components/DeviceJobDetailsMap.vue
@@ -1,5 +1,5 @@
<template>
    <div id="deviceJobDetailsMap" class="ztzf-cesium"></div>
    <div id="deviceJobDetailsMap" class="command-cesium"></div>
</template>
<script setup>
import { flyVisual } from '@/utils/cesium/mapUtil'
applications/drone-command/src/views/job/components/JobRelatedEvents.vue
@@ -44,7 +44,7 @@
            </div>
    </div>
    <div class="ztzf-table">
    <div class="command-table">
        <el-table
            v-loading="loading"
            :row-class-name="tableRowClassName"
@@ -90,7 +90,7 @@
          <div class="btnGroups">
            <el-popover
              v-if="scope.row.status === 3 || scope.row.status === 0"
              popper-class="ztzf-custom-qrcode-popover"
              popper-class="command-custom-qrcode-popover"
              :width="pxToRem(119)"
              :visible="scope.row.showQR"
              placement="top"
@@ -112,7 +112,7 @@
        </el-table>
    </div>
    <el-pagination v-if="list.length > 0"
        class="ztzf-pagination"
        class="command-pagination"
        popper-class="custom-pagination-dropdown"
        background
        :page-sizes="[10, 20, 30, 50]"
applications/drone-command/src/views/job/components/TaskAlgorithmBusiness.vue
@@ -2,7 +2,7 @@
<template>
  <div class="task-algorithm" v-if="showAlgorithm">
    <el-select
        class="ztzf-select"
        class="command-select"
        :teleported="false"
        :style="{ width: pxToRem(setWidth) }"
        v-model="ai_types"
@@ -18,7 +18,7 @@
    </el-select>
  </div>
  <div class="task-business" v-if="showBusiness">
    <el-select class="ztzf-select"
    <el-select class="command-select"
               :teleported="false"
               :style="{ width: pxToRem(setWidth) }"
               v-model="industry_type"
@@ -99,7 +99,7 @@
//     --el-tag-hover-color: none !important;
//     color: #fff !important;
// }
::v-deep(.ztzf-select) {
::v-deep(.command-select) {
  .el-select__wrapper {
    z-index: 1111;
applications/drone-command/src/views/job/components/TaskIntermediateContent/AddTask.vue
@@ -1,7 +1,7 @@
<!-- 新建任务 -->
<template>
    <el-dialog
        class="ztzf-dialog"
        class="command-dialog"
        modal-class="add-task"
        v-model="isShowAddTask"
        title="新建任务"
@@ -16,13 +16,13 @@
                <div class="search">
                    <div class="item">
                        <div class="itemchild">任务名称:</div>
                        <el-input class="ztzf-input" v-model="searchForm.name" placeholder="请输入"></el-input>
                        <el-input class="command-input" v-model="searchForm.name" placeholder="请输入"></el-input>
                    </div>
                    <div class="item">
                        <div class="itemchild">日期选择:</div>
                        <el-date-picker
                            popper-class="custom-date-picker"
                            class="ztzf-date-picker tasktimer"
                            class="command-date-picker tasktimer"
                            v-model="taskData"
                            type="date"
                            placeholder="请选择日期"
@@ -35,7 +35,7 @@
                        <el-time-picker
                            style="flex: 1"
                            popper-class="custom-time-picker"
                            class="ztzf-date-picker tasktimer"
                            class="command-date-picker tasktimer"
                            v-model="timeSlot"
                            placeholder="请选择"
                            format="HH:mm"
@@ -47,7 +47,7 @@
                        <el-select
                            :teleported="false"
                            class="ztzf-select"
                            class="command-select"
                            v-model="searchForm.file_id"
                            @change="getWayLineFile"
                            placeholder="请选择"
@@ -67,7 +67,7 @@
                    </div>
                    <div class="item">
                        <div class="itemchild">任务描述:</div>
                        <el-input class="ztzf-input" v-model="searchForm.remark" placeholder="请输入"></el-input>
                        <el-input class="command-input" v-model="searchForm.remark" placeholder="请输入"></el-input>
                    </div>
                </div>
                <div class="lines">
applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -50,7 +50,7 @@
                                effect="dark"
                                :content="scope.row.reason"
                                placement="top"
                                popper-class="reasonNotFly ztzf-tooltip-box1"
                                popper-class="reasonNotFly command-tooltip-box1"
                            >
                                <el-icon v-if="scope.row.status === 5 && scope.row.reason" color="#FF4848"><QuestionFilled /></el-icon>
                            </el-tooltip>
@@ -98,7 +98,7 @@
            </el-table>
        </div>
        <div class="pagination" v-if="jobListData.length > 0">
            <el-pagination class="ztzf-pagination" popper-class="custom-pagination-dropdown" background
            <el-pagination class="command-pagination" popper-class="custom-pagination-dropdown" background
                :page-sizes="[10, 20, 30, 40, 50, 100]" :size="size" v-model:current-page="jobListParams.current"
                v-model:page-size="jobListParams.size" layout="total, sizes, prev, pager, next, jumper" :total="total"
                @size-change="handleSizeChange" @current-change="handleCurrentChange" />
applications/drone-command/src/views/job/components/TaskIntermediateContent/TaskTable.vue
@@ -1,5 +1,5 @@
<template>
    <div class="table-container ztzf-table">
    <div class="table-container command-table">
        <div class="taskTableTitle"><span>可飞行机巢列表</span></div>
        <el-table
            :data="tableData"
@@ -19,7 +19,7 @@
        </el-table>
        <div class="pagination">
            <el-pagination
                class="ztzf-pagination"
                class="command-pagination"
                v-model:current-page="pagingParams.page"
                v-model:page-size="pagingParams.size"
                layout=" prev, pager, next"
applications/drone-command/src/views/job/components/executeNestDetails.vue
@@ -5,7 +5,7 @@
            任务执行详情
        </div>
    </div>
    <div class="ztzf-table">
    <div class="command-table">
        <el-table
            v-loading="loading"
            element-loading-background="rgba(22, 43, 74, 0.79)"
@@ -55,7 +55,7 @@
                            effect="dark"
                            :content="scope.row.reason"
                            placement="top"
                            popper-class="reasonNotFly ztzf-tooltip-box1"
                            popper-class="reasonNotFly command-tooltip-box1"
                        >
                            <el-icon v-if="scope.row.status === 5 && scope.row.reason" color="#FF4848"><QuestionFilled /></el-icon>
                        </el-tooltip>
@@ -138,7 +138,7 @@
    }
}
.ztzf-table {
.command-table {
    margin-bottom: 16px;
}
applications/drone-command/src/views/layerManagement/components/folderFile.vue
@@ -5,18 +5,18 @@
      <div class="itemRow">
        <div class="label">{{ itSLandPlanning ? '文件' : '文件夹' }}名称</div>
        <div>
          <el-input class="ztzf-layer-input inputName" v-model="formData.name" placeholder="请输入" />
          <el-input class="command-layer-input inputName" v-model="formData.name" placeholder="请输入" />
        </div>
      </div>
      <div class="itemRow"
        v-if="(layerParams.addAnEditingFolder === 1 && layerParams.fileType !== 2) || (layerParams.addAnEditingFolder === 2 && layerParams.fileType !== 2) && !itSLandPlanning">
        <div class="label">关联算法</div>
        <div>
          <el-select class="ztzf-layer-search filter-item" @change="handleTypeChange" v-model="formData.event_type"
          <el-select class="command-layer-search filter-item" @change="handleTypeChange" v-model="formData.event_type"
            placeholder="请选择" clearable>
            <el-option v-for="item in types" :key="item.value" :label="item.label" :value="item.value" />
          </el-select>
          <el-select v-model="formData.algorithm_name" class="ztzf-layer-search" placeholder="请选择" clearable
          <el-select v-model="formData.algorithm_name" class="command-layer-search" placeholder="请选择" clearable
            :disabled="!formData.event_type">
            <el-option v-for="item in algorithms" :key="item.value" :label="item.label" :value="item.value" />
          </el-select>
applications/drone-command/src/views/layerManagement/components/leftList.vue
@@ -2,20 +2,20 @@
  <div class="table-overlay">
    <div class="searchBox">
      <el-input
        class="ztzf-layer-input inputName"
        class="command-layer-input inputName"
        v-model="formData.dkbh"
        placeholder="输入名称模糊搜索"
      ></el-input>
      <div class="searchAndReset">
        <el-button type="primary" icon="el-icon-search" @click="search">搜索</el-button>
        <el-button class="ztzf-button" icon="el-icon-refresh" @click="reset">重置</el-button>
        <el-button class="command-button" icon="el-icon-refresh" @click="reset">重置</el-button>
      </div>
    </div>
    <div class="btnGroups">
      <el-button v-if="activeName !== '国土空间规划'" class="ztzf-button" @click="addFence">{{
      <el-button v-if="activeName !== '国土空间规划'" class="command-button" @click="addFence">{{
        props.activeName === '自定义识别区' ? '新增识别区' : '新增禁飞区'
      }}</el-button>
      <el-button v-if="activeName !== '国土空间规划'" class="ztzf-button" @click="addFolder"
      <el-button v-if="activeName !== '国土空间规划'" class="command-button" @click="addFolder"
        >新增文件夹</el-button
      >
      <el-upload
@@ -25,19 +25,19 @@
        :before-upload="e => uploadFlightFile(e, '1')"
        accept=".zip"
      >
        <el-button class="ztzf-button"> 上传 </el-button>
        <el-button class="command-button"> 上传 </el-button>
      </el-upload>
      <!-- <template v-if="activeName === '自定义禁飞区'">
        <template v-if="failuresInfo.length">
          <el-button class="ztzf-button" @click="synchronize"> 继续同步 </el-button>
          <el-button class="ztzf-button" @click="failuresDel"> 取消同步 </el-button>
          <el-button class="command-button" @click="synchronize"> 继续同步 </el-button>
          <el-button class="command-button" @click="failuresDel"> 取消同步 </el-button>
        </template>
        <el-button v-else class="ztzf-button" @click="synchronize"> 同步 </el-button>
        <el-button v-else class="command-button" @click="synchronize"> 同步 </el-button>
      </template> -->
      <!-- <el-button class="ztzf-button" @click="selectAll">全选</el-button> -->
      <el-button class="ztzf-button" @click="selectAll">
      <!-- <el-button class="command-button" @click="selectAll">全选</el-button> -->
      <el-button class="command-button" @click="selectAll">
  {{ checkedKeys.length === getAllFenceKeys().length ? '取消全选' : '全选' }}
</el-button>
    </div>
@@ -48,7 +48,7 @@
      element-loading-text="加载中..."
    >
      <el-tree
        class="ztzf-el-tree"
        class="command-el-tree"
        :class="{ isTheTerritory: activeName === '国土空间规划' }"
        ref="treeRef"
        v-model="checkedKeys"
applications/drone-command/src/views/layerManagement/components/rightEdit.vue
@@ -7,7 +7,7 @@
        <div>
          <el-select
            :disabled="isDisabled"
            class="ztzf-layer-search"
            class="command-layer-search"
            clearable
            v-model="formData.folder_id"
            placeholder="请选择"
@@ -26,7 +26,7 @@
        <div>
          <el-input
            :disabled="isDisabled"
            class="ztzf-layer-input inputName"
            class="command-layer-input inputName"
            v-model="formData.name"
            placeholder="请输入"
          />
@@ -35,10 +35,10 @@
      <div class="itemRow" v-if="layerParams.fenceType === 2">
        <div class="label">禁飞区管控时间</div>
        <div>
          <!-- popper-class="ztzf-layer-date-picker-popper  " -->
          <!-- popper-class="command-layer-date-picker-popper  " -->
          <el-date-picker
            :disabled="isDisabled"
            class="ztzf-layer-date-picker"
            class="command-layer-date-picker"
            v-model="timeValue"
            type="datetimerange"
            format="YYYY-MM-DD HH:mm"
@@ -92,7 +92,7 @@
            <span
              ><el-input
                :disabled="isDisabled"
                class="ztzf-layer-input inputName"
                class="command-layer-input inputName"
                v-model="formData.altitude"
                placeholder="请输入"
            /></span>
applications/drone-command/src/views/layerManagement/index.vue
@@ -20,7 +20,7 @@
         <div class="tool-tip" v-if="!isShowWaringTip && layerParams.addNest">
          {{ showToolTipText }}
        </div>
        <div id="layMap" class="ztzf-cesium"></div>
        <div id="layMap" class="command-cesium"></div>
        <leftList
          v-if="
            !layerParams.addNest &&
applications/drone-command/src/views/license/license.vue
@@ -41,7 +41,7 @@
        </avue-crud>
        <!-- 新增弹窗 -->
        <el-dialog class="ztzf-dialog-mange" title="新增 License" v-model="addDialogVisible" width="70%" :close-on-click-modal="false"
        <el-dialog class="command-dialog-mange" title="新增 License" v-model="addDialogVisible" width="70%" :close-on-click-modal="false"
            @close="resetForm">
            <el-form :model="newLicense" ref="licenseForm" label-width="120px">
                <el-form-item label="机构ID" prop="licenseId">
applications/drone-command/src/views/monitor/log/flightLog.vue
@@ -1,7 +1,7 @@
<!-- 飞行日志 -->
<template>
  <div class="flight">
    <div class="ztzf-form-search">
    <div class="command-form-search">
      <el-form :model="params" inline>
        <el-row :gutter="24">
          <el-col :span="4">
@@ -84,13 +84,13 @@
    </div>
    <div class="pagination">
      <el-pagination class="ztzf-pagination" popper-class="custom-pagination-dropdown" background
      <el-pagination class="command-pagination" popper-class="custom-pagination-dropdown" background
                     :page-sizes="[10, 20, 30, 40, 50, 100]" :size="size" v-model:current-page="params.page"
                     v-model:page-size="params.page_size" layout="total, sizes, prev, pager, next, jumper" :total="total"
                     @size-change="handleSizeChange" @current-change="handleCurrentChange" />
    </div>
  </div>
  <el-dialog class="ztzf-dialog-mange" append-to-body v-model="isShowView" title="查看" :width="pxToRem(1200)" :close-on-click-modal="false" :destroy-on-close="true">
  <el-dialog class="command-dialog-mange" append-to-body v-model="isShowView" title="查看" :width="pxToRem(1200)" :close-on-click-modal="false" :destroy-on-close="true">
    <el-table border :data="tableDataDetails" height="466">
      <el-table-column label="序号" type="index" width="60"></el-table-column>
      <el-table-column prop="flight_type" label="飞行类型" align="center" show-overflow-tooltip></el-table-column>
@@ -102,15 +102,15 @@
    </el-table>
    <template #footer>
      <div class="pagination dialog-footer" style="display: flex;justify-content: end;margin-top: 10px;">
        <el-pagination class="ztzf-pagination" popper-class="custom-pagination-dropdown" background
        <el-pagination class="command-pagination" popper-class="custom-pagination-dropdown" background
                       :page-sizes="[10, 20, 30, 40, 50, 100]" :size="size" v-model:current-page="detailParams.current"
                       v-model:page-size="detailParams.size" layout="total, sizes, prev, pager, next, jumper" :total="detailTotal"
                       @size-change="handleSizeChangeDetails" @current-change="handleCurrentChangeDetails" />
      </div>
    </template>
  </el-dialog>
  <el-dialog class="ztzf-dialog-mange" append-to-body v-model="isShowFlyMapView" title="查看" :width="pxToRem(1000)" :close-on-click-modal="false" :destroy-on-close="true">
    <div id="flyMap" class="ztzf-cesium"></div>
  <el-dialog class="command-dialog-mange" append-to-body v-model="isShowFlyMapView" title="查看" :width="pxToRem(1000)" :close-on-click-modal="false" :destroy-on-close="true">
    <div id="flyMap" class="command-cesium"></div>
  </el-dialog>
</template>
<script setup>
@@ -468,7 +468,7 @@
    background: center center no-repeat none !important;
    color: #8eb8ea !important;
  }
  :deep(.ztzf-select){
  :deep(.command-select){
    .el-select__selection {
      width: 200px;
    }
applications/drone-command/src/views/permissionManage/permissionDept.vue
@@ -1,9 +1,7 @@
<template>
  <basic-container>
    部门管理
  </basic-container>
    <PermissionDept />
</template>
<script setup>
import PermissionDept from './permissionDept/index.vue'
</script>
<style scoped lang="scss">
</style>
applications/drone-command/src/views/permissionManage/permissionDept/FormDiaLog.vue
New file
@@ -0,0 +1,305 @@
<template>
    <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
        <div class="detail-container" v-if="dialogReadonly">
            <div class="detail-title">机构详情</div>
            <el-row>
                <el-col :span="12">
                    <div class="label">部门名称</div>
                    <div class="val">{{ formData.deptName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">部门全称</div>
                    <div class="val">{{ formData.fullName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">上级部门</div>
                    <div class="val">{{ formData.parentName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">部门类型</div>
                    <div class="val">{{ getDictLabel(formData.deptCategory, dictObj.org_category) }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">部门性质</div>
                    <div class="val">{{ getDictLabel(formData.deptNature, dictObj.org_nature) }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">行政区划</div>
                    <div class="val">{{ formData.areaName || '-' }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">部署模式</div>
                    <div class="val">{{ deploymentModeLabel(formData.deploymentMode) }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">排序</div>
                    <div class="val">{{ formData.sort }}</div>
                </el-col>
                <el-col :span="24">
                    <div class="label">备注</div>
                    <div class="val">{{ formData.remark || '-' }}</div>
                </el-col>
            </el-row>
        </div>
        <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" label-width="100px">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="部门名称" prop="deptName">
                        <el-input class="command-data-cockpit-search-input" v-model="formData.deptName" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="部门全称" prop="fullName">
                        <el-input class="command-data-cockpit-search-input" v-model="formData.fullName" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="上级部门" prop="parentId">
                        <el-tree-select
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-tree-select-popper"
                            v-model="formData.parentId"
                            :data="deptTree"
                            :props="treeProps"
                            node-key="id"
                            check-strictly
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="部门类型" prop="deptCategory">
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper" v-model="formData.deptCategory" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.org_category" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="部门性质" prop="deptNature">
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper" v-model="formData.deptNature" placeholder="请选择" clearable>
                            <el-option v-for="item in dictObj.org_nature" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="排序" prop="sort">
                        <el-input-number v-model="formData.sort" :min="0" :controls="false" placeholder="请输入" />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="行政区划" prop="areaCode">
                        <el-cascader
                            class="command-data-cockpit-cascader"
                            popper-class="command-data-cockpit-cascader-popper"
                            v-model="formData.areaCode"
                            :props="areaProps"
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="部署模式" prop="deploymentMode">
                        <el-radio-group v-model="formData.deploymentMode">
                            <el-radio :label="0">统一部署</el-radio>
                            <el-radio :label="1">独立部署</el-radio>
                        </el-radio-group>
                    </el-form-item>
                </el-col>
                <el-col :span="24">
                    <el-form-item label="备注" prop="remark">
                        <el-input class="command-data-cockpit-search-input" v-model="formData.remark" type="textarea" :rows="3" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <template #footer>
            <el-button color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button color="#284FE3" v-if="!dialogReadonly" type="primary" :loading="submitting" :disabled="submitting" @click="handleSubmit">
                保存
            </el-button>
        </template>
    </el-dialog>
</template>
<script setup>
import { computed, inject, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { add, getDept, getDeptTree, update } from '@/api/system/dept'
import { getLazyTree } from '@/api/base/region'
import { fieldRules, getDictLabel } from '@ztzf/utils'
const initForm = () => ({
    deptName: '',
    fullName: '',
    parentId: '',
    parentName: '',
    deptCategory: '',
    deptNature: '',
    areaCode: '',
    areaName: '',
    deploymentMode: 0,
    sort: 0,
    remark: '',
})
const treeProps = {
    label: 'name',
    children: 'children',
}
const areaProps = {
    label: 'title',
    value: 'value',
    emitPath: false,
    checkStrictly: true,
    lazy: true,
    lazyLoad(node, resolve) {
        const level = node.level
        let list = []
        const callback = () => {
            resolve(
                (list || []).map(ele => ({
                    ...ele,
                    value: ele.value,
                    leaf: level >= 2,
                }))
            )
        }
        if (level === 0) {
            getLazyTree('000000000000').then(res => {
                list = res.data.data
                callback()
            })
        } else {
            getLazyTree(node.value).then(res => {
                list = res.data.data
                callback()
            })
        }
    },
}
const dictObj = inject('dictObj')
const deptTree = inject('deptTree')
const emit = defineEmits(['success'])
const formRef = ref(null)
const formData = ref(initForm())
const visible = ref(false)
const dialogMode = ref('add')
const submitting = ref(false)
const dialogReadonly = computed(() => dialogMode.value === 'view')
const dialogTitle = computed(() => {
    if (dialogMode.value === 'edit') return '编辑'
    if (dialogMode.value === 'view') return '查看'
    return '新增'
})
const rules = {
    deptName: fieldRules(true, 50),
    fullName: fieldRules(true, 50),
    parentId: fieldRules(false),
    deptCategory: fieldRules(true),
    deptNature: fieldRules(true),
    areaCode: fieldRules(true),
    deploymentMode: fieldRules(true),
    sort: fieldRules(true),
}
function deploymentModeLabel(value) {
    if (value === 1) return '独立部署'
    if (value === 0) return '统一部署'
    return value ?? '-'
}
function normalizeAreaCode(value) {
    if (!value) return ''
    if (Array.isArray(value)) {
        return value[value.length - 1]
    }
    if (typeof value === 'string' && value.includes(',')) {
        const codes = value.split(',')
        return codes[codes.length - 1]
    }
    return value
}
function getFullAreaCode(areaCode) {
    if (!areaCode) return ''
    const code = areaCode.toString()
    if (code.includes(',')) return code
    if (code.endsWith('0000000000')) {
        return code
    } else if (code.endsWith('00000000') && !code.endsWith('0000000000')) {
        const provinceCode = code.substring(0, 2) + '0000000000'
        return `${provinceCode},${code}`
    } else {
        const provinceCode = code.substring(0, 2) + '0000000000'
        const cityCode = code.substring(0, 4) + '00000000'
        return `${provinceCode},${cityCode},${code}`
    }
}
function handleCancel() {
    visible.value = false
}
async function handleSubmit() {
    const isValid = await formRef.value?.validate().catch(() => false)
    if (!isValid) return
    submitting.value = true
    try {
        const payload = {
            ...formData.value,
            areaCode: normalizeAreaCode(formData.value.areaCode),
        }
        if (dialogMode.value === 'edit') {
            await update(payload)
        } else {
            await add(payload)
        }
        ElMessage.success(dialogMode.value === 'add' ? '新增成功' : '更新成功')
        visible.value = false
        emit('success')
    } finally {
        submitting.value = false
    }
}
async function loadDetail(id) {
    if (!id) return
    const res = await getDept(id)
    const data = res?.data?.data ?? {}
    formData.value = {
        ...data,
        areaCode: getFullAreaCode(data.areaCode),
    }
}
function handleClosed() {
    formData.value = initForm()
}
async function open({ mode, row, parent } = {}) {
    dialogMode.value = mode || 'add'
    visible.value = true
    await getDeptTree().then(res => {
        if (deptTree) {
            deptTree.value = res?.data?.data ?? []
        }
    })
    if (dialogMode.value === 'add') {
        formData.value = initForm()
        if (parent?.id) {
            formData.value.parentId = parent.id
            formData.value.parentName = parent.deptName
        }
        return
    }
    formData.value = row
    await loadDetail(row?.id)
}
defineExpose({ open })
</script>
applications/drone-command/src/views/permissionManage/permissionDept/index.vue
New file
@@ -0,0 +1,224 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="部门名称" prop="deptName">
                <el-input
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.deptName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item label="部门全称" prop="fullName">
                <el-input
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.fullName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item class="history-search-actions">
                <el-button :icon="RefreshRight" @click="resetForm"></el-button>
                <el-button class="search-btn" :icon="Search" @click="handleSearch"></el-button>
            </el-form-item>
        </el-form>
        <div class="command-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="handleAdd">新增</el-button>
            <el-button :icon="Delete" color="#1A2652" type="primary" :disabled="!selectedIds.length" @click="handleDelete()">
                删除
            </el-button>
        </div>
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table
                    class="command-data-cockpit-table"
                    :data="list"
                    row-key="id"
                    lazy
                    :load="loadChildren"
                    :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
                    @selection-change="handleSelectionChange"
                >
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="deptName" show-overflow-tooltip label="部门名称" />
                    <el-table-column prop="fullName" show-overflow-tooltip label="部门全称" />
                    <el-table-column prop="deptCategory" show-overflow-tooltip width="120" label="部门类型">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.deptCategory, dictObj.org_category) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="deptNature" show-overflow-tooltip width="120" label="部门性质">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.deptNature, dictObj.org_nature) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="sort" show-overflow-tooltip width="80" label="排序" />
                    <el-table-column prop="bingId" show-overflow-tooltip width="100" label="组织id" />
                    <el-table-column prop="areaName" show-overflow-tooltip width="180" label="行政区划" />
                    <el-table-column prop="deploymentMode" show-overflow-tooltip width="120" label="部署模式">
                        <template v-slot="{ row }">
                            {{ deploymentModeLabel(row.deploymentMode) }}
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" class-name="operation-btns" width="220">
                        <template v-slot="{ row }">
                            <el-link @click="handleView(row)">查看</el-link>
                            <el-link @click="handleEdit(row)">编辑</el-link>
                            <el-link @click="handleAddChild(row)">新增子项</el-link>
                            <el-link type="danger" @click="handleDelete(row)">删除</el-link>
                        </template>
                    </el-table-column>
                </el-table>
            </div>
            <div class="command-table-pagination">
                <el-pagination
                    popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
                    :total="total"
                    @change="getList"
                />
            </div>
        </div>
        <FormDiaLog ref="dialogRef" @success="getList" />
    </basic-container>
</template>
<script setup>
import { Delete, Plus, RefreshRight, Search } from '@element-plus/icons-vue'
import { onMounted, provide, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getDictionary } from '@/api/system/dict'
import { getChildLazyTree, getDeptTree, getLazyList, remove } from '@/api/system/dept'
import { getDictLabel } from '@ztzf/utils'
import FormDiaLog from './FormDiaLog.vue'
const initSearchParams = () => ({
    deptName: '',
    fullName: '',
    current: 1,
    size: 10,
})
const searchParams = ref(initSearchParams())
const loading = ref(true)
const list = ref([])
const total = ref(0)
const selectedIds = ref([])
const queryParamsRef = ref(null)
const dialogRef = ref(null)
const dictObj = ref({
    org_category: [],
    org_nature: [],
})
const deptTree = ref([])
provide('dictObj', dictObj)
provide('deptTree', deptTree)
function deploymentModeLabel(value) {
    if (value === 1) return '独立部署'
    if (value === 0) return '统一部署'
    return value ?? '-'
}
async function getList() {
    loading.value = true
    try {
        const res = await getLazyList(0, searchParams.value)
        list.value = res?.data?.data ?? []
        total.value = list.value.length
    } finally {
        loading.value = false
    }
}
function handleSearch() {
    searchParams.value.current = 1
    getList()
}
function resetForm() {
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    getList()
}
function handleAdd() {
    dialogRef.value?.open({ mode: 'add' })
}
function handleAddChild(row) {
    dialogRef.value?.open({ mode: 'add', parent: row })
}
function handleView(row) {
    dialogRef.value?.open({ mode: 'view', row: { ...row } })
}
function handleEdit(row) {
    dialogRef.value?.open({ mode: 'edit', row: { ...row } })
}
async function handleDelete(row) {
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'command-page-view-message-box',
        confirmButtonClass: 'command-message-box-confirm',
        cancelButtonClass: 'command-message-box-cancel',
    })
    const ids = row ? row.id : selectedIds.value.join(',')
    await remove(ids)
    ElMessage.success('删除成功')
    selectedIds.value = []
    getList()
}
function handleSelectionChange(rows) {
    selectedIds.value = rows.map(item => item.id)
}
function getDictList() {
    Promise.all([getDictionary({ code: 'org_category' }), getDictionary({ code: 'org_nature' })]).then(
        ([categoryRes, natureRes]) => {
            dictObj.value = {
                org_category: categoryRes?.data?.data ?? [],
                org_nature: natureRes?.data?.data ?? [],
            }
        }
    )
}
function getDeptTreeList() {
    getDeptTree().then(res => {
        deptTree.value = res.data.data
    })
}
function loadChildren(row, treeNode, resolve) {
    getChildLazyTree({ parentId: row.id })
        .then(res => {
            resolve(res?.data?.data ?? [])
        })
        .catch(() => resolve([]))
}
onMounted(() => {
    getList()
    getDictList()
    getDeptTreeList()
})
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/permissionManage/permissionRole.vue
@@ -1,9 +1,7 @@
<template>
  <basic-container>
    角色管理
  </basic-container>
    <PermissionRole />
</template>
<script setup>
import PermissionRole from './permissionRole/index.vue'
</script>
<style scoped lang="scss">
</style>
applications/drone-command/src/views/permissionManage/permissionRole/FormDiaLog.vue
New file
@@ -0,0 +1,153 @@
<template>
    <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
        <div class="detail-container" v-if="dialogReadonly">
            <div class="detail-title">角色详情</div>
            <el-row>
                <el-col :span="12">
                    <div class="label">角色名称</div>
                    <div class="val">{{ formData.roleName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">角色别名</div>
                    <div class="val">{{ formData.roleAlias }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">上级角色</div>
                    <div class="val">{{ formData.parentName || '-' }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">角色排序</div>
                    <div class="val">{{ formData.sort }}</div>
                </el-col>
            </el-row>
        </div>
        <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" label-width="100px">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="角色名称" prop="roleName">
                        <el-input class="command-data-cockpit-search-input" v-model="formData.roleName" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="角色别名" prop="roleAlias">
                        <el-input class="command-data-cockpit-search-input" v-model="formData.roleAlias" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="上级角色" prop="parentId">
                        <el-tree-select
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-tree-select-popper"
                            v-model="formData.parentId"
                            :data="roleTree"
                            :props="treeProps"
                            node-key="id"
                            check-strictly
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="角色排序" prop="sort">
                        <el-input-number v-model="formData.sort" :min="0" :controls="false" placeholder="请输入" />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <template #footer>
            <el-button color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button color="#284FE3" v-if="!dialogReadonly" type="primary" :loading="submitting" :disabled="submitting" @click="handleSubmit">
                保存
            </el-button>
        </template>
    </el-dialog>
</template>
<script setup>
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { add, getRoleTreeById, update } from '@/api/system/role'
import { fieldRules } from '@ztzf/utils'
const initForm = () => ({
    roleName: '',
    roleAlias: '',
    parentId: '',
    parentName: '',
    sort: 0,
})
const treeProps = {
    label: 'title',
    children: 'children',
    value: 'id',
}
const emit = defineEmits(['success'])
const formRef = ref(null)
const formData = ref(initForm())
const visible = ref(false)
const dialogMode = ref('add')
const submitting = ref(false)
const dialogReadonly = computed(() => dialogMode.value === 'view')
const dialogTitle = computed(() => {
    if (dialogMode.value === 'edit') return '编辑'
    if (dialogMode.value === 'view') return '查看'
    return '新增'
})
const roleTree = ref([])
const rules = {
    roleName: fieldRules(true, 50),
    roleAlias: fieldRules(true, 50),
    parentId: fieldRules(false),
    sort: fieldRules(true),
}
function handleCancel() {
    visible.value = false
}
async function handleSubmit() {
    const isValid = await formRef.value?.validate().catch(() => false)
    if (!isValid) return
    submitting.value = true
    try {
        if (dialogMode.value === 'edit') {
            await update(formData.value)
        } else {
            await add(formData.value)
        }
        ElMessage.success(dialogMode.value === 'add' ? '新增成功' : '更新成功')
        visible.value = false
        emit('success')
    } finally {
        submitting.value = false
    }
}
async function loadRoleTree(roleId) {
    const res = await getRoleTreeById(roleId)
    roleTree.value = res?.data?.data ?? []
}
function handleClosed() {
    formData.value = initForm()
}
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    visible.value = true
    if (dialogMode.value === 'add') {
        formData.value = initForm()
        await loadRoleTree()
        return
    }
    formData.value = { ...row }
    await loadRoleTree(row?.id)
}
defineExpose({ open })
</script>
applications/drone-command/src/views/permissionManage/permissionRole/RolePermissionDialog.vue
New file
@@ -0,0 +1,170 @@
<template>
    <el-dialog
        v-model="visible"
        title="角色权限配置"
        width="620px"
        append-to-body
        :close-on-click-modal="false"
        @closed="handleClosed"
    >
        <el-tabs type="border-card">
            <el-tab-pane label="后台管理">
                <el-tree
                    ref="treeMenu"
                    :data="menuGrantList"
                    show-checkbox
                    node-key="id"
                    :default-checked-keys="menuTreeObj"
                    :props="treeProps"
                />
            </el-tab-pane>
            <el-tab-pane label="移动app">
                <el-tree
                    ref="treeMenuApp"
                    :data="menuGrantListApp"
                    show-checkbox
                    node-key="id"
                    :default-checked-keys="menuTreeObjApp"
                    :props="treeProps"
                />
            </el-tab-pane>
            <el-tab-pane label="新大屏菜单">
                <el-tree
                    ref="treeMenuWebNew"
                    :data="menuGrantListNew"
                    show-checkbox
                    node-key="id"
                    :default-checked-keys="menuTreeObjNew"
                    :props="treeProps"
                />
            </el-tab-pane>
            <el-tab-pane label="无人机管控">
                <el-tree
                    ref="droneControl"
                    :data="menuGrantListDroneControl"
                    show-checkbox
                    node-key="id"
                    :default-checked-keys="droneControlMenuTreeObj"
                    :props="treeProps"
                />
            </el-tab-pane>
            <el-tab-pane label="任务工单">
                <el-tree
                    ref="workRef"
                    :data="menuGrantListWord"
                    show-checkbox
                    node-key="id"
                    :default-checked-keys="menuTreeObjWork"
                    :props="treeProps"
                />
            </el-tab-pane>
            <el-tab-pane label="大屏菜单">
                <el-tree
                    ref="treeMenuWebOld"
                    :data="menuGrantListOld"
                    show-checkbox
                    node-key="id"
                    :default-checked-keys="menuTreeObjOld"
                    :props="treeProps"
                />
            </el-tab-pane>
        </el-tabs>
        <template #footer>
            <el-button @click="visible = false">取 消</el-button>
            <el-button type="primary" @click="submit">确 定</el-button>
        </template>
    </el-dialog>
</template>
<script setup>
import { ref } from 'vue'
import { grant, grantTree, getRole } from '@/api/system/role'
const visible = ref(false)
const roleId = ref('')
const treeProps = {
    label: 'title',
    value: 'key',
}
const menuGrantList = ref([])
const menuGrantListApp = ref([])
const menuGrantListOld = ref([])
const menuGrantListNew = ref([])
const menuGrantListDroneControl = ref([])
const menuGrantListWord = ref([])
const menuTreeObj = ref([])
const menuTreeObjApp = ref([])
const menuTreeObjOld = ref([])
const menuTreeObjNew = ref([])
const droneControlMenuTreeObj = ref([])
const menuTreeObjWork = ref([])
const treeMenu = ref(null)
const treeMenuApp = ref(null)
const treeMenuWebOld = ref(null)
const treeMenuWebNew = ref(null)
const workRef = ref(null)
const droneControl = ref(null)
const emit = defineEmits(['success'])
function resetState() {
    menuGrantList.value = []
    menuGrantListApp.value = []
    menuGrantListOld.value = []
    menuGrantListNew.value = []
    menuGrantListDroneControl.value = []
    menuGrantListWord.value = []
    menuTreeObj.value = []
    menuTreeObjApp.value = []
    menuTreeObjOld.value = []
    menuTreeObjNew.value = []
    droneControlMenuTreeObj.value = []
    menuTreeObjWork.value = []
}
async function loadGrantTree(sysType, setter, keysSetter) {
    const grantRes = await grantTree({ sysType })
    setter.value = grantRes?.data?.data?.menu ?? []
    const roleRes = await getRole(roleId.value)
    keysSetter.value = roleRes?.data?.data?.menu ?? []
}
async function open({ roleId: id } = {}) {
    roleId.value = id || ''
    visible.value = true
    resetState()
    await Promise.all([
        loadGrantTree(1, menuGrantList, menuTreeObj),
        loadGrantTree(3, menuGrantListApp, menuTreeObjApp),
        loadGrantTree(4, menuGrantListNew, menuTreeObjNew),
        loadGrantTree(5, menuGrantListDroneControl, droneControlMenuTreeObj),
        loadGrantTree(6, menuGrantListWord, menuTreeObjWork),
        loadGrantTree(2, menuGrantListOld, menuTreeObjOld),
    ])
}
function submit() {
    const menuList = treeMenu.value?.getCheckedKeys?.() ?? []
    const menuListApp = treeMenuApp.value?.getCheckedKeys?.() ?? []
    const menuListWebNew = treeMenuWebNew.value?.getCheckedKeys?.() ?? []
    const menuListWebWork = workRef.value?.getCheckedKeys?.() ?? []
    const menuListDroneControl = droneControl.value?.getCheckedKeys?.() ?? []
    const menuListWebOld = treeMenuWebOld.value?.getCheckedKeys?.() ?? []
    grant(
        [roleId.value],
        [...menuList, ...menuListApp, ...menuListWebNew, ...menuListDroneControl, ...menuListWebOld, ...menuListWebWork],
        [],
        []
    ).then(() => {
        visible.value = false
        emit('success')
    })
}
function handleClosed() {
    resetState()
}
defineExpose({ open })
</script>
applications/drone-command/src/views/permissionManage/permissionRole/index.vue
New file
@@ -0,0 +1,189 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="角色名称" prop="roleName">
                <el-input
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.roleName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item label="角色别名" prop="roleAlias">
                <el-input
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.roleAlias"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item class="history-search-actions">
                <el-button :icon="RefreshRight" @click="resetForm"></el-button>
                <el-button class="search-btn" :icon="Search" @click="handleSearch"></el-button>
            </el-form-item>
        </el-form>
        <div class="command-table-toolbar">
            <el-button :icon="Plus" color="#284FE3" type="primary" @click="handleAdd">新增</el-button>
            <el-button :icon="Delete" color="#1A2652" type="primary" :disabled="!selectedIds.length" @click="handleDelete()">
                删除
            </el-button>
            <el-button
                color="#1A2652"
                type="primary"
                :disabled="selectedIds.length !== 1"
                v-if="userInfo.role_name?.includes('admin')"
                @click="handleRole"
            >
                权限设置
            </el-button>
        </div>
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table
                    class="command-data-cockpit-table"
                    :data="list"
                    row-key="id"
                    :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
                    @selection-change="handleSelectionChange"
                >
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="roleName" show-overflow-tooltip label="角色名称" />
                    <el-table-column prop="roleAlias" show-overflow-tooltip label="角色别名" />
                    <el-table-column prop="sort" show-overflow-tooltip label="角色排序" />
                    <el-table-column label="操作" class-name="operation-btns">
                        <template v-slot="{ row }">
                            <el-link @click="handleView(row)">查看</el-link>
                            <el-link @click="handleEdit(row)">编辑</el-link>
                            <el-link v-if="userInfo.role_name?.includes('admin')" @click="handleRole(row)">权限设置</el-link>
                            <el-link type="danger" @click="handleDelete(row)">删除</el-link>
                        </template>
                    </el-table-column>
                </el-table>
            </div>
            <div class="command-table-pagination">
                <el-pagination
                    popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
                    :total="total"
                    @change="loadList"
                />
            </div>
        </div>
        <FormDiaLog ref="dialogRef" @success="loadList" />
        <RolePermissionDialog ref="roleDialogRef" @success="loadList" />
    </basic-container>
</template>
<script setup>
import { Delete, Plus, RefreshRight, Search } from '@element-plus/icons-vue'
import { computed, onMounted, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getList as getRolePage, remove } from '@/api/system/role'
import { useStore } from 'vuex'
import FormDiaLog from './FormDiaLog.vue'
import RolePermissionDialog from './RolePermissionDialog.vue'
const initSearchParams = () => ({
    roleName: '',
    roleAlias: '',
    current: 1,
    size: 10,
})
const searchParams = ref(initSearchParams())
const loading = ref(true)
const list = ref([])
const total = ref(0)
const selectedIds = ref([])
const selectedRows = ref([])
const queryParamsRef = ref(null)
const dialogRef = ref(null)
const roleDialogRef = ref(null)
const store = useStore()
const userInfo = computed(() => store.getters.userInfo || {})
async function loadList() {
    loading.value = true
    try {
        const res = await getRolePage(searchParams.value.current, searchParams.value.size, searchParams.value)
        const records = res?.data?.data?.records ?? res?.data?.data ?? []
        list.value = records
        total.value = res?.data?.data?.total ?? res?.data?.total ?? records.length
    } finally {
        loading.value = false
    }
}
function handleSearch() {
    searchParams.value.current = 1
    loadList()
}
function resetForm() {
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    loadList()
}
function handleAdd() {
    dialogRef.value?.open({ mode: 'add' })
}
function handleView(row) {
    dialogRef.value?.open({ mode: 'view', row: { ...row } })
}
function handleEdit(row) {
    dialogRef.value?.open({ mode: 'edit', row: { ...row } })
}
function handleRole(row) {
    const roleId = row?.id ?? selectedRows.value[0]?.id
    if (!roleId) {
        ElMessage.warning('只能选择一条数据')
        return
    }
    if (selectedRows.value.length !== 1 && !row) {
        ElMessage.warning('只能选择一条数据')
        return
    }
    roleDialogRef.value?.open({ roleId })
}
async function handleDelete(row) {
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'command-page-view-message-box',
        confirmButtonClass: 'command-message-box-confirm',
        cancelButtonClass: 'command-message-box-cancel',
    })
    const ids = row ? row.id : selectedIds.value.join(',')
    await remove(ids)
    ElMessage.success('删除成功')
    selectedIds.value = []
    loadList()
}
function handleSelectionChange(rows) {
    selectedIds.value = rows.map(item => item.id)
    selectedRows.value = rows
}
onMounted(() => {
    loadList()
})
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/permissionManage/permissionUser.vue
@@ -1,9 +1,7 @@
<template>
  <basic-container>
    用户管理
  </basic-container>
    <PermissionUser />
</template>
<script setup>
import PermissionUser from './permissionUser/index.vue'
</script>
<style scoped lang="scss">
</style>
applications/drone-command/src/views/permissionManage/permissionUser/FormDiaLog.vue
New file
@@ -0,0 +1,421 @@
<template>
    <el-dialog class="command-page-view-dialog" v-model="visible" :title="dialogTitle" @closed="handleClosed" destroy-on-close>
        <div class="detail-container" v-if="dialogReadonly">
            <div class="detail-title">用户详情</div>
            <el-row>
                <el-col :span="12">
                    <div class="label">登录账号</div>
                    <div class="val">{{ formData.account }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">用户姓名</div>
                    <div class="val">{{ formData.realName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">手机号</div>
                    <div class="val">{{ formData.phone }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">电子邮箱</div>
                    <div class="val">{{ formData.email }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">用户性别</div>
                    <div class="val">{{ getSexLabel(formData.sex) }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">用户生日</div>
                    <div class="val">{{ formData.birthday }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">所属角色</div>
                    <div class="val">{{ formData.roleName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">所属部门</div>
                    <div class="val">{{ formData.deptName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">用户编号</div>
                    <div class="val">{{ formData.code }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">账号状态</div>
                    <div class="val">{{ formData.statusName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">到期时间</div>
                    <div class="val">{{ formData.expireTime }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">次数限制</div>
                    <div class="val">{{ formData.maxLoginNum }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">飞行开始时间</div>
                    <div class="val">{{ formData.flightStartTime }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">飞行结束时间</div>
                    <div class="val">{{ formData.flightEndTime }}</div>
                </el-col>
            </el-row>
        </div>
        <el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" label-width="110px">
            <el-divider content-position="left">基础信息</el-divider>
            <el-row :gutter="16">
                <el-col :span="12">
                    <el-form-item label="登录账号" prop="account">
                        <el-input class="command-data-cockpit-search-input" v-model="formData.account" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12" v-if="dialogMode === 'add'">
                    <el-form-item label="密码" prop="password">
                        <el-input
                            class="command-data-cockpit-search-input"
                            v-model="formData.password"
                            type="password"
                            maxlength="50"
                            placeholder="请输入"
                            clearable
                            show-password
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12" v-if="dialogMode === 'add'">
                    <el-form-item label="确认密码" prop="password2">
                        <el-input
                            class="command-data-cockpit-search-input"
                            v-model="formData.password2"
                            type="password"
                            maxlength="50"
                            placeholder="请输入"
                            clearable
                            show-password
                        />
                    </el-form-item>
                </el-col>
            </el-row>
            <el-divider content-position="left">详细信息</el-divider>
            <el-row :gutter="16">
                <el-col :span="12">
                    <el-form-item label="用户姓名" prop="realName">
                        <el-input class="command-data-cockpit-search-input" v-model="formData.realName" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="手机号" prop="phone">
                        <el-input class="command-data-cockpit-search-input" v-model="formData.phone" maxlength="20" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="电子邮箱" prop="email">
                        <el-input class="command-data-cockpit-search-input" v-model="formData.email" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="用户性别" prop="sex">
                        <el-select class="command-data-cockpit-select" popper-class="command-data-cockpit-select-popper" v-model="formData.sex" placeholder="请选择" clearable>
                            <el-option label="男" :value="1" />
                            <el-option label="女" :value="2" />
                            <el-option label="未知" :value="3" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="用户生日" prop="birthday">
                        <el-date-picker
                            class="command-data-cockpit-date-picker"
                            popper-class="command-data-cockpit-date-picker-popper"
                            v-model="formData.birthday"
                            type="datetime"
                            placeholder="请选择"
                            format="YYYY-MM-DD HH:mm:ss"
                            value-format="YYYY-MM-DD HH:mm:ss"
                            clearable
                        />
                    </el-form-item>
                </el-col>
            </el-row>
            <el-divider content-position="left">职责信息</el-divider>
            <el-row :gutter="16">
                <el-col :span="12">
                    <el-form-item label="用户编号" prop="code">
                        <el-input class="command-data-cockpit-search-input" v-model="formData.code" maxlength="50" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="所属角色" prop="roleId">
                        <el-tree-select
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-tree-select-popper"
                            v-model="formData.roleId"
                            :data="roleTree"
                            :props="treeProps"
                            node-key="id"
                            check-strictly
                            clearable
                            placeholder="请选择"
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="所属部门" prop="deptId">
                        <el-tree-select
                            class="command-data-cockpit-select"
                            popper-class="command-data-cockpit-tree-select-popper"
                            v-model="formData.deptId"
                            :data="deptTree"
                            :props="deptTreeSelectProps"
                            node-key="id"
                            check-strictly
                            clearable
                            placeholder="请选择"
                        />
                    </el-form-item>
                </el-col>
            </el-row>
            <el-divider content-position="left">登录限制</el-divider>
            <el-row :gutter="16">
                <el-col :span="12">
                    <el-form-item label="到期时间" prop="expireTime">
                        <el-date-picker
                            class="command-data-cockpit-date-picker"
                            popper-class="command-data-cockpit-date-picker-popper"
                            v-model="formData.expireTime"
                            type="datetime"
                            placeholder="请选择"
                            format="YYYY-MM-DD HH:mm:ss"
                            value-format="YYYY-MM-DD HH:mm:ss"
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="次数限制" prop="maxLoginNum">
                        <el-input class="command-data-cockpit-search-input" v-model="formData.maxLoginNum" placeholder="请输入" clearable />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="飞行开始时间" prop="flightStartTime">
                        <el-time-picker
                            class="command-data-cockpit-date-picker"
                            popper-class="command-data-cockpit-date-picker-popper"
                            v-model="formData.flightStartTime"
                            placeholder="请选择"
                            format="HH:mm"
                            value-format="HH:mm"
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="飞行结束时间" prop="flightEndTime">
                        <el-time-picker
                            class="command-data-cockpit-date-picker"
                            popper-class="command-data-cockpit-date-picker-popper"
                            v-model="formData.flightEndTime"
                            placeholder="请选择"
                            format="HH:mm"
                            value-format="HH:mm"
                            clearable
                        />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <template #footer>
            <el-button color="#2B2B4C" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button color="#284FE3" v-if="!dialogReadonly" type="primary" :loading="submitting" :disabled="submitting" @click="handleSubmit">
                保存
            </el-button>
        </template>
    </el-dialog>
</template>
<script setup>
import { computed, onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { add, getUser, update } from '@/api/system/user'
import { getDeptTree } from '@/api/system/dept'
import { getRoleTree } from '@/api/system/role'
import func from '@/utils/func'
const initForm = () => ({
    account: '',
    password: '',
    password2: '',
    realName: '',
    phone: '',
    email: '',
    sex: null,
    birthday: '',
    code: '',
    roleId: '',
    deptId: '',
    expireTime: '',
    maxLoginNum: '',
    flightStartTime: '',
    flightEndTime: '',
})
const treeProps = {
    label: 'title',
    children: 'children',
}
const deptTreeSelectProps = {
    label: 'name',
    value: 'id',
    children: 'children',
}
const emit = defineEmits(['success'])
const formRef = ref(null)
const formData = ref(initForm())
const visible = ref(false)
const dialogMode = ref('add')
const submitting = ref(false)
const dialogReadonly = computed(() => dialogMode.value === 'view')
const dialogTitle = computed(() => {
    if (dialogMode.value === 'edit') return '编辑'
    if (dialogMode.value === 'view') return '查看'
    return '新增'
})
const roleTree = ref([])
const deptTree = ref([])
const validatePass = (rule, value, callback) => {
    if (value === '') {
        callback(new Error('请输入密码'))
    } else {
        callback()
    }
}
const validatePass2 = (rule, value, callback) => {
    if (value === '') {
        callback(new Error('请再次输入密码'))
    } else if (value !== formData.value.password) {
        callback(new Error('两次输入密码不一致'))
    } else {
        callback()
    }
}
const rules = {
    account: [{ required: true, message: '请输入登录账号', trigger: 'blur' }],
    password: [{ required: true, validator: validatePass, trigger: 'blur' }],
    password2: [{ required: true, validator: validatePass2, trigger: 'blur' }],
    realName: [
        { required: true, message: '请输入用户姓名', trigger: 'blur' },
        { min: 2, max: 50, message: '姓名长度在 2 到 50 个字符', trigger: 'blur' },
    ],
    roleId: [{ required: true, message: '请选择所属角色', trigger: 'change' }],
    deptId: [{ required: true, message: '请选择所属部门', trigger: 'change' }],
}
function getSexLabel(sex) {
    const map = { 1: '男', 2: '女', 3: '未知' }
    return map[sex] || ''
}
async function getRoleTreeData() {
    const res = await getRoleTree('000000')
    roleTree.value = res.data.data
}
async function getDeptTreeData() {
    const res = await getDeptTree('000000')
    deptTree.value = res.data.data
}
function handleCancel() {
    visible.value = false
}
function validateFlightTime() {
    if (formData.value.flightStartTime && formData.value.flightEndTime) {
        const [startHours, startMinutes] = formData.value.flightStartTime.split(':').map(Number)
        const [endHours, endMinutes] = formData.value.flightEndTime.split(':').map(Number)
        const startTotalMinutes = startHours * 60 + startMinutes
        const endTotalMinutes = endHours * 60 + endMinutes
        if (endTotalMinutes < startTotalMinutes) {
            ElMessage.error('飞行结束时间不能小于飞行开始时间')
            return false
        }
    }
    return true
}
async function handleSubmit() {
    const isValid = await formRef.value?.validate().catch(() => false)
    if (!isValid) return
    if (!validateFlightTime()) return
    submitting.value = true
    try {
        const submitData = { ...formData.value }
        submitData.roleId = func.toStr(submitData.roleId)
        submitData.name = submitData.realName
        if (dialogMode.value === 'add') {
            await add(submitData)
        } else {
            await update(submitData)
        }
        ElMessage.success(dialogMode.value === 'add' ? '新增成功' : '更新成功')
        visible.value = false
        emit('success')
    } finally {
        submitting.value = false
    }
}
async function loadDetail() {
    if (!formData.value.id) return
    const res = await getUser(formData.value.id)
    const data = res?.data?.data ?? {}
    if (data.roleId) {
        data.roleId = func.split(data.roleId)[0] || data.roleId
    }
    formData.value = data
}
function handleClosed() {
    formData.value = initForm()
}
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    visible.value = true
    if (dialogMode.value === 'add') {
        formData.value = initForm()
        return
    }
    formData.value = { id: row.id }
    await loadDetail()
}
onMounted(() => {
    getRoleTreeData()
    getDeptTreeData()
})
defineExpose({ open })
</script>
<style scoped lang="scss">
:deep(.el-select) {
    width: 100%;
}
:deep(.el-date-editor) {
    width: 100%;
}
:deep(.el-tree-select) {
    width: 100%;
}
</style>
applications/drone-command/src/views/permissionManage/permissionUser/index.vue
New file
@@ -0,0 +1,202 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="登录账号" prop="account">
                <el-input
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.account"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item label="用户姓名" prop="realName">
                <el-input
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.realName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item label="所属部门" prop="deptId">
                <el-tree-select
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-tree-select-popper"
                    v-model="searchParams.deptId"
                    :data="deptTree"
                    :props="treeProps"
                    node-key="id"
                    check-strictly
                    clearable
                    @change="handleSearch"
                />
            </el-form-item>
            <el-form-item class="history-search-actions">
                <el-button :icon="RefreshRight" @click="resetForm"></el-button>
                <el-button class="search-btn" :icon="Search" @click="handleSearch"></el-button>
            </el-form-item>
        </el-form>
        <div class="command-table-toolbar">
            <el-button v-if="permission.user_add" :icon="Plus" color="#284FE3" type="primary" @click="handleAdd">
                新增
            </el-button>
            <el-button
                v-if="permission.user_delete"
                :icon="Delete"
                color="#1A2652"
                type="primary"
                :disabled="!selectedIds.length"
                @click="handleDelete()"
            >
                删除
            </el-button>
        </div>
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="account" show-overflow-tooltip label="登录账号" />
                    <el-table-column prop="realName" show-overflow-tooltip label="用户姓名" />
                    <el-table-column prop="roleName" show-overflow-tooltip label="所属角色" />
                    <el-table-column prop="deptName" show-overflow-tooltip label="所属部门" />
                    <el-table-column prop="phone" show-overflow-tooltip width="120" label="手机号" />
                    <el-table-column label="操作" class-name="operation-btns" width="180">
                        <template v-slot="{ row }">
                            <el-link v-if="permission.user_view" @click="handleView(row)">查看</el-link>
                            <el-link v-if="permission.user_edit" @click="handleEdit(row)">编辑</el-link>
                            <el-link v-if="permission.user_delete" type="danger" @click="handleDelete(row)">删除</el-link>
                        </template>
                    </el-table-column>
                </el-table>
            </div>
            <div class="command-table-pagination">
                <el-pagination
                    popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
                    :total="total"
                    @change="getList"
                />
            </div>
        </div>
        <FormDiaLog ref="dialogRef" @success="getList" />
    </basic-container>
</template>
<script setup>
import { Delete, Plus, RefreshRight, Search } from '@element-plus/icons-vue'
import { computed, onMounted, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getDeptTree } from '@/api/system/dept'
import { getList as getUserList, remove } from '@/api/system/user'
import { useStore } from 'vuex'
import FormDiaLog from './FormDiaLog.vue'
const initSearchParams = () => ({
    account: '',
    realName: '',
    deptId: '',
    current: 1,
    size: 10,
})
const searchParams = ref(initSearchParams())
const loading = ref(true)
const list = ref([])
const total = ref(0)
const selectedIds = ref([])
const queryParamsRef = ref(null)
const dialogRef = ref(null)
const deptTree = ref([])
const treeProps = {
    label: 'name',
    children: 'children',
}
const store = useStore()
const permission = computed(() => store.getters.permission || {})
async function getList() {
    loading.value = true
    try {
        const res = await getUserList(
            searchParams.value.current,
            searchParams.value.size,
            {
                account: searchParams.value.account,
                realName: searchParams.value.realName,
            },
            searchParams.value.deptId
        )
        list.value = res?.data?.data?.records ?? []
        total.value = res?.data?.data?.total ?? 0
    } finally {
        loading.value = false
    }
}
function handleSearch() {
    searchParams.value.current = 1
    getList()
}
function resetForm() {
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    searchParams.value.deptId = ''
    getList()
}
function handleAdd() {
    dialogRef.value?.open({ mode: 'add' })
}
function handleView(row) {
    dialogRef.value?.open({ mode: 'view', row: { ...row } })
}
function handleEdit(row) {
    dialogRef.value?.open({ mode: 'edit', row: { ...row } })
}
async function handleDelete(row) {
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'command-page-view-message-box',
        confirmButtonClass: 'command-message-box-confirm',
        cancelButtonClass: 'command-message-box-cancel',
    })
    const ids = row ? row.id : selectedIds.value.join(',')
    await remove(ids)
    ElMessage.success('删除成功')
    selectedIds.value = []
    getList()
}
function handleSelectionChange(rows) {
    selectedIds.value = rows.map(item => item.id)
}
function getDeptTreeList() {
    getDeptTree().then(res => {
        deptTree.value = res?.data?.data ?? []
    })
}
onMounted(() => {
    getList()
    getDeptTreeList()
})
</script>
<style scoped lang="scss"></style>
applications/drone-command/src/views/recordManage/alarmRecords/fwDroneAlarmRecord.js
@@ -5,7 +5,7 @@
    return request({
        url: `/drone-fw/record/fwDroneAlarmRecord/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
@@ -37,10 +37,11 @@
}
//导出
export const exportFwDroneAlarmRecordApi = () => {
export const exportFwDroneAlarmRecordApi = (params) => {
    return request({
        url: `/drone-fw/record/fwDroneAlarmRecord/export-fwDroneAlarmRecord`,
        method: 'get',
        responseType: 'blob',
        params,
    })
}
applications/drone-command/src/views/recordManage/alarmRecords/index.vue
@@ -1,9 +1,9 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="无人机名称" prop="droneName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.droneName"
                    placeholder="请输入"
                    clearable
@@ -12,7 +12,7 @@
            </el-form-item>
            <el-form-item label="区域" prop="areaName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.areaName"
                    placeholder="请输入"
                    clearable
@@ -21,8 +21,8 @@
            </el-form-item>
            <el-form-item label="设备类型" prop="droneType">
                <el-select
                    class="ztzf-data-cockpit-select"
                    popper-class="ztzf-data-cockpit-select-popper"
                    class="command-data-cockpit-select"
                    popper-class="command-data-cockpit-select-popper"
                    v-model="searchParams.droneType"
                    placeholder="请选择"
                    clearable
@@ -36,10 +36,10 @@
                    />
                </el-select>
            </el-form-item>
            <el-form-item label="时间">
            <el-form-item label="告警时间">
                <el-date-picker
                    popper-class="ztzf-data-cockpit-date-picker-popper"
                    class="ztzf-data-cockpit-date-picker"
                    popper-class="command-data-cockpit-date-picker-popper"
                    class="command-data-cockpit-date-picker"
                    v-model="dateRange"
                    type="daterange"
                    range-separator="至"
@@ -55,13 +55,16 @@
            </el-form-item>
        </el-form>
        <div class="ztzf-table-toolbar">
            <el-button :icon="Download" color="#1A2652" type="primary" @click="exportFile" :loading="exportLoading">导出</el-button>
        <div class="command-table-toolbar">
            <el-button :icon="Download" color="#1A2652" type="primary" @click="exportFile" :loading="exportLoading">
                导出
            </el-button>
        </div>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="droneName" show-overflow-tooltip width="140" label="无人机名称" />
                    <el-table-column prop="droneSerialNo" show-overflow-tooltip label="无人机序列号" />
@@ -83,9 +86,9 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
            <div class="command-table-pagination">
                <el-pagination
                    popper-class="ztzf-data-cockpit-select-popper"
                    popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
@@ -148,6 +151,12 @@
    }
}
const selectedIds = ref([]) // 勾选的ID列表
// 勾选值设置
function handleSelectionChange(rows) {
    selectedIds.value = rows.map(item => item.id)
}
// 查询
function handleSearch() {
    searchParams.value.current = 1
@@ -164,7 +173,7 @@
function exportFile() {
    exportLoading.value = true
    exportFwDroneAlarmRecordApi()
    exportFwDroneAlarmRecordApi({ ids: selectedIds.value.join(',') })
        .then(res => {
            blobDownload(res)
        })
applications/drone-command/src/views/recordManage/historyTracks/TrajectoryDiaLog.vue
@@ -1,9 +1,15 @@
<template>
    <el-dialog class="ztzf-page-map-view-dialog" v-model="visible" :show-close="false"
        :close-on-click-modal="false" @closed="handleClosed" destroy-on-close>
    <el-dialog
        class="command-page-map-view-dialog"
        v-model="visible"
        :show-close="false"
        :close-on-click-modal="false"
        @closed="handleClosed"
        destroy-on-close
    >
        <div class="dialog-container">
            <div class="left-container">
                <div class="leftMap ztzf-cesium" id="leftMapContainer"></div>
                <div class="leftMap command-cesium" id="leftMapContainer"></div>
            </div>
            <div class="right-container">
                <div class="header">{{ dialogTitle }}</div>
@@ -15,7 +21,7 @@
                            <div class="val">{{ formData.droneName }}</div>
                        </el-col>
                        <el-col :span="24">
                            <div class="label">序列号</div>
                            <div class="label">无人机序列号</div>
                            <div class="val">{{ formData.serialNo }}</div>
                        </el-col>
                        <el-col :span="24">
@@ -32,9 +38,9 @@
                        </el-col>
                    </el-row>
                    <div class="detail-title">轨迹点</div>
                    <div class="ztzf-table-container">
                        <div class="ztzf-table-content">
                            <el-table class="ztzf-data-cockpit-table" :data="list">
                    <div class="command-table-container">
                        <div class="command-table-content">
                            <el-table class="command-data-cockpit-table" :data="list">
                                <el-table-column type="index" width="60" label="序号" />
                                <el-table-column label="经纬度">
                                    <template #default="{ row }">{{ row.longitude }},{{ row.latitude }}</template>
@@ -59,6 +65,7 @@
import { PublicCesium } from '@/utils/cesium/publicCesium'
import * as Cesium from 'cesium'
import { flyVisual } from '@/utils/cesium/mapUtil'
import { ArrowLineMaterialProperty } from '@/utils/cesium/Material'
const initForm = () => ({
    droneName: '',
@@ -84,12 +91,12 @@
})
// 关闭弹框
function handleCancel () {
function handleCancel() {
    visible.value = false
}
// 加载详情
async function loadDetail () {
async function loadDetail() {
    if (!formData.value.id) return
    const res = await fwDroneFlightRecordDetailApi({ id: formData.value.id })
    const res1 = await fwDroneFlightRecordDetailPageApi({ flightRecordId: formData.value.id })
@@ -98,28 +105,35 @@
    formData.value = res?.data?.data ?? {}
}
let arrowLineMaterialProperty = new ArrowLineMaterialProperty({
    color: new Cesium.Color(128 / 255, 215 / 255, 255 / 255, 1),
    directionColor: new Cesium.Color(1, 1, 1, 1),
    outlineColor: new Cesium.Color(1, 1, 1, 1),
    outlineWidth: 0,
    speed: 5,
})
// 角度转c3
function degreesToC3 ({ longitude, latitude, height = 0 }) {
function degreesToC3({ longitude, latitude, height = 0 }) {
    return Cesium.Cartesian3.fromDegrees(longitude, latitude, height)
}
// 渲染航线
function drawFlightPath () {
function drawFlightPath() {
    const positions = list.value.map(p => degreesToC3(p))
    droneLineEntity && geometricSource.entities.remove(droneLineEntity)
    const color = Cesium.Color.fromCssColorString('#00D690')
    droneLineEntity = geometricSource.entities.add({
        polyline: {
            width: 4,
            positions: positions,
            width: 3,
            material: new Cesium.PolylineOutlineMaterialProperty({ color }),
            material: arrowLineMaterialProperty,
            clampToGround: false,
        },
    })
    viewer.flyTo(droneLineEntity)
    viewer.flyTo(droneLineEntity, { duration: 0 })
}
// 关闭后重置
function handleClosed () {
function handleClosed() {
    formData.value = initForm()
    list.value = []
    viewer = null
@@ -128,7 +142,7 @@
}
let viewer, geometricSource, droneLineEntity
function initMap () {
function initMap() {
    if (viewer) return
    const publicCesiumInstance = new PublicCesium({
        dom: 'leftMapContainer',
@@ -143,7 +157,7 @@
}
// 打开弹框
async function open ({ mode, row } = {}) {
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    visible.value = true
    await nextTick()
applications/drone-command/src/views/recordManage/historyTracks/flyTrajectory.js
@@ -14,7 +14,7 @@
    return request({
        url: `/drone-fw/record/fwDroneFlightRecordDetail/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
applications/drone-command/src/views/recordManage/historyTracks/fwDroneFlightRecord.js
@@ -5,7 +5,7 @@
    return request({
        url: `/drone-fw/record/fwDroneFlightRecord/page`,
        method: 'get',
        params,
        params: { descs: 'update_time', ...params },
    })
}
applications/drone-command/src/views/recordManage/historyTracks/index.vue
@@ -1,9 +1,9 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="ztzf-page-history-search">
        <el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
            <el-form-item label="无人机名称" prop="droneName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.droneName"
                    placeholder="请输入"
                    clearable
@@ -12,7 +12,7 @@
            </el-form-item>
            <el-form-item label="区域" prop="areaName">
                <el-input
                    class="ztzf-data-cockpit-search-input"
                    class="command-data-cockpit-search-input"
                    v-model="searchParams.areaName"
                    placeholder="请输入"
                    clearable
@@ -21,8 +21,8 @@
            </el-form-item>
            <el-form-item label="告警时间">
                <el-date-picker
                    popper-class="ztzf-data-cockpit-date-picker-popper"
                    class="ztzf-data-cockpit-date-picker"
                    popper-class="command-data-cockpit-date-picker-popper"
                    class="command-data-cockpit-date-picker"
                    v-model="dateRange"
                    type="daterange"
                    range-separator="至"
@@ -38,16 +38,16 @@
            </el-form-item>
        </el-form>
        <div class="ztzf-table-toolbar">
        <div class="command-table-toolbar">
            <el-button :icon="Download" color="#1A2652" type="primary" @click="exportFile" :loading="exportLoading">导出</el-button>
        </div>
        <div class="ztzf-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="ztzf-table-content ztzf-table-content-bg">
                <el-table class="ztzf-data-cockpit-table" :data="list">
        <div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="command-table-content command-table-content-bg">
                <el-table class="command-data-cockpit-table" :data="list">
                    <el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
                    <el-table-column prop="droneName" show-overflow-tooltip width="140" label="无人机名称" />
                    <el-table-column prop="serialNo" show-overflow-tooltip label="序列号" />
                    <el-table-column prop="serialNo" show-overflow-tooltip label="无人机序列号" />
                    <el-table-column prop="alarmTime" show-overflow-tooltip label="告警时间" />
                    <el-table-column prop="areaName" show-overflow-tooltip label="区域" />
                    <el-table-column prop="triggerType" show-overflow-tooltip label="触发原因" />
@@ -64,9 +64,9 @@
                </el-table>
            </div>
            <div class="ztzf-table-pagination">
            <div class="command-table-pagination">
                <el-pagination
                    popper-class="ztzf-data-cockpit-select-popper"
                    popper-class="command-data-cockpit-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
applications/drone-command/src/views/recordManage/index.vue
File was deleted
applications/drone-command/src/views/resource/components/spotDetails.vue
@@ -1,6 +1,6 @@
<template>
  <el-dialog
    class="spotDialog ztzf-dialog-mange"
    class="spotDialog command-dialog-mange"
    :title="props.title"
    v-model="uploadPatchDialog"
    width="78%"
@@ -134,7 +134,7 @@
          <img @click="handleCancel" src="@/assets/images/home/territory/cancelbtn.png" alt="" />
        </div>
        <!-- 地图 -->
        <div id="spotMap" v-loading="loading"  element-loading-text="加载中..." element-loading-background="rgba(0, 0, 0, 0.7)" class="ztzf-cesium" v-show="uploadPatchDialog"></div>
        <div id="spotMap" v-loading="loading"  element-loading-text="加载中..." element-loading-background="rgba(0, 0, 0, 0.7)" class="command-cesium" v-show="uploadPatchDialog"></div>
      </div>
    </div>
  </el-dialog>
applications/drone-command/src/views/resource/patchManagement.vue
@@ -41,7 +41,7 @@
      </template>
    </avue-crud>
    <el-dialog title="上传图斑" class="ztzf-dialog-mange" append-to-body align-center v-model="box" width="550px">
    <el-dialog title="上传图斑" class="command-dialog-mange" append-to-body align-center v-model="box" width="550px">
      <el-form
        ref="ruleFormRef"
        style="max-width: 600px"
applications/drone-command/src/views/system/menu.vue
@@ -206,6 +206,10 @@
                label: '无人机管控',
                value: 5,
              },
                            {
                                label: '任务工单',
                                value: 6,
                            },
            ],
            hide: true,
            rules: [
applications/drone-command/src/views/system/param.vue
@@ -3,7 +3,7 @@
    <div class="avue-crud">
      <el-row :hidden="!search">
        <!-- 查询模块 -->
        <div class="ztzf-form-search">
        <div class="command-form-search">
          <el-form inline :model="query">
            <el-row :gutter="24">
              <el-col :span="8">
@@ -137,7 +137,7 @@
      </el-row>
      <!-- 表单模块 -->
      <el-dialog
        class="ztzf-dialog-mange"
        class="command-dialog-mange"
        :title="title"
        v-model="box"
        width="50%"
applications/drone-command/src/views/system/userinfo.vue
@@ -43,23 +43,23 @@
          <el-form :model="userInfo" label-width="120px">
            <el-form-item label="姓名:">
              <el-input
                class="ztzf-input"
                class="command-input"
                :style="{ width: pxToRem(350) }"
                v-model="userInfo.realName"
                disabled
              />
            </el-form-item>
            <!--                        <el-form-item label="昵称:">
                                                    <el-input class="ztzf-input" v-model="userInfo.name" />
                                                    <el-input class="command-input" v-model="userInfo.name" />
                                                </el-form-item>-->
            <el-form-item label="所属部门:">
              <el-input class="ztzf-input" v-model="userInfo.deptName" disabled />
              <el-input class="command-input" v-model="userInfo.deptName" disabled />
            </el-form-item>
            <el-form-item label="手机号:">
              <el-input class="ztzf-input" v-model="userInfo.phone" />
              <el-input class="command-input" v-model="userInfo.phone" />
            </el-form-item>
            <el-form-item label="邮箱:">
              <el-input class="ztzf-input" v-model="userInfo.email" />
              <el-input class="command-input" v-model="userInfo.email" />
            </el-form-item>
          </el-form>
        </div>
@@ -77,7 +77,7 @@
        <el-form :model="passwordForm" label-width="120px">
          <el-form-item label="原始密码:" prop="oldPassword">
            <el-input
              class="ztzf-input"
              class="command-input"
              v-model="passwordForm.oldPassword"
              type="password"
              show-password
@@ -85,7 +85,7 @@
          </el-form-item>
          <el-form-item label="新密码:" prop="newPassword">
            <el-input
              class="ztzf-input"
              class="command-input"
              v-model="passwordForm.newPassword"
              type="password"
              show-password
@@ -93,7 +93,7 @@
          </el-form-item>
          <el-form-item label="确认密码:" prop="newPassword1">
            <el-input
              class="ztzf-input"
              class="command-input"
              v-model="passwordForm.newPassword1"
              type="password"
              show-password
applications/drone-command/src/views/tickets/component/AddEditDetails.vue
@@ -1,5 +1,5 @@
<template>
    <el-dialog class="ztzf-dialog-mange" modal-class="add-edit-details" v-model="isShowAddEditDetails" :title="txtTitle"
    <el-dialog class="command-dialog-mange" modal-class="add-edit-details" v-model="isShowAddEditDetails" :title="txtTitle"
                         width="70%" :close-on-click-modal="false" :destroy-on-close="true" @close="closeFun"
    >
        <div class="add-edit-details">
applications/drone-command/src/views/tickets/orderLog.vue
@@ -85,7 +85,7 @@
          </el-table>
        </div>
        <div class="pagination" v-if="orderListTable.length > 0">
          <el-pagination class="ztzf-pagination" popper-class="custom-pagination-dropdown" background
          <el-pagination class="command-pagination" popper-class="custom-pagination-dropdown" background
                         :page-sizes="[10, 20, 30, 40, 50, 100]" v-model:current-page="orderListParams.current"
                         v-model:page-size="orderListParams.size" layout="total, sizes, prev, pager, next, jumper" :total="total"
                         @size-change="handleSizeChange" @current-change="handleCurrentChange" />
applications/drone-command/src/views/tickets/orderLogCopy.vue
@@ -4,7 +4,7 @@
      <el-tab-pane v-for="tab in filteredTabs" :key="tab.name" :label="`${tab.label} (${tab.count})`" :name="tab.name">
        <basic-main-content>
          <!-- 查询条件筛选栏 -->
          <div class="ztzf-form-search">
          <div class="command-form-search">
            <el-form :model="filters" inline>
              <el-row :gutter="24">
                <el-col :span="4">
@@ -94,7 +94,7 @@
          </div>
          <!-- 表格部分 -->
          <avue-crud class="ztzf-public-general-avue-crud" :data="tableData" :option="option" v-model:page="page"
          <avue-crud class="command-public-general-avue-crud" :data="tableData" :option="option" v-model:page="page"
            ref="crud" :table-loading="loading" @current-change="currentChange" @refresh-change="refreshChange"
            @on-load="onLoad" @search-change="searchChange" @size-change="sizeChange" v-if="activeTab === tab.name">
            <template #job_info_num="{ row }">
@@ -175,7 +175,7 @@
    </el-tabs>
    <!-- 新建工单对话框 -->
    <el-dialog class="ztzf-dialog-mange" v-model="dialogVisible" title="新建工单" width="70%" :close-on-click-modal="false"
    <el-dialog class="command-dialog-mange" v-model="dialogVisible" title="新建工单" width="70%" :close-on-click-modal="false"
      @close="resetForm">
      <el-form :model="form" :rules="rules" ref="testform" label-width="100px">
        <el-row :gutter="20">
@@ -271,7 +271,7 @@
    </el-dialog>
    <!-- 工单详情对话框 -->
    <el-dialog class="ztzf-dialog-mange" align-center v-model="detailVisible" :title="detailTitle" width="70%"
    <el-dialog class="command-dialog-mange" align-center v-model="detailVisible" :title="detailTitle" width="70%"
      :close-on-click-modal="false" @close="resetForm">
      <div class="event-title-center">{{ form.name }}</div>
      <el-form :model="form" :rules="rules" ref="testform" label-width="100px">
@@ -408,7 +408,7 @@
    </el-dialog>
    <!-- 工单详情 -->
    <el-dialog class="ztzf-dialog-mange" align-center v-model="detailVisibleCopy" title="工单详情" width="70%"
    <el-dialog class="command-dialog-mange" align-center v-model="detailVisibleCopy" title="工单详情" width="70%"
      :close-on-click-modal="false" @close="resetForm">
      <div class="event-title-center">{{ form.name }}</div>
      <el-form :model="form" ref="testform" label-width="100px">
applications/drone-command/src/views/tickets/ticket-copy.vue
@@ -10,7 +10,7 @@
        <basic-main-content>
          <!-- 查询条件筛选栏 -->
          <!-- 查询条件筛选栏 -->
          <div class="ztzf-form-search">
          <div class="command-form-search">
            <el-form :model="filters" inline>
              <el-row :gutter="24">
                <el-col :span="4">
@@ -168,7 +168,7 @@
          </div>
          <!-- 表格部分 -->
          <avue-crud
            class="ztzf-public-general-avue-crud"
            class="command-public-general-avue-crud"
            ref="avueCrud"
            v-model="tableData"
            :option="option"
@@ -908,7 +908,7 @@
      title="批量审核"
      width="1100"
      append-to-body
      class="ztzf-dialog-mange"
      class="command-dialog-mange"
      custom-class="review-dialog"
      @close="cancleBatchReject"
    >
@@ -990,7 +990,7 @@
      title="工单复核"
      width="30%"
      append-to-body
      class="ztzf-dialog-mange"
      class="command-dialog-mange"
      @close="reCheckDialog = false"
    >
      <div class="dialog-footer">
applications/drone-command/src/views/tickets/ticket.vue
@@ -9,7 +9,7 @@
      >
        <basic-main-content>
            <!-- 查询条件筛选栏 -->
           <div class="ztzf-form-search">
           <div class="command-form-search">
            <el-form :model="filters" inline>
              <div class="searchBox">
                <el-form-item label="关键字:">
@@ -151,7 +151,7 @@
          <div class="titketBox">
          <div class="dataTable">
        <el-table
           class="ztzf-public-general-el-table custom-header"
           class="command-public-general-el-table custom-header"
          ref="elTable"
          :data="tableData"
          v-loading="loading"
@@ -331,7 +331,7 @@
      width="1100"
       align-center
      append-to-body
      class="ztzf-dialog-mange"
      class="command-dialog-mange"
      custom-class="review-dialog"
      @close="cancleBatchReject"
    >
@@ -1598,7 +1598,7 @@
}
/* 表格样式 */
.ztzf-public-general-el-table {
.command-public-general-el-table {
  margin-bottom: 16px;
}
@@ -1608,7 +1608,7 @@
  justify-content: flex-end;
  margin-top: 10px;
}
.ztzf-public-general-el-table {
.command-public-general-el-table {
  :deep(.el-table__header-wrapper) {
    background-color: #f5f7fa;
  }
applications/drone-command/src/views/tickets/ticketComponent/RecheckDialog.vue
@@ -4,7 +4,7 @@
    title="工单复核"
    width="30%"
    append-to-body
    class="ztzf-dialog-mange"
    class="command-dialog-mange"
    @close="handleClose"
  >
    <div class="dialog-footer">
applications/mobile-web-view/package.json
@@ -77,7 +77,6 @@
        "@vitejs/plugin-vue": "catalog:",
        "@vue/compiler-sfc": "catalog:",
        "postcss-pxtorem": "catalog:",
        "prettier": "catalog:",
        "sass": "catalog:",
        "terser": "catalog:",
        "unplugin-auto-import": "catalog:",
applications/mobile-web-view/src/appPages/voiceCallDetail/index.vue
@@ -23,8 +23,9 @@
            <div class="status-group">
                <strong v-if="state==='ringing'" class="status-text incoming-status">来电中...</strong>
                <strong v-if="state==='calling'" class="status-text calling-status">呼叫中...</strong>
                <!--    <strong class="status-text">状态:<span :style="{color: getStateColor(state)}">{{ state }}</span></strong>-->
                <strong v-if="state==='in-call'" class="status-text">通话时长:{{ durationText }}</strong>
                <!--  通话结束提示-->
                <strong v-if="callEnded" class="status-text ended-status">{{ endMessage }}</strong>
            </div>
            <div class="button-group">
                <button v-if="state === 'idle' && !receiveParameters" @click="requestCall" :disabled="!connected" class="btn btn-call">呼叫</button>
@@ -128,6 +129,10 @@
const state = ref('idle') // idle | calling | ringing | in-call
const logText = ref('')
const remoteAudio = ref(null)
// 通话结束相关
const callEnded = ref(false)
const endMessage = ref('对方已挂断')
const endTimer = ref(null)
/**
 * 根据状态返回对应颜色
@@ -362,8 +367,6 @@
/* ---------------- 信令处理 ---------------- */
async function onSignal(msg) {
console.log('1cesss',msg);
    if (!msg) return
    const t = (msg.type || '').toString()
@@ -624,12 +627,41 @@
    lastOfferSdp = null
    state.value = 'idle'
    // 返回上一页(uni-app的语音通话列表页面)
    if (window.uni) {
        window.uni.navigateBack()
    // 清除之前的定时器
    if (endTimer.value) {
        clearTimeout(endTimer.value)
        endTimer.value = null
    }
    // 根据是谁发起的挂断决定不同的行为
    if (sendToPeer) {
        // 自己主动挂断:不提示,不延迟,直接跳转
        log('⏰ 自己主动挂断,直接返回')
        callEnded.value = false
        // 返回上一页(uni-app的语音通话列表页面)
        if (window.uni) {
            window.uni.navigateBack()
        } else {
            log('❌ uni对象不存在,无法返回上一页')
        }
    } else {
        log('❌ uni对象不存在,无法返回上一页')
        // 对方挂断:显示提示,延迟10秒跳转
        callEnded.value = true
        endMessage.value = '对方已挂断'
        // 延迟10秒后跳转页面
        endTimer.value = setTimeout(() => {
            log('⏰ 通话结束10秒后自动返回')
            // 返回上一页(uni-app的语音通话列表页面)
            if (window.uni) {
                window.uni.navigateBack()
            } else {
                log('❌ uni对象不存在,无法返回上一页')
            }
            // 重置通话结束状态
            callEnded.value = false
        }, 5000)
    }
}
@@ -668,12 +700,6 @@
    }
});
// 监听连接状态,连接成功后自动呼叫
// watch(() => connected.value, (newVal) => {
//     if (newVal && state.value === 'idle') {
//         requestCall()
//     }
// })
onBeforeUnmount(() => {
    cleanup(false)
@@ -702,6 +728,15 @@
    padding: 0;
}
/* 通话结束状态样式 */
.status-text.ended-status {
    color: #e74c3c;
    font-size: 18px;
    font-weight: bold;
    margin-top: 10px;
    text-align: center;
}
.content-wrapper {
    display: flex;
    flex-direction: column;
applications/mobile-web-view/src/hooks/useAreaBoundary.js
@@ -71,7 +71,7 @@
        geoJsonLayer && map.removeLayer(geoJsonLayer)
        geoJsonLayer = L.geoJSON(row.gJson, {
            style: feature => ({
                color: '#37D2D5', // 边界线颜色
                color: '#00F9EC', // 边界线颜色
                weight: 2, // 边界线宽度
                fillOpacity: 0, // 填充透明度
            }),
applications/task-work-order/env/.env.development
@@ -1,3 +1,13 @@
###
 # @Author       : yuan
 # @Date         : 2026-01-07 14:58:30
 # @LastEditors  : yuan
 # @LastEditTime : 2026-01-16 11:02:24
 # @FilePath     : \applications\task-work-order\env\.env.development
 # @Description  :
 # Copyright 2026 OBKoro1, All Rights Reserved.
 # 2026-01-07 14:58:30
###
NODE_ENV = 'development'
#开发环境配置
@@ -5,7 +15,7 @@
#开发环境代理地址(推荐本地新建文件 .env.development.local 来进行覆盖)
# VITE_APP_URL = https://wrj.shuixiongit.com/api
VITE_APP_URL= http://192.168.1.33:81
VITE_APP_URL= http://192.168.1.204
#新大屏地址
VITE_APP_DASHBOARD_URL = 'https://wrj.shuixiongit.com/command-center-dashboard/'
applications/task-work-order/package.json
@@ -72,7 +72,6 @@
    "@vitejs/plugin-vue": "catalog:",
    "@vue/compiler-sfc": "catalog:",
    "postcss-pxtorem": "catalog:",
    "prettier": "catalog:",
    "sass": "catalog:",
    "terser": "catalog:",
    "unplugin-auto-import": "catalog:",
applications/task-work-order/public/fonts/PangMen.TTF
copy from applications/drone-command/public/fonts/PANGMENZHENGDAOBIAOTITIMIANFEIBAN-2.TTF copy to applications/task-work-order/public/fonts/PangMen.TTF Binary files differ
applications/task-work-order/public/fonts/font.css
@@ -41,3 +41,11 @@
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "PangMen";
  src: url("PangMen.TTF") format("truetype");
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}
applications/task-work-order/public/img/bg/mainLogo.png

applications/task-work-order/src/api/dataCockpit/index.js
New file
@@ -0,0 +1,54 @@
/*
 * @Author       : yuan
 * @Date         : 2026-01-09 11:14:04
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-09 11:35:14
 * @FilePath     : \applications\drone-command\src\api\dataCockpit\index.js
 * @Description  :
 * Copyright 2026 OBKoro1, All Rights Reserved.
 * 2026-01-09 11:14:04
 */
import request from '@/axios'
// 设备查询
export const deviceSearchApi = (params) => {
    return request({
        url: '/drone-fw/cockpit/dataCockpit/deviceSearch',
        method: 'get',
        params
    })
}
// 历史告警/实时告警
export const alarmLogApi = (params) => {
    return request({
        url: '/drone-fw/cockpit/dataCockpit/alarmLog',
        method: 'get',
        params
    })
}
// 告警记录统计
export const alarmStatisticsApi = () => {
    return request({
        url: '/drone-fw/cockpit/dataCockpit/alarmStatistics',
        method: 'get'
    })
}
// 信号干扰/诱导驱离
export const interferenceAndExpulsionApi = (data) => {
    return request({
        url: '/drone-fw/cockpit/dataCockpit/interferenceAndExpulsion',
        method: 'post',
        data
    })
}
// 设备统计
export const deviceStatisticsApi = () => {
    return request({
        url: '/drone-fw/cockpit/dataCockpit/deviceStatistics',
        method: 'get'
    })
}
applications/task-work-order/src/assets/images/aiNowFly/Frame.png
applications/task-work-order/src/assets/images/aiNowFly/fly.png
applications/task-work-order/src/assets/images/aiNowFly/flying.png
applications/task-work-order/src/assets/images/aiNowFly/full-charge.svg
New file
@@ -0,0 +1,13 @@
<svg width="28" height="48" viewBox="0 0 28 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Vector">
<path d="M16.5455 0C16.7126 0 16.8781 0.0326726 17.0325 0.0961522C17.1869 0.159632 17.3272 0.252675 17.4454 0.36997C17.5636 0.487265 17.6573 0.626515 17.7213 0.779768C17.7853 0.933022 17.8182 1.09728 17.8182 1.26316V2.52632H24.1818C25.1945 2.52632 26.1656 2.92556 26.8817 3.63623C27.5977 4.34689 28 5.31076 28 6.31579V44.2105C28 45.2156 27.5977 46.1794 26.8817 46.8901C26.1656 47.6008 25.1945 48 24.1818 48H3.81818C2.80554 48 1.83437 47.6008 1.11832 46.8901C0.402272 46.1794 0 45.2156 0 44.2105V6.31579C0 5.31076 0.402272 4.34689 1.11832 3.63623C1.83437 2.92556 2.80554 2.52632 3.81818 2.52632H10.1818V1.26316C10.1818 0.928148 10.3159 0.606858 10.5546 0.36997C10.7933 0.133082 11.117 0 11.4545 0H16.5455ZM24.1818 5.05263H3.81818C3.48063 5.05263 3.15691 5.18571 2.91823 5.4226C2.67954 5.65949 2.54545 5.98078 2.54545 6.31579V44.2105C2.54545 44.5455 2.67954 44.8668 2.91823 45.1037C3.15691 45.3406 3.48063 45.4737 3.81818 45.4737H24.1818C24.5194 45.4737 24.8431 45.3406 25.0818 45.1037C25.3205 44.8668 25.4545 44.5455 25.4545 44.2105V6.31579C25.4545 5.98078 25.3205 5.65949 25.0818 5.4226C24.8431 5.18571 24.5194 5.05263 24.1818 5.05263Z" fill="url(#paint0_linear_1519_1955)"/>
<path d="M22.909 42.9474V8.33691H5.09082V42.9474H22.909Z" fill="#40FF5C"/>
</g>
<defs>
<linearGradient id="paint0_linear_1519_1955" x1="14" y1="0" x2="14" y2="48" gradientUnits="userSpaceOnUse">
<stop offset="0.25" stop-color="#00FFF2"/>
<stop offset="0.514423" stop-color="#00FFF2"/>
<stop offset="0.721154" stop-color="#00FFF2"/>
</linearGradient>
</defs>
</svg>
applications/task-work-order/src/assets/images/aiNowFly/low-battery.svg
New file
@@ -0,0 +1,6 @@
<svg width="28" height="48" viewBox="0 0 28 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Group 1321316190">
<path id="Vector" d="M16.3293 0C16.4942 0 16.6576 0.03249 16.81 0.0956148C16.9624 0.15874 17.1008 0.251263 17.2175 0.367902C17.3341 0.484542 17.4266 0.623013 17.4897 0.77541C17.5529 0.927807 17.5854 1.09114 17.5854 1.2561V2.5122H23.8659C24.8653 2.5122 25.8237 2.90921 26.5304 3.6159C27.2371 4.32259 27.6341 5.28107 27.6341 6.28049V43.9634C27.6341 44.9628 27.2371 45.9213 26.5304 46.628C25.8237 47.3347 24.8653 47.7317 23.8659 47.7317H3.76829C2.76888 47.7317 1.8104 47.3347 1.10371 46.628C0.397016 45.9213 0 44.9628 0 43.9634V6.28049C0 5.28107 0.397016 4.32259 1.10371 3.6159C1.8104 2.90921 2.76888 2.5122 3.76829 2.5122H10.0488V1.2561C10.0488 0.92296 10.1811 0.603466 10.4167 0.367902C10.6522 0.132338 10.9717 0 11.3049 0H16.3293ZM23.8659 5.02439H3.76829C3.43515 5.02439 3.11566 5.15673 2.8801 5.39229C2.64453 5.62786 2.5122 5.94735 2.5122 6.28049V43.9634C2.5122 44.2966 2.64453 44.616 2.8801 44.8516C3.11566 45.0872 3.43515 45.2195 3.76829 45.2195H23.8659C24.199 45.2195 24.5185 45.0872 24.754 44.8516C24.9896 44.616 25.122 44.2966 25.122 43.9634V6.28049C25.122 5.94735 24.9896 5.62786 24.754 5.39229C24.5185 5.15673 24.199 5.02439 23.8659 5.02439Z" fill="#00FFF2"/>
<path id="Vector_2" d="M22.6098 42.707V37.6826H5.02441V42.707H22.6098Z" fill="#F20303"/>
</g>
</svg>
applications/task-work-order/src/assets/images/aiNowFly/medium-battery.svg
New file
@@ -0,0 +1,6 @@
<svg width="28" height="48" viewBox="0 0 28 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Vector">
<path d="M16.5455 0C16.7126 0 16.8781 0.0326726 17.0325 0.0961522C17.1869 0.159632 17.3272 0.252675 17.4454 0.36997C17.5636 0.487265 17.6573 0.626515 17.7213 0.779768C17.7853 0.933022 17.8182 1.09728 17.8182 1.26316V2.52632H24.1818C25.1945 2.52632 26.1656 2.92556 26.8817 3.63623C27.5977 4.34689 28 5.31076 28 6.31579V44.2105C28 45.2156 27.5977 46.1794 26.8817 46.8901C26.1656 47.6008 25.1945 48 24.1818 48H3.81818C2.80554 48 1.83437 47.6008 1.11832 46.8901C0.402272 46.1794 0 45.2156 0 44.2105V6.31579C0 5.31076 0.402272 4.34689 1.11832 3.63623C1.83437 2.92556 2.80554 2.52632 3.81818 2.52632H10.1818V1.26316C10.1818 0.928148 10.3159 0.606858 10.5546 0.36997C10.7933 0.133082 11.117 0 11.4545 0H16.5455ZM24.1818 5.05263H3.81818C3.48063 5.05263 3.15691 5.18571 2.91823 5.4226C2.67954 5.65949 2.54545 5.98078 2.54545 6.31579V44.2105C2.54545 44.5455 2.67954 44.8668 2.91823 45.1037C3.15691 45.3406 3.48063 45.4737 3.81818 45.4737H24.1818C24.5194 45.4737 24.8431 45.3406 25.0818 45.1037C25.3205 44.8668 25.4545 44.5455 25.4545 44.2105V6.31579C25.4545 5.98078 25.3205 5.65949 25.0818 5.4226C24.8431 5.18571 24.5194 5.05263 24.1818 5.05263Z" fill="#00FFF2"/>
<path d="M22.9091 42.9474V23.4947H5.09091V42.9474H22.9091Z" fill="#00EEFF"/>
</g>
</svg>
applications/task-work-order/src/assets/images/aiNowFly/starting.png
applications/task-work-order/src/assets/images/dataCockpit/equipment.png
applications/task-work-order/src/assets/images/dataCockpit/fzsb.png
applications/task-work-order/src/assets/images/dataCockpit/layerControl.png
applications/task-work-order/src/assets/images/dataCockpit/left-btn.png
applications/task-work-order/src/assets/images/dataCockpit/map/equipment.png
applications/task-work-order/src/assets/images/dataCockpit/right-btn.png
applications/task-work-order/src/assets/images/dataCockpit/title-bg.png
applications/task-work-order/src/assets/images/dataCockpit/warning.png
applications/task-work-order/src/assets/images/dataCockpit/zcsb.png
applications/task-work-order/src/assets/images/no-data-bg.png
applications/task-work-order/src/assets/images/pageLayout/collapse.png
applications/task-work-order/src/components/PlaybackVideo/PlaybackVideo.vue
@@ -1,6 +1,6 @@
<template>
    <el-dialog
        class="ztzf-dialog-video playback-dialog"
        class="work-dialog-video playback-dialog"
        v-model="isShow"
        append-to-body
        :close-on-click-modal="false"
applications/task-work-order/src/components/PlaybackVideo/components/MapContainer.vue
@@ -1,5 +1,5 @@
<template>
    <div class="ztzf-cesium map-box" id="map">
    <div class="work-cesium map-box" id="map">
        <PlanarRouteLineList
            :curRouteLineData="curRouteLineData"
            @routeLineListClick="routeLineListClick"
applications/task-work-order/src/components/basic-container/main.vue
@@ -2,8 +2,8 @@
 * @Author       : yuan
 * @Date         : 2025-06-14 15:19:16
 * @LastEditors  : yuan
 * @LastEditTime : 2025-06-27 14:34:02
 * @FilePath     : \src\components\basic-container\main.vue
 * @LastEditTime : 2026-01-14 10:10:35
 * @FilePath     : \applications\drone-command\src\components\basic-container\main.vue
 * @Description  : 
 * Copyright 2025 OBKoro1, All Rights Reserved. 
 * 2025-06-14 15:19:16
@@ -47,26 +47,25 @@
.basic-container {
  height: 0;
  flex: 1;
  padding: 10px;
  // box-sizing: border-box;
  // height: 100%;
  .basic-container__card {
    display: flex;
    flex-direction: column;
    height: 100% !important;
    overflow: hidden;
    background: transparent;
    .el-card__body {
    ::v-deep(.el-card__body) {
      padding: 0 !important;
      height: 0 !important;
      flex: 1 !important;
      display: flex;
      flex-direction: column;
      height: 100% !important;
      overflow: hidden;
      box-sizing: border-box;
      &>div {
        height: 0;
        flex: 1;
      }
    }
  }
@@ -80,10 +79,6 @@
  &__card {
    width: 100%;
  }
  &:first-child {
    padding-top: 0;
  }
}
</style>
applications/task-work-order/src/components/commonDialog.vue
@@ -1,5 +1,5 @@
<template>
  <el-dialog class="ztzf-dialog-mange" append-to-body v-model="props.isShowView" :title="props.title"
  <el-dialog class="work-dialog-mange" append-to-body v-model="props.isShowView" :title="props.title"
             :width="props.width" :close-on-click-modal="false" :destroy-on-close="true" @close="cancel">
    <slot name="default" />
    <template #footer v-if="props.addEditView !== 'view'">
applications/task-work-order/src/components/map-container/device-map-container.vue
New file
@@ -0,0 +1,437 @@
<template>
    <div class="work-cesium map-container" :id="props.containerId"></div>
    <div v-if="props.showLayerControl" class="layer-control-root" :class="{ collapsed: props.leftCollapsed }">
        <div class="layer-control-wrap" ref="layerWrapRef">
            <div class="layer-control" @click="toggleLayerPanel">
                <img :src="layerControlIcon" alt="图层控制" />
            </div>
            <div v-if="showLayerPanel" class="layer-panel">
                <div class="panel-title">图层管理</div>
                <div class="panel-content">
                    <el-tree
                        :data="layerTree"
                        show-checkbox
                        default-expand-all
                        node-key="key"
                        :props="layerTreeProps"
                        :default-checked-keys="defaultCheckedKeys"
                    />
                </div>
            </div>
        </div>
    </div>
</template>
<script setup>
import * as Cesium from 'cesium'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import layerControlIcon from '@/assets/images/dataCockpit/layerControl.png'
import equipmentIcon from '@/assets/images/dataCockpit/map/equipment.png'
const props = defineProps({
    onlineDevices: {
        type: Array,
        default: () => [],
    },
    leftCollapsed: {
        type: Boolean,
        default: false,
    },
    containerId: {
        type: String,
        default: 'device-map-container',
    },
    showLayerControl: {
        type: Boolean,
        default: true,
    },
})
let viewInstance = null
let viewer = null
const deviceEntityIds = new Set()
const showLayerPanel = ref(false)
const layerWrapRef = ref(null)
const layerTreeProps = {
    label: 'label',
    children: 'children',
}
const defaultCheckedKeys = ['global', 'city-base']
const layerTree = ref([
    {
        key: 'base',
        label: '地理信息图层',
        children: [
            { key: 'global', label: '全球地形' },
            { key: 'admin', label: '行政区划' },
        ],
    },
    {
        key: 'city',
        label: '城市CIM图层',
        children: [
            { key: 'city-base', label: '皖山白模' },
            { key: 'city-grid', label: '皖山白模光栅网格' },
            { key: 'city-tilt', label: '皖山倾斜摄影' },
            { key: 'city-tilt-grid', label: '皖山倾斜摄影网格' },
        ],
    },
    {
        key: 'sky',
        label: '空域要素图层',
        children: [
            { key: 'airspace', label: '空域边界' },
            { key: 'route', label: '飞行航路' },
        ],
    },
])
const getDevicePosition = item => {
    const longitudeRaw = item.longitude ?? item.lng ?? item.lon
    const latitudeRaw = item.latitude ?? item.lat
    if (longitudeRaw == null || latitudeRaw == null) return null
    const longitude = Number(longitudeRaw)
    const latitude = Number(latitudeRaw)
    if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) return null
    return { longitude, latitude }
}
const clearDeviceEntities = () => {
    if (!viewer) return
    deviceEntityIds.forEach(id => {
        viewer.entities.removeById(id)
    })
    deviceEntityIds.clear()
}
const RING_STYLES = [
    { inner: 0, outer: 5000, gradient: ['#FF361C', '#360B00'] },
    { inner: 5000, outer: 8000, gradient: ['#FFC609', '#583300'] },
    { inner: 8000, outer: 10000, gradient: ['#2AEDBF', '#012B11'] },
]
const MATERIAL_TYPE = 'RadialGradientMaterial'
let materialRegistered = false
const registerRadialGradientMaterial = () => {
    if (materialRegistered || !Cesium?.Material) return
    materialRegistered = true
    Cesium.Material._materialCache.addMaterial(MATERIAL_TYPE, {
        fabric: {
            type: MATERIAL_TYPE,
            uniforms: {
                color1: new Cesium.Color(1.0, 1.0, 1.0, 1.0),
                color2: new Cesium.Color(0.0, 0.0, 0.0, 1.0),
            },
            source: `
                czm_material czm_getMaterial(czm_materialInput materialInput) {
                    czm_material material = czm_getDefaultMaterial(materialInput);
                    vec2 st = materialInput.st - vec2(0.5);
                    float t = clamp(length(st) * 2.0, 0.0, 1.0);
                    vec4 color = mix(color1, color2, t);
                    material.diffuse = color.rgb;
                    material.alpha = color.a;
                    return material;
                }
            `,
        },
        translucent: () => true,
    })
}
const buildCirclePositions = (center, radiusMeters, steps = 64) => {
    const positions = []
    const latRad = Cesium.Math.toRadians(center.latitude)
    const metersToLat = 1 / 111320
    const metersToLon = 1 / (111320 * Math.cos(latRad))
    for (let i = 0; i <= steps; i += 1) {
        const angle = Cesium.Math.toRadians((i / steps) * 360)
        const dx = radiusMeters * Math.cos(angle)
        const dy = radiusMeters * Math.sin(angle)
        const lon = center.longitude + dx * metersToLon
        const lat = center.latitude + dy * metersToLat
        positions.push(Cesium.Cartesian3.fromDegrees(lon, lat))
    }
    return positions
}
const addDeviceRings = (center, baseId) => {
    RING_STYLES.forEach((ring, index) => {
        const outerPositions = buildCirclePositions(center, ring.outer)
        const holes = ring.inner
            ? [new Cesium.PolygonHierarchy(buildCirclePositions(center, ring.inner))]
            : []
        const entityId = `${baseId}-ring-${index}`
        deviceEntityIds.add(entityId)
        registerRadialGradientMaterial()
        const [startColor, endColor] = ring.gradient
        const color1 = Cesium.Color.fromCssColorString(startColor).withAlpha(0.34)
        const color2 = Cesium.Color.fromCssColorString(endColor).withAlpha(0.34)
        const material = new RadialGradientMaterialProperty(color1, color2)
        viewer.entities.add({
            id: entityId,
            polygon: {
                hierarchy: new Cesium.PolygonHierarchy(outerPositions, holes),
                material,
            },
        })
    })
}
class RadialGradientMaterialProperty {
    constructor(color1, color2) {
        this._definitionChanged = new Cesium.Event()
        this.color1 = color1
        this.color2 = color2
    }
    get isConstant() {
        return true
    }
    get definitionChanged() {
        return this._definitionChanged
    }
    getType() {
        return MATERIAL_TYPE
    }
    getValue(time, result) {
        const target = result || {}
        target.color1 = this.color1
        target.color2 = this.color2
        return target
    }
    equals(other) {
        return (
            other instanceof RadialGradientMaterialProperty &&
            Cesium.Color.equals(this.color1, other.color1) &&
            Cesium.Color.equals(this.color2, other.color2)
        )
    }
}
const renderDeviceEntities = devices => {
    if (!viewer) return
    clearDeviceEntities()
    devices.forEach((item, index) => {
        const position = getDevicePosition(item)
        if (!position) return
        const entityId = `online-device-${item.id ?? index}`
        deviceEntityIds.add(entityId)
        addDeviceRings(position, entityId)
        viewer.entities.add({
            id: entityId,
            position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
            billboard: {
                image: equipmentIcon,
                width: 40.34,
                height: 40.34,
            },
        })
    })
}
watch(
    () => props.onlineDevices,
    devices => {
        renderDeviceEntities(devices || [])
    },
    { deep: true }
)
watch(
    () => props.leftCollapsed,
    isCollapsed => {
        if (isCollapsed) showLayerPanel.value = false
    }
)
const toggleLayerPanel = () => {
    if (!props.showLayerControl) return
    showLayerPanel.value = !showLayerPanel.value
}
const handleClickOutside = event => {
    if (!showLayerPanel.value) return
    const target = event.target
    if (layerWrapRef.value?.contains(target)) return
    showLayerPanel.value = false
}
onMounted(() => {
    document.addEventListener('click', handleClickOutside)
    viewInstance = new PublicCesium({
        dom: props.containerId,
        flatMode: false,
        terrain: false,
        layerMode: 4,
        contour: false,
    })
    viewer = viewInstance.getViewer()
    renderDeviceEntities(props.onlineDevices)
})
onBeforeUnmount(() => {
    document.removeEventListener('click', handleClickOutside)
    clearDeviceEntities()
    if (viewInstance) {
        viewInstance?.viewerDestroy()
        viewInstance = null
    }
    viewer = null
})
</script>
<style lang="scss" scoped>
.map-container {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
.layer-control-root {
    position: absolute;
    left: 411px;
    bottom: 22px;
    z-index: 9;
    transition: transform 0.3s ease-in-out;
    pointer-events: none;
    &.collapsed {
        transform: translateX(-401px);
    }
}
.layer-control-wrap {
    position: relative;
    display: flex;
    align-items: flex-end;
    pointer-events: auto;
}
.layer-control {
    width: 34px;
    height: 34px;
    cursor: pointer;
    img {
        width: 100%;
        height: 100%;
        display: block;
    }
}
.layer-panel {
    display: flex;
    flex-direction: column;
    position: absolute;
    left: 46px;
    bottom: 0;
    width: 160px;
    max-height: 442px;
    background: #191932;
    border-radius: 8px 8px 8px 8px;
    .panel-title {
        padding: 0 16px;
        line-height: 42px;
        font-family: 'Open Sans', Open Sans;
        font-weight: 400;
        font-size: 12px;
        color: #ffffff;
        text-align: left;
        font-style: normal;
        text-transform: none;
        border-bottom: 1px solid rgba(70, 70, 100, 0.5);
        box-sizing: border-box;
    }
    .panel-content {
        padding: 0 16px;
        height: 0;
        flex: 1;
        overflow: auto;
    }
    ::v-deep(.el-tree) {
        background: transparent;
        color: #c3c3dd;
        font-size: 12px;
        .el-tree-node {
            line-height: 30px !important;
            .el-tree-node__content {
                display: flex;
                align-items: center;
                height: 40px !important;
                line-height: 40px !important;
                border-bottom: 1px solid rgba(70, 70, 100, 0.5);
                box-sizing: border-box;
            }
            .el-tree-node__children {
                .el-tree-node__content {
                    border: none;
                }
            }
        }
    }
}
.layer-panel :deep(.el-tree-node__label) {
    color: #c3c3dd;
}
.layer-panel :deep(.el-tree-node__expand-icon) {
    order: 3;
    margin-left: auto;
}
.layer-panel :deep(.el-tree-node__expand-icon.is-leaf) {
    visibility: hidden;
}
.layer-panel :deep(.el-checkbox) {
    order: 1;
}
.layer-panel :deep(.el-tree-node__label) {
    order: 2;
}
.layer-panel :deep(.el-tree-node__expand-icon:not(.is-leaf) ~ .el-checkbox) {
    display: none;
}
.layer-panel :deep(.el-tree-node__content:hover) {
    background: transparent !important;
}
.layer-panel :deep(.el-tree-node.is-current > .el-tree-node__content) {
    background: transparent !important;
    color: #ffffff;
}
.layer-panel :deep(.el-tree-node.is-current > .el-tree-node__content .el-tree-node__label) {
    color: #ffffff;
}
.layer-panel :deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
    background-color: #023aff;
    border-color: #023aff;
}
.layer-panel :deep(.el-checkbox__inner) {
    background-color: transparent;
    border-color: #a1a3d4;
}
</style>
applications/task-work-order/src/components/map-container/mapContainer-copy.vue
@@ -3,10 +3,10 @@
 * @Date: 2024-10-25 15:07:51
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2025-04-28 11:34:40
 * @FilePath: \drone-web-manage\src\components\map-container\mapContainer.vue
 * @Description:
 *
 * Copyright (c) 2024 by shuishen, All Rights Reserved.
 * @FilePath: \drone-command\src\components\map-container\mapContainer.vue
 * @Description:
 *
 * Copyright (c) 2024 by shuishen, All Rights Reserved.
-->
<template>
  <div class="map-container">
@@ -261,4 +261,4 @@
  height: 120%;
  transform: translate(-50%, -50%);
}
</style>
</style>
applications/task-work-order/src/components/map-container/mapContainer.vue
@@ -3,14 +3,14 @@
 * @Date: 2024-10-25 15:07:51
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2025-04-28 11:34:40
 * @FilePath: \drone-web-manage\src\components\map-container\mapContainer.vue
 * @FilePath: \drone-command\src\components\map-container\mapContainer.vue
 * @Description:
 *
 * Copyright (c) 2024 by shuishen, All Rights Reserved.
-->
<template>
  <div class="map-container">
    <div class="viewer-container ztzf-cesium" id="viewer-container">
    <div class="viewer-container work-cesium" id="viewer-container">
      <div class="content">
        <slot name="content"></slot>
      </div>
applications/task-work-order/src/config/website.js
@@ -1,13 +1,23 @@
/*
 * @Author       : yuan
 * @Date         : 2026-01-06 09:47:27
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-16 11:12:30
 * @FilePath     : \applications\task-work-order\src\config\website.js
 * @Description  :
 * Copyright 2026 OBKoro1, All Rights Reserved.
 * 2026-01-06 09:47:27
 */
/**
 * 全局配置文件
 */
export default {
  title: 'saber',
  logo: 'S',
  key: 'saber', //配置主键,目前用于存储
  key: 'work', //配置主键,目前用于存储
  indexTitle: '',
  clientId: 'drone', // 客户端id
  clientSecret: 'drone_secret', // 客户端密钥
  clientId: 'saber', // 客户端id
  clientSecret: 'saber_secret', // 客户端密钥
  tenantMode: true, // 是否开启租户模式
  tenantId: '000000', // 管理组租户编号
  captchaMode: true, // 是否开启验证码模式
@@ -32,8 +42,8 @@
    menu: true,
  },
  fistPage: {
    name: '个人工作台',
    path: '/wel/index',
    name: '数据驾驶舱',
    path: '/dataCockpit/index',
    // path: '/tickets/ticket',
  },
applications/task-work-order/src/page/index/index.vue
@@ -1,26 +1,40 @@
<template>
  <div class="avue-contail" :class="{ 'avue--collapse': isCollapse }">
    <div class="avue-layout" :class="{ 'avue-layout--horizontal': isHorizontal }">
      <div class="avue-sidebar" v-show="validSidebar">
        <!-- 左侧导航栏 -->
        <logo />
        <sidebar />
      </div>
      <div class="avue-main">
        <!-- 顶部导航栏 -->
        <top ref="top" />
        <!-- 顶部标签卡 -->
        <div class="tags-box">
          <tags />
      <div class="layer-page-leaf-container">
        <div class="avue-sidebar" v-show="validSidebar">
          <!-- 左侧导航栏 -->
          <logo />
          <sidebar />
          <div class="footer">
            <div class="avue-breadcrumb" :class="[{ 'avue-breadcrumb--active': isCollapse }]">
              <img :src="collapse" alt="" @click="setCollapse">
            </div>
          </div>
        </div>
        <search class="main-avue-view-container" v-show="isSearch"></search>
        <!-- 主体视图层 -->
        <div class="main-avue-view-container" v-show="!isSearch" v-if="isRefresh">
          <router-view #="{ Component }">
            <keep-alive :include="$store.getters.tagsKeep">
              <component :is="Component" />
            </keep-alive>
          </router-view>
      </div>
      <div class="layer-page-right-container">
        <div class="layer-content-top">
          <top ref="top" />
          <div class="tags-box">
            <tags />
          </div>
        </div>
        <div class="layer-content-bottom">
          <div class="avue-main">
            <div class="main-avue-view-container" v-show="!isSearch" v-if="isRefresh">
              <router-view #="{ Component }">
                <keep-alive :include="$store.getters.tagsKeep">
                  <component :is="Component" />
                </keep-alive>
              </router-view>
            </div>
          </div>
        </div>
      </div>
    </div>
@@ -29,16 +43,18 @@
  </div>
</template>
<script>
import index from '@/mixins/index';
import wechat from './wechat.vue';
import index from '@/mixins/index'
import wechat from './wechat.vue'
//import { validatenull } from 'utils/validate';
import { mapGetters } from 'vuex';
import tags from './tags.vue';
import search from './search.vue';
import logo from './logo.vue';
import top from './top/index.vue';
import sidebar from './sidebar/index.vue';
import GlobalWS from '@/page/index/GlobalWS.vue';
import { mapGetters } from 'vuex'
import tags from './tags.vue'
import search from './search.vue'
import logo from './logo.vue'
import top from './top/index.vue'
import sidebar from './sidebar/index.vue'
import GlobalWS from '@/page/index/GlobalWS.vue'
import collapse from '@/assets/images/pageLayout/collapse.png'
export default {
  mixins: [index],
@@ -52,10 +68,15 @@
    wechat,
  },
  name: 'index',
  provide() {
  data () {
    return {
      collapse: collapse
    }
  },
  provide () {
    return {
      index: this,
    };
    }
  },
  computed: {
    ...mapGetters([
@@ -67,27 +88,27 @@
      'menu',
      'setting',
    ]),
    validSidebar() {
    validSidebar () {
      return !(
        (this.$route.meta || {}).menu === false || (this.$route.query || {}).menu === 'false'
      );
      )
    },
  },
  props: [],
  methods: {
    //打开菜单
    openMenu(item = {}) {
    openMenu (item = {}) {
      this.$store.dispatch('GetMenu', item.id).then(data => {
        if (data.length !== 0) {
          this.$router.$avueRouter.formatRoutes(data, true);
          this.$router.$avueRouter.formatRoutes(data, true)
          // 获取url里面的redirect
          const redirect = decodeURIComponent(this.$route.query.redirect || '');
          const redirect = decodeURIComponent(this.$route.query.redirect || '')
          if (redirect) {
            console.log('redirect', redirect);
            const [path, queryString] = redirect.split('?');
            const query = queryString ? Object.fromEntries(new URLSearchParams(queryString)) : {};
            this.$router.push({ path, query });
            console.log('redirect', redirect)
            const [path, queryString] = redirect.split('?')
            const query = queryString ? Object.fromEntries(new URLSearchParams(queryString)) : {}
            this.$router.push({ path, query })
          }
        }
        //当点击顶部菜单后默认打开第一个菜单
@@ -111,10 +132,14 @@
            }, itemActive.meta)
          });
        }*/
      });
      })
    },
    setCollapse () {
      this.$store.commit('SET_COLLAPSE')
    },
  },
};
}
</script>
<style lang="scss" scoped>
@@ -130,9 +155,6 @@
// }
.tags-box {
  background: transparent;
  padding: 0 10px;
  --el-color-primary: rgba(20, 65, 255, 1);
  height: pxToVh(55);
  padding-bottom: 30px;
}
</style>
applications/task-work-order/src/page/index/logo.vue
@@ -1,3 +1,13 @@
<!--
 * @Author       : yuan
 * @Date         : 2026-01-06 09:47:27
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-16 14:20:30
 * @FilePath     : \applications\task-work-order\src\page\index\logo.vue
 * @Description  :
 * Copyright 2026 OBKoro1, All Rights Reserved.
 * 2026-01-06 09:47:27
-->
<template>
  <div class="avue-logo">
    <transition name="fade">
@@ -10,7 +20,6 @@
      <template v-if="getScreen(!isCollapse)">
        <div class="fadeStyle">
          <img v-if="!hideMenuTopLogo" class="logoImg" src="/img/bg/mainLogo.png" alt=""/>
          <div class="titleName">综合管理平台</div>
          <!-- <span style="font-size: 20px;"  key="1">
              {{ this.parentDeptInfo.sysName }}
          </span> -->
@@ -45,18 +54,26 @@
  box-shadow: none;
}
.avue-logo_subtitle{
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  .logoImg{
    width: 80%;
    width: 40px;
    height: 40px;
  }
}
.fadeStyle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  height: 100%;
  .logoImg{
    width: 50px;
    height: 20px;
    width: 40px;
    height: 40px;
  }
  .titleName{
    font-family: YouSheBiaoTiHei, YouSheBiaoTiHei;
applications/task-work-order/src/page/index/sidebar/index.vue
@@ -39,23 +39,4 @@
};
</script>
<style lang="scss" scoped>
.el-menu{
  :deep(){
    .el-menu-item.is-active{
      background: linear-gradient( 90deg, rgba(179,194,255,0.02) 0%, rgba(20,65,255,0.09) 100%) !important;
      border-radius: 16px 16px 16px 16px;
      i,span{
        color: rgba(20, 65, 255, 1) !important;
      }
    }
    .el-sub-menu i{
      font-size: 16px;
    }
    .el-menu-item i{
      font-size: 16px;
    }
  }
}
</style>
applications/task-work-order/src/page/index/tags.vue
@@ -20,22 +20,6 @@
          </template>
        </el-tab-pane>
      </el-tabs>
      <el-dropdown class="avue-tags__menu">
        <el-button type="primary" style="background: #409eff;border-color: #409eff">
          {{ $t('tagsView.menu') }}
          <!-- <i class="el-icon-arrow-down el-icon--right"></i> -->
        </el-button>
        <template #dropdown>
          <el-dropdown-menu>
            <el-dropdown-item @click="openSearch">{{ $t('tagsView.search') }}</el-dropdown-item>
            <el-dropdown-item @click="closeOthersTags">{{ $t('tagsView.closeOthers') }}
            </el-dropdown-item>
            <el-dropdown-item @click="closeAllTags">{{ $t('tagsView.closeAll') }}</el-dropdown-item>
            <el-dropdown-item @click="clearCacheTags">{{ $t('tagsView.clearCache') }}
            </el-dropdown-item>
          </el-dropdown-menu>
        </template>
      </el-dropdown>
    </div>
  </div>
</template>
@@ -169,6 +153,5 @@
<style lang="scss" scoped>
.avue-tags{
  border-radius: 4px;
}
</style>
applications/task-work-order/src/page/index/top/index.vue
@@ -1,40 +1,28 @@
<template>
  <div class="avue-top">
    <div class="top-bar__left">
      <div class="avue-breadcrumb" :class="[{ 'avue-breadcrumb--active': isCollapse }]"
        v-if="setting.collapse && !isHorizontal">
        <i class="icon-navicon" @click="setCollapse"></i>
      </div>
      任务工单管理子系统
    </div>
    <div class="top-bar__title">
<!--<top-menu ref="topMenu" v-if="setting.menu"></top-menu>-->
      <top-search class="top-bar__item" v-if="setting.search"></top-search>
    </div>
    <div class="top-bar__right">
      <div class="icon-box">
        <top-lock v-if="setting.lock"/>
        <top-qna />
        <top-full v-if="setting.fullscreen" title="全屏"/>
        <img class="gateway" @click="jumpMH" src="@/assets/images/mh.svg" alt="进入门户" title="进入门户" width="20" height="20">
      </div>
      <div class="top-user">
        <img class="top-bar__img" :src="userInfo.avatar "  alt=""/>
        <div class="icon-box">
         <img class="gateway" @click="jumpMH" src="@/assets/images/mh.png" alt="进入门户" title="进入门户">
        </div>
        <el-dropdown>
          <span class="el-dropdown-link">
            {{ userInfo.real_name }}
            <el-icon class="el-icon--right">
              <arrow-down />
            </el-icon>
            <img class="top-bar__img" :src="userInfo.avatar" alt="" />
          </span>
          <template #dropdown>
            <el-dropdown-menu>
<!--              <el-dropdown-item>
              <!--              <el-dropdown-item>
                <router-link to="/">{{ $t('navbar.dashboard') }}</router-link>
              </el-dropdown-item>-->
              <el-dropdown-item>
              <!-- <el-dropdown-item>
                <router-link to="/info/index">{{ $t('navbar.userinfo') }}</router-link>
              </el-dropdown-item>
              </el-dropdown-item> -->
              <el-dropdown-item @click="logout" divided>{{ $t('navbar.logOut') }}
              </el-dropdown-item>
            </el-dropdown-menu>
@@ -92,9 +80,6 @@
    ]),
  },
  methods: {
    setCollapse () {
      this.$store.commit('SET_COLLAPSE')
    },
    logout () {
      this.$confirm(this.$t('logoutTip'), this.$t('提示'), {
        confirmButtonText: this.$t('submitText'),
@@ -121,10 +106,7 @@
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 15px;
  height: 50px;
  background-color: #fff;
  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
  height: 80px;
}
.top-bar__left {
@@ -143,16 +125,20 @@
  display: flex !important;
  align-items: center;
  height: 100%;
  pointer-events: auto;
  box-sizing: border-box;
  .icon-box{
  .icon-box {
    margin-right: 62px;
    display: flex;
    align-items: center;
    gap: 0 20px;
    .gateway{
      width: 25px;
      height: 25px;
    .gateway {
      width: 32px;
      height: 32px;
    }
    >*{
    >* {
      cursor: pointer;
    }
  }
@@ -169,10 +155,9 @@
}
.top-bar__img {
  width: 32px;
  height: 32px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  margin-right: 10px;
}
.el-dropdown-link {
applications/task-work-order/src/page/index/top/top-search.vue
@@ -1,6 +1,6 @@
<template>
  <el-autocomplete
    class="top-search ztzf-top-search"
    class="top-search work-top-search"
    popper-class="my-autocomplete"
    v-model="value"
    :fetch-suggestions="querySearch"
@@ -88,7 +88,7 @@
<style lang="scss">
.ztzf-top-search{
.work-top-search{
  display: flex !important;
  width: 150px;
  align-items: center;
applications/task-work-order/src/router/views/index.js
@@ -15,16 +15,7 @@
          i18n: 'dashboard',
        },
        component: () => import(/* webpackChunkName: "views" */ '@/views/wel/index.vue'),
      },
      {
        path: 'dashboard',
        name: '控制台',
        meta: {
          i18n: 'dashboard',
          menu: false,
        },
        component: () => import(/* webpackChunkName: "views" */ '@/views/wel/dashboard.vue'),
      },
      }
    ],
  },
  // 事件工单
applications/task-work-order/src/store/modules/user.js
@@ -280,7 +280,7 @@
        })
      }
      return new Promise(resolve => {
        getRoutes({tenantId,sysType: 1}).then(res => {
        getRoutes({tenantId,sysType: 6}).then(res => {
          const data = res.data.data;
          let menu = deepClone(data);
          if (!res.data?.data?.length){
@@ -299,7 +299,7 @@
    },
    GetButtons({ commit }) {
      return new Promise(resolve => {
        getButtons({sysType: 1}).then(res => {
        getButtons({sysType: 6}).then(res => {
          const data = res.data.data;
          commit('SET_PERMISSION', data);
          resolve();
applications/task-work-order/src/styles/common.scss
@@ -8,6 +8,8 @@
// 地图编辑样式
@import './mapEdit.scss';
@import './common/cockpit.scss';
a {
  text-decoration: none;
  color: #333;
@@ -26,10 +28,11 @@
}
.avue-layout {
  position: relative;
  display: flex;
  width: 100%;
  height: 100%;
  background: url("@/assets/images/layoutBg.png") no-repeat center / 100% 100%;
  background: #EBEEF4;
  overflow: hidden;
  &--horizontal {
@@ -56,21 +59,95 @@
      width: $sidebar_width
    }
  }
  > .layer-page-leaf-container {
    width: 179px;
    height: 100%;
    box-sizing: border-box;
    .avue-sidebar {
      display: flex;
      flex-direction: column;
      .avue-logo {
        height: 76px;
      }
      .footer {
        height: 102px;
        display: flex;
        align-items: center;
        justify-content: center;
        .avue-breadcrumb {
          display: flex;
          align-items: center;
          justify-content: center;
          width: 36px;
          height: 36px;
          cursor: pointer;
        }
      }
      .el-menu-item,
      .el-sub-menu__title {
        height: 56px !important;
        line-height: 56px !important;
      }
    }
  }
  > .layer-page-right-container {
    padding: 0 30px;
    padding-bottom: 30px;
    display: flex;
    flex-direction: column;
    width: 0;
    flex: 1;
    height: 100%;
    box-sizing: border-box;
    .layer-content-bottom {
      width: 100%;
      height: 0;
      flex: 1;
    }
  }
}
.avue-contail {
  width: 100%;
  height: 100%;
  background: #f0f2f5;
  background-size: 100%;
  background-repeat: no-repeat;
}
.avue--collapse {
  .avue-layout {
    > .layer-page-leaf-container {
      width: 53px;
      height: 100%;
  .avue-sidebar,
  .avue-logo {
    width: $sidebar_collapse;
      .avue-sidebar {
        width: 100%;
        .el-menu--collapse {
          width: 100%;
          .el-sub-menu__title {
            padding: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            i {
              margin-right: 0;
            }
          }
        }
      }
    }
  }
}
@@ -122,7 +199,7 @@
  bottom: 0;
}
.ztzf-cesium {
.work-cesium {
  .cesium-viewer {
    height: 100%;
    overflow: hidden;
@@ -191,7 +268,7 @@
  }
}
.ztzf-cesium {
.work-cesium {
  .cesium-viewer {
    height: 100%;
    overflow: hidden;
applications/task-work-order/src/styles/common/cockpit.scss
New file
@@ -0,0 +1,1118 @@
.gd-table {
  height: 0;
  flex: 1;
  background: transparent !important;
  .el-table--border .el-table__inner-wrapper:after,
  .el-table--border:after,
  .el-table--border:before,
  .el-table__inner-wrapper:before {
    height: 0;
  }
  tr {
    background: transparent !important;
  }
  th.el-table__cell {
    height: 40px;
    border-bottom: 1px solid #DBDFF1 !important;
    .cell {
      padding: 0;
      padding-left: 16px;
      font-family: Source Han Sans CN, Source Han Sans CN;
      font-weight: 400;
      font-size: 14px;
      color: #9292C1;
      text-align: left;
      font-style: normal;
      text-transform: none;
    }
  }
  td.el-table__cell {
    padding: 9px 0;
    height: 56px;
    .cell {
      padding: 0;
      padding-left: 16px;
      font-family: Source Han Sans CN, Source Han Sans CN;
      font-weight: 400;
      font-size: 14px;
      color: #383874;
      text-align: left;
      font-style: normal;
      text-transform: none;
    }
  }
  td.operation-btns .cell {
    display: flex;
    flex-wrap: nowrap;
    gap: 12px;
    /* 按钮间距(推荐) */
    white-space: nowrap;
    /* 双保险 */
    a {
      min-width: 28px;
      color: #0075FF;
      &:last-child {
        margin-right: 0;
      }
    }
  }
  th.el-table__cell:not(.fixed-column),
  td.el-table__cell:not(.fixed-column) {
    background: transparent !important;
  }
  .fixed-column {
    background-color: #1A1A2A !important;
  }
}
.gd-input {
  .el-input__wrapper {
    padding: 0 8px !important;
    background: #DDE2ED;
    box-shadow: none !important;
    border: none;
    .el-input__inner {
      color: #383874;
      &::placeholder {
        color: #A1A3D4;
      }
    }
  }
  &.is-disabled {
    .el-input__wrapper {
      background-color: #DDE2ED;
    }
  }
}
.gd-select {
  .el-select__input {
    color: #8ac3fd !important;
  }
  .el-select__wrapper {
    padding: 0 8px;
    background: #DDE2ED;
    box-shadow: none !important;
    border: none;
    width: 100%;
  }
  .el-select__placeholder {
    &.is-transparent {
      color: #A1A3D4 !important;
    }
    &.el-select__selected-item {
      color: #383874;
    }
  }
  .el-select {
    --el-select-border-color-hover: rgb(0, 162, 255) !important;
  }
  .el-input {
    --el-border-color: rgb(0, 162, 255);
  }
  .el-select .el-input__wrapper {
    background-color: #DDE2ED !important;
  }
  .el-input__wrapper {
    --el-input-text-color: #DDE2ED !important;
  }
}
.gd-tree-select-popper {
  background: #DDE2ED !important;
  border: none !important;
  .el-popper__arrow::before {
    background: #DDE2ED !important;
    border: 1px solid #DDE2ED !important;
  }
  .el-tree {
    background: transparent !important;
    color: #fff !important;
    .el-tree-node__content:hover {
      background: none !important;
    }
    .el-tree-node__content:hover {
      background: none !important;
    }
    .el-tree-node.is-current > .el-tree-node__content {
      color: #479dff !important;
    }
  }
  .el-select-dropdown__item {
    background: none !important;
    color: #383874 !important;
    input {
      color: #383874 !important;
      &::placeholder {
        color: #383874 !important;
      }
    }
  }
  .el-tree {
    --el-tree-node-hover-bg-color: none !important;
    color: #fff !important;
    .el-checkbox__inner {
      background: none !important;
      border: 1px solid #3194ef !important;
    }
  }
  .el-select-dropdown__item.is-selected {
    color: #383874 !important;
  }
  .el-popper.is-light {
    background: #012a50 !important;
    box-shadow: none !important;
    border: none !important;
    border-radius: 4px !important;
    margin-top: 2px;
    .el-select__selection {
      max-width: 240px !important;
      .el-select__selected-item {
        .el-tag {
          background: #0b1d38 !important;
          color: #383874;
        }
      }
    }
    .el-popper__arrow::before {
      background: #012a50 !important;
      border: 1px solid #012a50 !important;
    }
  }
}
.gd-date-picker {
  box-shadow: none !important;
  background: #DDE2ED;
  border: none;
  border-radius: 5px;
  box-sizing: border-box !important;
  .el-input__wrapper {
    box-shadow: none !important;
    background-color: transparent !important;
    &.is-focus {
      box-shadow: none !important;
    }
  }
  .el-range-input {
    color: #fff;
    &::placeholder {
      color: #A1A3D4;
    }
  }
  .el-range-separator {
    color: #ffffff;
  }
}
.gd-date-picker-popper {
  background: #DDE2ED !important;
  border: none !important;
  border-radius: 0px 0px 8px 8px;
  .el-date-picker__prev-btn,
  .el-date-picker__next-btn {
    .el-icon {
      color: #fff !important;
    }
  }
  .el-date-picker__header-label {
    color: #fff !important;
  }
  .el-picker-panel {
    .el-picker-panel__body-wrapper {
      .el-picker-panel__body {
        background: #DDE2ED !important;
        color: #fff !important;
        .el-date-range-picker__time-header {
          .el-input__wrapper {
            box-shadow: none;
            background: #DDE2ED !important;
          }
        }
        .el-date-picker__time-header {
          border: none;
          .el-date-picker__editor-wrap {
            .el-time-panel {
              background: #DDE2ED !important;
              border-radius: 0px 0px 8px 8px;
              border: 1px solid !important;
              border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1 !important;
              // 将 time-picker-popper 的样式移到这里
              .el-time-panel__header {
                background: #DDE2ED !important;
                color: #ffffff !important;
                border-bottom: 1px solid rgba(71, 157, 255, 0.3) !important;
              }
              .el-time-panel__content {
                .el-time-spinner__item {
                  color: #e6e6e6 !important;
                  &:hover {
                    background: rgba(71, 157, 255, 0.3) !important;
                  }
                  &.active {
                    color: #479dff !important;
                    font-weight: bold;
                  }
                }
              }
              .el-time-panel__footer {
                background: #DDE2ED !important;
                border-top: 1px solid rgba(71, 157, 255, 0.3) !important;
                .el-button {
                  color: #fff !important;
                  &:hover {
                    color: #479dff !important;
                  }
                }
                .el-time-panel__btn.cancel {
                  color: #fff !important;
                  &:hover {
                    color: #fff !important;
                  }
                }
              }
            }
          }
        }
        .el-picker-panel__content {
          .el-date-range-picker__header {
            .el-picker-panel__icon-btn {
              color: #fff !important;
              &:hover {
                color: var(--el-color-primary) !important;
              }
              .el-icon {
                &::before {
                  color: inherit !important;
                }
              }
            }
            & > div {
              span {
                color: #fff !important;
              }
            }
          }
          .el-date-table {
            th {
              color: #fff !important;
            }
            td.disabled div {
              background-color: #00193b !important;
              color: #505050 !important;
              cursor: not-allowed;
            }
            // td:hover,
            // td.current:not(.disabled) {
            //     background: rgba(60, 121, 202) !important;
            //     color: white !important;
            // }
            td.start-date span,
            td.start-date span,
            td.end-date span {
              background-color: #DDE2ED !important;
            }
            td.in-range div,
            td.in-range div:hover,
            &.is-week-mode .el-date-table__row.current div,
            &.is-week-mode .el-date-table__row:hover div {
              background-color: rgba(60, 121, 202) !important;
            }
          }
        }
      }
    }
  }
  .el-popper__arrow {
    &::before {
      background: #DDE2ED !important;
      border: 1px solid #DDE2ED !important;
      box-sizing: border-box;
    }
  }
  .el-picker-panel__sidebar {
    background: #DDE2ED;
    color: #fff;
    .el-picker-panel__shortcut {
      color: #ffffff;
    }
  }
  .el-picker-panel__body {
    .el-input.is-disabled .el-input__wrapper {
      background: #DDE2ED;
      box-shadow: 0 0 0 0.1rem #409eff;
      color: #fff;
      .el-input__inner {
        color: #fff !important;
      }
    }
    .el-button.is-disabled,
    .el-button.is-disabled:hover {
      background: #DDE2ED;
    }
    .el-input__wrapper {
      background: #DDE2ED;
      box-shadow: 0 0 0 0.1rem #409eff;
      color: #fff;
      .el-input__inner {
        color: #fff !important;
      }
    }
  }
  .el-picker-panel__footer {
    background: #DDE2ED;
    color: #fff;
    border-top: none;
    .el-button--small {
      box-shadow: 0 0 0 0.1rem #409eff;
      background-color: #DDE2ED;
      color: #fff;
      border: none;
      &:hover {
        background-color: #DDE2ED;
        color: #fff;
        border-color: #409eff;
      }
    }
  }
}
.gd-dialog {
  padding: 30px;
  display: flex;
  flex-direction: column;
  width: 770px;
  background: #FFFFFF;
  border: 1px solid #CCCCCC;
  border-radius: 6px 6px 6px 6px;
  .el-dialog__header {
    span {
      font-family: Source Han Sans CN, Source Han Sans CN;
      font-weight: bold;
      font-size: 18px;
      color: #383874;
      line-height: 22px;
      text-align: left;
      font-style: normal;
      text-transform: none;
    }
    .el-icon {
      color: #383874;
    }
  }
  .el-dialog__body {
    height: 0;
    flex: 1;
    .detail-container {
      padding: 0 10px;
      .detail-title {
        margin-bottom: 30px;
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 500;
        font-size: 18px;
        color: #FFFFFF;
        text-align: left;
        font-style: normal;
        text-transform: none;
      }
      .el-row {
        .el-col {
          margin-bottom: 20px;
          display: flex;
          align-items: center;
          .label {
            width: 88px;
            font-family: Source Han Sans CN, Source Han Sans CN;
            font-weight: 500;
            font-size: 14px;
            color: #86909C;
            line-height: 22px;
            text-align: right;
            font-style: normal;
            text-transform: none;
          }
          .val {
            margin-left: 12px;
            font-family: Source Han Sans CN, Source Han Sans CN;
            font-weight: 400;
            font-size: 14px;
            color: #4E5969;
            line-height: 22px;
            text-align: left;
            font-style: normal;
            text-transform: none;
          }
        }
      }
      .gd-table-container {
        height: 360px;
        .gd-pagination-parent {
          justify-content: center;
        }
      }
    }
    .dialog-form {
      .el-form-item {
        display: flex;
        align-items: center;
        .el-form-item__label {
          display: flex;
          align-items: center;
          font-family: Source Han Sans CN, Source Han Sans CN;
          font-weight: 500;
          font-size: 14px;
          color: #4E5969;
          line-height: 22px;
          text-align: right;
          font-style: normal;
          text-transform: none;
        }
        .el-form-item__content {
          display: flex;
          align-items: center;
          .el-input,
          .el-input-number {
            width: 0;
            flex: 1;
          }
          .el-cascader {
            width: 0;
            flex: 1;
            display: flex;
            align-items: center;
            border: none;
            .el-input__wrapper {
              box-shadow: none !important;
            }
          }
          .el-textarea {
            .el-textarea__inner {
              color: #fff;
              background: #DDE2ED !important;
              border: none;
              box-shadow: none;
            }
          }
          .el-button {
            color: #fff;
            background: #DDE2ED !important;
            border: none;
            box-shadow: none;
          }
          .gd-date-picker {
            background: #DDE2ED;
          }
          .gd-date-picker-popper {
            background: #DDE2ED !important;
            .el-picker-panel {
              .el-picker-panel__body-wrapper {
                .el-picker-panel__body {
                  background: #DDE2ED !important;
                  .el-date-picker__time-header {
                    .el-date-picker__editor-wrap {
                      .el-time-panel {
                        background: #DDE2ED !important;
                        // 将 time-picker-popper 的样式移到这里
                        .el-time-panel__header {
                          background: #DDE2ED !important;
                        }
                      }
                    }
                  }
                }
              }
            }
            .el-popper__arrow {
              &::before {
                background: #DDE2ED !important;
                border: 1px solid #DDE2ED !important;
                box-sizing: border-box;
              }
            }
          }
        }
        .el-select {
          .el-select__wrapper {
            background: #F2F3F5;
            .el-select__suffix {
              .el-icon {
                color: #4E5969;
              }
            }
          }
          .el-select__placeholder {
            &.is-transparent {
              color: #86909C !important;
            }
            &.el-select__selected-item {
              color: #383874;
            }
          }
        }
        .el-input {
          .el-input__wrapper {
            padding: 0 8px !important;
            background: #F2F3F5;
            box-shadow: none !important;
            border: none;
            .el-input__inner {
              color: #383874;
              &::placeholder {
                color: #86909C;
              }
            }
          }
          &.is-disabled {
            .el-input__wrapper {
              background-color: #F2F3F5;
            }
          }
        }
      }
    }
  }
  .el-dialog__footer {
    display: flex;
    justify-content: center;
    .el-button {
      margin-left: 16px;
      width: 96px;
      height: 36px;
      color: #161B2C;
      &:first-child {
        margin-left: 0;
      }
    }
    .save-btn {
      color: #FFFFFF;
    }
  }
}
.gd-pagination-parent {
  padding-top: 30px;
  display: flex;
  justify-content: flex-end;
  .el-pagination__total {
    font-family: Source Han Sans CN, Source Han Sans CN;
    font-weight: 400;
    font-size: 14px;
    color: #798492;
    font-style: normal;
    text-transform: none;
  }
  .btn-prev,
  .btn-next {
    background: transparent !important;
    .el-icon {
      color: #C9CDD4;
    }
  }
  .el-pager {
    li {
      margin-left: 8px;
      font-family: Nunito Sans, Nunito Sans;
      font-weight: 600;
      font-size: 14px;
      color: #4E5969;
      font-style: normal;
      text-transform: none;
      background: transparent;
      &:first-child {
        margin-left: 0;
      }
      &.is-active {
        font-family: Nunito Sans, Nunito Sans;
        font-weight: 600;
        font-size: 14px;
        color: #1C4AE5;
        font-style: normal;
        text-transform: none;
        background: rgba(28, 74, 229, 0.1);
      }
    }
  }
  .el-select {
    .el-select__wrapper {
      .el-select__placeholder {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 14px;
        color: #798492;
        font-style: normal;
        text-transform: none;
      }
      background: #F2F3F5;
      border: none;
      box-shadow: none;
    }
  }
}
.gd-confirm-custom {
  background: #1A1A2A;
  border-radius: 6px;
  border: 1px solid #2E2E46;
  .el-message-box__header {
    .el-message-box__title {
      font-family: Source Han Sans CN, Source Han Sans CN;
      font-weight: bold;
      font-size: 18px;
      color: #FFFFFF;
      line-height: 22px;
      text-align: left;
      font-style: normal;
      text-transform: none;
    }
  }
  .el-message-box__content {
    color: #D4D5D7;
  }
  .el-message-box__btns {
    display: flex;
    justify-content: center;
    .el-button {
      margin-left: 16px;
      width: 96px;
      height: 36px;
      &:first-child {
        margin-left: 0;
      }
    }
  }
}
.gd-confirm-button {
  background: #284FE3 !important;
  border-color: #284FE3 !important;
}
.gd-confirm-cancel-button {
  background: #2B2B4C !important;
  border-color: #2B2B4C !important;
  color: #FFFFFF !important;
}
.gd-search-form {
  padding-bottom: 20px;
  display: flex;
  .el-form-item__label {
    font-family: Source Han Sans CN, Source Han Sans CN;
    font-weight: 400;
    font-size: 14px;
    color: #383874;
    font-style: normal;
    text-transform: none;
    margin-right: 12px;
    padding: 0 !important;
    height: 36px;
    line-height: 36px;
  }
  .gd-select {
    .el-select__wrapper {
      background: #DDE2ED;
      width: 200px;
      height: 36px;
    }
  }
  .gd-input {
    width: 200px;
    height: 36px;
    .el-input__wrapper {
      background: #DDE2ED;
    }
    &.is-disabled {
      .el-input__wrapper {
        background-color: #DDE2ED;
      }
    }
  }
  .gd-date-picker {
    background: #DDE2ED;
  }
  .gd-date-picker-popper {
    background: #DDE2ED !important;
    .el-picker-panel {
      .el-picker-panel__body-wrapper {
        .el-picker-panel__body {
          background: #DDE2ED !important;
          .el-date-picker__time-header {
            .el-date-picker__editor-wrap {
              .el-time-panel {
                background: #DDE2ED !important;
                // 将 time-picker-popper 的样式移到这里
                .el-time-panel__header {
                  background: #DDE2ED !important;
                }
              }
            }
          }
        }
      }
    }
    .el-popper__arrow {
      &::before {
        background: #DDE2ED !important;
        border: 1px solid #DDE2ED !important;
        box-sizing: border-box;
      }
    }
  }
  .gd-search-actions {
    .el-button {
      margin-left: 20px;
      width: 80px;
      background: #FFFFFF !important;
      border: none !important;
      border-radius: 4px 4px 4px 4px;
      &:first-child {
        margin-left: 0;
      }
      .el-icon {
        font-size: 16px;
        color: #383874;
      }
    }
    .search-btn {
      background: #383874 !important;
      .el-icon {
        color: #fff;
      }
    }
  }
  .el-form-item {
    margin-left: 30px;
    margin-bottom: 0;
    padding: 0;
    display: flex;
    align-items: center;
    &:first-child {
      margin-left: 0;
    }
  }
  .el-date-editor {
    width: 240px;
    height: 36px;
  }
  .el-button {
    width: 96px;
    height: 36px;
  }
}
.gd-dialog-form {
  .el-form-item {
    margin-left: 20px;
  }
  .el-form-item__label {
    font-family: Source Han Sans CN, Source Han Sans CN;
    font-weight: 500;
    font-size: 14px;
    color: #C5C9D6;
    font-style: normal;
    text-transform: none;
  }
  .gd-input {
    .el-input__wrapper {
      background: #DDE2ED;
    }
    &.is-disabled {
      .el-input__wrapper {
        background-color: #DDE2ED;
      }
    }
  }
  .gd-date-picker {
    background: #DDE2ED;
  }
  .gd-date-picker-popper {
    background: #DDE2ED !important;
    .el-picker-panel {
      .el-picker-panel__body-wrapper {
        .el-picker-panel__body {
          background: #DDE2ED !important;
          .el-date-picker__time-header {
            .el-date-picker__editor-wrap {
              .el-time-panel {
                background: #DDE2ED !important;
                // 将 time-picker-popper 的样式移到这里
                .el-time-panel__header {
                  background: #DDE2ED !important;
                }
              }
            }
          }
        }
      }
    }
    .el-popper__arrow {
      &::before {
        background: #DDE2ED !important;
        border: 1px solid #DDE2ED !important;
        box-sizing: border-box;
      }
    }
  }
  .gd-search-actions {
    .el-button {
      width: 96px;
      background: transparent !important;
      border-radius: 4px 4px 4px 4px;
      border: 1px solid rgba(255, 255, 255, 0.3);
      .el-icon {
        font-size: 16px;
        color: #fff;
      }
    }
  }
  .el-input {
    width: 160px;
  }
  .el-date-editor {
    width: 226px;
  }
  .el-select {
    .el-select__wrapper {
      width: 160px;
    }
  }
}
.gd-table-container {
  padding: 12px 20px 30px 20px;
  height: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  background: #FFFFFF;
  box-shadow: 0px 8px 16px 0px rgba(128, 122, 139, 0.08);
  border-radius: 8px 8px 8px 8px;
  .gd-table-content {
    height: 0;
    flex: 1;
    display: flex;
    flex-direction: column;
  }
}
.gd-table-toolbar {
  padding-bottom: 20px;
  .el-button {
    min-width: 88px;
    border: none;
  }
  .el-button.is-disabled,
  .el-button.is-disabled:hover {
    background: #4C34FF;
    border: none;
  }
}
.gd-select-popper {
  border: none;
  background: #F2F3F5 !important;
  .el-select-dropdown {
    .el-scrollbar {
      .el-select-dropdown__wrap {
        .el-select-dropdown__list {
          .el-select-dropdown__item {
            color: #4E5969;
            &.hover {
              background-color: rgba(0, 120, 233, 0.63) !important;
              color: #383874;
            }
            &.selected {
              color: #8ac3fd !important;
              font-weight: 700;
            }
          }
        }
      }
    }
    .el-select-dropdown__empty {
      background: #F2F3F5;
      border-radius: 0px 0px 8px 8px;
    }
  }
  .el-popper__arrow::before {
    background: #F2F3F5 !important;
    border: 1px solid #F2F3F5 !important;
  }
}
applications/task-work-order/src/styles/element-ui.scss
@@ -1,736 +1,111 @@
.el-card.is-always-shadow {
    box-shadow: none;
    border: none !important;
  }
  .el-menu {
    border-right: none;
  }
  .el-message__icon,
  .el-message__content {
    display: inline-block;
  }
  .el-date-editor .el-range-input,
  .el-date-editor .el-range-separator {
    height: auto;
    overflow: hidden;
  }
  .el-dialog__wrapper {
    z-index: 2048;
  }
  .el-col {
    margin-bottom: 8px;
  }
  .el-main {
    padding: 0 !important;
  }
  .el-dropdown-menu__item--divided:before,
  .el-menu,
  .el-menu--horizontal>.el-menu-item:not(.is-disabled):focus,
  .el-menu--horizontal>.el-menu-item:not(.is-disabled):hover,
  .el-menu--horizontal>.el-sub-menu .el-sub-menu__title:hover {
    background-color: transparent;
  }
  .el-dropdown-menu__item--divided:before,
  .el-menu,
  .el-menu--horizontal>.el-menu-item:not(.is-disabled):focus,
  .el-menu--horizontal>.el-menu-item:not(.is-disabled):hover,
  .el-menu--horizontal>.el-sub-menu .el-sub-menu__title:hover {
    background-color: transparent !important;
  }
  .el-collapse-item__header {
    height: auto;
    overflow: hidden;
  }
  .el-button.is-text:not(.is-disabled):active {
    background-color: transparent;
  }
  .el-button.is-text:not(.is-disabled):focus,
  .el-button.is-text:not(.is-disabled):hover {
    background-color: transparent;
  }
  .avue-icon i,
  .avue-icon svg {
    line-height: 20px;
  }
  .avue--detail .el-form-item {
    background-color: #fafafa;
  }
  .showFullScreenDlg {
    display: flex;
    justify-content: space-between;
    .el-dialog {
      position: relative;
      padding: 0;
      overflow: hidden;
      .el-dialog__body {
        width: 100%;
        height: 100%;
        iframe {
          width: 100%;
          height: 100%;
        }
      }
      .el-dialog__header {
        height: 0;
        overflow: hidden;
        padding: 0;
        .el-dialog__headerbtn {
          position: absolute;
          left: 0;
          //top: 14px;
          .el-dialog__close {
            color: #fff;
            font-size: 35px;
            z-index: 99;
          }
        }
      }
    }
  }
  .ztzf-drawer-body-basic-container {
    .el-drawer__header {
      margin-bottom: 0;
    }
    .el-drawer__body {
      display: flex;
      flex-direction: column;
    }
  }
  .ztzf-el-card__body {
     .el-card__body {
      .avue-form{
      padding: 0;}}
  }
  .ztzf-public-general-avue-crud {
    .el-table {
      tr {
        min-height: 30px !important;
        line-height: 30px !important;
        box-sizing: border-box !important;
      }
      tr th,
      tr td {
        padding: 6px 0 !important;
        min-height: 30px !important;
        line-height: 30px !important;
        box-sizing: border-box !important;
        div.cell {
          min-height: 30px !important;
          line-height: 30px !important;
        }
      }
    }
  }
  .ztzf-layer-input {
      .el-input__wrapper {
          padding: 0 8px !important;
          // background: #2D2D2D;
      background-color: transparent !important;
          box-shadow: none !important;
          border: 1px solid #fff;
          .el-input__inner {
              color: #ffffff;
              &::placeholder {
          color: rgba(255,255,255,0.37);
              }
          }
      }
      &.is-disabled {
          .el-input__wrapper {
              background-color: #012a50;
          }
      }
  }
  .ztzf-button {
    background-color: transparent !important;
    color: #fff !important;
  }
  .ztzf-el-tree {
    background-color: transparent !important;
    --el-tree-node-hover-bg-color:transparent !important;
    color: #fff !important;
  }
  .ztzf-layer-search {
    color: #fff !important;
    background-color: transparent !important;
    .el-select__wrapper {
      background-color: transparent !important;
    }
    .el-select__popper.el-popper {
      background-color: transparent !important;
    }
    .el-popper.is-light {
      background-color: transparent !important;
    }
    .el-select__placeholder {
      &.is-transparent {
        color: rgba(255,255,255,0.37) !important;
      }
      &.is-disabled {
        color: rgba(255,255,255,0.37) !important;
      }
    }
    .el-select__selected-item {
      color: #ffffff !important;
      &.is-disabled {
        color: rgba(255,255,255,0.37) !important;
        z-index: 99;
      }
    }
  }
  .ztzf-select-popper.el-select__popper {
      border:#2D2D2D !important;
      .el-select-dropdown {
          overflow-x: hidden !important;
          .el-scrollbar {
              .el-select-dropdown__wrap {
                  background: linear-gradient(180deg, rgba(13, 53, 86, 0.85) 0%, rgba(1, 35, 80, 0.85) 100%);
                  border-radius: 0px 0px 8px 8px;
                  border: 1px solid;
          border-image: linear-gradient(180deg, #fff, #fff) 1 1;
                  overflow-x: hidden !important;
                  margin-right: 0 !important;
                  .el-select-dropdown__list {
                      padding: 0 !important;
                      .el-select-dropdown__item {
                          background: #2D2D2D !important;
                          color: white;
                          &.hover {
                              background-color: #2D2D2D !important;
                              color: white;
                          }
                          &.selected {
                              color: #fff !important;
                              font-weight: 700;
                          }
                      }
                  }
              }
          }
          .el-select-dropdown__empty {
              background: #2D2D2D;
              border-radius: 0px 0px 8px 8px;
              border: 1px solid;
              border-image: linear-gradient(180deg, #fff, #fff) 1 1;
          }
      }
      .el-popper__arrow::before {
          background: #2D2D2D !important;
          border: 1px solid #fff !important;
      }
  }
  //时间日期选择器
  .ztzf-layer-date-picker {
      box-shadow: none !important;
      background: transparent;
      border: 1px solid #fff;
      border-radius: 5px;
      .el-range-editor.is-disabled input {
          background-color: transparent !important;
      }
      .el-range-editor.is-disabled {
          background-color: transparent !important;
      }
      .el-input__wrapper {
          box-shadow: none !important;
          background-color: transparent !important;
      width: 300px;
          &.is-focus {
              box-shadow: none !important;
          }
      }
      .el-range-input {
          color: #fff;
          &::placeholder {
              color: rgba(255, 255, 255, 0.37);
          }
      }
      .el-range-separator {
          color: #ffffff;
      }
  }
  // 日期下拉框
  .ztzf-layer-date-picker-popper {
      background: #012350 !important;
      border: 1px solid !important;
      border-radius: 0px 0px 8px 8px;
      border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1 !important;
      .el-picker-panel .el-time-panel{
          background: #012350 !important;
      }
      .el-time-spinner__item {
      color: #fff !important;}
      .el-time-spinner__item:hover:not(.is-disabled):not(.is-active){
          background: transparent !important;
      }
      .el-picker-panel {
          .el-picker-panel__body-wrapper {
              .el-picker-panel__body {
                  background: #012350 !important;
                  color: #fff !important;
                  .el-date-picker__time-header {
                      border: none;
                      .el-date-picker__editor-wrap {
                          .el-time-panel {
                              background: #012350 !important;
                              border-radius: 0px 0px 8px 8px;
                              border: 1px solid !important;
                              border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1 !important;
                              // 将 time-picker-popper 的样式移到这里
                              .el-time-panel__header {
                                  background: #012350 !important;
                                  color: #ffffff !important;
                                  border-bottom: 1px solid rgba(255, 255, 255, 0.37) !important;
                              }
                              .el-time-panel__content {
                                  .el-time-spinner__item {
                                      color: #e6e6e6 !important;
                                      &:hover {
                                          background: #012350 !important;
                                      }
                                      &.active {
                                          color: #479dff !important;
                                          font-weight: bold;
                                      }
                                  }
                              }
                              .el-time-panel__footer {
                                  background: #012350 !important;
                                  border-top: 1px solid rgba(71, 157, 255, 0.3) !important;
                                  .el-button {
                                      color: #fff !important;
                                      &:hover {
                                          color: #479dff !important;
                                      }
                                  }
                                  .el-time-panel__btn.cancel {
                                      color: #fff !important;
                                      &:hover {
                                          color: #fff !important;
                                      }
                                  }
                              }
                          }
                      }
                  }
                  .el-picker-panel__content {
                      .el-date-range-picker__header {
                          .el-picker-panel__icon-btn {
                              color: #fff !important;
                              &:hover {
                                  color: var(--el-color-primary) !important;
                              }
                              .el-icon {
                                  &::before {
                                      color: inherit !important;
                                  }
                              }
                          }
                          & > div {
                              span {
                                  color: #fff !important;
                              }
                          }
                      }
                      .el-date-table {
                          th {
                              color: #fff !important;
                          }
                          td.disabled div {
                              background-color: #00193b !important;
                              color: #505050 !important;
                              cursor: not-allowed;
                          }
                          // td:hover,
                          // td.current:not(.disabled) {
                          //     background: rgba(60, 121, 202) !important;
                          //     color: white !important;
                          // }
                          td.start-date span,
                          td.start-date span,
                          td.end-date span {
                              background-color: #012350 !important;
                          }
                          td.in-range div,
                          td.in-range div:hover,
                          &.is-week-mode .el-date-table__row.current div,
                          &.is-week-mode .el-date-table__row:hover div {
                              background-color: rgba(60, 121, 202) !important;
                          }
                      }
                  }
              }
          }
      }
      .el-popper__arrow {
          &::before {
              background: #0d3556 !important;
              border: 1px solid #0d3556 !important;
              box-sizing: border-box;
          }
      }
      .el-picker-panel__sidebar {
          background: #012350;
          color: #fff;
          .el-picker-panel__shortcut {
              color: #ffffff;
          }
      }
      .el-picker-panel__body {
          .el-input.is-disabled .el-input__wrapper {
              background: #012350;
              box-shadow: 0 0 0 0.1rem#409eff;
              color: #fff;
              .el-input__inner {
                  color: #fff !important;
              }
          }
          .el-button.is-disabled,
          .el-button.is-disabled:hover {
              background: #012350;
          }
          .el-input__wrapper {
              background: #012350;
              box-shadow: 0 0 0 0.1rem#409eff;
              color: #fff;
              .el-input__inner {
                  color: #fff !important;
              }
          }
      }
      .el-picker-panel__footer {
          background: #012350;
          color: #fff;
          border-top: none;
          .el-button--small {
              box-shadow: 0 0 0 0.1rem#409eff;
              background-color: #012350;
              color: #fff;
              border: none;
              &:hover {
                  background-color: #012350;
                  color: #fff;
                  border-color: #409eff;
              }
          }
      }
  }
  .ztzf-layer-time-picker-popper {
    background: rgba(0,0,0,0.8) !important;
      border-radius: 0px 0px 8px 8px;
      border: 1px solid !important;
      border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1 !important;
      .el-popper__arrow::before {
          background: rgba(0,0,0,0.8) !important;
          border: 1px solid #479dff !important;
      }
      .el-time-panel__header {
          background: rgba(0,0,0,0.8) !important;
          color: #ffffff !important;
          border-bottom: 1px solid rgba(71, 157, 255, 0.3) !important;
      }
      .el-time-panel__content {
          // background: #012350 !important;
          .el-time-spinner__item {
              color: #e6e6e6 !important;
              &:hover {
                  background: rgba(71, 157, 255, 0.3) !important;
              }
              &.active {
                  color: #479dff !important;
                  font-weight: bold;
              }
          }
      }
      .el-time-panel__footer {
          background: rgba(0,0,0,0.8) !important;
          border-top: 1px solid rgba(71, 157, 255, 0.3) !important;
          .el-button {
              color: #fff !important;
              &:hover {
                  color: #479dff !important;
              }
          }
          .el-time-panel__btn.cancel {
              color: #fff !important;
              &:hover {
                  color: #fff !important;
              }
          }
      }
  }
.ztzf-custom-qrcode-popover {
    //background: url("@/assets/images/home/eventOverviewDetail/qrCodeBg.png") no-repeat center center !important;
    //background-size: cover !important;
    border: none !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    padding: 5px;
    min-width: 173px !important;
    height: 180px !important;
    /* 隐藏默认箭头 */
    .el-popper__arrow {
        display: none !important;
    }
    /* 调整二维码内容区域样式 */
    .qrcode-content {
        background: #fff;
        padding: 5px;
        display: flex;
        justify-content: center;
        align-items: center;
        width: 146px;
        height: 130px;
        margin-top: 12px;
        .close-btn {
            position: absolute;
            top: 3px;
            right: 3px;
            width: 20px;
            height: 20px;
            color: white;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            z-index: 100;
        }
    }
    box-shadow: none;
    border: none !important;
}
.ztzf-dialog-video {
    background: #0f1929;
    box-shadow: inset 0px -50px 50px 0px rgba(27, 148, 255, 0.13);
    border-radius: 20px 0px 0px 0px;
    border: 2px solid;
    padding: 0 !important;
    border-image: linear-gradient(180deg,
                    rgba(81, 168, 255, 0),
                    rgba(48, 111, 202, 1),
                    rgba(255, 255, 255, 1),
                    rgba(27, 148, 255, 1)) 2 2;
    .el-dialog__header {
        width: 100%;
        height: 47px;
        //padding: 0;
        margin-bottom: 14px;
        //background: url("/src/assets/images/home/inspection-vector.png") no-repeat center;
        //background-size: 100% 100%;
        font-weight: bold;
        font-size: 16px;
        line-height: 47px;
    }
    .el-dialog__title {
        width: 112px;
        font-family: Segoe UI, Segoe UI;
        font-weight: bold;
        font-size: 16px;
        text-shadow: 0px 0px 5px rgba(154, 218, 255, 0.6);
        text-align: left;
        font-style: normal;
        text-transform: none;
        background: linear-gradient(90deg, #fbfdff 0%, #86d4ff 100%);
        margin-left: 16px;
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
    }
    .el-dialog .el-dialog__header {
        padding: 0px !important;
        padding-left: 0px !important;
    }
    .el-scrollbar__thumb {
        background: #13c6ff !important;
    }
.el-menu {
    border-right: none;
}
  //======以下针对表格功能 增删查改样式和弹窗=========
  .ztzf-dialog-mange {
    .el-dialog__body {
      padding: 2rem 2rem 4rem;
      flex: 1;
      overflow: scroll;
      border-top: 0.1rem solid #f0f0f0;
    }
    .el-dialog__footer {
      z-index: 2;
      display: block;
      padding: 1rem 1.6rem;
      box-sizing: border-box;
      border-top: 0.1rem solid #f0f0f0;
      width: 100%;
      position: absolute;
      left: 0;
      bottom: 0;
      background-color: #fff;
      text-align: right;
    }
  }
  .ztzf-search-mange {
    height: 40px;
    .search-contain {
      display: flex;justify-content: space-between;
    }
  }
  .ztzf-form-mange {
    .el-form-item {
      width: 300px;
      .el-form-item__content {
        width: 100%;
      }
      .el-form-item__label {
        width: 120px;
      }
    }
  }
  .ztzf-form-search {
    .el-form-item {
      width: 100%;
      .el-form-item__label {
        width: 80px;
      }
      .el-form-item__content {
        display: flex;
        flex: 1;
      }
    }
  }
  .ztzf-table-mange {
    th.el-table__cell  {
      color: rgba(0, 0, 0, .85);
      background-color: #fafafa;
    }
  }
  .table-view-mange {
    width: 100%;
    border-top: 0.1rem solid #ebeef5;
    border-left: 0.1rem solid #ebeef5;
    margin-left: 0;
    margin-right: 0;
    .el-col {
      padding-left: 0;
      padding-right: 0;
      margin-bottom: 0;
      height: 32px;
      line-height: 32px;
      border-right: 0.1rem solid #ebeef5;
      text-align: center;
      align-items: center;
      .el-input__wrapper {
        box-shadow: none !important;
        border-bottom: 0.1rem solid #ebeef5;
      }
    }
    .label {
      color: #909399;
      box-sizing: border-box;
      background: #fafafa;
    }
  }
.el-message__icon,
.el-message__content {
    display: inline-block;
}
.el-date-editor .el-range-input,
.el-date-editor .el-range-separator {
    height: auto;
    overflow: hidden;
}
.el-dialog__wrapper {
    z-index: 2048;
}
.el-col {
    margin-bottom: 8px;
}
.el-main {
    padding: 0 !important;
}
.el-dropdown-menu__item--divided:before,
.el-menu,
.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus,
.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover,
.el-menu--horizontal>.el-sub-menu .el-sub-menu__title:hover {
    background-color: transparent;
}
.el-dropdown-menu__item--divided:before,
.el-menu,
.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus,
.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover,
.el-menu--horizontal>.el-sub-menu .el-sub-menu__title:hover {
    background-color: transparent !important;
}
.el-collapse-item__header {
    height: auto;
    overflow: hidden;
}
.el-button.is-text:not(.is-disabled):active {
    background-color: transparent;
}
.el-button.is-text:not(.is-disabled):focus,
.el-button.is-text:not(.is-disabled):hover {
    background-color: transparent;
}
.avue-icon i,
.avue-icon svg {
    line-height: 20px;
}
.avue--detail .el-form-item {
    background-color: #fafafa;
}
.showFullScreenDlg {
    display: flex;
    justify-content: space-between;
    .el-dialog {
        position: relative;
        padding: 0;
        overflow: hidden;
        .el-dialog__body {
            width: 100%;
            height: 100%;
            iframe {
                width: 100%;
                height: 100%;
            }
        }
        .el-dialog__header {
            height: 0;
            overflow: hidden;
            padding: 0;
            .el-dialog__headerbtn {
                position: absolute;
                left: 0;
                //top: 14px;
                .el-dialog__close {
                    color: #fff;
                    font-size: 35px;
                    z-index: 99;
                }
            }
        }
    }
}
applications/task-work-order/src/styles/sidebar.scss
@@ -1,6 +1,5 @@
.avue-sidebar {
  width: $sidebar_width;
  width: 179px;
  height: 100%;
  user-select: none;
  position: relative;
@@ -11,12 +10,13 @@
  box-sizing: border-box;
  box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
  .el-scrollbar__wrap {
    overflow-x: hidden;
  }
  .avue-menu {
    height: calc(100% - #{$top_height});
    height: calc(100% - 42px);
  }
  &--tip {
@@ -36,40 +36,83 @@
    background-color: rgba(0, 0, 0, .4);
  }
  .el-menu-item, .el-sub-menu__title {
  .el-menu-item {
  }
  .el-sub-menu {
    .el-sub-menu__title {
    }
    .el-menu-item {
    }
    &.is-active {
      .el-sub-menu__title {
        i,
        span {
          color: #384CFF !important;
        }
        span {
          font-family: Source Han Sans CN, Source Han Sans CN;
          font-weight: 500;
          font-size: 14px;
          line-height: 56px;
          font-style: normal;
          text-transform: none;
        }
      }
    }
  }
  .el-menu-item,
  .el-sub-menu__title {
    i {
      margin-right: 5px;
    }
    i, span {
    i,
    span {
      color: hsla(0, 0%, 100%, .7);
    }
    &:hover {
      background: transparent;
      i, span {
        color: #fff;
      i,
      span {
        color: #384CFF;
      }
    }
    &.is-active {
      background-color: transparent !important;
      &:before {
        content: '';
        top: 0;
        top: 10px;
        left: 0;
        bottom: 0;
        width: 4px;
        background: var(--el-color-primary);
        bottom: 10px;
        width: 3px;
        background: #384CFF;
        position: absolute;
        border-radius: 0 6px 6px 0;
      }
      background-color: rgba(0, 0, 0, .8);
      i,
      span {
        color: #384CFF !important;
      }
      i, span {
        color: #fff;
      span {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 500;
        font-size: 14px;
        line-height: 56px;
        font-style: normal;
        text-transform: none;
      }
    }
  }
}
}
applications/task-work-order/src/styles/tags.scss
@@ -1,39 +1,55 @@
.avue-tags {
  user-select: none;
  position: relative;
  padding: 0 10px;
  margin-bottom: 10px;
  box-sizing: border-box;
  border-top: 1px solid #f6f6f6;
  background-color: #fff;
  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .05);
  .el-tabs--card > .el-tabs__header {
  .el-tabs--card>.el-tabs__header {
    margin: 0;
  }
  .el-tabs--card > .el-tabs__header .el-tabs__nav,
  .el-tabs--card > .el-tabs__header .el-tabs__item,
  .el-tabs--card > .el-tabs__header {
  .el-tabs--card>.el-tabs__header .el-tabs__nav,
  .el-tabs--card>.el-tabs__header .el-tabs__item,
  .el-tabs--card>.el-tabs__header {
    border: none;
  }
  .el-tabs--card > .el-tabs__header .el-tabs__item:first-child {
  .el-tabs--card>.el-tabs__header .el-tabs__item:first-child {
    border-left-width: 1px
  }
  .el-tabs--card > .el-tabs__header .el-tabs__item {
    margin: 0 3px;
    height: 40px;
    line-height: 40px;
    font-size: 13px;
    font-weight: normal;
    color: #ccc;
  .el-tabs--card>.el-tabs__header .el-tabs__item {
    padding: 0 20px !important;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-left: 10px;
    min-width: 100px;
    height: 36px;
    font-family: PangMen;
    font-weight: 400;
    font-size: 16px;
    color: #4E5969;
    text-align: center;
    font-style: normal;
    text-transform: none;
    background: #DBDFED;
    border-radius: 3px 3px 3px 3px;
    &.is-active {
      color: var(--el-color-primary);
      border-bottom: 3px solid var(--el-color-primary);
      padding: 0 !important;
      padding-left: 20px !important;
      color: #FFFFFF;
      background: #5B6BFF;
      .is-icon-close {
        margin: 0 10px;
      }
    }
    &:first-child {
      margin-left: 0;
    }
  }
@@ -98,4 +114,4 @@
    padding: 1px 0 0 15px;
    box-sizing: border-box;
  }
}
}
applications/task-work-order/src/styles/top.scss
@@ -1,7 +1,5 @@
.avue-top {
  position: relative;
  background-color: #fff;
  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15);
  color: rgba(0, 0, 0, .65);
  font-size: 28px;
  height: $top_height;
@@ -10,7 +8,9 @@
  white-space: nowrap;
  .el-menu-item {
    i, span {
    i,
    span {
      font-size: 13px;
    }
  }
@@ -69,7 +69,7 @@
.top-bar__left,
.top-bar__right {
  height: $top_height;
  height: 100%;
  position: absolute;
  top: 0;
@@ -79,7 +79,15 @@
}
.top-bar__left {
  left: 10px;
  display: flex;
  align-items: center;
  font-family: PangMen;
  font-weight: 400;
  font-size: 30px;
  color: #161B2C;
  text-align: left;
  font-style: normal;
  text-transform: none;
}
.top-bar__right {
@@ -136,4 +144,4 @@
    font-weight: bold;
    color: #fff;
  }
}
}
applications/task-work-order/src/utils/cesium/DrawPolygon.js
File was renamed from applications/task-work-order/src/views/layerManagement/components/utils.js
@@ -1,7 +1,8 @@
import * as Cesium from 'cesium'
import _, { cloneDeep, throttle } from 'lodash'
import * as turf from '@turf/turf'
import { boxTransformScale } from '@/utils/turfFunc'
import { ElMessage } from 'element-plus'
import { flyVisual, getPointPositionsHeight } from '@/utils/cesium/mapUtil'
/**
 * 多边形绘制与编辑工具类
@@ -14,8 +15,6 @@
 */
export class DrawPolygon {
    constructor() {
        // 是否绘制
        this.whetherToDraw =false
        // 图斑预览模式标记
        this.isPureSpotPreview = false
        // 是否删除测区
@@ -39,6 +38,8 @@
        // 存储端点的 DataSource
        this.editPolygonDataSource = null
        this.editPolygonPointDataSource = null
        // 存储中点(边中点)的 DataSource:用于编辑态插入新端点
        this.editPolygonMidPointDataSource = null
        // 鼠标事件处理器
        this.handler = null
        // 右键菜单 DOM
@@ -63,15 +64,17 @@
        this.delPoint = this.delPoint.bind(this)
        // 外部订阅者
        this.listeners = []
    }
    // 实体命名常量
    static ENTITY_NAMES = {
        POLYGON: '区域-面',
        POINT: '区域-端点',
        // 编辑态中点(用于快速插入新端点)
        MID_POINT: '区域-中点',
        POLYGON_ID: 'planar-route-polygon',
        POINT_ID_PREFIX: 'planar-route-point',
        MID_POINT_ID_PREFIX: 'planar-route-mid-point',
    }
    // 颜色常量
@@ -98,10 +101,20 @@
    // 编辑图斑
    editThePatch(data) {
        this.isPreviewMode = data
    }
    // 绘制
    drawTheArea(data){
        this.whetherToDraw = data
        // 关闭编辑能力时隐藏端点/中点,并清空中点,避免残留交互
        if (!this.isPreviewMode) {
            this.editPolygonPointDataSource && (this.editPolygonPointDataSource.entities.show = false)
            this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = false)
            this.editPolygonMidPointDataSource?.entities?.removeAll?.()
            return
        }
        if (this.editingMode) {
            this.editPolygonPointDataSource && (this.editPolygonPointDataSource.entities.show = true)
            this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = true)
            this.rebuildEditPoints()
            this.rebuildMidPoints()
        }
    }
    // 删除测区
    deleteTheArea(data) {
@@ -110,12 +123,6 @@
    // 开始绘制
    startDrawing() {
        if (!this.whetherToDraw) {
            return;
          }
        this.drawingMode = true
        this.curPolygon = new Cesium.PolygonHierarchy()
@@ -130,9 +137,16 @@
            this.viewer?.dataSources.add(this.editPolygonPointDataSource)
        }
        // 中点数据源:编辑模式下用于“边上插点”
        if (!this.editPolygonMidPointDataSource) {
            this.editPolygonMidPointDataSource = new Cesium.CustomDataSource('editPolygonMidPointDataSource')
            this.viewer?.dataSources.add(this.editPolygonMidPointDataSource)
        }
        // 清空之前的点
        this.editPolygonDataSource?.entities.removeAll()
        this.editPolygonPointDataSource?.entities.removeAll()
        this.editPolygonMidPointDataSource?.entities.removeAll()
    }
    // 创建多边形实体(含边界线)
@@ -177,10 +191,162 @@
                ind: pointIndex, // 索引记录
            },
        })
    }
    createMidPointEntity(startInd, position) {
        // 使用 CallbackProperty 让中点位置随端点拖拽实时计算
        // startInd 表示该中点属于边:(startInd) -> (startInd + 1)
        const updatePosition = () => {
            const positions = this.curPolygon?.positions
            if (!positions || positions.length < 2) return position
            const n = positions.length
            const p1 = positions[startInd]
            const p2 = positions[(startInd + 1) % n]
            if (!p1 || !p2) return position
            return Cesium.Cartesian3.midpoint(p1, p2, new Cesium.Cartesian3())
        }
        this.editPolygonMidPointDataSource.entities.add({
            name: DrawPolygon.ENTITY_NAMES.MID_POINT,
            id: `${DrawPolygon.ENTITY_NAMES.MID_POINT_ID_PREFIX}${startInd}`,
            position: new Cesium.CallbackProperty(updatePosition, false),
            point: {
                pixelSize: 10,
                color: Cesium.Color.fromCssColorString('rgba(255, 255, 255, .7)'),
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
                disableDepthTestDistance: Number.POSITIVE_INFINITY,
            },
            customData: {
                startInd,
            },
        })
    }
    rebuildEditPoints() {
        // 插入/删除端点后:端点实体 id 与 customData.ind 需要全量重建,保证索引与 positions 一致
        if (!this.isPreviewMode) return
        if (!this.editPolygonPointDataSource || !this.curPolygon?.positions) return
        this.editPolygonPointDataSource.entities.removeAll()
        this.curPolygon.positions.forEach((position, index) => {
            this.editPolygonPointDataSource.entities.add({
                name: DrawPolygon.ENTITY_NAMES.POINT,
                id: `${DrawPolygon.ENTITY_NAMES.POINT_ID_PREFIX}${index}`,
                position: position.clone(),
                point: {
                    pixelSize: 14,
                    color: Cesium.Color.WHITE,
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
                    disableDepthTestDistance: Number.POSITIVE_INFINITY,
                },
                customData: {
                    ind: index,
                },
            })
        })
    }
    rebuildMidPoints() {
        // 仅在“顶点数量变化/切换编辑态”时维护中点实体数量
        // 拖拽过程中中点位置由 CallbackProperty 自动更新,不需要重建
        if (!this.isPreviewMode) return
        if (!this.editPolygonMidPointDataSource || !this.curPolygon?.positions) return
        const positions = this.curPolygon.positions
        const n = positions.length
        const entities = this.editPolygonMidPointDataSource.entities
        if (!this.editingMode || n < 2) {
            entities.removeAll()
            return
        }
        const neededIds = new Set()
        for (let i = 0; i < n; i += 1) {
            const id = `${DrawPolygon.ENTITY_NAMES.MID_POINT_ID_PREFIX}${i}`
            neededIds.add(id)
            // 缺少则补齐:保证每条边都有一个中点实体
            if (!entities.getById(id)) {
                const p1 = positions[i]
                const p2 = positions[(i + 1) % n]
                if (!p1 || !p2) continue
                const mid = Cesium.Cartesian3.midpoint(p1, p2, new Cesium.Cartesian3())
                this.createMidPointEntity(i, mid)
            }
        }
        entities.values.slice().forEach(entity => {
            if (entity?.name !== DrawPolygon.ENTITY_NAMES.MID_POINT) return
            // 多余则移除:例如删除端点导致边数量减少
            if (!neededIds.has(entity.id)) {
                entities.remove(entity)
            }
        })
    }
    insertPointFromMidPoint(midPointEntity) {
        // 点击中点:在对应边上插入一个新端点(白点),并立即进入拖拽态
        if (!this.isPreviewMode) return
        if (!this.editingMode) return
        if (!midPointEntity?.customData) return
        const startInd = midPointEntity.customData.startInd
        // 中点位置可能是 CallbackProperty,需要取当前时刻的值
        const positionProperty = midPointEntity.position
        const entityPosition = positionProperty?.getValue
            ? positionProperty.getValue(Cesium.JulianDate.now())
            : positionProperty
        if (!entityPosition) return
        // 插入到 startInd 与 startInd+1 之间
        const insertIndex = Math.min(Math.max(startInd + 1, 0), this.curPolygon.positions.length)
        this.curPolygon.positions.splice(insertIndex, 0, entityPosition.clone ? entityPosition.clone() : entityPosition)
        this.rebuildEditPoints()
        this.rebuildMidPoints()
        const newPointEntity = this.editPolygonPointDataSource?.entities?.getById(
            `${DrawPolygon.ENTITY_NAMES.POINT_ID_PREFIX}${insertIndex}`
        )
        if (newPointEntity) {
            this.isDragging = true
            this.draggedEntity = newPointEntity
            this.currentDragPointPosition = this.curPolygon.positions[insertIndex]
            this.disableMapControl()
        }
        this.notify('getPolygonPositions', this.curPolygon.positions)
    }
    // 清除不在范围内的点
    removeLastInvalidPoint() {
        const posLen = this.curPolygon.positions.length
        if (posLen === 0) return
        let targetPointId = ''
        let removeCount = 0
        if (posLen === 2) {
            removeCount = 2
            targetPointId = `${DrawPolygon.ENTITY_NAMES.POINT_ID_PREFIX}0`
        } else if (posLen > 2) {
            removeCount = 1
            const pointIndex = posLen - 2
            targetPointId = `${DrawPolygon.ENTITY_NAMES.POINT_ID_PREFIX}${pointIndex}`
        }
        this.curPolygon.positions.splice(posLen - removeCount, removeCount)
        const invalidPoint = this.editPolygonPointDataSource.entities.getById(targetPointId)
        if (invalidPoint) {
            this.editPolygonPointDataSource.entities.remove(invalidPoint)
        }
        if (this.curPolygon.positions.length === 0 && this.polygonEntity) {
            this.editPolygonDataSource.entities.remove(this.polygonEntity)
            this.polygonEntity = null
        }
    }
    // 添加一个点
    addPosition(position, isAdd = true) {
        // 第一个点要重复压入一次,形成动态绘制效果
@@ -211,12 +377,18 @@
        const pickedEntity = this.viewer.scene.pick(movement.position)?.id
        const isPoint = pickedEntity?.name === DrawPolygon.ENTITY_NAMES.POINT
        const isMidPoint = pickedEntity?.name === DrawPolygon.ENTITY_NAMES.MID_POINT
        if (pickedEntity && isPoint) {
            this.isDragging = true
            this.draggedEntity = pickedEntity
            this.currentDragPointPosition = this.curPolygon.positions[this.draggedEntity?.customData.ind]
            this.disableMapControl() // 禁止地图交互
            return
        }
        if (pickedEntity && isMidPoint) {
            this.insertPointFromMidPoint(pickedEntity)
        }
    }
@@ -249,6 +421,7 @@
            this.isDragging = false
            this.draggedEntity = null
            this.enableMapControl()
            this.rebuildMidPoints()
        }
    }
@@ -262,7 +435,7 @@
        // 编辑模式下,拖拽点实时更新
        if (this.editingMode && this.draggedEntity?.customData) {
            this.draggedEntity.position = cartesian
            this.curPolygon.positions[this.draggedEntity?.customData.ind] = cartesian
            this.curPolygon.positions[this.draggedEntity?.customData.ind] = cartesian
        }
        // 绘制模式下,实时更新最后一个点
@@ -293,27 +466,28 @@
    // 鼠标左键点击
    handleLeftClick(click) {
        this.removeMenuPopup()
        const pickedAllEntity = this.viewer.scene.drillPick(click.position).filter(i => i.id)
        const isStartPoint = pickedAllEntity.find(i => i.id.name === '起飞点')
        const isPolygonPoint = pickedAllEntity.find(i => i.id.name === DrawPolygon.ENTITY_NAMES.POINT)
        const isPolygon = pickedAllEntity.find(i => i.id.name === DrawPolygon.ENTITY_NAMES.POLYGON)
        if (this.drawingMode && !this.whetherToDraw) {
            return; // 阻断后续绘制逻辑
          }
        if (isStartPoint) return
        // 如果不是绘制模式
        if (!this.drawingMode) {
            if (!isPolygon) {
                this.editingMode = false
                this.editPolygonPointDataSource.entities.show = false
                this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = false)
                return
            }
            if (isPolygon) {
                this.editingMode = true
                this.editPolygonPointDataSource.entities.show = true
                this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = true)
                this.rebuildMidPoints()
            }
            return
        }
@@ -334,13 +508,22 @@
        const cartesian = this.viewer.scene.pickPosition(click.position)
        if (!cartesian) return
        let arr = [...this.curPolygon.positions]
        arr.pop()
        // 校验多边形是否自交
        if (arr.length > 2 && !this.curDragPointIsValid([...arr, cartesian])) {
            ElMessage.warning('测区不支持交叉绘制,请重新绘制')
            return
        }
        this.addPosition(cartesian)
    }
    // 鼠标右键点击(弹出菜单)
    handleRightClick(click) {
    handleRightClick(click) {
        const that = this
        if (that.drawingMode) return
        that.removeMenuPopup()
        const pickedAllEntity = that.viewer.scene.drillPick(click.position).filter(i => i.id)
@@ -351,16 +534,14 @@
        } = click
        let pickedEntity, tooltipEvent, menuType
        if (isPolygon) {
            pickedEntity = isPolygon
            tooltipEvent = that.delPolygon
            menuType = 'polygon'
        } else if (isEditPoint) {
        if (isEditPoint) {
            pickedEntity = isEditPoint
            tooltipEvent = that.delPoint
            menuType = 'edit-point'
        } else if (isPolygon) {
            pickedEntity = isPolygon
            tooltipEvent = that.delPolygon
            menuType = 'polygon'
        }
        if (pickedEntity && this.isDeleteTheArea) {
@@ -385,6 +566,11 @@
            this.editPolygonPointDataSource.entities.removeAll()
            this.editPolygonPointDataSource = null
        }
        if (this.editPolygonMidPointDataSource) {
            this.editPolygonMidPointDataSource.entities.removeAll()
            this.editPolygonMidPointDataSource = null
        }
        this.editingMode = false
        this.polygonEntity = null
        this.curPolygon = null
@@ -398,6 +584,8 @@
            this.drawingMode = false
            this.editingMode = true
            this.editPolygonPointDataSource.entities.show = true
            this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = true)
            this.rebuildMidPoints()
            if (!this.curDragPointIsValid(this.curPolygon.positions)) {
                this.isShowWaringTip = true
@@ -421,7 +609,12 @@
        this.notify('getPolygonPositions', [])
        this.startDrawing()
    }
    // 删除图斑
    delSpot() {
        this.removeEntities()
        this.isPreviewMode = true
        this.startDrawing()
    }
    // 删除端点
    delPoint() {
        if (this.curPolygon.positions.length <= 3) {
@@ -431,13 +624,9 @@
        if (!this.delPolygonPoint) return
        this.curPolygon.positions.splice(this.delPolygonPoint.customData.ind, 1)
        this.editPolygonPointDataSource.entities.remove(this.delPolygonPoint)
        this.removeMenuPopup()
        // 更新剩余点索引
        this.editPolygonPointDataSource.entities.values.forEach((item, index) => {
            item.customData.ind = index
        })
        this.rebuildEditPoints()
        this.rebuildMidPoints()
        this.notify('getPolygonPositions', this.curPolygon.positions)
    }
@@ -454,7 +643,6 @@
        menuPopup.id = 'planarPolygonEditMenu'
        menuPopup.className = 'planar-polygon-edit-menu'
        const menuItems =
            type === 'polygon'
                ? [{ title: '删除测区', class: 'del-planar-polygon' }]
@@ -466,7 +654,6 @@
            titleDiv.className = item.class
            menuPopup.appendChild(titleDiv)
        })
        this.isPreviewMode = true
        menuPopupVBox.appendChild(menuPopup)
        return menuPopupVBox
@@ -522,23 +709,18 @@
        return intersections.features.length === 0
    }
    // 初始化已有多边形
    initPolygon(viewer, positions, isPurePreview = false) {
    async initPolygon(viewer, positions, isPurePreview = false) {
        this.initHandler(viewer)
        if (this.whetherToDraw) {
            this.startDrawing();
          } else {
            // 新增:不允许绘制时,确保绘制模式为 false,避免误触发
            this.drawingMode = false;
          }
        this.isPureSpotPreview = isPurePreview
        this.startDrawing()
        let newPosition = positions.map(item => {
            return Cesium.Cartesian3.fromDegrees(Number(item.lng), Number(item.lat), Number(item.height))
            return Cesium.Cartesian3.fromDegrees(Number(item.lng), Number(item.lat), Number(item?.height || 0))
        })
        // 预览航线的时候调用
        if (!this.isPureSpotPreview) {
            this.notify('getPolygonPositions', newPosition)
        }
        newPosition.forEach(item => {
            this.addPosition(item, false)
@@ -555,17 +737,20 @@
            duration: 0.5,
        })
        let pointList = await getPointPositionsHeight(positions, viewer)
        flyVisual({ positionsData: pointList.map(item => [item.lng, item.lat, item.ASL]), viewer })
        this.drawingMode = false
        this.editingMode = true
        this.editPolygonPointDataSource && (this.editPolygonPointDataSource.entities.show = true)
        this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = true)
        this.rebuildMidPoints()
    }
    // 初始化事件处理器
    initHandler(viewer) {
        this.viewer = viewer
        if(this.whetherToDraw){
            this.startDrawing()
        }
        this.startDrawing()
        if (!this.handler) {
            this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
applications/task-work-order/src/utils/cesium/Material/index.js
@@ -54,6 +54,7 @@
    this._opacity = undefined
    this._alphaPower = undefined
    this._speed = undefined
    this._xRepeatCount = undefined
    this.color = options.color || Cesium.Color.fromBytes(0, 255, 255, 255)
    this.glowPower = .25
@@ -64,6 +65,9 @@
    this.opacity = options.opacity || 0.5
    this.alphaPower = options.alphaPower || 1.5
    this.speed = options.speed || 5
    this.xRepeatCount = options.xRepeatCount || 1
  }
  get isConstant () {
@@ -132,15 +136,17 @@
      color: new Cesium.Color(1.0, 1.0, 0.0, 0.7),
      bgColor: new Cesium.Color(0.0, 1.0, 0.0, 0.0),
      speed: 5,
      globalAlpha: 1.0
      globalAlpha: 1.0,
      xRepeatCount: 1.0
    },
    source: `
          uniform vec4 bgColor;
          uniform vec4 color;
          uniform float speed;
          uniform float globalAlpha;
          uniform float xRepeatCount; // 新增的 uniform 在着色器中
          czm_material czm_getMaterial(czm_materialInput materialInput) {
           czm_material czm_getMaterial(czm_materialInput materialInput) {
              czm_material material = czm_getDefaultMaterial(materialInput);
              vec2 st = materialInput.st;
              float time = fract(czm_frameNumber * speed / 1000.0);
@@ -150,7 +156,7 @@
                  colorMars3D = vec3(1.0);
              }
              material.alpha = color.a * 1.5 * smoothstep(0.0, 1.0, fract(st.s - time));
              material.alpha = color.a * 1.5 * smoothstep(0.0, 1.0, fract(st.s * xRepeatCount - time)); // 使用 xRepeatCount 来调整纹理重复
              material.diffuse = max(colorMars3D.rgb * material.alpha, colorMars3D.rgb);
              if (material.alpha < bgColor.a) {
@@ -182,6 +188,8 @@
    result.color = Cesium.Property.getValueOrUndefined(this._color, time)
    result.globalAlpha = Cesium.Property.getValueOrUndefined(this._opacity, time)
    result.speed = Cesium.Property.getValueOrUndefined(this._speed, time)
    result.xRepeatCount = Cesium.Property.getValueOrUndefined(this._xRepeatCount, time)
    return result
  }
@@ -191,7 +199,8 @@
      (other instanceof LineTrailMaterial &&
        Cesium.Property.equals(this._color, other._color) &&
        Cesium.Property.equals(this._opacity, other._opacity) &&
        Cesium.Property.equals(this._speed, other._speed))
        Cesium.Property.equals(this._speed, other._speed) &&
        Cesium.Property.equals(this._xRepeatCount, other._xRepeatCount))
    )
  }
}
@@ -200,6 +209,7 @@
  color: Cesium.createPropertyDescriptor('color'),
  opacity: Cesium.createPropertyDescriptor('opacity'),
  speed: Cesium.createPropertyDescriptor('speed'),
  xRepeatCount: Cesium.createPropertyDescriptor('xRepeatCount'),
})
applications/task-work-order/src/utils/cesium/common.js
@@ -131,22 +131,22 @@
    let newLat = lat
    switch (direction) {
        case 'W': // 向前(heading 方向)
        case 'KeyW': // 向前(heading 方向)
            newLon += moveStep * Math.sin(headingRad)
            newLat += moveStep * Math.cos(headingRad)
            break
        case 'S': // 向后(反方向)
        case 'KeyS': // 向后(反方向)
            newLon -= moveStep * Math.sin(headingRad)
            newLat -= moveStep * Math.cos(headingRad)
            break
        case 'D': // 向右(heading + 90°)
        case 'KeyD': // 向右(heading + 90°)
            newLon += moveStep * Math.cos(headingRad)
            newLat -= moveStep * Math.sin(headingRad)
            break
        case 'A': // 向左(heading - 90°)
        case 'KeyA': // 向左(heading - 90°)
            newLon -= moveStep * Math.cos(headingRad)
            newLat += moveStep * Math.sin(headingRad)
            break
applications/task-work-order/src/utils/cesium/compressImage.js
New file
@@ -0,0 +1,55 @@
/**
 * @description: 图片压缩
 * @param {*} src 图片地址
 * @param {*} quality 压缩质量
 * @param {*} maxWidth 最大宽度
 * @param {*} callback 回调函数
 * @return {*}
 */
function compressImage(src, quality, maxWidth, callback) {
    const img = new Image()
    img.setAttribute('crossOrigin', 'Anonymous')
    img.onload = function () {
        const canvas = document.createElement('canvas')
        const ctx = canvas.getContext('2d')
        let width = img.width
        let height = img.height
        if (width > maxWidth) {
            height *= maxWidth / width
            width = maxWidth
        }
        canvas.width = width
        canvas.height = height
        ctx.drawImage(img, 0, 0, width, height)
        const newData = canvas.toDataURL('image/jpeg', quality)
        // 使用atob()将base64转换为二进制字符串
        const binaryString = window.atob(newData.split(',')[1])
        const mimeString = newData.split(',')[0].split(':')[1].split(';')[0]
        // 创建Blob对象
        const array = []
        for (let i = 0; i < binaryString.length; i++) {
            array.push(binaryString.charCodeAt(i))
        }
        const blob = new Blob([new Uint8Array(array)], { type: mimeString })
        callback(blob)
    }
    img.src = src
    return src
}
export default function imageCompress(src, quality, maxWidth) {
    // 使用例子
    return compressImage(src, quality, maxWidth, function (blob) {
        // 创建一个新的图片URL
        const imageUrl = URL.createObjectURL(blob)
        // 可以在这里使用imageUrl,比如设置为img元素的src
        // document.getElementById('your-image-element').src = imageUrl;
        // 当不需要这个URL的时候,释放它
        URL.revokeObjectURL(imageUrl)
    })
}
applications/task-work-order/src/utils/cesium/compressImage2.js
New file
@@ -0,0 +1,46 @@
/*
 * @Author: GuLiMmo 2820890765@qq.com
 * @Date: 2024-05-21 10:14:11
 * @LastEditors: GuLiMmo 2820890765@qq.com
 * @LastEditTime: 2024-05-21 10:20:34
 * @FilePath: /bigScreen/src/utils/cesium/compressImage2.js
 * @Description:
 * Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
 */
export default function compressImage(
    src,
    maxWidth,
    maxHeight,
    outputFormat = 'image/jpeg',
    quality = 0.7
) {
    return new Promise((resolve, reject) => {
        const image = new Image()
        image.setAttribute('crossOrigin', 'Anonymous')
        image.src = src
        image.onload = () => {
            let width = image.width
            let height = image.height
            if (width > maxWidth) {
                height *= maxWidth / width
                width = maxWidth
            }
            if (height > maxHeight) {
                width *= maxHeight / height
                height = maxHeight
            }
            const canvas = document.createElement('canvas')
            canvas.width = width
            canvas.height = height
            const ctx = canvas.getContext('2d')
            ctx.drawImage(image, 0, 0, width, height)
            const newDataUrl = canvas.toDataURL(outputFormat, quality)
            resolve(newDataUrl)
        }
        image.onerror = reject
    })
}
applications/task-work-order/src/utils/cesium/createRouteLine.js
@@ -2,17 +2,16 @@
import { Decimal } from 'decimal.js'
import _, { cloneDeep, throttle } from 'lodash'
import { v4 as uuidv4 } from 'uuid'
import { render } from 'vue'
import { analysisPointLineKmz, handlePointListForKmz } from '@/views/newRoutePlan/Waypoint/pointWayLineUtils'
import HistoryDronePopup from '@/components/DeviceJobDetails/HistoryDronePopup.vue'
import { getNewPolygonData } from '@/views/RoutePlan/PlanarAirLine/planarRouteUtils'
import {
    getNewPolygonData,
} from '@/views/newRoutePlan/routeUtils'
import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz'
import { ArrowLineMaterialProperty, LineTrailMaterial, PolylineGlowMaterial, } from '@/utils/cesium/Material'
import { ArrowLineMaterialProperty, LineTrailMaterial, PolylineGlowMaterial } from '@/utils/cesium/Material'
import CreateFrustum from '@/utils/cesium/frustum/CreateFrustum'
import { ElMessage } from 'element-plus'
@@ -23,61 +22,21 @@
import aircraftGltf from '@/assets/gltf/aircraft.gltf'
import newNumPoint from '@/assets/images/newStartPoint.png'
import {
    getPolyLine,
} from '@/views/RoutePlan/PointAirLine/pointWayLineUtils'
import { getPolyLine } from '@/views/newRoutePlan/Waypoint/pointWayLineUtils'
import store from '@/store/index'
import newStartPoint from '@/assets/images/newStartPoint.png'
import newStartPointMore from '@/assets/images/newStartPointMore.png'
import {
    arrowLineMaterialProperty, arrowLineMaterialPropertyGray, arrowLineMaterialPropertyGrayT,
    arrowLineMaterialPropertyGreen,
    arrowLineMaterialPropertyOrange,
} from '@/hooks/useMorePointLine/useMorePointLine'
let arrowLineMaterialProperty = new ArrowLineMaterialProperty({
    color: new Cesium.Color(128 / 255, 215 / 255, 255 / 255, 1),
    directionColor: new Cesium.Color(1, 1, 1, 1),
    outlineColor: new Cesium.Color(1, 1, 1, 1),
    outlineWidth: 0,
    speed: 5
})
let arrowLineMaterialPropertyOrange = new ArrowLineMaterialProperty({
    color: new Cesium.Color(255 / 255, 185 / 255, 58 / 255, 1),
    directionColor: new Cesium.Color(1, 1, 1, 1),
    outlineColor: new Cesium.Color(1, 1, 1, 1),
    outlineWidth: 0,
    speed: 5,
})
let arrowLineMaterialPropertyGreen = new ArrowLineMaterialProperty({
    color: new Cesium.Color(6 / 255, 217 / 255, 87 / 255, 1),
    directionColor: new Cesium.Color(1, 1, 1, 1),
    outlineColor: new Cesium.Color(1, 1, 1, 1),
    outlineWidth: 0,
    speed: 5,
})
// 记录index
let indexLog = 0
// 颜色
function getLineColor(lineNumber) {
    // 颜色顺序: 蓝(0), 黄(1), 绿(2)
    const colors = [arrowLineMaterialProperty, arrowLineMaterialPropertyOrange, arrowLineMaterialPropertyGreen]
    // 计算颜色索引 (从0开始)
    const colorIndex = (lineNumber - 1) % 3
    // 返回对应颜色
    return colors[colorIndex]
}
let runningLineMaterial = new LineTrailMaterial({
    color: Cesium.Color.fromCssColorString('#1FFF69'),
    opacity: 1,
    speed: 10,
})
let runningTextColor = Cesium.Color.fromCssColorString('#1FFF69')
let runningTextOffset = new Cesium.Cartesian2(0, -32)
let pendingLineMaterial = new PolylineGlowMaterial({
    color: Cesium.Color.fromCssColorString('#FFB81D'),
})
let pendingTextColor = Cesium.Color.fromCssColorString('#FFB81D')
let pendingTextOffset = new Cesium.Cartesian2(0, 32)
let MapPopUpBox = HistoryDronePopup
function getZoomFactor (camerasInfo, type) {
@@ -95,10 +54,108 @@
    return zoom_factor
}
// 记录index
let indexLog = 0
// 颜色
function getPendingExecutionLineColor (lineNumber) {
    // 颜色顺序: 蓝(灰), 更灰
    const colors = [arrowLineMaterialPropertyGray, arrowLineMaterialPropertyGrayT]
    // 计算颜色索引 (从0开始)
    const colorIndex = (lineNumber - 1) % 2
    // 返回对应颜色
    return colors[colorIndex]
}
function getLineColor (lineNumber) {
    // 颜色顺序: 蓝(0), 黄(1), 绿(2)
    const colors = [arrowLineMaterialProperty, arrowLineMaterialPropertyOrange, arrowLineMaterialPropertyGreen]
    // 计算颜色索引 (从0开始)
    const colorIndex = (lineNumber - 1) % 3
    // 返回对应颜色
    return colors[colorIndex]
}
function getBase64Image (imgUrl) {
    const img = new Image()
    const canvas = document.createElement('canvas')
    const ctx = canvas.getContext('2d')
    return new Promise(resolve => {
        img.onload = function () {
            canvas.width = img.width
            canvas.height = img.height
            ctx.drawImage(img, 0, 0)
            const base64 = canvas.toDataURL('image/png')
            resolve(base64)
        }
        img.src = imgUrl
    })
}
async function createCombinedSVG (index, billboardImageUrl) {
    const base64Image = await getBase64Image(billboardImageUrl)
    const svg = `
      <svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
        <image href="${base64Image}" x="10" y="10" width="80" height="80"/>
        <text x="50" y="54" font-size="26" fill="white" text-anchor="middle" dominant-baseline="middle">
          ${index + 1}
        </text>
      </svg>
    `
    return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`
}
function findHeightByCoordinates (data, targetLng, targetLat) {
    // 展平二维数组,然后查找第一个匹配的点
    const allPoints = data.flat()
    const point = allPoints.find(point =>
        point.longitude === targetLng && point.latitude === targetLat
    )
    return point ? point.height : null
}
function renderingMachineNest (options) {
    const { viewer, dockTransformPosition, dockPosition, data } = options
    viewer.entities.add({
        name: 'work-drone-route-point-dock',
        position: dockTransformPosition,
        billboard: {
            image: endingOnlineImg,
            width: 50,
            height: 50,
            disableDepthTestDistance: Number.POSITIVE_INFINITY,
        },
        customData: {
            data,
        },
    })
    viewer.entities.add({
        name: 'work-drone-route--point-polyline',
        position: dockTransformPosition,
        polyline: getPolyLine(viewer, { value: [dockPosition] }, 0),
    })
}
function renderingStartingPoint (options) {
    const { viewer, startPosition, data } = options
    viewer.entities.add({
        name: 'work-drone-route-point-start',
        position: startPosition,
        billboard: {
            image: rwqfdImg,
            width: 50,
            height: 50,
            disableDepthTestDistance: Number.POSITIVE_INFINITY,
        },
        customData: {
            data,
        },
    })
}
export default class CreateRouteLine {
    /**
     *
     *
     * @param {*} options 参数
     */
    constructor(options) {
@@ -119,10 +176,9 @@
    /**
     * 初始化viewer
     * @param {*} viewer
     * @param {*} viewer
     */
    initCreateRoute (viewer) {
        indexLog = 0
        this.viewer = viewer
    }
@@ -138,24 +194,27 @@
            const currentWaypointIndex = output?.ext['current_waypoint_index']
            this.updataMultiNestData(sn, {
                currentWaypointIndex
                currentWaypointIndex,
            })
        }
    }
    /**
     * 请求航线文件数据并解析处理
     * 请求航线文件数据并绘制解析处理
     * @param {*} data 包含文件url等信息
     * @param {*} dronePosition 无人机位置
     * @returns
     * @param {*} dockPosition 无人机位置
     * @returns
     */
    async parsingFiles (data, dronePosition = null, isShowDock = false, isShowPointBillboard = true) {
    async parsingFiles (data, dockPosition = null, isShowDock = false, isShowPointBillboard = true) {
        console.log(data,'888')
        const { url, wayline_type, device_sn } = data
        if (!url) return dronePosition != null ? {
            dronePosition: [{ lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height }]
        } : {}
        indexLog = 0
        if (!url)
            return dockPosition != null
                ? {
                    dronePosition: [{ lng: dockPosition.longitude, lat: dockPosition.latitude, alt: dockPosition.height }],
                }
                : {}
        this.updataMultiNestData(device_sn)
        const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`)
@@ -178,16 +237,16 @@
        const missionConfig = removeTextKey(waylinesXmlJson.missionConfig)
        const waylinesXMLObj = removeTextKey(waylinesXmlJson.Folder)
        if (templateType === "mapping3d") {
        if (templateType === 'mapping3d') {
            if (!waylinesXMLObj[0].Placemark.length) return ElMessage.error('没有航线点位')
        } else {
            if (!waylinesXMLObj.Placemark.length) return ElMessage.error('没有航线点位')
        }
        if ([2, 4, 5, 6, 7, 10].includes(Number(wayline_type))) {
            if (templateType === "mapping3d") {
        if ([2, 4, 5, 7, 10].includes(Number(wayline_type))) {
            if (templateType === 'mapping3d') {
                let morePolygonData = waylinesXMLObj.map(i => ({
                    data: i.Placemark
                    data: i.Placemark,
                }))
                let newPolygonData = getNewPolygonData(morePolygonData)
@@ -214,7 +273,7 @@
                    waylinesXMLObj[0],
                    missionConfig,
                    templateXmlJson,
                    dronePosition,
                    dockPosition,
                    data,
                    startPoint,
                    isShowDock,
@@ -227,7 +286,7 @@
                waylinesXMLObj,
                missionConfig,
                templateXmlJson,
                dronePosition,
                dockPosition,
                data,
                startPoint,
                isShowDock,
@@ -238,7 +297,7 @@
            return this.drawPointRoute(
                waylinesXMLObj,
                missionConfig,
                dronePosition,
                dockPosition,
                data,
                startPoint,
                isShowDock,
@@ -248,50 +307,231 @@
        }
    }
    // 拆分航线
    async splitWayLine (data, dronePosition = null, isShowDock = false, isShowPointBillboard = true) {
        const {
            pointPlacemark,
            polygonList,
            pointList,
            templateType,
            startPoint,
            execute_height_mode,
            auto_flight_speed,
            take_off_security_height,
            buffer_distance_meters,
            missionConfig,
            waylinesXMLObjR,
        } = await analysisPointLineKmz(`${data.domain_url}${data.object_key}`)
        const { positionArray, filePositions } = this.disposeData(
            waylinesXMLObjR,
            missionConfig,
            null,
            data,
            startPoint,
            0
        )
        let bigPointList = handlePointListForKmz({
            placemark: pointPlacemark,
            startPoint: startPoint,
            execute_height_mode: execute_height_mode,
            auto_flight_speed: auto_flight_speed,
        })
        // 路径线
        let entityConfig
        bigPointList.shift()
        let flyPositions = bigPointList.map(i => [Number(i.longitude), Number(i.latitude), Number(i.height)])
        // 渲染多个点
        let firstTopPosition = null
        let resultPosotions = []
        await Promise.all(data.wayline_file_list.map(async (item, index) => {
            // 小航线渲染线
            const kmzUrl = import.meta.env.VITE_APP_AIRLINE_URL + item.object_key
            const { pointList } = await analysisPointLineKmz(kmzUrl)
            resultPosotions.push(pointList)
        })).then(res => {
            // 目的是区分大航线点属于哪条小航线
            bigPointList.forEach(bigObj => {
                let foundIndex = -1
                // 遍历 resultPositions 查找匹配项
                resultPosotions.some((subArray, subIndex) => {
                    const found = subArray.some(obj =>
                        obj.latitude === bigObj.latitude &&
                        obj.longitude === bigObj.longitude
                    )
                    if (found) {
                        foundIndex = subIndex
                        return true // 找到就停止搜索
                    }
                    return false
                })
                // 如果找到了,给 bigPointList 的对象添加标记
                if (foundIndex !== -1) {
                    bigObj.log = foundIndex // 或 bigObj.index = foundIndex
                }
            })
            console.log(bigPointList, '查看结果值') // 现在 bigPointList 会有 log 属性
            bigPointList.map(async (i, index) => {
                firstTopPosition = i
                const position = Cesium.Cartesian3.fromDegrees(Number(i.longitude), Number(i.latitude), Number(findHeightByCoordinates(resultPosotions, i.longitude, i.latitude)))
                const combinedImage = await createCombinedSVG(index, i.log % 2 ? newStartPoint : newStartPointMore)
                entityConfig = {
                    name: 'work-drone-route-point-split',
                    position: position,
                    billboard: {
                        disableDepthTestDistance: Number.POSITIVE_INFINITY,
                        image: combinedImage,
                        width: 50,
                        height: 50,
                        verticalOrigin: Cesium.VerticalOrigin.CENTER,
                        horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
                    }
                }
                // 只在第一个点添加 label
                if (index === 0) {
                    entityConfig.label = {
                        text: data.task_name || '',
                        font: 'bold 16px sans-serif',
                        fillColor: data.status === 2 ? runningTextColor : pendingTextColor,
                        pixelOffset: data.status === 2 ? runningTextOffset : pendingTextOffset,
                        style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                        outlineWidth: 2,
                        outlineColor: Cesium.Color.BLACK,
                        disableDepthTestDistance: Number.POSITIVE_INFINITY,
                    }
                }
                this.viewer.entities.add(entityConfig)
            })
        })
        // 渲染航线
        data.wayline_file_list.map(async (item, index) => {
            // 小航线渲染线
            const kmzUrl = import.meta.env.VITE_APP_AIRLINE_URL + item.object_key
            const { pointList } = await analysisPointLineKmz(kmzUrl)
            // if (curRouteLineData.value.droneData && splitLines.length === 1) {
            //     // 把pointList的第一个点替换为无人机位置
            //     pointList[0] = curRouteLineData.value.droneData[0]
            // }
            if (take_off_security_height + pointList[0].height > firstTopPosition.height) {
                // 安全高度+机巢高度 > 第一个点高度
                const positionTest = {
                    longitude: pointList[0].longitude,
                    latitude: pointList[0].latitude,
                    height: take_off_security_height + pointList[0].height,
                }
                const positionTest1 = {
                    longitude: pointList[1].longitude,
                    latitude: pointList[1].latitude,
                    height: take_off_security_height + pointList[0].height,
                }
                pointList.splice(1, 0, positionTest)
                // 往数组中插入元素
                pointList.splice(2, 0, positionTest1)
                // console.log(pointList, 'pointList')
            } else if (take_off_security_height + pointList[0].height < firstTopPosition.height) {
                const positionTest = {
                    longitude: pointList[0].longitude,
                    latitude: pointList[0].latitude,
                    height: firstTopPosition.height,
                }
                pointList.splice(1, 0, positionTest)
            }
            const routePositions = pointList.map(i =>
                Cesium.Cartesian3.fromDegrees(Number(i.longitude), Number(i.latitude), Number(i.height))
            )
            this.viewer.entities.add({
                name: 'work-drone-route-line-split',
                polyline: {
                    width: 4,
                    positions: routePositions,
                    material: data.status === 1 ? arrowLineMaterialPropertyGray : getLineColor(index + 1),
                    clampToGround: false,
                },
            })
        })
            // 落点线
            ; (bigPointList || [])?.forEach((item, index) => {
                const topPosition = Cesium.Cartesian3.fromDegrees(
                    Number(item.longitude),
                    Number(item.latitude),
                    Number(item.height)
                )
                let setting = {
                    name: 'work-drone-route--planar-polyline-split',
                    position: topPosition,
                    polyline: getPolyLine(this.viewer, { value: bigPointList }, index),
                }
                this.viewer.entities.add(setting)
            })
        return {
            entity: entityConfig,
            routeLinePositions: positionArray,
            filePositions:
                dronePosition != null
                    ? [{ lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height }, ...filePositions]
                    : filePositions,
        }
    }
    /**
     * 绘制面状航线
     * @param {*} lineObj 航线文件中的
     * @param {*} missionConfig 航线文件中的
     * @param {*} templateXmlJson 航线文件中的
     * @param {*} dronePosition 无人机位置
     * @param {*} data
     * @param {*} startPoint
     * @param {*} isShowPointBillboard 是否显示,航线起飞点,终点等标注
     * @returns
     * @param {*} dockPosition 无人机位置
     * @param {*} data
     * @param {*} startPoint
     * @returns
     */
    async drawPlanarRoute (lineObj, missionConfig, templateXmlJson, dronePosition, data, startPoint, isShowDock, isShowPointBillboard, wayline_type) {
        const { positionArray, filePositions } = this.disposeData(lineObj, missionConfig, dronePosition, data, startPoint, wayline_type)
    async drawPlanarRoute (
        lineObj,
        missionConfig,
        templateXmlJson,
        dockPosition,
        data,
        startPoint,
        isShowDock,
        isShowPointBillboard,
        wayline_type
    ) {
        const { positionArray, filePositions } = this.disposeData(
            lineObj,
            missionConfig,
            dockPosition,
            data,
            startPoint,
            wayline_type
        )
        const { device_sn } = data
        const droneTransformPosition = Cesium.Cartesian3.fromDegrees(
            Number(dronePosition.longitude),
            Number(dronePosition.latitude),
            Number(dronePosition.height),
        const dockTransformPosition = Cesium.Cartesian3.fromDegrees(
            Number(dockPosition.longitude),
            Number(dockPosition.latitude),
            Number(dockPosition.height)
        )
        let coordArr = null
        const placemark = templateXmlJson.Folder?.Placemark
        // 取出点位
        const coordinates =
            placemark.Polygon?.outerBoundaryIs.LinearRing.coordinates?.[
                '#text'
            ]?.split('\n') || []
        const coordinates = placemark.Polygon?.outerBoundaryIs.LinearRing.coordinates?.['#text']?.split('\n') || []
        // 数组转换
        coordArr = coordinates.map((coordinate) =>
        coordArr = coordinates.map(coordinate =>
            coordinate
                .replace(/\s+/g, '')
                .split(',')
                .map((v) => Number(v)),
                .map(v => Number(v))
        )
        // 获取当前经纬度绝对高度
        let newCoordArr = coordArr.map(item => ([item[0], item[1], 0]))
        let newCoordArr = coordArr.map(item => [item[0], item[1], 0])
        let planarRouteEntity = this.viewer.entities.add({
            name: 'work-drone-route-planar-polyline',
            polyline: {
                width: 3,
                positions: positionArray,
@@ -300,30 +540,30 @@
            },
            customData: {
                data
            }
                data,
            },
        })
        let droppointPositions = (filePositions || [])?.map(i => ({
            longitude: Number(i.lng),
            latitude: Number(i.lat),
            height: Number(i.alt),
        }));
        }))
        // 落点线
        (droppointPositions || [])?.forEach((item, index) => {
            const topPosition = Cesium.Cartesian3.fromDegrees(
                Number(item.longitude),
                Number(item.latitude),
                Number(item.height)
            )
            let setting = {
                name: 'work-drone-route-planar-polyline',
                position: topPosition,
                polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
            }
            this.viewer.entities.add(setting)
        })
            // 落点线
            ; (droppointPositions || [])?.forEach((item, index) => {
                const topPosition = Cesium.Cartesian3.fromDegrees(
                    Number(item.longitude),
                    Number(item.latitude),
                    Number(item.height)
                )
                let setting = {
                    name: 'work-drone-route--planar-polyline',
                    position: topPosition,
                    polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
                }
                this.viewer.entities.add(setting)
            })
        // 面状航线第一个点突出展示
        isShowPointBillboard && this.startingIncreasePoint(positionArray)
@@ -340,9 +580,7 @@
            name: 'work-drone-route-planar-border',
            polyline: {
                positions: Cesium.Cartesian3.fromDegreesArrayHeights(
                    cloneCoordArr.flat(),
                ),
                positions: Cesium.Cartesian3.fromDegreesArrayHeights(cloneCoordArr.flat()),
                width: 4,
                material: new Cesium.PolylineOutlineMaterialProperty({
                    color: new Cesium.Color.fromBytes(74, 138, 233),
@@ -357,11 +595,8 @@
        // 绘制面状地块--------------------
        this.viewer.entities.add({
            name: 'work-drone-route-planar-polygon',
            polygon: {
                hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(
                    newCoordArr.flat(),
                ),
                hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(newCoordArr.flat()),
                material: new Cesium.Color.fromBytes(75, 159, 221, 100),
                outline: true,
                outlineColor: new Cesium.Color.fromBytes(35, 85, 216, 255),
@@ -384,7 +619,7 @@
                label: {
                    text: data.job_name || '',
                    font: 'bold 16px YouSheBiaoTiHei',
                    font: 'bold 16px sans-serif',
                    fillColor: Cesium.Color.WHITE,
                    pixelOffset: new Cesium.Cartesian2(0, -50),
                    style: Cesium.LabelStyle.FILL_AND_OUTLINE,
@@ -395,64 +630,31 @@
            })
        } else {
            // 显示机巢位置
            if (isShowDock) {
                // 起点
                this.viewer.entities.add({
                    name: 'work-drone-route-point-dock',
                    position: droneTransformPosition,
                    billboard: {
                        image: endingOnlineImg,
                        width: 50,
                        height: 50,
                        disableDepthTestDistance: Number.POSITIVE_INFINITY,
                    },
                    customData: {
                        data
                    }
                })
                this.viewer.entities.add({
                    name: 'work-drone-route--point-polyline',
                    position: droneTransformPosition,
                    polyline: getPolyLine(this.viewer, { value: [dronePosition] }, 0),
                })
            }
            const startPosition = Cesium.Cartesian3.fromDegrees(
                Number(filePositions[0].lng),
                Number(filePositions[0].lat),
                Number(filePositions[0].alt)
            )
            isShowDock && renderingMachineNest({
                viewer: this.viewer,
                dockTransformPosition,
                dockPosition,
                data
            })
            // 起点
            isShowPointBillboard && this.viewer.entities.add({
                name: 'work-drone-route-point-start',
                position: startPosition,
                billboard: {
                    image: rwqfdImg,
                    width: 50,
                    height: 50,
                    disableDepthTestDistance: Number.POSITIVE_INFINITY,
                },
                customData: {
                    data
                }
            isShowPointBillboard && renderingStartingPoint({
                viewer: this.viewer,
                startPosition: Cesium.Cartesian3.fromDegrees(
                    Number(filePositions[0].lng),
                    Number(filePositions[0].lat),
                    Number(filePositions[0].alt)
                ),
                data
            })
        }
        return {
            entity: planarRouteEntity,
            routeLinePositions: positionArray,
            filePositions: dronePosition != null ? [
                { lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height },
                ...filePositions
            ] : filePositions
            filePositions:
                dockPosition != null
                    ? [{ lng: dockPosition.longitude, lat: dockPosition.latitude, alt: dockPosition.height }, ...filePositions]
                    : filePositions,
        }
    }
@@ -472,9 +674,7 @@
        let setting = {
            name: 'work-drone-route-planar-start',
            position: positions,
            billboard: {
                image: this.planarBillboard('#2D8CF0'),
                pixelOffset: new Cesium.Cartesian2(146, 72),
@@ -489,35 +689,48 @@
     * 绘制点航线
     * @param {*} lineObj 航线文件中的
     * @param {*} missionConfig 航线文件中的
     * @param {*} dronePosition 无人机位置
     * @param {*} dockPosition 无人机位置
     * @param {*} data 任务信息,航线地址等
     * @param {*} startPoint
     * @param {*} startPoint
     * @param {*} isShowDock 是否显示机巢位置
     * @param {*} isShowPointBillboard 是否显示,航线起飞点,终点等标注
     * @returns
     * @returns
     */
    async drawPointRoute (lineObj, missionConfig, dronePosition, data, startPoint, isShowDock, isShowPointBillboard, wayline_type) {
        const { positionArray, filePositions } = this.disposeData(lineObj, missionConfig, dronePosition, data, startPoint, wayline_type)
        const droneTransformPosition = Cesium.Cartesian3.fromDegrees(
            Number(dronePosition.longitude),
            Number(dronePosition.latitude),
            Number(dronePosition.height),
    async drawPointRoute (
        lineObj,
        missionConfig,
        dockPosition,
        data,
        startPoint,
        isShowDock,
        isShowPointBillboard,
        wayline_type
    ) {
        const { positionArray, filePositions } = this.disposeData(
            lineObj,
            missionConfig,
            dockPosition,
            data,
            startPoint,
            wayline_type
        )
        const dockTransformPosition = Cesium.Cartesian3.fromDegrees(
            Number(dockPosition.longitude),
            Number(dockPosition.latitude),
            Number(dockPosition.height)
        )
        // 路径线
        let polyline
        indexLog = indexLog + 1
        if (this.type === 'clusterScheduling') {
            filePositions.forEach((item, index) => {
                let position = Cesium.Cartesian3.fromDegrees(item.lng, item.lat, item.alt)
                if (index === 0) {
                    this.viewer.entities.add({
                        name: 'work-drone-route-point-start',
                        position,
                        billboard: {
                            image: rwqfdImg,
                            outlineWidth: 0,
@@ -525,10 +738,9 @@
                            height: 50,
                            disableDepthTestDistance: Number.POSITIVE_INFINITY,
                        },
                        label: {
                            text: data.job_name || '',
                            font: 'bold 16px YouSheBiaoTiHei',
                            font: 'bold 16px sans-serif',
                            fillColor: data.type === 2 ? runningTextColor : pendingTextColor,
                            pixelOffset: data.type === 2 ? runningTextOffset : pendingTextOffset,
                            style: Cesium.LabelStyle.FILL_AND_OUTLINE,
@@ -540,9 +752,7 @@
                } else if (index === filePositions.length - 1) {
                    this.viewer.entities.add({
                        name: 'work-drone-route-point-end',
                        position,
                        billboard: {
                            image: newEndPointImg,
                            outlineWidth: 0,
@@ -556,9 +766,7 @@
                } else {
                    this.viewer.entities.add({
                        name: 'work-drone-route-point-approach',
                        position,
                        billboard: {
                            image: newNumPoint,
                            width: 50,
@@ -577,96 +785,56 @@
                    })
                }
            })
            polyline = this.viewer.entities.add({
                name: 'work-drone-route-point-polyline',
                polyline: {
                    width: 4,
                    positions: positionArray,
                    material: data.type === 2 ? runningLineMaterial : pendingLineMaterial,
                    material: data.type === 2 ? getLineColor(indexLog) : arrowLineMaterialPropertyGray,
                    clampToGround: false,
                }
                },
            })
        } else {
            // 显示机巢位置
            if (isShowDock) {
                // 起点
                this.viewer.entities.add({
                    name: 'work-drone-route-point-dock',
                    position: droneTransformPosition,
                    billboard: {
                        image: endingOnlineImg,
                        width: 50,
                        height: 50,
                        disableDepthTestDistance: Number.POSITIVE_INFINITY,
                    },
                    customData: {
                        data
                    }
                })
                this.viewer.entities.add({
                    name: 'work-drone-route--point-polyline',
                    position: droneTransformPosition,
                    polyline: getPolyLine(this.viewer, { value: [dronePosition] }, 0),
                })
            }
            const startPosition = Cesium.Cartesian3.fromDegrees(
                Number(filePositions[0].lng),
                Number(filePositions[0].lat),
                Number(filePositions[0].alt)
            )
            isShowDock && renderingMachineNest({
                viewer: this.viewer,
                dockTransformPosition,
                dockPosition
            })
            // 起点
            isShowPointBillboard && this.viewer.entities.add({
                name: 'work-drone-route-point-start',
                position: startPosition,
                billboard: {
                    image: rwqfdImg,
                    width: 50,
                    height: 50,
                    disableDepthTestDistance: Number.POSITIVE_INFINITY,
                },
                customData: {
                    data
                }
            isShowPointBillboard && renderingStartingPoint({
                viewer: this.viewer,
                startPosition: Cesium.Cartesian3.fromDegrees(
                    Number(filePositions[0].lng),
                    Number(filePositions[0].lat),
                    Number(filePositions[0].alt)
                ),
                data
            })
            // 终点
            isShowPointBillboard && this.viewer.entities.add({
                name: 'work-drone-route-point-end',
                position: positionArray[positionArray.length - 1],
                billboard: {
                    image: new Cesium.ConstantProperty(endPointImg),
                    width: 30,
                    height: 30,
                    verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐
                },
            })
            isShowPointBillboard &&
                this.viewer.entities.add({
                    name: 'work-drone-route-point-end',
                    position: positionArray[positionArray.length - 1],
                    billboard: {
                        image: new Cesium.ConstantProperty(endPointImg),
                        width: 30,
                        height: 30,
                        verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐
                    },
                })
            console.log(data.status, '失败')
            polyline = this.viewer.entities.add({
                name: 'work-drone-route-point-polyline',
                polyline: {
                    width: 4,
                    positions: positionArray,
                    material: getLineColor(indexLog),
                    material: data.status === 5 ? arrowLineMaterialPropertyGray : getLineColor(indexLog),
                    clampToGround: false,
                },
                customData: {
                    data
                }
                    data,
                },
            })
        }
@@ -674,47 +842,45 @@
            longitude: Number(i.lng),
            latitude: Number(i.lat),
            height: Number(i.alt),
        }));
        }))
        // 落点线
        (droppointPositions || [])?.forEach((item, index) => {
            const topPosition = Cesium.Cartesian3.fromDegrees(
                Number(item.longitude),
                Number(item.latitude),
                Number(item.height)
            )
            // 落点线
            ; (droppointPositions || [])?.forEach((item, index) => {
                const topPosition = Cesium.Cartesian3.fromDegrees(
                    Number(item.longitude),
                    Number(item.latitude),
                    Number(item.height)
                )
            let setting = {
                name: 'work-drone-route-point-polyline',
                position: topPosition,
                polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
            }
            this.viewer.entities.add(setting)
        })
                let setting = {
                    name: 'work-drone-route--point-polyline',
                    position: topPosition,
                    polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
                }
                this.viewer.entities.add(setting)
            })
        return {
            entity: polyline,
            routeLinePositions: positionArray,
            filePositions: dronePosition != null ? [
                { lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height },
                ...filePositions
            ] : filePositions,
            filePositions:
                dockPosition !== null
                    ? [{ lng: dockPosition.longitude, lat: dockPosition.latitude, alt: dockPosition.height }, ...filePositions]
                    : filePositions,
        }
    }
    /**
     * 通用处理数据
     * @param {*} lineObj
     * @param {*} missionConfig
     * @param {*} dronePosition
     * @param {*} startPoint
     * @param {*} lineObj
     * @param {*} missionConfig
     * @param {*} dronePosition
     * @param {*} startPoint
     * @returns {} {positionArray: 带拼接点位置, filePositions:光航线点位置}
     */
    disposeData (lineObj, missionConfig, dronePosition, data, startPoint, wayline_type) {
        indexLog = indexLog + 1
        const { device_sn } = data
        const executeHeightMode = lineObj.executeHeightMode === "WGS84"
        const executeHeightMode = lineObj.executeHeightMode === 'WGS84'
        let filePositions = lineObj.Placemark.map(item => {
            const [lng, lat] = item.Point.coordinates.split(',')
@@ -730,12 +896,14 @@
        let safetyHeight
        if (executeHeightMode) {
            let startHeight = new Decimal(missionConfig.takeOffSecurityHeight)
                .plus(dronePosition.height).toNumber()
            let startHeight = new Decimal(missionConfig.takeOffSecurityHeight).plus(dronePosition?.height || 0).toNumber()
            safetyHeight = startHeight < filePositions[0].alt ? filePositions[0].alt : startHeight
        } else {
            safetyHeight = missionConfig.takeOffSecurityHeight < filePositions[0].alt ? filePositions[0].alt : missionConfig.takeOffSecurityHeight
            safetyHeight =
                missionConfig.takeOffSecurityHeight < filePositions[0].alt
                    ? filePositions[0].alt
                    : missionConfig.takeOffSecurityHeight
        }
        let safetyPositions = []
@@ -773,28 +941,38 @@
        })
        this.updataMultiNestData(device_sn, {
            currentRoutePositions
            currentRoutePositions,
        })
        if ([0].includes(wayline_type)) {
            let startHeight = executeHeightMode ? filePositions[0].alt : filePositions[0].alt + (dronePosition != null ? dronePosition.height : 0)
            let endHeight = executeHeightMode ? filePositions[1].alt : filePositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
            let startHeight = executeHeightMode
                ? filePositions[0].alt
                : filePositions[0].alt + (dronePosition != null ? dronePosition.height : 0)
            let endHeight = executeHeightMode
                ? filePositions[1].alt
                : filePositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
            this.updataMultiNestData(device_sn, {
                showPopupPosition: Cesium.Cartesian3.midpoint(
                    Cesium.Cartesian3.fromDegrees(filePositions[0].lng, filePositions[0].lat, startHeight),
                    Cesium.Cartesian3.fromDegrees(filePositions[1].lng, filePositions[1].lat, endHeight),
                    new Cesium.Cartesian3())
                    new Cesium.Cartesian3()
                ),
            })
        } else {
            let startHeight = executeHeightMode ? safetyPositions[1].alt : safetyPositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
            let endHeight = executeHeightMode ? safetyPositions[2].alt : safetyPositions[2].alt + (dronePosition != null ? dronePosition.height : 0)
            let startHeight = executeHeightMode
                ? safetyPositions[1].alt
                : safetyPositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
            let endHeight = executeHeightMode
                ? safetyPositions[2].alt
                : safetyPositions[2].alt + (dronePosition != null ? dronePosition.height : 0)
            this.updataMultiNestData(device_sn, {
                showPopupPosition: Cesium.Cartesian3.midpoint(
                    Cesium.Cartesian3.fromDegrees(safetyPositions[1].lng, safetyPositions[1].lat, startHeight),
                    Cesium.Cartesian3.fromDegrees(safetyPositions[2].lng, safetyPositions[2].lat, endHeight),
                    new Cesium.Cartesian3())
                    new Cesium.Cartesian3()
                ),
            })
        }
@@ -805,9 +983,9 @@
                return {
                    ...item,
                    alt: Number(height)
                    alt: Number(height),
                }
            })
            }),
        }
    }
@@ -819,9 +997,10 @@
            return i?.name && i?.name.includes('work-drone-route-')
        })
        Array.isArray(entities) && entities.forEach(item => {
            this.viewer?.entities.remove(item)
        })
        Array.isArray(entities) &&
            entities.forEach(item => {
                this.viewer?.entities.remove(item)
            })
    }
    // 当前无人机视椎体相关
@@ -847,11 +1026,22 @@
                ['wide', 'ir', 'zoom'].includes(curDroneData?.camera_type)
            ) {
                if (curDroneData.camera_type === 'wide') {
                    fov = 60
                    fov = 65
                } else {
                    const diffFocal = zoomFactor.value[curDroneData.camera_type].max - getZoomFactor(host?.cameras?.[0], curDroneData.camera_type)
                    fov = (60 / zoomFactor.value[curDroneData.camera_type].max) * (diffFocal === 0 ? 1 : diffFocal)
                    const cameraMultiplier = getZoomFactor(host?.cameras?.[0], curDroneData.camera_type)
                    switch (cameraMultiplier) {
                        case 2: fov = 30; break
                        case 3: fov = 19; break
                        case 4: fov = 15; break
                        case 5: fov = 13; break
                        case 6: fov = 11; break
                        case 7: fov = 9; break
                        case 8: fov = 8.34; break
                        case 9: fov = 7.68; break
                        case 10: fov = 6.9; break
                        case 14: fov = 5; break
                        default: fov = 55.946 * Math.exp(-0.4504 * cameraMultiplier) + 6.323
                    }
                }
            }
@@ -871,8 +1061,8 @@
                    pitch: 0,
                    heading: attitude_head,
                    isShowVideoPlan: this.isShowVideoPlan
                })
                    isShowVideoPlan: this.isShowVideoPlan,
                }),
            })
        }
    }
@@ -882,7 +1072,7 @@
        if (device_sn && live_status) {
            this.updataMultiNestData(device_sn, {
                camera_type: live_status?.[0].video_type || 'wide'
                camera_type: live_status?.[0].video_type || 'wide',
            })
        }
    }
@@ -897,14 +1087,9 @@
        }
    }
    // 设置当前无人机视椎体信息面板
    setFrustumInfoDetails (data) {
        const {
            dock_sn,
            distance_to_airport,
            distance_to_next_point,
            estimated_time_to_next_point,
            plane_mileage
        } = data
        const { dock_sn, distance_to_airport, distance_to_next_point, estimated_time_to_next_point, plane_mileage } = data
        if (dock_sn) {
            const aircraftEntity = this.viewer?.entities.values.find(i => {
@@ -924,7 +1109,9 @@
                }
                aircraftEntity.label = new Cesium.LabelGraphics({
                    text: `离机场平面距离:${distance_to_airport}m\n离下个航点平面距离:${Math.round(distance_to_next_point)}m\n到达下个航点剩余时间:${arrivalTime}\n航线总平面里程:${Math.round(plane_mileage)}m`,
                    text: `离机场平面距离:${distance_to_airport}m\n离下个航点平面距离:${Math.round(
                        distance_to_next_point
                    )}m\n到达下个航点剩余时间:${arrivalTime}\n航线总平面里程:${Math.round(plane_mileage)}m`,
                    font: '13px monospace',
                    showBackground: true,
                    horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
@@ -936,7 +1123,6 @@
                return
            }
        }
    }
@@ -954,8 +1140,10 @@
        }
    }
    // 设置无人机模型
    setAircraftGltf (host) {
        const parent_sn = host?.parent_sn
        const attitude_head = Number(host?.payloads?.[0]?.gimbal_yaw) - 90 || 0
        if (parent_sn) {
            const aircraftEntity = this.viewer?.entities.values.find(i => {
@@ -963,9 +1151,18 @@
            })
            const position = Cesium.Cartesian3.fromDegrees(host?.longitude, host?.latitude, host?.height)
            // ✅ 新增:Heading / Pitch / Roll(单位:度 -> 弧度)
            const heading = Cesium.Math.toRadians(attitude_head) // 航向角
            const pitch = Cesium.Math.toRadians(0)     // 俯仰角
            const roll = Cesium.Math.toRadians(0)       // 横滚角
            // ✅ 新增:构造 HeadingPitchRoll 和 四元数
            const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll)
            const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr)
            if (aircraftEntity) {
                aircraftEntity.position = new Cesium.ConstantPositionProperty(position)
                aircraftEntity.orientation = new Cesium.ConstantProperty(orientation)
                return
            }
@@ -974,18 +1171,35 @@
                this.viewer?.entities.add({
                    name: `aircraft-glf-${parent_sn}`,
                    position,
                    orientation,
                    label: {},
                    model: {
                        uri: aircraftGltf, // 或 .glb
                        scale: 1.0, // 缩放比例
                        scale: 64, // 缩放比例
                        minimumPixelSize: 64, // 最小像素尺寸(保证模型远处可见)
                        maximumScale: 128, // 最大缩放(可选)
                        maximumScale: 64, // 最大缩放(可选)
                    },
                })
            }
        }
    }
    // 移除无人机模型
    removeAircraftGltf (host) {
        const parent_sn = host?.parent_sn
        if (parent_sn) {
            const aircraftEntity = this.viewer?.entities.values.find(i => {
                return i?.name && i?.name.includes(`aircraft-glf-${parent_sn}`)
            })
            if (aircraftEntity) {
                this.viewer?.entities.remove(aircraftEntity)
            }
        }
    }
    // 移除所有无人机视椎体
    removeViewInfoFrustum () {
        this.multiNestData.forEach(item => {
            if ('viewInfoFrustum' in item && item.viewInfoFrustum) {
@@ -1010,9 +1224,10 @@
                return i?.name && i?.name.includes(`aircraft-glf-${parent_sn}`)
            })
            Array.isArray(entities) && entities.forEach(item => {
                this.viewer?.entities.remove(item)
            })
            Array.isArray(entities) &&
                entities.forEach(item => {
                    this.viewer?.entities.remove(item)
                })
        }
    }
@@ -1021,13 +1236,15 @@
            return i?.name && i?.name.includes(`aircraft-glf-`)
        })
        Array.isArray(entities) && entities.forEach(item => {
            this.viewer?.entities.remove(item)
        })
        Array.isArray(entities) &&
            entities.forEach(item => {
                this.viewer?.entities.remove(item)
            })
        this.multiNestData.length > 0 && (this.multiNestData.forEach(item => {
            item.viewInfoFrustum?.clear()
        }))
        this.multiNestData.length > 0 &&
            this.multiNestData.forEach(item => {
                item.viewInfoFrustum?.clear()
            })
    }
    /**
@@ -1043,19 +1260,19 @@
            this.multiNestData[isHaveInd] = {
                ...this.multiNestData[isHaveInd],
                ...data
                ...data,
            }
        } else {
            this.multiNestData.push({
                device_sn,
                ...data
                ...data,
            })
        }
    }
    /**
     * 根据机巢sn获取当前机巢对应的相关信息
     * @param {*} device_sn
     * @param {*} device_sn
     */
    getCurDroneData (device_sn) {
        const isHave = this.multiNestData.find(item => item.device_sn === device_sn)
@@ -1071,14 +1288,16 @@
            return {
                ...item,
                show: true,
                uuid: uuidv4()
                uuid: uuidv4(),
            }
        })
        this.viewer.scene.postRender.addEventListener(that.labelBoxRenderHandler)
    }
    showSingleDetailMapPopup (data) {
        this.detailsMapPopupData.forEach(i => i.job_id === data.job_id && i.device_sn === data.device_sn && (i.show = true))
        this.detailsMapPopupData.forEach(
            i => i?.job_id === data?.job_id && i?.device_sn === data?.device_sn && (i.show = true)
        )
    }
    getLabelDom (data) {
@@ -1090,28 +1309,30 @@
        tooltipContainer.style.position = 'absolute'
        tooltipContainer.style.transform = 'translate(-50%,10%)'
        tooltipContainer.style.pointerEvents = 'none'
        document.querySelector('.content-map-popups')?.append(tooltipContainer)
        document.querySelector('.content-map-popups').append(tooltipContainer)
        render(vNode, tooltipContainer)
        return tooltipContainer
    }
    labelBoxRender () {
        this.detailsMapPopupData.filter(i => i.show === true).forEach(item => {
            const { showPopupPosition } = this.getCurDroneData(item.device_sn)
        this.detailsMapPopupData
            .filter(i => i.show === true)
            .forEach(item => {
                const { showPopupPosition } = this.getCurDroneData(item.device_sn)
            let dom = document.querySelector(`[id="${item.uuid}"]`)
                let dom = document.querySelector(`[id="${item.uuid}"]`)
            if (!dom) {
                dom = this.getLabelDom(item)
            }
                if (!dom) {
                    dom = this.getLabelDom(item)
                }
            const screenPosition = this.viewer?.scene.cartesianToCanvasCoordinates(showPopupPosition)
            if (screenPosition) {
                dom.style.left = `${screenPosition.x}px`
                dom.style.top = `${screenPosition.y}px`
                dom.style.display = 'block'
            }
        })
                const screenPosition = this.viewer?.scene.cartesianToCanvasCoordinates(showPopupPosition)
                if (screenPosition) {
                    dom.style.left = `${screenPosition.x}px`
                    dom.style.top = `${screenPosition.y}px`
                    dom.style.display = 'block'
                }
            })
    }
    // 移除弹框标签
@@ -1137,4 +1358,4 @@
        const that = this
        that.viewer?.scene.postRender.removeEventListener(that.labelBoxRenderHandler)
    }
}
}
applications/task-work-order/src/utils/cesium/eventBus.js
File was deleted
applications/task-work-order/src/utils/cesium/frustum/CesiumVideoFrustum.js
New file
@@ -0,0 +1,712 @@
/*
 * @Author       : yuan
 * @Date         : 2025-08-16 17:01:46
 * @LastEditors  : yuan
 * @LastEditTime : 2025-09-26 16:27:20
 * @FilePath     : \src\utils\cesium\frustum\CesiumVideoFrustum.js
 * @Description  :
 * Copyright 2025 OBKoro1, All Rights Reserved.
 * 2025-08-16 17:01:46
 */
import * as Cesium from 'cesium'
var videoShed3dShader = `
uniform float ztzf_frustum_opacity;
uniform sampler2D ztzf_frustum_videoTexture;
uniform sampler2D ztzf_frustum_maskTexture;
uniform vec4 ztzf_frustum_hiddenAreaColor;
uniform sampler2D shadowMap_texture;
uniform mat4 shadowMap_matrix;
uniform vec4 shadowMap_lightPositionEC;
uniform vec4 shadowMap_texelSizeDepthBias;
uniform vec4 shadowMap_normalOffsetScale;
uniform bool ztzf_frustum_flipx;
uniform bool ztzf_frustum_flipy;
// 新增边框参数
uniform vec4 ztzf_frustum_borderColor;  // 边框颜色(RGBA)
uniform float ztzf_frustum_borderWidth; // 边框宽度(0.0-1.0)
uniform sampler2D colorTexture;
uniform sampler2D depthTexture;
in vec2 v_textureCoordinates;
vec4 toEye(in vec2 uv, in float depth) {
  vec2 xy = vec2((uv.x * 2.0 - 1.0), (uv.y * 2.0 - 1.0));
  vec4 posInCamera = czm_inverseProjection * vec4(xy, depth, 1.0);
  posInCamera = posInCamera / posInCamera.w;
  return posInCamera;
}
float getDepthMars3D(in vec4 depth) {
  float z_window = czm_unpackDepth(depth);
  z_window = czm_reverseLogDepth(z_window);
  float n_range = czm_depthRange.near;
  float f_range = czm_depthRange.far;
  return (2.0 * z_window - n_range - f_range) / (f_range - n_range);
}
float _czm_sampleShadowMap(sampler2D shadowMap, vec2 uv) {
  return texture(shadowMap, uv).r;
}
float _czm_shadowDepthCompare(sampler2D shadowMap, vec2 uv, float depth) {
  return step(depth, _czm_sampleShadowMap(shadowMap, uv));
}
float _czm_shadowVisibility(sampler2D shadowMap, czm_shadowParameters shadowParameters) {
  float depthBias = shadowParameters.depthBias;
  float depth = shadowParameters.depth;
  float nDotL = shadowParameters.nDotL;
  float normalShadingSmooth = shadowParameters.normalShadingSmooth;
  float darkness = shadowParameters.darkness;
  vec2 uv = shadowParameters.texCoords;
  depth -= depthBias;
  vec2 texelStepSize = shadowParameters.texelStepSize;
  float radius = 1.0;
  float dx0 = -texelStepSize.x * radius;
  float dy0 = -texelStepSize.y * radius;
  float dx1 = texelStepSize.x * radius;
  float dy1 = texelStepSize.y * radius;
  float visibility = (_czm_shadowDepthCompare(shadowMap, uv, depth) +
    _czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, dy0), depth) +
    _czm_shadowDepthCompare(shadowMap, uv + vec2(0.0, dy0), depth) +
    _czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, dy0), depth) +
    _czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, 0.0), depth) +
    _czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, 0.0), depth) +
    _czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, dy1), depth) +
    _czm_shadowDepthCompare(shadowMap, uv + vec2(0.0, dy1), depth) +
    _czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, dy1), depth)) * (1.0 / 9.0);
  return visibility;
}
vec3 pointProjectOnPlane(in vec3 planeNormal, in vec3 planeOrigin, in vec3 point) {
  vec3 v01 = point - planeOrigin;
  float d = dot(planeNormal, v01);
  return (point - planeNormal * d);
}
float ptm(vec3 pt) {
  return sqrt(pt.x * pt.x + pt.y * pt.y + pt.z * pt.z);
}
void main() {
  const float PI = 3.141592653589793;
  vec4 color = texture(colorTexture, v_textureCoordinates);
  vec4 currD = texture(depthTexture, v_textureCoordinates);
  float depth = getDepthMars3D(currD);
  vec4 positionEC = toEye(v_textureCoordinates, depth);
  vec3 normalEC = vec3(1.0);
  czm_shadowParameters shadowParameters;
  shadowParameters.texelStepSize = shadowMap_texelSizeDepthBias.xy;
  shadowParameters.depthBias = shadowMap_texelSizeDepthBias.z;
  shadowParameters.normalShadingSmooth = shadowMap_texelSizeDepthBias.w;
  shadowParameters.darkness = shadowMap_normalOffsetScale.w;
  shadowParameters.depthBias *= max(depth * 0.01, 1.0);
  vec3 directionEC = normalize(positionEC.xyz - shadowMap_lightPositionEC.xyz);
  float nDotL = clamp(dot(normalEC, -directionEC), 0.0, 1.0);
  vec4 shadowPosition = shadowMap_matrix * positionEC;
  shadowPosition /= shadowPosition.w;
  if(any(lessThan(shadowPosition.xyz, vec3(0.0))) || any(greaterThan(shadowPosition.xyz, vec3(1.0)))) {
    out_FragColor = color;
    return;
  }
  shadowParameters.texCoords = shadowPosition.xy;
  shadowParameters.depth = shadowPosition.z;
  shadowParameters.nDotL = nDotL;
  float visibility = _czm_shadowVisibility(shadowMap_texture, shadowParameters);
  //视频投射
  if(visibility == 1.0) {
    if(ztzf_frustum_flipx){
      shadowPosition.x = shadowPosition.x + (0.5 - shadowPosition.x) * 2.0;
    }
    if(ztzf_frustum_flipy){
      shadowPosition.y = shadowPosition.y + (0.5 - shadowPosition.y) * 2.0;
    }
    vec4 videoColor = texture(ztzf_frustum_videoTexture, shadowPosition.xy);
    vec4 maskColor = texture(ztzf_frustum_maskTexture, shadowPosition.xy);
    videoColor *= maskColor;
    // 计算到边缘的距离
    float distToEdge = min(
        min(shadowPosition.x, 1.0 - shadowPosition.x),
        min(shadowPosition.y, 1.0 - shadowPosition.y)
    );
    // 混合视频颜色和背景
    vec4 finalColor = mix(color, vec4(videoColor.xyz, 1.0), ztzf_frustum_opacity * videoColor.a);
    // 添加边框效果
    if(distToEdge < ztzf_frustum_borderWidth && videoColor.a > 0.0) {
        float borderFactor = smoothstep(0.0, ztzf_frustum_borderWidth, distToEdge);
        finalColor = mix(ztzf_frustum_borderColor, finalColor, borderFactor);
    }
    out_FragColor = finalColor;
  } else {
    if(abs(shadowPosition.z - 0.0) < 0.01) {
      return;
    }
    out_FragColor = vec4(mix(color.rgb, ztzf_frustum_hiddenAreaColor.rgb, ztzf_frustum_hiddenAreaColor.a), ztzf_frustum_hiddenAreaColor.a);
  }
}
    `
class CesiumVideoFrustum {
    constructor(viewer, param) {
        this.param = param
        var option = this._initCameraParam()
        if (option || (option = {}), this.viewer = viewer, this._cameraPosition = option.cameraPosition, this._position = option.position,
            this.type = option.type, this._alpha = option.alpha || 0, this.element = option.element, this.color = option.color,
            this._debugFrustum = Cesium.defaultValue(option.debugFrustum, !0), this._aspectRatio = option.aspectRatio || this._getWinWidHei(),
            this._camerafov = option.fov || Cesium.Math.toDegrees(this.viewer.scene.camera.frustum.fov),
            this._isShadowMap = option.isShadowMap || false,
            this.texture = option.texture || new Cesium.Texture({
                context: this.viewer.scene.context,
                source: {
                    width: 1,
                    height: 1,
                    arrayBufferView: new Uint8Array([255, 255, 255, 255])
                },
                flipY: !1
            }), this.defaultShow = Cesium.defaultValue(option.show, !0), !this.cameraPosition || !this.position) return void console.log('初始化失败:请确认相机位置与视点位置正确!')
        this.activeVideo()
        this._createShadowMap()
        this._getOrientation()
        this._addCameraFrustum()
        this._addPostProcess()
        this.viewer.scene.primitives.add(this)
    }
    get alpha () {
        return this._alpha
    }
    set alpha (e) {
        this._alpha = e
        this._changeViewPos()
    }
    get aspectRatio () {
        return this._aspectRatio
    }
    set aspectRatio (e) {
        this._aspectRatio = e
        this._changeVideoWidHei()
    }
    get debugFrustum () {
        return this._debugFrustum
    }
    set debugFrustum (e) {
        this._debugFrustum = e
        this.cameraFrustum.show = e
        this.cameraFrustumOutline.show = e
    }
    get fov () {
        return this._camerafov
    }
    set fov (e) {
        this._camerafov = e
        this._changeCameraFov()
    }
    get cameraPosition () {
        return this._cameraPosition
    }
    set cameraPosition (e) {
        e && (this._cameraPosition = e, this._changeCameraPos())
    }
    get isShadowMap () {
        return this._isShadowMap
    }
    set isShadowMap (e) {
        this._isShadowMap = e
        this._changeCameraIsShadowMap()
    }
    get position () {
        return this._position
    }
    set position (e) {
        e && (this._position = e, this._changeViewPos())
    }
    get params () {
        var t = {}
        return t.element = this.element,
            t.position = this.position,
            t.cameraPosition = this.cameraPosition,
            t.fov = this.fov,
            t.isShadowMap = this.isShadowMap,
            t.aspectRatio = this.aspectRatio,
            t.alpha = this.alpha,
            t.debugFrustum = this.debugFrustum,
            t.show = this.show
    }
    get show () {
        return this.defaultShow
    }
    set show (e) {
        this.defaultShow = Boolean(e),
            this._switchShow()
    }
    isDestroyed () {
        return false
    }
    _initCameraParam () {
        var cameraPosition = Cesium.Cartesian3.fromDegrees(this.param.position.x * 1, this.param.position.y * 1, this.param.position.z * 1)
        const hpr = Cesium.HeadingPitchRoll.fromDegrees(this.param.rotation.y * 1, this.param.rotation.x * 1, 0)
        const orientation = Cesium.Transforms.headingPitchRollQuaternion(cameraPosition, hpr)
        let matrix = Cesium.Matrix3.fromQuaternion(
            orientation,
            new Cesium.Matrix3()
        )
        let direction = Cesium.Matrix3.getColumn(matrix, 2, new Cesium.Cartesian3())
        let frontDirect = Cesium.Cartesian3.multiplyByScalar(
            direction,
            250,
            new Cesium.Cartesian3()
        )
        let position = Cesium.Cartesian3.add(cameraPosition, frontDirect, new Cesium.Cartesian3())
        return {
            cameraPosition: cameraPosition,
            position: position,
            alpha: this.param.alpha,
            near: this.param.near,
            fov: this.param.fov,
            isShadowMap: this.param.isShadowMap,
            debugFrustum: this.param.debugFrustum,
            aspectRatio: this.param.aspectRatio,
            element: this.param.element,
        }
    }
    _changeAlpha (e) {
        this.alpha = e
    }
    _changeAspectRatio (e) {
        this.aspectRatio = e
    }
    _changeRotation (e) {
        if (e) {
            this.param.rotation = e
            var option = this._initCameraParam()
            this.position = option.position
        }
    }
    _changeCameraPosition (e) {
        if (e) {
            this.param.position = e
            var option = this._initCameraParam()
            this.cameraPosition = option.cameraPosition
        }
    }
    _changeFov (e) {
        if (e) {
            this.param.fov = e
            var option = this._initCameraParam()
            this.fov = option.fov
        }
    }
    _changeIsShadowMap (e) {
        this.param.isShadowMap = e
        var option = this._initCameraParam()
        this.isShadowMap = option.isShadowMap
    }
    _changeFar (e) {
        if (e) {
            this.param.far = e
            var option = this._initCameraParam()
            this.position = option.position
        }
    }
    _changeNear (e) {
        if (e) {
            this.param.near = e
            this.near = this.param.near
            this._changeCameraPos()
        }
    }
    _getWinWidHei () {
        var viewer = this.viewer.scene
        return viewer.canvas.clientWidth / viewer.canvas.clientHeight
    }
    _changeCameraFov () {
        this.postProcess && this.viewer.scene.postProcessStages.remove(this.postProcess)
        this.viewer.scene.primitives.remove(this.cameraFrustum),
            this.viewer.scene.primitives.remove(this.cameraFrustumOutline),
            this._createShadowMap(this.cameraPosition, this.position),
            this._getOrientation(),
            this._addCameraFrustum(),
            this._addPostProcess()
    }
    _changeCameraIsShadowMap () {
        this.postProcess && this.viewer.scene.postProcessStages.remove(this.postProcess)
        this.viewShadowMap && this.viewShadowMap.destroy()
        this.viewShadowMap = null
        this.viewer.scene.primitives.remove(this.cameraFrustum),
            this.viewer.scene.primitives.remove(this.cameraFrustumOutline),
            this._createShadowMap(this.cameraPosition, this.position),
            this._getOrientation(),
            this._addCameraFrustum(),
            this._addPostProcess()
    }
    _changeVideoWidHei () {
        this.postProcess && this.viewer.scene.postProcessStages.remove(this.postProcess)
        this.viewer.scene.primitives.remove(this.cameraFrustum),
            this.viewer.scene.primitives.remove(this.cameraFrustumOutline),
            this._createShadowMap(this.cameraPosition, this.position),
            this._getOrientation(),
            this._addCameraFrustum(),
            this._addPostProcess()
    }
    _changeCameraPos () {
        this.postProcess && this.viewer.scene.postProcessStages.remove(this.postProcess)
        this.viewShadowMap && this.viewShadowMap.destroy()
        this.viewShadowMap = null
        this.viewer.scene.primitives.remove(this.cameraFrustum),
            this.viewer.scene.primitives.remove(this.cameraFrustumOutline),
            // this.cameraFrustum.destroy(),
            this._createShadowMap(this.cameraPosition, this.position),
            this._getOrientation(),
            this._addCameraFrustum(),
            this._addPostProcess()
    }
    _changeViewPos () {
        this.postProcess && this.viewer.scene.postProcessStages.remove(this.postProcess)
        this.viewShadowMap && this.viewShadowMap.destroy()
        this.viewShadowMap = null
        this.viewer.scene.primitives.remove(this.cameraFrustum),
            this.viewer.scene.primitives.remove(this.cameraFrustumOutline),
            // this.cameraFrustum.destroy(),
            this._createShadowMap(this.cameraPosition, this.position),
            this._getOrientation(),
            this._addCameraFrustum(),
            this._addPostProcess()
    }
    _switchShow () {
        this.show ? !this.postProcess && this._addPostProcess() : (this.postProcess && this.viewer.scene.postProcessStages.remove(this.postProcess), delete this.postProcess, this.postProcess = null),
            this.cameraFrustum.show = this.show,
            this.cameraFrustumOutline.show = this.show
    }
    activeVideo () {
        var video = this.element,
            that = this
        if (video) {
            var viewer = this.viewer
            this.activeVideoListener || (this.activeVideoListener = function () {
                that.videoTexture && that.videoTexture.destroy(),
                    that.videoTexture = new Cesium.Texture({
                        context: viewer.scene.context,
                        source: video,
                        width: 1,
                        height: 1,
                        pixelFormat: Cesium.PixelFormat.RGBA,
                        pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE
                    })
            }),
                viewer.clock.onTick.addEventListener(this.activeVideoListener)
        }
    }
    locate () {
        var cameraPosition = Cesium.clone(this.cameraPosition),
            position = Cesium.clone(this.position)
        this.viewer.Camera.position = cameraPosition,
            this.viewer.camera.direction = Cesium.Cartesian3.subtract(position, cameraPosition, new Cesium.Cartesian3(0, 0, 0)),
            this.viewer.camera.up = Cesium.Cartesian3.normalize(cameraPosition, new Cesium.Cartesian3(0, 0, 0))
    }
    update (e) {
        this.viewShadowMap && this.viewer.scene.frameState.shadowMaps.push(this.viewShadowMap) // *重点* 多投影
    }
    _createShadowMap () {
        var e = Cesium.Cartesian3.fromDegrees(this.param.position.x * 1, this.param.position.y * 1, this.param.position.z * 1)
        var t = this.position
        var i = this.viewer.scene
        var a = new Cesium.Camera(i)
        a.position = e
        this.customFrustum = new Cesium.PerspectiveFrustum({
            fov: Cesium.Math.toRadians(this.fov),
            aspectRatio: this.aspectRatio,
            near: this.near,
            far: 250
        })
        a.frustum = new Cesium.PerspectiveFrustum({
            fov: Cesium.Math.toRadians(this.fov),
            aspectRatio: this.aspectRatio,
            near: this.near,
            far: 250
        })
        let curOrientation = {}
        const headingPitchRoll = new Cesium.HeadingPitchRoll(
            Cesium.Math.toRadians(this.param.rotation.y, 0),
            Cesium.Math.toRadians(this.param.rotation.x, 0),
            Cesium.Math.toRadians(0),
        )
        curOrientation['heading'] = headingPitchRoll['heading']
        curOrientation['pitch'] = headingPitchRoll['pitch']
        curOrientation['roll'] = headingPitchRoll['roll']
        let curDestination = {}
        curDestination.destination = e
        curDestination.orientation = curOrientation
        a.setView(curDestination)
        var n = Cesium.Cartesian3.distance(t, e)
        this.viewDis = n
        if (!this.isShadowMap) return
        this.viewShadowMap = new Cesium.ShadowMap({
            lightCamera: a,
            enable: !1,
            isPointLight: !1,
            isSpotLight: !0,
            cascadesEnabled: !1,
            context: i.context,
            pointLightRadius: n,
        })
    }
    _getOrientation () {
        const hpr = Cesium.HeadingPitchRoll.fromDegrees(180 + this.param.rotation.y * 1, 0, 90 - this.param.rotation.x * 1 || 0)
        const orientation = Cesium.Transforms.headingPitchRollQuaternion(this.cameraPosition, hpr)
        return this.orientation = orientation,
            orientation
    }
    _addCameraFrustum () {
        var e = this
        this.cameraFrustum = new Cesium.Primitive({
            geometryInstances: new Cesium.GeometryInstance({
                geometry: new Cesium.FrustumGeometry({
                    origin: e.cameraPosition,
                    orientation: e.orientation,
                    frustum: e.customFrustum,
                    _drawNearPlane: !0
                }),
                attributes: {
                    color: Cesium.ColorGeometryInstanceAttribute.fromColor(
                        Cesium.Color.fromBytes(0, 213, 144, 20)
                    ),
                }
            }),
            releaseGeometryInstances: false,
            appearance: new Cesium.PerInstanceColorAppearance({
                translucent: true,
                flat: !0,
                closed: true,
            }),
            asynchronous: false,
            show: this.debugFrustum && this.show
        })
        this.cameraFrustumOutline = new Cesium.Primitive({
            geometryInstances: new Cesium.GeometryInstance({
                geometry: new Cesium.FrustumOutlineGeometry({
                    origin: e.cameraPosition,
                    orientation: e.orientation,
                    frustum: e.customFrustum,
                    _drawNearPlane: !0
                }),
                attributes: {
                    color: Cesium.ColorGeometryInstanceAttribute.fromColor(
                        Cesium.Color.fromBytes(0, 213, 144, 255)
                    ),
                }
            }),
            appearance: new Cesium.PerInstanceColorAppearance({
                closed: true,
                flat: true,
                translucent: true
            }),
            asynchronous: false,
            show: this.debugFrustum && this.show
        })
        this.viewer.scene.primitives.add(this.cameraFrustum)
        this.viewer.scene.primitives.add(this.cameraFrustumOutline)
    }
    _addPostProcess () {
        if (!this.isShadowMap) return
        var e = this,
            t = videoShed3dShader
        const i = e.viewShadowMap['_primitiveBias']
        this.postProcess = new Cesium.PostProcessStage({
            fragmentShader: t,
            uniforms: {
                ztzf_frustum_videoTexture: function () {
                    return e.videoTexture
                },
                ztzf_frustum_maskTexture: function () {
                    return new Cesium['Texture']({
                        'context': e.viewer.scene.context,
                        'source': {
                            'width': 0x1,
                            'height': 0x1,
                            'arrayBufferView': new Uint8Array([255, 255, 255, 255])
                        },
                        'flipY': ![]
                    })
                },
                ztzf_frustum_opacity: function () {
                    return e.alpha
                },
                ztzf_frustum_hiddenAreaColor: () => {
                    return new Cesium.Color(0, 0, 0, 0.5)
                },
                shadowMap_texture: function () {
                    if (!e.viewShadowMap || !e.viewShadowMap['_shadowMapTexture'] || e.viewShadowMap['_shadowMapTexture']['isDestroyed']()) {
                        return new Cesium['Texture']({
                            'context': e.viewer.scene.context,
                            'source': {
                                'width': 0x1,
                                'height': 0x1,
                                'arrayBufferView': new Uint8Array([0x0, 0x0, 0x0, 0x0])
                            },
                            'flipY': ![]
                        })
                    }
                    return e.viewShadowMap._shadowMapTexture
                },
                shadowMap_matrix: function () {
                    return e.viewShadowMap._shadowMapMatrix
                },
                shadowMap_lightPositionEC: function () {
                    return e.viewShadowMap._lightPositionEC
                },
                shadowMap_texelSizeDepthBias: function () {
                    var t = new Cesium.Cartesian2
                    return t.x = 1 / e.viewShadowMap._textureSize.x,
                        t.y = 1 / e.viewShadowMap._textureSize.y,
                        Cesium.Cartesian4.fromElements(t.x, t.y, i.depthBias, i.normalShadingSmooth, this.combinedUniforms1)
                },
                shadowMap_normalOffsetScale: function () {
                    return Cesium.Cartesian4.fromElements(i.normalOffsetScale, e.viewShadowMap._distance, e.viewShadowMap.maximumDistance, e.viewShadowMap._darkness, this.combinedUniforms2)
                },
                ztzf_frustum_flipx: () => {
                    return false
                },
                ztzf_frustum_flipy: () => {
                    return false
                },
                ztzf_frustum_borderColor: () => {
                    return new Cesium.Color(0 / 255, 213 / 255, 144 / 255, 255 / 255)
                },
                ztzf_frustum_borderWidth: () => {
                    return 0.01
                },
            }
        }),
            this.viewer.scene.postProcessStages.add(this.postProcess)
    }
    destroy () {
        this.viewer.scene.postProcessStages.remove(this.postProcess),
            this.viewer.scene.primitives.remove(this.cameraFrustum),
            this.viewer.scene.primitives.remove(this.cameraFrustumOutline),
            //this._videoEle && this._videoEle.parentNode.removeChild(this._videoEle),
            this.activeVideoListener && this.viewer.clock.onTick.removeEventListener(this.activeVideoListener),
            this.activeVideoListener && delete this.activeVideoListener,
            delete this.postProcess,
            delete this.viewShadowMap,
            delete this.color,
            delete this.viewDis,
            delete this.cameraPosition,
            delete this.position,
            delete this.alpha,
            delete this._camerafov,
            delete this._isShadowMap,
            delete this._cameraPosition,
            delete this.videoTexture,
            delete this.cameraFrustum,
            delete this.cameraFrustumOutline,
            delete this._videoEle,
            delete this._debugFrustum,
            delete this._position,
            delete this._aspectRatio,
            delete this.element,
            delete this.orientation,
            delete this.texture,
            delete this.videoId,
            this.viewer.scene.primitives.remove(this),
            delete this.viewer
    }
}
export default CesiumVideoFrustum
applications/task-work-order/src/utils/cesium/frustum/CoordinateTranslate.js
New file
@@ -0,0 +1,229 @@
/*
 * @Author       : yuan
 * @Date         : 2025-08-15 14:06:49
 * @LastEditors  : yuan
 * @LastEditTime : 2025-08-15 14:06:59
 * @FilePath     : \src\utils\cesium\frustum\CoordinateTranslate.js
 * @Description  :
 * Copyright 2025 OBKoro1, All Rights Reserved.
 * 2025-08-15 14:06:49
 */
let ECEF = (function () {
    var _ = function () {
        this.PI = 3.141592653589793238
        this.a = 6378137.0
        this.b = 6356752.3142
        this.f = (this.a - this.b) / this.a
        this.e_sq = this.f * (2.0 - this.f)
        this.ee = 0.00669437999013
        this.WGSF = 1 / 298.257223563
        this.WGSe2 = this.WGSF * (2 - this.WGSF)
        this.WGSa = 6378137.00000
        this.EPSILON = 1.0e-12
    }
    _.prototype.CalculateCoordinates = function (point, azimuth, elevation, distance) {
        var vertical_height = distance * Math.sin(2 * this.PI / 360 * elevation)//垂直高度
        var horizontal_distance = distance * Math.cos(2 * this.PI / 360 * elevation)//水平距离
        if (azimuth > 360) azimuth = azimuth % 360
        if (azimuth < 0) azimuth = 360 + (azimuth % 360)
        var point1 = this.lonLat2WebMercator(point)
        var lnglat = null
        var x_length, y_length
        if (azimuth <= 90) {//第四象限
            x_length = horizontal_distance * Math.cos(2 * this.PI / 360 * azimuth)
            y_length = horizontal_distance * Math.sin(2 * this.PI / 360 * azimuth)
            lnglat = {
                x: point1.x + x_length,
                y: point1.y - y_length
            }
        } else if (azimuth > 90 && azimuth <= 180) {//第三象限
            x_length = horizontal_distance * Math.sin(2 * this.PI / 360 * (azimuth - 90))
            y_length = horizontal_distance * Math.cos(2 * this.PI / 360 * (azimuth - 90))
            lnglat = {
                x: point1.x - x_length,
                y: point1.y - y_length
            }
        } else if (azimuth > 180 && azimuth <= 270) {//第二象限
            x_length = horizontal_distance * Math.cos(2 * this.PI / 360 * (azimuth - 180))
            y_length = horizontal_distance * Math.sin(2 * this.PI / 360 * (azimuth - 180))
            lnglat = {
                x: point1.x - x_length,
                y: point1.y + y_length
            }
        } else {//第一象限
            x_length = horizontal_distance * Math.sin(2 * this.PI / 360 * (azimuth - 270))
            y_length = horizontal_distance * Math.cos(2 * this.PI / 360 * (azimuth - 270))
            lnglat = {
                x: point1.x + x_length,
                y: point1.y + y_length
            }
        }
        lnglat = this.webMercator2LonLat(lnglat)
        return {
            lng: lnglat.x,
            lat: lnglat.y,
            height: vertical_height
        }
    }
    /*
       *经纬度转Web墨卡托
       *@lonLat 经纬度
       */
    _.prototype.lonLat2WebMercator = function (lonLat) {
        let x = lonLat.x * this.a / 180
        let y = Math.log(Math.tan((90 + lonLat.y) * this.PI / 360)) / (this.PI / 180)
        y = y * this.a / 180
        return {
            x: x,
            y: y
        }
    }
    /*
       *Web墨卡托转经纬度
       *@mercator 平面坐标
       */
    _.prototype.webMercator2LonLat = function (mercator) {
        let x = mercator.x / this.a * 180
        let y = mercator.y / this.a * 180
        y = 180 / this.PI * (2 * (Math.exp(y * this.PI / 180)) - this.PI / 2)
        return {
            x: x,
            y: y
        }
    }
    _.prototype.get_atan = function (z, y) {
        let x
        if (z == 0) {
            x = this.PI / 2
        } else {
            if (y == 0) {
                x = this.PI
            } else {
                x = Math.atan(Math.abs(y / z))
                if ((y > 0) && (z < 0)) {
                    x = this.PI - x
                } else if ((y < 0) && (z < 0)) {
                    x = this.PI + x
                } else if ((y < 0) && (z > 0)) {
                    x = 2 * this.M_PI - x
                }
            }
        }
        return x
    }
    //WGS84转ECEF坐标系
    _.prototype.ConvertLLAToXYZ = function (LLACoor) {
        let lon = this.PI / 180 * LLACoor.longitude
        let lat = this.PI / 180 * LLACoor.latitude
        let H = LLACoor.altitude
        let N0 = this.a / Math.sqrt(1.0 - this.ee * Math.sin(lat) * Math.sin(lat))
        let x = (N0 + H) * Math.cos(lat) * Math.cos(lon)
        let y = (N0 + H) * Math.cos(lat) * Math.sin(lon)
        let z = (N0 * (1.0 - this.ee) + H) * Math.sin(lat)
        return {
            x: x,
            y: y,
            z: z
        }
    }
    //ECEF坐标系转WGS84
    _.prototype.ConvertXYZToLLA = function (XYZCoor) {
        let longitude = this.get_atan(XYZCoor.x, XYZCoor.y)
        if (longitude < 0) {
            longitude = longitude + this.PI
        }
        let latitude = this.get_atan(Math.sqrt(XYZCoor.x * XYZCoor.x + XYZCoor.y * XYZCoor.y), XYZCoor.z)
        let W = Math.sqrt(1 - this.WGSe2 * Math.sin(latitude) * Math.sin(latitude))
        let N = this.WGSa / W
        let B1
        do {
            B1 = latitude
            W = Math.sqrt(1 - this.WGSe2 * Math.sin(B1) * Math.sin(B1))
            N = this.WGSa / W
            latitude = this.get_atan(Math.sqrt(XYZCoor.x * XYZCoor.x + XYZCoor.y * XYZCoor.y), (XYZCoor.z + N * this.WGSe2 * Math.sin(B1)))
        }
        while (Math.abs(latitude - B1) > this.EPSILON)
        var altitude = Math.sqrt(XYZCoor.x * XYZCoor.x + XYZCoor.y * XYZCoor.y) / Math.cos(latitude) - this.WGSa / Math.sqrt(1 - this.WGSe2 * Math.sin(latitude) * Math.sin(latitude))
        return {
            longitude: longitude * 180 / this.PI,
            latitude: latitude * 180 / this.PI,
            altitude: altitude
        }
    }
    /*北东天坐标系转WGS84
    @ a A点坐标
    @ p 相对参数,距离、方位角、仰角
    */
    //    俯视角pitch -elevation
    //航向角heading(yaw) -azimuth
    _.prototype.enu_to_ecef = function (a, p) {
        //距离
        let distance = p.distance
        //方位角
        let azimuth = p.azimuth
        //仰角
        let elevation = p.elevation
        let zUp = elevation >= 0 ? distance * Math.sin(this.PI / 180 * elevation) : (-1) * distance * Math.sin(this.PI / 180 * Math.abs(elevation))
        let d = distance * Math.cos(this.PI / 180 * Math.abs(elevation))
        let xEast
        let yNorth
        if (azimuth <= 90) {
            xEast = d * Math.sin(this.PI / 180 * azimuth)
            yNorth = d * Math.cos(this.PI / 180 * azimuth)
        } else if (azimuth > 90 && azimuth < 180) {
            xEast = d * Math.cos(this.PI / 180 * (azimuth - 90))
            yNorth = (-1) * d * Math.sin(this.PI / 180 * (azimuth - 90))
        } else if (azimuth > 180 && azimuth < 270) {
            xEast = (-1) * d * Math.sin(this.PI / 180 * (azimuth - 180))
            yNorth = (-1) * d * Math.cos(this.PI / 180 * (azimuth - 180))
        } else {
            xEast = (-1) * d * Math.sin(this.PI / 180 * (360 - azimuth))
            yNorth = d * Math.cos(this.PI / 180 * (360 - azimuth))
        }
        let lamb = this.radians(a.latitude)
        let phi = this.radians(a.longitude)
        let h0 = a.altitude
        let s = Math.sin(lamb)
        let N = this.a / Math.sqrt(1.0 - this.e_sq * s * s)
        let sin_lambda = Math.sin(lamb)
        let cos_lambda = Math.cos(lamb)
        let sin_phi = Math.sin(phi)
        let cos_phi = Math.cos(phi)
        let x0 = (h0 + N) * cos_lambda * cos_phi
        let y0 = (h0 + N) * cos_lambda * sin_phi
        let z0 = (h0 + (1 - this.e_sq) * N) * sin_lambda
        let t = cos_lambda * zUp - sin_lambda * yNorth
        let zd = sin_lambda * zUp + cos_lambda * yNorth
        let xd = cos_phi * t - sin_phi * xEast
        let yd = sin_phi * t + cos_phi * xEast
        return this.ConvertXYZToLLA({
            x: xd + x0,
            y: yd + y0,
            z: zd + z0
        })
    }
    _.prototype.radians = function (degree) {
        return this.PI / 180 * degree
    }
    return _
})()
export default ECEF
applications/task-work-order/src/utils/cesium/kmz.js
@@ -209,6 +209,11 @@
}
/**
 * 递归处理对象,移除 #text 键并返回其值,同时处理数值字符串到数字的转换
 * @param {any} obj - 需要处理的对象或值
 * @returns {any} 处理后的对象或值
 */
export function removeTextKey(obj) {
    if (typeof obj !== 'object' || obj === null) {
        // 如果是数值字符串,转换为数值
@@ -219,18 +224,21 @@
    }
    if (Array.isArray(obj)) {
        // 递归处理数组中的每个元素
        return obj.map(item => removeTextKey(item));
    }
    if (Object.prototype.hasOwnProperty.call(obj, '#text')) {
        // 如果 #text 的值是数值字符串,转换为数值
        // 如果存在 #text 属性,返回其值(如果是数值字符串则转换为数字)
        const textValue = obj['#text'];
        return typeof textValue === 'string' && /^-?\d+(\.\d+)?$/.test(textValue) ? Number(textValue) : textValue;
    }
    // 递归处理对象属性
    const newObj = {};
    for (const key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            // 如果属性值是空对象,则设为空字符串;否则递归处理
            newObj[key] = Object.keys(obj[key]).length === 0 ? '' : removeTextKey(obj[key]);
        }
    }
applications/task-work-order/src/utils/cesium/mapUtil.js
@@ -1,7 +1,8 @@
import * as Cesium from 'cesium'
import * as turf from '@turf/turf'
const { VITE_APP_BASE, VITE_APP_ENV, VITE_APP_REGION_URL } = import.meta.env
import store from '@/store'
const { VITE_APP_BASE, VITE_APP_ENV, VITE_APP_REGION_URL } = import.meta.env
export const getLngLatDistance = (lat1, lng1, lat2, lng2) => {
    const radLat1 = (lat1 * Math.PI) / 180.0
    const radLat2 = (lat2 * Math.PI) / 180.0
@@ -190,6 +191,66 @@
    const newArr = arr.sort((a, b) => a.distance - b.distance)
    return newArr[0]
}
// 获取多边形内部数据
export const samplePolygonInterior = (positions, samplingDistance) => {
    if (!positions.length) return []
    let minLng = Infinity,
        maxLng = -Infinity
    let minLat = Infinity,
        maxLat = -Infinity
    positions.forEach(pos => {
        minLng = Math.min(minLng, pos.lng)
        maxLng = Math.max(maxLng, pos.lng)
        minLat = Math.min(minLat, pos.lat)
        maxLat = Math.max(maxLat, pos.lat)
    })
    //  将 Cesium 多边形格式转为 Turf 格式
    const turfPolygonCoords = positions.map(pos => [pos.lng, pos.lat])
    turfPolygonCoords.push([positions[0].lng, positions[0].lat]) // 闭合多边形
    const turfPolygon = turf.polygon([turfPolygonCoords])
    //  生成网格点并判断是否在多边形内
    const sampledPoints = []
    for (let lng = minLng; lng <= maxLng; lng += samplingDistance) {
        for (let lat = minLat; lat <= maxLat; lat += samplingDistance) {
            // 创建 Turf 点
            const turfPoint = turf.point([lng, lat])
            // Turf 原生方法判断点是否在多边形内
            if (turf.booleanPointInPolygon(turfPoint, turfPolygon)) {
                sampledPoints.push({
                    lng: Number(lng.toFixed(6)),
                    lat: Number(lat.toFixed(6)),
                    height: 0,
                })
            }
        }
    }
    return sampledPoints
}
// 获取多边形面内的最高点(相对地形高度)
export const getPolygonHighestPoint = async (positions, viewer, samplingDistance = 0.0002) => {
    try {
        const interiorPoints = samplePolygonInterior(positions, samplingDistance, viewer)
        const allPoints = [...positions, ...interiorPoints]
        // 批量获取所有点的地形高度
        const pointsWithTerrain = await getPointPositionsHeight(allPoints, viewer)
        // console.log('所有点的地形高度', pointsWithTerrain)
        // 地形高度(ASL)最高的点
        const highestTerrainPoint = pointsWithTerrain.reduce(
            (max, curr) => (curr.ASL > max.ASL ? curr : max),
            pointsWithTerrain[0]
        )
        // console.log('地形最高点信息:', highestTerrainPoint)
        return highestTerrainPoint
    } catch (error) {
        console.error('获取地形最高点失败:', error)
        return null
    }
}
// 获取当前经纬度地形数据
export const getLnglatAltitude = (longitude, latitude, viewer) => {
@@ -285,31 +346,24 @@
    return new Promise((resolve, reject) => {
        if (!data || !data.length) {
            resolve([])
            return
        }
        // 假设 viewer 已经初始化并且 terrainProvider 是有效的
        const terrainProvider = viewer?.terrainProvider
        // 创建 Cartographic 对象
        const cartographics = data.map(item => {
            const { lng, lat } = item
            return Cesium.Cartographic.fromDegrees(Number(lng), Number(lat))
        })
        // 获取地形数据的Promise
        const promise = Cesium.sampleTerrainMostDetailed(terrainProvider, cartographics)
        // 使用 Cesium.when 处理 Promise
        promise
            .then(function (updatedPositions) {
                // updatedPositions 是一个数组,包含更新后的 Cartographic 对象
                //  是一个数组,包含更新后的 Cartographic 对象
                const newPosition = updatedPositions.map((item, index) => {
                    const longitude = Cesium.Math.toDegrees(item.longitude)
                    const latitude = Cesium.Math.toDegrees(item.latitude)
                    let pointData = {
                        ...data[index],
                        longitude,
@@ -317,18 +371,14 @@
                        ASL: Number(item.height),
                        customHeight: Number(item.height),
                    }
                    if (droneHeight) pointData.TH = Number(item.height) + Number(droneHeight)
                    return pointData
                })
                resolve(newPosition)
                // 在这里,你可以使用 height 值进行后续操作
            })
            .catch(function (error) {
                // console.error('获取高程时发生错误:', error);
                reject(error)
                console.log('获取高程时发生错误:', error)
                resolve(data.map(item => ({ ...item, ASL: 0, customHeight: 0, longitude: item.lng, latitude: item.lat })))
            })
    })
}
@@ -410,11 +460,15 @@
    if (!Array.isArray(positionsData) || positionsData.length === 0) return
    // 如果是一个点,加两个点方便后续生成外包围盒
    if (positionsData.length === 1){
        const [lon,lat,height] = positionsData[0]
        positionsData = [[lon+0.001,lat+0.001,height],[lon-0.001,lat-0.001,height],[lon,lat,height]]
    if (positionsData.length === 1) {
        const [lon, lat, height = 0] = positionsData[0]
        positionsData = [
            [lon + 0.001, lat + 0.001, height],
            [lon - 0.001, lat - 0.001, height],
            [lon, lat, height],
        ]
    }
    const positions = positionsData.map(([lon, lat, height]) =>
    const positions = positionsData.map(([lon, lat, height = 0]) =>
        Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat), Number(height || 0))
    )
@@ -439,7 +493,7 @@
            range: boundingSphere.radius * multiple,
        },
        complete: () => {
            // 飞行完成后检查相机高度是否小于最高点,如果小于直接飞到最高点
            // 飞行完成后检查相机高度是否小于最高点
            const cameraHeight = Cesium.Cartographic.fromCartesian(viewer.camera.position).height
            if (cameraHeight < maxHeight) {
                viewer.camera.flyTo({
@@ -529,7 +583,7 @@
// 辅助函数:创建旋转文字的画布
export function createRotatedTextCanvas(time) {
    const prefix = '预计'
    const prefix = '预计到达'
    const suffix = '分钟'
    const canvas = document.createElement('canvas')
    const context = canvas.getContext('2d')
@@ -537,7 +591,9 @@
    context.font = 'bold 16px Source Han Sans CN'
    // 测量各部分文本宽度
    const prefixWidth = context.measureText(prefix).width
    const timeWidth = context.measureText(time).width
    let timeWidth = context.measureText(time).width
    const timeWidthMore = `${time >= 10 ? 6 : 0}`
    timeWidth = timeWidth + Number(timeWidthMore)
    const suffixWidth = context.measureText(suffix).width
    const totalWidth = prefixWidth + timeWidth + suffixWidth
    const textHeight = 20 // 估算高度
@@ -550,17 +606,116 @@
    context.textAlign = 'left' // 改为左对齐,方便分段绘制
    // 清除画布
    context.clearRect(0, 0, canvas.width, canvas.height)
    // 绘制“预计”(白色)
    // 绘制“预计到达”(白色)
    context.fillStyle = '#FFFFFF'
    context.fillText(prefix, 10, 15)
    // 绘制时间(蓝色)
    context.font = 'bold 30px Source Han Sans CN'
    context.font = 'bold 24px Source Han Sans CN'
    context.fillStyle = '#1EE7E7' // 亮蓝色
    context.fillText(time, 10 + prefixWidth, 12)
    // 设置宽度
    context.textBaseline = 'middle'
    context.fillText(time, 10 + prefixWidth, 14)
    // 绘制“分钟”(白色)
    context.font = 'bold 16px Source Han Sans CN'
    context.fillStyle = '#FFFFFF'
    context.fillText(suffix, 20 + prefixWidth + timeWidth, 15)
    context.fillText(suffix, 14 + prefixWidth + timeWidth, 15)
    return canvas
}
export const saveCurrentCameraPosition = viewer => {
    const position = viewer?.camera.position.clone() // 相机位置(Cartesian3)
    const heading = viewer?.camera.heading // 航向角(弧度)
    const pitch = viewer?.camera.pitch // 俯仰角(弧度)
    const roll = viewer?.camera.roll // 翻滚角(弧度)
    store.commit('setCameraPosition', {
        duration: 0,
        destination: position,
        orientation: { heading, pitch, roll },
    })
}
// 连接地面线
export function getDockPolyLine(data, viewer) {
    if (!data.longitude || !data.latitude) return {}
    return {
        positions: new Cesium.CallbackProperty(() => {
            const pointPosition = Cesium.Cartesian3.fromDegrees(+data.longitude, +data.latitude, +data.height)
            if (!pointPosition) return []
            // 获取点的位置
            const cartographic = Cesium.Cartographic.fromCartesian(pointPosition)
            // 获取地形高度
            const terrainHeight = viewer.scene.globe.getHeight(cartographic) || 0
            // 创建地面点位置
            const groundPosition = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, terrainHeight)
            return [pointPosition, groundPosition]
        }, false),
        width: 0.5,
        material: Cesium.Color.WHITE,
    }
}
// 飞向一个机巢中心
export const flyDockCenter = (viewer, info) => {
    const { longitude = 115, latitude = 28, height = 0 } = info
    const position = Cesium.Cartesian3.fromDegrees(+longitude, +latitude, +height)
    const droneEntity = viewer?.entities.add({ position: position, point: { pixelSize: 0 } })
    viewer?.flyTo(droneEntity, {
        offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-60), 22000),
        duration: 0.5,
        complete: () => {
            viewer?.entities.remove(droneEntity)
        },
    })
}
// 在固定的经度上获得高度
export function getHeightOnFixedLonLat(ray, pointParams, ellipsoid = Cesium.Ellipsoid.WGS84) {
    const { longitude: lon, latitude: lat } = pointParams
    // 1. 地表点(高度=0)
    const p0 = Cesium.Cartesian3.fromDegrees(lon, lat, 0, ellipsoid)
    // 2. 该点处的法线方向(up 向量,单位向量)
    const up = ellipsoid.geodeticSurfaceNormal(p0, new Cesium.Cartesian3())
    // 3. 射线起点和方向
    const r0 = ray.origin // 相机位置
    const rd = ray.direction // 射线方向(已归一化?不一定,但不影响)
    // 4. 解:求直线 L(t) = p0 + t * up 与射线 R(s) = r0 + s * rd 的最近点
    // 使用向量公式求两条直线的最近点参数 t
    const w0 = Cesium.Cartesian3.subtract(p0, r0, new Cesium.Cartesian3())
    const a = Cesium.Cartesian3.dot(up, up) // = 1(因为 up 是单位向量)
    const b = Cesium.Cartesian3.dot(up, rd)
    const c = Cesium.Cartesian3.dot(rd, rd) // = |rd|^2
    const d = Cesium.Cartesian3.dot(up, w0)
    const e = Cesium.Cartesian3.dot(rd, w0)
    const denom = a * c - b * b
    let t
    if (Math.abs(denom) < Cesium.Math.EPSILON10) {
        // 两直线平行,取 t = 0
        t = 0
    } else {
        t = (b * e - c * d) / denom
    }
    // 5. 得到高度线上对应的点
    const resultPoint = Cesium.Cartesian3.add(
        p0,
        Cesium.Cartesian3.multiplyByScalar(up, t, new Cesium.Cartesian3()),
        new Cesium.Cartesian3()
    )
    // 6. 转回 Cartographic 获取精确高度
    const carto = Cesium.Cartographic.fromCartesian(resultPoint, ellipsoid)
    return {
        cartesian: resultPoint,
        height: carto.height,
        longitude: Cesium.Math.toDegrees(carto.longitude),
        latitude: Cesium.Math.toDegrees(carto.latitude),
    }
}
applications/task-work-order/src/utils/cesium/publicCesium.js
@@ -70,6 +70,7 @@
 * @param {boolean} [options.flyToContour=false] - 是否飞行到轮廓,默认为 false
 * @param {number} [options.multiple=1.4] - 范围倍数
 * @param {number} [options.dockOptions={}] - 机巢显示
 * @param {array} [options.terrainLoadCallback] - 地形加载回调
 * @param {array} [options.boundaryChange] - 边界改变事件
 * @param {array} [options.boundaryColor] - 边界颜色
 * @param {array} [options.dockRangeType] - 机巢范围类型 1显示覆盖圆;2显示覆盖圈
@@ -92,15 +93,14 @@
            dom,
            flatMode = true,
            terrain = false,
            layerMode = 0,
            layerMode = 17,
            contour = true,
            flyToContour = false,
            multiple = 1.4,
            dockOptions = {},
            boundaryColor = '#7FFFD4',
            boundaryChange,
            terrainLoadCallback,
            boundaryColor = '#00F9EC',
            dockRangeType = 1,
            useDockHeight = false
        } = options
@@ -120,6 +120,10 @@
            baseLayer: false,
            fullscreenButton: false,
        }
        // 尝试解决出现多个id相同的dom
        const domIdList = document.querySelectorAll(`#${dom}`)
        if (domIdList.length>1) domIdList[1].remove()
        this.viewer = new Viewer(dom, viewerOptions)
        this.viewerDom = dom
@@ -143,33 +147,47 @@
        this.viewer?.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK) // 禁用双击
        if (terrain) {
            // 正则:第一位非0,第二位任意数字,后10位都是0
            const re = /^[1-9]\d0{10}$/
            let result = null
            // 1️⃣先判断 areaCode
            if (re.test(store.state.user.userInfo.detail.areaCode)) {
                result = store.state.user.userInfo.detail.areaCode.slice(0, 6)
            } else {
                // 2️⃣如果不满足,从 ancestors 里找
                const matchedItems = store.state.user.userInfo.detail.ancestors
                    .split(',')
                    .filter(item => re.test(item)) // 过滤出符合条件的
                // 取第一个符合条件的前6位(可以改成 last one 看需求)
                if (matchedItems.length > 0) {
                    result = matchedItems[0].slice(0, 6)
                }
            }
            try {
                Cesium.CesiumTerrainProvider.fromUrl(`${import.meta.env.VITE_APP_TERRAIN_URL}${result}`, {
                    requestVertexNormals: true, // 启用地形法线增强立体感
                    requestWaterMask: true, // 启用水体遮罩效果
                }).then(terrainProvider => {
                    this.viewer.terrainProvider = terrainProvider
                })
                const noTerrainMechanism = ['1962779164650135554']
                if (noTerrainMechanism.includes(store.state.user.userInfo?.deptId)) {
                    // 使用Cesium World Terrain
                    Cesium.createWorldTerrainAsync({
                        requestWaterMask: false,    // 请求水域效果
                        requestVertexNormals: true // 请求光照和地形法线
                    }).then(terrainProvider => {
                        this.viewer.terrainProvider = terrainProvider
                        terrainLoadCallback?.()
                    })
                } else {
                    /*// 正则:第一位非0,第二位任意数字,后10位都是0
                    const re = /^[1-9]\d0{10}$/
                    let result = null
                    // 1️⃣先判断 areaCode
                    if (re.test(store.state.user.userInfo.detail.areaCode)) {
                        result = store.state.user.userInfo.detail.areaCode.slice(0, 6)
                    } else {
                        // 2️⃣如果不满足,从 ancestors 里找
                        const matchedItems = store.state.user.userInfo.detail.ancestors
                            .split(',')
                            .filter(item => re.test(item)) // 过滤出符合条件的
                        // 取第一个符合条件的前6位(可以改成 last one 看需求)
                        if (matchedItems.length > 0) {
                            result = matchedItems[0].slice(0, 6)
                        }
                    }
                    // 使用公司地形
                    Cesium.CesiumTerrainProvider.fromUrl(`${import.meta.env.VITE_APP_TERRAIN_URL}${result}`, {
                        requestVertexNormals: true, // 启用地形法线增强立体感
                        requestWaterMask: true, // 启用水体遮罩效果
                    }).then(terrainProvider => {
                        this.viewer.terrainProvider = terrainProvider
                        terrainLoadCallback?.()
                    })*/
                }
            } catch (error) {
                console.error('地形加载失败:', error)
            }
@@ -180,6 +198,12 @@
        this.switchFlatMode(flatMode)
        this.switchContour(contour)
        flyToContour && this.flyToContour(contour)
        if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) {
            let dpr = window.devicePixelRatio
            while (dpr >= 2.0) dpr /= 2.0 // 避免过高缩放导致模糊
            this.viewer.resolutionScale = dpr // 设置分辨率缩放比例
        }
    }
    getViewer () {
@@ -188,53 +212,69 @@
    // 销毁viewer
    viewerDestroy (removeDom = true) {
        if (!this.viewer) return
        // // 👇 关键:先停止所有动态行为
        // this.viewer.clock.shouldAnimate = false;
        // this.viewer.scene.requestRenderMode = false;
        // // 取消所有可能的 pending 渲染
        // if (this.viewer.cesiumWidget) {
        //     this.viewer.cesiumWidget.render = () => {}; // 空函数覆盖
        // }
        // // 清理实体和图层
        // this.viewer?.entities.removeAll();
        // this.viewer?.imageryLayers.removeAll();
        // // 清理数据源
        // try {
        //     const dataSources = this.viewer.dataSources;
        //     while (dataSources.length > 0) {
        //         dataSources.remove(dataSources.get(0));
        //     }
        // } catch (e) {
        //     console.warn('清理数据源时出错:', e);
        // }
        // // 清理场景中的图元
        // try {
        //     this.viewer?.scene.primitives.removeAll();
        // } catch (e) {
        //     console.warn('清理图元时出错:', e);
        // }
        // // 获取webgl上下文
        // let gl = this.viewer?.scene.context._originalGLContext
        // if (gl) {
        //     try {
        //         gl.canvas.width = 1
        //         gl.canvas.height = 1
        //         gl.getExtension("WEBGL_lose_context").loseContext()
        //     } catch (e) {
        //         console.warn('释放WebGL上下文时出错:', e)
        //     }
        //     gl = null
        // }
        this.viewer?.destroy() // 销毁Viewer实例
        this.viewer = null
        smallMpaCenterDistance = null
        if (this.viewer) {
            this.viewer?.entities.removeAll()
            this.viewer?.imageryLayers.removeAll()
            this.viewer?.dataSources.removeAll()
            // try {
            //     this.viewer?.scene?.primitives?.removeAll()
            //     this.viewer?.entities?.removeAll()
            // } catch (e) {
            //     console.warn('清理资源时出错:', e)
            //     console.log(this.viewer, 1111111111111)
            // }
            // 获取webgl上下文
            let gl = this.viewer?.scene.context._originalGLContext
            gl.canvas.width = 1
            gl.canvas.height = 1
            gl.getExtension("WEBGL_lose_context").loseContext()
            gl = null
            this.viewer?.destroy() // 销毁Viewer实例
            this.viewer = null
            var cesiumContainer = document.getElementById(this.viewerDom)
            if (cesiumContainer && removeDom) {
                cesiumContainer.remove() // 移除与地图相关的DOM元素
                this.viewerDom = null
            }
        var cesiumContainer = document.getElementById(this.viewerDom)
        if (cesiumContainer && removeDom) {
            cesiumContainer.remove() // 移除与地图相关的DOM元素
            this.viewerDom = null
        }
    }
    setShowDock (sns) {
        this.boundary?.setShowDock(sns)
    }
    setDockCoverColor (sns) {
        this.boundary?.setDockCoverColor(sns)
    }
    // 切换轮廓显示
    async switchContour (open) {
        if (open) {
            await this.boundary?.openContour()
        } else {
            this.boundary?.closeContour()
        }
    }
    // // 飞向轮廓居中
    // 飞向轮廓居中
    flyToContour () {
        this.boundary?.flyToBoundary()
    }
@@ -257,7 +297,7 @@
        this.globalBaseMapLayers?.forEach(item => {
            if (item.mapLayer) item.mapLayer.show = false
        })
        // store.state.common.mapSetting.mode = mode
        store.state.common.mapSetting.mode = mode
        // 标准地图(天地图矢量)加载
        if (mode === 0) {
            mapLayers.push(
@@ -324,9 +364,7 @@
            this.viewer?.imageryLayers.lowerToBottom(find.mapLayer)
        })
    }
    // 设置或重置滤镜
    setCurrentLayerFilter (data, mode) {
        if (mode === 17) {
            data.mapLayer.brightness = 0.6 // 亮度(默认值)
applications/task-work-order/src/utils/cesium/use-kmz-tsa.js
New file
@@ -0,0 +1,675 @@
import _ from 'lodash'
import JSZIP from 'jszip'
import { JSONToXML } from './kmz'
import { getDateFromTimestamp } from '@/utils/date'
export default function kmzFile () {
    // 云台信息
    const droneModel = {
        52: '0',
        53: '1',
        66: '0',
        67: '1',
        80: '0',
        81: '1',
    }
    // 创建图斑航线
    const create = async (waylineBasicInfo, lnglats, spotList) => {
        try {
            const date = new Date()
            const username = window.localStorage.getItem('bs_username')
            const updateTime = date.getTime()
            const fileName = '图斑航线规划_' + date.getTime()
            // 无人机机型、云台型号、航线高度
            const { droneEnumValue, payloadEnumValue, height } = waylineBasicInfo
            const defaultHeight = height || 100
            // 起始点位 (参考点位-具体以机场为准)
            const startPoint = lnglats[0]
            const takeOffPoint = [startPoint[1], startPoint[0], 0].join(',')
            // template.xml对象模版
            const fileTemplate = {
                author: { '#text': username },
                createTime: { '#text': updateTime },
                updateTime: { '#text': updateTime },
                missionConfig: {
                    // 飞向首航点模式
                    flyToWaylineMode: { '#text': 'safely' },
                    // 航线结束动作
                    finishAction: { '#text': 'goHome' },
                    // 失控是否继续执行航线
                    exitOnRCLost: { '#text': 'goContinue' },
                    // 失控动作类型
                    executeRCLostAction: { '#text': 'goBack' },
                    // 安全起飞高度
                    takeOffSecurityHeight: { '#text': 20 },
                    // 参考起飞点
                    takeOffRefPoint: { '#text': takeOffPoint },
                    // 参考起飞点绝对高度
                    takeOffRefPointAGLHeight: { '#text': 0 },
                    // 全局航线过渡速度
                    globalTransitionalSpeed: { '#text': 10 },
                    // 全局返航高度
                    globalRTHHeight: { '#text': defaultHeight },
                    // 无人机参数
                    droneInfo: {
                        droneEnumValue: { '#text': droneEnumValue },
                        droneSubEnumValue: {
                            '#text': droneModel[payloadEnumValue],
                        },
                    },
                    // 云台参数
                    payloadInfo: {
                        payloadEnumValue: { '#text': payloadEnumValue },
                        payloadSubEnumValue: {
                            '#text': droneModel[payloadEnumValue],
                        },
                        payloadPositionIndex: { '#text': 0 },
                    },
                },
                Folder: {
                    // 预定义模板类型 - 航点飞行
                    templateType: { '#text': 'waypoint' },
                    // 是否使用全局航线过渡速度
                    useGlobalTransitionalSpeed: { '#text': 0 },
                    templateId: { '#text': 0 },
                    // 坐标系参数信息
                    waylineCoordinateSysParam: {
                        // 经纬度坐标系 - 固定值
                        coordinateMode: { '#text': 'WGS84' },
                        // 航点高程参考平面    - 相对高度
                        heightMode: { '#text': 'relativeToStartPoint' },
                    },
                    // 全局航线飞行速度
                    autoFlightSpeed: { '#text': 10 },
                    // 云台俯仰角控制模式
                    gimbalPitchMode: { '#text': 'usePointSetting' },
                    // 全局高度
                    globalHeight: { '#text': '100' },
                    // 全局偏航角模式参数 - 可使用当前默认值
                    globalWaypointHeadingParam: {
                        waypointHeadingMode: { '#text': 'followWayline' },
                        waypointHeadingAngle: { '#text': 0 },
                        waypointPoiPoint: {
                            '#text': '0.000000,0.000000,0.000000',
                        },
                        waypointHeadingPathMode: { '#text': 'followBadArc' },
                        waypointHeadingPoiIndex: { '#text': 0 },
                    },
                    // 全局航点类型 - 直线飞行,飞行器到点停
                    globalWaypointTurnMode: {
                        '#text': 'toPointAndStopWithDiscontinuityCurvature',
                    },
                    // 全局航段轨迹是否尽量贴合直线
                    globalUseStraightLine: { '#text': 1 },
                    // 航点点位信息 - 位置、事件等
                    Placemark: [],
                    // payloadParam: {
                    //   payloadPositionIndex: { '#text': 0 },
                    //   focusMode: { '#text': 'firstPoint' },
                    //   meteringMode: { '#text': 'average' },
                    //   returnMode: { '#text': 'singleReturnFirst' },
                    //   samplingRate: { '#text': '240000' },
                    //   scanningMode: { '#text': 'repetitive' },
                    //   imageFormat: { '#text': 'wide,zoom' },
                    // },
                },
            }
            // 航点创建
            const polygonNo = {
                no: '',
                index: 0,
            }
            lnglats.forEach((lnglat, index) => {
                const obj = spotList.find(spot => spot.dkfw.find(item => _.isEqual(item, lnglat)))
                let fileSuffix = ''
                if (obj) {
                    if (polygonNo.no === obj.dkbh) {
                        polygonNo.index++
                    } else {
                        polygonNo.no = obj.dkbh
                        polygonNo.index = 1
                    }
                    fileSuffix = `图斑航线拍摄(${obj.dkbh}-${polygonNo.index})`
                } else {
                    fileSuffix = '图斑航线拍摄(起点)'
                }
                const placemark = {
                    Point: {
                        // 航点经纬度
                        coordinates: { '#text': `${lnglat[0]},${lnglat[1]}` },
                    },
                    // 航点index
                    index: { '#text': index },
                    // 全局航线高度(椭球高)
                    ellipsoidHeight: { '#text': defaultHeight },
                    // 全局航线高度 (EGM96海拔高/相对起飞点高度/AGL相对地面高度)
                    height: { '#text': defaultHeight },
                    // 航点飞行速度
                    waypointSpeed: { '#text': 10 },
                    // 沿航线方向 - 可使用当前默认值
                    waypointHeadingParam: {
                        waypointHeadingMode: { '#text': 'followWayline' },
                        waypointHeadingAngle: { '#text': 0 },
                        waypointPoiPoint: {
                            '#text': '0.000000,0.000000,0.000000',
                        },
                        waypointHeadingPathMode: { '#text': 'followBadArc' },
                        waypointHeadingPoiIndex: { '#text': 0 },
                    },
                    // 航点类型
                    waypointTurnParam: {
                        waypointTurnMode: {
                            '#text': 'toPointAndStopWithDiscontinuityCurvature',
                        },
                        waypointTurnDampingDist: { '#text': 0.2 },
                    },
                    // 是否全局参数
                    useGlobalHeight: { '#text': 1 },
                    useGlobalSpeed: { '#text': 1 },
                    useGlobalHeadingParam: { '#text': 1 },
                    useGlobalTurnParam: { '#text': 1 },
                    useStraightLine: { '#text': 1 },
                    // 事件组
                    actionGroup: {
                        actionGroupId: { '#text': index },
                        actionGroupStartIndex: { '#text': index },
                        actionGroupEndIndex: { '#text': index },
                        actionGroupMode: { '#text': 'sequence' },
                        actionTrigger: {
                            actionTriggerType: { '#text': 'reachPoint' },
                        },
                        // 单个事件 - 如果是多个事件action可改成数组 action: [{}]
                        action: {
                            actionId: { '#text': 0 },
                            actionActuatorFunc: { '#text': 'takePhoto' },
                            actionActuatorFuncParam: {
                                fileSuffix: { '#text': fileSuffix },
                                payloadPositionIndex: { '#text': '0' },
                                useGlobalPayloadLensIndex: { '#text': '1' },
                            },
                        },
                    },
                }
                fileTemplate.Folder.Placemark.push(placemark)
            })
            const fileWaylines = waylinesContext(fileTemplate)
            return {
                fileName,
                template: fileTemplate,
                waylines: fileWaylines,
            }
        } catch (error) {
            console.log(error)
            return false
        }
    }
    // 创建正常航线
    const createNoramlWaylines = (waylineBasicInfo, lnglats) => {
        try {
            const date = new Date()
            const username = window.localStorage.getItem('bs_username')
            const updateTime = date.getTime()
            const { year, month, day, hours, minutes } = getDateFromTimestamp(updateTime)
            const fileName = '新建航线_' + year + month + day + hours + minutes
            // 无人机机型、云台型号、航线高度
            const { droneEnumValue, payloadEnumValue, height, waylineName } = waylineBasicInfo
            const defaultHeight = height || 100
            // 起始点位 (参考点位-具体以机场为准)
            const startPoint = lnglats[0]
            const takeOffPoint = [startPoint.latitude, startPoint.longitude, startPoint.height].join(',')
            // template.xml对象模版
            const fileTemplate = {
                author: { '#text': username },
                createTime: { '#text': updateTime },
                updateTime: { '#text': updateTime },
                missionConfig: {
                    // 飞向首航点模式
                    flyToWaylineMode: { '#text': 'safely' },
                    // 航线结束动作
                    finishAction: { '#text': 'goHome' },
                    // 失控是否继续执行航线
                    exitOnRCLost: { '#text': 'goContinue' },
                    // 失控动作类型
                    executeRCLostAction: { '#text': 'goBack' },
                    // 安全起飞高度
                    takeOffSecurityHeight: { '#text': 20 },
                    // 参考起飞点
                    takeOffRefPoint: { '#text': takeOffPoint },
                    // 参考起飞点绝对高度
                    takeOffRefPointAGLHeight: { '#text': 0 },
                    // 全局航线过渡速度
                    globalTransitionalSpeed: { '#text': 10 },
                    // 全局返航高度
                    globalRTHHeight: { '#text': defaultHeight },
                    // 无人机参数
                    droneInfo: {
                        droneEnumValue: { '#text': droneEnumValue },
                        droneSubEnumValue: {
                            '#text': droneModel[payloadEnumValue],
                        },
                    },
                    // 是否自动绕行(暂未验证M30T机型使用此参数后会不会无法起飞)
                    autoRerouteInfo: {
                        missionAutoRerouteMode: { '#text': 1 },
                        transitionalAutoRerouteMode: { '#text': 1 },
                    },
                    // 云台参数
                    payloadInfo: {
                        payloadEnumValue: { '#text': payloadEnumValue },
                        payloadSubEnumValue: {
                            '#text': droneModel[payloadEnumValue],
                        },
                        payloadPositionIndex: { '#text': 0 },
                    },
                },
                Folder: {
                    // 预定义模板类型 - 航点飞行
                    templateType: { '#text': 'waypoint' },
                    // 是否使用全局航线过渡速度
                    useGlobalTransitionalSpeed: { '#text': 0 },
                    templateId: { '#text': 0 },
                    // 坐标系参数信息
                    waylineCoordinateSysParam: {
                        // 经纬度坐标系 - 固定值
                        coordinateMode: { '#text': 'WGS84' },
                        // 航点高程参考平面    - 相对高度
                        heightMode: { '#text': 'relativeToStartPoint' },
                    },
                    // 全局航线飞行速度
                    autoFlightSpeed: { '#text': 10 },
                    // 云台俯仰角控制模式
                    gimbalPitchMode: { '#text': 'usePointSetting' },
                    // 全局高度
                    globalHeight: { '#text': '100' },
                    // 全局偏航角模式参数 - 可使用当前默认值
                    globalWaypointHeadingParam: {
                        waypointHeadingMode: { '#text': 'followWayline' },
                        waypointHeadingAngle: { '#text': 0 },
                        waypointPoiPoint: {
                            '#text': '0.000000,0.000000,0.000000',
                        },
                        waypointHeadingPathMode: { '#text': 'followBadArc' },
                        waypointHeadingPoiIndex: { '#text': 0 },
                    },
                    // 全局航点类型 - 直线飞行,飞行器到点停
                    globalWaypointTurnMode: {
                        '#text': 'toPointAndStopWithDiscontinuityCurvature',
                    },
                    // 全局航段轨迹是否尽量贴合直线
                    globalUseStraightLine: { '#text': 1 },
                    // 航点点位信息 - 位置、事件等
                    Placemark: [],
                    payloadParam: {
                        payloadPositionIndex: { '#text': 0 },
                        focusMode: { '#text': 'firstPoint' },
                        meteringMode: { '#text': 'average' },
                        returnMode: { '#text': 'singleReturnFirst' },
                        samplingRate: { '#text': '240000' },
                        scanningMode: { '#text': 'repetitive' },
                        imageFormat: { '#text': 'wide,zoom' },
                    },
                },
            }
            lnglats.forEach((lnglat, index) => {
                const placemark = {
                    Point: {
                        // 航点经纬度
                        coordinates: {
                            '#text': `${lnglat.longitude},${lnglat.latitude}`,
                        },
                    },
                    // 航点index
                    index: { '#text': index },
                    // 全局航线高度(椭球高)
                    ellipsoidHeight: { '#text': lnglat.height },
                    // 全局航线高度 (EGM96海拔高/相对起飞点高度/AGL相对地面高度)
                    height: { '#text': lnglat.height },
                    // 航点飞行速度
                    waypointSpeed: { '#text': lnglat?.speed || 10 },
                    // 沿航线方向 - 可使用当前默认值
                    waypointHeadingParam: {
                        waypointHeadingMode: { '#text': 'followWayline' },
                        waypointHeadingAngle: { '#text': 0 },
                        waypointPoiPoint: {
                            '#text': '0.000000,0.000000,0.000000',
                        },
                        waypointHeadingPathMode: { '#text': 'followBadArc' },
                        waypointHeadingPoiIndex: { '#text': 0 },
                    },
                    // 航点类型
                    waypointTurnParam: {
                        waypointTurnMode: {
                            '#text': 'toPointAndStopWithDiscontinuityCurvature',
                        },
                        waypointTurnDampingDist: { '#text': 0.2 },
                    },
                    // 是否全局参数
                    useGlobalHeight: { '#text': 1 },
                    useGlobalSpeed: { '#text': 0 },
                    useGlobalHeadingParam: { '#text': 1 },
                    useGlobalTurnParam: { '#text': 1 },
                    useStraightLine: { '#text': 1 },
                }
                if (lnglat?.actions) {
                    const actionGroup = {
                        actionGroupId: { '#text': index },
                        actionGroupStartIndex: { '#text': index },
                        actionGroupEndIndex: { '#text': index },
                        actionGroupMode: { '#text': 'sequence' },
                        actionTrigger: {
                            actionTriggerType: { '#text': 'reachPoint' },
                        },
                        // 单个事件 - 如果是多个事件action可改成数组 action: [{}]
                        // action: [{
                        //   actionId: { '#text': 0 },
                        //   actionActuatorFunc: { '#text': '' },
                        //   actionActuatorFuncParam: {},
                        // }],
                        action: [],
                    }
                    lnglat?.actions.forEach((action, index) => {
                        const singleAction = {
                            actionId: { '#text': index },
                            actionActuatorFunc: { '#text': '' },
                            actionActuatorFuncParam: {},
                        }
                        // actionGroup.action['actionActuatorFunc'] = { '#text': action.key }
                        // action.params.forEach(p => {
                        //   if (p.value || p.value !== '') {
                        //     actionGroup.action['actionActuatorFuncParam'][p.key] = { '#text': p.value }
                        //   }
                        // })
                        singleAction['actionActuatorFunc'] = {
                            '#text': action.key,
                        }
                        action.params.forEach(p => {
                            if (p.value || p.value !== '') {
                                singleAction['actionActuatorFuncParam'][p.key] = {
                                    '#text': p.value,
                                }
                            }
                        })
                        if (lnglat?.actions.length > 1) {
                            actionGroup.action.push(singleAction)
                        } else {
                            actionGroup.action = singleAction
                        }
                    })
                    placemark.actionGroup = actionGroup
                }
                fileTemplate.Folder.Placemark.push(placemark)
            })
            const fileWaylines = waylinesContext(fileTemplate)
            return {
                fileName: waylineName || fileName,
                template: fileTemplate,
                waylines: fileWaylines,
            }
        } catch (error) {
            console.log(error)
            return error
        }
    }
    // 创建面状航线
    const createPlanarWayline = (waylineBasicInfo, lnglats) => { }
    // 创建点状航线
    const createPointWayLines = (waylineBasicInfo, points, settingInfo, dockPosition) => {
        try {
            const { droneEnumValue, payloadEnumValue, waylineName } = waylineBasicInfo
            const {
                imageFormat,
                heightMode,
                globalHeight,
                autoFlightSpeed,
                gimbalPitchMode,
                waypointTurnMode,
                waypointHeadingMode,
                finishAction,
                globalTransitionalSpeed,
            } = settingInfo
            const date = new Date()
            const username = window.localStorage.getItem('bs_username')
            const updateTime = date.getTime()
            const { year, month, day, hours, minutes } = getDateFromTimestamp(updateTime)
            const fileName = '新建航线_' + year + month + day + hours + minutes
            // 无人机机型、云台型号、航线高度
            const defaultHeight = 100
            // 起始点位 (参考点位-具体以机场为准)
            const takeOffPoint = [dockPosition.latitude, dockPosition.longitude, 0].join(',')
            // template.xml对象模版
            const fileTemplate = {
                author: { '#text': username },
                createTime: { '#text': updateTime },
                updateTime: { '#text': updateTime },
                missionConfig: {
                    // 飞向首航点模式
                    flyToWaylineMode: { '#text': 'safely' },
                    // 航线结束动作
                    finishAction: { '#text': finishAction || 'goHome' },
                    // 失控是否继续执行航线
                    exitOnRCLost: { '#text': 'executeLostAction' },
                    // 失控动作类型
                    executeRCLostAction: { '#text': 'goBack' },
                    // 安全起飞高度
                    takeOffSecurityHeight: { '#text': 120 },
                    // 参考起飞点
                    takeOffRefPoint: { '#text': takeOffPoint },
                    // 参考起飞点绝对高度
                    takeOffRefPointAGLHeight: { '#text': 0 },
                    // 全局航线过渡速度
                    globalTransitionalSpeed: { '#text': globalTransitionalSpeed || 10 },
                    // 全局返航高度
                    globalRTHHeight: { '#text': defaultHeight },
                    // 无人机参数
                    droneInfo: {
                        droneEnumValue: { '#text': droneEnumValue },
                        droneSubEnumValue: {
                            '#text': droneModel[payloadEnumValue],
                        },
                    },
                    // 是否自动绕行(暂未验证M30T机型使用此参数后会不会无法起飞)
                    autoRerouteInfo: {
                        missionAutoRerouteMode: { '#text': 1 },
                        transitionalAutoRerouteMode: { '#text': 1 },
                    },
                    // 云台参数
                    payloadInfo: {
                        payloadEnumValue: { '#text': payloadEnumValue },
                        payloadSubEnumValue: {
                            '#text': droneModel[payloadEnumValue],
                        },
                        payloadPositionIndex: { '#text': 0 },
                    },
                },
                Folder: {
                    // 预定义模板类型 - 航点飞行
                    templateType: { '#text': 'waypoint' },
                    // 是否使用全局航线过渡速度
                    useGlobalTransitionalSpeed: { '#text': 0 },
                    templateId: { '#text': 0 },
                    // 坐标系参数信息
                    waylineCoordinateSysParam: {
                        // 经纬度坐标系 - 固定值
                        coordinateMode: { '#text': 'WGS84' },
                        // 航点高程参考平面    - 相对高度
                        heightMode: heightMode,
                    },
                    // 全局航线飞行速度
                    autoFlightSpeed: { '#text': autoFlightSpeed || 10 },
                    // 云台俯仰角控制模式
                    gimbalPitchMode: { '#text': gimbalPitchMode || 'manual' },
                    // 全局高度
                    globalHeight: { '#text': globalHeight || '100' },
                    // 全局偏航角模式参数 - 可使用当前默认值
                    globalWaypointHeadingParam: {
                        waypointHeadingMode: { '#text': waypointHeadingMode || 'followWayline' },
                        waypointHeadingAngle: { '#text': 0 },
                        waypointPoiPoint: {
                            '#text': '0.000000,0.000000,0.000000',
                        },
                        waypointHeadingPathMode: { '#text': 'followBadArc' },
                        waypointHeadingPoiIndex: { '#text': 0 },
                    },
                    // 全局航点类型 - 直线飞行,飞行器到点停
                    globalWaypointTurnMode: {
                        '#text': waypointTurnMode || 'toPointAndStopWithDiscontinuityCurvature',
                    },
                    // 全局航段轨迹是否尽量贴合直线
                    globalUseStraightLine: { '#text': 1 },
                    // 航点点位信息 - 位置、事件等
                    Placemark: [],
                    payloadParam: {
                        payloadPositionIndex: { '#text': 0 },
                        focusMode: { '#text': 'firstPoint' },
                        meteringMode: { '#text': 'average' },
                        returnMode: { '#text': 'singleReturnFirst' },
                        samplingRate: { '#text': '240000' },
                        scanningMode: { '#text': 'repetitive' },
                        imageFormat: imageFormat,
                    },
                },
            }
            points.length &&
                points.forEach((detail, ind) => {
                    const { Point, index, height, ellipsoidHeight, actionGroup, waypointSpeed } = detail
                    const placemark = {
                        Point,
                        // 航点index
                        index,
                        // 全局航线高度(椭球高)
                        ellipsoidHeight,
                        // 全局航线高度 (EGM96海拔高/相对起飞点高度/AGL相对地面高度)
                        height,
                        // 航点飞行速度
                        waypointSpeed: { '#text': waypointSpeed || 10 },
                        // 沿航线方向 - 可使用当前默认值
                        waypointHeadingParam: {
                            waypointHeadingMode: {
                                '#text': waypointHeadingMode || 'followWayline',
                            },
                            waypointHeadingAngle: { '#text': 0 },
                            waypointPoiPoint: {
                                '#text': '0.000000,0.000000,0.000000',
                            },
                            waypointHeadingPathMode: { '#text': 'followBadArc' },
                            waypointHeadingPoiIndex: { '#text': 0 },
                        },
                        // 航点类型
                        waypointTurnParam: {
                            waypointTurnMode: {
                                '#text': waypointTurnMode || 'toPointAndStopWithDiscontinuityCurvature',
                            },
                            waypointTurnDampingDist: { '#text': 0.2 },
                        },
                        // 是否全局参数
                        useGlobalHeight: { '#text': 1 },
                        useGlobalSpeed: { '#text': 0 },
                        useGlobalHeadingParam: { '#text': 1 },
                        useGlobalTurnParam: { '#text': 1 },
                        useStraightLine: { '#text': 1 },
                    }
                    if (actionGroup) {
                        placemark.actionGroup = actionGroup
                    }
                    fileTemplate.Folder.Placemark.push(placemark)
                })
            const fileWaylines = waylinesContext(fileTemplate)
            return {
                fileName: waylineName || fileName,
                template: fileTemplate,
                waylines: fileWaylines,
            }
        } catch (error) {
            console.log(error)
            return error
        }
    }
    const save = async fileInfo => {
        const JsZip = new JSZIP()
        const { fileName, template, waylines } = fileInfo
        const templateXml = JSONToXML(template, '', true)
        const waylinesWpml = JSONToXML(waylines, '', true)
        JsZip.file('wpmz/template.kml', templateXml)
        JsZip.file('wpmz/waylines.wpml', waylinesWpml)
        const fileBlob = await JsZip.generateAsync({ type: 'blob' })
        // saveAs(kmzFile, `${fileName}.kmz`)
        return fileBlob
    }
    const waylinesContext = templateJson => {
        const waylinesObj = {
            // 标准模版
            missionConfig: {
                flyToWaylineMode: null,
                finishAction: null,
                exitOnRCLost: null,
                executeRCLostAction: null,
                takeOffSecurityHeight: null,
                globalTransitionalSpeed: null,
                globalRTHHeight: null,
                droneInfo: null,
                payloadInfo: null,
            },
            Folder: {
                templateId: null,
                executeHeightMode: null,
                waylineId: null,
                autoFlightSpeed: null,
                /*
          waylines.wpml和template.xml的Placemark通用
          不同点:
            waylineId = templateId
            ellipsoidHeight、 height 替换成 executeHeight
        */
                Placemark: null,
            },
        }
        Object.keys(waylinesObj).forEach(key => {
            if (Object.prototype.toString.call(waylinesObj[key]) === '[object Object]') {
                Object.keys(waylinesObj[key]).forEach(item => {
                    waylinesObj[key][item] = templateJson[key][item]
                    if (item === 'executeHeightMode') {
                        waylinesObj[key][item] = templateJson[key].waylineCoordinateSysParam.heightMode
                    }
                    if (item === 'waylineId') {
                        waylinesObj[key][item] = templateJson[key].templateId
                    }
                    if (item === 'Placemark') {
                        const placemarks = _.cloneDeep(templateJson[key][item])
                        placemarks.forEach(placemark => {
                            placemark.executeHeight = {
                                '#text': placemark.ellipsoidHeight?.['#text'] || '',
                            }
                            delete placemark.ellipsoidHeight
                            delete placemark.height
                        })
                        waylinesObj[key][item] = placemarks
                    }
                })
            } else {
                waylinesObj[key] = templateJson[key]
            }
        })
        return waylinesObj
    }
    return {
        create,
        createNoramlWaylines,
        createPlanarWayline,
        createPointWayLines,
        save,
    }
}
applications/task-work-order/src/utils/cesium/useBoundary.js
@@ -6,19 +6,17 @@
import { useStore } from 'vuex'
import { getDeviceRegion, getDeviceRegionCount } from '@/api/home/aggregation'
import userStore from '@/store/modules/user'
import { areaCodeToArr, getContourByCode } from '@/utils/cesium/mapUtil'
// import { getEllipse } from '@/hooks/useSingleDroneMap/useSingleDroneMap'
import { areaCodeToArr, getContourByCode, getDockPolyLine, getLnglatAltitude } from '@/utils/cesium/mapUtil'
import getBaseConfig from '@/buildConfig/config'
import { getDroneStatusImage } from '@/utils/stateToImageMap/drone'
import {
    GroundCirclePrimitiveManager
} from '@/utils/mapUtils'
import { GroundCirclePrimitiveManager } from '@/utils/mapUtils'
import lowBattery from '@/assets/images/aiNowFly/low-battery.svg'
import mediumBattery from '@/assets/images/aiNowFly/medium-battery.svg'
import fullBattery from '@/assets/images/aiNowFly/full-charge.svg'
const { VITE_APP_BASE, VITE_APP_ENV, VITE_APP_REGION_URL } = import.meta.env
// const { singleDockSystem } = getBaseConfig()
const { singleDockSystem } = getBaseConfig()
/**
 * 使用通用的边界
@@ -26,8 +24,15 @@
 * @param options
 */
export const useBoundary = (viewer, options = {}) => {
    const { multiple = 1.4, dockOptions = {}, boundaryChange, boundaryColor, dockRangeType, useDockHeight = false } = options
    const { scrollShowDock = false, showDock = false } = dockOptions
    const {
        multiple = 1.4,
        dockOptions = {},
        boundaryChange,
        boundaryColor,
        dockRangeType,
        useDockHeight = false,
    } = options
    const { scrollShowDock = false, showDock = false, showDockNameAndBattery = false } = dockOptions
    const manager = new GroundCirclePrimitiveManager()
    manager.init(viewer)
@@ -56,7 +61,7 @@
    }
    // 获取设备列表
    async function getDeviceRegionFun () {
    async function getDeviceRegionFun() {
        const res = await getDeviceRegion({ areaCode: selectedAreaCode })
        return res?.data?.data || []
    }
@@ -100,60 +105,127 @@
        return await res.json()
    }
    function getBase64Image(imgUrl) {
        const img = new Image()
        const canvas = document.createElement('canvas')
        const ctx = canvas.getContext('2d')
        return new Promise(resolve => {
            img.onload = function () {
                canvas.width = img.width
                canvas.height = img.height
                ctx.drawImage(img, 0, 0)
                const base64 = canvas.toDataURL('image/png')
                resolve(base64)
            }
            img.src = imgUrl
        })
    }
    function batteryLevelSvg(batteryNum) {
        if (batteryNum <= 30) {
            return lowBattery
        } else if (batteryNum > 30 && batteryNum <= 60) {
            return mediumBattery
        } else {
            return fullBattery
        }
    }
    async function nestSVG(nickname, batteryImgUrl, batteryNum) {
        let txt = nickname.length < 4 ? 130 : nickname.length * 38
        let tranx = nickname.length < 4 ? 86 : nickname.length * 26 + 3
        const batteryBase64Image = await getBase64Image(batteryImgUrl)
        const svg = `
          <svg width="${txt}" height="100" xmlns="http://www.w3.org/2000/svg">
            <g transform="translate(${tranx}, 10)">
                <!-- 电池图标 -->
                <image href="${batteryBase64Image}" x="0" y="2" width="12" height="13"/>
                <!-- 电量值 -->
                <text x="12" y="10" font-size="14" fill="${
                    batteryNum <= 30 ? '#FF604B' : batteryNum >= 60 ? '#40FF5C' : '#00FFF2'
                }"  text-anchor="start" dominant-baseline="middle" font-weight="bolder" font-family="Source Han Sans CN" text-rendering="geometricPrecision">
                    ${batteryNum}%
                </text>
            </g>
          </svg>
        `
        return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`
    }
    // 无人机散点渲染
    const droneSplashed = () => {
        let showDockList = showDockSnList === null
            ? _.cloneDeep(initDockList)
            : initDockList.filter(item => showDockSnList.includes(item.device_sn))
        manager.removeAll()
        let showDockList =
            showDockSnList === null
                ? _.cloneDeep(initDockList)
                : initDockList.filter(item => showDockSnList.includes(item.device_sn))
        showDockList.forEach((item, index) => {
        showDockList.forEach(async (item, index) => {
            if (item.status === 'OFFLINE') return
            let polyline = {}
            if (dockRangeType === 2) {
                manager.addCircleOutline({
                    data: {
                        lng: item.longitude,
                        lat: item.latitude
                        lat: item.latitude,
                    },
                    frameColor: Cesium.Color.fromCssColorString('#7FFFD4')
                    frameColor: Cesium.Color.fromCssColorString('#7FFFD4'),
                })
            } else {
                manager.addCircle({
                    data: {
                        lng: item.longitude,
                        lat: item.latitude
                        lat: item.latitude,
                    },
                    materialColor: Cesium.Color.CORNFLOWERBLUE.withAlpha(0.3)
                    materialColor: item.color ? item.color : Cesium.Color.CORNFLOWERBLUE.withAlpha(0.3),
                })
            }
            if (useDockHeight) {
                polyline = {
                    positions: new Cesium.CallbackProperty(() => {
                        const pointPosition = Cesium.Cartesian3.fromDegrees(+item.longitude, +item.latitude, +item.height)
                        if (!pointPosition) return []
                        // 获取点的位置
                        const cartographic = Cesium.Cartographic.fromCartesian(pointPosition)
                        // 获取地形高度
                        const terrainHeight = viewer.scene.globe.getHeight(cartographic) || 0
                        // 创建地面点位置
                        const groundPosition = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, terrainHeight)
                        return [pointPosition, groundPosition]
                    }, false),
                    width: 0.5,
                    material: Cesium.Color.WHITE,
                }
                polyline = getDockPolyLine(item, viewer)
            }
            const batteryImgUrl = batteryLevelSvg(item.capacity_percent)
            const combinedImage = await nestSVG(item.nickname, batteryImgUrl, item.capacity_percent)
            let pointObj = {}
            try {
                pointObj = await getLnglatAltitude(item.longitude, item.latitude, viewer)
            } catch (e) {}
            const position = Cesium.Cartesian3.fromDegrees(
                +item.longitude,
                +item.latitude,
                useDockHeight ? +item.height : pointObj?.height || 0
            )
            if (showDockNameAndBattery) {
                // 带电量
                dockSource.entities.add({
                    position,
                    billboard: {
                        // new Cesium.ConstantProperty(pointData.status === 'OFFLINE' ? endingImg : unSelectedOnline),
                        image: combinedImage,
                    },
                })
            }
            dockSource.entities.add({
                position: Cesium.Cartesian3.fromDegrees(+item.longitude, +item.latitude, !useDockHeight ? 0 : +item.height),
                label: {
                    text: item.nickname,
                    font: 'bold 16px Source Han Sans CN',
                    fillColor: Cesium.Color.WHITE,
                    outlineColor: Cesium.Color.BLACK,
                    outlineWidth: 2,
                    style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                    verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                    pixelOffset: new Cesium.Cartesian2(0, -24),
                },
                position,
                polyline,
                billboard: {
                    image: getDroneStatusImage(item.status),
                    width: 60,
                    height: 60,
                    eyeOffset: new Cesium.Cartesian3(0, 0, -5)
                    disableDepthTestDistance: Number.POSITIVE_INFINITY,
                    eyeOffset: new Cesium.Cartesian3(0, 0, -5),
                },
            })
        })
@@ -186,7 +258,7 @@
                            positions: positions,
                            width: 5, // 直接设置宽度
                            clampToGround: true,
                            material: Cesium.Color.fromCssColorString(boundaryColor)
                            material: Cesium.Color.fromCssColorString(boundaryColor),
                        },
                        polygon,
                    })
@@ -205,7 +277,7 @@
                        positions: positions,
                        width: 1, // 直接设置宽度
                        clampToGround: true,
                        material: Cesium.Color.fromCssColorString(boundaryColor)
                        material: Cesium.Color.fromCssColorString(boundaryColor),
                    },
                })
            })
@@ -217,7 +289,6 @@
    // 打开边界
    const openContour = async () => {
        const areaCode = selectedAreaCode || userAreaCode
        viewer.scene.postRender.removeEventListener(determineScaling)
        if (!areaCode) return
        const hierarchy = areaCodeToArr(areaCode.slice(0, 6))
@@ -258,25 +329,29 @@
            const outlineGJson = await getContourByCode(areaCode.slice(0, 6))
            scalingJudgment.forEach(item => item.show && (item.outline = outlineGJson))
            renderOutline(scalingJudgment[(hierarchy.length - 3) * -1])
        } catch (e) { }
        viewer.scene.postRender.addEventListener(determineScaling)
        } catch (e) {}
        try {
            viewer?.scene?.postRender?.addEventListener(determineScaling)
        }catch (e) {
        }
    }
    // 关闭边界
    function closeContour () {
    function closeContour() {
        removeAllEntities()
        viewer.scene.postRender.removeEventListener(determineScaling)
    }
    // 飞向边界
    async function flyToBoundary () {
    async function flyToBoundary() {
        // 单机巢系统
        if (singleDockSystem) {
            if (!initDockList.length) {
                initDockList = await getDeviceRegionFun()
            }
            viewer?.camera.flyTo({
                destination: Cesium.Cartesian3.fromDegrees(initDockList[0].longitude, initDockList[0].latitude, 24000),
                destination: Cesium.Cartesian3.fromDegrees(initDockList[0].longitude, initDockList[0].latitude, 22000),
                duration: 0,
            })
            return
@@ -286,7 +361,8 @@
        const dataSource = await Cesium.GeoJsonDataSource.load(gJson)
        if (!dataSource) return
        viewer.dataSources.add(dataSource)
        // 获取多边形边界所有点
        let positionList = []
        dataSource.entities.values.forEach(function (entity) {
            if (entity.polygon) {
                // 获取多边形的边界球
@@ -295,18 +371,16 @@
                    let cartographic = Cesium.Cartographic.fromCartesian(item)
                    let lng = Cesium.Math.toDegrees(cartographic.longitude) // 经度
                    let lat = Cesium.Math.toDegrees(cartographic.latitude) // 纬度
                    return [_.round(lng, 6), _.round(lat, 6)]
                })
                const newBox = boxTransformScale(curPolygonPosition, multiple)
                viewer.camera.flyTo({
                    destination: Cesium.Rectangle.fromDegrees(...newBox),
                    offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-90), 0),
                    duration: 0.5,
                })
                positionList = positionList.concat(curPolygonPosition)
            }
        })
        const newBox = boxTransformScale(positionList, multiple)
        viewer.camera.flyTo({
            destination: Cesium.Rectangle.fromDegrees(...newBox),
            offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-90), 0),
            duration: 0.5,
        })
        dataSource.entities.values.forEach(entity => {
@@ -320,7 +394,7 @@
     * 设置要显示的机巢列表
     * @param dockSns - 要显示的机巢设备序列号数组。如果为 undefined,则显示所有已初始化的机巢;如果为空数组,则不显示任何机巢。
     */
    function setShowDock (dockSns) {
    function setShowDock(dockSns) {
        if (dockSns === undefined) {
            showDockSnList = null
        } else if (dockSns.length === 0) {
@@ -328,21 +402,31 @@
        } else {
            showDockSnList = dockSns
        }
        removeDockCover()
        if (active === '县') droneSplashed()
        if (!showDock) return
        if (scrollShowDock ? active === '县' : true) {
            droneSplashed()
        }
    }
    function setDockCoverColor(arr) {
        initDockList.forEach(item => {
            arr.forEach(dock => {
                item.color = dock.device_sn === item.device_sn ? dock.color : null
            })
        })
        if (scrollShowDock ? active === '县' : true) {
            droneSplashed()
        }
    }
    onBeforeUnmount(() => {
        manager.destroy()
    })
    return {
        openContour,
        closeContour,
        flyToBoundary,
        setShowDock
        setShowDock,
        setDockCoverColor,
    }
}
applications/task-work-order/src/views/RoutePlan/PlanarAirLine/planarRouteUtils.js
File was deleted
applications/task-work-order/src/views/RoutePlan/PointAirLine/pointWayLineUtils.js
File was deleted
applications/task-work-order/src/views/algorithmMange/algorithmMange.vue
File was deleted
applications/task-work-order/src/views/algorithmRepository/algorithmRepository.vue
File was deleted
applications/task-work-order/src/views/authority/apiscope.vue
File was deleted
applications/task-work-order/src/views/authority/datascope.vue
File was deleted
applications/task-work-order/src/views/authority/role.vue
File was deleted
applications/task-work-order/src/views/base/region.vue
File was deleted
applications/task-work-order/src/views/basicManage/maintainRecord/FormDiaLog.vue
New file
@@ -0,0 +1,289 @@
<template>
    <el-dialog class="gd-dialog" v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed" destroy-on-close>
        <div class="detail-container" v-if="dialogReadonly">
            <div class="detail-title">设备详情</div>
            <el-row>
                <el-col :span="12">
                    <div class="label">设备名称</div>
                    <div class="val">{{ formData.deviceName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">设备类型</div>
                    <div class="val">{{ getDictLabel(formData.deviceType, dictObj.deviceType) }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">型号</div>
                    <div class="val">{{ formData.deviceModel }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">生产厂商</div>
                    <div class="val">{{ formData.manufacturer }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">所属部门</div>
                    <div class="val">{{ formData.belongDeptName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">负责人</div>
                    <div class="val">{{ formData.charger }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">维护计划</div>
                    <div class="val">{{ getPlanCycleLabel(formData) }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">维护时间</div>
                    <div class="val">{{ list?.[0]?.maintainTime }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">维护内容</div>
                    <div class="val">{{ formData.manufacturer }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">跟换部件</div>
                    <div class="val">{{ formData.manufacturer }}</div>
                </el-col>
            </el-row>
            <div class="detail-title">维护详情</div>
            <div class="gd-table-container" v-loading="loading">
                <div class="gd-table-content">
                    <el-table class="gd-table" :data="list">
                        <el-table-column type="index" width="60" label="序号" />
                        <el-table-column prop="maintainTime" label="维护时间" />
                        <el-table-column prop="maintainContent" label="维护内容" />
                        <el-table-column prop="replacePart" label="跟换部件" />
                    </el-table>
                </div>
                <div class="gd-pagination-parent">
                    <el-pagination
                        popper-class="gd-select-popper"
                        v-model:current-page="searchParams.current"
                        v-model:page-size="searchParams.size"
                        layout="total, prev, pager, next, sizes"
                        :total="total"
                        @change="getList"
                    />
                </div>
            </div>
        </div>
        <el-form
            class="dialog-form"
            v-else
            ref="formRef"
            :model="formData"
            :rules="rules"
            :disabled="dialogReadonly"
            label-width="120px"
        >
            <el-row>
                <el-col :span="12">
                    <el-form-item label="选择设备" prop="deviceId">
                        <el-select
                            class="gd-select"
                            popper-class="gd-select-popper"
                            v-model="formData.deviceId"
                            placeholder="请选择"
                            filterable
                            clearable
                        >
                            <el-option v-for="item in deviceList" :key="item.id" :label="item.deviceName" :value="item.id" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="维护计划周期" prop="planCycleType">
                        <el-select
                            class="gd-select"
                            popper-class="gd-select-popper"
                            v-model="formData.planCycleType"
                            placeholder="请选择"
                            clearable
                            @change="formData.planCycleValue = []"
                        >
                            <el-option v-for="item in planCycleOptions" :key="item.value" :label="item.label" :value="item.value" />
                        </el-select>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="维护计划时间" prop="planCycleValue">
                        <el-select
                            class="gd-select"
                            popper-class="gd-select-popper"
                            v-model="formData.planCycleValue"
                            placeholder="请选择"
                            clearable
                            multiple
                            :disabled="!formData.planCycleType"
                        >
                            <el-option
                                v-for="item in planCycleValueOptions"
                                :key="item.value"
                                :label="item.value"
                                :value="item.value"
                            />
                        </el-select>
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <template #footer>
            <el-button color="#F2F3F5" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button
                class="save-btn"
                color="#4C34FF"
                v-if="!dialogReadonly"
                :loading="submitting"
                :disabled="submitting"
                @click="handleSubmit"
            >
                保存
            </el-button>
        </template>
    </el-dialog>
</template>
<script setup>
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { fieldRules, getDictLabel } from '@ztzf/utils'
import { fwDeviceMaintainRecordPageApi } from '@/views/basicManage/maintainRecord/fwDeviceMaintainRecord'
import {
    fwDeviceMaintainPlanDetailApi,
    fwDeviceMaintainPlanSubmitApi,
    fwDevicePageApi,
} from '@/views/basicManage/maintainRecord/maintainRecordApi'
const initForm = () => ({
    deviceId: '',
    planCycleType: '',
    planCycleValue: [],
})
const planCycleOptions = inject('planCycleOptions')
const planCycleValueOptions = ref([])
const emit = defineEmits(['success'])
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = ref(false) // 弹框显隐
const dialogMode = ref('add') // 弹框模式
const submitting = ref(false) // 提交中
const deviceList = ref([]) // 设备列表
const getPlanCycleLabel = inject('getPlanCycleLabel')
const dictObj = inject('dictObj')
const dialogReadonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '维护计划' })
const planCycleLabel = computed(() => {
    const item = planCycleOptions.find(option => option.value === formData.value.planCycleType)
    return item?.label || formData.value.planCycleType || '-'
})
const rules = {
    deviceId: fieldRules(true),
    planCycleType: fieldRules(true),
    planCycleValue: [{ required: true, message: '请选择', trigger: ['blur', 'change'] }],
}
// 获取设备列表
function getDeviceList() {
    return fwDevicePageApi({ current: 1, size: 999 }).then(res => {
        deviceList.value = res?.data?.data?.records ?? []
    })
}
// 关闭弹框
function handleCancel() {
    visible.value = false
}
// 提交新增/编辑
async function handleSubmit() {
    const isValid = await formRef.value?.validate().catch(() => false)
    if (!isValid) return
    submitting.value = true
    try {
        await fwDeviceMaintainPlanSubmitApi(formData.value)
        ElMessage.success(dialogMode.value === 'add' ? '新增成功' : '更新成功')
        visible.value = false
        emit('success')
    } finally {
        submitting.value = false
    }
}
// 加载详情
async function loadDetail() {
    if (!formData.value.id) return
    const res = await fwDeviceMaintainPlanDetailApi({ id: formData.value.id })
    formData.value = res?.data?.data ?? {}
    console.log(formData.value, 66)
}
// 关闭后重置
function handleClosed() {
    formData.value = initForm()
}
const initSearchParams = () => ({
    current: 1, // 当前页
    size: 10, // 每页大小
})
const searchParams = ref(initSearchParams())
const total = ref(0) // 总条数
const loading = ref(true) // 列表加载中
const list = ref([]) // 列表数据
async function getList() {
    loading.value = true
    try {
        const params = { ...searchParams.value, planId: formData.value.id }
        const res = await fwDeviceMaintainRecordPageApi(params)
        list.value = res?.data?.data?.records ?? []
        total.value = res?.data?.data?.total ?? 0
    } finally {
        loading.value = false
    }
}
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    visible.value = true
    await getDeviceList()
    if (dialogMode.value === 'add') {
        formData.value = initForm()
    } else {
        formData.value = row
        await loadDetail()
        getList()
    }
}
defineExpose({ open })
watch(
    () => formData.value?.planCycleType,
    type => {
        planCycleValueOptions.value = []
        if (!type) return
        if (type === '1') {
            ;[...Array(12)].forEach((item, index) => {
                ;[...Array(31)].forEach((item1, index1) => {
                    planCycleValueOptions.value.push({ value: `${index + 1}月${index1 + 1}号` })
                })
            })
        } else if (type === '2') {
            ;[...Array(31)].forEach((item, index) => {
                planCycleValueOptions.value.push({ value: `${index + 1}号` })
            })
        } else if (type === '3') {
            ;[...Array(7)].forEach((item, index) => {
                planCycleValueOptions.value.push({ value: `星期${index + 1}` })
            })
        }
    }
)
</script>
applications/task-work-order/src/views/basicManage/maintainRecord/MaintenanceDiaLog.vue
New file
@@ -0,0 +1,177 @@
<template>
    <el-dialog class="gd-dialog" v-model="visible" :title="titleEnum[dialogMode]" @closed="handleClosed" destroy-on-close>
        <div class="detail-container" v-if="dialogReadonly">
            <div class="detail-title">设备详情</div>
            <el-row>
                <el-col :span="12">
                    <div class="label">设备名称</div>
                    <div class="val">{{ deviceName }}</div>
                </el-col>
                <el-col :span="12">
                    <div class="label">维护计划</div>
                    <div class="val">{{ planCycleLabel }}</div>
                </el-col>
            </el-row>
        </div>
        <el-form
            class="dialog-form"
            v-else
            ref="formRef"
            :model="formData"
            :rules="rules"
            :disabled="dialogReadonly"
            label-width="110px"
        >
            <el-row>
                <el-col :span="24">
                    <el-form-item label="维护内容" prop="maintainContent">
                        <el-input
                            class="gd-input"
                            v-model="formData.maintainContent"
                            maxlength="200"
                            type="textarea"
                            placeholder="请输入"
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="24">
                    <el-form-item label="更换部件" prop="replacePart">
                        <el-input
                            class="gd-input"
                            v-model="formData.replacePart"
                            maxlength="200"
                            type="textarea"
                            placeholder="请输入"
                            clearable
                        />
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="实际维护时间" prop="maintainTime">
                        <el-date-picker
                            class="gd-date-picker"
                            popper-class="gd-date-picker-popper"
                            v-model="formData.maintainTime"
                            type="date"
                            placeholder="选择日期"
                            value-format="YYYY-MM-DD HH:mm:ss"
                            clearable
                        />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <template #footer>
            <el-button color="#F2F3F5" @click="handleCancel">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
            <el-button
                v-if="!dialogReadonly"
                class="save-btn"
                color="#4C34FF"
                :loading="submitting"
                :disabled="submitting"
                @click="handleSubmit"
            >
                确定
            </el-button>
        </template>
    </el-dialog>
</template>
<script setup>
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { fieldRules } from '@ztzf/utils'
import { fwDeviceMaintainRecordSubmitApi } from '@/views/basicManage/maintainRecord/fwDeviceMaintainRecord'
import { fwDeviceMaintainPlanDetailApi } from '@/views/basicManage/maintainRecord/maintainRecordApi'
const initForm = () => ({
    deviceId: '',
    id: '',
    maintainContent: '',
    replacePart: '',
    maintainTime: '',
})
const planCycleOptions = [
    { label: '每年', value: '1' },
    { label: '每月', value: '2' },
    { label: '每周', value: '3' },
]
const emit = defineEmits(['success'])
const formRef = ref(null) // 表单实例
const formData = ref(initForm()) // 表单数据
const visible = ref(false) // 弹框显隐
const dialogMode = ref('add') // 弹框模式
const submitting = ref(false) // 提交中
const deviceList = ref([]) // 设备列表
const dialogReadonly = computed(() => dialogMode.value === 'view')
const titleEnum = ref({ edit: '编辑', view: '查看', add: '维护计划' })
const planCycleLabel = computed(() => {
    const item = planCycleOptions.find(option => option.value === formData.value.planCycleType)
    return item?.label || formData.value.planCycleType || '-'
})
const deviceName = computed(() => {
    const target = deviceList.value.find(item => item.id === formData.value.deviceId)
    return formData.value.deviceName || target?.deviceName || '-'
})
const rules = {
    maintainContent: fieldRules(true, 200),
    replacePart: fieldRules(true, 200),
    maintainTime: fieldRules(true),
}
// 关闭弹框
function handleCancel() {
    visible.value = false
}
// 提交新增/编辑
async function handleSubmit() {
    const isValid = await formRef.value?.validate().catch(() => false)
    if (!isValid) return
    submitting.value = true
    try {
        await fwDeviceMaintainRecordSubmitApi(formData.value)
        ElMessage.success(dialogMode.value === 'add' ? '新增成功' : '更新成功')
        visible.value = false
        emit('success')
    } finally {
        submitting.value = false
    }
}
// 加载详情
async function loadDetail() {
    console.log(formData.value)
    if (!formData.value.id) return
    const res = await fwDeviceMaintainPlanDetailApi({ id: formData.value.id })
    formData.value = {
        planId: res?.data?.data?.id,
        deviceId: res?.data?.data.deviceId,
    }
}
// 关闭后重置
function handleClosed() {
    formData.value = initForm()
}
// 打开弹框
async function open({ mode, row } = {}) {
    dialogMode.value = mode || 'add'
    visible.value = true
    if (dialogMode.value === 'add') {
        formData.value = initForm()
    } else {
        formData.value = row
        await loadDetail()
    }
}
defineExpose({ open })
</script>
applications/task-work-order/src/views/basicManage/maintainRecord/fwDeviceMaintainRecord.js
New file
@@ -0,0 +1,19 @@
import request from '@/axios'
// 分页
export const fwDeviceMaintainRecordPageApi = params => {
    return request({
        url: `/drone-fw/device/fwDeviceMaintainRecord/page`,
        method: 'get',
        params: { descs: 'update_time', ...params },
    })
}
// 新增或修改
export const fwDeviceMaintainRecordSubmitApi = data => {
    return request({
        url: `/drone-fw/device/fwDeviceMaintainRecord/submit`,
        method: 'post',
        data,
    })
}
applications/task-work-order/src/views/basicManage/maintainRecord/index.vue
New file
@@ -0,0 +1,236 @@
<template>
    <basic-container>
        <el-form ref="queryParamsRef" :model="searchParams" class="gd-search-form">
            <el-form-item label="设备名称" prop="deviceName">
                <el-input
                    class="gd-input"
                    v-model="searchParams.deviceName"
                    placeholder="请输入"
                    clearable
                    @clear="handleSearch"
                />
            </el-form-item>
            <el-form-item label="设备类型" prop="deviceType">
                <el-select
                    class="gd-select"
                    popper-class="gd-select-popper"
                    v-model="searchParams.deviceType"
                    placeholder="请选择"
                    clearable
                    @change="handleSearch"
                >
                    <el-option
                        v-for="item in dictObj.deviceType"
                        :key="item.dictKey"
                        :label="item.dictValue"
                        :value="item.dictKey"
                    />
                </el-select>
            </el-form-item>
            <el-form-item label="所属部门" prop="belongDept">
                <el-tree-select
                    class="gd-select"
                    popper-class="gd-tree-select-popper"
                    v-model="searchParams.belongDept"
                    :data="deptTree"
                    :props="treeProps"
                    node-key="id"
                    check-strictly
                    clearable
                    @change="handleSearch"
                />
            </el-form-item>
            <el-form-item class="gd-search-actions">
                <el-button :icon="RefreshRight" @click="resetForm"></el-button>
                <el-button class="search-btn" :icon="Search" @click="handleSearch"></el-button>
            </el-form-item>
        </el-form>
        <div class="gd-table-toolbar">
            <el-button :icon="Plus" color="#4C34FF" type="primary" @click="handleAdd">维护计划</el-button>
            <el-button :icon="Delete" color="#4C34FF" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
        </div>
        <div class="gd-table-container" v-loading="loading">
            <div class="gd-table-content gd-table-content-bg">
                <el-table class="gd-table" :data="list" @selection-change="handleSelectionChange">
                    <el-table-column type="selection" width="46" />
                    <el-table-column prop="deviceName" show-overflow-tooltip label="设备名称" />
                    <el-table-column prop="deviceType" show-overflow-tooltip label="设备类型">
                        <template v-slot="{ row }">
                            {{ getDictLabel(row.deviceType, dictObj.deviceType) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="deviceModel" show-overflow-tooltip label="型号" />
                    <el-table-column prop="manufacturer" show-overflow-tooltip label="生产厂商" />
                    <el-table-column prop="belongDeptName" show-overflow-tooltip label="所属部门" />
                    <el-table-column prop="charger" show-overflow-tooltip label="负责人" />
                    <el-table-column prop="maintainReminder" show-overflow-tooltip label="维护提醒" />
                    <el-table-column prop="planCycleType" show-overflow-tooltip label="维护计划">
                        <template v-slot="{ row }">
                            {{ getPlanCycleLabel(row) }}
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" class-name="operation-btns">
                        <template v-slot="{ row }">
                            <el-link @click="handleView(row)">查看</el-link>
                            <el-link @click="maintenance(row)">维护</el-link>
                            <el-link @click="handleDelete(row)">删除</el-link>
                        </template>
                    </el-table-column>
                </el-table>
            </div>
            <div class="gd-pagination-parent">
                <el-pagination
                    popper-class="gd-select-popper"
                    v-model:current-page="searchParams.current"
                    v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes"
                    :total="total"
                    @change="getList"
                />
            </div>
        </div>
        <FormDiaLog ref="dialogRef" @success="getList" />
        <MaintenanceDiaLog ref="maintenanceDialogRef" @success="getList" />
    </basic-container>
</template>
<script setup>
import { Search, RefreshRight, Plus, Delete } from '@element-plus/icons-vue'
import { onMounted, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getDictionaryByCode } from '@/api/system/dictbiz'
import { getDeptTree } from '@/api/system/dept'
import { getDictLabel } from '@ztzf/utils'
import FormDiaLog from './FormDiaLog.vue'
import MaintenanceDiaLog from '@/views/basicManage/maintainRecord/MaintenanceDiaLog.vue'
import {
    fwDeviceMaintainPlanPageApi,
    fwDeviceMaintainPlanRemoveApi,
} from '@/views/basicManage/maintainRecord/maintainRecordApi'
const initSearchParams = () => ({
    deviceName: '', // 设备名称
    deviceType: '', // 设备类型
    belongDept: '', // 所属部门
    current: 1, // 当前页
    size: 10, // 每页大小
})
const searchParams = ref(initSearchParams()) // 查询参数
const total = ref(0) // 总条数
const loading = ref(true) // 列表加载中
const list = ref([]) // 列表数据
const selectedIds = ref([]) // 勾选的ID列表
const queryParamsRef = ref(null) // 查询表单实例
const dialogRef = ref(null) // 弹框实例
const maintenanceDialogRef = ref(null) // 弹框实例
const dictObj = ref({
    deviceType: [], // 设备类型
    deviceAtt: [], // 设备属性
})
provide('dictObj', dictObj)
const deptTree = ref([]) // 部门树
const treeProps = {
    label: 'name',
    children: 'children',
}
const planCycleOptions = [
    { label: '每年', value: '1' },
    { label: '每月', value: '2' },
    { label: '每周', value: '3' },
]
provide('planCycleOptions', planCycleOptions)
// 获取列表
async function getList() {
    loading.value = true
    try {
        const res = await fwDeviceMaintainPlanPageApi(searchParams.value)
        list.value = res?.data?.data?.records ?? []
        total.value = res?.data?.data?.total ?? 0
    } finally {
        loading.value = false
    }
}
// 查询
function handleSearch() {
    searchParams.value.current = 1
    getList()
}
// 重置查询
function resetForm() {
    queryParamsRef.value?.resetFields()
    searchParams.value.current = 1
    getList()
}
// 查看
function handleView(row) {
    dialogRef.value?.open({ mode: 'view', row: { ...row } })
}
// 维护
function maintenance(row) {
    maintenanceDialogRef.value?.open({ mode: 'edit', row: { ...row } })
}
// 删除
async function handleDelete(row) {
    const tips = row ? '该条' : '选中的项'
    await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
        type: 'warning',
        customClass: 'gd-confirm-custom',
        confirmButtonClass: 'gd-confirm-button',
        cancelButtonClass: 'gd-confirm-cancel-button',
    })
    const ids = row ? row.id : selectedIds.value.join(',')
    await fwDeviceMaintainPlanRemoveApi({ ids })
    ElMessage.success('删除成功')
    selectedIds.value = []
    getList()
}
// 勾选值设置
function handleSelectionChange(rows) {
    selectedIds.value = rows.map(item => item.id)
}
// 获取字典
function getDictList() {
    getDictionaryByCode('deviceType,deviceAtt').then(res => {
        dictObj.value = res.data.data
    })
}
// 获取部门树
function getDeptTreeFun() {
    getDeptTree().then(res => {
        deptTree.value = res.data.data
    })
}
// 新增
function handleAdd() {
    dialogRef.value?.open({ mode: 'add' })
}
function getPlanCycleLabel(row) {
    const item = planCycleOptions.find(option => option.value === row.planCycleType)
    return item?.label + '-' + row.planCycleValue.join(',')
}
provide('getPlanCycleLabel', getPlanCycleLabel)
onMounted(() => {
    getList()
    getDictList()
    getDeptTreeFun()
})
</script>
<style scoped lang="scss"></style>
applications/task-work-order/src/views/basicManage/maintainRecord/maintainRecordApi.js
New file
@@ -0,0 +1,46 @@
import request from '@/axios'
// 列表
export const fwDeviceMaintainPlanPageApi = params => {
    return request({
        url: `/drone-fw/device/fwDeviceMaintainPlan/page`,
        method: 'get',
        params: { descs: 'update_time', ...params },
    })
}
// 新增或编辑
export const fwDeviceMaintainPlanSubmitApi = data => {
    return request({
        url: `/drone-fw/device/fwDeviceMaintainPlan/submit`,
        method: 'post',
        data,
    })
}
// 删除
export const fwDeviceMaintainPlanRemoveApi = params => {
    return request({
        url: `/drone-fw/device/fwDeviceMaintainPlan/remove`,
        method: 'post',
        params,
    })
}
// 详情
export const fwDeviceMaintainPlanDetailApi = params => {
    return request({
        url: `/drone-fw/device/fwDeviceMaintainPlan/detail`,
        method: 'get',
        params,
    })
}
// 设备分页
export const fwDevicePageApi = params => {
    return request({
        url: `/drone-fw/device/fwDevice/page`,
        method: 'get',
        params: { descs: 'update_time', ...params },
    })
}
applications/task-work-order/src/views/dataCenter/components/dataCenterMap.vue
File was deleted
applications/task-work-order/src/views/dataCenter/components/searchData.vue
File was deleted
applications/task-work-order/src/views/dataCenter/dataCenter.vue
File was deleted
applications/task-work-order/src/views/dataCockpit/components/CenterContainer.vue
New file
@@ -0,0 +1,237 @@
<template>
    <div class="center-container">
        <div class="left-box">
            <div class="logo">
                <img :src="warningLogo" alt="">
            </div>
            <div class="list">
                <div class="item" v-for="(item, ind) in warningList" :key="ind">
                    <div class="val" @click="warningTypeClick(item.type)">
                        {{ item.value }}
                    </div>
                    <div class="name">
                        {{ item.title }}
                    </div>
                </div>
            </div>
        </div>
        <div class="right-box">
            <div class="logo">
                <img :src="equipmentLogo" alt="">
            </div>
            <div class="list">
                <div class="item" v-for="(item, ind) in equipmentList" :key="ind">
                    <div class="val" :class="{highlight: item.isHighlight}" @click="equipmentClick(item.isHighlight)">
                        {{ item.value }}
                    </div>
                    <div class="name">
                        {{ item.title }}
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import warningLogo from '@/assets/images/dataCockpit/warning.png'
import equipmentLogo from '@/assets/images/dataCockpit/equipment.png'
import { alarmStatisticsApi } from '@/api/dataCockpit'
const warningList = ref([
    {
        title: '实时告警',
        value: 0,
        type: 1
    },
    {
        title: '设备告警',
        value: 0,
        type: 2
    },
    {
        title: '历史告警',
        value: 0,
        type: 3
    },
])
const equipmentList = ref([
    {
        title: '设备统计',
        value: 0,
        isHighlight: true,
    },
    {
        title: '无线电',
        value: 0,
    },
    {
        title: '光电',
        value: 0,
    },
    {
        title: '雷达',
        value: 0,
    }
])
const emit = defineEmits(['select-warning', 'select-equipment'])
const warningTypeClick = (type) => {
    emit('select-warning', type)
}
const equipmentClick = (isHighlight) => {
    if (isHighlight) {
        emit('select-equipment')
    }
}
const updateStats = async () => {
    const res = await alarmStatisticsApi()
    const data = res?.data?.data || {}
    warningList.value = [
        { title: '实时告警', value: data.realTimeAlarmCount ?? 0, type: 1 },
        { title: '设备告警', value: data.deviceAlarmCount ?? 0, type: 2 },
        { title: '历史告警', value: data.historyAlarmCount ?? 0, type: 3 }
    ]
    equipmentList.value = [
        { title: '设备统计', value: data.deviceCount ?? 0, isHighlight: true },
        { title: '无线电', value: data.radioCount ?? 0 },
        { title: '光电', value: data.opticalCount ?? 0 },
        { title: '雷达', value: data.radarCount ?? 0 }
    ]
}
onMounted(() => {
    updateStats()
})
</script>
<style lang="scss" scoped>
.center-container {
    display: flex;
    position: absolute;
    top: 75px;
    left: 465px;
    z-index: 9;
    pointer-events: none;
    .left-box {
        width: 356px;
        .list {
            .item {
                .val {
                    color: #FF4444;
                    cursor: pointer;
                }
            }
        }
    }
    .right-box {
        margin-left: 20px;
        width: 455px;
        .list {
            .item {
                .val.highlight {
                    color: #6382FF;
                    cursor: pointer;
                }
            }
        }
    }
    .left-box,
    .right-box {
        position: relative;
        padding: 17px 20px;
        display: flex;
        align-items: center;
        height: 100px;
        background: rgba(0,0,0,0.3);
        box-sizing: border-box;
        border-radius: 16px 16px 16px 16px;
        pointer-events: auto;
        &::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: -1;
            background: rgba(17, 23, 34, 0.56);
              backdrop-filter: blur(2px);
            border-radius: 16px 16px 16px 16px;
        }
        .logo {
            img {
                width: 40px;
                height: 40px;
            }
        }
        .list {
            width: 0;
            flex: 1;
            height: 100%;
            display: flex;
            .item {
                width: 0;
                flex: 1;
                position: relative;
                padding-left: 20px;
                color: #fff;
                .name {
                    font-family: Open Sans, Open Sans;
                    font-weight: 400;
                    font-size: 14px;
                    color: #FFFFFF;
                    text-align: left;
                    font-style: normal;
                    text-transform: none;
                }
                .val {
                    font-family: Source Han Sans CN, Source Han Sans CN;
                    font-weight: bold;
                    font-size: 30px;
                    text-align: left;
                    font-style: normal;
                    text-transform: none;
                }
                &::after {
                    content: '';
                    position: absolute;
                    top: 0;
                    right: 0;
                    width: 1px;
                    height: 100%;
                    width: 1px;
                    background: #333355;
                }
                &:last-child::after {
                    display: none;
                }
            }
        }
    }
}
</style>
applications/task-work-order/src/views/dataCockpit/components/DeviceHistoryDialog.vue
New file
@@ -0,0 +1,208 @@
<template>
    <el-dialog class="gd-dialog1" append-to-body v-model="visibleModel" :title="dialogTitle"
        :close-on-click-modal="false" :destroy-on-close="true">
        <el-form ref="queryParamsRef" :model="searchParams" class="gd-dialog-form">
            <el-form-item label="关键字" prop="keyword">
                <el-input class="gd-input" v-model="searchParams.keyword" placeholder="名称/序列号"
                    clearable @clear="handleSearch" />
            </el-form-item>
            <el-form-item prop="dateRange">
                <el-date-picker class="gd-date-picker"
                    popper-class="gd-tree-select-popper" v-model="searchParams.dateRange"
                    type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"
                    value-format="YYYY/MM/DD" @change="handleSearch" />
            </el-form-item>
            <el-form-item label="区域" prop="area">
                <el-tree-select class="gd-select" popper-class="gd-tree-select-popper"
                    v-model="searchParams.area" :data="areaTree" :props="areaTreeProps" node-key="value" check-strictly
                    clearable placeholder="请选择" @change="handleSearch" />
            </el-form-item>
            <el-form-item label="无人机类型" prop="droneType">
                <el-select class="gd-select" popper-class="gd-select-popper"
                    v-model="searchParams.droneType" placeholder="请选择" clearable @change="handleSearch">
                    <el-option v-for="item in droneTypeOptions" :key="item.value" :label="item.label"
                        :value="item.value" />
                </el-select>
            </el-form-item>
            <el-form-item class="gd-search-actions">
                <el-button :icon="RefreshRight" @click="resetForm"></el-button>
                <el-button :icon="Search" @click="handleSearch"></el-button>
            </el-form-item>
        </el-form>
        <div class="gd-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
            <div class="gd-table-content">
                <el-table class="gd-table" :data="list">
                    <el-table-column type="index" width="60" label="序号" />
                    <el-table-column prop="droneName" label="无人机名称" min-width="120" />
                    <el-table-column prop="droneSerialNo" label="无人机序列号" min-width="140" />
                    <el-table-column label="无人机类型" min-width="110">
                        <template #default="{ row }">
                            {{ getDroneTypeText(row.droneType) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="areaName" label="区域" min-width="90" />
                    <el-table-column prop="deviceName" label="数据来源" min-width="110" />
                    <el-table-column label="信号频段" min-width="100">
                        <template #default="{ row }">
                            {{ formatFrequency(row.signalFreqMhz) }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="alarmTime" label="发现时间" min-width="150" />
                    <el-table-column prop="stayDuration" label="停留时间" min-width="110" />
                    <el-table-column label="反制方式" min-width="110">
                        <template #default="{ row }">
                            {{ getCounterWayText(row.counterWay) }}
                        </template>
                    </el-table-column>
                </el-table>
            </div>
            <div class="gd-pagination-parent">
                <el-pagination popper-class="gd-select-popper"
                    v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
                    layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
            </div>
        </div>
    </el-dialog>
</template>
<script setup>
import { Search, RefreshRight } from '@element-plus/icons-vue'
import { computed, ref, watch } from 'vue'
import { alarmLogApi } from '@/api/dataCockpit'
const props = defineProps({
    modelValue: {
        type: Boolean,
        default: false
    },
    device: {
        type: Object,
        default: () => null
    }
})
const emit = defineEmits(['update:modelValue'])
const visibleModel = computed({
    get: () => props.modelValue,
    set: (val) => emit('update:modelValue', val)
})
const dialogTitle = computed(() => {
    return `反制设备名称 ${props.device?.name || ''}`.trim()
})
const initSearchParams = () => ({
    keyword: '',
    dateRange: [],
    area: '',
    droneType: '',
    current: 1,
    size: 10
})
const searchParams = ref(initSearchParams())
const queryParamsRef = ref(null)
const loading = ref(true)
const list = ref([])
const total = ref(0)
const areaTree = ref([
    {
        label: '古村区',
        value: '古村区'
    },
    {
        label: '新区',
        value: '新区'
    },
    {
        label: '产业园',
        value: '产业园'
    }
])
const areaTreeProps = {
    label: 'label',
    children: 'children'
}
const droneTypeOptions = [
    { label: '微型机', value: '1' },
    { label: '植保机', value: '2' }
]
const getList = async () => {
    if (!props.device?.id) {
        list.value = []
        total.value = 0
        return
    }
    const [startTime, endTime] = searchParams.value.dateRange || []
    const keyword = (searchParams.value.keyword || '').trim()
    const params = {
        alarmType: '2',
        deviceId: props.device.id,
        current: searchParams.value.current,
        size: searchParams.value.size,
        startTime,
        endTime,
        areaName: searchParams.value.area || undefined,
        droneType: searchParams.value.droneType || undefined,
        droneName: keyword || undefined,
    }
    loading.value = true
    try {
        const res = await alarmLogApi(params)
        list.value = res?.data?.data?.records ?? []
        total.value = res?.data?.data?.total ?? 0
    } finally {
        loading.value = false
    }
}
function handleSearch () {
    searchParams.value.current = 1
    getList()
}
function resetForm () {
    searchParams.value = initSearchParams()
    getList()
}
const formatFrequency = (value) => {
    if (value == null || value === '') return '-'
    return `${value}MHZ`
}
const getCounterWayText = (value) => {
    if (value === 1 || value === '1') return '信号干扰'
    if (value === 2 || value === '2') return '诱导驱离'
    if (value === 3 || value === '3') return '无'
    return value || '-'
}
const getDroneTypeText = (value) => {
    if (value === 1 || value === '1') return '微型机'
    if (value === 2 || value === '2') return '植保机'
    return value || '-'
}
watch(
    () => visibleModel.value,
    (visible) => {
        if (visible) {
            searchParams.value.current = 1
            getList()
        }
    }
)
</script>
<style lang="scss" scoped></style>
applications/task-work-order/src/views/dataCockpit/components/EmptyState.vue
New file
@@ -0,0 +1,59 @@
<!--
 * @Author       : yuan
 * @Date         : 2026-01-09 15:06:02
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-09 15:10:44
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\EmptyState.vue
 * @Description  :
 * Copyright 2026 OBKoro1, All Rights Reserved.
 * 2026-01-09 15:06:02
-->
<template>
    <div class="empty-state">
        <div class="empty-content">
            <img :src="noDataBg" alt="暂无数据">
            <span class="empty-text">暂无数据</span>
        </div>
    </div>
</template>
<script setup>
import noDataBg from '@/assets/images/no-data-bg.png'
</script>
<style scoped lang="scss">
.empty-state {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1;
    width: 100%;
    height: 100%;
    min-height: 220px;
    .empty-content {
        display: flex;
        align-items: center;
        justify-content: center;
        flex-direction: column;
        text-align: center;
    }
    img {
        max-width: 78%;
        max-height: 78%;
        object-fit: contain;
    }
    .empty-text {
        font-family: Source Han Sans CN, Source Han Sans CN;
        font-weight: 400;
        font-size: 14px;
        color: #9E9EBA;
        line-height: 18px;
        text-align: center;
        font-style: normal;
        text-transform: none;
    }
}
</style>
applications/task-work-order/src/views/dataCockpit/components/EquipmentWarning.vue
New file
@@ -0,0 +1,101 @@
<!--
 * @Author       : yuan
 * @Date         : 2026-01-08 09:29:07
 * @LastEditors  : yuan
 * @LastEditTime : 2026-01-08 14:49:25
 * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\EquipmentWarning.vue
 * @Description  :
 * Copyright 2026 OBKoro1, All Rights Reserved.
 * 2026-01-08 09:29:07
-->
<template>
    <div
        class="equipment-warning"
        v-loading="loading"
        element-loading-background="rgba(5, 5, 15, 0.6)"
        element-loading-text="加载中..."
    >
        <EmptyState v-if="!equipmentWarningData.length" />
        <EquipmentTemplate
            v-else
            v-for="(item, ind) in equipmentWarningData"
            :key="ind"
            :data="item"
            @click="onCardClick"
        />
    </div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import EquipmentTemplate from './templateComponents/EquipmentTemplate.vue'
import { deviceSearchApi } from '@/api/dataCockpit'
import EmptyState from './EmptyState.vue'
const equipmentWarningData = ref([])
const loading = ref(true)
const minLoadingMs = 400
const statusMap = {
    0: { text: '在线', statusType: 'online' },
    1: { text: '离线', statusType: 'offline' },
    2: { text: '故障', statusType: 'fault' },
    3: { text: '报废', statusType: 'offline' }
}
const deviceTypeMap = {
    1: '便捷侦测箱',
    2: '反制枪',
    3: '察打一体'
}
const formatDeviceItem = (item) => {
    const statusInfo = statusMap[item?.status] || { text: '-', statusType: '' }
    return {
        id: item.id,
        name: item.deviceName || item.deviceModel || '-',
        status: statusInfo.text,
        statusType: statusInfo.statusType,
        type: deviceTypeMap[item.deviceType] || item.deviceType || '-',
        manufacturer: item.manufacturer || '-',
        azimuth: item.azimuth ?? 0,
        pitch: item.elevation ?? 0,
        range: item.effectiveRangeKm ?? 0,
        model: item.deviceModel || '-',
        longitude: item.longitude ?? '-',
        latitude: item.latitude ?? '-'
    }
}
const fetchDeviceWarning = async () => {
    const startAt = Date.now()
    loading.value = true
    try {
        const params = { current: 1, size: 20, deviceStatusList: '1,2,3' }
        const res = await deviceSearchApi(params)
        const records = res?.data?.data?.records ?? []
        equipmentWarningData.value = records.map(formatDeviceItem)
    } finally {
        const elapsed = Date.now() - startAt
        if (elapsed < minLoadingMs) {
            await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed))
        }
        loading.value = false
    }
}
onMounted(() => {
    fetchDeviceWarning()
})
const onCardClick = (item) => {
    console.log('点击设备卡片:', item.id)
}
</script>
<style lang="scss" scoped>
.equipment-warning {
    width: 100%;
    height: 100%;
}
</style>
applications/task-work-order/src/views/dataCockpit/components/HistoryWarning.vue
New file
@@ -0,0 +1,123 @@
<template>
    <div
        class="history-warning"
        v-loading="loading"
        element-loading-background="rgba(5, 5, 15, 0.6)"
        element-loading-text="加载中..."
    >
        <!-- 搜索区(设计稿红框) -->
        <div class="search-box">
            <!-- 关键字 -->
            <el-input v-model="query.keyword" class="gd-input" placeholder="请输入数据名称" clearable
                @keyup.enter="onSearch">
            </el-input>
            <!-- 日期范围 -->
            <el-date-picker v-model="query.dateRange"
                  class="gd-date-picker"
                 popper-class="gd-date-picker-popper"
                type="daterange"
                range-separator="~"
                start-placeholder="创建开始日期" end-placeholder="结束日期" format="YYYY年MM月DD日" value-format="YYYY-MM-DD"
                :clearable="true" @change="onSearch">
            </el-date-picker>
        </div>
        <!-- 列表 -->
        <div class="list-content">
            <EmptyState v-if="!filteredList.length" />
            <HistoryTemplate
                v-else
                v-for="(item, ind) in filteredList"
                :key="item.id || ind"
                :data="item"
                @click="onCardClick"
            />
        </div>
    </div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import HistoryTemplate from './templateComponents/HistoryTemplate.vue'
import { alarmLogApi } from '@/api/dataCockpit'
import EmptyState from './EmptyState.vue'
const historyWarningData = ref([])
const loading = ref(true)
const minLoadingMs = 400
// 查询条件:keyword + dateRange(YYYY-MM-DD数组)
const query = ref({
    keyword: '',
    dateRange: [] // [start, end]
})
const onSearch = () => {
    fetchHistoryWarning()
}
const onCardClick = (item) => {
    console.log('点击无人机卡片:', item.id)
}
const fetchHistoryWarning = async () => {
    const startAt = Date.now()
    loading.value = true
    try {
        const [startTime, endTime] = query.value.dateRange || []
        const keyword = (query.value.keyword || '').trim()
        const params = {
            current: 1,
            size: 20,
            alarmType: '2',
            startTime,
            endTime,
            droneName: keyword || undefined,
            droneSerialNo: keyword || undefined
        }
        const res = await alarmLogApi(params)
        const records = res?.data?.data?.records ?? []
        historyWarningData.value = records
    } finally {
        const elapsed = Date.now() - startAt
        if (elapsed < minLoadingMs) {
            await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed))
        }
        loading.value = false
    }
}
const filteredList = computed(() => {
    return historyWarningData.value
})
onMounted(() => {
    fetchHistoryWarning()
})
</script>
<style scoped lang="scss">
.history-warning {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    .search-box {
        display: flex;
        flex-direction: column;
        gap: 10px;
        ::v-deep(.gd-date-picker) {
            width: 100% !important;
        }
    }
    .list-content {
        height: 0;
        flex: 1;
    }
}
</style>
applications/task-work-order/src/views/dataCockpit/components/LeftContainer.vue
New file
@@ -0,0 +1,219 @@
<template>
    <div class="left-container" :class="{ collapsed: collapsedModel }">
        <div class="wrapper">
            <TitleTemplate>异常情况告警</TitleTemplate>
            <div class="category-container">
                <div class="category-item" v-for="(item, ind) in categoryList" :key="ind"
                    @click="activeIdModel = item.id" :class="{ active: activeIdModel === item.id }">
                    {{ item.name }}
                    <span class="badge" v-if="item.badge">{{ item.badge }}</span>
                </div>
            </div>
            <div class="content">
                <component :is="currentComponent" />
            </div>
        </div>
        <div class="collapse-btn" @click="toggleCollapse">
            <span class="arrow" :class="collapsedModel ? 'right' : 'left'"></span>
        </div>
    </div>
</template>
<script setup>
import TitleTemplate from './templateComponents/TitleTemplate.vue'
import RealWarning from './RealWarning.vue'
import EquipmentWarning from './EquipmentWarning.vue'
import HistoryWarning from './HistoryWarning.vue'
import { alarmStatisticsApi } from '@/api/dataCockpit'
import { onMounted } from 'vue'
const props = defineProps({
    activeId: {
        type: Number,
        default: 1
    },
    collapsed: {
        type: Boolean,
        default: false
    }
})
const emit = defineEmits(['update:activeId', 'update:collapsed'])
const activeIdModel = computed({
    get: () => props.activeId,
    set: (val) => emit('update:activeId', val)
})
const collapsedModel = computed({
    get: () => props.collapsed,
    set: (val) => emit('update:collapsed', val)
})
const categoryList = ref([
    {
        id: 1,
        name: '实时告警',
        badge: 0,
        component: RealWarning
    },
    {
        id: 2,
        name: '设备告警',
        badge: 0,
        component: EquipmentWarning
    },
    {
        id: 3,
        name: '历史告警',
        badge: 0,
        component: HistoryWarning
    }
])
const currentComponent = computed(() => {
    return categoryList.value.find(i => i.id === activeIdModel.value)?.component
})
const toggleCollapse = () => {
    collapsedModel.value = !collapsedModel.value
}
const updateBadges = async () => {
    const res = await alarmStatisticsApi()
    const data = res?.data?.data || {}
    const realTotal = data.realTimeAlarmCount ?? 0
    const historyTotal = data.historyAlarmCount ?? 0
    const deviceTotal = data.deviceAlarmCount ?? 0
    categoryList.value = categoryList.value.map((item) => {
        if (item.id === 1) return { ...item, badge: realTotal }
        if (item.id === 2) return { ...item, badge: deviceTotal }
        if (item.id === 3) return { ...item, badge: historyTotal }
        return item
    })
}
onMounted(() => {
    updateBadges()
})
</script>
<style lang="scss" scoped>
.left-container {
    position: relative;
    transition: transform 0.3s ease-in-out;
    &.collapsed {
        transform: translateX(-100%);
    }
}
.collapse-btn {
    position: absolute;
    top: 50%;
    left: 357px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    z-index: 10;
    transform: translateY(-50%);
    .arrow {
        width: 24px;
        height: 24px;
        background: url('@/assets/images/dataCockpit/left-btn.png') center / 100% 100% no-repeat;
    }
    &:hover {
        background: rgba(40, 79, 227, 0.6);
    }
}
.category-container {
    position: relative;
    display: flex;
    justify-content: space-between;
    &::after {
        content: '';
        position: absolute;
        left: 0;
        bottom: 0;
        width: 100%;
        height: 1px;
        background: #333355;
    }
    .category-item {
        position: relative;
        width: 0;
        flex: 1;
        margin-left: 26px;
        height: 68px;
        line-height: 68px;
        font-family: Source Code Pro, Source Code Pro;
        font-weight: 400;
        font-size: 14px;
        color: #86909C;
        text-align: center;
        font-style: normal;
        text-transform: none;
        cursor: pointer;
        &:first-child {
            margin-left: 0;
        }
        &.active {
            font-weight: normal;
            color: #FFFFFF;
        }
        &::after {
            content: '';
            position: absolute;
            left: 0;
            bottom: 0;
            width: 100%;
            height: 0;
        }
        &.active::after {
            height: 3px;
            background: #284FE3;
            box-shadow: 0px 3px 2px 0px rgba(15, 89, 255, 0.1), 0px 7px 5px 0px rgba(15, 89, 255, 0.15), 0px 13px 10px 0px rgba(15, 89, 255, 0.18), 0px 22px 18px 0px rgba(15, 89, 255, 0.21), 0px 42px 33px 0px rgba(15, 89, 255, 0.26), 0px 100px 80px 0px rgba(15, 89, 255, 0.36);
            border-radius: 5px 5px 0px 0px;
            z-index: 9;
        }
        .badge {
            position: absolute;
            top: 16px;
            right: 4px;
            min-width: 18px;
            line-height: 18px;
            font-size: 12px;
            color: #FFFFFF;
            background: #FF3B30;
            border-radius: 50%;
            text-align: center;
            box-shadow: 0px 2px 6px rgba(255, 59, 48, 0.6);
            pointer-events: none;
        }
    }
}
.content {
    height: 0;
    flex: 1;
    margin-top: 20px;
    overflow-x: hidden;
    overflow-y: auto;
}
</style>
Diff truncated after the above file
applications/task-work-order/src/views/dataCockpit/components/RealWarning.vue applications/task-work-order/src/views/dataCockpit/components/RightContainer.vue applications/task-work-order/src/views/dataCockpit/components/templateComponents/EquipmentTemplate.vue applications/task-work-order/src/views/dataCockpit/components/templateComponents/HistoryTemplate.vue applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealTemplate.vue applications/task-work-order/src/views/dataCockpit/components/templateComponents/TitleTemplate.vue applications/task-work-order/src/views/dataCockpit/index.vue applications/task-work-order/src/views/desk/notice.vue (deleted) applications/task-work-order/src/views/device/addDevice.vue (deleted) applications/task-work-order/src/views/device/airport.vue (deleted) applications/task-work-order/src/views/device/components/DeviceSettingBox.vue (deleted) applications/task-work-order/src/views/device/components/DeviceSettingPopover.vue (deleted) applications/task-work-order/src/views/device/components/DockControlPanel.vue (deleted) applications/task-work-order/src/views/device/components/devicePerShare.vue (deleted) applications/task-work-order/src/views/device/components/firmwareManage.vue (deleted) applications/task-work-order/src/views/device/components/use-device-setting.js (deleted) applications/task-work-order/src/views/device/components/use-dock-control.js (deleted) applications/task-work-order/src/views/device/devicePerMenu.vue (deleted) applications/task-work-order/src/views/device/fly.vue (deleted) applications/task-work-order/src/views/device/index.vue (deleted) applications/task-work-order/src/views/flow/deploy.vue (deleted) applications/task-work-order/src/views/flow/follow.vue (deleted) applications/task-work-order/src/views/flow/manager.vue (deleted) applications/task-work-order/src/views/flow/model.vue (deleted) applications/task-work-order/src/views/job/components/CancelTaskDialog.vue (deleted) applications/task-work-order/src/views/job/components/DeviceJobDetails.vue (deleted) applications/task-work-order/src/views/job/components/DeviceJobDetailsMap.vue (deleted) applications/task-work-order/src/views/job/components/FileTypePlay.vue (deleted) applications/task-work-order/src/views/job/components/JobRelatedEvents.vue (deleted) applications/task-work-order/src/views/job/components/SearchBox.vue (deleted) applications/task-work-order/src/views/job/components/TaskAlgorithmBusiness.vue (deleted) applications/task-work-order/src/views/job/components/TaskIntermediateContent/AddTask.vue (deleted) applications/task-work-order/src/views/job/components/TaskIntermediateContent/TaskIntermediateContent.vue (deleted) applications/task-work-order/src/views/job/components/TaskIntermediateContent/TaskTable.vue (deleted) applications/task-work-order/src/views/job/components/TaskTop/TaskEvent.vue (deleted) applications/task-work-order/src/views/job/components/TaskTop/TaskIndustry.vue (deleted) applications/task-work-order/src/views/job/components/TaskTop/TaskTime.vue (deleted) applications/task-work-order/src/views/job/components/TaskTop/TaskTop.vue (deleted) applications/task-work-order/src/views/job/components/TaskTop/TaskTotal.vue (deleted) applications/task-work-order/src/views/job/components/executeNestDetails.vue (deleted) applications/task-work-order/src/views/job/const/drc.js (deleted) applications/task-work-order/src/views/job/jobinfo.vue (deleted) applications/task-work-order/src/views/job/jobserver.vue (deleted) applications/task-work-order/src/views/job/jobstatistics.vue (deleted) applications/task-work-order/src/views/layerManagement/components/folderFile.vue (deleted) applications/task-work-order/src/views/layerManagement/components/leftList.vue (deleted) applications/task-work-order/src/views/layerManagement/components/nationalSpatialPlanning.vue (deleted) applications/task-work-order/src/views/layerManagement/components/rightEdit.vue (deleted) applications/task-work-order/src/views/layerManagement/index.vue (deleted) applications/task-work-order/src/views/license/license.vue (deleted) applications/task-work-order/src/views/license/upload.vue (deleted) applications/task-work-order/src/views/monitor/log/api.vue (deleted) applications/task-work-order/src/views/monitor/log/error.vue (deleted) applications/task-work-order/src/views/monitor/log/flightLog.vue (deleted) applications/task-work-order/src/views/monitor/log/usual.vue (deleted) applications/task-work-order/src/views/odm/task.vue (deleted) applications/task-work-order/src/views/orderView/orderDataManage/appInnovation/index.vue applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/index.vue applications/task-work-order/src/views/orderView/orderDataManage/evaluate/index.vue applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/index.vue applications/task-work-order/src/views/orderView/orderManage/clueEvents/index.vue applications/task-work-order/src/views/orderView/orderManage/inspectionReport/index.vue applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/index.vue applications/task-work-order/src/views/orderView/orderManage/operatingIncome/index.vue applications/task-work-order/src/views/orderView/orderManage/orderManage/index.vue applications/task-work-order/src/views/report/reportlist.vue (deleted) applications/task-work-order/src/views/resource/components/spotDetails.vue applications/task-work-order/src/views/resource/patchManagement.vue applications/task-work-order/src/views/system/menu.vue applications/task-work-order/src/views/system/param.vue applications/task-work-order/src/views/system/user/FormDiaLog.vue applications/task-work-order/src/views/system/user/index.vue applications/task-work-order/src/views/system/userinfo.vue applications/task-work-order/src/views/test.vue (deleted) applications/task-work-order/src/views/tickets/component/AddEditDetails.vue (deleted) applications/task-work-order/src/views/tickets/component/SearchBox.vue (deleted) applications/task-work-order/src/views/tickets/orderLog.vue (deleted) applications/task-work-order/src/views/tickets/orderLogCopy.vue (deleted) applications/task-work-order/src/views/tickets/ticket-copy.vue (deleted) applications/task-work-order/src/views/tickets/ticket.vue (deleted) applications/task-work-order/src/views/tickets/ticketComponent/CreateTicketDialog.vue (deleted) applications/task-work-order/src/views/tickets/ticketComponent/DispatchDialog.vue (deleted) applications/task-work-order/src/views/tickets/ticketComponent/RecheckDialog.vue (deleted) applications/task-work-order/src/views/tickets/ticketComponent/TicketDetailDialog.vue (deleted) applications/task-work-order/src/views/tool/code.vue (deleted) applications/task-work-order/src/views/tool/datasource.vue (deleted) applications/task-work-order/src/views/tool/model.vue (deleted) applications/task-work-order/src/views/wel/components/backlog.vue (deleted) applications/task-work-order/src/views/wel/components/calendarBox.vue (deleted) applications/task-work-order/src/views/wel/components/flightStatistics.vue (deleted) applications/task-work-order/src/views/wel/components/flyratio.vue (deleted) applications/task-work-order/src/views/wel/components/proportionStatic.vue (deleted) applications/task-work-order/src/views/wel/components/statistics.vue (deleted) applications/task-work-order/src/views/wel/components/taskOutcome.vue (deleted) applications/task-work-order/src/views/wel/dashboard.vue (deleted) applications/task-work-order/src/views/wel/index.vue applications/task-work-order/src/views/workspace/index.vue (deleted) detect_duplicates.js (deleted) packages/utils/date/index.js pnpm-lock.yaml uniapps/work-app/README.md uniapps/work-app/package.json uniapps/work-app/src/hooks/useGlobalWS.js uniapps/work-app/src/manifest.json uniapps/work-app/src/pages.json uniapps/work-app/src/pages/login/index.vue uniapps/work-app/src/subPackages/userDetail/infos/index.vue uniapps/work-app/src/subPackages/userDetail/password/index.vue uniapps/work-app/src/subPackages/workDetail/index.vue uniapps/work-app/src/utils/websocket.js