2 changed files with 383 additions and 381 deletions
-
5src/pages/measure/components/MeasureAction.scss
-
759src/pages/measure/components/MeasureAction.tsx
@ -0,0 +1,5 @@ |
|||
.analysis-table { |
|||
width: 300px; |
|||
height: calc(100% - 60px) ;// calc(100vh - var(--headerHeight) - var(--footerHeight) - 3rem - 60px); |
|||
overflow: auto; |
|||
} |
@ -1,411 +1,408 @@ |
|||
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 { |
|||
fetchAnalysisReport, |
|||
getBaseRecordPointSetByCode, |
|||
saveMeasurement, |
|||
startMeasurement, |
|||
fetchAnalysisReport, |
|||
getBaseRecordPointSetByCode, |
|||
saveMeasurement, |
|||
startMeasurement, |
|||
} from "../../../services/measure/analysis"; |
|||
import { createWebSocket, sharedWsUrl } from "../../../services/socket"; |
|||
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); |
|||
|
|||
export default function MeasureAction() { |
|||
const dispatch = useAppDispatch(); |
|||
const navigate = useNavigate(); |
|||
const dispatch = useAppDispatch(); |
|||
const navigate = useNavigate(); |
|||
|
|||
/** ----------------------- 引用 ----------------------- **/ |
|||
const canvasRef = useRef<MeasurementCanvasRef>(null); |
|||
const leftPoints = useRef<{ x: number; y: number }[]>([]); |
|||
const rightPoints = useRef<{ x: number; y: number }[]>([]); |
|||
const isLeftFinished = useRef(false); |
|||
/** ----------------------- 引用 ----------------------- **/ |
|||
const canvasRef = useRef<MeasurementCanvasRef>(null); |
|||
const leftPoints = useRef<{ x: number; y: number }[]>([]); |
|||
const rightPoints = useRef<{ x: number; y: number }[]>([]); |
|||
const isLeftFinished = useRef(false); |
|||
|
|||
/** ----------------------- 状态 ----------------------- **/ |
|||
const [showGrid, setShowGrid] = useState(true); |
|||
const [showStandard, setShowStandard] = useState(true); |
|||
const [showMark, setShowMark] = useState(true); |
|||
const [angleMarkBackup, setAngleMarkBackup] = useState(true); |
|||
const afterSave = useAppSelector((store) => store.context.newMeasureAfterSave); |
|||
const [angles, setAngles] = useState<AnalyzeAngle[]>([]); |
|||
const [taskStatus, setTaskStatus] = useState<MeasureState["data"]["taskStatus"]>("IDLE"); |
|||
const [startBtnText, setStartBtnText] = useState("开始测量"); |
|||
const [measurementFinished, setMeasurementFinished] = useState(false); |
|||
const [analysisClicked, setAnalysisClicked] = useState(false); |
|||
const [saveClicked, setSaveClicked] = useState(false); |
|||
const [analysisReport, setAnalysisReport] = useState<AnalysisReport | null>(null); |
|||
const [showAnalysisTable, setShowAnalysisTable] = useState(false); |
|||
const [taskStatusName, setTaskStatusName] = useState(""); |
|||
/** ----------------------- 状态 ----------------------- **/ |
|||
const [showGrid, setShowGrid] = useState(true); |
|||
const [showStandard, setShowStandard] = useState(true); |
|||
const [showMark, setShowMark] = useState(true); |
|||
const [angleMarkBackup, setAngleMarkBackup] = useState(true); |
|||
const afterSave = useAppSelector(store => store.context.newMeasureAfterSave); |
|||
const [angles, setAngles] = useState<AnalyzeAngle[]>([]); |
|||
const [taskStatus, setTaskStatus] = useState<MeasureState["data"]["taskStatus"]>("IDLE"); |
|||
const [startBtnText, setStartBtnText] = useState("开始测量"); |
|||
const [measurementFinished, setMeasurementFinished] = useState(false); |
|||
const [analysisClicked, setAnalysisClicked] = useState(false); |
|||
const [saveClicked, setSaveClicked] = useState(false); |
|||
const [analysisReport, setAnalysisReport] = useState<AnalysisReport | null>(null); |
|||
const [showAnalysisTable, setShowAnalysisTable] = useState(false); |
|||
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 [statusList, setStatusList] = useState(initialStatusList); |
|||
// 初始状态列表
|
|||
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 [statusList, setStatusList] = useState(initialStatusList); |
|||
|
|||
/** ----------------------- 事件处理函数 ----------------------- **/ |
|||
// 切换保存后自动开始新测量
|
|||
const onAfterSaveChange: CheckboxProps["onChange"] = (e) => { |
|||
dispatch(switchMeasureAfterSave(e.target.checked)); |
|||
}; |
|||
/** ----------------------- 事件处理函数 ----------------------- **/ |
|||
// 切换保存后自动开始新测量
|
|||
const onAfterSaveChange: CheckboxProps["onChange"] = e => { |
|||
dispatch(switchMeasureAfterSave(e.target.checked)); |
|||
}; |
|||
|
|||
// 分析按钮点击事件
|
|||
const onAnalysisBtnClick = () => { |
|||
setAnalysisClicked(true); |
|||
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) => ({ |
|||
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) }, |
|||
measure: { x: parseFloat(item.pointB.x), y: parseFloat(item.pointB.y) }, |
|||
distance: parseFloat(item.distance), |
|||
describe: item.describe, |
|||
})); |
|||
canvasRef.current?.setAnalysisData(analysisData); |
|||
} |
|||
setAnalysisReport(report); |
|||
setShowAnalysisTable(true); |
|||
} else { |
|||
message.error("分析报告请求失败: " + res.data.info); |
|||
} |
|||
}); |
|||
}; |
|||
// 分析按钮点击事件
|
|||
const onAnalysisBtnClick = () => { |
|||
setAnalysisClicked(true); |
|||
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 => ({ |
|||
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) }, |
|||
measure: { x: parseFloat(item.pointB.x), y: parseFloat(item.pointB.y) }, |
|||
distance: parseFloat(item.distance), |
|||
describe: item.describe, |
|||
})); |
|||
canvasRef.current?.setAnalysisData(analysisData); |
|||
} |
|||
setAnalysisReport(report); |
|||
setShowAnalysisTable(true); |
|||
} else { |
|||
message.error("分析报告请求失败: " + res.data.info); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
// 开始/重新测量按钮点击事件
|
|||
const onStart = () => { |
|||
if (startBtnText === "新测量") { |
|||
navigate("../newMeasure"); |
|||
return; |
|||
} |
|||
// 重置测量相关状态
|
|||
setShowAnalysisTable(false); |
|||
setMeasurementFinished(false); |
|||
setAnalysisClicked(false); |
|||
setSaveClicked(false); |
|||
isLeftFinished.current = false; |
|||
leftPoints.current = []; |
|||
rightPoints.current = []; |
|||
canvasRef.current?.clearShapes(); |
|||
canvasRef.current?.resetCanvas(); |
|||
if (startBtnText === "重新测量") { |
|||
setStatusList(initialStatusList); |
|||
} |
|||
startMeasurement().then((res) => { |
|||
if (res.status !== 0) { |
|||
message.error(res.data.info); |
|||
setTaskStatusName(taskStatusDescMap["IDLE"]); |
|||
} else { |
|||
const newStatusList = [...initialStatusList]; |
|||
newStatusList[0].color = "b"; |
|||
setStatusList(newStatusList); |
|||
message.success("已通知设备开始测量"); |
|||
setTaskStatusName(taskStatusDescMap["IDLE"]); |
|||
setStartBtnText("重新测量"); |
|||
} |
|||
}); |
|||
}; |
|||
// 开始/重新测量按钮点击事件
|
|||
const onStart = () => { |
|||
if (startBtnText === "新测量") { |
|||
navigate("../newMeasure"); |
|||
return; |
|||
} |
|||
// 重置测量相关状态
|
|||
setShowAnalysisTable(false); |
|||
setMeasurementFinished(false); |
|||
setAnalysisClicked(false); |
|||
setSaveClicked(false); |
|||
isLeftFinished.current = false; |
|||
leftPoints.current = []; |
|||
rightPoints.current = []; |
|||
canvasRef.current?.clearShapes(); |
|||
canvasRef.current?.resetCanvas(); |
|||
if (startBtnText === "重新测量") { |
|||
setStatusList(initialStatusList); |
|||
} |
|||
startMeasurement().then(res => { |
|||
if (res.status !== 0) { |
|||
message.error(res.data.info); |
|||
setTaskStatusName(taskStatusDescMap["IDLE"]); |
|||
} else { |
|||
const newStatusList = [...initialStatusList]; |
|||
newStatusList[0].color = "b"; |
|||
setStatusList(newStatusList); |
|||
message.success("已通知设备开始测量"); |
|||
setTaskStatusName(taskStatusDescMap["IDLE"]); |
|||
setStartBtnText("重新测量"); |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
// 保存按钮点击事件
|
|||
const onSaveBtnClick = () => { |
|||
setSaveClicked(true); |
|||
saveMeasurement().then((res) => { |
|||
if (res.status !== 0) { |
|||
message.error(res.data.info); |
|||
} else { |
|||
message.success("保存成功"); |
|||
if (afterSave) { |
|||
navigate("../config"); |
|||
} else { |
|||
setStartBtnText("新测量"); |
|||
} |
|||
} |
|||
}); |
|||
}; |
|||
// 保存按钮点击事件
|
|||
const onSaveBtnClick = () => { |
|||
setSaveClicked(true); |
|||
saveMeasurement().then(res => { |
|||
if (res.status !== 0) { |
|||
message.error(res.data.info); |
|||
} else { |
|||
message.success("保存成功"); |
|||
if (afterSave) { |
|||
navigate("../config"); |
|||
} else { |
|||
setStartBtnText("新测量"); |
|||
} |
|||
} |
|||
}); |
|||
}; |
|||
|
|||
// 辅助函数:渲染状态项的背景颜色
|
|||
const renderStatusBackground = (item: typeof initialStatusList[0]) => |
|||
item.statusCode === "START_RECORD_LEFT" ? item.background : ""; |
|||
// 辅助函数:渲染状态项的背景颜色
|
|||
const renderStatusBackground = (item: (typeof initialStatusList)[0]) => |
|||
item.statusCode === "START_RECORD_LEFT" ? item.background : ""; |
|||
|
|||
// 辅助函数:渲染状态项的图标
|
|||
const renderStatusIcon = (item: typeof initialStatusList[0]) => { |
|||
if (item.color === "g") { |
|||
return <img src={Gr_round} alt="green" />; |
|||
} else if (item.color === "b") { |
|||
return <img src={Bl_round} alt="blue" />; |
|||
} else { |
|||
return ( |
|||
<div |
|||
style={{ |
|||
width: "22px", |
|||
height: "22px", |
|||
background: "#c0c0c0", |
|||
borderRadius: "50%", |
|||
marginTop: "10px", |
|||
}} |
|||
/> |
|||
); |
|||
} |
|||
}; |
|||
// 辅助函数:渲染状态项的图标
|
|||
const renderStatusIcon = (item: (typeof initialStatusList)[0]) => { |
|||
if (item.color === "g") { |
|||
return <img src={Gr_round} alt="green" />; |
|||
} else if (item.color === "b") { |
|||
return <img src={Bl_round} alt="blue" />; |
|||
} else { |
|||
return ( |
|||
<div |
|||
style={{ |
|||
width: "22px", |
|||
height: "22px", |
|||
background: "#c0c0c0", |
|||
borderRadius: "50%", |
|||
marginTop: "10px", |
|||
}} |
|||
/> |
|||
); |
|||
} |
|||
}; |
|||
|
|||
/** ----------------------- WebSocket 消息处理 ----------------------- **/ |
|||
useEffect(() => { |
|||
// 处理任务状态消息
|
|||
const handleStateMessage = (data: any) => { |
|||
if (!data.data) return; |
|||
if (data.data.taskStatus === "IDLE") { |
|||
setTaskStatusName("空闲"); |
|||
} else if (!data.data.isMeasuringLeftEnd) { |
|||
setTaskStatusName("左侧正在测量"); |
|||
setStatusList((prev) => { |
|||
const updated = [...prev]; |
|||
updated[0].isReady = true; |
|||
return updated; |
|||
}); |
|||
} else if (data.data.isMeasuringLeftEnd && !data.data.isMeasuringRightEnd) { |
|||
setTaskStatusName("右侧正在测量"); |
|||
} else { |
|||
setTaskStatusName(taskStatusDescMap[data.data.taskStatus as keyof typeof taskStatusDescMap]); |
|||
/** ----------------------- WebSocket 消息处理 ----------------------- **/ |
|||
useEffect(() => { |
|||
// 处理任务状态消息
|
|||
const handleStateMessage = (data: any) => { |
|||
if (!data.data) return; |
|||
if (data.data.taskStatus === "IDLE") { |
|||
setTaskStatusName("空闲"); |
|||
} else if (!data.data.isMeasuringLeftEnd) { |
|||
setTaskStatusName("左侧正在测量"); |
|||
setStatusList(prev => { |
|||
const updated = [...prev]; |
|||
updated[0].isReady = true; |
|||
return updated; |
|||
}); |
|||
} else if (data.data.isMeasuringLeftEnd && !data.data.isMeasuringRightEnd) { |
|||
setTaskStatusName("右侧正在测量"); |
|||
} else { |
|||
setTaskStatusName(taskStatusDescMap[data.data.taskStatus as keyof typeof taskStatusDescMap]); |
|||
} |
|||
setTaskStatus(data.data.taskStatus); |
|||
}; |
|||
|
|||
} |
|||
setTaskStatus(data.data.taskStatus); |
|||
}; |
|||
// 处理事件消息
|
|||
const handleEventMessage = (data: any) => { |
|||
setStatusList(prev => { |
|||
const updated = [...prev]; |
|||
switch (data.data) { |
|||
case "START_RECORD_LEFT": |
|||
updated[0].color = "g"; |
|||
updated[1].color = "b"; |
|||
break; |
|||
case "FINISH_RECORD_LEFT": |
|||
updated[1].color = "g"; |
|||
updated[2].color = "g"; |
|||
updated[3].color = "b"; |
|||
isLeftFinished.current = true; |
|||
break; |
|||
case "START_RECORD_RIGHT": |
|||
updated[3].color = "g"; |
|||
updated[4].color = "b"; |
|||
break; |
|||
case "FINISH_RECORD_RIGHT": |
|||
updated[4].color = "g"; |
|||
updated[5].color = "g"; |
|||
setMeasurementFinished(true); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
return updated; |
|||
}); |
|||
}; |
|||
|
|||
// 处理事件消息
|
|||
const handleEventMessage = (data: any) => { |
|||
setStatusList((prev) => { |
|||
const updated = [...prev]; |
|||
switch (data.data) { |
|||
case "START_RECORD_LEFT": |
|||
updated[0].color = "g"; |
|||
updated[1].color = "b"; |
|||
break; |
|||
case "FINISH_RECORD_LEFT": |
|||
updated[1].color = "g"; |
|||
updated[2].color = "g"; |
|||
updated[3].color = "b"; |
|||
isLeftFinished.current = true; |
|||
break; |
|||
case "START_RECORD_RIGHT": |
|||
updated[3].color = "g"; |
|||
updated[4].color = "b"; |
|||
break; |
|||
case "FINISH_RECORD_RIGHT": |
|||
updated[4].color = "g"; |
|||
updated[5].color = "g"; |
|||
setMeasurementFinished(true); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
return updated; |
|||
}); |
|||
}; |
|||
// 处理点数据消息
|
|||
const handlePointReport = (data: any) => { |
|||
const pointData = data.data as { x: number; y: number }; |
|||
console.log(`pointData === ${pointData.x},${pointData.y}`); |
|||
if (!isLeftFinished.current) { |
|||
leftPoints.current.push(pointData); |
|||
canvasRef.current?.setMeasurementDataLeft([...leftPoints.current]); |
|||
} else { |
|||
rightPoints.current.push(pointData); |
|||
canvasRef.current?.setMeasurementDataRight([...rightPoints.current]); |
|||
} |
|||
}; |
|||
|
|||
// 处理点数据消息
|
|||
const handlePointReport = (data: any) => { |
|||
const pointData = data.data as { x: number; y: number }; |
|||
console.log(`pointData === ${pointData.x},${pointData.y}`); |
|||
if (!isLeftFinished.current) { |
|||
leftPoints.current.push(pointData); |
|||
canvasRef.current?.setMeasurementDataLeft([...leftPoints.current]); |
|||
} else { |
|||
rightPoints.current.push(pointData); |
|||
canvasRef.current?.setMeasurementDataRight([...rightPoints.current]); |
|||
} |
|||
}; |
|||
const subscription = wsClient.dataOb.subscribe((data: any) => { |
|||
if (data.messageType === "STATE" && data.path === "/measurement-task/get-task-state") { |
|||
handleStateMessage(data); |
|||
} else if (data.messageType === "EVENT" && data.path === "/measurement-task/event") { |
|||
handleEventMessage(data); |
|||
} else if (data.messageType === "STATE" && data.path === "/measurement-task/point-report") { |
|||
handlePointReport(data); |
|||
} |
|||
}); |
|||
wsClient.connect(); |
|||
return () => subscription.unsubscribe(); |
|||
}, []); |
|||
|
|||
const subscription = wsClient.dataOb.subscribe((data: any) => { |
|||
if (data.messageType === "STATE" && data.path === "/measurement-task/get-task-state") { |
|||
handleStateMessage(data); |
|||
} else if (data.messageType === "EVENT" && data.path === "/measurement-task/event") { |
|||
handleEventMessage(data); |
|||
} else if (data.messageType === "STATE" && data.path === "/measurement-task/point-report") { |
|||
handlePointReport(data); |
|||
} |
|||
}); |
|||
wsClient.connect(); |
|||
return () => subscription.unsubscribe(); |
|||
}, []); |
|||
/** ----------------------- 页面加载获取基础图形数据 ----------------------- **/ |
|||
useEffect(() => { |
|||
getBaseRecordPointSetByCode("6001").then(res => { |
|||
if (res.success) { |
|||
const benchmarkShapes = JSON.parse(res.data.points) as BenchmarkShape[]; |
|||
if (canvasRef.current) { |
|||
console.log("解析后的基础图形数据:", benchmarkShapes); |
|||
canvasRef.current.setBenchmarkData(benchmarkShapes); |
|||
} |
|||
} |
|||
}); |
|||
}, []); |
|||
|
|||
/** ----------------------- 页面加载获取基础图形数据 ----------------------- **/ |
|||
useEffect(() => { |
|||
getBaseRecordPointSetByCode("6001").then((res) => { |
|||
if (res.success) { |
|||
const benchmarkShapes = JSON.parse(res.data.points) as BenchmarkShape[]; |
|||
if (canvasRef.current) { |
|||
console.log("解析后的基础图形数据:", benchmarkShapes); |
|||
canvasRef.current.setBenchmarkData(benchmarkShapes); |
|||
} |
|||
} |
|||
}); |
|||
}, []); |
|||
/** ----------------------- 渲染 ----------------------- **/ |
|||
return ( |
|||
<div className="flex h-full px-6"> |
|||
{/* 左侧区域:包含开关区域和测量画布 */} |
|||
<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)} /> |
|||
<span>参考线</span> |
|||
</div> |
|||
{/* 标准线开关 */} |
|||
<div className="flex gap-2 items-center"> |
|||
<Switch |
|||
checked={showStandard} |
|||
onChange={checked => { |
|||
setShowStandard(checked); |
|||
if (!checked) { |
|||
setAngleMarkBackup(showMark); |
|||
setShowMark(false); |
|||
} else { |
|||
setShowMark(angleMarkBackup); |
|||
} |
|||
}} |
|||
/> |
|||
<span>标准线</span> |
|||
</div> |
|||
{/* 角度线开关,仅在点击分析按钮后显示 */} |
|||
{analysisClicked && ( |
|||
<div className="flex gap-2 items-center"> |
|||
<Switch |
|||
checked={showMark} |
|||
disabled={!showStandard} |
|||
onChange={checked => { |
|||
setShowMark(checked); |
|||
setAngleMarkBackup(checked); |
|||
}} |
|||
/> |
|||
<span>角度线</span> |
|||
</div> |
|||
)} |
|||
</div> |
|||
<div className="relative m-2"> |
|||
<MeasurementCanvas |
|||
width={800} |
|||
height={600} |
|||
logicalExtent={{ minX: -50, maxX: 50, minY: -20, maxY: 60 }} |
|||
gridStep={1} |
|||
origin={{ x: 0, y: 20 }} |
|||
pixelPerMm={8} |
|||
maxZoom={10} |
|||
showGrid={showGrid} |
|||
showBenchmark={showStandard} |
|||
showAnalysis={showMark} |
|||
showScale={false} |
|||
scaleInterval={1} |
|||
showCoordinates={true} |
|||
ref={canvasRef} |
|||
/> |
|||
</div> |
|||
</div> |
|||
{/* 右侧区域:根据 showAnalysisTable 状态显示测量步骤或分析表格 */} |
|||
<div className="min-w-[300px] flex-auto py-6 flex flex-col items-center"> |
|||
{showAnalysisTable && 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> |
|||
|
|||
/** ----------------------- 渲染 ----------------------- **/ |
|||
return ( |
|||
<div className="flex h-full"> |
|||
{/* 左侧区域:包含开关区域和测量画布 */} |
|||
<div className="flex-none"> |
|||
<div className="flex gap-4 items-center px-6 pt-5"> |
|||
{/* 参考线开关 */} |
|||
<div className="flex gap-2 items-center"> |
|||
<Switch defaultChecked onChange={(checked) => setShowGrid(checked)} /> |
|||
<span>参考线</span> |
|||
</div> |
|||
{/* 标准线开关 */} |
|||
<div className="flex gap-2 items-center"> |
|||
<Switch |
|||
checked={showStandard} |
|||
onChange={(checked) => { |
|||
setShowStandard(checked); |
|||
if (!checked) { |
|||
setAngleMarkBackup(showMark); |
|||
setShowMark(false); |
|||
} else { |
|||
setShowMark(angleMarkBackup); |
|||
} |
|||
}} |
|||
/> |
|||
<span>标准线</span> |
|||
</div> |
|||
{/* 角度线开关,仅在点击分析按钮后显示 */} |
|||
{analysisClicked && ( |
|||
<div className="flex gap-2 items-center"> |
|||
<Switch |
|||
checked={showMark} |
|||
disabled={!showStandard} |
|||
onChange={(checked) => { |
|||
setShowMark(checked); |
|||
setAngleMarkBackup(checked); |
|||
}} |
|||
/> |
|||
<span>角度线</span> |
|||
</div> |
|||
)} |
|||
</div> |
|||
<div className="relative m-2"> |
|||
<MeasurementCanvas |
|||
width={800} |
|||
height={600} |
|||
logicalExtent={{ minX: -50, maxX: 50, minY: -20, maxY: 60 }} |
|||
gridStep={1} |
|||
origin={{ x: 0, y: 20 }} |
|||
pixelPerMm={8} |
|||
maxZoom={10} |
|||
showGrid={showGrid} |
|||
showBenchmark={showStandard} |
|||
showAnalysis={showMark} |
|||
showScale={false} |
|||
scaleInterval={1} |
|||
showCoordinates={true} |
|||
ref={canvasRef} |
|||
/> |
|||
</div> |
|||
</div> |
|||
{/* 右侧区域:根据 showAnalysisTable 状态显示测量步骤或分析表格 */} |
|||
<div className="w-[300px] flex-none py-6"> |
|||
{showAnalysisTable && 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> |
|||
))} |
|||
<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> |
|||
) : ( |
|||
<div> |
|||
<h1 className="font-medium text-xl text-center">测量步骤</h1> |
|||
<div className="ml-[45px] w-[13rem] mt-5"> |
|||
{statusList.map((item, index) => ( |
|||
<div |
|||
key={index} |
|||
style={{ background: renderStatusBackground(item), borderRadius: "20px" }} |
|||
className="mt-[10px] h-[40px]" |
|||
> |
|||
<div style={{ display: "flex", lineHeight: "40px" }} className="pl-[1rem]"> |
|||
{renderStatusIcon(item)} |
|||
<div className="pl-[5px]">{item.name}</div> |
|||
</div> |
|||
</div> |
|||
))} |
|||
</div> |
|||
<section className="flex flex-col items-center gap-4 mt-6 border-t border-[#D8D8D8] py-4"> |
|||
<Button style={{ width: 200 }} size="large" type="primary" onClick={onStart}> |
|||
{startBtnText} |
|||
</Button> |
|||
<Button |
|||
style={{ width: 200 }} |
|||
size="large" |
|||
type="primary" |
|||
onClick={onAnalysisBtnClick} |
|||
disabled={!measurementFinished || analysisClicked} |
|||
> |
|||
分析 |
|||
</Button> |
|||
<Button |
|||
style={{ width: 200 }} |
|||
size="large" |
|||
type="primary" |
|||
onClick={onSaveBtnClick} |
|||
disabled={!measurementFinished || saveClicked} |
|||
> |
|||
保存 |
|||
</Button> |
|||
<Checkbox checked={afterSave} onChange={onAfterSaveChange}> |
|||
保存后自动开始新测量 |
|||
</Checkbox> |
|||
</section> |
|||
</div> |
|||
)} |
|||
</div> |
|||
</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="w-[13rem] mt-5"> |
|||
{statusList.map((item, index) => ( |
|||
<div |
|||
key={index} |
|||
style={{ background: renderStatusBackground(item), borderRadius: "20px" }} |
|||
className="mt-[10px] h-[40px]"> |
|||
<div style={{ display: "flex", lineHeight: "40px" }} className="pl-[1rem]"> |
|||
{renderStatusIcon(item)} |
|||
<div className="pl-[5px]">{item.name}</div> |
|||
</div> |
|||
</div> |
|||
))} |
|||
</div> |
|||
<section className="flex flex-col items-center gap-4 mt-6 border-t border-[#D8D8D8] py-4"> |
|||
<Button style={{ width: 200 }} size="large" type="primary" onClick={onStart}> |
|||
{startBtnText} |
|||
</Button> |
|||
<Button |
|||
style={{ width: 200 }} |
|||
size="large" |
|||
type="primary" |
|||
onClick={onAnalysisBtnClick} |
|||
disabled={!measurementFinished || analysisClicked}> |
|||
分析 |
|||
</Button> |
|||
<Button |
|||
style={{ width: 200 }} |
|||
size="large" |
|||
type="primary" |
|||
onClick={onSaveBtnClick} |
|||
disabled={!measurementFinished || saveClicked}> |
|||
保存 |
|||
</Button> |
|||
<Checkbox checked={afterSave} onChange={onAfterSaveChange}> |
|||
保存后自动开始新测量 |
|||
</Checkbox> |
|||
</section> |
|||
</div> |
|||
)} |
|||
</div> |
|||
</div> |
|||
); |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue