From 273aa353ed542a79e4138215ed3b8233f8d81afc Mon Sep 17 00:00:00 2001 From: LiuJiaNan <15703339975@163.com> Date: Mon, 27 Jul 2026 15:29:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(map):=20=E4=BC=98=E5=8C=96=E4=BA=BA?= =?UTF-8?q?=E5=91=98=E5=AE=9A=E4=BD=8D=E5=88=9D=E5=A7=8B=E5=8C=96=E7=82=B9?= =?UTF-8?q?=E4=BD=8D=E9=97=AA=E7=83=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Map/components/BottomUtils/index.js | 2 + src/pages/Container/Map/js/useMapMethods.js | 52 +++++++++++++------ .../Container/Map/js/usePeoplePosition.js | 2 +- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/pages/Container/Map/components/BottomUtils/index.js b/src/pages/Container/Map/components/BottomUtils/index.js index 06ce84d..8cd2770 100644 --- a/src/pages/Container/Map/components/BottomUtils/index.js +++ b/src/pages/Container/Map/components/BottomUtils/index.js @@ -189,6 +189,8 @@ function BottomUtils(props) { } if (item.type === "peoplePosition" && currentPort === "00003") { + // 连接 WebSocket 前初始化本次定位会话的统一时钟,首批人员共享同一采样基准。 + mapMethods.current.initPeoplePositionClock(); peoplePositionConnect(); return; } diff --git a/src/pages/Container/Map/js/useMapMethods.js b/src/pages/Container/Map/js/useMapMethods.js index ae51883..5348093 100644 --- a/src/pages/Container/Map/js/useMapMethods.js +++ b/src/pages/Container/Map/js/useMapMethods.js @@ -92,6 +92,7 @@ export default function useMapMethods(viewerRef, request) { }); const peopleId = useRef([]); // 人员定位 ID 列表 + const peoplePositionStartTime = useRef(null); // 当前人员定位会话共用的采样起始时间 // 设置中心点 const flyTo = ({ longitude, latitude, height } = defineCenterPoint) => { @@ -120,6 +121,7 @@ export default function useMapMethods(viewerRef, request) { // 添加港口点 const addPortPoint = async () => { + const viewer = getViewer(); await chunkedLoad(portPoint, 10, async (item) => { const entityCollection = createEntityCollection("portEntityCollection"); entityCollection.entities.add( @@ -131,9 +133,9 @@ export default function useMapMethods(viewerRef, request) { monitorItems: { data: { ...item, markType: "港口" } }, }), ); - getViewer().dataSources.add(entityCollection); + viewer.dataSources.add(entityCollection); }); - await addMergedEntityCollection(getViewer(), "portEntityCollection"); + await addMergedEntityCollection(viewer, "portEntityCollection"); }; // 移除港口点 @@ -143,6 +145,7 @@ export default function useMapMethods(viewerRef, request) { // 添加分公司点 const addBranchOfficePoint = async (portArea = "", pointInfo = null) => { + const viewer = getViewer(); let requestData = []; if (!pointInfo) { const { data } = await request.getCorpInfoListAll({ eqPortArea: portArea, inType: [0, 1, 2, 6] }); @@ -169,9 +172,9 @@ export default function useMapMethods(viewerRef, request) { }, }), ); - getViewer().dataSources.add(entityCollection); + viewer.dataSources.add(entityCollection); }); - await addMergedEntityCollection(getViewer(), "branchOfficeEntityCollection"); + await addMergedEntityCollection(viewer, "branchOfficeEntityCollection"); mitt.emit(changeCoverMaskVisibleMittKey, false); }; @@ -253,6 +256,7 @@ export default function useMapMethods(viewerRef, request) { }; if (!edgeMap[id]) return; + const viewer = getViewer(); mitt.emit(changeCoverMaskVisibleMittKey, true); const currentEdge = edgeMap[id](); const entityCollection = createEntityCollection("wallEntityCollection"); @@ -275,7 +279,7 @@ export default function useMapMethods(viewerRef, request) { id: createId(), wall: { positions, - material: new Cesium.WallPolylineTrailLinkMaterialProperty(getViewer()), + material: new Cesium.WallPolylineTrailLinkMaterialProperty(viewer), maximumHeights: Array.from({ length: pointCount }).fill(40), minimumHeights: Array.from({ length: pointCount }).fill(0), }, @@ -283,7 +287,7 @@ export default function useMapMethods(viewerRef, request) { ); }); - getViewer().dataSources.add(entityCollection); + viewer.dataSources.add(entityCollection); mitt.emit(changeCoverMaskVisibleMittKey, false); }; @@ -372,6 +376,7 @@ export default function useMapMethods(viewerRef, request) { throw new Error("请传入markType(扎点类型)"); if (!options.markIcon) throw new Error("请传入markIcon(扎点图标)"); + const viewer = getViewer(); mitt.emit(changeCoverMaskVisibleMittKey, true); await chunkedLoad(filterNull(pointList), 10, async (item) => { const entityCollection = createEntityCollection( @@ -389,10 +394,10 @@ export default function useMapMethods(viewerRef, request) { }, }), ); - getViewer().dataSources.add(entityCollection); + viewer.dataSources.add(entityCollection); }); const dataSource = await addMergedEntityCollection( - getViewer(), + viewer, "markEntityCollection", options.markType, ); @@ -405,6 +410,18 @@ export default function useMapMethods(viewerRef, request) { removeEntityCollection(getViewer(), `markEntityCollectionMerged${markType ? `_${markType}` : ""}`); }; + // 人员定位使用 Viewer 的全局时钟;每次开启定位仅初始化一次,避免首批点位互相重置时钟。 + const initPeoplePositionClock = () => { + const start = Cesium.JulianDate.now(); + const viewer = getViewer(); + peoplePositionStartTime.current = start; + viewer.clock.startTime = start.clone(); + viewer.clock.currentTime = start.clone(); + viewer.clock.clockRange = Cesium.ClockRange.CLAMPED; + viewer.clock.shouldAnimate = false; + return start; + }; + const addPeoplePoint = async (point, options) => { const image = peopleImg[point.icon_type]; if (!image) @@ -413,7 +430,11 @@ export default function useMapMethods(viewerRef, request) { throw new Error("请传入markType(扎点类型)"); const clonePoint = { ...point }; point.property = new Cesium.SampledPositionProperty(); - const start = Cesium.JulianDate.now(); + // 下一条实时定位到达前保持最后有效位置,避免全局时钟前进后点位短暂消失。 + point.property.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD; + point.property.forwardExtrapolationDuration = 0; + // 首批人员必须共用同一采样起点,后续实体创建不会影响已存在人员的播放时间。 + const start = peoplePositionStartTime.current || initPeoplePositionClock(); const position = Cesium.Cartesian3.fromDegrees(point.x, point.y, 0); point.property.addSample(start, position); point.lastTime = start; @@ -431,18 +452,15 @@ export default function useMapMethods(viewerRef, request) { data: { ...clonePoint, markType: `标记点_${options.markType}`, isShowModal: options.isShowModal ?? true }, }, }); - getViewer().clock.startTime = start.clone(); - getViewer().clock.currentTime = start.clone(); - getViewer().clock.clockRange = Cesium.ClockRange.CLAMPED; - getViewer().clock.shouldAnimate = false; }; const updatePeoplePoint = async (point) => { - if (getViewer().clock.shouldAnimate === false) { - getViewer().clock.shouldAnimate = true; + const viewer = getViewer(); + if (viewer.clock.shouldAnimate === false) { + viewer.clock.shouldAnimate = true; } if (point.icon_type !== point.lastIconType) { - const entity = getViewer().entities.getById(point.id); + const entity = viewer.entities.getById(point.id); if (!entity) return; entity.billboard.image = ( @@ -473,6 +491,7 @@ export default function useMapMethods(viewerRef, request) { getViewer().entities.removeById(id); }); peopleId.current = []; + peoplePositionStartTime.current = null; }; return { @@ -490,6 +509,7 @@ export default function useMapMethods(viewerRef, request) { removeWall, addMarkPoint, removeMarkPoint, + initPeoplePositionClock, addPeoplePoint, updatePeoplePoint, removePeoplePoint, diff --git a/src/pages/Container/Map/js/usePeoplePosition.js b/src/pages/Container/Map/js/usePeoplePosition.js index 31de788..16febba 100644 --- a/src/pages/Container/Map/js/usePeoplePosition.js +++ b/src/pages/Container/Map/js/usePeoplePosition.js @@ -13,7 +13,7 @@ export default function usePeoplePosition(mapMethods, currentBranchOffice) { const onMessage = async (message) => { const data = JSON.parse(message.data); - const peopleList = filterNull([data]); + const peopleList = filterNull(Array.isArray(data) ? data : [data]); for (let i = 0; i < peopleList.length; i++) { const item = peopleList[i]; if (!item)