Browse Source

feat: 重构并增强设备管理功能

- 使用 React 18 和 React DOM 18。
- 简化主进程,移除不必要的导入和注释。
- 新增 IPC 路由器,用于处理设备搜索、连接和断开。
- 在 ipcRouter.js 中实现了 UDP 设备搜索和 TCP 连接管理。
- 优化 SiderHeader 组件,实现设备搜索与连接状态管理。
- 新增 ImagePreview 组件,用于展示通过 IPC 接收到的图片。
- 在 ipcEvents.js 中引入设备管理相关的 IPC 事件。
- 清理主渲染入口,移除 StrictMode。
master
cles 2 weeks ago
parent
commit
767ab4a891
  1. 244
      package-lock.json
  2. 6
      package.json
  3. 28
      src/main/index.js
  4. 133
      src/main/ipcRouter.js
  5. 4
      src/preload/index.js
  6. 13
      src/renderer/src/common/ipcEvents.js
  7. 41
      src/renderer/src/components/ImageCollection/ImageCollection.jsx
  8. 134
      src/renderer/src/components/SiderHeader/SiderHeader.jsx
  9. 5
      src/renderer/src/main.jsx

244
package-lock.json

@ -15,7 +15,9 @@
"antd": "^5.27.1", "antd": "^5.27.1",
"chart.js": "^4.5.0", "chart.js": "^4.5.0",
"electron-updater": "^6.3.9", "electron-updater": "^6.3.9",
"react-chartjs-2": "^5.3.0" "react": "^18.0.0",
"react-chartjs-2": "^5.3.0",
"react-dom": "^18.0.0"
}, },
"devDependencies": { "devDependencies": {
"@electron-toolkit/eslint-config": "^2.0.0", "@electron-toolkit/eslint-config": "^2.0.0",
@ -29,8 +31,6 @@
"eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20", "eslint-plugin-react-refresh": "^0.4.20",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"vite": "^7.0.5" "vite": "^7.0.5"
} }
}, },
@ -2038,9 +2038,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.0.tgz",
"integrity": "sha512-rlKIeL854Ed0e09QGYFlmDNbka6I3EQFw7iZuugQjMb11KMpJCLPFL4ZPbMfaEhLADEL1yx0oujGkBQ7+qW3eA==", "integrity": "sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -2052,9 +2052,9 @@
] ]
}, },
"node_modules/@rollup/rollup-android-arm64": { "node_modules/@rollup/rollup-android-arm64": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.0.tgz",
"integrity": "sha512-cqPpZdKUSQYRtLLr6R4X3sD4jCBO1zUmeo3qrWBCqYIeH8Q3KRL4F3V7XJ2Rm8/RJOQBZuqzQGWPjjvFUcYa/w==", "integrity": "sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2066,9 +2066,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-arm64": { "node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.0.tgz",
"integrity": "sha512-99kMMSMQT7got6iYX3yyIiJfFndpojBmkHfTc1rIje8VbjhmqBXE+nb7ZZP3A5skLyujvT0eIUCUsxAe6NjWbw==", "integrity": "sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2080,9 +2080,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-x64": { "node_modules/@rollup/rollup-darwin-x64": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.0.tgz",
"integrity": "sha512-y8cXoD3wdWUDpjOLMKLx6l+NFz3NlkWKcBCBfttUn+VGSfgsQ5o/yDUGtzE9HvsodkP0+16N0P4Ty1VuhtRUGg==", "integrity": "sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2094,9 +2094,9 @@
] ]
}, },
"node_modules/@rollup/rollup-freebsd-arm64": { "node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.0.tgz",
"integrity": "sha512-3mY5Pr7qv4GS4ZvWoSP8zha8YoiqrU+e0ViPvB549jvliBbdNLrg2ywPGkgLC3cmvN8ya3za+Q2xVyT6z+vZqA==", "integrity": "sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2108,9 +2108,9 @@
] ]
}, },
"node_modules/@rollup/rollup-freebsd-x64": { "node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.0.tgz",
"integrity": "sha512-C9KzzOAQU5gU4kG8DTk+tjdKjpWhVWd5uVkinCwwFub2m7cDYLOdtXoMrExfeBmeRy9kBQMkiyJ+HULyF1yj9w==", "integrity": "sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2122,9 +2122,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-gnueabihf": { "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.0.tgz",
"integrity": "sha512-OVSQgEZDVLnTbMq5NBs6xkmz3AADByCWI4RdKSFNlDsYXdFtlxS59J+w+LippJe8KcmeSSM3ba+GlsM9+WwC1w==", "integrity": "sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -2136,9 +2136,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-musleabihf": { "node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.0.tgz",
"integrity": "sha512-ZnfSFA7fDUHNa4P3VwAcfaBLakCbYaxCk0jUnS3dTou9P95kwoOLAMlT3WmEJDBCSrOEFFV0Y1HXiwfLYJuLlA==", "integrity": "sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -2150,9 +2150,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-gnu": { "node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.0.tgz",
"integrity": "sha512-Z81u+gfrobVK2iV7GqZCBfEB1y6+I61AH466lNK+xy1jfqFLiQ9Qv716WUM5fxFrYxwC7ziVdZRU9qvGHkYIJg==", "integrity": "sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2164,9 +2164,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-musl": { "node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.0.tgz",
"integrity": "sha512-zoAwS0KCXSnTp9NH/h9aamBAIve0DXeYpll85shf9NJ0URjSTzzS+Z9evmolN+ICfD3v8skKUPyk2PO0uGdFqg==", "integrity": "sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2178,9 +2178,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-loongarch64-gnu": { "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.0.tgz",
"integrity": "sha512-2QyUyQQ1ZtwZGiq0nvODL+vLJBtciItC3/5cYN8ncDQcv5avrt2MbKt1XU/vFAJlLta5KujqyHdYtdag4YEjYQ==", "integrity": "sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
@ -2192,9 +2192,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-ppc64-gnu": { "node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.0.tgz",
"integrity": "sha512-k9aEmOWt+mrMuD3skjVJSSxHckJp+SiFzFG+v8JLXbc/xi9hv2icSkR3U7uQzqy+/QbbYY7iNB9eDTwrELo14g==", "integrity": "sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@ -2206,9 +2206,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-gnu": { "node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.0.tgz",
"integrity": "sha512-rDKRFFIWJ/zJn6uk2IdYLc09Z7zkE5IFIOWqpuU0o6ZpHcdniAyWkwSUWE/Z25N/wNDmFHHMzin84qW7Wzkjsw==", "integrity": "sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@ -2220,9 +2220,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-musl": { "node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.0.tgz",
"integrity": "sha512-FkkhIY/hYFVnOzz1WeV3S9Bd1h0hda/gRqvZCMpHWDHdiIHn6pqsY3b5eSbvGccWHMQ1uUzgZTKS4oGpykf8Tw==", "integrity": "sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@ -2234,9 +2234,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-s390x-gnu": { "node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.0.tgz",
"integrity": "sha512-gRf5c+A7QiOG3UwLyOOtyJMD31JJhMjBvpfhAitPAoqZFcOeK3Kc1Veg1z/trmt+2P6F/biT02fU19GGTS529A==", "integrity": "sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@ -2248,9 +2248,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-gnu": { "node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.0.tgz",
"integrity": "sha512-BR7+blScdLW1h/2hB/2oXM+dhTmpW3rQt1DeSiCP9mc2NMMkqVgjIN3DDsNpKmezffGC9R8XKVOLmBkRUcK/sA==", "integrity": "sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2262,9 +2262,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-musl": { "node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.0.tgz",
"integrity": "sha512-hDMOAe+6nX3V5ei1I7Au3wcr9h3ktKzDvF2ne5ovX8RZiAHEtX1A5SNNk4zt1Qt77CmnbqT+upb/umzoPMWiPg==", "integrity": "sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2275,10 +2275,24 @@
"linux" "linux"
] ]
}, },
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.0.tgz",
"integrity": "sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": { "node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.0.tgz",
"integrity": "sha512-wkNRzfiIGaElC9kXUT+HLx17z7D0jl+9tGYRKwd8r7cUqTL7GYAvgUY++U2hK6Ar7z5Z6IRRoWC8kQxpmM7TDA==", "integrity": "sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2290,9 +2304,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-ia32-msvc": { "node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.0.tgz",
"integrity": "sha512-gq5aW/SyNpjp71AAzroH37DtINDcX1Qw2iv9Chyz49ZgdOP3NV8QCyKZUrGsYX9Yyggj5soFiRCgsL3HwD8TdA==", "integrity": "sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@ -2304,9 +2318,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-x64-msvc": { "node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.0.tgz",
"integrity": "sha512-gEtqFbzmZLFk2xKh7g0Rlo8xzho8KrEFEkzvHbfUGkrgXOpZ4XagQ6n+wIZFNh1nTb8UD16J4nFSFKXYgnbdBg==", "integrity": "sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2671,9 +2685,9 @@
} }
}, },
"node_modules/antd": { "node_modules/antd": {
"version": "5.27.1", "version": "5.27.2",
"resolved": "https://registry.npmmirror.com/antd/-/antd-5.27.1.tgz", "resolved": "https://registry.npmmirror.com/antd/-/antd-5.27.2.tgz",
"integrity": "sha512-jGMSdBN7hAMvPV27B4RhzZfL6n6yu8yDbo7oXrlJasaOqB7bSDPcjdEy1kXy3JPsny/Qazb1ykzRI4EfcByAPQ==", "integrity": "sha512-9pYRUTrumIEuulRwZijhcnL6ScBZ1X6Y3iJa67VyNhEZAiXtENA7AtggsnqdVclQIm0WL4ky76qUdWrVhDWF3g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@ant-design/colors": "^7.2.1", "@ant-design/colors": "^7.2.1",
@ -3585,9 +3599,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001737", "version": "1.0.30001739",
"resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001739.tgz",
"integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", "integrity": "sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -4071,9 +4085,9 @@
} }
}, },
"node_modules/dayjs": { "node_modules/dayjs": {
"version": "1.11.15", "version": "1.11.18",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.15.tgz", "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.18.tgz",
"integrity": "sha512-MC+DfnSWiM9APs7fpiurHGCoeIx0Gdl6QZBy+5lu8MbYKN5FZEXqOgrundfibdfhGZ15o9hzmZ2xJjZnbvgKXQ==", "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/debug": { "node_modules/debug": {
@ -6748,7 +6762,6 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/js-yaml": { "node_modules/js-yaml": {
@ -7041,7 +7054,6 @@
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0" "js-tokens": "^3.0.0 || ^4.0.0"
@ -8758,10 +8770,13 @@
} }
}, },
"node_modules/react": { "node_modules/react": {
"version": "19.1.1", "version": "18.3.1",
"resolved": "https://registry.npmmirror.com/react/-/react-19.1.1.tgz", "resolved": "https://registry.npmmirror.com/react/-/react-18.3.1.tgz",
"integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT", "license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0"
},
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -8777,15 +8792,16 @@
} }
}, },
"node_modules/react-dom": { "node_modules/react-dom": {
"version": "19.1.1", "version": "18.3.1",
"resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-19.1.1.tgz", "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"scheduler": "^0.26.0" "loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^19.1.1" "react": "^18.3.1"
} }
}, },
"node_modules/react-is": { "node_modules/react-is": {
@ -9053,9 +9069,9 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.49.0", "version": "4.50.0",
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.49.0.tgz", "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.50.0.tgz",
"integrity": "sha512-3IVq0cGJ6H7fKXXEdVt+RcYvRCt8beYY9K1760wGQwSAHZcS9eot1zDG5axUbcp/kWRi5zKIIDX8MoKv/TzvZA==", "integrity": "sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@ -9069,26 +9085,27 @@
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.49.0", "@rollup/rollup-android-arm-eabi": "4.50.0",
"@rollup/rollup-android-arm64": "4.49.0", "@rollup/rollup-android-arm64": "4.50.0",
"@rollup/rollup-darwin-arm64": "4.49.0", "@rollup/rollup-darwin-arm64": "4.50.0",
"@rollup/rollup-darwin-x64": "4.49.0", "@rollup/rollup-darwin-x64": "4.50.0",
"@rollup/rollup-freebsd-arm64": "4.49.0", "@rollup/rollup-freebsd-arm64": "4.50.0",
"@rollup/rollup-freebsd-x64": "4.49.0", "@rollup/rollup-freebsd-x64": "4.50.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.49.0", "@rollup/rollup-linux-arm-gnueabihf": "4.50.0",
"@rollup/rollup-linux-arm-musleabihf": "4.49.0", "@rollup/rollup-linux-arm-musleabihf": "4.50.0",
"@rollup/rollup-linux-arm64-gnu": "4.49.0", "@rollup/rollup-linux-arm64-gnu": "4.50.0",
"@rollup/rollup-linux-arm64-musl": "4.49.0", "@rollup/rollup-linux-arm64-musl": "4.50.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.49.0", "@rollup/rollup-linux-loongarch64-gnu": "4.50.0",
"@rollup/rollup-linux-ppc64-gnu": "4.49.0", "@rollup/rollup-linux-ppc64-gnu": "4.50.0",
"@rollup/rollup-linux-riscv64-gnu": "4.49.0", "@rollup/rollup-linux-riscv64-gnu": "4.50.0",
"@rollup/rollup-linux-riscv64-musl": "4.49.0", "@rollup/rollup-linux-riscv64-musl": "4.50.0",
"@rollup/rollup-linux-s390x-gnu": "4.49.0", "@rollup/rollup-linux-s390x-gnu": "4.50.0",
"@rollup/rollup-linux-x64-gnu": "4.49.0", "@rollup/rollup-linux-x64-gnu": "4.50.0",
"@rollup/rollup-linux-x64-musl": "4.49.0", "@rollup/rollup-linux-x64-musl": "4.50.0",
"@rollup/rollup-win32-arm64-msvc": "4.49.0", "@rollup/rollup-openharmony-arm64": "4.50.0",
"@rollup/rollup-win32-ia32-msvc": "4.49.0", "@rollup/rollup-win32-arm64-msvc": "4.50.0",
"@rollup/rollup-win32-x64-msvc": "4.49.0", "@rollup/rollup-win32-ia32-msvc": "4.50.0",
"@rollup/rollup-win32-x64-msvc": "4.50.0",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
@ -9192,10 +9209,13 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/scheduler": { "node_modules/scheduler": {
"version": "0.26.0", "version": "0.23.2",
"resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.26.0.tgz", "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz",
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
"license": "MIT" "license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0"
}
}, },
"node_modules/scroll-into-view-if-needed": { "node_modules/scroll-into-view-if-needed": {
"version": "3.1.0", "version": "3.1.0",
@ -10236,9 +10256,9 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "7.1.3", "version": "7.1.4",
"resolved": "https://registry.npmmirror.com/vite/-/vite-7.1.3.tgz", "resolved": "https://registry.npmmirror.com/vite/-/vite-7.1.4.tgz",
"integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==", "integrity": "sha512-X5QFK4SGynAeeIt+A7ZWnApdUyHYm+pzv/8/A57LqSGcI88U6R6ipOs3uCesdc6yl7nl+zNO0t8LmqAdXcQihw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

6
package.json

@ -24,7 +24,9 @@
"antd": "^5.27.1", "antd": "^5.27.1",
"chart.js": "^4.5.0", "chart.js": "^4.5.0",
"electron-updater": "^6.3.9", "electron-updater": "^6.3.9",
"react-chartjs-2": "^5.3.0" "react": "^18.0.0",
"react-chartjs-2": "^5.3.0",
"react-dom": "^18.0.0"
}, },
"devDependencies": { "devDependencies": {
"@electron-toolkit/eslint-config": "^2.0.0", "@electron-toolkit/eslint-config": "^2.0.0",
@ -38,8 +40,6 @@
"eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20", "eslint-plugin-react-refresh": "^0.4.20",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"vite": "^7.0.5" "vite": "^7.0.5"
} }
} }

