diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useDisasterData.js b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useDisasterData.js index 7c6c8cb..c38e46c 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useDisasterData.js +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useDisasterData.js @@ -111,7 +111,10 @@ export function useDisasterData() { bases: forcePreset.value.bases, // 挖掘机建议数 - excavators: Math.max(1, Math.ceil(stationsCount / 2)), + // excavators: Math.max(1, Math.ceil(stationsCount / 2)), + + // 应急装备 + excavators: forcePreset.value.equipment, // 阻断信息:固定建议 blockInfo: '需发布', diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useDualMapCompare.js b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useDualMapCompare.js index 13fdbb5..4b0c6ce 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useDualMapCompare.js +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/composables/useDualMapCompare.js @@ -75,6 +75,9 @@ export function useDualMapCompare() { // viewer.scene.screenSpaceCameraController.enableTilt = false // viewer.scene.screenSpaceCameraController.enableLook = false + // 将 viewer 实例存储到容器上,供其他模块访问 + container._cesiumViewer = viewer + leftViewer.value = viewer console.log('[useDualMapCompare] 左侧Viewer初始化成功(交互已启用)') // 关键修改 @@ -261,6 +264,32 @@ export function useDualMapCompare() { // 如果只是加载左侧模型(Viewer已存在) if (loadLeftModel && leftViewer.value) { console.log('[useDualMapCompare] 加载左侧灾前模型...') + + // 在加载模型前,确保左侧相机位置与右侧同步 + if (rightViewerInstance && leftViewer.value) { + console.log('[useDualMapCompare] 同步相机位置(延迟加载场景)...') + const rightCamera = rightViewerInstance.camera + + try { + leftViewer.value.camera.setView({ + destination: rightCamera.position.clone(), + orientation: { + heading: rightCamera.heading, + pitch: rightCamera.pitch, + roll: rightCamera.roll + } + }) + + // 强制更新 + leftViewer.value.scene.requestRender() + leftViewer.value.camera.changed.raiseEvent() + + console.log('[useDualMapCompare] 相机位置同步完成') + } catch (error) { + console.error('[useDualMapCompare] 相机同步失败:', error) + } + } + try { const tileset = await load3DTileset(leftViewer.value, 'before', false) if (tileset) { @@ -321,18 +350,57 @@ export function useDualMapCompare() { throw error } - // 立即同步右侧相机的当前位置到左侧 + // 等待左侧 viewer 完全初始化 + await new Promise(resolve => setTimeout(resolve, 100)) + + // 立即同步右侧相机的当前位置到左侧(关键修复) console.log('[useDualMapCompare] 同步初始相机位置...') const rightCamera = rightViewerInstance.camera - leftViewerInstance.camera.setView({ - destination: rightCamera.position.clone(), - orientation: { - heading: rightCamera.heading, - pitch: rightCamera.pitch, - roll: rightCamera.roll - } + + // 记录当前右侧相机状态 + console.log('[useDualMapCompare] 右侧相机位置:', { + position: rightCamera.position, + heading: Cesium.Math.toDegrees(rightCamera.heading), + pitch: Cesium.Math.toDegrees(rightCamera.pitch), + roll: Cesium.Math.toDegrees(rightCamera.roll) }) + // 强制同步相机位置(使用多种方式确保成功) + try { + // 方式1: 使用 setView (推荐) + leftViewerInstance.camera.setView({ + destination: rightCamera.position.clone(), + orientation: { + heading: rightCamera.heading, + pitch: rightCamera.pitch, + roll: rightCamera.roll + } + }) + + // 方式2: 直接设置相机属性 (备份方案) + leftViewerInstance.camera.position = rightCamera.position.clone() + leftViewerInstance.camera.direction = rightCamera.direction.clone() + leftViewerInstance.camera.up = rightCamera.up.clone() + leftViewerInstance.camera.right = rightCamera.right.clone() + + console.log('[useDualMapCompare] 初始相机同步成功') + + // 验证同步结果 + const leftCamera = leftViewerInstance.camera + console.log('[useDualMapCompare] 左侧相机位置:', { + position: leftCamera.position, + heading: Cesium.Math.toDegrees(leftCamera.heading), + pitch: Cesium.Math.toDegrees(leftCamera.pitch), + roll: Cesium.Math.toDegrees(leftCamera.roll) + }) + } catch (error) { + console.error('[useDualMapCompare] 初始相机同步失败:', error) + } + + // 强制渲染左侧场景 + leftViewerInstance.scene.requestRender() + leftViewerInstance.camera.changed.raiseEvent() + // 设置相机同步(单向:右→左) setupCameraSync(rightViewerInstance, leftViewerInstance) @@ -396,6 +464,13 @@ export function useDualMapCompare() { console.log('[useDualMapCompare] 左侧Viewer已销毁') } + // 清理容器上的 viewer 引用 + const leftContainer = document.getElementById('leftCesiumContainer') + if (leftContainer && leftContainer._cesiumViewer) { + delete leftContainer._cesiumViewer + console.log('[useDualMapCompare] 已清理容器上的 viewer 引用') + } + // 触发右侧viewer resize恢复全屏 if (rightViewer.value && !rightViewer.value.isDestroyed()) { setTimeout(() => { diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/config/collapseBoundary.js b/packages/screen/src/views/3DSituationalAwarenessRefactor/config/collapseBoundary.js new file mode 100644 index 0000000..28b02cc --- /dev/null +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/config/collapseBoundary.js @@ -0,0 +1,121 @@ +/** + * 灾后坍塌边界Cartesian3坐标数据 + * 用于绘制蓝色实线边界 + */ +export const collapseBoundaryData = [ + { + x: -1706347.5312101133, + y: 5248165.078710412, + z: 3187394.127873529 + }, + { + x: -1706350.6376697333, + y: 5248165.028669288, + z: 3187394.2121145395 + }, + { + x: -1706352.8364036318, + y: 5248165.537099652, + z: 3187392.3620141866 + }, + { + x: -1706354.4533845682, + y: 5248165.6287691165, + z: 3187390.116655845 + }, + { + x: -1706355.2233232812, + y: 5248165.6021544365, + z: 3187387.277126487 + }, + { + x: -1706354.7432117031, + y: 5248164.178150934, + z: 3187384.824506141 + }, + { + x: -1706352.4841280153, + y: 5248163.731050433, + z: 3187383.6322611645 + }, + { + x: -1706351.712005713, + y: 5248162.186013809, + z: 3187381.647760827 + }, + { + x: -1706350.324299354, + y: 5248162.767355746, + z: 3187380.3936922415 + }, + { + x: -1706348.0854380748, + y: 5248165.163894288, + z: 3187379.1822132985 + }, + { + x: -1706346.0893322548, + y: 5248165.439709174, + z: 3187377.6419475204 + }, + { + x: -1706343.2634703086, + y: 5248165.840835863, + z: 3187378.9524312993 + }, + { + x: -1706341.9044425671, + y: 5248167.062865315, + z: 3187377.660352469 + }, + { + x: -1706339.8929941722, + y: 5248167.694347973, + z: 3187378.3585356465 + }, + { + x: -1706338.307401523, + y: 5248167.350063955, + z: 3187379.80788505 + }, + { + x: -1706338.0093617737, + y: 5248167.209763139, + z: 3187381.8907824843 + }, + { + x: -1706339.5459232442, + y: 5248166.094283297, + z: 3187382.861357757 + }, + { + x: -1706339.2268297782, + y: 5248165.94099689, + z: 3187385.2711631004 + }, + { + x: -1706340.8980982322, + y: 5248162.72254414, + z: 3187386.811143851 + }, + { + x: -1706342.4054570391, + y: 5248163.2478962615, + z: 3187387.4919083416 + }, + { + x: -1706342.8948843458, + y: 5248163.095840862, + z: 3187390.2277356824 + }, + { + x: -1706344.3551372418, + y: 5248163.910523915, + z: 3187392.2502082493 + }, + { + x: -1706346.0369703155, + y: 5248164.550006937, + z: 3187393.6480220454 + } +] diff --git a/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue b/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue index 476b96b..f86918d 100644 --- a/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue +++ b/packages/screen/src/views/3DSituationalAwarenessRefactor/index.vue @@ -284,6 +284,9 @@ import soldierIcon from './assets/images/SketchPngfbec927027ff9e49207749ebaafd22 import deviceIcon from './assets/images/SketchPng860d54f2a31f5f441fc6a88081224f1e98534bf6d5ca1246e420983bdf690380.png' import emergencyBaseIcon from './assets/images/应急基地.png' import reserveCenterIcon from './assets/images/储备中心.png' +import cityEmergencyIcon from './assets/images/市应急点.png' +import districtEmergencyIcon from './assets/images/区县应急点.png' +import otherEmergencyIcon from './assets/images/其他应急点.png' import mediaIcon from './assets/images/media.png' import collapseLeftArrow from './assets/images/折叠面板左箭头.png' import collapseRightArrow from './assets/images/折叠面板右箭头.png' @@ -329,6 +332,11 @@ const { markerEntities, reserveCenterEntities, emergencyResourceEntities, + drawCollapseBoundary, + drawCollapseBoundaryLeft, + clearCollapseBoundary, + showCollapseBoundary, + hideCollapseBoundary, } = useMapMarkers() // 3D Tiles 加载 @@ -366,6 +374,9 @@ const mapClickHandler = useMapClickHandler({ emergencyCenterIcon, emergencyBaseIcon, reserveCenterIcon, + cityEmergencyIcon, + districtEmergencyIcon, + otherEmergencyIcon, }, rangeCircleEntity, }) @@ -631,8 +642,8 @@ const handleForcePresetToggle = async () => { console.log('[index.vue] 已关闭地图对比模式') } - // 2. 显示所有标记和范围圈 - showAllMarkersAndRange() + // 2. 显示所有标记和范围圈(现在是异步函数) + await showAllMarkersAndRange() // 3. 调整相机到最佳视角 flyToBestViewForMarkers() @@ -705,7 +716,7 @@ const handleDistanceChange = async (newDistance) => { /** * 显示所有标记点和范围圈 */ -const showAllMarkersAndRange = () => { +const showAllMarkersAndRange = async () => { const viewer = mapStore.viewer if (!viewer) return @@ -730,15 +741,37 @@ const showAllMarkersAndRange = () => { console.log('[index.vue] 已清除其他标记') - // 2. 显示储备中心/预置点标记 - reserveCenterEntities.value.forEach((entity) => { - if (entity) { - entity.show = true - } - }) - console.log(`[index.vue] 显示 ${reserveCenterEntities.value.length} 个储备中心/预置点标记`) + // 2. 加载全部储备中心/预置点到地图(不限制距离) + console.log('[index.vue] 加载全部储备中心/预置点到地图...') + await loadReserveCentersAndPresets(DISASTER_CENTER.lon, DISASTER_CENTER.lat, true) - // 3. 在灾害中心点附近添加人员和装备点位 + // 3. 加载范围内的储备中心/预置点数据(用于面板显示和统计) + console.log('[index.vue] 加载范围内储备中心/预置点数据(用于面板显示)...') + const rangeResponse = await request({ + url: `/snow-ops-platform/yhYjll/list`, + method: 'GET', + params: { + longitude: DISASTER_CENTER.lon, + latitude: DISASTER_CENTER.lat, + maxDistance: disasterData.forcePreset.value.searchRadius, + }, + }) + + // 更新面板的 stations 数据(仅范围内) + if (rangeResponse?.data && Array.isArray(rangeResponse.data)) { + const transformedStations = disasterData.transformReserveDataToStations( + rangeResponse.data, + { longitude: DISASTER_CENTER.lon, latitude: DISASTER_CENTER.lat } + ) + if (transformedStations.length > 0) { + disasterData.forcePreset.value.stations = transformedStations + console.log('[index.vue] 已更新面板 stations(范围内):', transformedStations.length, '个') + } + } + + console.log(`[index.vue] 显示全部储备中心/预置点标记`) + + // 4. 在灾害中心点附近添加人员和装备点位 const offset = 0.0002 // 约22米偏移,更明显 // 检查是否已存在中心点人员和装备标记,避免重复添加 @@ -800,7 +833,7 @@ const showAllMarkersAndRange = () => { // 强制渲染场景 viewer.scene.requestRender() - // 4. 显示范围圈 + // 5. 显示范围圈 showRangeCircle() console.log('[index.vue] 快速响应标记显示完成') @@ -947,35 +980,45 @@ const loadEmergencyResources = async (longitude, latitude) => { /** * 加载储备中心和预置点数据 + * @param {number} longitude - 经度(可选,不传则查询全部) + * @param {number} latitude - 纬度(可选,不传则查询全部) + * @param {boolean} loadAllForMap - 是否加载全部点位到地图(true时不限制距离) */ -const loadReserveCentersAndPresets = async (longitude, latitude) => { +const loadReserveCentersAndPresets = async (longitude, latitude, loadAllForMap = false) => { try { + // 构建请求参数 + const params = {} + + if (!loadAllForMap && longitude !== undefined && latitude !== undefined) { + // 正常模式:传递经纬度和距离限制 + params.longitude = longitude + params.latitude = latitude + params.maxDistance = disasterData.forcePreset.value.searchRadius + } + // loadAllForMap 为 true 时,不传任何参数,获取全部数据 + const response = await request({ url: `/snow-ops-platform/yhYjll/list`, method: 'GET', - params: { - longitude, - latitude, - maxDistance: disasterData.forcePreset.value.searchRadius, - }, + params, }) if (response?.data && Array.isArray(response.data)) { - console.log('[index.vue] 储备中心和预置点数据加载成功:', response.data) + console.log('[index.vue] 储备中心和预置点数据加载成功:', response.data.length, '个点位') - // 1. 转换数据为标准 stations 格式并更新到 forcePreset + // 1. 转换数据为标准 stations 格式 const transformedStations = disasterData.transformReserveDataToStations( response.data, { longitude, latitude } ) - if (transformedStations.length > 0) { - // 更新 forcePreset.stations (用于 station-list 显示) + // 2. 仅在非"全部加载"模式下更新 forcePreset.stations(用于 station-list 显示) + if (!loadAllForMap && transformedStations.length > 0) { disasterData.forcePreset.value.stations = transformedStations - console.log('[index.vue] 已更新 forcePreset.stations:', transformedStations) + console.log('[index.vue] 已更新 forcePreset.stations:', transformedStations.length, '个') } - // 2. 添加地图标记 + // 3. 添加地图标记(全部或范围内) if (mapStore.viewer) { console.log('[index.vue] 添加储备中心和预置点地图标记...') clearReserveCenterMarkers(mapStore.viewer) @@ -1166,6 +1209,11 @@ const initializeScene = async () => { }, }) console.log('[index.vue] 中心点标记已添加') + + // 绘制坍塌边界蓝色实线 + console.log('[index.vue] 绘制坍塌边界蓝色实线...') + drawCollapseBoundary(viewer) + console.log('[index.vue] 坍塌边界蓝色实线已添加') } } catch (error) { console.error('[index.vue] 3D模型加载失败:', error) @@ -1176,6 +1224,14 @@ const initializeScene = async () => { console.log('[index.vue] 开始加载左侧灾前模型...') await toggleCompareMode(true, viewer, { loadLeftModel: true }) console.log('[index.vue] 左侧灾前模型加载完成') + + // 在左侧地图绘制蓝色虚线边界 + const leftContainer = document.getElementById('leftCesiumContainer') + if (leftContainer) { + console.log('[index.vue] 绘制左侧坍塌边界蓝色虚线...') + drawCollapseBoundaryLeft(leftContainer) + console.log('[index.vue] 左侧坍塌边界蓝色虚线已添加') + } } catch (error) { console.error('[index.vue] 左侧模型加载失败:', error) } @@ -1289,10 +1345,13 @@ onUnmounted(() => { console.log('[index.vue] 组件卸载,开始清理资源...') const viewer = mapStore.viewer + const leftContainer = document.getElementById('leftCesiumContainer') // 清理范围圈 if (viewer) { clearRangeCircle(viewer) + // 清理坍塌边界线(包括左右两侧) + clearCollapseBoundary(viewer, leftContainer) } // 清理地图点击处理器