From 794dfadf25327ed83f87fb63bacf6d86c80563d3 Mon Sep 17 00:00:00 2001
From: 张含笑 <zhx18749296735@163.com>
Date: Tue, 27 Jan 2026 14:51:50 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'
---
applications/drone-command/src/views/dataCockpit/components/RealWarning.vue | 4
applications/drone-command/src/components/map-container/device-map-container.vue | 123 ++++++++++++++++++++++++++++--
applications/drone-command/src/views/permissionManage/permissionDept/FormDiaLog.vue | 31 +++++--
applications/task-work-order/src/styles/common/cockpit.scss | 24 +++--
applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue | 58 +-------------
5 files changed, 155 insertions(+), 85 deletions(-)
diff --git a/applications/drone-command/src/components/map-container/device-map-container.vue b/applications/drone-command/src/components/map-container/device-map-container.vue
index a4311a4..f7fb7e9 100644
--- a/applications/drone-command/src/components/map-container/device-map-container.vue
+++ b/applications/drone-command/src/components/map-container/device-map-container.vue
@@ -119,6 +119,14 @@
let droneTrackTickHandler = null
let droneTrackStartTime = null
let droneTrackRuntime = []
+let favoritePulseRafId = null
+let favoritePulseStartAt = 0
+let favoritePulseElapsed = 0
+const favoritePulseEntities = []
+const pulseBaseColor = Cesium.Color.fromCssColorString('#FF3B30')
+const PULSE_MIN_RADIUS_M = 60
+const PULSE_MAX_RADIUS_M = 180
+const PULSE_DURATION_S = 1.6
const detailVisible = ref(true)
const clusterVisible = ref(false)
const countyCenterMap = new Map()
@@ -172,6 +180,57 @@
*/
const adminBoundaryVisible = ref(treeCheckedKeys.value.includes('admin'))
+
+const isFavorited = item => {
+ const value = item?.favorited ?? item?.isFavorite
+ return value === 1 || value === '1' || value === true
+}
+
+let pulseCanvas = null
+const getPulseCanvas = () => {
+ if (pulseCanvas) return pulseCanvas
+ const size = 256
+ const canvas = document.createElement('canvas')
+ canvas.width = size
+ canvas.height = size
+ const ctx = canvas.getContext('2d')
+ const center = size / 2
+ const radius = center - 2
+ const gradient = ctx.createRadialGradient(center, center, radius * 0.1, center, center, radius)
+ gradient.addColorStop(0, 'rgba(255,59,48,0.55)')
+ gradient.addColorStop(0.35, 'rgba(255,59,48,0.35)')
+ gradient.addColorStop(0.7, 'rgba(255,59,48,0.15)')
+ gradient.addColorStop(1, 'rgba(255,59,48,0)')
+ ctx.fillStyle = gradient
+ ctx.beginPath()
+ ctx.arc(center, center, radius, 0, Math.PI * 2)
+ ctx.fill()
+ pulseCanvas = canvas
+ return canvas
+}
+
+const createFavoritePulseEntity = (billboard) => {
+ if (!viewer || !billboard) return
+ const material = new Cesium.ImageMaterialProperty({
+ image: getPulseCanvas(),
+ transparent: true,
+ })
+ const pulse = {
+ material,
+ offset: Math.random() * PULSE_DURATION_S,
+ entity: null,
+ }
+ pulse.entity = viewer.entities.add({
+ position: new Cesium.CallbackProperty(() => billboard.position, false),
+ ellipse: {
+ semiMajorAxis: new Cesium.CallbackProperty(() => getPulseRadius(pulse), false),
+ semiMinorAxis: new Cesium.CallbackProperty(() => getPulseRadius(pulse), false),
+ material,
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+ },
+ })
+ favoritePulseEntities.push(pulse)
+}
const getDevicePosition = item => {
const longitudeRaw = item.longitude ?? item.lng ?? item.lon
@@ -311,6 +370,12 @@
}
}
+const getPulseRadius = pulse => {
+ const phase = ((favoritePulseElapsed + pulse.offset) % PULSE_DURATION_S) / PULSE_DURATION_S
+ const wave = phase < 0.5 ? phase * 2 : (1 - phase) * 2
+ return PULSE_MIN_RADIUS_M + (PULSE_MAX_RADIUS_M - PULSE_MIN_RADIUS_M) * wave
+}
+
const setDetailVisibility = visible => {
detailVisible.value = visible
if (defenseZonePrimitive) defenseZonePrimitive.show = visible
@@ -345,6 +410,11 @@
const setDroneVisibility = visible => {
if (droneTrackBillboardCollection) droneTrackBillboardCollection.show = visible
if (droneTrackPolylineCollection) droneTrackPolylineCollection.show = visible
+ if (favoritePulseEntities.length) {
+ favoritePulseEntities.forEach(pulse => {
+ if (pulse?.entity) pulse.entity.show = visible
+ })
+ }
if (!visible && selectedTargetType.value === 'drone') {
closePopup()
}
@@ -453,6 +523,7 @@
const clearDroneTrackEntities = () => {
if (!viewer) return
stopDroneTrackAnimation()
+ stopFavoritePulseAnimation()
if (droneTrackBillboardCollection) {
if (!droneTrackBillboardCollection.isDestroyed?.()) {
removeCockpitPrimitive(droneTrackBillboardCollection)
@@ -466,6 +537,12 @@
droneTrackPolylineCollection = null
}
droneTrackRuntime = []
+ if (favoritePulseEntities.length) {
+ favoritePulseEntities.forEach(pulse => {
+ if (pulse?.entity) viewer?.entities?.remove(pulse.entity)
+ })
+ favoritePulseEntities.length = 0
+ }
dronePickMap.clear()
if (selectedTargetType.value === 'drone') {
closePopup()
@@ -532,6 +609,28 @@
droneTrackTickHandler = null
}
+const startFavoritePulseAnimation = () => {
+ if (!viewer || favoritePulseRafId) return
+ favoritePulseStartAt = performance.now()
+ const tick = now => {
+ if (!viewer || viewer.isDestroyed?.() || !favoritePulseEntities.length) {
+ favoritePulseRafId = null
+ return
+ }
+ favoritePulseElapsed = (now - favoritePulseStartAt) / 1000
+ viewer.scene.requestRender()
+ favoritePulseRafId = requestAnimationFrame(tick)
+ }
+ favoritePulseRafId = requestAnimationFrame(tick)
+}
+
+const stopFavoritePulseAnimation = () => {
+ if (favoritePulseRafId) {
+ cancelAnimationFrame(favoritePulseRafId)
+ favoritePulseRafId = null
+ }
+}
+
const renderSimulatedDroneTrack = (list) => {
if (!viewer) return
clearDroneTrackEntities()
@@ -576,16 +675,19 @@
})
billboard.id = droneId
const speedMs = Math.round(Cesium.Cartesian3.distance(positions[0], positions[1]) / segmentDuration)
- dronePickMap.set(droneId, {
- data: {
- ...item,
- flightHeightM: item.flightHeightM ?? points[0].height,
- flightSpeedMs: item.flightSpeedMs ?? speedMs,
- longitude: item.longitude ?? points[0].longitude,
- latitude: item.latitude ?? points[0].latitude,
- },
- billboard,
- })
+ dronePickMap.set(droneId, {
+ data: {
+ ...item,
+ flightHeightM: item.flightHeightM ?? points[0].height,
+ flightSpeedMs: item.flightSpeedMs ?? speedMs,
+ longitude: item.longitude ?? points[0].longitude,
+ latitude: item.latitude ?? points[0].latitude,
+ },
+ billboard,
+ })
+ if (isFavorited(item)) {
+ createFavoritePulseEntity(billboard)
+ }
droneTrackRuntime.push({
positions,
billboard,
@@ -595,6 +697,7 @@
})
viewer.clock.multiplier = 1
startDroneTrackAnimation()
+ startFavoritePulseAnimation()
reorderCockpitPrimitives()
}
diff --git a/applications/drone-command/src/views/dataCockpit/components/RealWarning.vue b/applications/drone-command/src/views/dataCockpit/components/RealWarning.vue
index 9125f86..09d99e6 100644
--- a/applications/drone-command/src/views/dataCockpit/components/RealWarning.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/RealWarning.vue
@@ -2,7 +2,7 @@
* @Author : yuan
* @Date : 2026-01-08 09:29:07
* @LastEditors : yuan
- * @LastEditTime : 2026-01-27 12:01:37
+ * @LastEditTime : 2026-01-27 14:49:56
* @FilePath : \applications\drone-command\src\views\dataCockpit\components\RealWarning.vue
* @Description :
* Copyright 2026 OBKoro1, All Rights Reserved.
@@ -10,7 +10,7 @@
-->
<template>
<div class="real-warning" v-loading="props.loading" element-loading-background="rgba(5, 5, 15, 0.6)"
- element-loading-text="???...">
+ element-loading-text="加载中...">
<div class="list-content">
<EmptyState v-if="!props.data.length && !props.loading" />
<RealTemplate v-else v-for="(item, ind) in props.data" :key="ind" :data="item"
diff --git a/applications/drone-command/src/views/permissionManage/permissionDept/FormDiaLog.vue b/applications/drone-command/src/views/permissionManage/permissionDept/FormDiaLog.vue
index 95955ad..37310d6 100644
--- a/applications/drone-command/src/views/permissionManage/permissionDept/FormDiaLog.vue
+++ b/applications/drone-command/src/views/permissionManage/permissionDept/FormDiaLog.vue
@@ -71,14 +71,14 @@
<el-col :span="12">
<el-form-item label="部门类型" prop="deptCategory">
<el-select class="command-select" popper-class="command-select-popper" v-model="formData.deptCategory" placeholder="请选择" clearable>
- <el-option v-for="item in dictObj.org_category" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" />
+ <el-option v-for="item in dictObj.org_category" :key="item.dictKey" :label="item.dictValue" :value="Number(item.dictKey)" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="部门性质" prop="deptNature">
<el-select class="command-select" popper-class="command-select-popper" v-model="formData.deptNature" placeholder="请选择" clearable>
- <el-option v-for="item in dictObj.org_nature" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" />
+ <el-option v-for="item in dictObj.org_nature" :key="item.dictKey" :label="item.dictValue" :value="Number(item.dictKey)" />
</el-select>
</el-form-item>
</el-col>
@@ -137,9 +137,9 @@
fullName: '',
parentId: '',
parentName: '',
- deptCategory: '',
- deptNature: '',
- areaCode: '',
+ deptCategory: null,
+ deptNature: null,
+ areaCode: [],
areaName: '',
deploymentMode: 0,
sort: 0,
@@ -154,7 +154,7 @@
const areaProps = {
label: 'title',
value: 'value',
- emitPath: false,
+ emitPath: true,
checkStrictly: true,
lazy: true,
lazyLoad(node, resolve) {
@@ -230,19 +230,26 @@
}
function getFullAreaCode(areaCode) {
- if (!areaCode) return ''
+ if (!areaCode) return []
+ if (Array.isArray(areaCode)) return areaCode
const code = areaCode.toString()
- if (code.includes(',')) return code
+ if (code.includes(',')) return code.split(',')
if (code.endsWith('0000000000')) {
- return code
+ return [code]
} else if (code.endsWith('00000000') && !code.endsWith('0000000000')) {
const provinceCode = code.substring(0, 2) + '0000000000'
- return `${provinceCode},${code}`
+ return [provinceCode, code]
} else {
const provinceCode = code.substring(0, 2) + '0000000000'
const cityCode = code.substring(0, 4) + '00000000'
- return `${provinceCode},${cityCode},${code}`
+ return [provinceCode, cityCode, code]
}
+}
+
+function normalizeNumber(value) {
+ if (value === '' || value === null || value === undefined) return null
+ const num = Number(value)
+ return Number.isNaN(num) ? null : num
}
function handleCancel() {
@@ -283,6 +290,8 @@
const data = res?.data?.data ?? {}
formData.value = {
...data,
+ deptCategory: normalizeNumber(data.deptCategory),
+ deptNature: normalizeNumber(data.deptNature),
areaCode: getFullAreaCode(data.areaCode),
}
}
diff --git a/applications/task-work-order/src/styles/common/cockpit.scss b/applications/task-work-order/src/styles/common/cockpit.scss
index e84887a..3c10b27 100644
--- a/applications/task-work-order/src/styles/common/cockpit.scss
+++ b/applications/task-work-order/src/styles/common/cockpit.scss
@@ -475,6 +475,7 @@
.detail-container {
display: flex;
justify-content: space-between;
+ height: 680px;
.detail-left {
margin-right: 20px;
@@ -484,16 +485,21 @@
}
.detail-right {
- &>div {
- .detail-title {
- margin: 10px 0;
- }
+ width: 920px;
+ flex: 1;
+ overflow: auto;
+ display: flex;
+ flex-direction: column;
- &:first-child {
- .detail-title {
- margin-top: 0;
- }
- }
+ .view-container {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .gd-cesium {
+ width: 100%;
+ min-height: 380px;
+ flex: 1;
}
}
}
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 ccbe7a3..89a3c4e 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
@@ -5,11 +5,10 @@
:title="titleEnum[dialogMode]"
@closed="visible = false"
destroy-on-close
- width="auto"
:close-on-click-modal="false"
>
- <div class="content" style="display: flex">
- <div class="processBox" v-if="dialogMode !== 'add' && processList.length">
+ <div class="detail-container" style="display: flex">
+ <div class="detail-left" v-if="dialogMode !== 'add' && processList.length">
<el-timeline class="gd-timeline">
<el-timeline-item
v-for="(activity, index) in processList"
@@ -27,9 +26,10 @@
</el-timeline-item>
</el-timeline>
</div>
- <div class="leftBox">
+
+ <div class="detail-right">
<!-- 查看模式 -->
- <div class="detail-container" v-if="dialogReadonly">
+ <div class="view-container" v-if="dialogReadonly">
<div class="detail-title">工单详情</div>
<el-row class="detail-row-view">
<el-col :span="12">
@@ -507,52 +507,4 @@
</script>
<style lang="scss" scoped>
-.content {
- display: flex;
- gap: 0 20px;
- height: 680px;
-
- .leftBox {
- width: 920px;
- flex: 1;
- overflow: auto;
- display: flex;
- flex-direction: column;
- .gd-cesium {
- width: 100%;
- min-height: 380px;
- flex: 1;
- }
- }
- .processBox {
- width: 312px;
- padding: 24px 0;
- border: 1px solid #e4e4e4;
- overflow: auto;
-
- .item-content {
- position: relative;
- .flowName {
- width: 80px;
- position: absolute;
- left: -120px;
- top: 0px;
- }
- }
- }
- .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%;
- }
- }
-}
</style>
--
Gitblit v1.9.3