28
src/main/index.js

@ -1,10 +1,9 @@
import { app, shell, BrowserWindow, ipcMain } from 'electron' import { app, shell, BrowserWindow } from 'electron'
import { join } from 'path' import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils' import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset' import icon from '../../resources/icon.png?asset'
import { registerIpRouter } from './ipcRouter'
function createWindow() { function createWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({ const mainWindow = new BrowserWindow({
width: 1440, width: 1440,
height: 960, height: 960,
@ -26,8 +25,6 @@ function createWindow() {
return { action: 'deny' } return { action: 'deny' }
}) })
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env['ELECTRON_RENDERER_URL']) { if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
} else { } else {
@ -35,40 +32,23 @@ function createWindow() {
} }
} }
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => { app.whenReady().then(() => {
// Set app user model id for windows
electronApp.setAppUserModelId('com.electron') electronApp.setAppUserModelId('com.electron')
// Default open or close DevTools by F12 in development
// and ignore CommandOrControl + R in production.
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
app.on('browser-window-created', (_, window) => { app.on('browser-window-created', (_, window) => {
optimizer.watchWindowShortcuts(window) optimizer.watchWindowShortcuts(window)
}) })
// IPC处理函数注册
// IPC test registerIpRouter()
ipcMain.on('ping', () => console.log('pong'))
createWindow() createWindow()
app.on('activate', function () { app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow() if (BrowserWindow.getAllWindows().length === 0) createWindow()
}) })
}) })
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => { app.on('window-all-closed', () => {
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
} }
}) })
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

