diff --git a/package-lock.json b/package-lock.json index f4b61c6..707831a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,12 +12,16 @@ "@ant-design/icons": "^5.6.1", "@electron-toolkit/preload": "^3.0.2", "@electron-toolkit/utils": "^4.0.0", + "ahooks": "^3.9.5", "antd": "^5.27.1", "chart.js": "^4.5.0", "electron-updater": "^6.3.9", + "konva": "^9.3.22", "react": "^18.0.0", "react-chartjs-2": "^5.3.0", - "react-dom": "^18.0.0" + "react-dom": "^18.0.0", + "react-konva": "^18.2.12", + "zustand": "^5.0.8" }, "devDependencies": { "@electron-toolkit/eslint-config": "^2.0.0", @@ -2455,6 +2459,12 @@ "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "license": "MIT" }, + "node_modules/@types/js-cookie": { + "version": "3.0.6", + "resolved": "https://registry.npmmirror.com/@types/js-cookie/-/js-cookie-3.0.6.tgz", + "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==", + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2499,6 +2509,25 @@ "xmlbuilder": ">=11.0.1" } }, + "node_modules/@types/react": { + "version": "19.1.12", + "resolved": "https://registry.npmmirror.com/@types/react/-/react-19.1.12.tgz", + "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-reconciler": { + "version": "0.28.9", + "resolved": "https://registry.npmmirror.com/@types/react-reconciler/-/react-reconciler-0.28.9.tgz", + "integrity": "sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/responselike": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/@types/responselike/-/responselike-1.0.3.tgz", @@ -2631,6 +2660,31 @@ "node": ">=8" } }, + "node_modules/ahooks": { + "version": "3.9.5", + "resolved": "https://registry.npmmirror.com/ahooks/-/ahooks-3.9.5.tgz", + "integrity": "sha512-TrjXie49Q8HuHKTa84Fm9A+famMDAG1+7a9S9Gq6RQ0h90Jgqmiq3CkObuRjWT/C4d6nRZCw35Y2k2fmybb5eA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@types/js-cookie": "^3.0.6", + "dayjs": "^1.9.1", + "intersection-observer": "^0.12.0", + "js-cookie": "^3.0.5", + "lodash": "^4.17.21", + "react-fast-compare": "^3.2.2", + "resize-observer-polyfill": "^1.5.1", + "screenfull": "^5.0.0", + "tslib": "^2.4.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", @@ -6235,6 +6289,12 @@ "node": ">= 0.4" } }, + "node_modules/intersection-observer": { + "version": "0.12.2", + "resolved": "https://registry.npmmirror.com/intersection-observer/-/intersection-observer-0.12.2.tgz", + "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==", + "license": "Apache-2.0" + }, "node_modules/ip-address": { "version": "10.0.1", "resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.0.1.tgz", @@ -6724,6 +6784,18 @@ "node": ">= 0.4" } }, + "node_modules/its-fine": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/its-fine/-/its-fine-1.2.5.tgz", + "integrity": "sha512-fXtDA0X0t0eBYAGLVM5YsgJGsJ5jEmqZEPrGbzdf5awjv0xE7nqv3TVnvtUF060Tkes15DbDAKW/I48vsb6SyA==", + "license": "MIT", + "dependencies": { + "@types/react-reconciler": "^0.28.0" + }, + "peerDependencies": { + "react": ">=18.0" + } + }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz", @@ -6758,6 +6830,15 @@ "node": ">=10" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6872,6 +6953,26 @@ "json-buffer": "3.0.1" } }, + "node_modules/konva": { + "version": "9.3.22", + "resolved": "https://registry.npmmirror.com/konva/-/konva-9.3.22.tgz", + "integrity": "sha512-yQI5d1bmELlD/fowuyfOp9ff+oamg26WOCkyqUyc+nczD/lhRa3EvD2MZOoc4c1293TAubW9n34fSQLgSeEgSw==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/lavrton" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/konva" + }, + { + "type": "github", + "url": "https://github.com/sponsors/lavrton" + } + ], + "license": "MIT" + }, "node_modules/lazy-val": { "version": "1.0.5", "resolved": "https://registry.npmmirror.com/lazy-val/-/lazy-val-1.0.5.tgz", @@ -6970,7 +7071,6 @@ "version": "4.17.21", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, "license": "MIT" }, "node_modules/lodash.defaults": { @@ -8804,6 +8904,12 @@ "react": "^18.3.1" } }, + "node_modules/react-fast-compare": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz", + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", @@ -8811,6 +8917,53 @@ "dev": true, "license": "MIT" }, + "node_modules/react-konva": { + "version": "18.2.12", + "resolved": "https://registry.npmmirror.com/react-konva/-/react-konva-18.2.12.tgz", + "integrity": "sha512-tszrM/emkX1u2reJTn3M9nMG9kuFv09s974dUEXK7luIN3z0VRD8PUjwyaLWi8Ba52ntQceZ0nfYWC6VlPa3vA==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/lavrton" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/konva" + }, + { + "type": "github", + "url": "https://github.com/sponsors/lavrton" + } + ], + "license": "MIT", + "dependencies": { + "@types/react-reconciler": "^0.28.2", + "its-fine": "^1.1.1", + "react-reconciler": "~0.29.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "konva": "^8.0.1 || ^7.2.5 || ^9.0.0", + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/react-reconciler": { + "version": "0.29.2", + "resolved": "https://registry.npmmirror.com/react-reconciler/-/react-reconciler-0.29.2.tgz", + "integrity": "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.17.0.tgz", @@ -9217,6 +9370,18 @@ "loose-envify": "^1.1.0" } }, + "node_modules/screenfull": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/screenfull/-/screenfull-5.2.0.tgz", + "integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/scroll-into-view-if-needed": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", @@ -10006,6 +10171,12 @@ "utf8-byte-length": "^1.0.1" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", @@ -10625,6 +10796,35 @@ "engines": { "node": ">= 10" } + }, + "node_modules/zustand": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/zustand/-/zustand-5.0.8.tgz", + "integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 600ed33..120c4d9 100644 --- a/package.json +++ b/package.json @@ -21,12 +21,16 @@ "@ant-design/icons": "^5.6.1", "@electron-toolkit/preload": "^3.0.2", "@electron-toolkit/utils": "^4.0.0", + "ahooks": "^3.9.5", "antd": "^5.27.1", "chart.js": "^4.5.0", "electron-updater": "^6.3.9", + "konva": "^9.3.22", "react": "^18.0.0", "react-chartjs-2": "^5.3.0", - "react-dom": "^18.0.0" + "react-dom": "^18.0.0", + "react-konva": "^18.2.12", + "zustand": "^5.0.8" }, "devDependencies": { "@electron-toolkit/eslint-config": "^2.0.0", diff --git a/src/renderer/src/common/ipcEvents.js b/src/renderer/src/common/ipcEvents.js index 3e2263d..c782d32 100644 --- a/src/renderer/src/common/ipcEvents.js +++ b/src/renderer/src/common/ipcEvents.js @@ -9,5 +9,5 @@ export const IPC_EVENT = { SENSOR_DATA: 'sensor:data', // 传感器数据 IMAGE_DATA: 'image:data', // 图像数据 - HEARTBEAT: 'heartbeat' // 心跳包(如需推送) + HEARTBEAT: 'heartbeat' // 心跳包 } diff --git a/src/renderer/src/components/ImageCollection/ImageCollection.jsx b/src/renderer/src/components/ImageCollection/ImageCollection.jsx index 8f7a24f..57137a3 100644 --- a/src/renderer/src/components/ImageCollection/ImageCollection.jsx +++ b/src/renderer/src/components/ImageCollection/ImageCollection.jsx @@ -1,17 +1,254 @@ -import { useEffect, useState } from 'react' +import { useEffect, useState, useRef, useCallback } from 'react' import { IPC_EVENT } from '../../common/ipcEvents' +import { Stage, Layer, Image as KonvaImage, Rect } from 'react-konva' +import useRectangleStore from '../../stores/rectangleStore' function ImagePreview() { const [imgSrc, setImgSrc] = useState(null) - const [timestamp, setTimestamp] = useState(null) + const [image, setImage] = useState(null) + const [stageSize, setStageSize] = useState({ width: 800, height: 600 }) + const [imageScale, setImageScale] = useState(1) + const [imagePos, setImagePos] = useState({ x: 0, y: 0 }) + const [rectangle, setRectangle] = useState(null) // 只保持一个矩形 + const [isDrawing, setIsDrawing] = useState(false) + const [startPos, setStartPos] = useState({ x: 0, y: 0 }) + const [currentMousePos, setCurrentMousePos] = useState({ x: 0, y: 0 }) + const stageRef = useRef(null) + const containerRef = useRef(null) + // 使用Zustand store + const rectangleData = useRectangleStore((state) => state.rectangleData) + const setRectangleData = useRectangleStore((state) => state.setRectangleData) + + // 监听store中的矩形数据变化,转换为显示坐标 + useEffect(() => { + if (rectangleData && image && imageScale) { + // 将原始图片坐标转换为显示坐标 + const displayRect = { + x: rectangleData.x * imageScale + imagePos.x, + y: rectangleData.y * imageScale + imagePos.y, + width: rectangleData.width * imageScale, + height: rectangleData.height * imageScale + } + setRectangle(displayRect) + console.log('从store更新矩形显示:', { + 原始数据: rectangleData, + 显示坐标: displayRect + }) + } else if (!rectangleData) { + setRectangle(null) + } + }, [rectangleData, image, imageScale, imagePos.x, imagePos.y]) + + // 计算容器和图片尺寸 + const updateSizes = useCallback(() => { + if (!containerRef.current || !image) return + + const containerRect = containerRef.current.getBoundingClientRect() + const containerWidth = containerRect.width - 32 // 减去 padding + const containerHeight = window.innerHeight - containerRect.top - 100 // 留些边距 + + // 计算图片缩放比例,保持长宽比 + const scaleX = containerWidth / image.width + const scaleY = containerHeight / image.height + const scale = Math.min(scaleX, scaleY, 1) // 不放大,只缩小 + + const scaledWidth = image.width * scale + const scaledHeight = image.height * scale + + // 居中显示 + const x = (containerWidth - scaledWidth) / 2 + const y = (containerHeight - scaledHeight) / 2 + + setStageSize({ width: containerWidth, height: containerHeight }) + setImageScale(scale) + setImagePos({ x, y }) + }, [image]) + + // 监听窗口尺寸变化 + useEffect(() => { + const handleResize = () => { + updateSizes() + } + + updateSizes() + window.addEventListener('resize', handleResize) + return () => window.removeEventListener('resize', handleResize) + }, [image, updateSizes]) + + // 加载图片到 Canvas useEffect(() => { - // console.log('test') - const handler = (event, data) => { - // data.values.image 是 base64 字符串 - console.log(data) - setImgSrc(`data:image/png;base64,${data.values.image}`) - setTimestamp(data.values.timestamp) + if (imgSrc) { + const img = new window.Image() + img.onload = () => { + setImage(img) + } + img.src = imgSrc + } + }, [imgSrc]) + + // 监听鼠标移动 + const handleMouseMove = (e) => { + const stage = e.target.getStage() + const point = clampPointToImage(stage.getPointerPosition()) + setCurrentMousePos(point) + } + + // 矩形绘制完成事件处理 + const onRectangleDrawComplete = (rectData) => { + // 将坐标转换为相对于图片的坐标 + const relativeRect = { + x: rectData.x - imagePos.x, + y: rectData.y - imagePos.y, + width: rectData.width, + height: rectData.height + } + + // 将坐标转换为相对于原始图片尺寸的坐标(考虑缩放) + const originalImageRect = { + x: relativeRect.x / imageScale, + y: relativeRect.y / imageScale, + width: relativeRect.width / imageScale, + height: relativeRect.height / imageScale + } + + console.log('矩形绘制完成:') + console.log(' Stage坐标:', rectData) + console.log(' 相对于显示图片的坐标:', relativeRect) + console.log(' 相对于原始图片的坐标:', originalImageRect) + console.log( + `最终坐标: x=${Math.round(originalImageRect.x)}, y=${Math.round(originalImageRect.y)}` + ) + console.log( + `最终尺寸: width=${Math.round(originalImageRect.width)}, height=${Math.round(originalImageRect.height)}` + ) + + // 将矩形数据保存到Zustand store + const rectInfo = { + x: Math.round(originalImageRect.x), + y: Math.round(originalImageRect.y), + width: Math.round(originalImageRect.width), + height: Math.round(originalImageRect.height) + } + + // 更新到store,供其他组件使用 + setRectangleData(rectInfo) + } + + // 检查点是否在图片范围内 + const isPointInImage = (point) => { + if (!image) return false + + const imageLeft = imagePos.x + const imageTop = imagePos.y + const imageRight = imagePos.x + image.width * imageScale + const imageBottom = imagePos.y + image.height * imageScale + + return ( + point.x >= imageLeft && point.x <= imageRight && point.y >= imageTop && point.y <= imageBottom + ) + } + + // 将点限制在图片范围内 + const clampPointToImage = (point) => { + if (!image) return point + + const imageLeft = imagePos.x + const imageTop = imagePos.y + const imageRight = imagePos.x + image.width * imageScale + const imageBottom = imagePos.y + image.height * imageScale + + return { + x: Math.max(imageLeft, Math.min(imageRight, point.x)), + y: Math.max(imageTop, Math.min(imageBottom, point.y)) + } + } + + // 开始绘制矩形 + const handleMouseDown = (e) => { + if (e.evt.button !== 0) return // 只响应左键 + + const stage = e.target.getStage() + const point = stage.getPointerPosition() + + // 如果点击在图片范围内,开始绘制新矩形 + if (isPointInImage(point)) { + // 清除之前的矩形 + setRectangle(null) + // 清除store中的数据 + setRectangleData(null) + + setStartPos(clampPointToImage(point)) + setIsDrawing(true) + } else { + // 如果点击在图片范围外,清除矩形 + setRectangle(null) + setRectangleData(null) + } + } + + // 绘制矩形过程中 + const handleMouseUp = (e) => { + if (!isDrawing) return + + const stage = e.target.getStage() + const point = clampPointToImage(stage.getPointerPosition()) + + const width = point.x - startPos.x + const height = point.y - startPos.y + + // 只有当矩形有一定大小时才添加 + if (Math.abs(width) > 5 && Math.abs(height) > 5) { + const newRect = { + x: Math.min(startPos.x, point.x), + y: Math.min(startPos.y, point.y), + width: Math.abs(width), + height: Math.abs(height) + } + setRectangle(newRect) // 设置当前矩形,会替换之前的矩形 + + // 触发绘制完成事件 + onRectangleDrawComplete(newRect) + } + + setIsDrawing(false) + } + + // 处理点击事件(用于清除矩形) + const handleStageClick = (e) => { + // 如果正在绘制,不处理点击事件 + if (isDrawing) return + + const stage = e.target.getStage() + const point = stage.getPointerPosition() + + // 如果点击的不是图片区域,清除矩形 + if (!isPointInImage(point)) { + setRectangle(null) + setRectangleData(null) + } + } + + // 当前正在绘制的矩形 + const getCurrentRect = () => { + if (!isDrawing) return null + + const width = currentMousePos.x - startPos.x + const height = currentMousePos.y - startPos.y + + return { + x: Math.min(startPos.x, currentMousePos.x), + y: Math.min(startPos.y, currentMousePos.y), + width: Math.abs(width), + height: Math.abs(height) + } + } + + useEffect(() => { + const handler = (_, data) => { + const base64 = data.values.image + const src = `data:image/png;base64,${base64}` + setImgSrc(src) } window.electron.ipcRenderer.on(IPC_EVENT.IMAGE_DATA, handler) return () => { @@ -20,18 +257,69 @@ function ImagePreview() { }, []) return ( -
+
{imgSrc ? ( - <> - 设备图像 -
{timestamp}
- + + + {/* 图片 */} + {image && ( + + )} + + {/* 当前矩形 */} + {rectangle && ( + + )} + + {/* 正在绘制的矩形 */} + {isDrawing && getCurrentRect() && ( + + )} + + ) : ( -
暂无图像数据
+
暂无图像数据
)}
) diff --git a/src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx b/src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx index 4f71e1f..0453559 100644 --- a/src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx +++ b/src/renderer/src/components/MeasurementPointSetting/MeasurementPointSetting.jsx @@ -1,5 +1,5 @@ import styles from './MeasurementPointSetting.module.css' -import { Flex, Input, Select, InputNumber, Button, Table } from 'antd' +import { Flex, Input, Select, InputNumber, Button, Table, Tooltip, Modal } from 'antd' import { PlusOutlined, MinusOutlined, @@ -8,83 +8,137 @@ import { SendOutlined, BoxPlotFilled } from '@ant-design/icons' +import useRectangleStore from '../../stores/rectangleStore' +import { useState } from 'react' function MeasurementPointSetting() { - // 测点列表数据 - const dataSource = [ - { - key: '1', - 项目: '传感器', - 数值: '', + // 从Zustand store获取矩形数据和设置方法 + const rectangleData = useRectangleStore((state) => state.rectangleData) + const setRectangleData = useRectangleStore((state) => state.setRectangleData) + + // 表单数据状态 + const [formData, setFormData] = useState({ + location: 1, // 测点位置 + description: '', // 测点描述 + coefficient: 0, // 计算系数 + baseTarget: 'n' // 基准标靶 + }) + + // 测点列表状态 + const [sensorList, setSensorList] = useState([]) + + // 选中的传感器状态 + const [selectedSensorKey, setSelectedSensorKey] = useState(null) + + // 根据选中的传感器更新矩形数据 + const updateRectangleFromSensor = (sensorKey) => { + if (!sensorKey) { + setRectangleData(null) + return + } + + // 查找选中的传感器 + const selectedSensor = sensorList.find((sensor) => sensor.key === sensorKey) + if (!selectedSensor) { + setRectangleData(null) + return + } + + // 查找基准标靶的坐标信息 + const baseTargetInfo = selectedSensor.children?.find((child) => child.name === '基准标靶') + if (!baseTargetInfo || !baseTargetInfo.children) { + setRectangleData(null) + return + } + + // 提取坐标数据 + const xData = baseTargetInfo.children.find((coord) => coord.name === 'x') + const yData = baseTargetInfo.children.find((coord) => coord.name === 'y') + const wData = baseTargetInfo.children.find((coord) => coord.name === 'w') + const hData = baseTargetInfo.children.find((coord) => coord.name === 'h') + + if (xData && yData && wData && hData) { + const rectangleInfo = { + x: Number(xData.value) || 0, + y: Number(yData.value) || 0, + width: Number(wData.value) || 0, + height: Number(hData.value) || 0 + } + + console.log('从传感器数据更新矩形:', rectangleInfo) + setRectangleData(rectangleInfo) + } else { + setRectangleData(null) + } + } + + // 处理传感器选中 + const handleSensorSelect = (sensorKey) => { + setSelectedSensorKey(sensorKey) + updateRectangleFromSensor(sensorKey) + } + + // 表单输入处理函数 + const handleFormChange = (field, value) => { + setFormData((prev) => ({ + ...prev, + [field]: value + })) + } + + // 添加传感器数据收集函数 + const handleAddSensor = () => { + // 检查是否有矩形绘制数据 + if (!rectangleData || !rectangleData.x) { + alert('请先在图片上绘制矩形区域!') + return + } + + // 生成新的传感器项 + const newSensorKey = String(sensorList.length + 1) + const newSensor = { + key: newSensorKey, + name: '传感器', + value: '', children: [ - { key: '1-1', 项目: '测点位置', 数值: 1 }, - { - key: '1-2', - 项目: '测点描述', - 数值: '我去年买了个表' - }, - { key: '1-3', 项目: '计算系数', 数值: 0.448 }, + { key: `${newSensorKey}-1`, name: '测点位置', value: formData.location }, + { key: `${newSensorKey}-2`, name: '测点描述', value: formData.description }, + { key: `${newSensorKey}-3`, name: '计算系数', value: formData.coefficient }, { - key: '1-4', - 项目: '基准标靶', - 数值: 'n', + key: `${newSensorKey}-4`, + name: '基准标靶', + value: formData.baseTarget, children: [ - { key: '1-4-1', 项目: 'x', 数值: 349 }, - { key: '1-4-2', 项目: 'y', 数值: 1108 }, - { key: '1-4-3', 项目: 'w', 数值: 125 }, - { key: '1-4-4', 项目: 'h', 数值: 115 } + { key: `${newSensorKey}-4-1`, name: 'x', value: rectangleData.x }, + { key: `${newSensorKey}-4-2`, name: 'y', value: rectangleData.y }, + { key: `${newSensorKey}-4-3`, name: 'w', value: rectangleData.width }, + { key: `${newSensorKey}-4-4`, name: 'h', value: rectangleData.height } ] } ] - }, - { - key: '2', - 项目: '传感器', - 数值: '' - }, - { - key: '3', - 项目: '传感器', - 数值: '' - }, - { - key: '4', - 项目: '传感器', - 数值: '' - }, - { - key: '5', - 项目: '传感器', - 数值: '' - }, - { - key: '6', - 项目: '传感器', - 数值: '' - }, - { - key: '7', - 项目: '传感器', - 数值: '' - }, - { - key: '8', - 项目: '传感器', - 数值: '' - }, - { - key: '9', - 项目: '传感器', - 数值: '' } - ] + + // 更新传感器列表 + setSensorList((prev) => [...prev, newSensor]) + + // 重置表单数据 + setFormData((prev) => ({ + location: prev.location + 1, // 自动递增位置 + description: '', + coefficient: 0, + baseTarget: 'n' + })) + } + + // 测点列表数据(使用状态) + const dataSource = sensorList // 表格列配置 const columns = [ { title: '项目', - dataIndex: '项目', - key: '项目', + dataIndex: 'name', + key: 'name', width: 120, render: (text, record) => { // 根据层级设置不同的字体大小 @@ -107,8 +161,8 @@ function MeasurementPointSetting() { }, { title: '数值', - dataIndex: '数值', - key: '数值', + dataIndex: 'value', + key: 'value', render: (value, record) => { // 根据层级设置不同的字体大小 const level = record.key.split('-').length - 1 @@ -121,13 +175,75 @@ function MeasurementPointSetting() { color: level === 0 ? '#333' : level === 1 ? '#666' : '#999' }} > - {value || ''} + {value !== null && value !== undefined ? value : ''} ) } } ] + // 删除传感器处理函数 + const handleDel = () => { + if (!selectedSensorKey) { + Modal.info({ + title: '提示', + content: '请先选择要删除的传感器!', + centered: true + }) + return + } + + Modal.confirm({ + title: '删除确认', + content: '是否要删除该传感器?', + okText: '确定', + cancelText: '取消', + centered: true, + onOk: () => { + // 删除选中的传感器 + setSensorList((prev) => prev.filter((sensor) => sensor.key !== selectedSensorKey)) + setSelectedSensorKey(null) + console.log('删除传感器:', selectedSensorKey) + }, + onCancel: () => { + console.log('取消删除') + } + }) + } + + // 清空传感器列表处理函数 + const handleClear = () => { + if (sensorList.length === 0) { + Modal.info({ + title: '提示', + content: '传感器列表已经为空!', + centered: true + }) + return + } + + Modal.confirm({ + title: '清空确认', + content: '是否要清空所有传感器?', + okText: '确定', + cancelText: '取消', + centered: true, + onOk: () => { + // 清空传感器列表 + setSensorList([]) + setSelectedSensorKey(null) + console.log('清空传感器列表') + }, + onCancel: () => { + console.log('取消清空') + } + }) + } + + const handleLoad = () => {} + + const handleSet = () => {} + return ( {/* 标题 */} @@ -144,19 +260,30 @@ function MeasurementPointSetting() { 测点位置: - + handleFormChange('description', e.target.value)} + style={{ flex: 1 }} + /> 计算系数: handleFormChange('coefficient', value)} precision={4} style={{ flex: 1 }} /> @@ -166,7 +293,8 @@ function MeasurementPointSetting() { 基准标靶: