From 0ef47a49fa6dce546be2d0f23b4949734e6a0cf9 Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Thu, 29 Jan 2026 18:31:55 +0800
Subject: [PATCH] Merge branch 'master' of http://139.196.74.78:10010/r/jagzwxm/ja_web

---
 applications/task-work-order/src/views/orderView/flyingHand/index.vue                                 |    6 
 applications/task-work-order/src/views/orderView/orderManage/inspectionReport/index.vue               |   85 +
 applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js     |    9 
 uniapps/work-wx/src/subPackages/flightApplication/index.vue                                           |    6 
 applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue            |   61 +
 uniapps/work-wx/src/store/modules/app/index.js                                                        |    7 
 applications/drone-command/src/views/basicManage/maintainRecord/FormDiaLog.vue                        |   63 +
 uniapps/work-wx/src/api/index.js                                                                      |    8 
 applications/drone-command/src/views/basicManage/deviceStock/fwDeviceScrapApi.js                      |   11 
 applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue         |   31 
 applications/task-work-order/src/views/orderView/flyingHand/FlyingHandDialog.vue                      |    4 
 applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/FormDiaLog.vue         |   82 +
 applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue          |   61 +
 applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/ApplyViewDialog.vue        |  141 ++-
 applications/task-work-order/src/views/orderView/orderManage/inspectionReport/inspectionRequestApi.js |    9 
 applications/drone-command/src/views/basicManage/deviceStock/DeviceScrapDiaLog.vue                    |   25 
 applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue               |    5 
 uniapps/work-wx/src/subPackages/flightApplication/details.vue                                         |   91 +
 applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js         |    9 
 applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue         |    1 
 applications/task-work-order/src/views/orderView/equipmentResource/index.vue                          |  237 +++++
 applications/drone-command/src/views/basicManage/deviceStock/index.vue                                |   30 
 applications/task-work-order/src/views/orderView/equipmentResource/FormDiaLog.vue                     |  631 +++++++++++++++
 applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue                           |  271 +++--
 applications/drone-command/src/views/audioCall.vue                                                    |  600 +++++++-------
 applications/drone-command/src/assets/images/areaManage/positionIcon.png                              |    0 
 uniapps/work-wx/src/pages/page/index.vue                                                              |   16 
 27 files changed, 1,919 insertions(+), 581 deletions(-)

diff --git a/applications/drone-command/src/assets/images/areaManage/positionIcon.png b/applications/drone-command/src/assets/images/areaManage/positionIcon.png
new file mode 100644
index 0000000..a569e90
--- /dev/null
+++ b/applications/drone-command/src/assets/images/areaManage/positionIcon.png
Binary files differ
diff --git a/applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue b/applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
index 7e0f3b4..14a03ee 100644
--- a/applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
+++ b/applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
@@ -1,6 +1,10 @@
 <template>
-	<el-dialog class="command-page-view-dialog" v-model="visible" :title="titleEnum[dialogMode]"
-		:close-on-click-modal="false">
+	<el-dialog
+		class="command-page-view-dialog"
+		v-model="visible"
+		:title="titleEnum[dialogMode]"
+		:close-on-click-modal="false"
+	>
 		<div class="detail-container" v-if="readonly">
 			<el-row>
 				<el-col :span="12">
@@ -12,75 +16,98 @@
 					<div class="val">{{ formData.contactPerson }}</div>
 				</el-col>
 				<el-col :span="12">
-					<div class="label">位置</div>
-					<div class="val">{{ formData.address }}</div>
-				</el-col>
-				<el-col :span="12">
 					<div class="label">联系方式</div>
 					<div class="val">{{ formData.contactPhone }}</div>
 				</el-col>
+				<el-col :span="12">
+					<div class="label">位置</div>
+					<div class="val">{{ formData.address }}</div>
+				</el-col>
 			</el-row>
 		</div>
-		<el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly"
-			label-width="100px">
+		<el-form
+			class="dialog-form"
+			v-else
+			ref="formRef"
+			:model="formData"
+			:rules="rules"
+			:disabled="readonly"
+			label-width="100px"
+		>
 			<el-row>
 				<el-col :span="24">
 					<el-form-item label="派出所名称" prop="stationName">
-						<el-input class="command-input" v-model="formData.stationName" maxlength="50"
-							placeholder="请输入" clearable />
-					</el-form-item>
-				</el-col>
-				<el-col :span="24">
-					<el-form-item label="位置" prop="longitude">
-						<el-button  class="address-btn" @click="selectLocation">
-							{{ formData.longitude }}, {{ formData.latitude }}, {{ formData.address }}
-						</el-button>
+						<el-input
+							class="command-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="command-input" v-model="formData.contactPerson" maxlength="50"
-							placeholder="请输入" clearable />
+						<el-input
+							class="command-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="command-input" v-model="formData.contactPhone" maxlength="50"
-							placeholder="请输入" clearable />
+						<el-input
+							class="command-input"
+							v-model="formData.contactPhone"
+							maxlength="50"
+							placeholder="请输入"
+							clearable
+						/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="24">
+					<el-form-item label="位置" prop="longitude">
+						<div class="location-container">
+							<div class="location-info" v-if="formData.longitude && formData.latitude">
+								经度: {{ formData.longitude }}, 纬度: {{ formData.latitude }}
+								<span v-if="formData.address">, {{ formData.address }}</span>
+							</div>
+							<div class="location-info" v-else>请在地图上点击选择位置</div>
+							<div class="inline-map-container">
+								<CommonCesiumMap
+									ref="inlineMapRef"
+									class="inline-map command-cesium"
+									:active="visible && !readonly"
+									:flat-mode="false"
+									:terrain="true"
+									:layer-mode="4"
+									:boundary="false"
+								/>
+							</div>
+						</div>
 					</el-form-item>
 				</el-col>
 			</el-row>
 		</el-form>
 		<template #footer>
-			<el-button v-if="dialogMode != 'view'" color="#2B2B4C" @click="handleCancel">{{ readonly ? '关闭' : '取消' }}</el-button>
-			<el-button color="#284FE3" v-if="!readonly" type="primary" :loading="submitting" :disabled="submitting"
-				@click="handleSubmit">
+			<el-button v-if="dialogMode != 'view'" color="#2B2B4C" @click="handleCancel">
+				{{ readonly ? '关闭' : '取消' }}
+			</el-button>
+			<el-button
+				color="#284FE3"
+				v-if="!readonly"
+				type="primary"
+				:loading="submitting"
+				:disabled="submitting"
+				@click="handleSubmit"
+			>
 				确定
 			</el-button>
 		</template>
 	</el-dialog>
-
-	<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">
-					<CommonCesiumMap
-						ref="mapRef"
-						class="leftMap command-cesium"
-						:active="visible1"
-						:flat-mode="false"
-						:terrain="true"
-						:layer-mode="4"
-						:boundary="false"
-					/>
-				</div>
-			</div>
-
-			<template #footer>
-				<el-button color="#2B2B4C" @click="handleMapCancel">取消</el-button>
-				<el-button color="#284FE3" type="primary" @click="handleMapConfirm">确定</el-button>
-			</template>
-		</el-dialog>
 </template>
 
 <script setup>
@@ -93,27 +120,26 @@
 import axios from 'axios'
 import { saveOperationLog } from '@ztzf/apis'
 import { useRoute } from 'vue-router'
+import positionIcon from '@/assets/images/areaManage/positionIcon.png'
 
 const initForm = () => ({
 	address: '', // 位置
 	contactPerson: '', // 联系人
 	contactPhone: '', // 联系方式
 	stationName: '', // 派出所名称
-	longitude: 0,
-	latitude: 0,
+	longitude: null,
+	latitude: null,
 })
 
 const emit = defineEmits(['success'])
 const formRef = ref(null) // 表单实例
 const formData = ref(initForm()) // 表单数据
 const visible = defineModel() // 弹框显隐
-const visible1 = ref(false) // 弹框显隐
 const dialogMode = ref('add') // 弹框模式
 const submitting = ref(false) // 提交中
 const readonly = computed(() => dialogMode.value === 'view')
 const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
-const tempLocation = ref({ longitude: null, latitude: null })
-const mapRef = ref(null)
+const inlineMapRef = ref(null) // 内嵌地图引用
 const route = useRoute()
 
 const rules = {
@@ -121,15 +147,16 @@
 	stationName: fieldRules(true, 50),
 	contactPerson: fieldRules(true, 50),
 	contactPhone: fieldRules(true, 50),
+	longitude: fieldRules(true),
 }
 
 // 关闭弹框
-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
@@ -149,13 +176,31 @@
 }
 
 // 加载详情
-async function loadDetail () {
+async function loadDetail() {
 	if (!formData.value.id) return
 	const res = await fwPoliceStationDetailApi({ id: formData.value.id })
 	formData.value = res?.data?.data ?? {}
 }
 
-function LeftClickEvent (click) {
+let viewer
+let redPointEntity
+let leftClickBound = false
+const labelParams = {
+	font: '18px',
+	fillColor: Cesium.Color.WHITE, // 文字颜色:白色
+	backgroundColor: Cesium.Color.BLACK, //背景颜色
+	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,
+	heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+}
+
+function LeftClickEvent(click) {
 	const pos = click.position // Cartesian2 屏幕坐标
 	// 屏幕坐标 -> 椭球面坐标(不考虑地形/模型)
 	const cartesian3 = viewer.camera.pickEllipsoid(pos, viewer.scene.globe.ellipsoid)
@@ -166,29 +211,24 @@
 	const carto = Cesium.Cartographic.fromCartesian(cartesian3)
 	const longitude = Cesium.Math.toDegrees(carto.longitude)
 	const latitude = Cesium.Math.toDegrees(carto.latitude)
-	tempLocation.value = { longitude, latitude }
+	// 直接更新 formData
+	formData.value.longitude = _.round(longitude, 6)
+	formData.value.latitude = _.round(latitude, 6)
+	getLocationName(longitude, latitude)
+
 	// 添加红点(全局仅保留一个)并渲染经纬度标签
 	if (!redPointEntity) {
 		redPointEntity = viewer.entities.add({
 			position: Cesium.Cartesian3.fromDegrees(longitude, latitude),
-			point: {
-				color: Cesium.Color.RED,
-				pixelSize: 10,
+			billboard: {
+				image: positionIcon,
+				width: 20,
+				height: 20,
+				verticalOrigin: Cesium.VerticalOrigin.CENTER,
 			},
 			label: {
 				text: `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`,
-				font: '14px',
-				fillColor: Cesium.Color.WHITE, // 文字颜色:白色
-				backgroundColor: Cesium.Color.BLACK.withAlpha(0.6), //背景颜色
-				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,
-				eyeOffset: new Cesium.Cartesian3(0, 0, -20), // 负值更靠近相机(显示在前)
+				...labelParams
 			},
 		})
 	} else {
@@ -197,36 +237,20 @@
 	}
 }
 
-function positionClosed () {
-	redPointEntity = null
-}
-
-let viewer
-let redPointEntity
-let leftClickBound = false
-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: positionIcon,
+				width: 20,
+				height: 20,
+				verticalOrigin: Cesium.VerticalOrigin.CENTER,
 			},
 			label: {
 				text: `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`,
-				font: '14px',
-				fillColor: Cesium.Color.WHITE,
-				backgroundColor: Cesium.Color.BLACK.withAlpha(0.6),
-				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,
-				eyeOffset: new Cesium.Cartesian3(0, 0, -20),
+				...labelParams
 			},
 		})
 	} else {
@@ -235,34 +259,27 @@
 	}
 }
 
-function formatCoord (value) {
+function formatCoord(value) {
 	if (value == null || value === '') return '-'
 	return _.round(Number(value), 6)
 }
 
-async function selectLocation () {
-	visible1.value = true
+// 初始化内嵌地图
+async function initInlineMap() {
 	await nextTick()
-	const map = mapRef.value?.getMap()
+	const map = inlineMapRef.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)
 	}
 }
 
-function handleMapCancel () {
-	tempLocation.value = { longitude: null, latitude: null }
-	visible1.value = false
-}
-
-async function getLocationName () {
+async function getLocationName(longitude, latitude) {
 	const tk = import.meta.env.VITE_APP_TDT_TOKEN
-	const { latitude, longitude } = tempLocation.value
 	const http = axios.create({ withCredentials: false })
 	const res = await http({
 		url: `https://api.tianditu.gov.cn/geocoder?postStr={'lon':${longitude},'lat':${latitude},'ver':1}&tk=${tk}`,
@@ -271,24 +288,44 @@
 	formData.value.address = res.data.result.formatted_address
 }
 
-function handleMapConfirm () {
-	if (tempLocation.value.longitude != null && tempLocation.value.latitude != null) {
-		formData.value.longitude = _.round(tempLocation.value.longitude, 6)
-		formData.value.latitude = _.round(tempLocation.value.latitude, 6)
-		getLocationName()
-	}
-	visible1.value = false
-}
-
 // 打开弹框
-async function open ({ mode, row } = {}) {
+async function open({ mode, row } = {}) {
 	dialogMode.value = mode || 'add'
 	formData.value = dialogMode.value === 'add' ? initForm() : row
-	dialogMode.value !== 'add' && (await loadDetail())
+	redPointEntity = null
+	leftClickBound = false
+	if (dialogMode.value !== 'add') {
+		await loadDetail()
+	}
+	// 延迟初始化地图,确保 DOM 渲染完成
+	setTimeout(() => {
+		initInlineMap()
+	}, 500)
 }
 
 defineExpose({ open })
 </script>
 <style scoped lang="scss">
+.location-container {
+	width: 100%;
+}
 
+.location-info {
+	margin-bottom: 8px;
+	color: #fff;
+	font-size: 14px;
+}
+
+.inline-map-container {
+	width: 100%;
+	height: 300px;
+	border-radius: 4px;
+	overflow: hidden;
+	border: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.inline-map {
+	width: 100%;
+	height: 100%;
+}
 </style>
diff --git a/applications/drone-command/src/views/audioCall.vue b/applications/drone-command/src/views/audioCall.vue
index 55de074..90910ac 100644
--- a/applications/drone-command/src/views/audioCall.vue
+++ b/applications/drone-command/src/views/audioCall.vue
@@ -1,439 +1,439 @@
 <template>
 	<div style="padding:16px">
-	  <h2>1v1 语音通话 WebRTC Demo(对齐后端 ChatMessage,JS)</h2>
-  
-	  <div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">
-		<span>我的 userId:</span>
-		<input v-model="uid" style="width:120px" />
-  
-		<span>对方 userId:</span>
-		<input v-model="peerUid" style="width:120px" />
-  
-		<button @click="connectWS" :disabled="connected">连接WS</button>
-  
-		<button @click="requestCall" :disabled="!connected || state!=='idle'">呼叫</button>
-  
-		<button v-if="state==='ringing'" @click="acceptCall">接听</button>
-		<button v-if="state==='ringing'" @click="rejectCall">拒绝</button>
-  
-		<button @click="hangup" :disabled="!connected || (state!=='calling' && state!=='in-call')">挂断</button>
-  
-		<strong>状态:{{ state }}</strong>
-		<strong v-if="state==='in-call'">通话时长:{{ durationText }}</strong>
-	  </div>
-  
-	  <div style="margin-top:8px;color:#666">
-		<small>提示:若 getUserMedia 不可用,请用 https 或 localhost 访问页面。</small>
-	  </div>
-  
-	  <pre style="margin-top:12px;max-height:280px;overflow:auto;">{{ logText }}</pre>
-  
-	  <audio ref="remoteAudio" autoplay></audio>
+		<h2>1v1 语音通话 WebRTC Demo(对齐后端 ChatMessage,JS)</h2>
+
+		<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">
+			<span>我的 userId:</span>
+			<input v-model="uid" style="width:120px" />
+
+			<span>对方 userId:</span>
+			<input v-model="peerUid" style="width:120px" />
+
+			<button @click="connectWS" :disabled="connected">连接WS</button>
+
+			<button @click="requestCall" :disabled="!connected || state!=='idle'">呼叫</button>
+
+			<button v-if="state==='ringing'" @click="acceptCall">接听</button>
+			<button v-if="state==='ringing'" @click="rejectCall">拒绝</button>
+
+			<button @click="hangup" :disabled="!connected || (state!=='calling' && state!=='in-call')">挂断</button>
+
+			<strong>状态:{{ state }}</strong>
+			<strong v-if="state==='in-call'">通话时长:{{ durationText }}</strong>
+		</div>
+
+		<div style="margin-top:8px;color:#666">
+			<small>提示:若 getUserMedia 不可用,请用 https 或 localhost 访问页面。</small>
+		</div>
+
+		<pre style="margin-top:12px;max-height:280px;overflow:auto;">{{ logText }}</pre>
+
+		<audio ref="remoteAudio" autoplay></audio>
 	</div>
-  </template>
-  
-  <script setup>
-  import { ref, computed, onBeforeUnmount } from 'vue'
-  
-  /** ✅ 你的 WS 地址前缀(后面拼 userId) */
-  const WS_BASE = 'wss://域名/ws/chat?userId='
-  
-  const uid = ref('2')
-  const peerUid = ref('3')
-  
-  const connected = ref(false)
-  const state = ref('idle') // idle | calling | ringing | in-call
-  const logText = ref('')
-  const remoteAudio = ref(null)
-  
-  let ws = null
-  let pc = null
-  let localStream = null
-  const pendingCandidates = []
-  
-  // 来电暂存
-  let incomingFrom = null
-  let acceptedByMe = false // 我是否已经点了接听
-  let offeredByPeer = false // 是否已收到对方 offer(用于流程判断)
-  
-  // 计时
-  let timer = null
-  const seconds = ref(0)
-  const durationText = computed(() => {
+</template>
+
+<script setup>
+import { ref, computed, onBeforeUnmount } from 'vue'
+
+/** ✅ 你的 WS 地址前缀(后面拼 userId) */
+const WS_BASE = 'wss://域名/ws/chat?userId='
+
+const uid = ref('2')
+const peerUid = ref('3')
+
+const connected = ref(false)
+const state = ref('idle') // idle | calling | ringing | in-call
+const logText = ref('')
+const remoteAudio = ref(null)
+
+let ws = null
+let pc = null
+let localStream = null
+const pendingCandidates = []
+
+// 来电暂存
+let incomingFrom = null
+let acceptedByMe = false // 我是否已经点了接听
+let offeredByPeer = false // 是否已收到对方 offer(用于流程判断)
+
+// 计时
+let timer = null
+const seconds = ref(0)
+const durationText = computed(() => {
 	const m = String(Math.floor(seconds.value / 60)).padStart(2, '0')
 	const s = String(seconds.value % 60).padStart(2, '0')
 	return `${m}:${s}`
-  })
-  function startTimer() {
+})
+function startTimer() {
 	stopTimer()
 	seconds.value = 0
 	timer = setInterval(() => seconds.value++, 1000)
-  }
-  function stopTimer() {
+}
+function stopTimer() {
 	if (timer) clearInterval(timer)
 	timer = null
-  }
-  
-  // 心跳
-  let pingTimer = null
-  function startPing() {
+}
+
+// 心跳
+let pingTimer = null
+function startPing() {
 	stopPing()
 	// 每 25 秒发一次 ping(你后端有 PING/PONG)
 	pingTimer = setInterval(() => {
-	  if (connected.value) send('ping', 'system', null)
+		if (connected.value) send('ping', 'system', null)
 	}, 25000)
-  }
-  function stopPing() {
+}
+function stopPing() {
 	if (pingTimer) clearInterval(pingTimer)
 	pingTimer = null
-  }
-  
-  function log(...args) {
+}
+
+function log(...args) {
 	const line = args.map(v => (typeof v === 'string' ? v : JSON.stringify(v))).join(' ')
 	logText.value += `[${new Date().toLocaleTimeString()}] ${line}\n`
 	console.log(...args)
-  }
-  
-  /**
-   * ✅ 严格按后端 ChatMessage 字段发:
-   * { type, from, to, payload, timestamp }
-   * 注意:后端会用 session 的 userId 覆盖 from,但我们带上也没坏处
-   */
-  function send(type, to, payload) {
+}
+
+/**
+ * ✅ 严格按后端 ChatMessage 字段发:
+ * { type, from, to, payload, timestamp }
+ * 注意:后端会用 session 的 userId 覆盖 from,但我们带上也没坏处
+ */
+function send(type, to, payload) {
 	if (!type) return
 	if (!ws || ws.readyState !== WebSocket.OPEN) return
-  
+
 	const msg = {
-	  type,                 // "call/accept/busy/offer/answer/candidate/hangup/ping"
-	  from: String(uid.value),
-	  to: String(to),
-	  payload: payload ?? null,
-	  timestamp: Date.now(),
+		type,                 // "call/accept/busy/offer/answer/candidate/hangup/ping"
+		from: String(uid.value),
+		to: String(to),
+		payload: payload ?? null,
+		timestamp: Date.now(),
 	}
 	ws.send(JSON.stringify(msg))
-  }
-  
-  /* ---------------- WebRTC ---------------- */
-  
-  function createPC() {
+}
+
+/* ---------------- WebRTC ---------------- */
+
+function createPC() {
 	pc = new RTCPeerConnection({
-	  iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
+		iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
 	})
-  
+
 	pc.onicecandidate = (e) => {
-	  if (e.candidate) {
-		send('candidate', peerUid.value, { candidate: e.candidate.toJSON ? e.candidate.toJSON() : e.candidate })
-	  }
+		if (e.candidate) {
+			send('candidate', peerUid.value, { candidate: e.candidate.toJSON ? e.candidate.toJSON() : e.candidate })
+		}
 	}
-  
+
 	pc.ontrack = (e) => {
-	  log('✅ 收到远端音频流')
-	  const stream = e.streams[0]
-	  if (remoteAudio.value) {
-		remoteAudio.value.srcObject = stream
-		remoteAudio.value.play().catch(() => {})
-	  }
+		log('✅ 收到远端音频流')
+		const stream = e.streams[0]
+		if (remoteAudio.value) {
+			remoteAudio.value.srcObject = stream
+			remoteAudio.value.play().catch(() => {})
+		}
 	}
-  
+
 	pc.onconnectionstatechange = () => {
-	  log('pc.connectionState =', pc.connectionState)
+		log('pc.connectionState =', pc.connectionState)
 	}
-  }
-  
-  async function getLocalAudio() {
+}
+
+async function getLocalAudio() {
 	if (localStream) return localStream
-  
+
 	if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
-	  log('❌ 当前环境不支持 getUserMedia:请使用 https 或 localhost 打开页面')
-	  throw new Error('getUserMedia not available')
+		log('❌ 当前环境不支持 getUserMedia:请使用 https 或 localhost 打开页面')
+		throw new Error('getUserMedia not available')
 	}
-  
+
 	try {
-	  localStream = await navigator.mediaDevices.getUserMedia({ audio: true })
-	  log('✅ 已获取本地麦克风')
-	  return localStream
+		localStream = await navigator.mediaDevices.getUserMedia({ audio: true })
+		log('✅ 已获取本地麦克风')
+		return localStream
 	} catch (e) {
-	  log('❌ 获取麦克风失败:', String(e))
-	  throw e
+		log('❌ 获取麦克风失败:', String(e))
+		throw e
 	}
-  }
-  
-  /** ✅ 关键:只 addTrack 一次,避免 “sender already exists” */
-  async function ensureLocalTracksAdded() {
+}
+
+/** ✅ 关键:只 addTrack 一次,避免 “sender already exists” */
+async function ensureLocalTracksAdded() {
 	if (!pc) createPC()
 	const stream = await getLocalAudio()
-  
+
 	const existingTrackIds = new Set(
-	  pc.getSenders().map(s => (s.track ? s.track.id : null)).filter(Boolean)
+		pc.getSenders().map(s => (s.track ? s.track.id : null)).filter(Boolean)
 	)
-  
+
 	stream.getTracks().forEach(track => {
-	  if (!existingTrackIds.has(track.id)) {
-		pc.addTrack(track, stream)
-		log('addTrack ok:', track.id)
-	  } else {
-		// log('skip addTrack:', track.id)
-	  }
+		if (!existingTrackIds.has(track.id)) {
+			pc.addTrack(track, stream)
+			log('addTrack ok:', track.id)
+		} else {
+			// log('skip addTrack:', track.id)
+		}
 	})
-  
+
 	return stream
-  }
-  
-  async function flushCandidates() {
+}
+
+async function flushCandidates() {
 	if (!pc || !pc.remoteDescription) return
 	while (pendingCandidates.length) {
-	  const c = pendingCandidates.shift()
-	  try {
-		await pc.addIceCandidate(new RTCIceCandidate(c))
-	  } catch (e) {
-		log('addIceCandidate failed:', String(e))
-	  }
+		const c = pendingCandidates.shift()
+		try {
+			await pc.addIceCandidate(new RTCIceCandidate(c))
+		} catch (e) {
+			log('addIceCandidate failed:', String(e))
+		}
 	}
-  }
-  
-  /* ---------------- 信令处理 ---------------- */
-  
-  async function onSignal(msg) {
+}
+
+/* ---------------- 信令处理 ---------------- */
+
+async function onSignal(msg) {
 	if (!msg) return
 	const t = (msg.type || '').toString()
-  
+
 	// ✅ 忽略后端回显给自己的消息(你的后端会回显 sender)
 	if (msg.from && String(msg.from) === String(uid.value)) {
-	  return
+		return
 	}
-  
+
 	// system 消息:login/pong/busy 仅记录
 	if (msg.from === 'system') {
-	  log('system msg:', msg)
-	  return
-	}
-  
-	if (!t) return
-  
-	if (t === 'call') {
-	  // 收到来电:弹接听按钮
-	  incomingFrom = String(msg.from)
-	  peerUid.value = incomingFrom
-	  acceptedByMe = false
-	  offeredByPeer = false
-  
-	  state.value = 'ringing'
-	  log('📞 收到来电 call,来自', incomingFrom)
-	  return
-	}
-  
-	if (t === 'accept') {
-	  // 主叫收到 accept:开始 offer
-	  log('✅ 对方接听 accept,开始建立连接(offer)')
-	  await startOffer()
-	  return
-	}
-  
-	if (t === 'busy') {
-	  log('❌ 对方忙线/拒绝')
-	  cleanup(false)
-	  return
-	}
-  
-	if (t === 'offer') {
-	  // 被叫收到 offer:只有点击接听后才真正应答(你要求“先点接听”)
-	  offeredByPeer = true
-	  incomingFrom = String(msg.from)
-	  peerUid.value = incomingFrom
-  
-	  if (!acceptedByMe) {
-		state.value = 'ringing'
-		log('📩 收到 offer(但未接听),已等待用户点击接听')
-		// 暂存 offer 到一个变量
-		lastOfferSdp = msg.payload?.sdp
+		log('system msg:', msg)
 		return
-	  }
-  
-	  // 已接听:直接 answer
-	  await answerOffer(msg.payload?.sdp, incomingFrom)
-	  return
 	}
-  
+
+	if (!t) return
+
+	if (t === 'call') {
+		// 收到来电:弹接听按钮
+		incomingFrom = String(msg.from)
+		peerUid.value = incomingFrom
+		acceptedByMe = false
+		offeredByPeer = false
+
+		state.value = 'ringing'
+		log('📞 收到来电 call,来自', incomingFrom)
+		return
+	}
+
+	if (t === 'accept') {
+		// 主叫收到 accept:开始 offer
+		log('✅ 对方接听 accept,开始建立连接(offer)')
+		await startOffer()
+		return
+	}
+
+	if (t === 'busy') {
+		log('❌ 对方忙线/拒绝')
+		cleanup(false)
+		return
+	}
+
+	if (t === 'offer') {
+		// 被叫收到 offer:只有点击接听后才真正应答(你要求“先点接听”)
+		offeredByPeer = true
+		incomingFrom = String(msg.from)
+		peerUid.value = incomingFrom
+
+		if (!acceptedByMe) {
+			state.value = 'ringing'
+			log('📩 收到 offer(但未接听),已等待用户点击接听')
+			// 暂存 offer 到一个变量
+			lastOfferSdp = msg.payload?.sdp
+			return
+		}
+
+		// 已接听:直接 answer
+		await answerOffer(msg.payload?.sdp, incomingFrom)
+		return
+	}
+
 	if (t === 'answer') {
-	  log('✅ 收到 answer,通话建立')
-	  if (!pc) createPC()
-	  await pc.setRemoteDescription(new RTCSessionDescription(msg.payload?.sdp))
-	  await flushCandidates()
-  
-	  state.value = 'in-call'
-	  startTimer()
-	  return
+		log('✅ 收到 answer,通话建立')
+		if (!pc) createPC()
+		await pc.setRemoteDescription(new RTCSessionDescription(msg.payload?.sdp))
+		await flushCandidates()
+
+		state.value = 'in-call'
+		startTimer()
+		return
 	}
-  
+
 	if (t === 'candidate') {
-	  const c = msg.payload?.candidate
-	  if (!c) return
-	  if (pc && pc.remoteDescription) {
-		await pc.addIceCandidate(new RTCIceCandidate(c))
-	  } else {
-		pendingCandidates.push(c)
-	  }
-	  return
+		const c = msg.payload?.candidate
+		if (!c) return
+		if (pc && pc.remoteDescription) {
+			await pc.addIceCandidate(new RTCIceCandidate(c))
+		} else {
+			pendingCandidates.push(c)
+		}
+		return
 	}
-  
+
 	if (t === 'hangup') {
-	  log('📴 对方挂断')
-	  cleanup(false)
-	  return
+		log('📴 对方挂断')
+		cleanup(false)
+		return
 	}
-  
+
 	if (t === 'pong') {
-	  // 心跳响应
-	  // log('pong')
-	  return
+		// 心跳响应
+		// log('pong')
+		return
 	}
-  }
-  
-  // 暂存 offer(用于“先点接听再 answer”)
-  let lastOfferSdp = null
-  
-  /* ---------------- 业务操作 ---------------- */
-  
-  function connectWS() {
+}
+
+// 暂存 offer(用于“先点接听再 answer”)
+let lastOfferSdp = null
+
+/* ---------------- 业务操作 ---------------- */
+
+function connectWS() {
 	if (ws) ws.close()
-  
+
 	ws = new WebSocket(WS_BASE + encodeURIComponent(uid.value))
-  
+
 	ws.onopen = () => {
-	  connected.value = true
-	  log('WS 已连接 userId=', uid.value)
-	  startPing()
+		connected.value = true
+		log('WS 已连接 userId=', uid.value)
+		startPing()
 	}
-  
+
 	ws.onclose = () => {
-	  connected.value = false
-	  log('WS 已关闭')
-	  stopPing()
+		connected.value = false
+		log('WS 已关闭')
+		stopPing()
 	}
-  
+
 	ws.onerror = (e) => log('WS error', e)
-  
+
 	ws.onmessage = (e) => {
-	  const msg = JSON.parse(e.data)
-	  log('收到信令', msg)
-	  onSignal(msg)
+		const msg = JSON.parse(e.data)
+		log('收到信令', msg)
+		onSignal(msg)
 	}
-  }
-  
-  function requestCall() {
+}
+
+function requestCall() {
 	state.value = 'calling'
 	incomingFrom = null
 	acceptedByMe = false
 	offeredByPeer = false
 	lastOfferSdp = null
-  
+
 	send('call', peerUid.value, null)
 	log('➡️ 发起呼叫 call 给', peerUid.value)
-  }
-  
-  function acceptCall() {
+}
+
+function acceptCall() {
 	if (!incomingFrom) return
 	acceptedByMe = true
-  
+
 	// 先告诉对方我接听了(后端会把双方置忙)
 	send('accept', incomingFrom, null)
 	log('✅ 已点击接听,发送 accept 给', incomingFrom)
-  
+
 	state.value = 'calling'
-  
+
 	// 如果 offer 已经提前到达(我们暂存了),立刻 answer
 	if (lastOfferSdp) {
-	  answerOffer(lastOfferSdp, incomingFrom)
-	  lastOfferSdp = null
+		answerOffer(lastOfferSdp, incomingFrom)
+		lastOfferSdp = null
 	}
-  }
-  
-  function rejectCall() {
+}
+
+function rejectCall() {
 	if (!incomingFrom) return
-  
+
 	// 你后端没有 reject,用 busy 表示拒绝/不可接听
 	send('busy', incomingFrom, null)
 	log('❌ 已拒绝来电,发送 busy 给', incomingFrom)
-  
+
 	incomingFrom = null
 	acceptedByMe = false
 	offeredByPeer = false
 	lastOfferSdp = null
 	state.value = 'idle'
-  }
-  
-  async function startOffer() {
+}
+
+async function startOffer() {
 	if (!pc) createPC()
-  
+
 	// ✅ 不会重复 addTrack
 	await ensureLocalTracksAdded()
-  
+
 	const offer = await pc.createOffer()
 	await pc.setLocalDescription(offer)
-  
+
 	send('offer', peerUid.value, { sdp: pc.localDescription })
 	log('➡️ 已发送 offer 给', peerUid.value)
-  }
-  
-  async function answerOffer(offerSdp, from) {
+}
+
+async function answerOffer(offerSdp, from) {
 	if (!offerSdp) {
-	  log('❌ offer sdp 为空,无法接听')
-	  return
+		log('❌ offer sdp 为空,无法接听')
+		return
 	}
 	if (!pc) createPC()
-  
+
 	await pc.setRemoteDescription(new RTCSessionDescription(offerSdp))
 	await flushCandidates()
-  
+
 	await ensureLocalTracksAdded()
-  
+
 	const answer = await pc.createAnswer()
 	await pc.setLocalDescription(answer)
-  
+
 	send('answer', from, { sdp: pc.localDescription })
 	log('⬅️ 已发送 answer 给', from)
-  
+
 	state.value = 'in-call'
 	startTimer()
-  }
-  
-  function hangup() {
+}
+
+function hangup() {
 	cleanup(true)
-  }
-  
-  /* ---------------- 清理 ---------------- */
-  
-  function cleanup(sendToPeer) {
+}
+
+/* ---------------- 清理 ---------------- */
+
+function cleanup(sendToPeer) {
 	try {
-	  if (sendToPeer) send('hangup', peerUid.value, null)
+		if (sendToPeer) send('hangup', peerUid.value, null)
 	} catch {}
-  
+
 	stopTimer()
-  
+
 	if (pc) {
-	  pc.getSenders().forEach(s => s.track && s.track.stop())
-	  pc.close()
+		pc.getSenders().forEach(s => s.track && s.track.stop())
+		pc.close()
 	}
 	if (localStream) {
-	  localStream.getTracks().forEach(t => t.stop())
+		localStream.getTracks().forEach(t => t.stop())
 	}
-  
+
 	pc = null
 	localStream = null
 	pendingCandidates.length = 0
-  
+
 	incomingFrom = null
 	acceptedByMe = false
 	offeredByPeer = false
 	lastOfferSdp = null
-  
+
 	state.value = 'idle'
-  }
-  
-  onBeforeUnmount(() => {
+}
+
+onBeforeUnmount(() => {
 	cleanup(false)
 	stopPing()
 	if (ws) ws.close()
-  })
-  </script>
+})
+</script>
   
\ No newline at end of file
diff --git a/applications/drone-command/src/views/basicManage/deviceStock/DeviceScrapDiaLog.vue b/applications/drone-command/src/views/basicManage/deviceStock/DeviceScrapDiaLog.vue
index ad4958c..9244051 100644
--- a/applications/drone-command/src/views/basicManage/deviceStock/DeviceScrapDiaLog.vue
+++ b/applications/drone-command/src/views/basicManage/deviceStock/DeviceScrapDiaLog.vue
@@ -13,6 +13,7 @@
 						<el-input class="command-input" v-model="formData.disposeWay" maxlength="200" type="textarea" placeholder="请输入" clearable />
 					</el-form-item>
 				</el-col>
+				<div v-if="checkAreaBind" class="warning-text"> 注:该设备已绑定区域,报废设备该区域无法正常使用,是否报废?</div>
 			</el-row>
 		</el-form>
 		<template #footer>
@@ -35,7 +36,7 @@
 import { computed, ref } from 'vue'
 import { ElMessage } from 'element-plus'
 import { fieldRules } from '@ztzf/utils'
-import { fwDeviceScrapSaveApi } from '@/views/basicManage/deviceStock/fwDeviceScrapApi'
+import { fwDeviceScrapSaveApi,fivDevicelcheckAreaBindApi } from '@/views/basicManage/deviceStock/fwDeviceScrapApi'
 import dayjs from 'dayjs'
 import { saveOperationLog } from '@ztzf/apis'
 import { useRoute } from 'vue-router'
@@ -105,9 +106,29 @@
 	dialogMode.value = mode || 'add'
 	visible.value = true
 	formData.value.deviceId = row.id
+	getCheckAreaBind()
+}
+const checkAreaBind = ref(false)
+function getCheckAreaBind() {
+	fivDevicelcheckAreaBindApi({
+		deviceId: formData.value.deviceId
+	}).then(res => {
+		checkAreaBind.value = res.data.data
+	})
 }
 
-onMounted(() => {})
+onMounted(() => {
+})
 
 defineExpose({ open })
 </script>
+
+<style scoped lang="scss">
+.warning-text {
+	width: 100%;
+	color: red;
+	font-size: 14px;
+	text-align: center;
+	margin: -10px 0 10px 0;
+}
+</style>
diff --git a/applications/drone-command/src/views/basicManage/deviceStock/fwDeviceScrapApi.js b/applications/drone-command/src/views/basicManage/deviceStock/fwDeviceScrapApi.js
index 048eb82..2fa608c 100644
--- a/applications/drone-command/src/views/basicManage/deviceStock/fwDeviceScrapApi.js
+++ b/applications/drone-command/src/views/basicManage/deviceStock/fwDeviceScrapApi.js
@@ -26,3 +26,14 @@
 		params,
 	})
 }
+
+// 设置区域绑定校验
+export const fivDevicelcheckAreaBindApi = params => {
+	return request({
+		url: `/drone-fw/device/fwDevice/checkAreaBind`,
+		method: 'get',
+		params,
+	})
+}
+
+
diff --git a/applications/drone-command/src/views/basicManage/deviceStock/index.vue b/applications/drone-command/src/views/basicManage/deviceStock/index.vue
index b87baa7..561ce69 100644
--- a/applications/drone-command/src/views/basicManage/deviceStock/index.vue
+++ b/applications/drone-command/src/views/basicManage/deviceStock/index.vue
@@ -1,9 +1,9 @@
 <template>
 	<basic-container>
 		<div class="chart">
-			<DeviceChart1 />
-			<DeviceChart2 />
-			<DeviceChart3 />
+			<DeviceChart1 :key="updateKey"/>
+			<DeviceChart2 :key="updateKey"/>
+			<DeviceChart3 :key="updateKey"/>
 		</div>
 
 		<el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
@@ -77,9 +77,9 @@
 							{{ dayjs(row.createTime).format('YYYY-MM-DD') }}
 						</template>
 					</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 label="所属部门" />
+					<el-table-column prop="source" show-overflow-tooltip width="120" label="来源" />
+					<el-table-column prop="purpose" show-overflow-tooltip width="130" label="用途" />
+					<el-table-column prop="belongDeptName" show-overflow-tooltip width="160" 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 }">
@@ -91,7 +91,7 @@
 							{{ getDictLabel(row.trackStatus, trackStatusOptions) }}
 						</template>
 					</el-table-column>
-					<el-table-column label="操作" class-name="operation-btns">
+					<el-table-column label="操作" class-name="operation-btns" width="140">
 						<template v-slot="{ row }">
 							<el-link @click="handleView(row)">查看</el-link>
 							<el-link @click="handleEdit(row)">编辑</el-link>
@@ -116,9 +116,9 @@
 			</div>
 		</div>
 
-		<FormDiaLog ref="dialogRef" @success="getList" />
-		<DeviceTrackDiaLog ref="outboundDialogRef" @success="getList" />
-		<DeviceScrapDiaLog ref="scrapDialogRef" @success="getList" />
+		<FormDiaLog ref="dialogRef" @success="successFun" />
+		<DeviceTrackDiaLog ref="outboundDialogRef" @success="successFun" />
+		<DeviceScrapDiaLog ref="scrapDialogRef" @success="successFun" />
 	</basic-container>
 </template>
 
