From 8a7fb9a519cae32d38ba66058d243aa7e6b6aeb2 Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Tue, 27 Jan 2026 08:57:49 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'
---
applications/task-work-order/src/views/orderView/flyingHand/FlyingHandDialog.vue | 278 +++++++++++++++++++
applications/task-work-order/src/views/orderView/flyingHand/index.vue | 222 +++++++++++++++
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/FormDiaLog.vue | 4
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue | 0
applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue | 6
applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js | 66 ++++
applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue | 2
applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue | 0
applications/drone-command/src/components/map-container/device-map-container.vue | 122 ++++++--
applications/drone-command/src/views/basicManage/deviceStock/index.vue | 3
applications/drone-command/src/views/dataCockpit/components/LegendBar.vue | 8
applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue | 28 +
applications/drone-command/src/components/map-container/device-map-materials.js | 40 ++
applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue | 6
applications/task-work-order/src/views/orderView/flyingHand/flyingHandApi.js | 37 ++
applications/drone-command/src/api/dataCockpit/index.js | 5
16 files changed, 766 insertions(+), 61 deletions(-)
diff --git a/applications/drone-command/src/api/dataCockpit/index.js b/applications/drone-command/src/api/dataCockpit/index.js
index f08ac7f..68024bd 100644
--- a/applications/drone-command/src/api/dataCockpit/index.js
+++ b/applications/drone-command/src/api/dataCockpit/index.js
@@ -2,7 +2,7 @@
* @Author : yuan
* @Date : 2026-01-09 11:14:04
* @LastEditors : yuan
- * @LastEditTime : 2026-01-26 09:38:16
+ * @LastEditTime : 2026-01-26 19:02:07
* @FilePath : \applications\drone-command\src\api\dataCockpit\index.js
* @Description :
* Copyright 2026 OBKoro1, All Rights Reserved.
@@ -78,7 +78,8 @@
export const cockpitAggregationApi = (params) => {
return request({
url: '/drone-fw/device/fwDevice/statisticalDeviceOut',
- method: 'get'
+ method: 'get',
+ params
})
}
diff --git a/applications/drone-command/src/components/map-container/device-map-container.vue b/applications/drone-command/src/components/map-container/device-map-container.vue
index a8f8566..f5813aa 100644
--- a/applications/drone-command/src/components/map-container/device-map-container.vue
+++ b/applications/drone-command/src/components/map-container/device-map-container.vue
@@ -2,8 +2,9 @@
<div class="map-shell">
<CommonCesiumMap ref="mapRef" class="command-cesium map-container" :dom-id="props.containerId" :active="true"
:flat-mode="false" :terrain="true" :layer-mode="4" :contour="false" :boundary="false"
- :show-admin-boundary="true" :zoom-to-boundary="true" :enable-stage-emit="true" :cluster-height="100000"
- @ready="handleMapReady" @stage-change="handleStageChange" />
+ :show-admin-boundary="true" :zoom-to-boundary="true" :enable-stage-emit="true"
+ :cluster-height="CLUSTER_HEIGHT" :detail-height="DETAIL_HEIGHT" @ready="handleMapReady"
+ @stage-change="handleStageChange" />
<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">
@@ -63,6 +64,9 @@
createRadialGradientMaterial,
getTexturedVertexFormat,
} from './device-map-materials'
+
+const CLUSTER_HEIGHT = 100000
+const DETAIL_HEIGHT = 10000
const props = defineProps({
onlineDevices: {
@@ -157,6 +161,14 @@
const latitude = Number(latitudeRaw)
if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) return null
return { longitude, latitude }
+}
+
+const getDeviceRange = item => {
+ if (!item) return null
+ const rawRange = item.effectiveRangeKm ?? item.range ?? item.coverRadiusM
+ const range = Number(rawRange)
+ if (!Number.isFinite(range) || range <= 0) return null
+ return range
}
const ensureCockpitPrimitiveLayer = () => {
@@ -305,6 +317,20 @@
if (aggregationSource) aggregationSource.show = visible
}
+const setDroneVisibility = visible => {
+ if (droneTrackBillboardCollection) droneTrackBillboardCollection.show = visible
+ if (droneTrackPolylineCollection) droneTrackPolylineCollection.show = visible
+ if (!visible && selectedTargetType.value === 'drone') {
+ closePopup()
+ }
+}
+const getStageByHeight = height => {
+ if (height == null) return 'detail'
+ if (height >= CLUSTER_HEIGHT) return 'cluster'
+ if (height <= DETAIL_HEIGHT) return 'detail'
+ return 'mid'
+}
+
const ensureCountyCenterMap = () => {
if (countyCenterMap.size) return
const geojson = JSON.parse(jaGeojsonRaw)
@@ -316,24 +342,44 @@
})
}
-const buildSimulatedTrackPoints = base => {
- ensureCountyCenterMap()
- const anchor = base || { longitude: 116.397, latitude: 39.908 }
- const offsets = [
- [0, 0, 120],
- [0.02, 0.012, 150],
- [0.04, 0.006, 180],
- [0.06, -0.008, 200],
- [0.03, -0.02, 160],
- [0.0, -0.015, 140],
- [-0.02, -0.005, 120],
- [-0.01, 0.01, 130],
+const buildSimulatedTrackPoints = () => {
+ return [
+ {
+ longitude: 114.963191,
+ latitude: 27.136716,
+ height: 120
+ },
+ {
+ longitude:114.957308,
+ latitude: 27.138452,
+ height: 120
+ },
+ {
+ longitude:114.952,
+ latitude: 27.136317,
+ height: 120
+ },
+ {
+ longitude:114.949293,
+ latitude: 27.133864,
+ height: 120
+ },
+ {
+ longitude:114.944666,
+ latitude: 27.130526,
+ height: 120
+ },
+ {
+ longitude:114.945909,
+ latitude:27.127845,
+ height: 120
+ },
+ {
+ longitude:114.962974,
+ latitude:27.136242,
+ height: 120
+ },
]
- return offsets.map(item => ({
- longitude: anchor.longitude + item[0],
- latitude: anchor.latitude + item[1],
- height: item[2],
- }))
}
@@ -422,13 +468,11 @@
const renderSimulatedDroneTrack = () => {
if (!viewer) return
clearDroneTrackEntities()
- ensureCountyCenterMap()
ensureDroneTrackCollections()
+ droneTrackBillboardCollection.show = detailVisible.value
+ droneTrackPolylineCollection.show = detailVisible.value
droneTrackRuntime = []
- const centers = Array.from(countyCenterMap.entries())
- const baseCenters = centers.length
- ? centers.slice(0, 4)
- : [['默认区域', { longitude: 116.397, latitude: 39.908 }]]
+ const baseCenters = [['默认区域', { longitude:114.958541, latitude: 27.121917 }]]
const segmentDuration = 6
const baseTrackColor = Cesium.Color.fromCssColorString('red')
baseCenters.forEach(([centerName, center], trackIndex) => {
@@ -454,7 +498,7 @@
})
const droneId = `drone-track-${trackIndex}`
const billboard = droneTrackBillboardCollection.add({
- position: positions[0],
+ position: positions[positions.length - 1],
image: droneIcon,
width: 36,
height: 36,
@@ -511,7 +555,8 @@
const position = getDevicePosition(item)
if (!position) return
const entityId = `online-device-${item.id ?? index}-${index}`
- addDeviceRings(position, ringFillInstancesByStyle, ringOutlineInstancesByStyle)
+ const rangeMeters = getDeviceRange(item)
+ addDeviceRings(position, ringFillInstancesByStyle, ringOutlineInstancesByStyle, rangeMeters)
const billboard = deviceBillboardCollection.add({
position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
image: equipmentIcon,
@@ -528,9 +573,20 @@
ringFillInstancesByStyle.forEach((instances, index) => {
if (!instances.length) return
const ring = RING_STYLES[index]
+ const alpha = ring.alpha ?? 0.64
+ const innerRatio =
+ typeof ring.innerRatio === 'number'
+ ? ring.innerRatio
+ : ring.inner && ring.outer
+ ? Math.min(Math.max(ring.inner / ring.outer, 0), 0.9)
+ : 0
const material = createRadialGradientMaterial(
- Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(0.64),
- Cesium.Color.fromCssColorString(ring.gradient[1]).withAlpha(0.64)
+ Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(alpha),
+ Cesium.Color.fromCssColorString(ring.gradient[1]).withAlpha(alpha),
+ {
+ gamma: 1.7,
+ innerCutoff: innerRatio,
+ }
)
const primitive = new Cesium.GroundPrimitive({
geometryInstances: instances,
@@ -629,7 +685,7 @@
const renderDefenseZones = zones => {
if (!viewer) return
clearDefenseZoneEntities()
- const result = buildZonePrimitives(zones, '#19D266', '#2AEDBF', '#012B11')
+ const result = buildZonePrimitives(zones, '#72F3FF', '#72F3FF', '#0A7C88')
defenseZonePrimitive = result.primitive
defenseZoneOutlinePrimitive = result.outlinePrimitive
if (defenseZonePrimitive) defenseZonePrimitive.show = detailVisible.value
@@ -640,7 +696,7 @@
const renderPartitions = zones => {
if (!viewer) return
clearPartitionEntities()
- const result = buildZonePrimitives(zones, '#FFCD2A', '#FFC609', '#583300')
+ const result = buildZonePrimitives(zones, '#FFD772', '#FFD772', '#A86B00')
partitionPrimitive = result.primitive
partitionOutlinePrimitive = result.outlinePrimitive
if (partitionPrimitive) partitionPrimitive.show = detailVisible.value
@@ -693,7 +749,7 @@
width: 66.73,
height: 43,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
- heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+ // disableDepthTestDistance: Number.POSITIVE_INFINITY,
},
label: {
text: `${count}`,
@@ -706,7 +762,6 @@
pixelOffset: new Cesium.Cartesian2(0, -35),
disableDepthTestDistance: Number.POSITIVE_INFINITY,
- heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
})
})
@@ -855,13 +910,14 @@
const showCluster = stage === 'cluster'
setClusterVisibility(showCluster)
setDetailVisibility(!showCluster)
+ setDroneVisibility(!showCluster)
}
const handleMapReady = ({ viewer: mapViewer }) => {
viewer = mapViewer
ensureCockpitPrimitiveLayer()
const height = viewer?.camera?.positionCartographic?.height
- const stage = height != null && height >= 100000 ? 'cluster' : 'detail'
+ const stage = getStageByHeight(height)
updateStageDisplay(stage)
renderDeviceEntities(props.onlineDevices)
loadDefenseZones()
diff --git a/applications/drone-command/src/components/map-container/device-map-materials.js b/applications/drone-command/src/components/map-container/device-map-materials.js
index bf2edc8..6177bb7 100644
--- a/applications/drone-command/src/components/map-container/device-map-materials.js
+++ b/applications/drone-command/src/components/map-container/device-map-materials.js
@@ -1,6 +1,15 @@
import * as Cesium from 'cesium'
-export const RING_STYLES = [{ inner: 0, outer: 2000, gradient: ['#FF361C', '#360B00'] }]
+export const RING_STYLES = [
+ {
+ inner: 0,
+ outer: 2000,
+ gradient: ['#FF6A57', '#A11806'],
+ alpha: 0.42,
+ outlineAlpha: 0.85,
+ innerRatio: 0.12,
+ },
+]
export const MATERIAL_TYPE = 'RadialGradientMaterial'
export const DRONE_TRACK_MATERIAL_TYPE = 'DroneTrackFlowMaterial'
@@ -26,15 +35,25 @@
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),
+ gamma: 1.7,
+ innerCutoff: 0.0,
},
source: `
+ uniform vec4 color1;
+ uniform vec4 color2;
+ uniform float gamma;
+ uniform float innerCutoff;
+
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);
+ float ramp = smoothstep(innerCutoff, 1.0, t);
+ float shaped = pow(t, gamma);
+ float alpha = color.a * ramp * shaped;
material.diffuse = color.rgb;
- material.alpha = color.a;
+ material.alpha = alpha;
return material;
}
`,
@@ -82,9 +101,9 @@
})
}
-export const createRadialGradientMaterial = (color1, color2) => {
+export const createRadialGradientMaterial = (color1, color2, options = {}) => {
registerRadialGradientMaterial()
- return Cesium.Material.fromType(MATERIAL_TYPE, { color1, color2 })
+ return Cesium.Material.fromType(MATERIAL_TYPE, { color1, color2, ...(options || {}) })
}
export const createDroneTrackMaterial = options => {
@@ -115,18 +134,19 @@
return positions
}
-export const addDeviceRings = (center, ringFillInstancesByStyle, ringOutlineInstancesByStyle) => {
+export const addDeviceRings = (center, ringFillInstancesByStyle, ringOutlineInstancesByStyle, rangeMeters) => {
if (!ringFillInstancesByStyle) return
const centerPosition = Cesium.Cartesian3.fromDegrees(center.longitude, center.latitude, 0)
const vertexFormat = getTexturedVertexFormat()
RING_STYLES.forEach((ring, index) => {
if (!ringFillInstancesByStyle[index]) return
+ const outer = Number.isFinite(rangeMeters) && rangeMeters > 0 ? rangeMeters : ring.outer
ringFillInstancesByStyle[index].push(
new Cesium.GeometryInstance({
geometry: new Cesium.EllipseGeometry({
center: centerPosition,
- semiMajorAxis: ring.outer,
- semiMinorAxis: ring.outer,
+ semiMajorAxis: outer,
+ semiMinorAxis: outer,
vertexFormat,
}),
})
@@ -135,7 +155,9 @@
if (!ringOutlineInstancesByStyle) return
RING_STYLES.forEach((ring, index) => {
if (!ringOutlineInstancesByStyle[index]) return
- const positions = buildCirclePositions(center, ring.outer)
+ const outer = Number.isFinite(rangeMeters) && rangeMeters > 0 ? rangeMeters : ring.outer
+ const positions = buildCirclePositions(center, outer)
+ const outlineAlpha = typeof ring.outlineAlpha === 'number' ? ring.outlineAlpha : 0.8
ringOutlineInstancesByStyle[index].push(
new Cesium.GeometryInstance({
geometry: new Cesium.GroundPolylineGeometry({
@@ -144,7 +166,7 @@
}),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
- Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(0.8)
+ Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(outlineAlpha)
),
},
})
diff --git a/applications/drone-command/src/views/basicManage/deviceStock/index.vue b/applications/drone-command/src/views/basicManage/deviceStock/index.vue
index 4933b82..e635bdd 100644
--- a/applications/drone-command/src/views/basicManage/deviceStock/index.vue
+++ b/applications/drone-command/src/views/basicManage/deviceStock/index.vue
@@ -62,7 +62,6 @@
<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-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="设备类型">
@@ -80,7 +79,7 @@
</el-table-column>
<el-table-column prop="source" show-overflow-tooltip width="112" label="来源" />
<el-table-column prop="purpose" show-overflow-tooltip width="206" label="用途" />
- <el-table-column prop="belongDeptName" show-overflow-tooltip width="124" label="所属部门" />
+ <el-table-column prop="belongDeptName" show-overflow-tooltip label="所属部门" />
<el-table-column prop="charger" show-overflow-tooltip width="100" label="负责人" />
<el-table-column prop="yxzt" show-overflow-tooltip width="116" label="运行状态">
<template v-slot="{ row }">
diff --git a/applications/drone-command/src/views/dataCockpit/components/LegendBar.vue b/applications/drone-command/src/views/dataCockpit/components/LegendBar.vue
index d115225..52535aa 100644
--- a/applications/drone-command/src/views/dataCockpit/components/LegendBar.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/LegendBar.vue
@@ -85,13 +85,13 @@
}
.legend-color.defense {
- background: radial-gradient(50% 50% at 50% 50%, #2aedbf 0%, #012b11 100%);
- border: 1px solid #19d266;
+ background: radial-gradient(50% 50% at 50% 50%, rgba(114, 243, 255, 0.45) 0%, rgba(10, 124, 136, 0.55) 100%);
+ border: 1px solid rgba(114, 243, 255, 0.85);
}
.legend-color.partition {
- background: radial-gradient(50% 50% at 50% 50%, #ffc609 0%, #583300 100%);
- border: 1px solid #ffcd2a;
+ background: radial-gradient(50% 50% at 50% 50%, rgba(255, 215, 114, 0.5) 0%, rgba(168, 107, 0, 0.6) 100%);
+ border: 1px solid rgba(255, 215, 114, 0.9);
}
.legend-icon {
diff --git a/applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue b/applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
index 631a674..0315403 100644
--- a/applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
@@ -67,7 +67,7 @@
</div>
<div class="col">
<span class="label">有效范围</span>
- {{ data.effectiveRangeKm }}m
+ {{ Number(data.effectiveRangeKm).toFixed(0) }}m
</div>
</div>
</div>
diff --git a/applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue b/applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue
index ca54c44..a939219 100644
--- a/applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue
+++ b/applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue
@@ -123,6 +123,7 @@
v-model="formData.effectiveRangeKm"
:min="0"
:precision="0"
+ :step="1"
:controls="false"
placeholder="请输入"
/>
@@ -227,7 +228,14 @@
function formatRange(value) {
if (value == null || value === '') return '-'
- return `${value}`
+ const normalized = normalizeRange(value)
+ return normalized == null ? '-' : `${normalized}`
+}
+
+function normalizeRange(value) {
+ const numeric = Number(value)
+ if (!Number.isFinite(numeric)) return null
+ return Math.trunc(numeric)
}
// 关闭弹框
@@ -242,14 +250,16 @@
submitting.value = true
try {
const {longitude, latitude} = parseDeployLocation(formData.value.deployLocation)
+ const normalizedRange = normalizeRange(formData.value.effectiveRangeKm)
const payload = {
id: formData.value.id || formData.value.deviceId || undefined,
- effectiveRangeKmIsNotNull: formData.value.effectiveRangeKm ? 1 : 2,
- effectiveRangeKm: formData.value.effectiveRangeKm,
+ effectiveRangeKmIsNotNull: normalizedRange != null ? 1 : 2,
+ effectiveRangeKm: normalizedRange,
longitude,
latitude
}
+ formData.value.effectiveRangeKm = normalizedRange
delete payload.deviceId
await detectionRangeSubmitApi(payload)
@@ -280,6 +290,7 @@
current: 1,
size: 200,
effectiveRangeKmIsNotNull: 2,
+ isTrack: 1
})
const records = res?.data?.data?.records ?? []
deviceOptions.value = records.map(item => ({
@@ -432,9 +443,18 @@
watch(
() => formData.value.effectiveRangeKm,
value => {
+ const normalized = normalizeRange(value)
+ if (value != null && normalized == null) {
+ formData.value.effectiveRangeKm = null
+ return
+ }
+ if (value != null && normalized !== value) {
+ formData.value.effectiveRangeKm = normalized
+ return
+ }
const parsed = parseDeployLocation(formData.value.deployLocation)
if (!parsed) return
- updateRangeCircle(parsed.longitude, parsed.latitude, value)
+ updateRangeCircle(parsed.longitude, parsed.latitude, normalized)
}
)
</script>
diff --git a/applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue b/applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue
index c2bda53..34735d3 100644
--- a/applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue
+++ b/applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue
@@ -204,8 +204,10 @@
function formatRange(row) {
if (!row) return '-'
- const value = row.effectiveRangeKm ?? row.range
- return value === null || value === undefined || value === '' ? '-' : value
+
+ let value = row.effectiveRangeKm ?? row.range
+
+ return value === null || value === undefined || value === '' ? '-' : Number(value).toFixed(0)
}
function formatDeviceCode(row) {
diff --git a/applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue b/applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue
index ea7f4af..d56f1f3 100644
--- a/applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue
+++ b/applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue
@@ -151,7 +151,10 @@
async function getList() {
loading.value = true
try {
- const res = await fwDevicePageApi(searchParams.value)
+ const res = await fwDevicePageApi({
+ ...searchParams.value,
+ deviceStatusList: '0,1,2'
+ })
list.value = res?.data?.data?.records ?? []
total.value = res?.data?.data?.total ?? 0
} finally {
@@ -176,6 +179,7 @@
function getDictList() {
getDictionaryByCode('deviceType,deviceStatus,deviceAtt').then(res => {
dictObj.value = res.data.data
+ dictObj.value.deviceStatus = dictObj.value.deviceStatus.filter(item => item.dictKey != 3)
})
}
diff --git a/applications/task-work-order/src/views/orderView/flyingHand/FlyingHandDialog.vue b/applications/task-work-order/src/views/orderView/flyingHand/FlyingHandDialog.vue
new file mode 100644
index 0000000..61aa55f
--- /dev/null
+++ b/applications/task-work-order/src/views/orderView/flyingHand/FlyingHandDialog.vue
@@ -0,0 +1,278 @@
+<!-- 飞手信息弹窗 -->
+<template>
+ <el-dialog
+ v-model="dialogVisible"
+ :title="dialogTitle"
+ class="gd-dialog"
+ destroy-on-close
+ :close-on-click-modal="false"
+ >
+ <el-form
+ ref="formRef"
+ :model="formData"
+ :rules="rules"
+ class="gd-form"
+ label-width="160px"
+ >
+ <el-row>
+ <el-col :span="12">
+ <el-form-item label="飞手姓名" prop="flyerName">
+ <el-select
+ class="gd-select"
+ popper-class="gd-select-popper"
+ v-model="formData.flyerName"
+ placeholder="请选择飞手姓名"
+ :disabled="dialogType === 'view'"
+ >
+ <el-option
+ v-for="item in flyingHandList"
+ :key="item.id"
+ :label="item.name"
+ :value="item.id"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="飞手电话" prop="flyerPhone">
+ <el-input
+ class="gd-input"
+ v-model="formData.flyerPhone"
+ placeholder="请输入飞手电话"
+ :disabled="dialogType === 'view'"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="飞行时长(小时)" prop="flightHours">
+ <el-input
+ class="gd-input"
+ v-model.number="formData.flightHours"
+ placeholder="请输入飞行时长"
+ type="number"
+ :disabled="dialogType === 'view'"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="技术特长" prop="technicalStrength">
+ <el-select
+ class="gd-select"
+ popper-class="gd-select-popper"
+ v-model="formData.technicalStrength"
+ placeholder="请选择技术特长"
+ :disabled="dialogType === 'view'"
+ >
+ <el-option
+ v-for="item in dictObj.technicalStrength"
+ :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="skilledUavType">
+ <el-select
+ class="gd-select"
+ popper-class="gd-select-popper"
+ v-model="formData.skilledUavType"
+ placeholder="请选择擅长机型"
+ clearable
+ @change="handleSearch"
+ >
+ <el-option
+ v-for="item in dictObj.skilledUavType"
+ :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="skilledTaskType">
+ <el-select
+ class="gd-select"
+ popper-class="gd-select-popper"
+ v-model="formData.skilledTaskType"
+ placeholder="请选择擅长任务类型"
+ :disabled="dialogType === 'view'"
+ >
+ <el-option
+ v-for="item in dictObj.skilledTaskType"
+ :key="item.dictKey"
+ :label="item.dictValue"
+ :value="item.dictKey"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="24">
+ <el-form-item label="项目经验" prop="projectExperience">
+ <el-input
+ class="gd-input"
+ v-model="formData.projectExperience"
+ placeholder="请输入项目经验"
+ type="textarea"
+ rows="3"
+ :disabled="dialogType === 'view'"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ <template #footer>
+ <el-button @click="handleClose">取消</el-button>
+ <el-button
+ v-if="dialogType !== 'view'"
+ type="primary"
+ @click="handleSubmit"
+ >
+ 确定
+ </el-button>
+ </template>
+ </el-dialog>
+</template>
+<script setup>
+import { ref, reactive, computed, watch, inject } from 'vue'
+import { submitFlyingHand } from './flyingHandApi'
+import { ElMessage } from 'element-plus'
+
+const props = defineProps({
+ modelValue: {
+ type: Boolean,
+ default: false
+ },
+ dialogType: {
+ type: String,
+ default: 'add', // add, edit, view
+ validator: (value) => ['add', 'edit', 'view'].includes(value)
+ },
+ flyingHandData: {
+ type: Object,
+ default: () => ({})
+ }
+})
+
+const dictObj = inject('dictObj') // 擅长任务类型
+
+const flyingHandList = inject('flyingHandList') // 飞手列表
+
+const emit = defineEmits(['update:modelValue', 'confirm'])
+
+const dialogVisible = computed({
+ get: () => props.modelValue,
+ set: (value) => emit('update:modelValue', value)
+})
+
+const dialogTitle = computed(() => {
+ switch (props.dialogType) {
+ case 'add':
+ return '添加飞手信息'
+ case 'edit':
+ return '编辑飞手信息'
+ case 'view':
+ return '查看飞手信息'
+ default:
+ return '飞手信息'
+ }
+})
+
+const formRef = ref(null)
+const formData = reactive({
+ flyerName: '',
+ flyerPhone: '',
+ flightHours: 0,
+ projectExperience: '',
+ technicalStrength: '',
+ skilledUavType: ''
+})
+
+const rules = {
+ flyerName: [
+ { required: true, message: '请输入飞手姓名', trigger: 'blur' }
+ ],
+ flyerPhone: [
+ { required: false, message: '请输入飞手电话', trigger: 'blur' },
+ { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+ ],
+ flightHours: [
+ { required: true, message: '请输入飞行时长', trigger: 'blur' },
+ { type: 'number', min: 0, message: '飞行时长必须大于等于0', trigger: 'blur' }
+ ],
+ technicalSkills: [
+ { required: true, message: '请选择技术特长', trigger: 'change' }
+ ],
+ skilledTaskType: [
+ { required: true, message: '请选择擅长任务类型', trigger: 'change' }
+ ],
+ projectExperience: [
+ { required: true, message: '请输入项目经验', trigger: 'blur' }
+ ]
+}
+
+// 监听飞手数据变化,更新表单
+watch(
+ () => props.flyingHandData,
+ (newData) => {
+ if (newData) {
+ Object.assign(formData, newData)
+ }
+ },
+ { deep: true, immediate: true }
+)
+
+// 处理关闭
+function handleClose() {
+ dialogVisible.value = false
+ resetForm()
+}
+
+// 重置表单
+function resetForm() {
+ formRef.value?.resetFields()
+ // 重置为默认值
+ Object.assign(formData, {
+ name: '',
+ phone: '',
+ flightHours: 0,
+ projectExperience: '',
+ technicalSkills: '',
+ technicalExpertise: '',
+ goodAtAircraftType: '',
+ goodAtTaskType: ''
+ })
+}
+
+// 处理提交
+async function handleSubmit() {
+ const isValid = await formRef.value?.validate()
+ if (!isValid) {
+ return
+ }
+
+ try {
+ if (props.dialogType === 'add') {
+ await submitFlyingHand(formData)
+ ElMessage.success('添加成功')
+ } else if (props.dialogType === 'edit') {
+ await submitFlyingHand(formData)
+ ElMessage.success('编辑成功')
+ }
+ emit('confirm')
+ dialogVisible.value = false
+ resetForm()
+ } catch (error) {
+ ElMessage.error('操作失败')
+ }
+}
+</script>
+<style scoped lang="scss">
+.gd-form {
+ .el-form-item {
+ margin-bottom: 20px;
+ }
+}
+</style>
\ No newline at end of file
diff --git a/applications/task-work-order/src/views/orderView/flyingHand/flyingHandApi.js b/applications/task-work-order/src/views/orderView/flyingHand/flyingHandApi.js
new file mode 100644
index 0000000..3d38189
--- /dev/null
+++ b/applications/task-work-order/src/views/orderView/flyingHand/flyingHandApi.js
@@ -0,0 +1,37 @@
+import request from '@/axios'
+
+// 飞手分页查询
+export function getFlyingHandPage(params) {
+ return request({
+ url: '/drone-gd/flyer/gdFlyer/page',
+ method: 'get',
+ params
+ })
+}
+
+// 飞手提交
+export function submitFlyingHand(data) {
+ return request({
+ url: '/drone-gd/flyer/gdFlyer/submit',
+ method: 'post',
+ data
+ })
+}
+
+// 飞手详情查询
+export function getFlyingHandDetail(params) {
+ return request({
+ url: '/drone-gd/flyer/gdFlyer/detail',
+ method: 'get',
+ params
+ })
+}
+
+// 飞手删除
+export function removeFlyingHand(data) {
+ return request({
+ url: '/drone-gd/flyer/gdFlyer/remove',
+ method: 'post',
+ data
+ })
+}
diff --git a/applications/task-work-order/src/views/orderView/flyingHand/index.vue b/applications/task-work-order/src/views/orderView/flyingHand/index.vue
index 60a2c0d..ac5e9d1 100644
--- a/applications/task-work-order/src/views/orderView/flyingHand/index.vue
+++ b/applications/task-work-order/src/views/orderView/flyingHand/index.vue
@@ -1,7 +1,223 @@
+<!-- 飞手信息管理 -->
<template>
- <basic-container>飞手</basic-container>
+ <basic-container>
+ <el-form ref="queryParamsRef" :model="searchParams" class="gd-search-form">
+ <el-form-item label="飞手姓名" prop="flyerName">
+ <el-input
+ class="gd-input gray"
+ v-model="searchParams.flyerName"
+ placeholder="请输入飞手姓名"
+ clearable
+ @clear="handleSearch"
+ />
+ </el-form-item>
+
+ <el-form-item label="擅长机型" prop="skilledUavType">
+ <el-select
+ class="gd-select gray"
+ popper-class="gd-select-popper"
+ v-model="searchParams.skilledUavType"
+ placeholder="请选择"
+ clearable
+ @change="handleSearch"
+ >
+ <el-option
+ v-for="item in dictObj.skilledUavType"
+ :key="item.dictKey"
+ :label="item.dictValue"
+ :value="item.dictKey"
+ />
+ </el-select>
+ </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="addFlyingHand">新增</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">
+ <el-table-column type="index" width="64" label="序号" />
+ <el-table-column prop="flyerName" show-overflow-tooltip label="飞手姓名" />
+ <el-table-column prop="flyerPhone" show-overflow-tooltip label="飞手电话" />
+ <el-table-column prop="flightHours" show-overflow-tooltip label="飞行时长(小时)" />
+ <el-table-column prop="projectExperience" show-overflow-tooltip label="项目经验" />
+ <el-table-column prop="technicalStrength" show-overflow-tooltip label="技术特长">
+ <template v-slot="{ row }">
+ {{ getDictLabel(row.technicalStrength, dictObj.technicalStrength) }}
+ </template>
+ </el-table-column>
+ <el-table-column prop="skilledUavType" show-overflow-tooltip label="擅长机型" />
+ <el-table-column prop="skilledTaskType" show-overflow-tooltip label="擅长任务类型">
+ <template v-slot="{ row }">
+ {{ getDictLabel(row.skilledTaskType, dictObj.skilledTaskType) }}
+ </template>
+ </el-table-column>
+ <el-table-column label="操作" class-name="operation-btns" width="180">
+ <template v-slot="{ row }">
+ <el-link @click="viewFlyingHand(row)">查看</el-link>
+ <el-link type="primary" @click="editFlyingHand(row)">编辑</el-link>
+ <el-link type="danger" @click="deleteFlyingHand(row.id)">删除</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>
+
+ <!-- 飞手信息弹窗 -->
+ <FlyingHandDialog
+ ref="flyingHandDialogRef"
+ v-model="dialogVisible"
+ :dialog-type="dialogType"
+ :flying-hand-data="flyingHandData"
+ @confirm="handleConfirm"
+ />
+ </basic-container>
</template>
+<script setup>
+import { Search, RefreshRight, Plus } from '@element-plus/icons-vue'
+import { onMounted, ref, nextTick } from 'vue'
+import { getDictionaryByCode } from '@/api/system/dictbiz'
+import { getDictLabel } from '@ztzf/utils'
+import { getFlyingHandPage, removeFlyingHand } from './flyingHandApi'
+import { deviceFlyerApi } from '@/api/zkxt'
+import FlyingHandDialog from './FlyingHandDialog.vue'
+import { prod } from 'mathjs'
-<script setup></script>
+// 初始化查询参数
+const initSearchParams = () => ({
+ flyerName: '', // 飞手姓名
+ skilledUavType: '', // 擅长任务类型
+ current: 1, // 当前页
+ size: 10, // 每页大小
+})
+const searchParams = ref(initSearchParams()) // 查询参数
+const total = ref(0) // 总条数
+const loading = ref(true) // 列表加载中
+const list = ref([]) // 列表数据
+const queryParamsRef = ref(null) // 查询表单实例
+const flyingHandDialogRef = ref(null)
+const dialogVisible = ref(false)
+const dialogType = ref('add') // add, edit, view
+const flyingHandData = ref({})
-<style scoped lang="scss"></style>
+const dictObj = ref({
+ skilledTaskType: [], // 擅长任务类型
+ skilledUavType: [], // 擅长机型
+ technicalStrength: [] // 技术特长
+})
+
+provide('dictObj', dictObj)
+
+// 获取列表
+async function getList() {
+ loading.value = true
+ try {
+ const res = await getFlyingHandPage(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 addFlyingHand() {
+ dialogType.value = 'add'
+ flyingHandData.value = {}
+ dialogVisible.value = true
+}
+
+// 查看飞手
+function viewFlyingHand(row) {
+ dialogType.value = 'view'
+ flyingHandData.value = { ...row }
+ dialogVisible.value = true
+}
+
+// 编辑飞手
+function editFlyingHand(row) {
+ dialogType.value = 'edit'
+ flyingHandData.value = { ...row }
+ dialogVisible.value = true
+}
+
+// 删除飞手
+function deleteFlyingHand(id) {
+ ElMessageBox.confirm('确定要删除该飞手信息吗?', '提示', {
+ confirmButtonText: '确定',
+ cancelButtonText: '取消',
+ type: 'warning',
+ }).then(async () => {
+ try {
+ await removeFlyingHand(id)
+ ElMessage.success('删除成功')
+ getList()
+ } catch (error) {
+ ElMessage.error('删除失败')
+ }
+ }).catch(() => {
+ // 取消删除
+ })
+}
+
+// 处理弹窗确认
+function handleConfirm() {
+ dialogVisible.value = false
+ getList()
+}
+
+// 获取字典
+function getDictList() {
+ getDictionaryByCode('skilledTaskType,skilledUavType,technicalStrength').then(res => {
+ dictObj.value = res.data.data
+ })
+}
+
+// 获取飞手列表
+const flyingHandList = ref([])
+function getFlyingHandList() {
+ deviceFlyerApi({ current: 1, size: 10 }).then(res => {
+ flyingHandList.value = res?.data?.data ?? []
+ })
+}
+
+provide('flyingHandList', flyingHandList)
+
+onMounted(() => {
+ getList()
+ getDictList()
+ getFlyingHandList()
+})
+</script>
+<style scoped lang="scss"></style>
\ No newline at end of file
diff --git a/applications/task-work-order/src/views/orderView/organizationalMessage/agenciesManagement/FormDiaLog.vue b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/FormDiaLog.vue
similarity index 96%
rename from applications/task-work-order/src/views/orderView/organizationalMessage/agenciesManagement/FormDiaLog.vue
rename to applications/task-work-order/src/views/orderView/organizational/agenciesManagement/FormDiaLog.vue
index 888f1df..d29556b 100644
--- a/applications/task-work-order/src/views/orderView/organizationalMessage/agenciesManagement/FormDiaLog.vue
+++ b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/FormDiaLog.vue
@@ -158,6 +158,10 @@
if (data.status !== undefined) {
data.status = String(data.status)
}
+ // 将 areaCode 转换为字符串类型,与 regionOptions 中的 value 类型保持一致
+ if (data.areaCode !== undefined) {
+ data.areaCode = String(data.areaCode)
+ }
formData.value = data
}
diff --git a/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js
new file mode 100644
index 0000000..2f4dd8c
--- /dev/null
+++ b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js
@@ -0,0 +1,66 @@
+import request from '@/axios'
+
+// 列表
+export const agenciesPageApi = params => {
+ return request({
+ url: `/blade-system/dept/page`,
+ method: 'get',
+ params: { ...params },
+ })
+}
+
+// 新增或编辑
+export const agenciesSubmitApi = data => {
+ return request({
+ url: `/blade-system/dept/submit`,
+ method: 'post',
+ data,
+ })
+}
+
+// 删除
+export const agenciesRemoveApi = params => {
+ return request({
+ url: `/blade-system/dept/remove`,
+ method: 'post',
+ params,
+ })
+}
+
+// 详情
+export const agenciesDetailApi = params => {
+ return request({
+ url: `/blade-system/dept/detail`,
+ method: 'get',
+ params,
+ })
+}
+
+// 导入机构
+export const agenciesImportApi = (data,params) => {
+ return request({
+ url: `/blade-system/dept/import-region`,
+ method: 'post',
+ data,
+ params
+ })
+}
+
+// 导出机构
+export const agenciesExportApi = params => {
+ return request({
+ url: `/blade-system/dept/export-region`,
+ method: 'get',
+ params,
+ responseType: 'blob',
+ })
+}
+
+// 懒加载列表
+export const regionLazyTreeApi = params => {
+ return request({
+ url: `/blade-system/region/lazy-tree`,
+ method: 'get',
+ params,
+ })
+}
diff --git a/applications/task-work-order/src/views/orderView/organizationalMessage/agenciesManagement/index.vue b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue
similarity index 100%
rename from applications/task-work-order/src/views/orderView/organizationalMessage/agenciesManagement/index.vue
rename to applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue
diff --git a/applications/task-work-order/src/views/orderView/organizationalMessage/zoningManagement/index.vue b/applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue
similarity index 100%
rename from applications/task-work-order/src/views/orderView/organizationalMessage/zoningManagement/index.vue
rename to applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue
--
Gitblit v1.9.3