You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

642 lines
21 KiB

5 months ago
5 months ago
5 months ago
4 months ago
4 months ago
5 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
4 months ago
5 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
5 months ago
5 months ago
4 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
  1. import React, { useState, useEffect, useRef, useCallback, useMemo } from "react";
  2. import { Button, Checkbox, CheckboxProps, Drawer, Input, InputNumber, message, Select, Spin, Switch } from "antd";
  3. import { DownOutlined, UpOutlined, LeftOutlined, RightOutlined, UndoOutlined, RedoOutlined } from '@ant-design/icons';
  4. import { useNavigate } from "react-router";
  5. import {
  6. fetchAnalysisReport,
  7. getAlignPointsByRailSize,
  8. startMeasurement,
  9. stopMeasurement,
  10. } from "../../../services/measure/analysis";
  11. import { getBaseRecordPointSetByCode, gx_list } from "../../../services/track/trackShape"
  12. import { createWebSocket, sharedWsUrl } from "../../../services/socket";
  13. import { switchMeasureAfterSave } from "../../../store/features/contextSlice";
  14. import measureState, { updateGxState, updateMeasureData } from "../../../store/measure/measureState";
  15. import { AnalysisReport, trackItem } from "../../../services/measure/type";
  16. import { MeasureState, TaskState, TrackRecordSig } from "../../../services/wsTypes";
  17. import { useAppDispatch, useAppSelector } from "../../../utils/hooks";
  18. import Gr_round from "../../../assets/green_round.svg";
  19. import Bl_round from "../../../assets/blue_round.svg";
  20. import icon_left from "../../../assets/icon_left.svg";
  21. import icon_right from "../../../assets/icon_right.svg";
  22. import icon_up from "../../../assets/icon_up.svg";
  23. import icon_down from "../../../assets/icon_down.svg";
  24. import icon_leftR from "../../../assets/icon_leftR.svg";
  25. import icon_rightR from "../../../assets/icon_rightR.svg";
  26. import MeasurementCanvas, { AnalysisData, BenchmarkShape, MeasurementCanvasRef, Point } from "./konva/MeasurementCanvas";
  27. import "./MeasureAction.scss";
  28. import { GX_CODE } from "../../../constant";
  29. // 创建 websocket 客户端
  30. const wsClient = createWebSocket(sharedWsUrl);
  31. export default function MeasureAction() {
  32. const dispatch = useAppDispatch();
  33. const navigate = useNavigate();
  34. const deviceInfo = useAppSelector(store => store.context.device);
  35. const measureState = useAppSelector((store) => store.measureState);
  36. const [gxCode, setGxCode] = useState<string>(GX_CODE)
  37. const STEP_COLOR_GREEN = "green";
  38. const STEP_COLOR_BLUE = "blue";
  39. const STEP_COLOR_GREY = "grey";
  40. /** ----------------------- 引用 ----------------------- **/
  41. const canvasRef = useRef<MeasurementCanvasRef>(null);
  42. const leftPoints = useRef<{ x: number; y: number }[]>([]);
  43. const rightPoints = useRef<{ x: number; y: number }[]>([]);
  44. const isLeftFinished = useRef(false);
  45. /** ----------------------- 状态 ----------------------- **/
  46. const [showGrid, setShowGrid] = useState(true);
  47. const [showStandard, setShowStandard] = useState(true);
  48. const [showMark, setShowMark] = useState(true);
  49. // showMark的备份,记录showMark最近一次的值
  50. const [angleMarkBackup, setAngleMarkBackup] = useState(true);
  51. const afterSave = useAppSelector(store => store.context.newMeasureAfterSave);
  52. const [startBtnText, setStartBtnText] = useState("开始测量");
  53. const [measurementFinished, setMeasurementFinished] = useState(false);
  54. // 【分析】之后,会得到分析报告
  55. const [analysisReport, setAnalysisReport] = useState<AnalysisReport | null>(null);
  56. // 初始状态列表
  57. const initialStatusList = useMemo(
  58. () => [
  59. { name: "请移动到顶部,停顿2秒", background: "#ececec", isReady: false, color: STEP_COLOR_GREY },
  60. { name: "开始测量左侧", background: "#ececec", isReady: false, color: STEP_COLOR_GREY },
  61. { name: "左侧测量完成", background: "#ececec", isReady: false, color: STEP_COLOR_GREY },
  62. { name: "请移动到顶部,停顿2秒", background: "#ececec", isReady: false, color: STEP_COLOR_GREY },
  63. { name: "开始测量右侧", background: "#ececec", isReady: false, color: STEP_COLOR_GREY },
  64. { name: "右侧测量完成", background: "#ececec", isReady: false, color: STEP_COLOR_GREY },
  65. ],
  66. []
  67. );
  68. const [statusList, setStatusList] = useState(initialStatusList);
  69. // 打开抽屉(显示分析结果)
  70. const [openDrawer, setOpenDrawer] = useState(false);
  71. /** ----------------------- 事件处理函数 ----------------------- */
  72. // 切换保存后自动开始新测量
  73. const onAfterSaveChange: CheckboxProps["onChange"] = e => {
  74. dispatch(switchMeasureAfterSave(e.target.checked));
  75. };
  76. useEffect(()=>{
  77. setGxCode(measureState.gxCode)
  78. }, [measureState])
  79. // 分析按钮点击事件
  80. const onAnalysisBtnClick = () => {
  81. if (analysisReport) {
  82. setOpenDrawer(true);
  83. return;
  84. }
  85. fetchAnalysisReport(gxCode).then(res => {
  86. if (res.success) {
  87. const report: AnalysisReport = res.data;
  88. console.log(report);
  89. // 更新 canvas 的分析数据
  90. if (report && report.angleAnalysisList) {
  91. // 先过滤掉 distance 为 null 的数据
  92. const validItems = report.angleAnalysisList.filter(item => item.distance !== null);
  93. const analysisData: AnalysisData[] = validItems.map(item => ({
  94. pointA: { x: parseFloat(item.pointA.x), y: parseFloat(item.pointA.y) },
  95. pointB: { x: parseFloat(item.pointB.x), y: parseFloat(item.pointB.y) },
  96. base: { x: parseFloat(item.pointA.x), y: parseFloat(item.pointA.y) },
  97. measure: { x: parseFloat(item.pointB.x), y: parseFloat(item.pointB.y) },
  98. distance: parseFloat(item.distance),
  99. describe: item.describe,
  100. }));
  101. canvasRef.current?.setAnalysisData(analysisData);
  102. }
  103. setAnalysisReport(report);
  104. setOpenDrawer(true);
  105. } else {
  106. message.error("分析报告请求失败: " + res.data.info);
  107. }
  108. });
  109. };
  110. // 开始/重新测量按钮点击事件
  111. const onStart = useCallback(() => {
  112. if (!deviceInfo.isConnected) {
  113. message.error("请先连接设备");
  114. return;
  115. }
  116. // if(deviceInfo.power < 20){
  117. // message.error('电量低于20%,请充电后再测量!')
  118. // return
  119. // }
  120. // 重置测量相关状态
  121. setMeasurementFinished(false);
  122. setAnalysisReport(null);
  123. setshowCalibration(false)//校准线
  124. dispatch(updateMeasureData([]))
  125. isLeftFinished.current = false;
  126. leftPoints.current = [];
  127. rightPoints.current = [];
  128. canvasRef.current?.clearShapes();
  129. canvasRef.current?.resetCanvas();
  130. startMeasurement().then(res => {
  131. if (res.status !== 0) {
  132. message.error(res.data.info);
  133. } else {
  134. let list = [...initialStatusList]
  135. list.forEach((item, index) => {
  136. if(index === 0){
  137. item.color = STEP_COLOR_BLUE;
  138. }else{
  139. item.color = STEP_COLOR_GREY;
  140. }
  141. })
  142. setStatusList(list);
  143. message.success("已通知设备开始测量");
  144. setStartBtnText("重新测量");
  145. }
  146. });
  147. }, [initialStatusList, startBtnText, deviceInfo]);
  148. //停止测量
  149. const onStop = () => {
  150. stopMeasurement().then(res=>{
  151. if (res.status !== 0) {
  152. }else{
  153. message.error('已停止测量')
  154. }
  155. }).catch(e=>{
  156. message.error('调用出错')
  157. })
  158. }
  159. // 保存按钮点击事件
  160. const onSaveBtnClick = () => {
  161. dispatch(updateMeasureData(newMeasureData))
  162. navigate('/measure/config')
  163. //将校准的数据存入store
  164. };
  165. //校准
  166. const [showCalibration, setshowCalibration] = useState(false)
  167. const [caloading, setCaLoading] = useState(false)
  168. const onCalibrationBtnClick = () => {
  169. setCaLoading(true)
  170. //获取校准数据
  171. getAlignPointsByRailSize({railSize:railSize}).then(res => {
  172. if(res.success){
  173. setshowCalibration(true)
  174. canvasRef.current?.setMeasurementCalibrationData(res.data)
  175. }else{
  176. message.error('校准失败!')
  177. }
  178. setCaLoading(false)
  179. }).catch(e=>{
  180. setCaLoading(false)
  181. message.error('校准失败!')
  182. })
  183. }
  184. // 辅助函数:渲染状态项的图标
  185. const renderStatusIcon = (item: (typeof initialStatusList)[0]) => {
  186. if (item.color === STEP_COLOR_GREEN) {
  187. return <img src={Gr_round} alt="green" />;
  188. } else if (item.color === STEP_COLOR_BLUE) {
  189. return <img src={Bl_round} alt="blue" />;
  190. } else {
  191. return (
  192. <div
  193. style={{
  194. width: "22px",
  195. height: "22px",
  196. background: "#c0c0c0",
  197. borderRadius: "50%",
  198. marginTop: "10px",
  199. }}
  200. />
  201. );
  202. }
  203. };
  204. /** ----------------------- WebSocket 消息处理 ----------------------- **/
  205. useEffect(() => {
  206. // 处理任务状态消息
  207. const handleStateMessage = (state: MeasureState["data"]) => {};
  208. // 处理事件消息
  209. const handleEventMessage = (type: TaskState["data"]) => {
  210. setStatusList(prev => {
  211. const updated = [...prev];
  212. switch (type) {
  213. case "START_RECORD_LEFT":
  214. updated[0].color = STEP_COLOR_GREEN;
  215. updated[1].color = STEP_COLOR_BLUE;
  216. const audio1 = new Audio("/audio/begin_left.mp3");
  217. // 播放音频
  218. audio1
  219. .play()
  220. .then(() => {
  221. console.log("音频开始播放");
  222. })
  223. .catch(err => {
  224. console.error("播放音频时出错:", err);
  225. });
  226. break;
  227. case "FINISH_RECORD_LEFT":
  228. updated[1].color = STEP_COLOR_GREEN;
  229. updated[2].color = STEP_COLOR_GREEN;
  230. updated[3].color = STEP_COLOR_BLUE;
  231. isLeftFinished.current = true;
  232. const audio2 = new Audio("/audio/end_left.mp3");
  233. // 播放音频
  234. audio2
  235. .play()
  236. .then(() => {
  237. console.log("音频开始播放");
  238. })
  239. .catch(err => {
  240. console.error("播放音频时出错:", err);
  241. });
  242. break;
  243. case "START_RECORD_RIGHT":
  244. updated[3].color = STEP_COLOR_GREEN;
  245. updated[4].color = STEP_COLOR_BLUE;
  246. const audio3 = new Audio("/audio/begin_right.mp3");
  247. // 播放音频
  248. audio3
  249. .play()
  250. .then(() => {
  251. console.log("音频开始播放");
  252. })
  253. .catch(err => {
  254. console.error("播放音频时出错:", err);
  255. });
  256. break;
  257. case "FINISH_RECORD_RIGHT":
  258. updated[4].color = STEP_COLOR_GREEN;
  259. updated[5].color = STEP_COLOR_GREEN;
  260. setMeasurementFinished(true);
  261. const audio4 = new Audio("/audio/end_right.mp3");
  262. // 播放音频
  263. audio4
  264. .play()
  265. .then(() => {
  266. console.log("音频开始播放");
  267. })
  268. .catch(err => {
  269. console.error("播放音频时出错:", err);
  270. });
  271. break;
  272. case "WRONG_SIDE":
  273. const audio5 = new Audio("/audio/alert_left.mp3");
  274. // 播放音频
  275. audio5
  276. .play()
  277. .then(() => {
  278. console.log("音频开始播放");
  279. })
  280. .catch(err => {
  281. console.error("播放音频时出错:", err);
  282. });
  283. // 把状态全部置灰
  284. updated.forEach(u => (u.color = STEP_COLOR_GREY));
  285. // 调用停止测量
  286. onStop();
  287. break;
  288. default:
  289. break;
  290. }
  291. return updated;
  292. });
  293. };
  294. // 处理点数据消息
  295. const handlePointReport = (pointData: TrackRecordSig["data"]) => {
  296. if (!isLeftFinished.current) {
  297. leftPoints.current.push(pointData);
  298. canvasRef.current?.setMeasurementDataLeft([...leftPoints.current]);
  299. } else {
  300. rightPoints.current.push(pointData);
  301. canvasRef.current?.setMeasurementDataRight([...rightPoints.current]);
  302. }
  303. };
  304. const subscription = wsClient.dataOb.subscribe(data => {
  305. if (data.path === "/api/measurement-task/get-task-state") {
  306. handleStateMessage(data.data);
  307. } else if (data.path === "/api/measurement-task/event") {
  308. handleEventMessage(data.data);
  309. } else if (data.path === "/api/measurement-task/point-report") {
  310. handlePointReport(data.data);
  311. }
  312. });
  313. wsClient.connect();
  314. return () => subscription.unsubscribe();
  315. }, [onStart]);
  316. /** ----------------------- 页面加载获取基础图形数据 -------基线---------------- **/
  317. useEffect(() => {
  318. queryBasePoints(gxCode)
  319. //获取轨型
  320. getTrackDataList()
  321. }, []);
  322. //获取测量基线
  323. const queryBasePoints = (gxCode:string) => {
  324. getBaseRecordPointSetByCode(gxCode).then(res => {
  325. if (res.success) {
  326. const benchmarkShapes = JSON.parse(res.data.points) as BenchmarkShape[];
  327. if (canvasRef.current) {
  328. console.log("解析后的基础图形数据:", benchmarkShapes);
  329. canvasRef.current.setBenchmarkData(benchmarkShapes);
  330. }
  331. }
  332. });
  333. }
  334. /********************************轨型数据************************************* */
  335. const [trackList, setTrackList] = useState<trackItem[]>([])
  336. const [defaultTrackValue, setDefaultTrackValue] = useState<string>()
  337. const getTrackDataList = () => {
  338. gx_list().then(res => {
  339. if(res.data && res.data.length){
  340. let resData:trackItem[] = res.data;
  341. let list:trackItem[] = []
  342. resData.map(item => {
  343. if(item.points){
  344. list.push(item)
  345. }
  346. })
  347. setTrackList(list)
  348. }
  349. })
  350. }
  351. useEffect(()=>{
  352. if(trackList && trackList.length){
  353. setDefaultTrackValue(trackList[0].code)
  354. }
  355. },[trackList])
  356. //当前选择的轨型 默认"GX-60"
  357. const [railSize, setRailSize] = useState<string>(GX_CODE)
  358. const onTrackChange = (value: string) => {
  359. setRailSize(value)
  360. queryBasePoints(value)
  361. //缓存至STORE
  362. dispatch(updateGxState(value))
  363. }
  364. //上下移动
  365. const onMoveLine = (type:string) => {
  366. let list = canvasRef.current?.getMeasurementCalibrationData()
  367. if(list && list.length){
  368. list.forEach(item => {
  369. if(type === 'up'){//向上移动,原数据减y X轴不动
  370. item.y = item.y - distance/1000;
  371. }
  372. if(type === 'down'){//向上移动,原数据加y X轴不动
  373. item.y = item.y + distance/1000;
  374. }
  375. if(type === 'left'){//向左移动,原数据减x Y轴不动
  376. item.x = item.x - distance/1000;
  377. }
  378. if(type === 'right'){//向右移动,原数据加x Y轴不动
  379. item.x = item.x + distance/1000;
  380. }
  381. })
  382. canvasRef.current?.setMeasurementCalibrationData(list)
  383. setNewMeasureData(list)
  384. }
  385. }
  386. //旋转
  387. let [measurementRotation, setMeasurementRotation] = useState<number>(0)
  388. let [newMeasureData, setNewMeasureData] = useState<Point[]>()
  389. let [angle, setAngle] = useState<number>(1);//角度单位 分
  390. let [distance, setDistance] = useState<number>(10)
  391. const onRotationLine = (type:string) => {
  392. let mrValue = 0
  393. if(type === 'left'){//逆时针
  394. mrValue = measurementRotation - (angle/60) * Math.PI / 180;
  395. }
  396. if(type === 'right'){//顺时针
  397. mrValue = measurementRotation + (angle/60) * Math.PI / 180;
  398. }
  399. let list = canvasRef.current?.getMeasurementCalibrationData()
  400. if(list && list.length){
  401. list.forEach((item, index) => {
  402. let cloneItem = rotatePoint(item, mrValue)
  403. item.x = cloneItem.x
  404. item.y = cloneItem.y
  405. })
  406. canvasRef.current?.setMeasurementCalibrationData(list)
  407. setNewMeasureData(list)
  408. }
  409. }
  410. const rotatePoint = (pt:{x:number;y:number}, angle:number) => {
  411. const item = {
  412. x: pt.x * Math.cos(angle) - pt.y * Math.sin(angle),
  413. y: pt.x * Math.sin(angle) + pt.y * Math.cos(angle)
  414. };
  415. return item
  416. }
  417. /** ----------------------- 渲染 ----------------------- **/
  418. return (
  419. <>
  420. <div className="flex h-full px-6">
  421. {/* 左侧区域:包含开关区域和测量画布 */}
  422. <div className="">
  423. <div className="relative flex gap-4 items-center py-3">
  424. <Select
  425. className="w-[150px]"
  426. placeholder="请选择轨型"
  427. key={defaultTrackValue}
  428. defaultValue={defaultTrackValue}
  429. onChange={onTrackChange}
  430. options={trackList.map((item) => ({
  431. label: item.name,
  432. value: item.code,
  433. }))}
  434. ></Select>
  435. {/* <div
  436. className="absolute text-primary border border-primary rounded px-4 py-[2px] font-medium cursor-pointer hover:text-primary/[0.8]"
  437. onClick={() => navigate("../config", { replace: true })}>
  438. </div> */}
  439. <section className="ml-auto flex gap-4 items-center">
  440. {/* 参考线开关 */}
  441. <div className="flex gap-2 items-center">
  442. <Switch defaultChecked onChange={checked => setShowGrid(checked)} />
  443. <span>线</span>
  444. </div>
  445. {/* 标准线开关 */}
  446. <div className="flex gap-2 items-center">
  447. <Switch
  448. checked={showStandard}
  449. onChange={checked => {
  450. setShowStandard(checked);
  451. if (!checked) {
  452. setAngleMarkBackup(showMark);
  453. setShowMark(false);
  454. } else {
  455. setShowMark(angleMarkBackup);
  456. }
  457. }}
  458. />
  459. <span>线</span>
  460. </div>
  461. {/* 角度线开关,仅在点击分析按钮后显示 */}
  462. {analysisReport && (
  463. <div className="flex gap-2 items-center">
  464. <Switch
  465. checked={showMark}
  466. disabled={!showStandard}
  467. onChange={checked => {
  468. setShowMark(checked);
  469. setAngleMarkBackup(checked);
  470. }}
  471. />
  472. <span>线</span>
  473. </div>
  474. )}
  475. </section>
  476. </div>
  477. <Spin spinning={caloading} tip="正在校准...">
  478. <div className="relative">
  479. <MeasurementCanvas
  480. width={800}
  481. height={600}
  482. logicalExtent={{ minX: -50, maxX: 50, minY: -20, maxY: 60 }}
  483. gridStep={1}
  484. origin={{ x: 0, y: 20 }}
  485. pixelPerMm={8}
  486. maxZoom={10}
  487. showGrid={showGrid}
  488. showBenchmark={showStandard}
  489. showAnalysis={showMark}
  490. showScale={false}
  491. scaleInterval={1}
  492. showCoordinates={showGrid}
  493. showCalibration={showCalibration}
  494. ref={canvasRef}
  495. />
  496. </div>
  497. {showCalibration &&
  498. <div className="flex justify-center h-[50px]">
  499. <div className="mt-[12px]">
  500. <InputNumber defaultValue={distance} placeholder="微米" onChange={(value) => value !== null && setDistance(Number(value))}></InputNumber>
  501. <span></span>
  502. </div>
  503. <img width={40} src={icon_left} onClick={()=>(onMoveLine("left"))} className="text-[20px] ml-[20px]" alt="左移"/>
  504. <img width={40} src={icon_right} onClick={()=>(onMoveLine("right"))} className="text-[20px] ml-[20px]" alt="右移"/>
  505. <img width={40} src={icon_up} onClick={()=>(onMoveLine("up"))} className="text-[20px] ml-[20px]" alt="上移"/>
  506. <img width={40} src={icon_down} onClick={()=>(onMoveLine("down"))} className="text-[20px] ml-[20px]" alt="下移"/>
  507. <img width={40} src={icon_leftR} onClick={()=>(onRotationLine("left"))} className="text-[20px] ml-[20px]" alt="逆时针旋转"/>
  508. <img width={40} src={icon_rightR} onClick={()=>(onRotationLine("right"))} className="text-[20px] ml-[20px]" alt="顺时针旋转"/>
  509. <div className="mt-[12px] ml-[20px]">
  510. <InputNumber placeholder="请输入角度" defaultValue={angle} onChange={(value) => value !== null && setAngle(Number(value))}></InputNumber>
  511. <span></span>
  512. </div>
  513. </div>
  514. }
  515. </Spin>
  516. </div>
  517. {/* 右侧区域:根据 showAnalysisTable 状态显示测量步骤或分析表格 */}
  518. <div className="min-w-[300px] flex-auto py-6 flex flex-col items-center">
  519. <div>
  520. <h1 className="font-medium text-xl text-center"></h1>
  521. <div className="w-[13rem] mt-5">
  522. {statusList.map((item, index) => (
  523. <div
  524. key={index}
  525. style={{ background: item.background, borderRadius: "20px" }}
  526. className="mt-[10px] h-[40px]">
  527. <div style={{ display: "flex", lineHeight: "40px" }} className="pl-[1rem]">
  528. {renderStatusIcon(item)}
  529. <div className="pl-[5px]">{item.name}</div>
  530. </div>
  531. </div>
  532. ))}
  533. </div>
  534. <section className="flex flex-col items-center gap-4 mt-6 border-t border-[#D8D8D8] py-4">
  535. <Button style={{ width: 200 }} size="large" type="primary" onClick={onStart}>
  536. {startBtnText}
  537. </Button>
  538. <Button
  539. style={{ width: 200 }}
  540. size="large"
  541. type="primary"
  542. onClick={onAnalysisBtnClick}
  543. disabled={!measurementFinished}>
  544. </Button>
  545. <Button
  546. style={{ width: 200 }}
  547. size="large"
  548. type="primary"
  549. onClick={onSaveBtnClick}
  550. disabled={!measurementFinished}
  551. >
  552. </Button>
  553. <Button
  554. style={{ width: 200 }}
  555. size="large"
  556. type="primary"
  557. onClick={onCalibrationBtnClick}
  558. disabled={!measurementFinished}
  559. >
  560. </Button>
  561. <Checkbox checked={afterSave} onChange={onAfterSaveChange}>
  562. </Checkbox>
  563. </section>
  564. </div>
  565. </div>
  566. </div>
  567. <Drawer title="分析结果" onClose={() => setOpenDrawer(false)} open={openDrawer}>
  568. {analysisReport && (
  569. <>
  570. <div className="analysis-table">
  571. <table
  572. style={{
  573. width: "100%",
  574. borderCollapse: "collapse",
  575. border: "1px solid #ccc",
  576. textAlign: "center",
  577. }}>
  578. <tbody>
  579. <tr style={{ height: "40px", fontSize: "18px", color: "#9E9E9E" }}>
  580. <td style={{ padding: "8px", border: "1px solid #ccc" }}>W1垂直磨耗</td>
  581. <td style={{ padding: "8px", border: "1px solid #ccc" }}>{analysisReport.w1}</td>
  582. </tr>
  583. <tr style={{ height: "40px", fontSize: "18px", color: "#9E9E9E" }}>
  584. <td style={{ padding: "8px", border: "1px solid #ccc" }}></td>
  585. <td style={{ padding: "8px", border: "1px solid #ccc" }}>
  586. {analysisReport.railHeadWidth}
  587. </td>
  588. </tr>
  589. {analysisReport.angleAnalysisList.map((item, index) => (
  590. <tr key={index} style={{ height: "40px", fontSize: "18px", color: "#9E9E9E" }}>
  591. <td style={{ padding: "8px", border: "1px solid #ccc" }}>{item.describe}</td>
  592. <td style={{ padding: "8px", border: "1px solid #ccc" }}>{item.distance}</td>
  593. </tr>
  594. ))}
  595. </tbody>
  596. </table>
  597. </div>
  598. {/* <div className="mt-5 flex justify-center">
  599. <Button style={{ minWidth: 200 }} size="large" type="primary" onClick={onExport}>
  600. </Button>
  601. </div> */}
  602. </>
  603. )}
  604. </Drawer>
  605. </>
  606. );
  607. }