@@ -175,6 +175,12 @@
 	{ dictKey: 1, dictValue: '已出库' }
 ])
 
+const updateKey = ref(0)
+function successFun() {
+	updateKey.value = updateKey.value + 1
+	getList()
+}
+
 // 获取列表
 async function getList() {
 	const range = dateRangeFormat(dateRange.value)
@@ -219,7 +225,7 @@
 
 // 归还
 async function returnDevice(row) {
-	await ElMessageBox.confirm(`确认归还吗?`, '提示', { 
+	await ElMessageBox.confirm(`确认归还吗?`, '提示', {
 		type: 'warning',
 		customClass: 'command-page-view-message-box',
 		confirmButtonClass: 'command-message-box-confirm',
@@ -240,7 +246,7 @@
 // 删除
 async function handleDelete(row) {
 	const tips = row ? '该条' : '选中的项'
-	await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', { 
+	await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
 		type: 'warning',
 		customClass: 'command-page-view-message-box',
 		confirmButtonClass: 'command-message-box-confirm',
diff --git a/applications/drone-command/src/views/basicManage/maintainRecord/FormDiaLog.vue b/applications/drone-command/src/views/basicManage/maintainRecord/FormDiaLog.vue
index adc1346..d60dcf1 100644
--- a/applications/drone-command/src/views/basicManage/maintainRecord/FormDiaLog.vue
+++ b/applications/drone-command/src/views/basicManage/maintainRecord/FormDiaLog.vue
@@ -4,8 +4,8 @@
  * @LastEditors  : yuan
  * @LastEditTime : 2026-01-20 16:38:55
  * @FilePath     : \applications\drone-command\src\views\basicManage\maintainRecord\FormDiaLog.vue
- * @Description  : 
- * Copyright 2026 OBKoro1, All Rights Reserved. 
+ * @Description  :
+ * Copyright 2026 OBKoro1, All Rights Reserved.
  * 2026-01-20 14:04:02
 -->
 <template>
@@ -102,11 +102,36 @@
 					</el-form-item>
 				</el-col>
 				<el-col :span="12">
-					<el-form-item label="维护计划时间" prop="planCycleValue">
-						<el-date-picker class="command-date-picker" popper-class="command-date-picker-popper"
-							v-model="formData.planCycleValue" type="date" placeholder="请选择" clearable
-							value-format="YYYY-MM-DD" :format="planCycleDateFormat" :disabled="!formData.planCycleType"
-							:disabled-date="getPlanCycleDisabledDate" :editable="false" />
+					<el-form-item label="维护计划时间" prop="planCycleValue" >
+						<el-date-picker
+							v-if="formData.planCycleType === '1'"
+							:disabled="!formData.planCycleType"
+							class="command-date-picker"
+							popper-class="command-date-picker-popper"
+							v-model="formData.planCycleValue"
+							type="date"
+							placeholder="请选择"
+							clearable
+							value-format="YYYY-MM-DD"
+							:format="planCycleDateFormat"
+							:disabled-date="getPlanCycleDisabledDate"
+							:editable="false" />
+						<el-select
+							v-else
+							:disabled="!formData.planCycleType"
+							class="command-select"
+							popper-class="command-select-popper"
+							v-model="formData.planCycleValue"
+							placeholder="请选择"
+							clearable
+						>
+							<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>
@@ -145,6 +170,7 @@
 dayjs.locale('zh-cn')
 
 const planCycleOptions = inject('planCycleOptions')
+const planCycleValueOptions = ref([])
 
 const emit = defineEmits(['success'])
 const formRef = ref(null) // 表单实例
@@ -186,6 +212,29 @@
 	visible.value = false
 }
 
+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') {
+			;['一', '二', '三', '四', '五', '六', '日'].forEach((item, index) => {
+				planCycleValueOptions.value.push({ value: `星期${item}` })
+			})
+		}
+	}
+)
+
 // 提交新增/编辑
 async function handleSubmit () {
 	const isValid = await formRef.value?.validate().catch(() => false)
diff --git a/applications/task-work-order/src/views/orderView/equipmentResource/FormDiaLog.vue b/applications/task-work-order/src/views/orderView/equipmentResource/FormDiaLog.vue
new file mode 100644
index 0000000..ad32451
--- /dev/null
+++ b/applications/task-work-order/src/views/orderView/equipmentResource/FormDiaLog.vue
@@ -0,0 +1,631 @@
+<!-- 设备资源弹窗 -->
+<template>
+	<el-dialog
+		v-model="dialogVisible"
+		:title="dialogTitle"
+		class="gd-dialog"
+		destroy-on-close
+		:close-on-click-modal="false"
+	>
+	<el-row class="detail-row-view" v-if="dialogType === 'view'">
+      <el-col :span="12">
+        <div class="label">设备类型</div>
+        <div class="val">{{ getDictLabel(formData.deviceType, deviceTypeOptions) }}</div>
+      </el-col>
+	  <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">{{ formData.nickname }}</div>
+      </el-col>
+	  <el-col :span="12">
+        <div class="label">负载设备</div>
+        <div class="val">{{ getDictLabel(formData.devicePayload, devicePayloadOptions) }}</div>
+      </el-col>
+	  <el-col :span="12">
+        <div class="label">流量剩余(GB)</div>
+        <div class="val">无</div>
+      </el-col>
+	  <el-col :span="12">
+        <div class="label">流量到期时间</div>
+        <div class="val">无</div>
+      </el-col>
+	  <el-col :span="12">
+        <div class="label">设备状态</div>
+        <div class="val">{{ getDictLabel(formData.modeCode, dictObj.modeCode) }}</div>
+      </el-col>
+	  <el-col :span="12">
+        <div class="label">设备SN</div>
+        <div class="val">{{ formData.deviceSn }}</div>
+      </el-col>
+	  <el-col :span="12">
+        <div class="label">设备位置</div>
+        <div class="val">{{ formData.latitude }}, {{ formData.longitude }}</div>
+      </el-col>
+	  <el-col :span="12">
+        <div class="label">保险有效期</div>
+        <div class="val">无</div>
+      </el-col>
+	  <el-col :span="12">
+        <div class="label">固件版本</div>
+        <div class="val">{{ formData.firmwareVersion }}</div>
+      </el-col>
+	   <el-col :span="12">
+        <div class="label">所属部门</div>
+        <div class="val">{{ formData.deviceDeptName }}</div>
+      </el-col>
+	  <el-col :span="12">
+        <div class="label">最新在线时间</div>
+        <div class="val">无</div>
+      </el-col>
+	  <el-col :span="12">
+        <div class="label">设备注册时间</div>
+        <div class="val">无</div>
+      </el-col>
+	</el-row>
+
+		<el-form
+			ref="formRef"
+			:model="formData"
+			:rules="rules"
+			class="gd-form"
+			label-width="160px"
+			v-else
+		>
+			<el-row>
+				<el-col :span="12">
+					<el-form-item label="设备类型" prop="deviceType">
+						<el-select
+							class="gd-select"
+							popper-class="gd-select-popper"
+							v-model="formData.deviceType"
+							placeholder="请选择"
+							:disabled="dialogType === 'view'"
+						>
+							<el-option
+								v-for="item in deviceTypeOptions"
+								: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="deviceName">
+						<el-input
+							class="gd-input"
+							v-model="formData.deviceName"
+							placeholder="请输入"
+							:disabled="dialogType === 'view'"
+						/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item label="设备名称" prop="nickname">
+						<el-input
+							class="gd-input"
+							v-model="formData.nickname"
+							placeholder="请输入"
+							:disabled="dialogType === 'view'"
+						/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item label="负载设备" prop="devicePayload">
+						<el-radio-group
+							v-model="formData.devicePayload"
+							:disabled="dialogType === 'view'"
+						>
+							<el-radio
+								v-for="item in devicePayloadOptions"
+								:key="item.dictKey"
+								:label="item.dictKey"
+							>
+								{{ item.dictValue }}
+							</el-radio>
+						</el-radio-group>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item label="设备状态" prop="modeCode">
+						<el-select
+							class="gd-select"
+							popper-class="gd-select-popper"
+							v-model="formData.modeCode"
+							placeholder="请选择"
+							:disabled="dialogType === 'view'"
+						>
+							<el-option
+								v-for="item in modeCodeOptions"
+								:key="item.dictKey"
+								:label="item.dictValue"
+								:value="item.dictKey"
+							/>
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item label="设备SN" prop="deviceSn">
+						<el-input
+							class="gd-input"
+							v-model="formData.deviceSn"
+							placeholder="请输入"
+							:disabled="dialogType === 'view'"
+						/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item label="设备位置" prop="deviceLocation">
+						<el-button  class="address-btn" @click="selectLocation">
+							{{ formData.longitude }}, {{ formData.latitude }}
+						</el-button>
+					</el-form-item>
+				</el-col>
+				<!-- <el-col :span="12">
+					<el-form-item label="所属部门" prop="departmentId">
+						<el-tree-select
+							class="gd-select"
+							popper-class="gd-tree-select-popper"
+							v-model="formData.departmentId"
+							:data="deptTree"
+							:props="treeProps"
+							node-key="id"
+							check-strictly
+							clearable
+							@change="handleSearch"
+						/>
+					</el-form-item>
+				</el-col> -->
+			</el-row>
+		</el-form>
+		<!-- <div style="width: 100%; height: 400px;">
+			<CommonCesiumMap
+					ref="mapRef"
+					class="gd-cesium"
+					:active="dialogVisible"
+					:flat-mode="false"
+					:terrain="true"
+					:layer-mode="4"
+					:boundary="false"
+					:zoomToBoundary="false"
+					@ready="readyMap"
+				/>
+		</div> -->
+		<template #footer>
+			<el-button color="#F2F3F5" v-if="dialogType !== 'view'" @click="handleClose">取消</el-button>
+			<el-button
+				v-if="dialogType !== 'view'"
+				type="primary"
+				color="#4C34FF"
+				@click="handleSubmit"
+			>
+				确定
+			</el-button>
+		</template>
+	</el-dialog>
+	<el-dialog v-model="visible1"
+		title="选择设备位置"
+		class="gd-dialog"
+		destroy-on-close
+		:close-on-click-modal="false"
+		@close="positionClosed"
+	>
+			<div style="width: 100%; height: 600px;">
+			<CommonCesiumMap
+					ref="mapRef"
+					class="gd-cesium"
+					:active="visible1"
+					:flat-mode="false"
+					:terrain="true"
+					:layer-mode="4"
+					:boundary="false"
+					:zoomToBoundary="false"
+					@ready="mapReady"
+				/>
+		</div>
+
+			<template #footer>
+				<el-button color="#F2F3F5" @click="handleMapCancel">取消</el-button>
+				<el-button color="#4C34FF" type="primary" @click="handleMapConfirm">确定</el-button>
+			</template>
+		</el-dialog>
+</template>
+<script setup>
+import { ref, reactive, computed, watch, inject, nextTick } from 'vue'
+import { submitExternalEquipment } from './equipmentResourceApi'
+import { ElMessage } from 'element-plus'
+import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
+import * as Cesium from 'cesium'
+import axios from 'axios'
+import _ from 'lodash'
+import { getDictLabel,fieldRules } from '@ztzf/utils'
+
+const props = defineProps({
+	modelValue: {
+		type: Boolean,
+		default: false
+	},
+	dialogType: {
+		type: String,
+		default: 'add', // add, edit, view
+		validator: (value) => ['add', 'edit', 'view'].includes(value)
+	},
+	equipmentData: {
+		type: Object,
+		default: () => ({})
+	}
+})
+
+const dictObj = inject('dictObj')
+
+const visible1 = ref(false) // 弹框显隐
+const mapRef = ref(null)
+const tempLocation = ref({ longitude: null, latitude: null })
+
+
+
+const deptTree = ref([]) // 部门树
+const treeProps = {
+	label: 'name',
+	children: 'children',
+}
+
+// 设备类型(0机场,1无人机)
+const modeCodeOptions = ref([
+	{ dictKey: 0, dictValue: '在线' },
+	{ dictKey: 1, dictValue: '离线' }
+])
+
+// 设备类型(0机场,1无人机)
+const deviceTypeOptions = ref([
+	{ dictKey: 0, dictValue: '机场' },
+	{ dictKey: 1, dictValue: '无人机' }
+])
+
+// 设备负载(0红外,1喇叭,2闪光灯)
+const devicePayloadOptions = ref([
+	{ dictKey: '红外', dictValue: '红外' },
+	{ dictKey: '喇叭', dictValue: '喇叭' },
+	{ dictKey: '闪光灯', dictValue: '闪光灯' }
+])
+
+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 initForm = () => ({
+	childSn: '', // 子设备(一般用途是机场下面的无人机)
+	deviceName: '', // 设备型号
+	devicePayload: '', // 设备负载(红外,喇叭,闪光灯)
+	deviceSn: '', // 设备编码
+	deviceType: null, // 设备类型(0机场,1无人机)
+	firmwareVersion: '', //	固件版本
+	insureExpiredTime: '', // 保险过期时间yyyy-MM-dd HH:mm:ss
+	latitude: 0, // 当前纬度
+	longitude: 0, // 当前经度
+	modeCode: null, // 设备状态:{0:"在线",1:"离线"}
+	nickname: '', // 设备昵称
+	trafficExpireTime: '', // 流量到期时间yyyy-MM-dd HH:mm:ss
+	trafficRemaining: '', // 剩余流量(单位:GB)
+	address: '', // 设备位置
+})
+
+const formData = ref(initForm())
+
+
+const rules = {
+	deviceType: fieldRules(true),
+	deviceModel: fieldRules(true, 50),
+	deviceName: fieldRules(true, 50),
+	deviceSn: fieldRules(true, 50),
+	deviceType: fieldRules(true),
+	modeCode: fieldRules(true),
+	nickname: fieldRules(true, 50),
+}
+
+// const rules = {
+// 	deviceType: [
+// 		{ required: true, message: '请选择', trigger: 'change' }
+// 	],
+// 	deviceModel: [
+// 		{ required: true, message: '请输入', trigger: 'blur' }
+// 	],
+// 	deviceName: [
+// 		{ required: true, message: '请输入', trigger: 'blur' }
+// 	],
+// 	deviceStatus: [
+// 		{ required: true, message: '请选择', trigger: 'change' }
+// 	],
+// 	deviceSn: [
+// 		{ required: true, message: '请输入', trigger: 'blur' }
+// 	]
+// }
+
+let viewer
+let map
+let redPointEntity
+let leftClickBound = false
+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,
+			},
+			label: {
+				text: `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`,
+				font: '14px',
+				fillColor: Cesium.Color.WHITE,
+				backgroundColor: Cesium.Color.BLACK.withAlpha(0.6),
+				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,
+				eyeOffset: new Cesium.Cartesian3(0, 0, -20),
+			},
+		})
+	} else {
+		redPointEntity.position = Cesium.Cartesian3.fromDegrees(longitude, latitude)
+		redPointEntity.label.text = `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`
+	}
+}
+
+async function selectLocation () {
+	visible1.value = true
+	if (viewer) return
+	await nextTick()
+	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)
+	}
+}
+
+async function getLocationName () {
+	const tk = import.meta.env.VITE_APP_TDT_TOKEN
+	const { latitude, longitude } = tempLocation.value
+	const http = axios.create({ withCredentials: false })
+	const res = await http({
+		url: `https://api.tianditu.gov.cn/geocoder?postStr={'lon':${longitude},'lat':${latitude},'ver':1}&tk=${tk}`,
+		method: 'get',
+	})
+	formData.value.address = res.data.result.formatted_address
+}
+
+function handleMapConfirm () {
+	if (tempLocation.value.longitude != null && tempLocation.value.latitude != null) {
+		formData.value.longitude = _.round(tempLocation.value.longitude, 6)
+		formData.value.latitude = _.round(tempLocation.value.latitude, 6)
+		getLocationName()
+	}
+	visible1.value = false
+}
+
+function handleMapCancel () {
+	tempLocation.value = { longitude: null, latitude: null }
+	visible1.value = false
+}
+
+function LeftClickEvent (click) {
+	const pos = click.position // Cartesian2 屏幕坐标
+	// 屏幕坐标 -> 椭球面坐标(不考虑地形/模型)
+	const cartesian3 = viewer.camera.pickEllipsoid(pos, viewer.scene.globe.ellipsoid)
+	if (!cartesian3) {
+		return { longitude: 112, latitude: 23 }
+	}
+	// 椭球面坐标 -> 经纬度
+	const carto = Cesium.Cartographic.fromCartesian(cartesian3)
+	const longitude = Cesium.Math.toDegrees(carto.longitude)
+	const latitude = Cesium.Math.toDegrees(carto.latitude)
+	tempLocation.value = { longitude, latitude }
+	// 添加红点(全局仅保留一个)并渲染经纬度标签
+	if (!redPointEntity) {
+		redPointEntity = viewer.entities.add({
+			position: Cesium.Cartesian3.fromDegrees(longitude, latitude),
+			point: {
+				color: Cesium.Color.RED,
+				pixelSize: 10,
+			},
+			label: {
+				text: `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`,
+				font: '14px',
+				fillColor: Cesium.Color.WHITE, // 文字颜色:白色
+				backgroundColor: Cesium.Color.BLACK.withAlpha(0.6), //背景颜色
+				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,
+				eyeOffset: new Cesium.Cartesian3(0, 0, -20), // 负值更靠近相机(显示在前)
+			},
+		})
+	} else {
+		redPointEntity.position = Cesium.Cartesian3.fromDegrees(longitude, latitude)
+		redPointEntity.label.text = `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`
+	}
+}
+
+function positionClosed () {
+	// 清除地图相关数据
+	if (viewer && leftClickBound) {
+		map.publicCesium?.removeLeftClickEvent?.(null, LeftClickEvent)
+		leftClickBound = false
+	}
+	redPointEntity = null
+	viewer = null
+}
+
+// 监听设备数据变化,更新表单
+watch(
+	() => props.equipmentData,
+	(newData) => {
+		if (newData) {
+			Object.assign(formData.value, newData)
+		} else {
+			formData.value = initForm()
+		}
+	},
+	{ deep: true, immediate: true }
+)
+
+// 处理关闭
+function handleClose() {
+	dialogVisible.value = false
+	positionClosed()
+	resetForm()
+}
+
+// 重置表单
+function resetForm() {
+	formRef.value?.resetFields()
+	// 重置为默认值
+		Object.assign(formData.value, {
+			childSn: '', // 子设备(一般用途是机场下面的无人机)
+			deviceName: '', // 设备型号f
+			devicePayload: '', // 设备负载(红外,喇叭,闪光灯)
+			deviceSn: '', // 设备编码
+			deviceType: null, // 设备类型(0机场,1无人机)
+			firmwareVersion: '', //	固件版本
+			insureExpiredTime: '', // 保险过期时间yyyy-MM-dd HH:mm:ss
+			latitude: 0, // 当前纬度
+			longitude: 0, // 当前经度
+			modeCode: null, // 设备状态:{0:"在线",1:"离线"}
+			nickname: '', // 设备昵称
+			trafficExpireTime: '', // 流量到期时间yyyy-MM-dd HH:mm:ss
+			trafficRemaining: '', // 剩余流量(单位:GB)
+			address: '', // 设备位置
+			departmentId: '', // 所属部门
+		})
+}
+
+// 处理提交
+async function handleSubmit() {
+	const isValid = await formRef.value?.validate()
+	if (!isValid) {
+		return
+	}
+
+	try {
+		if (props.dialogType === 'add') {
+			await submitExternalEquipment(formData.value)
+			ElMessage.success('添加成功')
+		} else if (props.dialogType === 'edit') {
+			await submitExternalEquipment(formData.value)
+			ElMessage.success('编辑成功')
+		}
+		emit('confirm')
+		dialogVisible.value = false
+		resetForm()
+	} catch (error) {
+		ElMessage.error('操作失败')
+	}
+}
+
+// 地图加载完毕
+function mapReady() {
+	mapRef.value.flyBoundary()
+}
+
+// 初始化地图实例
+function initMap() {
+	if (viewer) return
+	const map = mapRef.value?.getMap()
+	viewer = map?.viewer || null
+}
+</script>
+<style scoped lang="scss">
+.gd-form {
+	.el-form-item {
+		margin-bottom: 20px;
+	}
+}
+.command-page-map-view-dialog {
+    margin: 0;
+    padding: 20px;
+    display: flex;
+    flex-direction: column;
+    width: 80%;
+    height: 812px;
+    background: #1A1A2A;
+    border-radius: 6px;
+    border: 1px solid #2E2E46;
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+
+    .el-dialog__header,
+    .el-dialog__footer {
+        margin: 0;
+        padding: 0;
+    }
+
+    .el-dialog__footer {
+        margin-top: 20px;
+        text-align: center;
+
+        .el-button {
+            margin: 0 10px;
+        }
+    }
+
+    .el-dialog__body {
+        display: flex;
+        flex-direction: column;
+        height: 100%;
+        min-height: 600px;
+        padding: 0;
+
+        .dialog-container {
+            display: flex;
+            height: 100%;
+
+            .left-container {
+                width: 100%;
+                height: 100%;
+
+                .leftMap {
+                    width: 100%;
+                    height: 100%;
+                    min-height: 600px;
+                }
+            }
+        }
+    }
+}
+
+</style>
\ No newline at end of file
diff --git a/applications/task-work-order/src/views/orderView/equipmentResource/index.vue b/applications/task-work-order/src/views/orderView/equipmentResource/index.vue
new file mode 100644
index 0000000..c84cbbe
--- /dev/null
+++ b/applications/task-work-order/src/views/orderView/equipmentResource/index.vue
@@ -0,0 +1,237 @@
+<!-- 设备资源管理 -->
+<template>
+	<basic-container>
+		<el-form ref="queryParamsRef" :model="searchParams" class="gd-search-form">
+			<el-form-item label="设备名称" prop="nickname">
+				<el-input
+					class="gd-input gray"
+					v-model="searchParams.nickname"
+					placeholder="请输入设备名称"
+					clearable
+					@clear="handleSearch"
+				/>
+			</el-form-item>
+
+			<el-form-item label="设备类型" prop="deviceType">
+				<el-select
+					class="gd-select gray"
+					popper-class="gd-select-popper"
+					v-model="searchParams.deviceType"
+					placeholder="请选择"
+					clearable
+					@change="handleSearch"
+				>
+					<el-option
+						v-for="item in deviceTypeOptions"
+						:key="item.dictKey"
+						:label="item.dictValue"
+						:value="item.dictKey"
+					/>
+				</el-select>
+			</el-form-item>
+
+			<el-form-item label="设备状态" prop="modeCode">
+				<el-select
+					class="gd-select gray"
+					popper-class="gd-select-popper"
+					v-model="searchParams.modeCode"
+					placeholder="请选择"
+					clearable
+					@change="handleSearch"
+				>
+					<el-option
+						v-for="item in dictObj.modeCode"
+						: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="addEquipment">新增</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="deviceType" show-overflow-tooltip label="设备类型">
+						<template v-slot="{ row }">
+							{{ row.deviceType === '1' ? '无人机' : '机场' }}
+						</template>
+					</el-table-column>
+					<el-table-column prop="deviceName" show-overflow-tooltip label="设备型号" />
+					<el-table-column prop="nickname" show-overflow-tooltip label="设备名称" />
+					<el-table-column prop="devicePayload" show-overflow-tooltip label="负载设备" />
+					<el-table-column prop="modeCode" show-overflow-tooltip label="设备状态">
+						<template v-slot="{ row }">
+							{{ getDictLabel(row.modeCode, dictObj.modeCode) }}
+						</template>
+					</el-table-column>
+					<el-table-column prop="deviceSn" show-overflow-tooltip label="设备SN" />
+					<el-table-column show-overflow-tooltip label="设备位置" >
+						<template v-slot="{ row }">
+							{{ row.latitude ? (row.latitude + ', ' + row.longitude) : '未定位' }}
+						</template>
+					</el-table-column>
+					<el-table-column prop="firmwareVersion" show-overflow-tooltip label="固件版本" />
+					<el-table-column prop="deviceDeptName" show-overflow-tooltip label="所属部门" />
+					<el-table-column label="操作" class-name="operation-btns" width="180">
+						<template v-slot="{ row }">
+							<el-link @click="viewEquipment(row)">查看</el-link>
+							<!-- <el-link type="primary" @click="editEquipment(row)">编辑</el-link>
+							<el-link type="danger" @click="deleteEquipment(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>
+
+		<!-- 设备资源弹窗 -->
+		<EquipmentResourceDialog
+			ref="equipmentResourceDialogRef"
+			v-model="dialogVisible"
+			:dialog-type="dialogType"
+			:equipment-data="equipmentData"
+			@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 { getEquipmentPage, removeEquipment } from './equipmentResourceApi'
+import EquipmentResourceDialog from './FormDiaLog.vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+
+// 初始化查询参数
+const initSearchParams = () => ({
+	nickname: '', // 设备名称
+	deviceType: '', // 设备类型
+	modeCode: '', // 设备状态
+	current: 1, // 当前页
+	size: 10, // 每页大小
+})
+const searchParams = ref(initSearchParams()) // 查询参数
+const total = ref(0) // 总条数
+const loading = ref(true) // 列表加载中
+const list = ref([]) // 列表数据
+const queryParamsRef = ref(null) // 查询表单实例
+const equipmentResourceDialogRef = ref(null)
+const dialogVisible = ref(false)
+const dialogType = ref('add') // add, edit, view
+const equipmentData = ref({})
+
+// 设备类型(0机场,1无人机)
+const deviceTypeOptions = ref([
+	{ dictKey: 0, dictValue: '机场' },
+	{ dictKey: 1, dictValue: '无人机' }
+])
+
+
+const dictObj = ref({
+	modeCode: []
+})
+
+provide('dictObj', dictObj)
+
+// 获取列表
+async function getList() {
+	loading.value = true
+	try {
+		const res = await getEquipmentPage(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 addEquipment() {
+	dialogType.value = 'add'
+	equipmentData.value = {}
+	dialogVisible.value = true
+}
+
+// 查看设备
+function viewEquipment(row) {
+	dialogType.value = 'view'
+	equipmentData.value = { ...row }
+	dialogVisible.value = true
+}
+
+// 编辑设备
+function editEquipment(row) {
+	dialogType.value = 'edit'
+	equipmentData.value = { ...row }
+	dialogVisible.value = true
+}
+
+// 删除设备
+async function deleteEquipment(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 removeEquipment({ ids: ids })
+	ElMessage.success('删除成功')
+	getList()
+}
+
+// 处理弹窗确认
+function handleConfirm() {
+	dialogVisible.value = false
+	getList()
+}
+
+// 获取字典
+function getDictList() {
+	getDictionaryByCode('modeCode').then(res => {
+		dictObj.value = res.data.data
+	})
+}
+
+onMounted(() => {
+	getDictList()
+	getList()
+})
+</script>
+<style scoped lang="scss"></style>
\ No newline at end of file
diff --git a/applications/task-work-order/src/views/orderView/flyingHand/FlyingHandDialog.vue b/applications/task-work-order/src/views/orderView/flyingHand/FlyingHandDialog.vue
index f8988f7..f58c380 100644
--- a/applications/task-work-order/src/views/orderView/flyingHand/FlyingHandDialog.vue
+++ b/applications/task-work-order/src/views/orderView/flyingHand/FlyingHandDialog.vue
@@ -30,7 +30,7 @@
       </el-col>
 	  <el-col :span="12">
         <div class="label">擅长任务类型</div>
-        <div class="val">{{ getDictLabel(formData.skilledTaskType, dictObj.skilledTaskType) }}</div>
+        <div class="val">{{ getDictLabel(formData.skilledTaskType, dictObj.patrolTaskType) }}</div>
       </el-col>
 	  <el-col :span="12">
         <div class="label">项目经验</div>
@@ -149,7 +149,7 @@
 							:disabled="dialogType === 'view'"
 						>
 							<el-option
-								v-for="item in dictObj.skilledTaskType"
+								v-for="item in dictObj.patrolTaskType"
 								:key="item.dictKey"
 								:label="item.dictValue"
 								:value="item.dictKey"
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 686f871..8972b67 100644
--- a/applications/task-work-order/src/views/orderView/flyingHand/index.vue
+++ b/applications/task-work-order/src/views/orderView/flyingHand/index.vue
@@ -72,7 +72,7 @@
 					</el-table-column>
 					<el-table-column prop="skilledTaskType" show-overflow-tooltip label="擅长任务类型">
 						<template v-slot="{ row }">
-							{{ getDictLabel(row.skilledTaskType, dictObj.skilledTaskType) }}
+							{{ getDictLabel(row.skilledTaskType, dictObj.patrolTaskType) }}
 						</template>
 					</el-table-column>
 					<el-table-column label="操作" class-name="operation-btns" width="180">
@@ -136,7 +136,7 @@
 const flyingHandData = ref({})
 
 const dictObj = ref({
-	skilledTaskType: [], // 擅长任务类型
+	patrolTaskType: [], // 擅长任务类型
 	skilledUavType: [], // 擅长机型
 	technicalStrength: [] // 技术特长
 })
@@ -212,7 +212,7 @@
 
 // 获取字典
 function getDictList() {
-	getDictionaryByCode('skilledTaskType,skilledUavType,technicalStrength').then(res => {
+	getDictionaryByCode('patrolTaskType,skilledUavType,technicalStrength').then(res => {
 		dictObj.value = res.data.data
 	})
 }
diff --git a/applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/FormDiaLog.vue b/applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/FormDiaLog.vue
index 6d685f0..e2220ad 100644
--- a/applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/FormDiaLog.vue
+++ b/applications/task-work-order/src/views/orderView/orderDataManage/dataObjection/FormDiaLog.vue
@@ -292,7 +292,7 @@
 	catalogResourceName: fieldRules(true),
 	objectionDesc: fieldRules(true),
 	objectionBasis: fieldRules(true),
-	// attachId: dialogMode.value === 'add' ? fieldRules(true) : fieldRules(false)
+	attachId: fieldRules(true)
 
 }
 
@@ -375,6 +375,7 @@
 
 // 提交数据的通用函数
 async function submitForm(objectionStatus) {
+formData.value.attachId = uploadedFiles.value.length > 0 ? uploadedFiles.value.length : ''
   const isValid = await formRef.value?.validate().catch(() => false)
   if (!isValid) return
   
@@ -504,24 +505,69 @@
 	attachmentDetailsList.value = results.map(res => res.data.data).filter(Boolean)
 }
 // 处理附件下载
-async function handleDownloadAttach() {
-	if (attachmentDetailsList.value.length === 0) {
-		ElMessage.warning('没有可下载的附件')
-		return
-	}
+const handleDownloadAttach = async () => {
+  if (!attachmentDetailsList.value || attachmentDetailsList.value.length === 0) {
+    ElMessage.warning('没有可下载的附件')
+    return
+  }
 
-	// 遍历所有附件并下载
-	attachmentDetailsList.value.forEach(attach => {
-		if (attach.link) {
-			const link = document.createElement('a')
-			link.href = attach.link
-			link.download = attach.originalName || attach.name
-			link.style.display = 'none'
-			document.body.appendChild(link)
-			link.click()
-			document.body.removeChild(link)
-		}
-	})
+
+  // 顺序下载每个附件
+  for (let i = 0; i < attachmentDetailsList.value.length; i++) {
+    const attach = attachmentDetailsList.value[i]
+    
+    if (!attach.link) {
+      console.warn(`附件 ${attach.originalName || attach.name} 链接无效,跳过`)
+      continue
+    }
+
+    try {
+      // 显示当前下载的文件信息
+      const currentFileName = attach.originalName || attach.name || `附件_${i + 1}`
+   
+      
+      const response = await fetch(attach.link)
+      const blob = await response.blob()
+      const url = window.URL.createObjectURL(blob)
+      const a = document.createElement('a')
+      a.href = url
+      
+      // 设置文件名:优先使用 originalName
+      let fileName = attach.originalName || attach.name || `附件_${i + 1}`
+      
+      // 确保有扩展名
+      if (fileName && !fileName.includes('.')) {
+        const contentType = response.headers.get('content-type')
+        if (contentType && contentType.includes('zip')) {
+          fileName += '.zip'
+        } else if (contentType && contentType.includes('rar')) {
+          fileName += '.rar'
+        } else {
+          fileName += '.zip'
+        }
+      }
+      
+      a.download = fileName
+      a.style.display = 'none'
+      document.body.appendChild(a)
+      a.click()
+      
+      // 清理
+      setTimeout(() => {
+        window.URL.revokeObjectURL(url)
+        document.body.removeChild(a)
+      }, 100)
+      
+      // 文件间延迟(除了最后一个)
+      if (i < attachmentDetailsList.value.length - 1) {
+        await new Promise(resolve => setTimeout(resolve, 1000))
+      }
+      
+    } catch (error) {
+      console.error(`下载失败: ${attach.originalName || attach.name}`, error)
+    }
+  }
+  
 }
 
 
diff --git a/applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/ApplyViewDialog.vue b/applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/ApplyViewDialog.vue
index 1990fc0..006b0ac 100644
--- a/applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/ApplyViewDialog.vue
+++ b/applications/task-work-order/src/views/orderView/orderDataManage/supplyAdd/ApplyViewDialog.vue
@@ -1,5 +1,5 @@
 <template>
-	<el-dialog class="gd-dialog" :close-on-click-modal="false" v-model="visible" :title="'查看'" @closed="visible = false" destroy-on-close>
+	<el-dialog width="80%" class="gd-dialog" :close-on-click-modal="false" v-model="visible" :title="'查看'" @closed="visible = false" destroy-on-close>
 		<div class="detail-container">
 			<!-- 左侧审批记录 -->
 			<div class="detail-left">
@@ -98,7 +98,7 @@
 						<el-table-column prop="fileSize" label="文件大小" />
 						<el-table-column fixed="right" label="操作" min-width="100">
 							<template #default="scope">
-								<el-button link type="text" @click="downloadFileFromTable(scope.row)">下载</el-button>
+								<el-link  type="primary"  @click="downloadFileFromTable(scope.row)">下载</el-link>
 							</template>
 						</el-table-column>
 					</el-table>
@@ -236,32 +236,52 @@
 	ElMessage.error(error?.message || '文件上传失败,请重试')
 }
 
-// 查看文件
-const viewFile = file => {
-	if (file.link) {
-		window.open(file.link, '_blank')
-	} else {
-		ElMessage.warning('文件链接无效')
-	}
-}
+// 从表格下载文件 - 使用 fetch API
+const downloadFileFromTable = async (file) => {
+  if (!file.fileUrl) {
+    ElMessage.warning('文件链接无效')
+    return
+  }
 
-// 从表格下载文件
-const downloadFileFromTable = file => {
-	if (file.fileUrl) {
-		// 创建一个隐藏的a标签来触发下载
-		const a = document.createElement('a')
-		a.href = file.fileUrl
-		a.download = file.fileOriginalName || '下载附件'
-		document.body.appendChild(a)
-		a.click()
-		document.body.removeChild(a)
-	} else {
-		ElMessage.warning('文件链接无效')
-	}
+  try { 
+    const response = await fetch(file.fileUrl)
+    const blob = await response.blob()
+    const url = window.URL.createObjectURL(blob)
+    const a = document.createElement('a')
+    a.href = url
+    
+    // 获取原始文件名,处理中文和特殊字符
+    const originalName = file.fileOriginalName || 'download-file.zip'
+    // 确保文件名有正确的扩展名
+    let fileName = originalName
+    if (!fileName.includes('.')) {
+      // 从 Content-Type 推断扩展名
+      const contentType = response.headers.get('content-type')
+      if (contentType === 'application/zip') {
+        fileName += '.zip'
+      } else if (contentType === 'application/x-rar-compressed') {
+        fileName += '.rar'
+      }
+    }
+    
+    a.download = fileName
+    a.style.display = 'none'
+    document.body.appendChild(a)
+    a.click()
+    
+    // 清理
+    setTimeout(() => {
+      window.URL.revokeObjectURL(url)
+      document.body.removeChild(a)
+    }, 100)
+    
+  } catch (error) {
+    console.error('下载失败:', error)
+  }
 }
 
 // 下载所有文件
-const downloadAllFiles = () => {
+const downloadAllFiles =async () => {
 	if (!tableData.value || tableData.value.length === 0) {
 		ElMessage.warning('没有可下载的文件')
 		return
@@ -273,22 +293,56 @@
 		ElMessage.warning('所有文件链接均无效')
 		return
 	}
-
-	ElMessage.success(`开始下载${validFiles.length}个文件`)
-
-	// 依次下载每个文件(添加延迟以避免浏览器限制)
-	validFiles.forEach((file, index) => {
-		setTimeout(() => {
-			if (file.fileUrl) {
-				const a = document.createElement('a')
-				a.href = file.fileUrl
-				a.download = file.fileOriginalName || `下载附件_${index + 1}`
-				document.body.appendChild(a)
-				a.click()
-				document.body.removeChild(a)
-			}
-		}, index * 300) // 300ms delay between downloads
-	})
+  // 顺序下载每个文件
+  for (let i = 0; i < validFiles.length; i++) {
+    const file = validFiles[i]
+    
+    try {
+      const response = await fetch(file.fileUrl)
+      const blob = await response.blob()
+      const url = window.URL.createObjectURL(blob)
+      const a = document.createElement('a')
+      a.href = url
+      
+      // 设置文件名,优先使用原始文件名
+      let fileName = file.fileOriginalName || `附件_${i + 1}`
+      
+      // 确保文件名有扩展名
+      if (!fileName.includes('.')) {
+        const contentType = response.headers.get('content-type')
+        if (contentType === 'application/zip') {
+          fileName += '.zip'
+        } else if (contentType === 'application/x-rar-compressed') {
+          fileName += '.rar'
+        } else if (contentType === 'application/x-7z-compressed') {
+          fileName += '.7z'
+        } else {
+          fileName += '.zip'
+        }
+      }
+      
+      a.download = fileName
+      a.style.display = 'none'
+      document.body.appendChild(a)
+      a.click()
+      
+      // 清理资源
+      setTimeout(() => {
+        window.URL.revokeObjectURL(url)
+        document.body.removeChild(a)
+      }, 100)
+      
+      // 文件间添加延迟(除了最后一个文件)
+      if (i < validFiles.length - 1) {
+        await new Promise(resolve => setTimeout(resolve, 500))
+      }
+      
+    } catch (error) {
+      console.error(`文件下载失败: ${file.fileOriginalName || '未知文件'}`, error)
+      // 继续下载下一个文件
+    }
+  }
+  
 }
 
 // 根据部门ID获取部门名称
@@ -362,9 +416,6 @@
 
 // 拒绝申请
 const rejectTheApplication = () => {
-	// if (uploadedFiles.value.length === 0) {
-	// 	return ElMessage.warning('请上传数据')
-	// }
 	// 打开拒绝申请弹框
 	rejectionDialogVisible.value = true
 }
@@ -466,6 +517,10 @@
 	}
 }
 
+.el-upload__tip {
+	display: inline;
+}
+
 
 
 /* 上传按钮样式 */
diff --git a/applications/task-work-order/src/views/orderView/orderManage/inspectionReport/index.vue b/applications/task-work-order/src/views/orderView/orderManage/inspectionReport/index.vue
index 4c99877..c6c9dce 100644
--- a/applications/task-work-order/src/views/orderView/orderManage/inspectionReport/index.vue
+++ b/applications/task-work-order/src/views/orderView/orderManage/inspectionReport/index.vue
@@ -82,10 +82,9 @@
 		</div>
 		<el-dialog
 			class="gd-dialog"
-			append-to-body
 			v-model="isShowEditView"
 			:title="titleTxt"
-			:width="pxToRem(800)"
+			:width="pxToRem(500)"
 			:close-on-click-modal="false"
 			:destroy-on-close="true"
 			@close="handleClose"
@@ -111,10 +110,12 @@
 					</el-select>
 				</el-form-item>
 				<el-form-item label="上传文件" prop="link" v-if="titleTxt === '上传巡查报告'">
-					{{ uploadName }}
 					<el-upload class="avatar-uploader" action="" :show-file-list="false" :before-upload="onUploadFileBefore">
 						<el-button size="small" type="primary">点击上传</el-button>
 					</el-upload>
+					<div class="upload-tip">
+						{{ uploadName }}
+					</div>
 				</el-form-item>
 			</el-form>
 			<!-- </div> -->
@@ -311,43 +312,53 @@
 		ElMessage.warning('请选择要下载的文件')
 		return
 	}
-	if (selectedFiles.value.length === 1) {
-		selectedFiles.value.forEach((file, index) => {
-			// setTimeout(() => {
-			if (file.link) {
-				const a = document.createElement('a')
-				a.href = file.link
-				a.download = file.originalName || `attachment_${index + 1}`
-				document.body.appendChild(a)
-				a.click()
-				document.body.removeChild(a)
-			}
-			// }, index * 300) // 300ms delay between downloads
-		})
-	} else {
-		// 批量下载
-		try {
-			console.log(selectedFiles.value, '9999')
-			loadingData = ElLoading.service({ background: 'rgba(0, 0, 0, 0.5)', text: '打包中,请稍等...' })
-			const fileIds = selectedFiles.value.map(i => i.id)
-			// const res = await getDownloadStatusApi({ type: 'dpsjzx' })
-			// if (!['CANCELLED', 'COMPLETED'].includes(res.data.data?.status || 'COMPLETED')) {
-			// 	return ElMessage.warning('还有正在处理的')
-			// }
-			console.log(fileIds, '7777')
+	// if (selectedFiles.value.length === 1) {
+	// 	selectedFiles.value.forEach(async (file, index) => {
+	// 		if (file.link) {
+	// 			try {
+	// 				// 使用 fetch 获取 blob 数据来强制下载,避免浏览器打开文件
+	// 				const response = await fetch(file.link)
+	// 				if (!response.ok) {
+	// 					throw new Error(`Failed to fetch file: ${response.status}`)
+	// 				}
+	// 				const blob = await response.blob()
+	// 				const url = URL.createObjectURL(blob)
+	// 				const a = document.createElement('a')
+	// 				a.href = url
+	// 				a.download = file.originalName || `attachment_${index + 1}`
+	// 				document.body.appendChild(a)
+	// 				a.click()
+	// 				document.body.removeChild(a)
+	// 				// 释放 URL 对象
+	// 				setTimeout(() => URL.revokeObjectURL(url), 100)
+	// 				ElMessage.success('下载成功')
+	// 			} catch (error) {
+	// 				console.error('Download error:', error)
+	// 				ElMessage.error(`下载文件 ${file.originalName} 失败: ${error.message}`)
+	// 			}
+	// 		}
+	// 	})
+	// } else {
+			try {
+			const fileIds = selectedFiles.value.map(i => i.id).join(',')
 			await fjDownloadByByteApi({ attachIds: fileIds }).then(res => {
-				if (res.data.code === 200) {
+			const blob = res.data
+			const url = URL.createObjectURL(blob)
+			const a = document.createElement('a')
+			a.href = url
+			a.download = `巡查报告.zip`
+			document.body.appendChild(a)
+			a.click()
+			document.body.removeChild(a)
+			URL.revokeObjectURL(url)
 					ElMessage.success('下载成功')
-				} else {
-					ElMessage.error(res.msg || '下载失败')
-				}
-			})
-		} catch (e) {
-		} finally {
-			loadingData.close()
-		}
+				})
+			} catch (e) {
+			} finally {
+				// loadingData.close()
+			}
 
-	}
+	// }
 }
 
 // 文档上传
