|
|
@ -1,4 +1,4 @@ |
|
|
|
import React, { useState, useEffect, useRef } from 'react'; |
|
|
|
import React, { useState, useEffect, useRef } from "react"; |
|
|
|
import { Button, Checkbox, CheckboxProps, message, Switch } from "antd"; |
|
|
|
import { useNavigate } from "react-router"; |
|
|
|
import { |
|
|
@ -12,13 +12,10 @@ import { switchMeasureAfterSave } from "../../../store/features/contextSlice"; |
|
|
|
import { AnalysisReport, AnalyzeAngle } from "../../../services/measure/type"; |
|
|
|
import { MeasureState, taskStatusDescMap } from "../../../services/wsTypes"; |
|
|
|
import { useAppDispatch, useAppSelector } from "../../../utils/hooks"; |
|
|
|
import Gr_round from '../../../assets/green_round.svg'; |
|
|
|
import Bl_round from '../../../assets/blue_round.svg'; |
|
|
|
import MeasurementCanvas, { |
|
|
|
AnalysisData, |
|
|
|
BenchmarkShape, |
|
|
|
MeasurementCanvasRef, |
|
|
|
} from "./konva/MeasurementCanvas"; |
|
|
|
import Gr_round from "../../../assets/green_round.svg"; |
|
|
|
import Bl_round from "../../../assets/blue_round.svg"; |
|
|
|
import MeasurementCanvas, { AnalysisData, BenchmarkShape, MeasurementCanvasRef } from "./konva/MeasurementCanvas"; |
|
|
|
import "./MeasureAction.scss"; |
|
|
|
|
|
|
|
// 创建 websocket 客户端
|
|
|
|
const wsClient = createWebSocket(sharedWsUrl); |
|
|
@ -38,7 +35,7 @@ export default function MeasureAction() { |
|
|
|
const [showStandard, setShowStandard] = useState(true); |
|
|
|
const [showMark, setShowMark] = 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 [taskStatus, setTaskStatus] = useState<MeasureState["data"]["taskStatus"]>("IDLE"); |
|
|
|
const [startBtnText, setStartBtnText] = useState("开始测量"); |
|
|
@ -62,20 +59,20 @@ export default function MeasureAction() { |
|
|
|
|
|
|
|
/** ----------------------- 事件处理函数 ----------------------- **/ |
|
|
|
// 切换保存后自动开始新测量
|
|
|
|
const onAfterSaveChange: CheckboxProps["onChange"] = (e) => { |
|
|
|
const onAfterSaveChange: CheckboxProps["onChange"] = e => { |
|
|
|
dispatch(switchMeasureAfterSave(e.target.checked)); |
|
|
|
}; |
|
|
|
|
|
|
|
// 分析按钮点击事件
|
|
|
|
const onAnalysisBtnClick = () => { |
|
|
|
setAnalysisClicked(true); |
|
|
|
fetchAnalysisReport("6001").then((res) => { |
|
|
|
fetchAnalysisReport("6001").then(res => { |
|
|
|
if (res.success) { |
|
|
|
const report: AnalysisReport = res.data; |
|
|
|
console.log(report); |
|
|
|
// 更新 canvas 的分析数据
|
|
|
|
if (report && report.angleAnalysisList) { |
|
|
|
const analysisData: AnalysisData[] = report.angleAnalysisList.map((item) => ({ |
|
|
|
const analysisData: AnalysisData[] = report.angleAnalysisList.map(item => ({ |
|
|
|
pointA: { x: parseFloat(item.pointA.x), y: parseFloat(item.pointA.y) }, |
|
|
|
pointB: { x: parseFloat(item.pointB.x), y: parseFloat(item.pointB.y) }, |
|
|
|
base: { x: parseFloat(item.pointA.x), y: parseFloat(item.pointA.y) }, |
|
|
@ -112,7 +109,7 @@ export default function MeasureAction() { |
|
|
|
if (startBtnText === "重新测量") { |
|
|
|
setStatusList(initialStatusList); |
|
|
|
} |
|
|
|
startMeasurement().then((res) => { |
|
|
|
startMeasurement().then(res => { |
|
|
|
if (res.status !== 0) { |
|
|
|
message.error(res.data.info); |
|
|
|
setTaskStatusName(taskStatusDescMap["IDLE"]); |
|
|
@ -130,7 +127,7 @@ export default function MeasureAction() { |
|
|
|
// 保存按钮点击事件
|
|
|
|
const onSaveBtnClick = () => { |
|
|
|
setSaveClicked(true); |
|
|
|
saveMeasurement().then((res) => { |
|
|
|
saveMeasurement().then(res => { |
|
|
|
if (res.status !== 0) { |
|
|
|
message.error(res.data.info); |
|
|
|
} else { |
|
|
@ -145,11 +142,11 @@ export default function MeasureAction() { |
|
|
|
}; |
|
|
|
|
|
|
|
// 辅助函数:渲染状态项的背景颜色
|
|
|
|
const renderStatusBackground = (item: typeof initialStatusList[0]) => |
|
|
|
const renderStatusBackground = (item: (typeof initialStatusList)[0]) => |
|
|
|
item.statusCode === "START_RECORD_LEFT" ? item.background : ""; |
|
|
|
|
|
|
|
// 辅助函数:渲染状态项的图标
|
|
|
|
const renderStatusIcon = (item: typeof initialStatusList[0]) => { |
|
|
|
const renderStatusIcon = (item: (typeof initialStatusList)[0]) => { |
|
|
|
if (item.color === "g") { |
|
|
|
return <img src={Gr_round} alt="green" />; |
|
|
|
} else if (item.color === "b") { |
|
|
@ -178,7 +175,7 @@ export default function MeasureAction() { |
|
|
|
setTaskStatusName("空闲"); |
|
|
|
} else if (!data.data.isMeasuringLeftEnd) { |
|
|
|
setTaskStatusName("左侧正在测量"); |
|
|
|
setStatusList((prev) => { |
|
|
|
setStatusList(prev => { |
|
|
|
const updated = [...prev]; |
|
|
|
updated[0].isReady = true; |
|
|
|
return updated; |
|
|
@ -187,14 +184,13 @@ export default function MeasureAction() { |
|
|
|
setTaskStatusName("右侧正在测量"); |
|
|
|
} else { |
|
|
|
setTaskStatusName(taskStatusDescMap[data.data.taskStatus as keyof typeof taskStatusDescMap]); |
|
|
|
|
|
|
|
} |
|
|
|
setTaskStatus(data.data.taskStatus); |
|
|
|
}; |
|
|
|
|
|
|
|
// 处理事件消息
|
|
|
|
const handleEventMessage = (data: any) => { |
|
|
|
setStatusList((prev) => { |
|
|
|
setStatusList(prev => { |
|
|
|
const updated = [...prev]; |
|
|
|
switch (data.data) { |
|
|
|
case "START_RECORD_LEFT": |
|
|
@ -251,7 +247,7 @@ export default function MeasureAction() { |
|
|
|
|
|
|
|
/** ----------------------- 页面加载获取基础图形数据 ----------------------- **/ |
|
|
|
useEffect(() => { |
|
|
|
getBaseRecordPointSetByCode("6001").then((res) => { |
|
|
|
getBaseRecordPointSetByCode("6001").then(res => { |
|
|
|
if (res.success) { |
|
|
|
const benchmarkShapes = JSON.parse(res.data.points) as BenchmarkShape[]; |
|
|
|
if (canvasRef.current) { |
|
|
@ -264,20 +260,20 @@ export default function MeasureAction() { |
|
|
|
|
|
|
|
/** ----------------------- 渲染 ----------------------- **/ |
|
|
|
return ( |
|
|
|
<div className="flex h-full"> |
|
|
|
<div className="flex h-full px-6"> |
|
|
|
{/* 左侧区域:包含开关区域和测量画布 */} |
|
|
|
<div className="flex-none"> |
|
|
|
<div className=""> |
|
|
|
<div className="flex gap-4 items-center px-6 pt-5"> |
|
|
|
{/* 参考线开关 */} |
|
|
|
<div className="flex gap-2 items-center"> |
|
|
|
<Switch defaultChecked onChange={(checked) => setShowGrid(checked)} /> |
|
|
|
<Switch defaultChecked onChange={checked => setShowGrid(checked)} /> |
|
|
|
<span>参考线</span> |
|
|
|
</div> |
|
|
|
{/* 标准线开关 */} |
|
|
|
<div className="flex gap-2 items-center"> |
|
|
|
<Switch |
|
|
|
checked={showStandard} |
|
|
|
onChange={(checked) => { |
|
|
|
onChange={checked => { |
|
|
|
setShowStandard(checked); |
|
|
|
if (!checked) { |
|
|
|
setAngleMarkBackup(showMark); |
|
|
@ -295,7 +291,7 @@ export default function MeasureAction() { |
|
|
|
<Switch |
|
|
|
checked={showMark} |
|
|
|
disabled={!showStandard} |
|
|
|
onChange={(checked) => { |
|
|
|
onChange={checked => { |
|
|
|
setShowMark(checked); |
|
|
|
setAngleMarkBackup(checked); |
|
|
|
}} |
|
|
@ -324,8 +320,9 @@ export default function MeasureAction() { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
{/* 右侧区域:根据 showAnalysisTable 状态显示测量步骤或分析表格 */} |
|
|
|
<div className="w-[300px] flex-none py-6"> |
|
|
|
<div className="min-w-[300px] flex-auto py-6 flex flex-col items-center"> |
|
|
|
{showAnalysisTable && analysisReport ? ( |
|
|
|
<> |
|
|
|
<div className="analysis-table"> |
|
|
|
<table |
|
|
|
style={{ |
|
|
@ -333,8 +330,7 @@ export default function MeasureAction() { |
|
|
|
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> |
|
|
@ -342,7 +338,9 @@ export default function MeasureAction() { |
|
|
|
</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> |
|
|
|
<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" }}> |
|
|
@ -350,26 +348,27 @@ export default function MeasureAction() { |
|
|
|
<td style={{ padding: "8px", border: "1px solid #ccc" }}>{item.distance}</td> |
|
|
|
</tr> |
|
|
|
))} |
|
|
|
<tr style={{ height: "40px", fontSize: "18px", color: "#9E9E9E" }}> |
|
|
|
<td colSpan={2} style={{ textAlign: "center", padding: "8px", border: "1px solid #ccc" }}> |
|
|
|
<Button style={{ width: 200 }} size="large" type="primary" onClick={() => navigate("../config")}> |
|
|
|
新测量 |
|
|
|
</Button> |
|
|
|
</td> |
|
|
|
</tr> |
|
|
|
</tbody> |
|
|
|
</table> |
|
|
|
</div> |
|
|
|
|
|
|
|
<Button |
|
|
|
style={{ width: 200, marginTop: 18 }} |
|
|
|
size="large" |
|
|
|
type="primary" |
|
|
|
onClick={() => navigate("../config")}> |
|
|
|
新测量 |
|
|
|
</Button> |
|
|
|
</> |
|
|
|
) : ( |
|
|
|
<div> |
|
|
|
<h1 className="font-medium text-xl text-center">测量步骤</h1> |
|
|
|
<div className="ml-[45px] w-[13rem] mt-5"> |
|
|
|
<div className="w-[13rem] mt-5"> |
|
|
|
{statusList.map((item, index) => ( |
|
|
|
<div |
|
|
|
key={index} |
|
|
|
style={{ background: renderStatusBackground(item), borderRadius: "20px" }} |
|
|
|
className="mt-[10px] h-[40px]" |
|
|
|
> |
|
|
|
className="mt-[10px] h-[40px]"> |
|
|
|
<div style={{ display: "flex", lineHeight: "40px" }} className="pl-[1rem]"> |
|
|
|
{renderStatusIcon(item)} |
|
|
|
<div className="pl-[5px]">{item.name}</div> |
|
|
@ -386,8 +385,7 @@ export default function MeasureAction() { |
|
|
|
size="large" |
|
|
|
type="primary" |
|
|
|
onClick={onAnalysisBtnClick} |
|
|
|
disabled={!measurementFinished || analysisClicked} |
|
|
|
> |
|
|
|
disabled={!measurementFinished || analysisClicked}> |
|
|
|
分析 |
|
|
|
</Button> |
|
|
|
<Button |
|
|
@ -395,8 +393,7 @@ export default function MeasureAction() { |
|
|
|
size="large" |
|
|
|
type="primary" |
|
|
|
onClick={onSaveBtnClick} |
|
|
|
disabled={!measurementFinished || saveClicked} |
|
|
|
> |
|
|
|
disabled={!measurementFinished || saveClicked}> |
|
|
|
保存 |
|
|
|
</Button> |
|
|
|
<Checkbox checked={afterSave} onChange={onAfterSaveChange}> |
|
|
|