撤回 布局修改 任务点击的接口格式修改

This commit is contained in:
mmc
2026-09-08 10:12:09 +08:00
parent 3afbe9e131
commit 13bb3ca633
5 changed files with 164 additions and 153 deletions

View File

@@ -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) {

View File

@@ -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')}
</Button>
<Button
type="default"
onClick={handleUndoMark}
disabled={markedPoints?.length === 0}
>
{t('devices.undoMark')}
</Button>
<Button
type="default"
onClick={handleClearPoints}

View File

@@ -25,7 +25,7 @@ import {
import {
getRobotRouteList,
getSingleRouteList,
getPlaneDeviceListAll, saveDeviceTask, getRobotTaskList,
saveDeviceTask, getRobotTaskList,
deleteDeviceTask,
getSiteDevice,
excuetRoute,
@@ -184,15 +184,17 @@ export default function RobotTaskPage() {
if (!taskId) return;
try {
const res = await getRouteByTask({ taskId });
if (res?.code === 200 && res?.data) {
if (res?.code === 200 && res?.data?.jsonData?.path) {
let pathPoints = [];
try {
const data = res.data;
pathPoints = data || [];
pathPoints = data.jsonData.path || [];
setRouteMode(data?.mode || 'coverage'); // 设置路线模式
} catch (err) {
console.error('解析任务路径数据失败:', err);
}
setRouteMode('coverage')
setPolyPoints(pathPoints);
}
} catch (err) {
@@ -567,30 +569,6 @@ export default function RobotTaskPage() {
setExecuteModalVisible(true);
setSelectedRobotForExecute(null);
// 调用接口检查是否有路径点
const res = await getSingleRouteList({ id: route?.id });
let hasPathPoints = false;
if (res?.code === 200 && res?.data) {
try {
if (res.data.jsonData) {
const data = typeof res.data.jsonData === 'string'
? JSON.parse(res.data.jsonData)
: res.data.jsonData;
hasPathPoints = data?.path && data.path.length > 0;
}
} catch (err) {
console.error('解析路线数据失败:', err);
}
}
//if (!hasPathPoints) {
// utils.message.warning(t('devices.routeExecuteFixed2'));
// return;
//}
} catch (err) {
console.error('检查路线失败:', err);
utils.message.error(t('devices.checkRouteFail'));
@@ -1219,7 +1197,7 @@ export default function RobotTaskPage() {
extra={
<Space size={4} wrap> {/* Space开启自动换行,小屏按钮自动折行 */}
<Button size="small" icon={<DeleteOutlined />} type="primary" style={{ borderRadius: 4 }} onClick={() => setClearNavLine(v => v + 1)}>{t('devices.clearRealtimeTrack')}</Button>
<Button size="small" icon={<DeleteOutlined />} danger style={{ borderRadius: 4 }} onClick={() => { setPoints([]); setRouteMode(""); setClearPlanLine(v => v + 1); }}>{t('devices.clearPlanRoute')}</Button>
<Button size="small" icon={<DeleteOutlined />} danger style={{ borderRadius: 4 }} onClick={() => { setPoints([]); setPolyPoints([]); setRouteMode(""); setClearPlanLine(v => v + 1); }}>{t('devices.clearPlanRoute')}</Button>
{/*<Button size="small" icon={<PlusOutlined />} style={{ borderRadius: 4 }}>{t('common.addNew')}</Button>
<Button size="small" icon={<ImportOutlined />} style={{ borderRadius: 4 }}>{t('common.import')}</Button>
<Button size="small" icon={<NodeIndexOutlined />} style={{ borderRadius: 4 }}>{t('devices.optimize')}</Button>
@@ -1283,28 +1261,9 @@ export default function RobotTaskPage() {
<span style={{ color: '#9eff9e' }}>{t('devices.remoteMode')}</span>
)}
</span>
{/* const fixStatusMap: Record<string, string> = {
'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'),
}
*/}
<span>
{t('devices.heading')}
{robotRealtimePosition?.headingStatus == '1' ? (
<span style={{ color: '#9eff9e' }}>{t('devices.initialized')}</span>
) : (
<span style={{ color: '#ff6f6f' }}>{t('devices.uninitialized')}</span>
)}
</span>
<span>
{t('devices.location')}
@@ -1559,94 +1518,7 @@ export default function RobotTaskPage() {
</div>
</Card>
</Col>
<Col xs={24} xl={10} style={{ display: "flex", flexDirection: "column" }} >
<Card
title={<span style={{ fontSize: fontTitle, fontWeight: 700 }}>{t('devices.operationSuggestion')}</span>}
variant="borderless"
styles={{ body: { padding: '12px 16px' } }}
style={{ ...cardStyle, marginBottom: 8 }}
>
<Timeline
mode="left"
items={[
{
color: 'green',
label: '09:30',
children: (
<>
<Text strong>{t('devices.timelineMow', { robot: 'RBT-01' })}</Text>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.timelineMowDetail')}</div>
</>
)
},
{
color: 'blue',
label: '11:00',
children: (
<>
<Text strong>{t('devices.timelineInspect', { robot: 'RBT-02' })}</Text>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.timelineInspectDetail')}</div>
</>
)
},
{
color: 'gray',
label: '14:00',
children: (
<>
<Text strong>{t('devices.timelineMaintenance')}</Text>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.timelineMaintenanceDetail')}</div>
</>
)
}
]}
/>
<Button type="primary" block style={{ marginTop: 16, borderRadius: 18 }}>{t('devices.autoGenerateTodayPlan')}</Button>
</Card>
<Card
title={<span style={{ fontSize: fontTitle, fontWeight: 700 }}>{t('devices.deviceEnvironment')}</span>}
variant="borderless"
styles={{ body: { padding: '16px' } }}
style={{ ...cardStyle, flex: 1 }}
>
<Row gutter={[8, 8]}>
<Col span={12}>
<div style={{ background: '#F7F8FA', padding: 12, borderRadius: 10 }}>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.currentTemp')}</div>
<div style={{ fontSize: 18, fontWeight: 700 }}>24.5 ℃</div>
</div>
</Col>
<Col span={12}>
<div style={{ background: '#F7F8FA', padding: 12, borderRadius: 10 }}>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.humidity')}</div>
<div style={{ fontSize: 18, fontWeight: 700 }}>65 %</div>
</div>
</Col>
<Col span={12}>
<div style={{ background: '#F7F8FA', padding: 12, borderRadius: 10 }}>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.positionAccuracy')}</div>
<div style={{ fontSize: 18, fontWeight: 700, color: '#00B42A' }}>{t('devices.rtkFixed')}</div>
</div>
</Col>
<Col span={12}>
<div style={{ background: '#F7F8FA', padding: 12, borderRadius: 10 }}>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.networkLatency')}</div>
<div style={{ fontSize: 18, fontWeight: 700 }}>45 ms</div>
</div>
</Col>
</Row>
<div style={{ marginTop: 8, padding: 12, background: 'rgba(255, 125, 0, 0.1)', borderRadius: 8, display: 'flex', alignItems: 'center', gap: 8 }}>
<AlertCircle size={16} style={{ color: '#FF7D00' }} />
<span style={{ fontSize: 13, color: '#FF7D00' }}>{t('devices.waterRiskWarning')}</span>
</div>
</Card>
</Col>
</Row>
<Row gutter={16}>
<Col xs={24} xl={6}>
<Col xs={24} xl={10}>
<Card
title={
<div className="flex justify-between items-center w-full">
@@ -1661,7 +1533,7 @@ export default function RobotTaskPage() {
</div>
}
variant="borderless"
styles={{ body: { padding: '12px', height: 400, overflow: 'auto' } }}
styles={{ body: { padding: '12px', height: 732, overflow: 'auto' } }}
style={cardStyle}
>
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
@@ -1789,16 +1661,105 @@ export default function RobotTaskPage() {
</div>
</Card>
</Col>
<Col xs={24} xl={18}>
</Row>
<Row gutter={16} >
<Col xs={24} xl={6} style={{ display: "flex", flexDirection: "column" }} >
<Card
title={<span style={{ fontSize: fontTitle, fontWeight: 700 }}>{t('devices.operationSuggestion')}</span>}
variant="borderless"
styles={{ body: { padding: '12px 16px' } }}
style={{ ...cardStyle, marginBottom: 8 }}
>
<Timeline
mode="left"
items={[
{
color: 'green',
label: '09:30',
children: (
<>
<Text strong>{t('devices.timelineMow', { robot: 'RBT-01' })}</Text>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.timelineMowDetail')}</div>
</>
)
},
{
color: 'blue',
label: '11:00',
children: (
<>
<Text strong>{t('devices.timelineInspect', { robot: 'RBT-02' })}</Text>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.timelineInspectDetail')}</div>
</>
)
},
{
color: 'gray',
label: '14:00',
children: (
<>
<Text strong>{t('devices.timelineMaintenance')}</Text>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.timelineMaintenanceDetail')}</div>
</>
)
}
]}
/>
<Button type="primary" block style={{ marginTop: 16, borderRadius: 18 }}>{t('devices.autoGenerateTodayPlan')}</Button>
</Card>
<Card
title={<span style={{ fontSize: fontTitle, fontWeight: 700 }}>{t('devices.deviceEnvironment')}</span>}
variant="borderless"
styles={{ body: { padding: '16px' } }}
style={{ ...cardStyle, flex: 1 }}
>
<Row gutter={[8, 8]}>
<Col span={12}>
<div style={{ background: '#F7F8FA', padding: 12, borderRadius: 10 }}>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.currentTemp')}</div>
<div style={{ fontSize: 18, fontWeight: 700 }}>24.5 ℃</div>
</div>
</Col>
<Col span={12}>
<div style={{ background: '#F7F8FA', padding: 12, borderRadius: 10 }}>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.humidity')}</div>
<div style={{ fontSize: 18, fontWeight: 700 }}>65 %</div>
</div>
</Col>
<Col span={12}>
<div style={{ background: '#F7F8FA', padding: 12, borderRadius: 10 }}>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.positionAccuracy')}</div>
<div style={{ fontSize: 18, fontWeight: 700, color: '#00B42A' }}>{t('devices.rtkFixed')}</div>
</div>
</Col>
<Col span={12}>
<div style={{ background: '#F7F8FA', padding: 12, borderRadius: 10 }}>
<div style={{ fontSize: 12, color: '#86909C' }}>{t('devices.networkLatency')}</div>
<div style={{ fontSize: 18, fontWeight: 700 }}>45 ms</div>
</div>
</Col>
</Row>
<div style={{ marginTop: 8, padding: 12, background: 'rgba(255, 125, 0, 0.1)', borderRadius: 8, display: 'flex', alignItems: 'center', gap: 8 }}>
<AlertCircle size={16} style={{ color: '#FF7D00' }} />
<span style={{ fontSize: 13, color: '#FF7D00' }}>{t('devices.waterRiskWarning')}</span>
</div>
</Card>
</Col>
<Col xs={24} xl={18} style={{ display: 'flex', flexDirection: 'column', }}>
<Card
title={<span style={{ fontSize: fontTitle, fontWeight: 700 }}>{t('devices.taskBoard')}</span>}
variant="borderless"
styles={{ body: { padding: '12px' } }}
style={{ ...cardStyle, height: '100%' }}
styles={{ body: { padding: '12px', flex: 1 } }}
style={{ ...cardStyle, height: '100%', display: "flex", flexDirection: "column" }}
>
<Row gutter={16}>
<Row gutter={16} style={{ height: "100%" }} >
<Col span={8}>
<div style={{ background: '#F7F8FA', padding: 12, borderRadius: 8, height: 400 }}>
<div style={{ background: '#F7F8FA', padding: 12, borderRadius: 8, height: "100%" }}>
<Title level={5} style={{ fontSize: 14, marginBottom: 12 }}><Badge color="#86909C" text={t('devices.pending')} /></Title>
{waitList.map((item, idx) => (
<Card key={idx} size="small" style={{ marginBottom: 8, borderRadius: 6 }}>
@@ -1809,7 +1770,7 @@ export default function RobotTaskPage() {
</div>
</Col>
<Col span={8}>
<div style={{ background: '#E8F3FF', padding: 12, borderRadius: 8, height: 400 }}>
<div style={{ background: '#E8F3FF', padding: 12, borderRadius: 8, height: "100%" }}>
<Title level={5} style={{ fontSize: 14, marginBottom: 12 }}><Badge color="#165DFF" text={t('devices.statInProgress')} /></Title>
{runList.map((item, idx) => (
<Card key={idx} size="small" style={{ marginBottom: 8, borderRadius: 6 }}>
@@ -1822,7 +1783,7 @@ export default function RobotTaskPage() {
</div>
</Col>
<Col span={8}>
<div style={{ background: '#F6FFED', padding: 12, borderRadius: 8, height: 400 }}>
<div style={{ background: '#F6FFED', padding: 12, borderRadius: 8, height: "100%" }}>
<Title level={5} style={{ fontSize: 14, marginBottom: 12 }}><Badge color="#52C41A" text={t('workOrder.completed')} /></Title>
{finishList.slice(0, 3).map((item, idx) => (
<Card key={idx} size="small" style={{ marginBottom: 8, borderRadius: 6 }}>

View File

@@ -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',

View File

@@ -631,6 +631,9 @@ const zh = {
lostControlAction: '丢失控制操作',
lostControlActionLabel: '丢失控制操作标签',
markPoint: '打点',
undoMark: '撤回打点',
noPointsToUndo: '暂无打点可撤回',
pointUndone: '已撤回,剩余 {{count}} 个打点',
markedPointsCount: '已标记点数量',
minBattery: '最小电量',
minBatteryLabel: '最小电量标签',