diff --git a/applications/task-work-order/src/views/orderView/orderManage/inspectionReport/inspectionRequestApi.js b/applications/task-work-order/src/views/orderView/orderManage/inspectionReport/inspectionRequestApi.js
index cbca14f..319f662 100644
--- a/applications/task-work-order/src/views/orderView/orderManage/inspectionReport/inspectionRequestApi.js
+++ b/applications/task-work-order/src/views/orderView/orderManage/inspectionReport/inspectionRequestApi.js
@@ -46,10 +46,11 @@
 }
 
 // 批量打包下载
-export const fjDownloadByByteApi = data => {
-	return request({
+export const fjDownloadByByteApi = params => {
+		return request({
 		url: `/blade-resource/attach/downloadByByte`,
-		method: 'post',
-		data,
+		method: 'get',
+		params,
+		responseType: 'blob'
 	})
 }
diff --git a/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue b/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue
index 14c0372..29518ae 100644
--- a/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue
+++ b/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/FormDiaLog.vue
@@ -286,6 +286,7 @@
 async function getFlyerList(index, skilledTaskType) {
 	const res = await gdFlyerPageApi({
 		size: 999,
+		current: 1,
 		skilledTaskType,
 	})
 	flyerListMap.value[index] = res?.data?.data?.records ?? []
diff --git a/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue b/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue
index 2a2648d..73953d4 100644
--- a/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue
+++ b/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue
@@ -134,6 +134,7 @@
 									v-model="formData.patrolRouteUrl"
 									placeholder="请选择"
 									clearable
+									@change="getAirDetails"
 								>
 									<el-option v-for="item in routeOptions" :key="item.id" :label="item.name" :value="item.id" />
 								</el-select>
@@ -371,7 +372,7 @@
 
 // 获取飞手列表
 async function getFlyerList() {
-	const res = await gdFlyerPageApi({ size: 999 })
+	const res = await gdFlyerPageApi({ size: 999,current: 1 })
 	flyerList.value = res?.data?.data?.records ?? []
 }
 
@@ -428,7 +429,14 @@
 	} finally {
 	}
 }
-
+import { ArrowLineMaterialProperty } from '@/utils/cesium/Material'
+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,
+})
 // 获取航线详情
 function getAirDetails() {
 	queryAirById(formData.value.patrolRouteUrl).then(res => {
@@ -439,8 +447,8 @@
 			polyline: {
 				positions: Cesium.Cartesian3.fromDegreesArray(result),
 				clampToGround: true,
-				width: 3,
-				material: Cesium.Color.RED,
+				width: 4,
+				material: arrowLineMaterialProperty,
 			},
 		})
 		// 渲染起点(蓝色)
@@ -531,7 +539,20 @@
 			}
   }
 }