133
src/main/ipcRouter.js

@ -0,0 +1,133 @@
import { ipcMain } from 'electron'
import dgram from 'dgram'
import net from 'net'
import { IPC_EVENT } from '../renderer/src/common/ipcEvents.js'
import fs from 'fs'
// 全局保存所有TCP连接
const tcpClients = new Map()
export function registerIpRouter() {
ipcMain.on(IPC_EVENT.DEVICE_SEARCH, searchDevice) // 设备搜索
ipcMain.on(IPC_EVENT.DEVICE_CONNECT, connectDevice) // 设备连接
ipcMain.on(IPC_EVENT.DEVICE_DISCONNECT, disconnectDevice) // 设备断开
}
const searchDevice = (event) => {
const message = Buffer.from(JSON.stringify({ command: 'name', type: 'get' }))
const PORT = 2230
const BROADCAST_ADDR = '255.255.255.255'
const udpClient = dgram.createSocket('udp4')
let timer = null
const resultMap = new Map()
udpClient.bind(() => {
udpClient.setBroadcast(true)
udpClient.send(message, 0, message.length, PORT, BROADCAST_ADDR, (err) => {
if (err) {
console.error('UDP send failed', err)
udpClient.close()
} else {
console.log('UDP send successful')
}
})
})
udpClient.on('message', (msg, rinfo) => {
try {
// 以 IP 为 key,自动去重
resultMap.set(rinfo.address, {
from: rinfo.address,
data: msg.toString()
})
} catch (e) {
console.error('parse UDP message failed:', e)
}
// 每收到一条消息就重置定时器
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
udpClient.close()
console.log('UDP socket closed after timeout')
// 关闭后统一回复所有结果(已去重)
event.reply && event.reply(IPC_EVENT.DEVICE_SEARCH_REPLY, Array.from(resultMap.values()))
}, 1000) // 1秒内没有新消息就关闭
})
udpClient.on('error', (err) => {
console.error('UDP error:', err)
udpClient.close()
// 出错时也可以回复已收到的内容
event.reply && event.reply(IPC_EVENT.DEVICE_SEARCH_REPLY, Array.from(resultMap.values()))
})
}
const connectDevice = (event, { ip, port }) => {
if (!ip || !port) {
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: false, error: '参数缺失' })
return
}
if (tcpClients.has(ip)) {
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: false, error: '已连接' })
return
}
const client = new net.Socket()
client.connect(Number(port), ip, () => {
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: true, ip })
tcpClients.set(ip, client)
})
let buffer = ''
client.on('data', (data) => {
buffer += data.toString()
let index
while ((index = buffer.indexOf('\n')) !== -1) {
const line = buffer.slice(0, index)
buffer = buffer.slice(index + 1)
if (!line.trim()) continue
let msg
try {
msg = JSON.parse(line)
console.log(msg.command)
} catch (e) {
console.error('TCP data parse error:', e)
fs.appendFileSync('error_log.txt', line + '\n')
continue
}
if (!msg || !msg.command) {
console.log('invalid msg format:', msg)
continue
}
switch (msg.command) {
case 'result':
event.sender.send(IPC_EVENT.SENSOR_DATA, { ip, ...msg })
break
case 'image':
event.sender.send(IPC_EVENT.IMAGE_DATA, { ip, ...msg })
break
case 'heartbeat':
break
default:
console.warn('unknow command type:', msg.command)
}
}
})
client.on('error', (err) => {
event.reply(IPC_EVENT.DEVICE_CONNECT_REPLY, { success: false, error: err.message })
client.destroy()
tcpClients.delete(ip)
})
client.on('close', () => {
tcpClients.delete(ip)
})
}
const disconnectDevice = (event, { ip }) => {
const client = tcpClients.get(ip)
if (client) {
client.destroy()
tcpClients.delete(ip)
event.reply(IPC_EVENT.DEVICE_DISCONNECT_REPLY, { success: true })
} else {
event.reply(IPC_EVENT.DEVICE_DISCONNECT_REPLY, { success: false, error: '未连接' })
}
}

