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] =?UTF-8?q?feat(screen):=20=E4=B8=BA3D=E6=80=81=E5=8A=BF?= =?UTF-8?q?=E6=84=9F=E7=9F=A5=E6=B7=BB=E5=8A=A0=E8=A7=86=E9=A2=91=E6=A8=A1?= =?UTF-8?q?=E6=80=81=E6=A1=86=E5=92=8C=E4=B8=AD=E5=BF=83=E6=A0=87=E8=AE=B0?= =?UTF-8?q?=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;