-
+.imgBox {
+	display: flex;
+	flex-wrap: wrap;
+	gap: 10px;
+	> div {
+		width: calc((100% - 40px) / 5); /* 5列布局,减去4个10px间隙 */
+		height: 110px;
+		flex-shrink: 0;
+	}
+	.el-image {
+		width: 100%;
+		height: 100%;
+	}
+}
 :deep(.el-timeline-item__timestamp) {
   font-size: 12px;
   color: #999;
diff --git a/applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue b/applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
index 94c76cd..1da7cd5 100644
--- a/applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
+++ b/applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
@@ -265,6 +265,9 @@
 import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
 import { gdPatrolTaskPageApi } from '@/views/orderView/orderManage/inspectionRequest/inspectionRequestApi'
 import { Check } from '@element-plus/icons-vue'
+import droneIcon from '@/assets/images/orderView/orderManage/drone.png'
+import droneActiveIcon from '@/assets/images/orderView/orderManage/droneActive.png'
+
 // 初始化表单数据
 const initForm = () => ({
 	workOrderName: '',
@@ -302,8 +305,6 @@
 const processList = ref([])
 let viewPlane
 const hasPatrolTaskList = computed(() => ['30', '40', '50', '60'].includes(String(formData.value.workOrderStatus)))
-import droneIcon from '@/assets/images/orderView/orderManage/drone.png'
-import droneActiveIcon from '@/assets/images/orderView/orderManage/droneActive.png'
 
 const gdStatusObj = {
 	'10': { reason: '拒绝原因', operationType: '2' },
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
index 2f4dd8c..53ea264 100644
--- a/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js
+++ b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/agenciesApi.js
@@ -56,6 +56,15 @@
   })
 }
 
+// 导出模板
+export const agenciesExportTemplateApi = () => {
+  return request({
+    url: `/blade-system/dept/export-template`,
+    method: 'get',
+    responseType: 'blob',
+  })
+}
+
 // 懒加载列表
 export const regionLazyTreeApi = params => {
   return request({
diff --git a/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue
index 5621c62..ba5a2c2 100644
--- a/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue
+++ b/applications/task-work-order/src/views/orderView/organizational/agenciesManagement/index.vue
@@ -48,7 +48,7 @@
     <div class="gd-table-toolbar">
       <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增机构</el-button>
       <el-button :icon="Upload" @click="handleImport">导入机构</el-button>
-      <el-button :icon="Download" @click="handleExport">导出机构</el-button>
+      <el-button :icon="Download" :loading="exportLoading" :disabled="exportLoading" @click="handleExport">导出机构</el-button>
     </div>
 
     <div class="gd-table-container" v-loading="loading">
@@ -105,17 +105,26 @@
       @close="handleImportClose"
     >
       <el-form class="gd-dialog-form" ref="importFormRef" :model="importParams" :rules="importRules" label-width="140px">
+        <el-form-item label="导出模板">
+          <el-button 
+            color="#F2F3F5"
+            type="primary" 
+            @click="handleExportTemplate"
+          >
+            点击导出
+          </el-button>
+        </el-form-item>
         <el-form-item label="上传文件" prop="file">
           {{ importFileName }}
           <el-upload class="avatar-uploader" action="" :show-file-list="false" :before-upload="onImportFileBefore">
-            <el-button size="small" type="primary">点击上传</el-button>
+            <el-button  type="primary">点击上传</el-button>
           </el-upload>
         </el-form-item>
       </el-form>
       <template #footer>
         <el-button color="#F2F3F5" @click="isShowImportView = false">取消</el-button>
         <el-button
-          class=""
+         
           color="#4C34FF"
           :loading="importSubmitting"
           :disabled="importSubmitting"
@@ -138,6 +147,7 @@
   agenciesRemoveApi,
   agenciesImportApi,
   agenciesExportApi,
+  agenciesExportTemplateApi,
   regionLazyTreeApi,
   agenciesSubmitApi
 } from './agenciesApi'
@@ -183,6 +193,12 @@
   file: [{ required: true, message: '请上传文件', trigger: ['change'] }],
 })
 
+// 导出加载状态
+const exportLoading = ref(false)
+
+// 模板导出加载状态
+const templateExportLoading = ref(false)
+
 provide('regionOptions', regionOptions)
 provide('dictObj', dictObj)
 
@@ -217,9 +233,6 @@
     resolve([])
   }
 }