4
src/preload/index.js

@ -1,12 +1,8 @@
import { contextBridge } from 'electron' import { contextBridge } from 'electron'
import { electronAPI } from '@electron-toolkit/preload' import { electronAPI } from '@electron-toolkit/preload'
// Custom APIs for renderer
const api = {} const api = {}
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) { if (process.contextIsolated) {
try { try {
contextBridge.exposeInMainWorld('electron', electronAPI) contextBridge.exposeInMainWorld('electron', electronAPI)

13
src/renderer/src/common/ipcEvents.js

@ -0,0 +1,13 @@
export const IPC_EVENT = {
//设备相关
DEVICE_SEARCH: 'device:search',
DEVICE_SEARCH_REPLY: 'device:search:reply',
DEVICE_CONNECT: 'device:connect',
DEVICE_CONNECT_REPLY: 'device:connect:reply',
DEVICE_DISCONNECT: 'device:disconnect',
DEVICE_DISCONNECT_REPLY: 'device:disconnect:reply',
SENSOR_DATA: 'sensor:data', // 传感器数据
IMAGE_DATA: 'image:data', // 图像数据
HEARTBEAT: 'heartbeat' // 心跳包(如需推送)
}

41
src/renderer/src/components/ImageCollection/ImageCollection.jsx

@ -1,7 +1,40 @@
import React from 'react' import { useEffect, useState } from 'react'
import { IPC_EVENT } from '../../common/ipcEvents'
function ImageCollection() { function ImagePreview() {
return <div>ImageCollection</div> const [imgSrc, setImgSrc] = useState(null)
const [timestamp, setTimestamp] = useState(null)
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)
}
window.electron.ipcRenderer.on(IPC_EVENT.IMAGE_DATA, handler)
return () => {
window.electron.ipcRenderer.removeListener(IPC_EVENT.IMAGE_DATA, handler)
}
}, [])
return (
<div style={{ textAlign: 'center', padding: 16 }}>
{imgSrc ? (
<>
<img
src={imgSrc}
alt="设备图像"
style={{ maxWidth: '100%', maxHeight: 400, border: '1px solid #eee', borderRadius: 4 }}
/>
<div style={{ marginTop: 8, color: '#888', fontSize: 12 }}>{timestamp}</div>
</>
) : (
<div style={{ color: '#aaa' }}>暂无图像数据</div>
)}
</div>
)
} }
export default ImageCollection export default ImagePreview

