|
@ -1,5 +1,5 @@ |
|
|
import React, { useState, useEffect, useRef } from "react"; |
|
|
|
|
|
import { Button, Checkbox, CheckboxProps, message, Switch } from "antd"; |
|
|
|
|
|
|
|
|
import React, { useState, useEffect, useRef, useCallback, useMemo } from "react"; |
|
|
|
|
|
import { Button, Checkbox, CheckboxProps, Drawer, message, Switch } from "antd"; |
|
|
import { useNavigate } from "react-router"; |
|
|
import { useNavigate } from "react-router"; |
|
|
import { |
|
|
import { |
|
|
fetchAnalysisReport, |
|
|
fetchAnalysisReport, |
|
@ -9,8 +9,8 @@ import { |
|
|
} from "../../../services/measure/analysis"; |
|
|
} from "../../../services/measure/analysis"; |
|
|
import { createWebSocket, sharedWsUrl } from "../../../services/socket"; |
|
|
import { createWebSocket, sharedWsUrl } from "../../../services/socket"; |
|
|
import { switchMeasureAfterSave } from "../../../store/features/contextSlice"; |
|
|
import { switchMeasureAfterSave } from "../../../store/features/contextSlice"; |
|
|
import { AnalysisReport, AnalyzeAngle } from "../../../services/measure/type"; |
|
|
|
|
|
import { MeasureState, TaskState, taskStatusDescMap, TrackRecordSig } from "../../../services/wsTypes"; |
|
|
|
|
|
|
|
|
import { AnalysisReport } from "../../../services/measure/type"; |
|
|
|
|
|
import { MeasureState, TaskState, TrackRecordSig } from "../../../services/wsTypes"; |
|
|
import { useAppDispatch, useAppSelector } from "../../../utils/hooks"; |
|
|
import { useAppDispatch, useAppSelector } from "../../../utils/hooks"; |
|
|
import Gr_round from "../../../assets/green_round.svg"; |
|
|
import Gr_round from "../../../assets/green_round.svg"; |
|
|
import Bl_round from "../../../assets/blue_round.svg"; |
|
|
import Bl_round from "../../../assets/blue_round.svg"; |
|
@ -34,28 +34,34 @@ export default function MeasureAction() { |
|
|
const [showGrid, setShowGrid] = useState(true); |
|
|
const [showGrid, setShowGrid] = useState(true); |
|
|
const [showStandard, setShowStandard] = useState(true); |
|
|
const [showStandard, setShowStandard] = useState(true); |
|
|
const [showMark, setShowMark] = useState(true); |
|
|
const [showMark, setShowMark] = useState(true); |
|
|
|
|
|
// showMark的备份,记录showMark最近一次的值
|
|
|
const [angleMarkBackup, setAngleMarkBackup] = useState(true); |
|
|
const [angleMarkBackup, setAngleMarkBackup] = useState(true); |
|
|
const afterSave = useAppSelector(store => store.context.newMeasureAfterSave); |
|
|
const afterSave = useAppSelector(store => store.context.newMeasureAfterSave); |
|
|
// const [angles, setAngles] = useState<AnalyzeAngle[]>([]);
|
|
|
// const [angles, setAngles] = useState<AnalyzeAngle[]>([]);
|
|
|
// const [taskStatus, setTaskStatus] = useState<MeasureState["data"]["taskStatus"]>("IDLE");
|
|
|
// const [taskStatus, setTaskStatus] = useState<MeasureState["data"]["taskStatus"]>("IDLE");
|
|
|
const [startBtnText, setStartBtnText] = useState("开始测量"); |
|
|
const [startBtnText, setStartBtnText] = useState("开始测量"); |
|
|
const [measurementFinished, setMeasurementFinished] = useState(false); |
|
|
const [measurementFinished, setMeasurementFinished] = useState(false); |
|
|
const [analysisClicked, setAnalysisClicked] = useState(false); |
|
|
|
|
|
const [saveClicked, setSaveClicked] = useState(false); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 【分析】之后,会得到分析报告
|
|
|
const [analysisReport, setAnalysisReport] = useState<AnalysisReport | null>(null); |
|
|
const [analysisReport, setAnalysisReport] = useState<AnalysisReport | null>(null); |
|
|
const [showAnalysisTable, setShowAnalysisTable] = useState(false); |
|
|
|
|
|
|
|
|
// const [showAnalysisTable, setShowAnalysisTable] = useState(false);
|
|
|
// const [taskStatusName, setTaskStatusName] = useState("");
|
|
|
// const [taskStatusName, setTaskStatusName] = useState("");
|
|
|
|
|
|
|
|
|
// 初始状态列表
|
|
|
// 初始状态列表
|
|
|
const initialStatusList = [ |
|
|
|
|
|
{ statusCode: "START_RECORD_LEFT", name: "请移动到顶部,停顿2秒", background: "#ececec", isReady: false, color: "h" }, |
|
|
|
|
|
{ statusCode: "START_RECORD_LEFT", name: "开始测量左侧", background: "#ececec", isReady: false, color: "h" }, |
|
|
|
|
|
{ statusCode: "START_RECORD_LEFT", name: "左侧测量完成", background: "#ececec", isReady: false, color: "h" }, |
|
|
|
|
|
{ statusCode: "START_RECORD_LEFT", name: "请移动到顶部,停顿2秒", background: "#ececec", isReady: false, color: "h" }, |
|
|
|
|
|
{ statusCode: "START_RECORD_LEFT", name: "开始测量右侧", background: "#ececec", isReady: false, color: "h" }, |
|
|
|
|
|
{ statusCode: "START_RECORD_LEFT", name: "右侧测量完成", background: "#ececec", isReady: false, color: "h" }, |
|
|
|
|
|
]; |
|
|
|
|
|
|
|
|
const initialStatusList = useMemo( |
|
|
|
|
|
() => [ |
|
|
|
|
|
{ name: "请移动到顶部,停顿2秒", background: "#ececec", isReady: false, color: "h" }, |
|
|
|
|
|
{ name: "开始测量左侧", background: "#ececec", isReady: false, color: "h" }, |
|
|
|
|
|
{ name: "左侧测量完成", background: "#ececec", isReady: false, color: "h" }, |
|
|
|
|
|
{ name: "请移动到顶部,停顿2秒", background: "#ececec", isReady: false, color: "h" }, |
|
|
|
|
|
{ name: "开始测量右侧", background: "#ececec", isReady: false, color: "h" }, |
|
|
|
|
|
{ name: "右侧测量完成", background: "#ececec", isReady: false, color: "h" }, |
|
|
|
|
|
], |
|
|
|
|
|
[] |
|
|
|
|
|
); |
|
|
const [statusList, setStatusList] = useState(initialStatusList); |
|
|
const [statusList, setStatusList] = useState(initialStatusList); |
|
|
|
|
|
// 打开抽屉(显示分析结果)
|
|
|
|
|
|
const [openDrawer, setOpenDrawer] = useState(false); |
|
|
|
|
|
|
|
|
/** ----------------------- 事件处理函数 ----------------------- */ |
|
|
/** ----------------------- 事件处理函数 ----------------------- */ |
|
|
// 切换保存后自动开始新测量
|
|
|
// 切换保存后自动开始新测量
|
|
@ -65,7 +71,10 @@ export default function MeasureAction() { |
|
|
|
|
|
|
|
|
// 分析按钮点击事件
|
|
|
// 分析按钮点击事件
|
|
|
const onAnalysisBtnClick = () => { |
|
|
const onAnalysisBtnClick = () => { |
|
|
setAnalysisClicked(true); |
|
|
|
|
|
|
|
|
if (analysisReport) { |
|
|
|
|
|
setOpenDrawer(true); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
fetchAnalysisReport("6001").then(res => { |
|
|
fetchAnalysisReport("6001").then(res => { |
|
|
if (res.success) { |
|
|
if (res.success) { |
|
|
const report: AnalysisReport = res.data; |
|
|
const report: AnalysisReport = res.data; |
|
@ -85,7 +94,7 @@ export default function MeasureAction() { |
|
|
canvasRef.current?.setAnalysisData(analysisData); |
|
|
canvasRef.current?.setAnalysisData(analysisData); |
|
|
} |
|
|
} |
|
|
setAnalysisReport(report); |
|
|
setAnalysisReport(report); |
|
|
setShowAnalysisTable(true); |
|
|
|
|
|
|
|
|
setOpenDrawer(true); |
|
|
} else { |
|
|
} else { |
|
|
message.error("分析报告请求失败: " + res.data.info); |
|
|
message.error("分析报告请求失败: " + res.data.info); |
|
|
} |
|
|
} |
|
@ -93,16 +102,11 @@ export default function MeasureAction() { |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 开始/重新测量按钮点击事件
|
|
|
// 开始/重新测量按钮点击事件
|
|
|
const onStart = () => { |
|
|
|
|
|
if (startBtnText === "新测量") { |
|
|
|
|
|
navigate("../config"); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const onStart = useCallback(() => { |
|
|
// 重置测量相关状态
|
|
|
// 重置测量相关状态
|
|
|
setShowAnalysisTable(false); |
|
|
|
|
|
setMeasurementFinished(false); |
|
|
setMeasurementFinished(false); |
|
|
setAnalysisClicked(false); |
|
|
|
|
|
setSaveClicked(false); |
|
|
|
|
|
|
|
|
setAnalysisReport(null); |
|
|
|
|
|
|
|
|
isLeftFinished.current = false; |
|
|
isLeftFinished.current = false; |
|
|
leftPoints.current = []; |
|
|
leftPoints.current = []; |
|
|
rightPoints.current = []; |
|
|
rightPoints.current = []; |
|
@ -124,20 +128,17 @@ export default function MeasureAction() { |
|
|
setStartBtnText("重新测量"); |
|
|
setStartBtnText("重新测量"); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
}; |
|
|
|
|
|
|
|
|
}, [initialStatusList, startBtnText]); |
|
|
|
|
|
|
|
|
// 保存按钮点击事件
|
|
|
// 保存按钮点击事件
|
|
|
const onSaveBtnClick = () => { |
|
|
const onSaveBtnClick = () => { |
|
|
setSaveClicked(true); |
|
|
|
|
|
saveMeasurement().then(res => { |
|
|
saveMeasurement().then(res => { |
|
|
if (res.status !== 0) { |
|
|
if (res.status !== 0) { |
|
|
message.error(res.data.info); |
|
|
message.error(res.data.info); |
|
|
} else { |
|
|
} else { |
|
|
message.success("保存成功"); |
|
|
message.success("保存成功"); |
|
|
if (afterSave) { |
|
|
if (afterSave) { |
|
|
navigate("../config"); |
|
|
|
|
|
} else { |
|
|
|
|
|
setStartBtnText("新测量"); |
|
|
|
|
|
|
|
|
navigate("../config", { replace: true }); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
@ -279,7 +280,7 @@ export default function MeasureAction() { |
|
|
}); |
|
|
}); |
|
|
wsClient.connect(); |
|
|
wsClient.connect(); |
|
|
return () => subscription.unsubscribe(); |
|
|
return () => subscription.unsubscribe(); |
|
|
}, []); |
|
|
|
|
|
|
|
|
}, [onStart]); |
|
|
|
|
|
|
|
|
/** ----------------------- 页面加载获取基础图形数据 ----------------------- **/ |
|
|
/** ----------------------- 页面加载获取基础图形数据 ----------------------- **/ |
|
|
useEffect(() => { |
|
|
useEffect(() => { |
|
@ -294,12 +295,21 @@ export default function MeasureAction() { |
|
|
}); |
|
|
}); |
|
|
}, []); |
|
|
}, []); |
|
|
|
|
|
|
|
|
|
|
|
const onExport = () => {}; |
|
|
/** ----------------------- 渲染 ----------------------- **/ |
|
|
/** ----------------------- 渲染 ----------------------- **/ |
|
|
return ( |
|
|
return ( |
|
|
|
|
|
<> |
|
|
<div className="flex h-full px-6"> |
|
|
<div className="flex h-full px-6"> |
|
|
{/* 左侧区域:包含开关区域和测量画布 */} |
|
|
{/* 左侧区域:包含开关区域和测量画布 */} |
|
|
<div className=""> |
|
|
<div className=""> |
|
|
<div className="flex gap-4 items-center px-6 pt-5"> |
|
|
|
|
|
|
|
|
<div className="relative flex gap-4 items-center pt-[10px]"> |
|
|
|
|
|
<div |
|
|
|
|
|
className="absolute flex gap-1 items-center" |
|
|
|
|
|
onClick={() => navigate("../config", { replace: true })}> |
|
|
|
|
|
<i className="border-t-2 border-l-2 border-primary w-[10px] h-[10px] -rotate-45"></i> |
|
|
|
|
|
<span className="text-primary text-base font-medium">返回</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
<section className="ml-auto flex gap-4 items-center"> |
|
|
{/* 参考线开关 */} |
|
|
{/* 参考线开关 */} |
|
|
<div className="flex gap-2 items-center"> |
|
|
<div className="flex gap-2 items-center"> |
|
|
<Switch defaultChecked onChange={checked => setShowGrid(checked)} /> |
|
|
<Switch defaultChecked onChange={checked => setShowGrid(checked)} /> |
|
@ -322,7 +332,7 @@ export default function MeasureAction() { |
|
|
<span>标准线</span> |
|
|
<span>标准线</span> |
|
|
</div> |
|
|
</div> |
|
|
{/* 角度线开关,仅在点击分析按钮后显示 */} |
|
|
{/* 角度线开关,仅在点击分析按钮后显示 */} |
|
|
{analysisClicked && ( |
|
|
|
|
|
|
|
|
{analysisReport && ( |
|
|
<div className="flex gap-2 items-center"> |
|
|
<div className="flex gap-2 items-center"> |
|
|
<Switch |
|
|
<Switch |
|
|
checked={showMark} |
|
|
checked={showMark} |
|
@ -335,7 +345,9 @@ export default function MeasureAction() { |
|
|
<span>角度线</span> |
|
|
<span>角度线</span> |
|
|
</div> |
|
|
</div> |
|
|
)} |
|
|
)} |
|
|
|
|
|
</section> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div className="relative m-2"> |
|
|
<div className="relative m-2"> |
|
|
<MeasurementCanvas |
|
|
<MeasurementCanvas |
|
|
width={800} |
|
|
width={800} |
|
@ -357,49 +369,6 @@ export default function MeasureAction() { |
|
|
</div> |
|
|
</div> |
|
|
{/* 右侧区域:根据 showAnalysisTable 状态显示测量步骤或分析表格 */} |
|
|
{/* 右侧区域:根据 showAnalysisTable 状态显示测量步骤或分析表格 */} |
|
|
<div className="min-w-[300px] flex-auto py-6 flex flex-col items-center"> |
|
|
<div className="min-w-[300px] flex-auto py-6 flex flex-col items-center"> |
|
|
{showAnalysisTable && analysisReport ? ( |
|
|
|
|
|
<> |
|
|
|
|
|
<header className="bg-[#e8f0ff] w-[300px] text-center text-lg font-medium py-2 text-primary border border-[#c1c6d4]"> |
|
|
|
|
|
分析 |
|
|
|
|
|
</header> |
|
|
|
|
|
<div className="analysis-table"> |
|
|
|
|
|
<table |
|
|
|
|
|
style={{ |
|
|
|
|
|
width: "100%", |
|
|
|
|
|
borderCollapse: "collapse", |
|
|
|
|
|
border: "1px solid #ccc", |
|
|
|
|
|
textAlign: "center", |
|
|
|
|
|
}}> |
|
|
|
|
|
<tbody> |
|
|
|
|
|
<tr style={{ height: "40px", fontSize: "18px", color: "#9E9E9E" }}> |
|
|
|
|
|
<td style={{ padding: "8px", border: "1px solid #ccc" }}>W1垂直磨耗</td> |
|
|
|
|
|
<td style={{ padding: "8px", border: "1px solid #ccc" }}>{analysisReport.w1}</td> |
|
|
|
|
|
</tr> |
|
|
|
|
|
<tr style={{ height: "40px", fontSize: "18px", color: "#9E9E9E" }}> |
|
|
|
|
|
<td style={{ padding: "8px", border: "1px solid #ccc" }}>轨头宽度</td> |
|
|
|
|
|
<td style={{ padding: "8px", border: "1px solid #ccc" }}> |
|
|
|
|
|
{analysisReport.railHeadWidth} |
|
|
|
|
|
</td> |
|
|
|
|
|
</tr> |
|
|
|
|
|
{analysisReport.angleAnalysisList.map((item, index) => ( |
|
|
|
|
|
<tr key={index} style={{ height: "40px", fontSize: "18px", color: "#9E9E9E" }}> |
|
|
|
|
|
<td style={{ padding: "8px", border: "1px solid #ccc" }}>{item.describe}</td> |
|
|
|
|
|
<td style={{ padding: "8px", border: "1px solid #ccc" }}>{item.distance}</td> |
|
|
|
|
|
</tr> |
|
|
|
|
|
))} |
|
|
|
|
|
</tbody> |
|
|
|
|
|
</table> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<Button |
|
|
|
|
|
style={{ width: 200, marginTop: 18 }} |
|
|
|
|
|
size="large" |
|
|
|
|
|
type="primary" |
|
|
|
|
|
onClick={() => navigate("../config")}> |
|
|
|
|
|
新测量 |
|
|
|
|
|
</Button> |
|
|
|
|
|
</> |
|
|
|
|
|
) : ( |
|
|
|
|
|
<div> |
|
|
<div> |
|
|
<h1 className="font-medium text-xl text-center">测量步骤</h1> |
|
|
<h1 className="font-medium text-xl text-center">测量步骤</h1> |
|
|
<div className="w-[13rem] mt-5"> |
|
|
<div className="w-[13rem] mt-5"> |
|
@ -424,7 +393,7 @@ export default function MeasureAction() { |
|
|
size="large" |
|
|
size="large" |
|
|
type="primary" |
|
|
type="primary" |
|
|
onClick={onAnalysisBtnClick} |
|
|
onClick={onAnalysisBtnClick} |
|
|
disabled={!measurementFinished || analysisClicked}> |
|
|
|
|
|
|
|
|
disabled={!measurementFinished}> |
|
|
分析 |
|
|
分析 |
|
|
</Button> |
|
|
</Button> |
|
|
<Button |
|
|
<Button |
|
@ -432,7 +401,7 @@ export default function MeasureAction() { |
|
|
size="large" |
|
|
size="large" |
|
|
type="primary" |
|
|
type="primary" |
|
|
onClick={onSaveBtnClick} |
|
|
onClick={onSaveBtnClick} |
|
|
disabled={!measurementFinished || saveClicked}> |
|
|
|
|
|
|
|
|
disabled={!measurementFinished}> |
|
|
保存 |
|
|
保存 |
|
|
</Button> |
|
|
</Button> |
|
|
<Checkbox checked={afterSave} onChange={onAfterSaveChange}> |
|
|
<Checkbox checked={afterSave} onChange={onAfterSaveChange}> |
|
@ -440,8 +409,48 @@ export default function MeasureAction() { |
|
|
</Checkbox> |
|
|
</Checkbox> |
|
|
</section> |
|
|
</section> |
|
|
</div> |
|
|
</div> |
|
|
)} |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<Drawer title="分析结果" onClose={() => setOpenDrawer(false)} open={openDrawer}> |
|
|
|
|
|
{analysisReport && ( |
|
|
|
|
|
<> |
|
|
|
|
|
<div className="analysis-table"> |
|
|
|
|
|
<table |
|
|
|
|
|
style={{ |
|
|
|
|
|
width: "100%", |
|
|
|
|
|
borderCollapse: "collapse", |
|
|
|
|
|
border: "1px solid #ccc", |
|
|
|
|
|
textAlign: "center", |
|
|
|
|
|
}}> |
|
|
|
|
|
<tbody> |
|
|
|
|
|
<tr style={{ height: "40px", fontSize: "18px", color: "#9E9E9E" }}> |
|
|
|
|
|
<td style={{ padding: "8px", border: "1px solid #ccc" }}>W1垂直磨耗</td> |
|
|
|
|
|
<td style={{ padding: "8px", border: "1px solid #ccc" }}>{analysisReport.w1}</td> |
|
|
|
|
|
</tr> |
|
|
|
|
|
<tr style={{ height: "40px", fontSize: "18px", color: "#9E9E9E" }}> |
|
|
|
|
|
<td style={{ padding: "8px", border: "1px solid #ccc" }}>轨头宽度</td> |
|
|
|
|
|
<td style={{ padding: "8px", border: "1px solid #ccc" }}> |
|
|
|
|
|
{analysisReport.railHeadWidth} |
|
|
|
|
|
</td> |
|
|
|
|
|
</tr> |
|
|
|
|
|
{analysisReport.angleAnalysisList.map((item, index) => ( |
|
|
|
|
|
<tr key={index} style={{ height: "40px", fontSize: "18px", color: "#9E9E9E" }}> |
|
|
|
|
|
<td style={{ padding: "8px", border: "1px solid #ccc" }}>{item.describe}</td> |
|
|
|
|
|
<td style={{ padding: "8px", border: "1px solid #ccc" }}>{item.distance}</td> |
|
|
|
|
|
</tr> |
|
|
|
|
|
))} |
|
|
|
|
|
</tbody> |
|
|
|
|
|
</table> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div className="mt-5 flex justify-center"> |
|
|
|
|
|
<Button style={{ minWidth: 200 }} size="large" type="primary" onClick={onExport}> |
|
|
|
|
|
导出 |
|
|
|
|
|
</Button> |
|
|
|
|
|
</div> |
|
|
|
|
|
</> |
|
|
|
|
|
)} |
|
|
|
|
|
</Drawer> |
|
|
|
|
|
</> |
|
|
); |
|
|
); |
|
|
} |
|
|
} |