diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 4c76587..50043bb 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -40,6 +40,9 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const polygonRef = useRef(null); // coverage 区域多边形 const navLineRef = useRef(null); // 完成点实时轨迹 const markedPointsRef = useRef([]); // 标记点实体 + const markedLineRef = useRef(null); // 标记点连线实体 + const onMarkPointRef = useRef(onMarkPoint); // 总是指向最新的 onMarkPoint,避免闭包滞后 + const onCameraClickRef = useRef(onCameraClick); // 同样处理,避免闭包滞后 const { t } = useTranslation(); @@ -474,19 +477,41 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, } }, [points]); - // 渲染绿色标记点 + // 渲染标记点 + 连线(唯一系统,避免重复标记) const drawMarkedPoints = (points) => { const viewer = viewerRef.current; if (!viewer) return; - // 先清空之前的标记点 + // 先清空之前的标记点与连线 markedPointsRef.current.forEach(id => { viewer.entities.removeById(id); }); markedPointsRef.current = []; + if (markedLineRef.current) { + try { + viewer.entities.remove(markedLineRef.current); + } catch (e) { } + markedLineRef.current = null; + } + if (!points || points.length === 0) return; + // 先绘制连线:有 2 个及以上打点才连线 + if (points.length >= 2) { + const linePositions = points.map(p => + window.Cesium.Cartesian3.fromDegrees(Number(p.lon), Number(p.lat)) + ); + markedLineRef.current = viewer.entities.add({ + polyline: { + positions: linePositions, + width: 4, + material: window.Cesium.Color.SKYBLUE, + clampToGround: true, + } + }); + } + points.forEach((p, i) => { const id = `marked-point-${i}`; viewer.entities.add({ @@ -1627,6 +1652,10 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, }; }, [points]); + // 每次渲染都更新回调引用,确保单击处理器用最新版本(避免闭包滞后) + onMarkPointRef.current = onMarkPoint; + onCameraClickRef.current = onCameraClick; + useEffect(() => { const viewer = viewerRef.current; if (!viewer) return; @@ -1650,7 +1679,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const picked = viewer.scene.pick(movement.position); if (window.Cesium.defined(picked) && picked.id?.properties?.robot) { const robot = picked.id.properties.robot.getValue(); - robot.feStatus == 1 && onCameraClick?.(robot); + robot.feStatus == 1 && onCameraClickRef.current?.(robot); } }, window.Cesium.ScreenSpaceEventType.LEFT_CLICK); @@ -1663,10 +1692,7 @@ const CesiumMap = React.forwardRef(({ ifClear, ifClearPlanLine, ifClearNavLine, const lon = window.Cesium.Math.toDegrees(cartographic.longitude); const lat = window.Cesium.Math.toDegrees(cartographic.latitude); const newPoint = { lon, lat }; - const newPoints = [...drawPoints, newPoint]; - setDrawPoints(newPoints); - updateDrawPath(newPoints); - onMarkPoint?.({ lon, lat }); + onMarkPointRef.current?.({ lon, lat }); }, window.Cesium.ScreenSpaceEventType.LEFT_CLICK); if (onClearDraw) { diff --git a/src/components/devices/DeviceStatusPage.tsx b/src/components/devices/DeviceStatusPage.tsx index 7700dd1..67ffb29 100644 --- a/src/components/devices/DeviceStatusPage.tsx +++ b/src/components/devices/DeviceStatusPage.tsx @@ -680,6 +680,17 @@ export default function DeviceStatusPage() { messageApi.success(t('devices.pointMarked', { count: markedPoints.length + 1 })); }; + // 撤回上一个打点 + const handleUndoMark = () => { + if (markedPoints.length === 0) { + message.info(t('devices.noPointsToUndo')); + return; + } + const newPoints = markedPoints.slice(0, -1); + setMarkedPoints(newPoints); + messageApi.success(t('devices.pointUndone', { count: newPoints.length })); + }; + // 清空标记点 const handleClearPoints = () => { setMarkedPoints([]); @@ -924,6 +935,13 @@ export default function DeviceStatusPage() { > {t('devices.markPoint')} + - + {/* @@ -1283,28 +1261,9 @@ export default function RobotTaskPage() { {t('devices.remoteMode')} )} - {/* const fixStatusMap: Record = { - 'no_fix': t('devices.noFix'), - 'single': t('devices.single'), - 'dgnss': t('devices.dgnss'), - 'ins': t('devices.ins'), - 'rtk_fixed': t('devices.rtkFixed'), - 'rtk_float': t('devices.rtkFloat'), - 'single_no_heading': t('devices.singleNoHeading'), - 'dgnss_no_heading': t('devices.dgnssNoHeading'), - 'rtk_fixed_no_heading': t('devices.rtkFixedNoHeading'), - 'rtk_float_no_heading': t('devices.rtkFloatNoHeading'), - } - */} - - {t('devices.heading')} - {robotRealtimePosition?.headingStatus == '1' ? ( - {t('devices.initialized')} - ) : ( - {t('devices.uninitialized')} - )} - + + {t('devices.location')} @@ -1559,94 +1518,7 @@ export default function RobotTaskPage() { - - - {t('devices.operationSuggestion')}} - variant="borderless" - styles={{ body: { padding: '12px 16px' } }} - style={{ ...cardStyle, marginBottom: 8 }} - > - - {t('devices.timelineMow', { robot: 'RBT-01' })} -
{t('devices.timelineMowDetail')}
- - ) - }, - { - color: 'blue', - label: '11:00', - children: ( - <> - {t('devices.timelineInspect', { robot: 'RBT-02' })} -
{t('devices.timelineInspectDetail')}
- - ) - }, - { - color: 'gray', - label: '14:00', - children: ( - <> - {t('devices.timelineMaintenance')} -
{t('devices.timelineMaintenanceDetail')}
- - ) - } - ]} - /> - - - - {t('devices.deviceEnvironment')}} - variant="borderless" - styles={{ body: { padding: '16px' } }} - style={{ ...cardStyle, flex: 1 }} - > - - -
-
{t('devices.currentTemp')}
-
24.5 ℃
-
- - -
-
{t('devices.humidity')}
-
65 %
-
- - -
-
{t('devices.positionAccuracy')}
-
{t('devices.rtkFixed')}
-
- - -
-
{t('devices.networkLatency')}
-
45 ms
-
- -
-
- - {t('devices.waterRiskWarning')} -
-
- - - - - + @@ -1661,7 +1533,7 @@ export default function RobotTaskPage() { } variant="borderless" - styles={{ body: { padding: '12px', height: 400, overflow: 'auto' } }} + styles={{ body: { padding: '12px', height: 732, overflow: 'auto' } }} style={cardStyle} >
@@ -1789,16 +1661,105 @@ export default function RobotTaskPage() {
- + + +
+ + + + {t('devices.operationSuggestion')}} + variant="borderless" + styles={{ body: { padding: '12px 16px' } }} + style={{ ...cardStyle, marginBottom: 8 }} + > + + {t('devices.timelineMow', { robot: 'RBT-01' })} +
{t('devices.timelineMowDetail')}
+ + ) + }, + { + color: 'blue', + label: '11:00', + children: ( + <> + {t('devices.timelineInspect', { robot: 'RBT-02' })} +
{t('devices.timelineInspectDetail')}
+ + ) + }, + { + color: 'gray', + label: '14:00', + children: ( + <> + {t('devices.timelineMaintenance')} +
{t('devices.timelineMaintenanceDetail')}
+ + ) + } + ]} + /> + +
+ + {t('devices.deviceEnvironment')}} + variant="borderless" + styles={{ body: { padding: '16px' } }} + style={{ ...cardStyle, flex: 1 }} + > + + +
+
{t('devices.currentTemp')}
+
24.5 ℃
+
+ + +
+
{t('devices.humidity')}
+
65 %
+
+ + +
+
{t('devices.positionAccuracy')}
+
{t('devices.rtkFixed')}
+
+ + +
+
{t('devices.networkLatency')}
+
45 ms
+
+ +
+
+ + {t('devices.waterRiskWarning')} +
+
+ + + {t('devices.taskBoard')}} variant="borderless" - styles={{ body: { padding: '12px' } }} - style={{ ...cardStyle, height: '100%' }} + styles={{ body: { padding: '12px', flex: 1 } }} + style={{ ...cardStyle, height: '100%', display: "flex", flexDirection: "column" }} > - + -
+
<Badge color="#86909C" text={t('devices.pending')} /> {waitList.map((item, idx) => ( @@ -1809,7 +1770,7 @@ export default function RobotTaskPage() {
-
+
<Badge color="#165DFF" text={t('devices.statInProgress')} /> {runList.map((item, idx) => ( @@ -1822,7 +1783,7 @@ export default function RobotTaskPage() {
-
+
<Badge color="#52C41A" text={t('workOrder.completed')} /> {finishList.slice(0, 3).map((item, idx) => ( diff --git a/src/locales/en/index.ts b/src/locales/en/index.ts index 4409fbe..dbcc216 100644 --- a/src/locales/en/index.ts +++ b/src/locales/en/index.ts @@ -609,6 +609,9 @@ const en = { lostControlAction: 'Lost Control Action', lostControlActionLabel: 'Lost Control Action Label', markPoint: 'Mark Point', + undoMark: 'Undo Mark', + noPointsToUndo: 'No points to undo', + pointUndone: 'Undone, {{count}} points left', markedPointsCount: 'Marked Points Count', minBattery: 'Min Battery', minBatteryLabel: 'Min Battery Label', diff --git a/src/locales/zh/index.ts b/src/locales/zh/index.ts index 426b6a7..476c3dc 100644 --- a/src/locales/zh/index.ts +++ b/src/locales/zh/index.ts @@ -631,6 +631,9 @@ const zh = { lostControlAction: '丢失控制操作', lostControlActionLabel: '丢失控制操作标签', markPoint: '打点', + undoMark: '撤回打点', + noPointsToUndo: '暂无打点可撤回', + pointUndone: '已撤回,剩余 {{count}} 个打点', markedPointsCount: '已标记点数量', minBattery: '最小电量', minBatteryLabel: '最小电量标签',