-
-
-
 // 获取列表
 async function getList() {
   loading.value = true
@@ -302,8 +315,32 @@
   }
 }
 
+// 导出模板
+async function handleExportTemplate() {
+  templateExportLoading.value = true
+  try {
+    const res = await agenciesExportTemplateApi()
+    const blob = new Blob([res.data])
+    const url = URL.createObjectURL(blob)
+    const a = document.createElement('a')
+    a.href = url
+    a.download = `机构导入模板.xlsx`
+    document.body.appendChild(a)
+    a.click()
+    document.body.removeChild(a)
+    URL.revokeObjectURL(url)
+  } catch (error) {
+    console.error('导出模板失败:', error)
+  } finally {
+    templateExportLoading.value = false
+  }
+}
+
 // 上传文件前处理
 function onImportFileBefore(file) {
+  // 提示请先导出模板
+  // ElMessage.warning('请先导出模板并按模板格式填写数据')
+  
   // 检查文件类型
   const allowedTypes = ['.xlsx', '.xls']
   const fileExtension = file.name.substring(file.name.lastIndexOf('.')).toLowerCase()
@@ -351,8 +388,10 @@
 }
 
 // 导出机构
-function handleExport() {
-  agenciesExportApi(searchParams.value).then(res => {
+async function handleExport() {
+  exportLoading.value = true
+  try {
+    const res = await agenciesExportApi(searchParams.value)
     const blob = new Blob([res.data])
     const url = URL.createObjectURL(blob)
     const a = document.createElement('a')
@@ -363,7 +402,11 @@
     a.click()
     document.body.removeChild(a)
     URL.revokeObjectURL(url)
-  })
+  } catch (error) {
+    console.error('导出失败:', error)
+  } finally {
+    exportLoading.value = false
+  }
 }
 
 onMounted(() => {
diff --git a/applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue b/applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue
index bdcccd5..c1f3dbc 100644
--- a/applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue
+++ b/applications/task-work-order/src/views/orderView/organizational/zoningManagement/index.vue
@@ -40,7 +40,7 @@
     <div class="gd-table-toolbar">
       <el-button :icon="Plus" color="#4C34FF" type="primary" @click="openForm('add')">新增区划</el-button>
       <el-button :icon="Upload" @click="handleImport">导入区划</el-button>
-      <el-button :icon="Download" @click="handleExport">导出区划</el-button>
+      <el-button :icon="Download" :loading="exportLoading" :disabled="exportLoading" @click="handleExport">导出区划</el-button>
     </div>
 
     <div class="gd-table-container" v-loading="loading">
@@ -102,17 +102,27 @@
       @close="handleImportClose"
     >
       <el-form class="gd-dialog-form" ref="importFormRef" :model="importParams" :rules="importRules" label-width="140px">
+        <el-form-item label="导出模板">
+          <!-- :loading="templateExportLoading" 
+            :disabled="templateExportLoading" -->
+          <el-button 
+            type="primary" 
+            color="#F2F3F5"
+            @click="handleExportTemplate"
+          >
+            点击导出
+          </el-button>
+        </el-form-item>
         <el-form-item label="上传文件" prop="file">
           {{ importFileName }}
           <el-upload class="avatar-uploader" action="" :show-file-list="false" :before-upload="onImportFileBefore">
-            <el-button size="small" type="primary">点击上传</el-button>
+            <el-button type="primary">点击上传</el-button>
           </el-upload>
         </el-form-item>
       </el-form>
       <template #footer>
         <el-button color="#F2F3F5" @click="isShowImportView = false">取消</el-button>
         <el-button
-          class=""
           color="#4C34FF"
           :loading="importSubmitting"
           :disabled="importSubmitting"
@@ -135,6 +145,7 @@
   zoningRemoveApi,
   zoningImportApi,
   zoningExportApi,
+  zoningExportTemplateApi,
   regionLazyTreeApi,
   zoningSubmitApi
 } from './zoningApi'
@@ -171,6 +182,12 @@
 const importRules = ref({
   file: [{ required: true, message: '请上传文件', trigger: ['change'] }],
 })
+
+// 导出加载状态
+const exportLoading = ref(false)
+
+// 模板导出加载状态
+const templateExportLoading = ref(false)
 
 provide('regionOptions', regionOptions)
 provide('dictObj', dictObj)
@@ -264,8 +281,32 @@
   }
 }
 
