From f04538d194d146620db0f7ac2d66a9cb4d8c2e29 Mon Sep 17 00:00:00 2001 From: Zzc <1373857752@qq.com> Date: Thu, 20 Nov 2025 16:58:12 +0800 Subject: [PATCH 1/8] =?UTF-8?q?chore(screen):=20=E6=B7=BB=E5=8A=A03D?= =?UTF-8?q?=E6=80=81=E5=8A=BF=E6=84=9F=E7=9F=A5=E5=9B=BE=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/images/储备中心.png | Bin 0 -> 2706 bytes .../assets/images/应急中心icon定位.png | Bin 0 -> 4143 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/储备中心.png create mode 100644 packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/应急中心icon定位.png diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/储备中心.png b/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/储备中心.png new file mode 100644 index 0000000000000000000000000000000000000000..67c9041343e9cb70ce6df58cf7f75b3bd398e724 GIT binary patch literal 2706 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 literal 0 HcmV?d00001 From 662930bdeb91481874c4b9fe59f8ed40f9414f14 Mon Sep 17 00:00:00 2001 From: Zzc <1373857752@qq.com> Date: Thu, 20 Nov 2025 16:59:33 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat(screen):=20=E4=B8=BA3D=E6=80=81?= =?UTF-8?q?=E5=8A=BF=E6=84=9F=E7=9F=A5=E6=B7=BB=E5=8A=A0=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E6=A8=A1=E6=80=81=E6=A1=86=E5=92=8C=E4=B8=AD=E5=BF=83=E6=A0=87?= =?UTF-8?q?=E8=AE=B0=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在左侧面板中集成视频模态框功能,并添加摄像头点击处理器 - 在地图上添加中心点和预设点标记,并通过API加载数据 - 更新视频模态框界面,改进控制台布局和样式 - 调整组件样式以获得更好的响应性和布局效果 - 更新模型对比配置中的3D瓦片URL --- .../components/LeftPanel/LocationPanel.vue | 6 +- .../components/LeftPanel/index.vue | 54 +++- .../components/PageHeader.vue | 2 +- .../components/RightPanel/VideoModal.vue | 287 +++++++++--------- .../RightPanel/VideoMonitorItem.vue | 11 +- .../components/RightPanel/index.vue | 2 + .../components/shared/DataField.vue | 6 +- .../composables/useMapMarkers.js | 109 ++++++- .../config/modelCompare.config.js | 2 +- .../3DSituationalAwarenessRefactor/index.vue | 116 +++++-- 10 files changed, 417 insertions(+), 178 deletions(-) diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/LocationPanel.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/LocationPanel.vue index 7d5ef5c..1599775 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/LocationPanel.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/LocationPanel.vue @@ -66,13 +66,13 @@ const locationInfo = [ .location-panel { width: 100%; - padding: clamp(12px, vh(16), 20px) clamp(16px, vw(20), 28px); + padding: clamp(16px, vh(16), 18px) clamp(6px, vw(6), 6px); background: url('../../assets/images/LocationPanel/地理位置内容背景.png') no-repeat center center; background-size: 100% 100%; border-radius: vw(8); box-shadow: 0 0 vw(12) rgba(0, 0, 0, 0.35); overflow: hidden; - min-width: 240px; + min-width: 248px; max-width: 100%; // min-height: 160px; } @@ -82,7 +82,7 @@ const locationInfo = [ display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-rows: repeat(2, minmax(0, 1fr)); - gap: clamp(4px, vh(6), 8px) clamp(4px, vw(6), 8px); + gap: clamp(2px, vh(4), 6px) 0px; width: 100%; height: 100%; } diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/index.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/index.vue index 03765eb..c9b1330 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/index.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/LeftPanel/index.vue @@ -7,6 +7,7 @@ src="../../assets/images/摄像头.png" alt="摄像头" class="camera-icon" + @click.stop="handleCameraClick" /> @@ -55,6 +56,14 @@ + + + @@ -65,6 +74,7 @@ import DisasterAnalysis from './DisasterAnalysis.vue' import ForcePreset from './ForcePreset.vue' import ForceDispatch from './ForceDispatch.vue' import LocationPanel from './LocationPanel.vue' +import VideoModal from '../RightPanel/VideoModal.vue' const isLocationOpen = ref(true) @@ -72,6 +82,30 @@ const toggleLocation = () => { isLocationOpen.value = !isLocationOpen.value } +// 视频弹窗状态 +const showVideoModal = ref(false) +const cameraVideoData = ref({ + id: 999, + type: 'drone', + title: '现场实时监控', + videoSrc: '', // 预留视频链接,后续补充 + dateRange: '', + hasAudio: true, + hasMegaphone: false, + hasZoom: false, + hasDirectionControl: false +}) + +// 打开视频弹窗 +const handleCameraClick = () => { + showVideoModal.value = true +} + +// 关闭视频弹窗 +const handleCloseVideoModal = () => { + showVideoModal.value = false +} + // 定义对外事件 const emit = defineEmits(['start-dispatch']) @@ -101,6 +135,8 @@ const handleStartDispatch = (payload) => { overflow-y: auto; overscroll-behavior: contain; padding-left: vw(40); // 为左侧折叠按钮预留空间 + padding-top: var(--sa-header-height); + box-sizing: border-box; &::-webkit-scrollbar { width: vw(6); @@ -122,7 +158,7 @@ const handleStartDispatch = (payload) => { .location-entry { position: absolute; - top: vh(32); + top: var(--sa-header-height); left: 100%; margin-left: vw(12); display: flex; @@ -188,15 +224,21 @@ const handleStartDispatch = (payload) => { } .camera-icon { - width: vw(20); - height: vw(20); - margin-left: vw(6); + width: vw(30); + height: vw(30); + flex-shrink: 0; + object-fit: contain; + align-self: center; cursor: pointer; - transition: opacity 0.2s ease; - vertical-align: middle; + transition: all 0.2s ease; &:hover { opacity: 0.8; + transform: scale(1.1); + } + + &:active { + transform: scale(0.95); } } diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/PageHeader.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/PageHeader.vue index 68ee91e..0081666 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/PageHeader.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/PageHeader.vue @@ -62,7 +62,7 @@ const handleBack = () => { align-items: center; gap: vw(10); padding: vh(10) vw(24); - margin-top: vh(20); + margin-top: 50px; background: url('../assets/images/返回按钮.png') 0 -1px no-repeat; background-size: 100% 100%; border: none; diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/RightPanel/VideoModal.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/RightPanel/VideoModal.vue index 09774af..dd17f4d 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/RightPanel/VideoModal.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/RightPanel/VideoModal.vue @@ -40,129 +40,132 @@ muted controls /> - - -
- 操作台背景 + +
+ + 操作台 -
- -
- 喊话 - 喊话 -
+ +
- -
- 声音 - 声音 -
+ +
+ 喊话 + 喊话 +
- -
- 左移 - 视觉左移 -
+ +
+ 声音 + 声音 +
- -
- 右移 - 视觉右移 -
+ +
+ 左移 + 视觉左移 +
- -
- 上移 - 视觉上移 -
+ +
+ 右移 + 视觉右移 +
- -
- 下移 - 视觉下移 -
+ +
+ 上移 + 视觉上移 +
- -
- 缩小 - 缩小 + +
+ 下移 + 视觉下移 +
+ + +
+ 缩小 + 缩小 +
@@ -348,6 +351,7 @@ body.body--no-scroll { background: rgba(0, 0, 0, 0.6); padding: clamp(10px, 1.5vh, 20px); overflow: hidden; + position: relative; } &__video { @@ -359,49 +363,50 @@ body.body--no-scroll { border-radius: vw(8); } - // 操作台 + // 操作台容器 &__console { - flex-shrink: 0; position: absolute; - bottom: 80px; + bottom: 100px; left: 50%; transform: translateX(-50%); - height: clamp(80px, 10vh, 120px); + z-index: 10; display: flex; align-items: center; - justify-content: center; + gap: clamp(10px, 1.5vw, 20px); } + // 轮盘图标 &__console-bg { - // position: absolute; - top: 0; - left: 0; - // width: 100%; - height: 85%; - object-fit: cover; - z-index: 0; + width: clamp(120px, 7vw, 140px); + height: clamp(120px, 7vw, 140px); + object-fit: contain; + flex-shrink: 0; } + // 控制按钮容器 &__control-bar { - position: relative; - z-index: 1; display: flex; align-items: center; - justify-content: center; - gap: clamp(15px, 2vw, 30px); - padding: 0 clamp(20px, 2vw, 40px); + gap: clamp(20px, 2.5vw, 40px); + padding: clamp(8px, 1vh, 12px) clamp(20px, 2.5vw, 40px); + background: rgba(0, 0, 0, 0.75); + border-radius: 50px; + backdrop-filter: blur(10px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); } &__control-item { display: flex; - flex-direction: column; + flex-direction: row; align-items: center; - gap: clamp(4px, 0.5vh, 8px); + gap: clamp(6px, 0.8vw, 10px); cursor: pointer; - transition: transform 0.2s ease; + transition: transform 0.2s ease, opacity 0.2s ease; + padding: clamp(4px, 0.5vh, 8px); &:hover { - transform: scale(1.1); + transform: scale(1.15); + opacity: 0.9; } &:active { @@ -411,22 +416,24 @@ body.body--no-scroll { &:focus-visible { outline: 2px solid rgba(135, 206, 250, 0.8); outline-offset: 4px; - border-radius: vw(4); + border-radius: clamp(4px, 0.5vw, 8px); } } &__control-icon { - width: clamp(24px, 2.5vw, 36px); - height: clamp(24px, 2.5vw, 36px); + width: clamp(28px, 3vw, 42px); + height: clamp(28px, 3vw, 42px); object-fit: contain; + filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3)); } &__control-label { - font-size: clamp(11px, 1vw, 13px); + font-size: clamp(12px, 1.1vw, 14px); font-family: SourceHanSansCN-Regular, sans-serif; color: #ffffff; white-space: nowrap; line-height: 1.2; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); } } diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/RightPanel/VideoMonitorItem.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/RightPanel/VideoMonitorItem.vue index 5867cdf..c97df57 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/RightPanel/VideoMonitorItem.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/RightPanel/VideoMonitorItem.vue @@ -225,8 +225,8 @@ onUnmounted(() => { top: -1px; left: -3px; z-index: 2; - width: clamp(140px, 10.1vw, 194px); - height: clamp(28px, vh(32), 36px); + width: clamp(140px, 10.1vw, 154px); + height: clamp(18px, vh(18), 18px); background: url("../../assets/images/video-time-bg.png") no-repeat center center; background-size: 100% 100%; @@ -275,9 +275,10 @@ onUnmounted(() => { transform-origin: center; &__icon { - width: clamp(12px, vw(14), 18px); - height: clamp(12px, vh(14), 18px); - margin-right: clamp(4px, vw(6), 8px); + width: clamp(20px, vw(20), 24px); + // height: clamp(14px, vw(14), 18px); + height: auto; + // margin-right: clamp(4px, vw(6), 8px); flex-shrink: 0; object-fit: contain; } diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/RightPanel/index.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/RightPanel/index.vue index 01c4c55..480ab12 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/RightPanel/index.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/RightPanel/index.vue @@ -41,6 +41,8 @@ import CollaborationInfo from './CollaborationInfo.vue' overflow-y: auto; overscroll-behavior: contain; padding-right: vw(40); // 为右侧折叠按钮预留空间 + padding-top: var(--sa-header-height); + box-sizing: border-box; &::-webkit-scrollbar { width: vw(6); diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/shared/DataField.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/shared/DataField.vue index 8b8b28b..fbbf00f 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/shared/DataField.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/shared/DataField.vue @@ -60,16 +60,16 @@ const valueClass = computed(() => { &__label { color: var(--text-white); font-size: fs(14); - font-family: SourceHanSansCN-Medium, sans-serif; + // font-family: SourceHanSansCN-Medium, sans-serif; font-weight: 500; white-space: nowrap; } &__value { color: var(--text-white); - font-size: fs(14); + font-size: fs(15); font-family: PingFangSC-Semibold, sans-serif; - font-weight: 600; + font-weight: 700; flex: 1; text-align: right; } diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useMapMarkers.js b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useMapMarkers.js index ef51416..eefbab7 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useMapMarkers.js +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useMapMarkers.js @@ -6,7 +6,8 @@ import { cesiumDataConfig } from '../config/cesiumData' import soldierIcon from '../assets/images/SketchPngfbec927027ff9e49207749ebaafd229429315341fda199251b6dfb1723ff17fb.png' import deviceIcon from '../assets/images/SketchPng860d54f2a31f5f441fc6a88081224f1e98534bf6d5ca1246e420983bdf690380.png' import emergencyBaseIcon from '../assets/images/应急基地.png' -import emergencyCenterIcon from '../assets/images/应急中心.png' +import emergencyCenterIcon from '../assets/images/应急中心icon定位.png' +import reserveCenterIcon from '../assets/images/储备中心.png' // 默认高度偏移(米)- 与 WuRenJi 保持一致 const DEFAULT_HEIGHT_OFFSET = 100 @@ -19,6 +20,7 @@ export function useMapMarkers() { const collapseAreaEntities = ref([]) const markerEntities = ref([]) const emergencyResourceEntities = ref([]) // 应急资源标记(由API数据动态生成) + const reserveCenterEntities = ref([]) // 储备中心和预置点标记 /** * 获取塌陷区域的所有位置点 @@ -574,6 +576,108 @@ export function useMapMarkers() { emergencyResourceEntities.value = [] } + /** + * 清除储备中心和预置点标记 + * @param {Cesium.Viewer} viewer + */ + const clearReserveCenterMarkers = (viewer) => { + if (!viewer) return + + console.log(`[useMapMarkers] 清除 ${reserveCenterEntities.value.length} 个储备中心/预置点标记`) + + reserveCenterEntities.value.forEach(entity => { + if (entity) viewer.entities.remove(entity) + }) + reserveCenterEntities.value = [] + } + + /** + * 根据API数据添加储备中心和预置点标记 + * @param {Cesium.Viewer} viewer + * @param {Array} reserveData - API返回的储备中心和预置点数据列表 + * @param {string} reserveData[].gl1Id - ID + * @param {string} reserveData[].gl1Yjllmc - 名称 + * @param {number} reserveData[].gl1Lng - 经度 + * @param {number} reserveData[].gl1Lat - 纬度 + * @param {string} reserveData[].gl1Qxmc - 区县名称 + * @param {string} reserveData[].gl1Rysl - 人员数量 + * @param {string} reserveData[].gl1Zdmj - 占地面积 + * @param {string} reserveData[].gl1Lx - 类型 (2=储备中心, 3=预置点) + * @param {Object} options - 配置选项 + * @param {number} [options.heightOffset=10] - 相对地面的高度偏移(米) + * @returns {Promise} + */ + const addReserveCenterMarkers = async (viewer, reserveData, options = {}) => { + if (!viewer) { + console.warn('[useMapMarkers] addReserveCenterMarkers: viewer 为空') + return + } + + if (!Array.isArray(reserveData) || reserveData.length === 0) { + console.warn('[useMapMarkers] addReserveCenterMarkers: reserveData 为空或不是数组') + return + } + + const markerOptions = { + heightOffset: options.heightOffset ?? DEFAULT_HEIGHT_OFFSET + } + + console.log('[useMapMarkers] 开始添加储备中心和预置点标记...', reserveData.length) + + const entities = [] + + for (const item of reserveData) { + if (!item.gl1Lat || !item.gl1Lng) { + console.warn('[useMapMarkers] 数据缺少坐标信息:', item) + continue + } + + const result = await resolveCartesianFromDegrees( + viewer, + item.gl1Lng, + item.gl1Lat, + markerOptions.heightOffset, + false + ) + + // 根据类型选择图标和类型标识 + // gl1Lx: 2=储备中心, 3=预置点 + const itemType = String(item.gl1Lx).trim() + const isReserveCenter = itemType === '2' + const icon = isReserveCenter ? reserveCenterIcon : emergencyBaseIcon + const type = isReserveCenter ? 'reserveCenter' : 'presetPoint' + + const entity = viewer.entities.add({ + position: result.position, + billboard: { + image: icon, + width: 48, + height: 48, + verticalOrigin: Cesium.VerticalOrigin.BOTTOM, + heightReference: resolveBillboardHeightReference(result.samplingSucceeded), + disableDepthTestDistance: Number.POSITIVE_INFINITY + }, + properties: { + type, + id: item.gl1Id, + name: item.gl1Yjllmc || (isReserveCenter ? '储备中心' : '预置点'), + district: item.gl1Qxmc || '-', + personnelCount: item.gl1Rysl || '0', + area: item.gl1Zdmj || '-' + } + }) + entities.push(entity) + } + + reserveCenterEntities.value = entities + console.log(`[useMapMarkers] 添加储备中心/预置点标记 ${entities.length} 个`) + + // 强制渲染场景,确保 CLAMP_TO_GROUND 立即生效 + if (entities.length > 0) { + viewer.scene.requestRender() + } + } + /** * 根据API数据添加应急资源标记(养护站) * @param {Cesium.Viewer} viewer @@ -665,6 +769,7 @@ export function useMapMarkers() { collapseAreaEntities, markerEntities, emergencyResourceEntities, + reserveCenterEntities, initializeMarkers, clearMarkers, setMarkersSplitDirection, @@ -675,6 +780,8 @@ export function useMapMarkers() { addRandomMarkers, addEmergencyResourceMarkers, clearEmergencyResourceMarkers, + addReserveCenterMarkers, + clearReserveCenterMarkers, getCollapseCenter: calculateCollapseCenter // 提前获取中心点,不依赖标记初始化 } } diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/config/modelCompare.config.js b/packages/screen/src/views/3DSituationalAwarenessRefactor/config/modelCompare.config.js index d1b17c1..b5cece5 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/config/modelCompare.config.js +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/config/modelCompare.config.js @@ -44,7 +44,7 @@ export const AFTER_3DTILES_CONFIG = { // 3D Tiles 服务 URL // url: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/model/S107/terra_b3dms/tileset.json', - url: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/model/ylzg/zxyj1119/terra_b3dms/tileset.json', + url: 'http://222.212.85.86:9000/300bdf2b-a150-406e-be63-d28bd29b409f/model/ylzg/zxyj1120/terra_b3dms/tileset.json', // 默认可见性(初始化时灾后模型默认显示) diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue index 1c222c0..8fc1446 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue @@ -181,6 +181,7 @@ import eventIcon from "./assets/images/事件icon.png"; import soldierIcon from "./assets/images/SketchPngfbec927027ff9e49207749ebaafd229429315341fda199251b6dfb1723ff17fb.png"; import deviceIcon from "./assets/images/SketchPng860d54f2a31f5f441fc6a88081224f1e98534bf6d5ca1246e420983bdf690380.png"; import emergencyBaseIcon from "./assets/images/应急基地.png"; +import reserveCenterIcon from "./assets/images/储备中心.png"; // 折叠按钮图标 import collapseLeftArrow from "./assets/images/折叠面板左箭头.png"; @@ -203,6 +204,9 @@ const handleDistanceChange = async (newDistance) => { // 重新加载应急资源数据并更新地图标记 await loadEmergencyResources(108.011506, 30.175827); + + // 重新加载储备中心和预置点数据并更新地图标记 + await loadReserveCentersAndPresets(108.011506, 30.175827); }; // 提供给子组件使用 @@ -222,6 +226,8 @@ const { getCollapseCenter, addEmergencyResourceMarkers, clearEmergencyResourceMarkers, + addReserveCenterMarkers, + clearReserveCenterMarkers, } = useMapMarkers(); // 地图 Tooltip 功能 @@ -300,6 +306,11 @@ const setupMapClickHandler = (viewer) => { showMarkerTooltip(viewer, markerEntity, click.position, icon); foundMarker = true; break; + } else if (type === 'reserveCenter' || type === 'presetPoint') { + const icon = type === 'reserveCenter' ? reserveCenterIcon : emergencyBaseIcon; + showMarkerTooltip(viewer, markerEntity, click.position, icon); + foundMarker = true; + break; } } } @@ -328,6 +339,10 @@ const setupMapClickHandler = (viewer) => { ? emergencyCenterIcon : emergencyBaseIcon; showMarkerTooltip(viewer, entity, click.position, icon); + } else if (type === 'reserveCenter' || type === 'presetPoint') { + // 储备中心和预置点 + const icon = type === 'reserveCenter' ? reserveCenterIcon : emergencyBaseIcon; + showMarkerTooltip(viewer, entity, click.position, icon); } } } else { @@ -431,6 +446,23 @@ const showMarkerTooltip = (viewer, entity, screenPosition, icon) => { { label: '距离', value: `${distance}公里` } ); } + } else if (type === 'reserveCenter') { + // 储备中心 + title = '储备中心'; + fields.push( + { label: '名称', value: properties.name?.getValue() || '-' }, + { label: '区县', value: properties.district?.getValue() || '-' }, + { label: '人员数量', value: properties.personnelCount?.getValue() || '0' }, + { label: '占地面积', value: properties.area?.getValue() ? `${properties.area?.getValue()}㎡` : '-' } + ); + } else if (type === 'presetPoint') { + // 预置点 + title = '预置点'; + fields.push( + { label: '名称', value: properties.name?.getValue() || '-' }, + { label: '区县', value: properties.district?.getValue() || '-' }, + { label: '人员数量', value: properties.personnelCount?.getValue() || '0' } + ); } // 显示 Tooltip,使用实体的屏幕坐标 @@ -519,22 +551,20 @@ onMounted(() => { }); viewer.entities.add(defaultPoint); - // 在默认点附近添加10个模拟点位(应急人员和应急装备),分散在10km范围内 - // 1度纬度约等于111km,1度经度在30度纬度约等于96km - // 10km约等于0.09度纬度,0.104度经度 + // 在默认点附近添加10个模拟点位(应急人员和应急装备) const simulatedPoints = [ - // 应急人员 - 分散在不同方向 - { type: 'soldier', name: '张三', department: '应急救援队', lon: 108.051, lat: 30.205, distance: 4.2, icon: soldierIcon }, - { type: 'soldier', name: '李四', department: '消防队', lon: 107.975, lat: 30.195, distance: 5.8, icon: soldierIcon }, - { type: 'soldier', name: '王五', department: '医疗队', lon: 108.025, lat: 30.155, distance: 3.5, icon: soldierIcon }, - { type: 'soldier', name: '赵六', department: '应急救援队', lon: 108.085, lat: 30.168, distance: 7.2, icon: soldierIcon }, - { type: 'soldier', name: '刘七', department: '消防队', lon: 107.945, lat: 30.182, distance: 8.5, icon: soldierIcon }, - // 应急装备 - 分散在不同方向 - { type: 'device', name: '救援车辆A', deviceType: '消防车', lon: 108.065, lat: 30.185, distance: 6.3, icon: deviceIcon }, - { type: 'device', name: '救援车辆B', deviceType: '救护车', lon: 107.960, lat: 30.165, distance: 6.8, icon: deviceIcon }, - { type: 'device', name: '无人机A', deviceType: 'DJI', lon: 108.035, lat: 30.225, distance: 5.5, icon: deviceIcon }, - { type: 'device', name: '无人机B', deviceType: 'DJI', lon: 108.095, lat: 30.195, distance: 9.2, icon: deviceIcon }, - { type: 'device', name: '通讯设备', deviceType: '卫星电话', lon: 107.930, lat: 30.175, distance: 9.8, icon: deviceIcon } + // 应急人员 (6个) + { type: 'soldier', name: '张三', department: '应急救援队', lon: 107.97, lat: 30.25, distance: 2.5, icon: soldierIcon }, + { type: 'soldier', name: '李四', department: '消防队', lon: 107.971901, lat: 30.251428, distance: 2.3, icon: soldierIcon }, + { type: 'soldier', name: '王五', department: '医疗队', lon: 107.974901, lat: 30.241428, distance: 3.1, icon: soldierIcon }, + { type: 'soldier', name: '赵六', department: '应急救援队', lon: 108.047344, lat: 30.164313, distance: 4.2, icon: soldierIcon }, + { type: 'soldier', name: '刘七', department: '消防队', lon: 108.046344, lat: 30.168313, distance: 3.8, icon: soldierIcon }, + { type: 'soldier', name: '陈八', department: '医疗队', lon: 108.050344, lat: 30.170313, distance: 4.5, icon: soldierIcon }, + // 应急装备 (4个) + { type: 'device', name: '救援车辆A', deviceType: '消防车', lon: 107.98088, lat: 30.2487, distance: 2.8, icon: deviceIcon }, + { type: 'device', name: '救援车辆B', deviceType: '救护车', lon: 107.97898, lat: 30.2502, distance: 2.6, icon: deviceIcon }, + { type: 'device', name: '无人机A', deviceType: 'DJI', lon: 108.049344, lat: 30.160313, distance: 4.8, icon: deviceIcon }, + { type: 'device', name: '无人机B', deviceType: 'DJI', lon: 108.043344, lat: 30.169313, distance: 3.6, icon: deviceIcon } ]; simulatedPoints.forEach(point => { @@ -721,10 +751,60 @@ const loadEmergencyResources = async (longitude, latitude) => { } }; +// 加载储备中心和预置点数据 +const loadReserveCentersAndPresets = async (longitude, latitude) => { + try { + const response = await request({ + url: `/snow-ops-platform/yhYjll/list`, + method: "GET", + params: { + longitude, + latitude, + maxDistance: disasterData.forcePreset.value.searchRadius, + }, + }); + + if (response?.data && Array.isArray(response.data)) { + console.log("[index.vue] 储备中心和预置点数据加载成功:", response.data); + + // 更新地图标记 + if (mapStore.viewer) { + console.log("[index.vue] 添加储备中心和预置点地图标记..."); + + // 清除旧的标记 + clearReserveCenterMarkers(mapStore.viewer); + + // 添加新的标记 + await addReserveCenterMarkers( + mapStore.viewer, + response.data, + { heightOffset: 10 } + ); + } else { + console.warn("[index.vue] 地图viewer未就绪,跳过标记更新"); + } + } else { + console.warn("[index.vue] 储备中心和预置点接口返回数据为空"); + } + + return response; + } catch (error) { + console.error("[index.vue] 加载储备中心和预置点数据失败:", error); + ElMessage.warning({ + message: "储备中心和预置点数据加载失败", + duration: 3000, + }); + return null; + } +}; + onMounted(async () => { // 加载应急资源数据(使用默认灾害点坐标) // await loadEmergencyResources(108.011506, 30.175827); const response = await loadEmergencyResources(108.011506, 30.175827); + + // 加载储备中心和预置点数据(使用相同的坐标) + await loadReserveCentersAndPresets(108.011506, 30.175827); }); /** @@ -1134,8 +1214,8 @@ const showMapTooltip = ({ x, y, title = "", icon = "", data = null }) => { // 左右面板列 - 浮动卡片样式 &__panel-column { position: absolute; - top: var(--sa-header-height); // 从 header 下方开始 - bottom: 0; + // top: var(--sa-header-height); // 从 header 下方开始 + // bottom: 0; display: flex; flex-direction: column; gap: var(--sa-gap); // 列内子面板之间的间距 @@ -1274,7 +1354,7 @@ const showMapTooltip = ({ x, y, title = "", icon = "", data = null }) => { // 加载动画层 - 一键启动后显示 &__loading-layer { position: absolute; - top: calc(var(--sa-header-height) + vh(20)); + top: calc(var(--sa-header-height) + vh(40)); left: 0; right: 0; // bottom: 0; From dadfa1b1cc2994f744f37dfdb228e0418467fc86 Mon Sep 17 00:00:00 2001 From: Zzc <1373857752@qq.com> Date: Thu, 20 Nov 2025 18:07:47 +0800 Subject: [PATCH 3/8] chore(screen): update tooltip images --- .../assets/images/Tooltip/bottomLeft.png | Bin 169 -> 166 bytes .../assets/images/Tooltip/bottomRight.png | Bin 140 -> 143 bytes .../assets/images/Tooltip/topLeft.png | Bin 137 -> 141 bytes .../assets/images/Tooltip/topRight.png | Bin 133 -> 136 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/Tooltip/bottomLeft.png b/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/Tooltip/bottomLeft.png index 3a372ac082fe196b2daa003af0b6696b07fa5c0a..030e3d58526e3b0d401fc526598ee407a48b3131 100644 GIT binary patch delta 138 zcmV;50CoSV0j2?vB!62;L_t(|0ql^m4Zt7_1V7#+Rklc!DY8qJ$Ow_3V=4*;prim9 zQ9uQ9jfAC>i@)ZduHU}}2uBP&1;GPJLd}<0m+1bf=DBq^Mx4s;txSr+j6m8x0<#qX sTm}FD|Nk*FdD#E}00v1!K~w_(L_t(|0ql}H3V<*WM91|cR-Q&Jy^ELd9-hj=TEsSi$p?}y z*)~NM9=otp48zFw@(4f~0kKC^p(03cNr$$(u$oW{@vJ}R63fRadmId|5~IZ#YIggY vRiWT<00030|2CZs_5c6?21!IgR09BW?>jc0UWp(80000}Q;ylI`N@;uuoF_-4XEP6h)G=A#F18Se6)Ubrmp5u2{vrRg(RMVwstT7ngg z&z83|g{Qb`bhPsxa=Eou{c6)pF delta 111 zcmeBY>|vaslHus-;uuoF_-4XIP6h=I)_A=a>ASb2R$t+1`(URk>+n$EiGzisQB{uR z%8tgDPn7Jpoe=o=%BD~}&UO7;`7Z(QelRdF{Qtl4$=s(v1{=sQW(Kxkx%r+}8ywlIi5>;uuoF_-2A3FM|RPi+yN=u*?J2)QW?QP96mgmzO@?&h+bK?sR#- zV^!+S3YOyEbLKuwDv43j4XJFhkr!vX%D~FN@c+LXqiY8P0|Oh#G-ig(9!=}FdhTa{ OA`G6celF{r5}E*n-6dH7 delta 108 zcmeBW>|~svl4|Ga;uuoF_-2BkAcFx1bFfZ`$I_7Or6C+r4|mUrl49%W;uuoF_-2BkAcFx1b9G>VdTD@uXqdjm0ZySA5zZ_VcE>FH^I?a5 zjOH|n8@JoHT3BtAyURCGO3F6GpNWCt|9`i@v*`>B3~V5Sm>JAFRFgSAFnR+;7(8A5 KT-G@yGywpVp(5%4 delta 104 zcmeBRY-OCFl4#}W;uuoF_-2BkAcFx1^X-Tg6TDVl^K#^nN=V`rba+=Z^V8wSl{12L z41c7@Uwic8NL7ioOIF6009#H5hX4Qdy_?T4Ffg!zOkrlYAggBovB2jnP=vwL)z4*} HQ$iB}9@Zw{ From 0d2b9b27d00fccad98483ca08ee65b10f7ae4ec8 Mon Sep 17 00:00:00 2001 From: Zzc <1373857752@qq.com> Date: Thu, 20 Nov 2025 18:08:24 +0800 Subject: [PATCH 4/8] =?UTF-8?q?feat(screen):=20=E4=B8=BA=E5=9C=B0=E5=9B=BE?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E6=8F=90=E7=A4=BA=E6=B7=BB=E5=8A=A0=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=A7=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为 MapTooltip 组件添加一个新的操作区域,用于显示“连线”和“联动”等交互式按钮。包含相应的 CSS 样式,用于布局和视觉分隔。 --- .../components/shared/MapTooltip.vue | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/shared/MapTooltip.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/shared/MapTooltip.vue index a0505d5..ca7ff3d 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/components/shared/MapTooltip.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/components/shared/MapTooltip.vue @@ -94,6 +94,11 @@
+ + +
+ +
@@ -280,8 +285,8 @@ const handleClose = () => { */ .map-tooltip__corner { position: absolute; - width: vw(20); - height: vw(20); // 使用 vw 保持正方形 + // width: vw(20); + // height: vw(20); // 使用 vw 保持正方形 pointer-events: none; // 装饰元素不响应鼠标事件 z-index: 1; // 确保显示在背景之上 @@ -431,6 +436,19 @@ const handleClose = () => { gap: vh(6); } +/** + * 操作按钮区域 + * 用于显示"连线"、"联动"等交互按钮 + */ +.map-tooltip__actions { + display: flex; + justify-content: center; + align-items: center; + margin-top: vh(16); + padding-top: vh(12); + border-top: 1px solid rgba(255, 255, 255, 0.1); +} + /** * 淡入淡出动画 * 结合缩放效果,提升视觉体验 From 06c462a57722d408b7cba93c6612ff97e2f25ffd Mon Sep 17 00:00:00 2001 From: Zzc <1373857752@qq.com> Date: Thu, 20 Nov 2025 18:09:04 +0800 Subject: [PATCH 5/8] =?UTF-8?q?feat(screen):=20=E4=B8=BA3D=E6=80=81?= =?UTF-8?q?=E5=8A=BF=E6=84=9F=E7=9F=A5=E6=B7=BB=E5=8A=A0=E4=BA=BA=E5=91=98?= =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引入 `useEntityAnimation` 可组合项,用于在Cesium查看器中沿预定义路径为紧急人员制作动画 - 添加脉冲广告牌效果、发光路径轨迹和相机跟踪选项 - 将动画启动与调度事件集成,支持可配置的时长和实体属性 - 增强地图工具提示,添加人员关联和基地连接的操作按钮 - 更新模拟点,显示预计到达时间,以提高态势感知能力 --- .../composables/useEntityAnimation.js | 261 ++++++++++++++++++ .../3DSituationalAwarenessRefactor/index.vue | 174 ++++++++++-- 2 files changed, 408 insertions(+), 27 deletions(-) create mode 100644 packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useEntityAnimation.js diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useEntityAnimation.js b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useEntityAnimation.js new file mode 100644 index 0000000..aa56c92 --- /dev/null +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useEntityAnimation.js @@ -0,0 +1,261 @@ +import { ref } from 'vue' +import * as Cesium from 'cesium' +import soldierIcon from '../assets/images/SketchPngfbec927027ff9e49207749ebaafd229429315341fda199251b6dfb1723ff17fb.png' + +/** + * 实体动画管理 Composable + * 负责管理地图实体的路径动画,特别是应急人员沿路径移动 + */ +export function useEntityAnimation() { + // 动画实体引用 + const animatedEntity = ref(null) + + // 动画是否正在运行 + const isAnimating = ref(false) + + /** + * 应急人员路径坐标 (Cartesian3 格式) + * 这些坐标定义了人员从起点到终点的移动路径 + */ + const PERSONNEL_PATH_COORDINATES = [ + { x: -1705480.2641142386, y: 5247435.146513505, z: 3189090.667137187 }, + { x: -1705494.4820981335, y: 5247449.285793587, z: 3189060.452114544 }, + { x: -1705510.0809808762, y: 5247465.767274618, z: 3189023.050648535 }, + { x: -1705511.0338125327, y: 5247476.4118378535, z: 3188998.3643177846 }, + { x: -1705515.8155320068, y: 5247491.504151518, z: 3188970.3225750974 }, + { x: -1705512.9523099929, y: 5247504.710873116, z: 3188943.7936982675 }, + { x: -1705519.7649184526, y: 5247519.060354441, z: 3188915.1883725724 }, + { x: -1705528.241912857, y: 5247539.302819527, z: 3188872.220619207 }, + { x: -1705530.7649293465, y: 5247548.26353356, z: 3188852.4565014304 }, + { x: -1705536.847870567, y: 5247562.107401437, z: 3188816.1164027476 }, + { x: -1705554.2817406887, y: 5247571.234068825, z: 3188789.6105980803 }, + { x: -1705573.026007999, y: 5247580.50225183, z: 3188770.244426234 }, + { x: -1705602.018256302, y: 5247597.236114229, z: 3188743.7470805836 } + ] + + /** + * 启动应急人员沿路径移动动画 + * @param {Cesium.Viewer} viewer - Cesium viewer 实例 + * @param {Object} options - 配置选项 + * @param {number} [options.duration=36] - 动画总时长(秒) + * @param {boolean} [options.trackEntity=false] - 是否相机跟随实体 + * @param {string} [options.personnelName='应急人员'] - 人员名称 + * @param {string} [options.department='应急救援队'] - 所属部门 + * @returns {Cesium.Entity} 返回创建的动画实体 + */ + const startPersonnelMovement = (viewer, options = {}) => { + if (!viewer) { + console.warn('[useEntityAnimation] startPersonnelMovement: viewer 为空') + return null + } + + if (isAnimating.value) { + console.warn('[useEntityAnimation] 动画已在运行中,请先停止当前动画') + return animatedEntity.value + } + + const config = { + duration: options.duration ?? 60, // 默认 60 秒,让移动更清晰可见 + trackEntity: options.trackEntity ?? false, + personnelName: options.personnelName ?? '应急人员', + department: options.department ?? '应急救援队' + } + + console.log('[useEntityAnimation] 开始启动人员移动动画...', config) + + // 设置动画时间范围 + const startTime = Cesium.JulianDate.now() + const stopTime = Cesium.JulianDate.addSeconds( + startTime, + config.duration, + new Cesium.JulianDate() + ) + + // 配置 viewer 时钟 + viewer.clock.startTime = startTime.clone() + viewer.clock.stopTime = stopTime.clone() + viewer.clock.currentTime = startTime.clone() + viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP // 动画结束后停止 + viewer.clock.multiplier = 1 // 实时速度 + viewer.clock.shouldAnimate = true + + // 创建 SampledPositionProperty 定义路径 + const positionProperty = new Cesium.SampledPositionProperty() + + // 计算每个路径点的时间间隔 + const numberOfPoints = PERSONNEL_PATH_COORDINATES.length + const timeInterval = config.duration / (numberOfPoints - 1) + + // 添加路径采样点 + PERSONNEL_PATH_COORDINATES.forEach((coord, index) => { + const time = Cesium.JulianDate.addSeconds( + startTime, + index * timeInterval, + new Cesium.JulianDate() + ) + const position = new Cesium.Cartesian3(coord.x, coord.y, coord.z) + positionProperty.addSample(time, position) + }) + + // 创建脉冲缩放效果 - 让图标闪烁更醒目 + const pulseScale = new Cesium.CallbackProperty((time) => { + const elapsed = Cesium.JulianDate.secondsDifference(time, startTime) + // 使用正弦波产生脉冲效果,频率 3Hz,幅度 ±30% + return 1.0 + Math.sin(elapsed * 3) * 0.3 + }, false) + + // 创建动画实体 + const entity = viewer.entities.add({ + availability: new Cesium.TimeIntervalCollection([ + new Cesium.TimeInterval({ + start: startTime, + stop: stopTime + }) + ]), + position: positionProperty, + orientation: new Cesium.VelocityOrientationProperty(positionProperty), // 自动朝向移动方向 + billboard: { + image: soldierIcon, + width: 48, // 增大尺寸,从 36 增加到 48 + height: 56, // 从 40 增加到 56 + scale: pulseScale, // 使用脉冲缩放效果 + verticalOrigin: Cesium.VerticalOrigin.BOTTOM, + heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, + disableDepthTestDistance: Number.POSITIVE_INFINITY, + // 添加距离缩放,让图标在不同距离下都清晰可见 + scaleByDistance: new Cesium.NearFarScalar( + 1000, 1.5, // 近距离(1000米)放大到 1.5 倍 + 50000, 0.8 // 远距离(50000米)缩小到 0.8 倍 + ) + }, + // 添加发光轨迹线 + path: { + resolution: 1, + material: new Cesium.PolylineGlowMaterialProperty({ + glowPower: 0.4, // 发光强度 + taperPower: 0.5, // 渐变效果 + color: Cesium.Color.CYAN // 青色轨迹 + }), + width: 8, + leadTime: 0, // 前方不显示轨迹 + trailTime: config.duration // 后方显示完整轨迹 + }, + properties: { + type: 'animatedSoldier', + name: config.personnelName, + department: config.department, + isAnimating: true + } + }) + + animatedEntity.value = entity + isAnimating.value = true + + // 可选: 相机跟随实体 + if (config.trackEntity) { + viewer.trackedEntity = entity + // 设置相机视角偏移 + entity.viewFrom = new Cesium.Cartesian3(-100.0, -100.0, 50.0) + } + + console.log('[useEntityAnimation] 人员移动动画已启动') + + // 监听动画结束事件 + const removeListener = viewer.clock.onStop.addEventListener(() => { + console.log('[useEntityAnimation] 动画已结束') + isAnimating.value = false + removeListener() // 移除监听器 + }) + + return entity + } + + /** + * 停止人员移动动画 + * @param {Cesium.Viewer} viewer - Cesium viewer 实例 + * @param {boolean} [removeEntity=true] - 是否移除动画实体 + */ + const stopPersonnelMovement = (viewer, removeEntity = true) => { + if (!viewer) { + console.warn('[useEntityAnimation] stopPersonnelMovement: viewer 为空') + return + } + + console.log('[useEntityAnimation] 停止人员移动动画') + + // 停止时钟动画 + viewer.clock.shouldAnimate = false + + // 取消相机跟随 + if (viewer.trackedEntity === animatedEntity.value) { + viewer.trackedEntity = undefined + } + + // 移除实体 + if (removeEntity && animatedEntity.value) { + viewer.entities.remove(animatedEntity.value) + animatedEntity.value = null + } + + isAnimating.value = false + } + + /** + * 暂停动画 + * @param {Cesium.Viewer} viewer - Cesium viewer 实例 + */ + const pauseAnimation = (viewer) => { + if (!viewer) return + viewer.clock.shouldAnimate = false + console.log('[useEntityAnimation] 动画已暂停') + } + + /** + * 恢复动画 + * @param {Cesium.Viewer} viewer - Cesium viewer 实例 + */ + const resumeAnimation = (viewer) => { + if (!viewer) return + if (isAnimating.value) { + viewer.clock.shouldAnimate = true + console.log('[useEntityAnimation] 动画已恢复') + } + } + + /** + * 获取路径的起点坐标 (用于初始标记点) + * @returns {Cesium.Cartesian3} 起点坐标 + */ + const getStartPosition = () => { + const firstCoord = PERSONNEL_PATH_COORDINATES[0] + return new Cesium.Cartesian3(firstCoord.x, firstCoord.y, firstCoord.z) + } + + /** + * 将 Cartesian3 坐标转换为经纬度 + * @param {Cesium.Cartesian3} cartesian - Cartesian3 坐标 + * @returns {Object} { longitude, latitude, height } + */ + const cartesianToLonLat = (cartesian) => { + const cartographic = Cesium.Cartographic.fromCartesian(cartesian) + return { + longitude: Cesium.Math.toDegrees(cartographic.longitude), + latitude: Cesium.Math.toDegrees(cartographic.latitude), + height: cartographic.height + } + } + + return { + animatedEntity, + isAnimating, + startPersonnelMovement, + stopPersonnelMovement, + pauseAnimation, + resumeAnimation, + getStartPosition, + cartesianToLonLat, + PERSONNEL_PATH_COORDINATES + } +} + +export default useEntityAnimation diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue index 8fc1446..ddfad6e 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue @@ -126,6 +126,18 @@ {{ field.value }} + + + @@ -172,6 +184,7 @@ import { useDualMapCompare } from "./composables/useDualMapCompare"; import { useMapMarkers } from "./composables/useMapMarkers"; import { use3DTiles } from "./composables/use3DTiles"; import { useMapTooltip } from "./composables/useMapTooltip"; +import { useEntityAnimation } from "./composables/useEntityAnimation"; import { useMapStore } from "@/map"; import { request } from "@shared/utils/request"; @@ -233,6 +246,9 @@ const { // 地图 Tooltip 功能 const { tooltipState: mapTooltip, showTooltip, hideTooltip, updateTooltipPosition } = useMapTooltip(); +// 实体动画功能 +const { startPersonnelMovement, stopPersonnelMovement, isAnimating } = useEntityAnimation(); + // 当前显示 tooltip 的实体(用于相机移动时更新位置) const currentTooltipEntity = ref(null); @@ -403,28 +419,45 @@ const showMarkerTooltip = (viewer, entity, screenPosition, icon) => { // 构建 Tooltip 数据 let title = ''; const fields = []; + const actions = []; if (type === 'soldier') { - title = '单兵信息'; + // 应急人员 + title = '应急人员'; fields.push( { label: '姓名', value: properties.name?.getValue() || '-' }, { label: '部门', value: properties.department?.getValue() || '-' }, - { label: '位置', value: properties.location?.getValue() || '-' } + { label: '位置信息', value: properties.location?.getValue() || '-' }, + { label: '预计到达时间', value: properties.estimatedArrival?.getValue() || '-' } ); + actions.push({ + label: '联动', + type: 'link', + data: entity + }); } else if (type === 'device') { - title = '设备信息'; + // 应急装备 + title = '应急装备'; fields.push( { label: '设备名称', value: properties.name?.getValue() || '-' }, { label: '设备类型', value: properties.deviceType?.getValue() || '-' }, - { label: '位置', value: properties.location?.getValue() || '-' } + { label: '位置信息', value: properties.location?.getValue() || '-' }, + { label: '预计到达时间', value: properties.estimatedArrival?.getValue() || '-' } ); } else if (type === 'emergencyBase') { + // 应急基地 title = '应急基地'; fields.push( - { label: '名称', value: properties.name?.getValue() || '-' }, - { label: '地址', value: properties.address?.getValue() || '-' }, - { label: '距离', value: properties.distance?.getValue() || '-' } + { label: '基地名称', value: properties.name?.getValue() || '-' }, + { label: '路线编号', value: properties.routeNumber?.getValue() || '-' }, + { label: '隶属单位', value: properties.department?.getValue() || '-' }, + { label: '位置信息', value: properties.location?.getValue() || '-' } ); + actions.push({ + label: '连线', + type: 'connect', + data: entity + }); } else if (type === 'station') { const stationName = properties.name?.getValue() || ''; const distance = properties.distance?.getValue() || 0; @@ -451,17 +484,14 @@ const showMarkerTooltip = (viewer, entity, screenPosition, icon) => { title = '储备中心'; fields.push( { label: '名称', value: properties.name?.getValue() || '-' }, - { label: '区县', value: properties.district?.getValue() || '-' }, - { label: '人员数量', value: properties.personnelCount?.getValue() || '0' }, - { label: '占地面积', value: properties.area?.getValue() ? `${properties.area?.getValue()}㎡` : '-' } + { label: '位置信息', value: properties.location?.getValue() || '-' } ); } else if (type === 'presetPoint') { // 预置点 title = '预置点'; fields.push( { label: '名称', value: properties.name?.getValue() || '-' }, - { label: '区县', value: properties.district?.getValue() || '-' }, - { label: '人员数量', value: properties.personnelCount?.getValue() || '0' } + { label: '位置信息', value: properties.location?.getValue() || '-' } ); } @@ -471,7 +501,7 @@ const showMarkerTooltip = (viewer, entity, screenPosition, icon) => { y: canvasPosition.y, title, icon, - data: { fields } + data: { fields, actions: actions.length > 0 ? actions : undefined } }); // 保存当前实体,用于相机移动时更新位置 @@ -554,17 +584,17 @@ onMounted(() => { // 在默认点附近添加10个模拟点位(应急人员和应急装备) const simulatedPoints = [ // 应急人员 (6个) - { type: 'soldier', name: '张三', department: '应急救援队', lon: 107.97, lat: 30.25, distance: 2.5, icon: soldierIcon }, - { type: 'soldier', name: '李四', department: '消防队', lon: 107.971901, lat: 30.251428, distance: 2.3, icon: soldierIcon }, - { type: 'soldier', name: '王五', department: '医疗队', lon: 107.974901, lat: 30.241428, distance: 3.1, icon: soldierIcon }, - { type: 'soldier', name: '赵六', department: '应急救援队', lon: 108.047344, lat: 30.164313, distance: 4.2, icon: soldierIcon }, - { type: 'soldier', name: '刘七', department: '消防队', lon: 108.046344, lat: 30.168313, distance: 3.8, icon: soldierIcon }, - { type: 'soldier', name: '陈八', department: '医疗队', lon: 108.050344, lat: 30.170313, distance: 4.5, icon: soldierIcon }, + { type: 'soldier', name: '张三', department: '应急救援队', lon: 107.97, lat: 30.25, distance: 2.5, estimatedArrival: '10分钟', icon: soldierIcon }, + { type: 'soldier', name: '李四', department: '消防队', lon: 107.971901, lat: 30.251428, distance: 2.3, estimatedArrival: '8分钟', icon: soldierIcon }, + { type: 'soldier', name: '王五', department: '医疗队', lon: 107.974901, lat: 30.241428, distance: 3.1, estimatedArrival: '12分钟', icon: soldierIcon }, + { type: 'soldier', name: '赵六', department: '应急救援队', lon: 108.047344, lat: 30.164313, distance: 4.2, estimatedArrival: '15分钟', icon: soldierIcon }, + { type: 'soldier', name: '刘七', department: '消防队', lon: 108.046344, lat: 30.168313, distance: 3.8, estimatedArrival: '14分钟', icon: soldierIcon }, + { type: 'soldier', name: '陈八', department: '医疗队', lon: 108.050344, lat: 30.170313, distance: 4.5, estimatedArrival: '16分钟', icon: soldierIcon }, // 应急装备 (4个) - { type: 'device', name: '救援车辆A', deviceType: '消防车', lon: 107.98088, lat: 30.2487, distance: 2.8, icon: deviceIcon }, - { type: 'device', name: '救援车辆B', deviceType: '救护车', lon: 107.97898, lat: 30.2502, distance: 2.6, icon: deviceIcon }, - { type: 'device', name: '无人机A', deviceType: 'DJI', lon: 108.049344, lat: 30.160313, distance: 4.8, icon: deviceIcon }, - { type: 'device', name: '无人机B', deviceType: 'DJI', lon: 108.043344, lat: 30.169313, distance: 3.6, icon: deviceIcon } + { type: 'device', name: '救援车辆A', deviceType: '消防车', lon: 107.98088, lat: 30.2487, distance: 2.8, estimatedArrival: '11分钟', icon: deviceIcon }, + { type: 'device', name: '救援车辆B', deviceType: '救护车', lon: 107.97898, lat: 30.2502, distance: 2.6, estimatedArrival: '9分钟', icon: deviceIcon }, + { type: 'device', name: '无人机A', deviceType: 'DJI', lon: 108.049344, lat: 30.160313, distance: 4.8, estimatedArrival: '17分钟', icon: deviceIcon }, + { type: 'device', name: '无人机B', deviceType: 'DJI', lon: 108.043344, lat: 30.169313, distance: 3.6, estimatedArrival: '13分钟', icon: deviceIcon } ]; simulatedPoints.forEach(point => { @@ -583,13 +613,15 @@ onMounted(() => { type: 'soldier', name: point.name, department: point.department, - location: `目前为止距离现场${point.distance}公里` + location: `目前为止距离现场${point.distance}公里`, + estimatedArrival: point.estimatedArrival } : { type: 'device', name: point.name, deviceType: point.deviceType, - location: `目前为止距离现场${point.distance}公里` + location: `目前为止距离现场${point.distance}公里`, + estimatedArrival: point.estimatedArrival } }); }); @@ -888,6 +920,55 @@ const handlePersonnelLink = (personnel) => { showPersonnelDetail.value = false; }; +/** + * 处理 Tooltip 操作按钮点击事件 + * @param {Object} action - 操作对象,包含 type 和 data + */ +const handleTooltipAction = (action) => { + console.log('[index.vue] Tooltip 操作按钮点击:', action); + + if (action.type === 'link') { + // 应急人员的"联动"操作 + const entity = action.data; + const properties = entity.properties; + + console.log('[index.vue] 应急人员联动:', { + name: properties.name?.getValue(), + department: properties.department?.getValue() + }); + + // 可以在这里实现联动功能,比如: + // 1. 飞行到人员位置 + // 2. 打开详情弹窗 + // 3. 高亮显示相关信息 + ElMessage.success(`已联动应急人员: ${properties.name?.getValue() || '未知'}`); + + // 关闭 tooltip + hideTooltip(); + currentTooltipEntity.value = null; + + } else if (action.type === 'connect') { + // 应急基地的"连线"操作 + const entity = action.data; + const properties = entity.properties; + + console.log('[index.vue] 应急基地连线:', { + name: properties.name?.getValue(), + routeNumber: properties.routeNumber?.getValue() + }); + + // 可以在这里实现连线功能,比如: + // 1. 在地图上绘制线路 + // 2. 显示路径规划 + // 3. 计算距离和时间 + ElMessage.success(`已连线应急基地: ${properties.name?.getValue() || '未知'}`); + + // 关闭 tooltip + hideTooltip(); + currentTooltipEntity.value = null; + } +}; + /** * 关闭地图 Tooltip * 统一的关闭入口,便于后续扩展埋点或联动逻辑 @@ -898,7 +979,7 @@ const handleMapTooltipClose = () => { /** * 处理力量调度启动事件 - * 显示加载动画,3秒后自动隐藏 + * 显示加载动画,3秒后自动隐藏,然后启动人员移动动画 */ const handleStartDispatch = (payload) => { console.log('[index.vue] 启动力量调度:', payload); @@ -906,9 +987,22 @@ const handleStartDispatch = (payload) => { // 显示加载动画 showLoading.value = true; - // 3秒后自动隐藏加载动画 + // 3秒后隐藏加载动画并启动人员移动 setTimeout(() => { showLoading.value = false; + + // 启动应急人员沿路径移动动画 + if (mapStore.viewer) { + console.log('[index.vue] 启动应急人员移动动画...'); + startPersonnelMovement(mapStore.viewer, { + duration: 60, // 60秒完成整个路径,移动更清晰可见 + trackEntity: false, // 不自动跟随相机(可设为 true 启用跟随) + personnelName: '应急救援人员', + department: '应急救援队' + }); + } else { + console.warn('[index.vue] 地图viewer未就绪,无法启动动画'); + } }, 3000); }; @@ -1401,6 +1495,32 @@ const showMapTooltip = ({ x, y, title = "", icon = "", data = null }) => { } } +// Tooltip 操作按钮样式 +// 用于"连线"、"联动"等交互按钮 +.tooltip-action-btn { + min-width: vw(100); + height: vh(36); + padding: 0 vw(24); + background: url('./assets/images/地图tooltip-button.png') no-repeat center/100% 100%; + border: none; + color: var(--text-white); + font-size: fs(14); + font-family: SourceHanSansCN-Medium, sans-serif; + font-weight: 500; + cursor: pointer; + transition: all 0.3s ease; + + &:hover { + filter: brightness(1.2); + transform: translateY(vh(-2)); + } + + &:active { + filter: brightness(0.9); + transform: translateY(0); + } +} + // 窄容器嵌入的紧凑布局(<1100px 宽度) .situational-awareness.is-compact { --sa-left-width: calc(380 / 1920 * var(--cq-inline-100, 100vw)); From 586d669e884ae207e39d1c1edf59b823a4a275a7 Mon Sep 17 00:00:00 2001 From: Zzc <1373857752@qq.com> Date: Fri, 21 Nov 2025 11:06:32 +0800 Subject: [PATCH 6/8] =?UTF-8?q?feat(screen):=20=E5=9C=A8=E5=B7=A6=E4=BE=A7?= =?UTF-8?q?=E9=9D=A2=E6=9D=BF=E4=B8=AD=E6=B7=BB=E5=8A=A0=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E6=BA=90=E5=B9=B6=E6=9B=B4=E6=96=B0=E6=91=84=E5=83=8F=E5=A4=B4?= =?UTF-8?q?=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将摄像头图标更新为 media_dvr_on.png - 为 3D 态势感知的快速感知功能设置视频源 --- .../assets/images/media_dvr_on.png | Bin 0 -> 2760 bytes .../components/LeftPanel/index.vue | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/media_dvr_on.png diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/media_dvr_on.png b/packages/screen/src/views/3DSituationalAwarenessRefactor/assets/images/media_dvr_on.png new file mode 100644 index 0000000000000000000000000000000000000000..7e83f72517b9d4870817f26a0f29c23ebf43624c GIT binary patch literal 2760 zcmV;(3ODtMP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuGGD$>1RCodHn`>+wR~5(Sv1`YV z+G%3fY1}5YlRi>H#3PhKpn{4@2tFc&kbnGgOaV1K1oiJ1rReSIW2idWE8VBQm;-^36S9!db4Dkgb_Aa&J0rw&a+IfS9JBDFlRWJ}ixHk)S+yPjX&zK>`6;03laP zm_iT(An;AeiDpcXh6#}Io2AT$C0!D5S#nk~%S97`N6=Si*yfy$M??aBWky2;$oO6A z8FvSaGRiRGt;BjYeyzkzkGDm_>sdR@M1Txuet$wTAR$VRNeEVEPV2O#AZ8F;Ap#p% zG5#$gh!d%hngAKkGWJOcE=eFeEorv9tI0$_2UZ#aC_20>5?DqPBNYLm@pq_of)l~$ zl!S=>L4v7pgM=d<@Xs_P;7W-g<5}Wvli>a=X~!iWwDD3Q&;ecW0Cc-8)K#Y|BQXJL ziaC$_Gx8%6+<&zIUC@c87aey?5+M-*Vsxik>z9y#oRX|2?~#C5FrkW~U{oky8(@ZS_vg`@ekn zmBl|kKT_Z&sntMNbVheR}o zysK3}S9Hb;wuiRF_EnPrl_teyy(hA=>}5h;!;jzz9QD^Zv*o7VCy8Opg5#35m|hNUfANPF}Ej{x#~|f5j_)b=mXrPP+H;iZ_PV zJNrJr)%|bErjryOQX#U$*OVa=94kHcR7H&jE}lN=?U}e_{jBKw*TppB zb>{8I?i_X>>1$87)W)<`ertm+uw_C794ddnd4vFsKKJgFkoq$Eso$_vrzYOLWOW`q z#V1_bK7MVtb+5c3qHN7Z>pvhp9+HqC{aqx`%@X1WU1{wH+MwzJJ;C_gcQKWtYJ*D*2XU+oSy*)_tGd;y&M?66{F#N{)+w6Xk80-j?oZ z3Xt~`1eyPk&~m)`u6OI%S$n_d7kAnr0fq{W?K$hoT?6hzgI$@IV|I~<17}6R8|4PY z5rF%{7Ta5w%L}*id|pf83uDFa%oU11P(STU&Hw|zu_vz1+dtW}#W_&Ak?NO>aI<7a z1iV~cGme1Ir~o^=Ba(&#AcnQKCx3f9dfpm7JLByy_||TeGxnSFylp*o(+2l}>wAM8 zXO&0<=KmJSya+g0USlZ=m)+D{4EiOVY#1+%dyg{LlQwkgM(e~EhTO09wAl~j9ea@s zvRpJnzWmm-ci_;NwBVLxms!81hS3nSbrO4xjV?FtxC>oxNg9({omr zZ`r$C$Ipud3kg!`($Vk8TYtN8qx+u~8(J=4Y%#ViJz=e{hACWqKht&2p*jE4$IjV* z+P>cTc7Ip>zYV8)?iFzTKH;KMgw~w<9IP!cKIMmJTchm{Isnt*oA7i@3IDrE!b9pI1k|48qpGGMH+HJvkH{P zKk$Rw710BKEW0<^mb3Q_blT6Uzh=YOflZO`h7kgkDr&m=Ftt%a4m~T$jAaj~YN9mV zS!$l`X}9-v=9{bSfN5+ioi6`I2nZkXCI|r?l7aO6xaZ;DS}!AOsXVHAi;zAlldd48hg;mmbB4Wjv9Bo$wKbuSefCV<{V0`2rkHc3zv;1L8R zB z7_HNwK4~rg(jzOOv{M1oamCF>FG7!CnIw(or6lokTh%v)vLQ1ix!8;muWG-sAXwd4 z+;vDsm!2U6Fk%JM1X2t!7>W=#SWTGUym-yE!f3U-hBXG(7+7N