134
src/renderer/src/components/SiderHeader/SiderHeader.jsx

@ -1,6 +1,7 @@
import { useState, useEffect } from 'react'
import styles from './SiderHeader.module.css' import styles from './SiderHeader.module.css'
import { Flex } from 'antd' import { Flex, Select, Button, Input, Spin, message } from 'antd'
import { Select, Button, Input } from 'antd' import { IPC_EVENT } from '../../common/ipcEvents.js'
import { import {
VideoCameraFilled, VideoCameraFilled,
GoldFilled, GoldFilled,
@ -13,6 +14,93 @@ import MeasurementPointSetting from '../MeasurementPointSetting/MeasurementPoint
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
function SiderHeader({ showSystemSettings = true }) { function SiderHeader({ showSystemSettings = true }) {
const [searching, setSearching] = useState(false)
const [deviceList, setDeviceList] = useState([])
const [selectedDevice, setSelectedDevice] = useState(undefined)
const [devicePort, setDevicePort] = useState('2230')
const [connected, setConnected] = useState(null) // IP
//
useEffect(() => {
const handler = (event, results) => {
setSearching(false)
if (Array.isArray(results) && results.length > 0) {
const ips = results.map((item) => item.from)
setDeviceList(ips.map((ip) => ({ value: ip, label: ip })))
message.success(`发现${ips.length}台设备`)
} else {
setDeviceList([])
message.warning('未发现设备')
}
}
window.electron.ipcRenderer.on(IPC_EVENT.DEVICE_SEARCH_REPLY, handler)
return () => {
window.electron.ipcRenderer.removeListener(IPC_EVENT.DEVICE_SEARCH_REPLY, handler)
}
}, [])
// /
useEffect(() => {
const connectHandler = (event, result) => {
if (result.success) {
setConnected(result.ip)
message.success('设备连接成功')
} else {
setConnected(null)
message.error('设备连接失败: ' + (result.error || '未知错误'))
}
}
const disconnectHandler = (event, result) => {
setConnected(null)
if (result.success) {
message.success('设备已断开')
} else {
message.error('断开失败: ' + (result.error || '未知错误'))
}
}
window.electron.ipcRenderer.on(IPC_EVENT.DEVICE_CONNECT_REPLY, connectHandler)
window.electron.ipcRenderer.on(IPC_EVENT.DEVICE_DISCONNECT_REPLY, disconnectHandler)
return () => {
window.electron.ipcRenderer.removeListener(IPC_EVENT.DEVICE_CONNECT_REPLY, connectHandler)
window.electron.ipcRenderer.removeListener(
IPC_EVENT.DEVICE_DISCONNECT_REPLY,
disconnectHandler
)
}
}, [])
//
const handleSearchDevice = () => {
setSearching(true)
setDeviceList([])
setSelectedDevice(undefined)
window.electron.ipcRenderer.send(IPC_EVENT.DEVICE_SEARCH)
}
// /
const handleConnectOrDisconnect = () => {
if (connected) {
//
window.electron.ipcRenderer.send(IPC_EVENT.DEVICE_DISCONNECT, { ip: connected })
} else {
//
if (!selectedDevice || !devicePort) {
message.error('请选择设备并填写端口')
return
}
window.electron.ipcRenderer.send(IPC_EVENT.DEVICE_CONNECT, {
ip: selectedDevice,
port: devicePort
})
}
}
// UI
const uiDisabled = !!connected
//
console.log('connected:', connected, 'uiDisabled:', uiDisabled)
return ( return (
<Flex vertical> <Flex vertical>
<Flex className={styles.header}> <Flex className={styles.header}>
@ -24,21 +112,47 @@ function SiderHeader({ showSystemSettings = true }) {
<Flex align="center"> <Flex align="center">
<VideoCameraFilled className={styles.icon} /> <VideoCameraFilled className={styles.icon} />
<span className={styles.label}>设备列表:</span> <span className={styles.label}>设备列表:</span>
<Select className={styles.deviceSelect} placeholder="" /> <Select
className={styles.deviceSelect}
placeholder="请选择设备"
loading={searching}
value={selectedDevice}
options={deviceList}
onChange={setSelectedDevice}
style={{ minWidth: 120, flex: 1 }}
notFoundContent={searching ? <Spin size="small" /> : '无设备'}
disabled={searching || uiDisabled}
/>
</Flex> </Flex>
<Flex align="center"> <Flex align="center">
<span className={styles.label}>设备端口:</span> <span className={styles.label}>设备端口:</span>
<GoldFilled className={styles.icon} /> <GoldFilled className={styles.icon} />
<Input className={styles.deviceInput} defaultValue="2230" /> <Input
className={styles.deviceInput}
value={devicePort}
onChange={(e) => setDevicePort(e.target.value)}
disabled={uiDisabled}
/>
</Flex> </Flex>
<Flex gap={8}> <Flex gap={8}>
<Button className={styles.actionButton}> <Button
<SecurityScanFilled /> className={styles.actionButton}
搜索设备 type="primary"
icon={<SecurityScanFilled />}
loading={searching}
onClick={handleSearchDevice}
disabled={searching || uiDisabled}
>
{searching ? '正在搜索...' : '搜索设备'}
</Button> </Button>
<Button className={styles.actionButton}> <Button
<ApiFilled /> className={styles.actionButton}
连接设备 icon={<ApiFilled />}
danger={!!connected}
type={connected ? 'primary' : 'default'}
onClick={handleConnectOrDisconnect}
>
{connected ? '断开连接' : '连接设备'}
</Button> </Button>
</Flex> </Flex>
</Flex> </Flex>

5
src/renderer/src/main.jsx

@ -1,4 +1,3 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
import App from './App' import App from './App'
import 'antd/dist/reset.css' import 'antd/dist/reset.css'
@ -8,8 +7,6 @@ import { ConfigProvider } from 'antd'
import './assets/base.css' import './assets/base.css'
createRoot(document.getElementById('root')).render( createRoot(document.getElementById('root')).render(
<ConfigProvider locale={zhCN}> <ConfigProvider locale={zhCN}>
<StrictMode> <App />
<App />
</StrictMode>
</ConfigProvider> </ConfigProvider>
) )

Loading…
Cancel
Save