+// 导出模板
+async function handleExportTemplate() {
+  templateExportLoading.value = true
+  try {
+    const res = await zoningExportTemplateApi()
+    const blob = new Blob([res.data])
+    const url = URL.createObjectURL(blob)
+    const a = document.createElement('a')
+    a.href = url
+    a.download = `区划导入模板.xlsx`
+    document.body.appendChild(a)
+    a.click()
+    document.body.removeChild(a)
+    URL.revokeObjectURL(url)
+  } catch (error) {
+    console.error('导出模板失败:', error)
+  } finally {
+    templateExportLoading.value = false
+  }
+}
+
 // 上传文件前处理
 function onImportFileBefore(file) {
+  // 提示请先导出模板
+  // ElMessage.warning('请先导出模板并按模板格式填写数据')
+  
   // 检查文件类型
   const allowedTypes = ['.xlsx', '.xls']
   const fileExtension = file.name.substring(file.name.lastIndexOf('.')).toLowerCase()
@@ -313,18 +354,24 @@
 }
 
 // 导出区划
-function handleExport() {
-  zoningExportApi(searchParams.value).then(res => {
+async function handleExport() {
+  exportLoading.value = true
+  try {
+    const res = await zoningExportApi(searchParams.value)
     const blob = new Blob([res.data])
     const url = URL.createObjectURL(blob)
     const a = document.createElement('a')
     a.href = url
-    a.download = `区划列表${new Date().getTime()}.xlsx`
+    a.download = `区划列表.xlsx`
     document.body.appendChild(a)
     a.click()
     document.body.removeChild(a)
     URL.revokeObjectURL(url)
-  })
+  } catch (error) {
+    console.error('导出失败:', error)
+  } finally {
+    exportLoading.value = false
+  }
 }
 
 onMounted(() => {
diff --git a/applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js b/applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js
index ff818d5..737a506 100644
--- a/applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js
+++ b/applications/task-work-order/src/views/orderView/organizational/zoningManagement/zoningApi.js
@@ -56,6 +56,15 @@
   })
 }
 
+// 导出模板
+export const zoningExportTemplateApi = () => {
+  return request({
+    url: `/blade-system/region/export-template`,
+    method: 'get',
+    responseType: 'blob',
+  })
+}
+
 // 懒加载列表
 export const regionLazyTreeApi = params => {
   return request({
diff --git a/uniapps/work-wx/src/api/index.js b/uniapps/work-wx/src/api/index.js
index ef3f6d0..98c7a21 100644
--- a/uniapps/work-wx/src/api/index.js
+++ b/uniapps/work-wx/src/api/index.js
@@ -175,6 +175,14 @@
   })
 }
 
+// 无人机起飞状态
+export const flyActivityTakeoffStatusApi = () => {
+  return request({
+    url: `/system/dict/data/type/fly_activity_takeoff_status`,
+    method: 'get'
+  })
+}
+
 // 无人机厂商
 export const manufacturerInfoApi = data => {
   return request({
diff --git a/uniapps/work-wx/src/pages/page/index.vue b/uniapps/work-wx/src/pages/page/index.vue
index 4860d82..55e40b8 100644
--- a/uniapps/work-wx/src/pages/page/index.vue
+++ b/uniapps/work-wx/src/pages/page/index.vue
@@ -86,7 +86,7 @@
   import docSvg from '@/static/images/doc.png'
   import lbBottomPng from '@/static/images/lb_bottom.png'
   import { sysNoticePageInfoApi, droneFlightTaskApi } from '@/api/index'
-  import { flightTaskApi,flightRulesApi,flightModeApi,flightPlanApi,flyActivityStatusApi,proTypeApi} from '@/api/index'
+  import { flightTaskApi,flightRulesApi,flightModeApi,flightPlanApi,flyActivityStatusApi,flyActivityTakeoffStatusApi,proTypeApi} from '@/api/index'
 
 import { useAppStore } from "@/store";
 const appStore = useAppStore()
@@ -293,6 +293,19 @@
     })
 }
 
+// 获取飞行状态
+function flyActivityTakeoffStatus() {
+    flyActivityTakeoffStatusApi().then(res => {
+        flyActivityTakeoffStatus.value = res.data.data.map(item => ({
+            ...item,
+            label: item.dictLabel || '暂无',
+            value: item.dictValue || '暂无',
+        }))
+        // 存储到store
+        appStore.setFlyActivityTakeoffStatus(flyActivityTakeoffStatus.value)
+    })
+}
+
 // 获取飞行器类型
 const typeList = ref([])
 function getProTypeApi() {
@@ -318,6 +331,7 @@
   getFlightRules()
   getFlightPlan()
   getFlyActivityStatus()
+  flyActivityTakeoffStatus()
   getProTypeApi()
 })
 </script>
diff --git a/uniapps/work-wx/src/store/modules/app/index.js b/uniapps/work-wx/src/store/modules/app/index.js
index 17e463b..560d41c 100644
--- a/uniapps/work-wx/src/store/modules/app/index.js
+++ b/uniapps/work-wx/src/store/modules/app/index.js
@@ -16,6 +16,7 @@
     flightRules: [], // 飞行规则
     flightPlan: [], // 飞行计划
     flyActivityStatus: [], // 飞行活动状态
+    flyActivityTakeoffStatus: [], // 无人机起飞状态
     proType: [], // 飞行器类型
   }),
   getters: {
@@ -41,6 +42,9 @@
     getFlyActivityStatus (state) {
       return state.flyActivityStatus
     },
+    getFlyActivityTakeoffStatus (state) {
+      return state.flyActivityTakeoffStatus
+    },
     getProType (state) {
       return state.proType
     },
@@ -62,6 +66,9 @@
     setFlyActivityStatus (data) {
       this.flyActivityStatus = data
     },
+    setFlyActivityTakeoffStatus (data) {
+      this.flyActivityTakeoffStatus = data
+    },
     setProType (data) {
       this.proType = data
     },
diff --git a/uniapps/work-wx/src/subPackages/flightApplication/details.vue b/uniapps/work-wx/src/subPackages/flightApplication/details.vue
index 2369026..86a03a6 100644
--- a/uniapps/work-wx/src/subPackages/flightApplication/details.vue
+++ b/uniapps/work-wx/src/subPackages/flightApplication/details.vue
@@ -195,7 +195,54 @@
                 border="none"
             ></u-input>
         </u-form-item>
+        <u-popup :show="applyShow" :round="10" mode="center" @close="close" :closeable="true" :mask-close-able="true">
+		 <u-form labelPosition="top" :model="formParams" :rules="rules" ref="formRef">
+            <u-form-item
+                label="计划申请起飞时间"
+                labelWidth="260rpx"
+                prop="actualTakeoffTime"
+                :borderBottom="true"
+                ref="item1"
+                @click="isShowStartTime = true"
+                required
+        >
+            <u-input v-model="formParams.actualTakeoffTime" border="none" placeholder="请选择" suffixIcon="calendar"></u-input>
+        </u-form-item>
+         <u-form-item
+                label="计划申请结束时间"
+                labelWidth="260rpx"
+                prop="actualLandingTime"
+                :borderBottom="true"
+                ref="item1"
+                @click="isShowEndTime = true"
+                required
+        >
+            <u-input v-model="formParams.actualLandingTime" border="none" placeholder="请选择" suffixIcon="calendar"></u-input>
+        </u-form-item>
+
+        <u-datetime-picker
+            :show="isShowStartTime"
+            v-model="startTime"
+            mode="datetime"
+            :min-date="minDate"
+            :formatter="formatter"
+            @confirm="onConfirmStartTime"
+             @cancel="isShowStartTime = false">
+        </u-datetime-picker>
+        <u-datetime-picker
+            :show="isShowEndTime"
+            v-model="endTime"
+            mode="datetime"
+            :min-date="minDate"
+            :formatter="formatter"
+            @confirm="onConfirmEndTime"
+            @cancel="isShowEndTime = false">
+        </u-datetime-picker>
+         </u-form>
+         <u-button @click="submitApply" color="#1D6FE9">确定</u-button>
+	</u-popup>
     </u-form>
+    <u-button v-if="formParams.planApprovalStatus === '99'" @click="submitApply" color="#1D6FE9">申请</u-button>
 </view>
 </template>
 <script setup>
@@ -232,17 +279,28 @@
 const isShowStartTime = ref(false)
 // 是否显示结束时间选择器
 const isShowEndTime = ref(false)
-// const startTime = ref(Date.now()); // 设置默认值为当前时间戳
-// const endTime = ref(Date.now()); // 设置默认值为当前时间戳
+const minDate = ref(Date.now()); // 设置最小日期为当前时间戳
+const startTime = ref(Date.now()); // 设置默认值为当前时间戳
+const endTime = ref(Date.now()); // 设置默认值为当前时间戳
 
-// const onConfirmStartTime = (e) => {
-//     formParams.value.flightStartTime = dayjs(e.value).format('YYYY-MM-DD HH:mm:ss')
-//     isShowStartTime.value = false
-// }
-// const onConfirmEndTime = (e) => {
-//     formParams.value.flightEndTime = dayjs(e.value).format('YYYY-MM-DD HH:mm:ss')
-//     isShowEndTime.value = false
-// }
+const onConfirmStartTime = (e) => {
+    formParams.value.actualTakeoffTime = dayjs(e.value).format('YYYY-MM-DD HH:mm')
+    isShowStartTime.value = false
+}
+const onConfirmEndTime = (e) => {
+    formParams.value.actualLandingTime = dayjs(e.value).format('YYYY-MM-DD HH:mm')
+    isShowEndTime.value = false
+}
+
+
+const formatter = (type, value) => {
+    if (type === 'year') return `${value}年`;
+    if (type === 'month') return `${value}月`;
+    if (type === 'day') return `${value}日`;
+    if (type === 'hour') return `${value}时`;
+    if (type === 'minute') return `${value}分`;
+    return value;
+};
 
 
 
@@ -306,6 +364,19 @@
     })
 }
 
+// 申请
+const applyShow = ref(false)
+function submitApply() {
+   applyShow.value = true
+}
+
+function close() {
+    // 清空时间
+    formParams.value.actualTakeoffTime = ''
+    formParams.value.actualLandingTime = ''
+    applyShow.value = false
+}
+
 onLoad((options) => {
      flightPlanDetailsApi(options.id).then(res => {
         // return
diff --git a/uniapps/work-wx/src/subPackages/flightApplication/index.vue b/uniapps/work-wx/src/subPackages/flightApplication/index.vue
index f060e70..d2a747b 100644
--- a/uniapps/work-wx/src/subPackages/flightApplication/index.vue
+++ b/uniapps/work-wx/src/subPackages/flightApplication/index.vue
@@ -29,10 +29,11 @@
                             {{ approvalRecord.planApprovalStatusLabel || ''}}
                         </view>
                     </view>
+                    <view class="txt">计划起飞状态:{{approvalRecord.fly_activity_takeoff_status || ''}}</view>
                     <view class="txt">飞行器:{{approvalRecord.spacecraftLabel || '无'}}</view>
                     <view class="txt">飞行任务:{{approvalRecord.flightTaskTypeLabel || ''}}</view>
-                    <view class="txt">飞行起飞时间:{{approvalRecord.actualTakeoffTime || ''}}</view>
-                    <view class="txt">飞行降落时间:{{approvalRecord.actualLandingTime || ''}}</view>
+                    <view class="txt">飞行起飞时间:{{approvalRecord.flightStartTime || ''}}</view>
+                    <view class="txt">飞行降落时间:{{approvalRecord.flightEndTime || ''}}</view>
                 </view>
             </u-list>
         </view>
@@ -115,6 +116,7 @@
         newData.forEach(item => {
             item.spacecraftLabel = aircraftInfoList.value.find(info => info.value === item.spacecraftId)?.label || '无'
             item.planApprovalStatusLabel = appStore.flyActivityStatus.find(status => status.dictValue === item.planApprovalStatus)?.dictLabel || '无'
+            item.flyActivityTakeoffStatusLabel = appStore.flyActivityTakeoffStatus.find(status => status.dictValue === item.planStatus)?.dictLabel || '无'
             item.flightTaskTypeLabel = appStore.taskType.find(type => type.dictValue === item.flightTaskType)?.dictLabel || '无'
         })
         // 如果是第一页,直接替换数据;否则追加数据

--
Gitblit v1.9.3