From 8a3837f5115296cedca76921b47b9c7c1ad07250 Mon Sep 17 00:00:00 2001 From: "2192596591@qq.com" <2192596591@qq.com> Date: Sat, 21 Mar 2026 10:59:10 +0800 Subject: [PATCH] =?UTF-8?q?=E9=9B=86=E6=88=90mqtt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- STOMP_INTEGRATION.md | 175 ++++++ package-lock.json | 531 +++++++++++++++++- package.json | 3 + src/App.vue | 4 + src/assets/css-vars.css | 4 +- src/components/StompDemo.vue | 222 ++++++++ .../components/draw-line-render/index.vue | 4 +- .../mt-edit/components/line-render/index.vue | 11 +- src/services/nodeService.ts | 135 +++++ src/utils/MQTT_USAGE.md | 300 ++++++++++ src/utils/STOMP_USAGE.md | 309 ++++++++++ src/utils/globalUtils.ts | 30 +- src/utils/stompService.ts | 455 +++++++++++++++ 13 files changed, 2130 insertions(+), 53 deletions(-) create mode 100644 STOMP_INTEGRATION.md create mode 100644 src/components/StompDemo.vue create mode 100644 src/services/nodeService.ts create mode 100644 src/utils/MQTT_USAGE.md create mode 100644 src/utils/STOMP_USAGE.md create mode 100644 src/utils/stompService.ts diff --git a/STOMP_INTEGRATION.md b/STOMP_INTEGRATION.md new file mode 100644 index 0000000..0b8236b --- /dev/null +++ b/STOMP_INTEGRATION.md @@ -0,0 +1,175 @@ +# STOMP 集成完成 + +## 已创建的文件 + +### 核心文件 +1. **`src/utils/stompService.ts`** - STOMP 核心服务 + - 完整的 STOMP 客户端封装(基于 @stomp/stompjs) + - 支持连接、订阅、发布 + - 自动重连机制 + - 服务注册和消息分发 + +### 示例文件 +2. **`src/utils/STOMP_USAGE.md`** - 详细使用文档 + - API 文档 + - 使用示例 + - 迁移指南 + - 故障排除 + +3. **`src/services/nodeService.ts`** - Node 服务示例 + - 展示如何封装业务服务 + - 设备节点管理 + - 消息处理逻辑 + +4. **`src/components/StompDemo.vue`** - Vue 组件示例 + - 完整的演示界面 + - 连接控制 + - 消息发送和日志 + +## 快速开始 + +### 1. 在 main.ts 中初始化 + +```typescript +import { createApp } from 'vue'; +import App from './App.vue'; +import { stompService } from './utils/stompService'; + +const app = createApp(App); + +// 连接 STOMP 服务器(默认地址:ws://localhost:8080/stomp-endpoint) +stompService.connect({ + // 可选配置 + // brokerUrl: 'ws://your-server:8080/stomp-endpoint', + // username: 'admin', + // password: 'password', + reconnectDelay: 5000 +}); + +app.mount('#app'); +``` + +### 2. 在组件中使用 + +```vue + +``` + +### 3. 查看演示 + +将 `StompDemo.vue` 组件添加到您的路由中: + +```typescript +// router/index.ts +import StompDemo from '@/components/StompDemo.vue'; + +{ + path: '/stomp-demo', + name: 'StompDemo', + component: StompDemo +} +``` + +然后访问 `/stomp-demo` 查看演示界面。 + +## 主要特性 + +✅ **自动重连** - 连接丢失后自动重连(默认 5 秒间隔) +✅ **服务注册** - 支持多个业务服务注册 +✅ **消息分发** - 自动将消息分发到对应服务 +✅ **TypeScript** - 完整的类型支持 +✅ **错误处理** - 完善的错误处理和日志 +✅ **易于扩展** - 可轻松添加新服务 + +## 消息命令类型 + +```typescript +MqttCmd = { + Channel: 'channel', // 通道状态 + NodeInit: 'nodeInit', // Node 初始化 + Node: 'node', // Node 更新 + User: 'user', // 用户登录 + Control: 'control', // 控制命令 + VideoInit: 'videoInit', // 视频初始化 + VideoCtl: 'videoCtl', // 视频控制 + Lock: 'lock', // 锁控 + Alarm: 'alarm', // 告警 + Info: 'info', // 通知提示 + SocketDebugger: 'socketDebugger' // 调试 +} +``` + +## 从 ExtJS 迁移 + +### ExtJS 版本 +```javascript +Rec.mqtt.connect(); +Rec.service.node = { msgNode: function(data) {} }; +Rec.mqtt.send(cmd, value); +``` + +### Vue 版本 +```typescript +import { stompService } from '@/utils/stompService'; + +stompService.connect(); +stompService.registerService('node', { msgNode: (data) => {} }); +stompService.send('/app/web/write', cmd, value); +``` + +## 连接地址 + +**默认连接地址:** +- 开发环境:`ws://localhost:8080/stomp-endpoint` +- 生产环境:`ws://your-domain/stomp-endpoint` + +**自定义连接地址:** +```typescript +stompService.connect({ + brokerUrl: 'ws://192.168.1.100:8080/stomp-endpoint' +}); +``` + +## 注意事项 + +1. **连接时机**: 建议在 `main.ts` 中应用启动时连接 +2. **服务注册**: 在组件的 `onMounted` 生命周期注册 +3. **资源清理**: 组件卸载时根据需要清理 +4. **TypeScript**: 所有方法都有完整类型定义 + +## 需要帮助? + +- 查看 `src/utils/STOMP_USAGE.md` 获取详细文档 +- 查看 `src/components/StompDemo.vue` 获取完整示例 +- 查看 `src/services/nodeService.ts` 获取服务封装示例 + +## 下一步 + +1. 根据您的业务需求创建相应的服务 +2. 将现有的 ExtJS 服务迁移到新的架构 +3. 添加更多错误处理和日志记录 +4. 实现消息持久化和离线缓存 diff --git a/package-lock.json b/package-lock.json index 39b5163..0fa6797 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "maotu", "version": "0.3.1", "dependencies": { + "@stomp/stompjs": "^7.3.0", "@tweenjs/tween.js": "^25.0.0", "@vueuse/core": "^10.6.1", "ace-builds": "^1.32.0", @@ -22,7 +23,9 @@ "html2canvas": "^1.4.1", "less": "^4.2.0", "mitt": "^3.0.1", + "mqtt": "^5.15.0", "pinia": "^3.0.4", + "sockjs-client": "^1.6.1", "stats.js": "^0.17.0", "three": "^0.182.0", "vue": "^3.3.4", @@ -547,9 +550,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.6", - "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.6.tgz", - "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "version": "7.29.2", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -2084,6 +2087,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@stomp/stompjs": { + "version": "7.3.0", + "resolved": "https://registry.npmmirror.com/@stomp/stompjs/-/stompjs-7.3.0.tgz", + "integrity": "sha512-nKMLoFfJhrQAqkvvKd1vLq/cVBGCMwPRCD0LqW7UT1fecRx9C3GoKEIR2CYwVuErGeZu8w0kFkl2rlhPlqHVgQ==", + "license": "Apache-2.0" + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz", @@ -2195,7 +2204,6 @@ "version": "18.19.130", "resolved": "https://registry.npmmirror.com/@types/node/-/node-18.19.130.tgz", "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~5.26.4" @@ -2207,6 +2215,15 @@ "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", "license": "MIT" }, + "node_modules/@types/readable-stream": { + "version": "4.0.23", + "resolved": "https://registry.npmmirror.com/@types/readable-stream/-/readable-stream-4.0.23.tgz", + "integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/semver": { "version": "7.7.1", "resolved": "https://registry.npmmirror.com/@types/semver/-/semver-7.7.1.tgz", @@ -2274,6 +2291,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", @@ -3340,6 +3366,18 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/ace-builds": { "version": "1.43.5", "resolved": "https://registry.npmmirror.com/ace-builds/-/ace-builds-1.43.5.tgz", @@ -3812,6 +3850,26 @@ "node": ">= 0.6.0" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/baseline-browser-mapping": { "version": "2.9.14", "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz", @@ -3854,6 +3912,34 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/bl": { + "version": "6.1.6", + "resolved": "https://registry.npmmirror.com/bl/-/bl-6.1.6.tgz", + "integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz", @@ -3891,6 +3977,24 @@ "node": ">=8" } }, + "node_modules/broker-factory": { + "version": "3.1.14", + "resolved": "https://registry.npmmirror.com/broker-factory/-/broker-factory-3.1.14.tgz", + "integrity": "sha512-L45k5HMbPIrMid0nTOZ/UPXG/c0aRuQKVrSDFIb1zOkvfiyHgYmIjc3cSiN1KwQIvRDOtKE0tfb3I9EZ3CmpQQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-factory": "^7.0.49" + } + }, + "node_modules/broker-factory/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/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.1.tgz", @@ -3926,6 +4030,36 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmmirror.com/cac/-/cac-6.7.14.tgz", @@ -4276,6 +4410,12 @@ "node": ">=14" } }, + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", + "license": "MIT" + }, "node_modules/component-emitter": { "version": "1.3.1", "resolved": "https://registry.npmmirror.com/component-emitter/-/component-emitter-1.3.1.tgz", @@ -4306,6 +4446,21 @@ "dev": true, "license": "MIT" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "node_modules/confbox": { "version": "0.2.2", "resolved": "https://registry.npmmirror.com/confbox/-/confbox-0.2.2.tgz", @@ -4683,7 +4838,6 @@ "version": "4.4.3", "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -5711,6 +5865,33 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmmirror.com/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -5872,6 +6053,25 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-unique-numbers": { + "version": "9.0.27", + "resolved": "https://registry.npmmirror.com/fast-unique-numbers/-/fast-unique-numbers-9.0.27.tgz", + "integrity": "sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.2.0" + } + }, + "node_modules/fast-unique-numbers/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/fastq": { "version": "1.20.1", "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.1.tgz", @@ -5882,6 +6082,18 @@ "reusify": "^1.0.4" } }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmmirror.com/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.8.2.tgz", @@ -6582,6 +6794,12 @@ "he": "bin/he" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, "node_modules/hookable": { "version": "5.5.3", "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", @@ -6636,6 +6854,12 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmmirror.com/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "license": "MIT" + }, "node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -6678,6 +6902,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", @@ -6761,7 +7005,6 @@ "version": "2.0.4", "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/ini": { @@ -6786,6 +7029,15 @@ "node": ">= 0.4" } }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/is-accessor-descriptor": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", @@ -7423,6 +7675,16 @@ "node": ">=14" } }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", @@ -7758,7 +8020,6 @@ "version": "10.4.3", "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, "license": "ISC" }, "node_modules/magic-string": { @@ -7946,7 +8207,6 @@ "version": "1.2.8", "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8034,6 +8294,65 @@ "pathe": "^2.0.1" } }, + "node_modules/mqtt": { + "version": "5.15.0", + "resolved": "https://registry.npmmirror.com/mqtt/-/mqtt-5.15.0.tgz", + "integrity": "sha512-KC+wAssYk83Qu5bT8YDzDYgUJxPhbLeVsDvpY2QvL28PnXYJzC2WkKruyMUgBAZaQ7h9lo9k2g4neRNUUxzgMw==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.21", + "@types/ws": "^8.18.1", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.4.1", + "help-me": "^5.0.0", + "lru-cache": "^10.4.3", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.2", + "number-allocator": "^1.0.14", + "readable-stream": "^4.7.0", + "rfdc": "^1.4.1", + "socks": "^2.8.6", + "split2": "^4.2.0", + "worker-timers": "^8.0.23", + "ws": "^8.18.3" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.2", + "resolved": "https://registry.npmmirror.com/mqtt-packet/-/mqtt-packet-9.0.2.tgz", + "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", + "license": "MIT", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/mqtt/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.1.tgz", @@ -8048,7 +8367,6 @@ "version": "2.1.3", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/muggle-string": { @@ -8323,6 +8641,16 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmmirror.com/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, "node_modules/nwsapi": { "version": "2.2.23", "resolved": "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.23.tgz", @@ -9087,6 +9415,21 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmmirror.com/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmmirror.com/proto-list/-/proto-list-1.2.4.tgz", @@ -9165,7 +9508,6 @@ "version": "2.2.0", "resolved": "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz", "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true, "license": "MIT" }, "node_modules/queue-microtask": { @@ -9223,7 +9565,6 @@ "version": "3.6.2", "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -9356,7 +9697,6 @@ "version": "1.0.0", "resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true, "license": "MIT" }, "node_modules/resize-detector": { @@ -9589,7 +9929,6 @@ "version": "5.2.1", "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -9989,6 +10328,16 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmmirror.com/snapdragon/-/snapdragon-0.8.2.tgz", @@ -10114,6 +10463,48 @@ "node": ">=0.10.0" } }, + "node_modules/sockjs-client": { + "version": "1.6.1", + "resolved": "https://registry.npmmirror.com/sockjs-client/-/sockjs-client-1.6.1.tgz", + "integrity": "sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==", + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "eventsource": "^2.0.2", + "faye-websocket": "^0.11.4", + "inherits": "^2.0.4", + "url-parse": "^1.5.10" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://tidelift.com/funding/github/npm/sockjs-client" + } + }, + "node_modules/sockjs-client/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmmirror.com/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", @@ -10205,6 +10596,15 @@ "node": ">=0.10.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -10318,7 +10718,6 @@ "version": "1.3.0", "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -11326,6 +11725,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmmirror.com/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, "node_modules/typedarray.prototype.slice": { "version": "1.0.5", "resolved": "https://registry.npmmirror.com/typedarray.prototype.slice/-/typedarray.prototype.slice-1.0.5.tgz", @@ -11419,7 +11824,6 @@ "version": "5.26.5", "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true, "license": "MIT" }, "node_modules/union-value": { @@ -11610,7 +12014,6 @@ "version": "1.5.10", "resolved": "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, "license": "MIT", "dependencies": { "querystringify": "^2.1.1", @@ -11631,7 +12034,6 @@ "version": "1.0.2", "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, "license": "MIT" }, "node_modules/utrie": { @@ -12146,6 +12548,29 @@ "node": ">=12" } }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmmirror.com/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", @@ -12322,6 +12747,77 @@ "node": ">=0.10.0" } }, + "node_modules/worker-factory": { + "version": "7.0.49", + "resolved": "https://registry.npmmirror.com/worker-factory/-/worker-factory-7.0.49.tgz", + "integrity": "sha512-lW7tpgy6aUv2dFsQhv1yv+XFzdkCf/leoKRTGMPVK5/die6RrUjqgJHJf556qO+ZfytNG6wPXc17E8zzsOLUDw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1" + } + }, + "node_modules/worker-factory/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/worker-timers": { + "version": "8.0.31", + "resolved": "https://registry.npmmirror.com/worker-timers/-/worker-timers-8.0.31.tgz", + "integrity": "sha512-ngkq5S6JuZyztom8tDgBzorLo9byhBMko/sXfgiUD945AuzKGg1GCgDMCC3NaYkicLpGKXutONM36wEX8UbBCA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1", + "worker-timers-broker": "^8.0.16", + "worker-timers-worker": "^9.0.14" + } + }, + "node_modules/worker-timers-broker": { + "version": "8.0.16", + "resolved": "https://registry.npmmirror.com/worker-timers-broker/-/worker-timers-broker-8.0.16.tgz", + "integrity": "sha512-JyP3AvUGyPGbBGW7XiUewm2+0pN/aYo1QpVf5kdXAfkDZcN3p7NbWrG6XnyDEpDIvfHk/+LCnOW/NsuiU9riYA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "broker-factory": "^3.1.14", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-timers-worker": "^9.0.14" + } + }, + "node_modules/worker-timers-broker/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/worker-timers-worker": { + "version": "9.0.14", + "resolved": "https://registry.npmmirror.com/worker-timers-worker/-/worker-timers-worker-9.0.14.tgz", + "integrity": "sha512-/qF06C60sXmSLfUl7WglvrDIbspmPOM8UrG63Dnn4bi2x4/DfqHS/+dxF5B+MdHnYO5tVuZYLHdAodrKdabTIg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1", + "worker-factory": "^7.0.49" + } + }, + "node_modules/worker-timers-worker/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/worker-timers/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/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -12434,7 +12930,6 @@ "version": "8.19.0", "resolved": "https://registry.npmmirror.com/ws/-/ws-8.19.0.tgz", "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", - "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index 0a4e944..0301a16 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ }, "typings": "dist/export.d.ts", "dependencies": { + "@stomp/stompjs": "^7.3.0", "@tweenjs/tween.js": "^25.0.0", "@vueuse/core": "^10.6.1", "ace-builds": "^1.32.0", @@ -43,7 +44,9 @@ "html2canvas": "^1.4.1", "less": "^4.2.0", "mitt": "^3.0.1", + "mqtt": "^5.15.0", "pinia": "^3.0.4", + "sockjs-client": "^1.6.1", "stats.js": "^0.17.0", "three": "^0.182.0", "vue": "^3.3.4", diff --git a/src/App.vue b/src/App.vue index c5d095c..d35907c 100644 --- a/src/App.vue +++ b/src/App.vue @@ -4,6 +4,7 @@ + @@ -17,6 +18,9 @@ import { leftAsideStore } from '@/export'; import viewIndex from '@/layout/view_index.vue'; import zhCn from 'element-plus/es/locale/lang/zh-cn'; // xq 组件 +// F:\vue\workspace\maotu-webtopo\src\components\StompDemo.vue +import StompDemo from '@/components/StompDemo.vue'; + import VueMyTest from '@/components/my-test.vue'; import VueMyButton from '@/components/my-button.vue'; import vueInstrument from '@/components/vue-xq-test/vue-instrument.vue'; diff --git a/src/assets/css-vars.css b/src/assets/css-vars.css index 7c5774c..7b02830 100644 --- a/src/assets/css-vars.css +++ b/src/assets/css-vars.css @@ -45,8 +45,8 @@ --el-box-shadow: 0px 12px 32px 4px rgba(0, 0, 0, 0.36), 0px 8px 20px rgba(0, 0, 0, 0.72); --el-box-shadow-light: 0px 0px 12px rgba(0, 0, 0, 0.72); --el-box-shadow-lighter: 0px 0px 6px rgba(0, 0, 0, 0.72); - --el-box-shadow-dark: 0px 16px 48px 16px rgba(0, 0, 0, 0.72), 0px 12px 32px #000000, - 0px 8px 16px -8px #000000; + --el-box-shadow-dark: + 0px 16px 48px 16px rgba(0, 0, 0, 0.72), 0px 12px 32px #000000, 0px 8px 16px -8px #000000; --el-bg-color-page: #0a0a0a; --el-bg-color: #141414; --el-bg-color-overlay: #1d1e1f; diff --git a/src/components/StompDemo.vue b/src/components/StompDemo.vue new file mode 100644 index 0000000..f88f394 --- /dev/null +++ b/src/components/StompDemo.vue @@ -0,0 +1,222 @@ + + + + + STOMP 连接演示 + + {{ isConnected ? '已连接' : '未连接' }} + + + + + + + 连接 + 断开 + + + + + 发送消息 + + + + + + + + + + + + + 发送 + + + + + + + 消息日志 + + + + + + + {{ row.direction }} + + + + + + 清空日志 + + + + + + + diff --git a/src/components/mt-edit/components/draw-line-render/index.vue b/src/components/mt-edit/components/draw-line-render/index.vue index f8dd9ce..cf4c75a 100644 --- a/src/components/mt-edit/components/draw-line-render/index.vue +++ b/src/components/mt-edit/components/draw-line-render/index.vue @@ -83,8 +83,8 @@ ? 1000 : 0 : lineRenderProps.itemJson.props.ani_play.val - ? 0 - : 1000 + ? 0 + : 1000 " :dur="`${ lineRenderProps.itemJson.props.ani_dur.val < 1 diff --git a/src/components/mt-edit/components/line-render/index.vue b/src/components/mt-edit/components/line-render/index.vue index 0db3327..9223530 100644 --- a/src/components/mt-edit/components/line-render/index.vue +++ b/src/components/mt-edit/components/line-render/index.vue @@ -83,8 +83,8 @@ ? 1000 : 0 : lineRenderProps.itemJson.props.ani_play.val - ? 0 - : 1000 + ? 0 + : 1000 " :dur="`${ lineRenderProps.itemJson.props.ani_dur.val < 1 @@ -147,8 +147,8 @@ ? 1000 : 0 : lineRenderProps.itemJson.props.ani_play.val - ? 0 - : 1000 + ? 0 + : 1000 " :dur="`${ lineRenderProps.itemJson.props.ani_dur.val < 1 @@ -534,7 +534,8 @@ watch( diff --git a/src/services/nodeService.ts b/src/services/nodeService.ts new file mode 100644 index 0000000..ac47286 --- /dev/null +++ b/src/services/nodeService.ts @@ -0,0 +1,135 @@ +/** + * Node 服务示例 + * 处理设备节点相关的 STOMP 消息 + */ + +import { stompService } from '@/utils/stompService'; + +interface NodeData { + id: string; + name: string; + status: string; + [key: string]: any; +} + +interface NodeRuntime { + node: NodeData; + [key: string]: any; +} + +class NodeService { + public runtimes: Record = {}; + + constructor() { + // 注册服务 + stompService.registerService('node', this); + } + + /** + * STOMP 连接成功后调用 + */ + mqttReady(): void { + console.log('[NodeService] STOMP 已就绪'); + // 可以在这里请求初始化数据 + } + + /** + * 连接丢失时调用 + */ + onConnectionLost(): void { + console.log('[NodeService] STOMP 连接丢失'); + } + + /** + * 处理 Node 初始化消息 + * @param value 初始化数据 + */ + msgNodeInit(value: any): void { + console.log('[NodeService] 收到 Node 初始化数据:', value); + + // 处理初始化逻辑 + if (value && Array.isArray(value.nodes)) { + value.nodes.forEach((node: NodeData) => { + this.runtimes[node.id] = { + node, + lastUpdate: new Date() + }; + }); + } + } + + /** + * 处理 Node 更新消息 + * @param value 更新数据 + */ + msgNode(value: any): void { + console.log('[NodeService] 收到 Node 更新:', value); + + if (value && value.id) { + const runtime = this.runtimes[value.id]; + if (runtime) { + // 更新现有节点 + runtime.node = { ...runtime.node, ...value }; + runtime.lastUpdate = new Date(); + } else { + // 添加新节点 + this.runtimes[value.id] = { + node: value, + lastUpdate: new Date() + }; + } + + // 触发更新事件(可以使用 mitt 或其他事件总线) + this.emitNodeUpdate(value.id); + } + } + + /** + * 获取节点数据 + * @param nodeId 节点 ID + */ + getNode(nodeId: string): NodeData | null { + const runtime = this.runtimes[nodeId]; + return runtime ? runtime.node : null; + } + + /** + * 获取所有节点 + */ + getAllNodes(): NodeData[] { + return Object.values(this.runtimes).map((runtime) => runtime.node); + } + + /** + * 更新节点属性 + * @param nodeId 节点 ID + * @param updates 更新的属性 + */ + updateNode(nodeId: string, updates: Partial): void { + const runtime = this.runtimes[nodeId]; + if (runtime) { + runtime.node = { ...runtime.node, ...updates }; + runtime.lastUpdate = new Date(); + + // 发送更新到服务器 + stompService.send('/app/web/write', 'nodeUpdate', { + id: nodeId, + updates + }); + } + } + + /** + * 触发节点更新事件 + * @param nodeId 节点 ID + */ + private emitNodeUpdate(nodeId: string): void { + // 这里可以使用 mitt 事件总线 + // emitter.emit('node-update', nodeId); + console.log(`[NodeService] 节点 ${nodeId} 已更新`); + } +} + +// 导出单例 +export const nodeService = new NodeService(); +export default nodeService; diff --git a/src/utils/MQTT_USAGE.md b/src/utils/MQTT_USAGE.md new file mode 100644 index 0000000..81c4e8c --- /dev/null +++ b/src/utils/MQTT_USAGE.md @@ -0,0 +1,300 @@ +# MQTT 服务使用指南 + +## 简介 + +这是一个基于 `mqtt` 库封装的 MQTT 服务,提供了完整的连接、订阅、发布和消息处理功能。 + +## 主要功能 + +- ✅ 自动连接和重连 +- ✅ 服务注册机制 +- ✅ 消息自动分发 +- ✅ TypeScript 类型支持 +- ✅ 错误处理和日志记录 + +## 使用方法 + +### 1. 在 main.ts 中初始化 + +```typescript +import { mqttService } from './utils/mqttService'; + +// 应用启动时连接 MQTT +const app = createApp(App); + +// 连接 MQTT 服务器 +mqttService.connect({ + // brokerUrl: 'ws://your-mqtt-broker:8083/mqtt', // 可选,默认使用当前域名 + // username: 'your-username', // 可选 + // password: 'your-password', // 可选 + // clientId: 'your-client-id', // 可选,自动生成 + reconnectPeriod: 5000 // 重连间隔,默认 5000ms +}); + +app.mount('#app'); +``` + +### 2. 注册服务 + +```typescript +// 在您的组件或 store 中 +import { mqttService } from '@/utils/mqttService'; + +// 注册您的服务 +mqttService.registerService('node', { + // 当 MQTT 连接成功时会调用此方法 + mqttReady() { + console.log('Node 服务已就绪'); + }, + + // 处理 Node 初始化消息 + msgNodeInit(data: any) { + console.log('收到 Node 初始化数据:', data); + }, + + // 处理 Node 更新消息 + msgNode(data: any) { + console.log('收到 Node 更新:', data); + }, + + // 运行时的数据 + runtimes: {} +}); + +// 注册其他服务 +mqttService.registerService('channel', { + online(cid: string, isOnline: boolean) { + console.log(`通道 ${cid} 状态:`, isOnline); + } +}); + +mqttService.registerService('video', { + initVideo(data: any) { + console.log('初始化视频:', data); + }, + playVideo(data: any) { + console.log('播放视频:', data); + } +}); +``` + +### 3. 发送消息 + +```typescript +import { mqttService, MqttCmd } from '@/utils/mqttService'; + +// 发送控制命令 +mqttService.send('/app/web/write', MqttCmd.Control, { + nodeId: 'node-001', + action: 'turnOn' +}); + +// 发送 Node 更新 +mqttService.send('/app/web/write', MqttCmd.Node, { + node: { + id: 'node-001', + status: 'active' + } +}); +``` + +### 4. 订阅主题 + +```typescript +import { mqttService } from '@/utils/mqttService'; + +// 订阅自定义主题 +mqttService.subscribe('/topic/custom', (message) => { + console.log('收到自定义消息:', message); +}); + +// 取消订阅 +mqttService.unsubscribe('/topic/custom'); +``` + +### 5. 在 Vue 组件中使用 + +```vue + + + 发送命令 + 连接状态:{{ isConnected ? '已连接' : '未连接' }} + + + + +``` + +## 消息命令类型 + +```typescript +export const MqttCmd = { + Channel: 'channel', // 通道状态 + NodeInit: 'nodeInit', // Node 初始化 + Node: 'node', // Node 更新 + User: 'user', // 用户登录 + Control: 'control', // 控制命令 + VideoInit: 'videoInit', // 视频初始化 + VideoCtl: 'videoCtl', // 视频控制 + Lock: 'lock', // 锁控 + Alarm: 'alarm', // 告警 + Info: 'info', // 通知提示 + SocketDebugger: 'socketDebugger' // 调试 +}; +``` + +## API 文档 + +### mqttService.connect(config?) + +连接 MQTT 服务器 + +**参数:** + +- `config?: MqttConfig` - 连接配置 + - `brokerUrl?: string` - MQTT 服务器地址 + - `username?: string` - 用户名 + - `password?: string` - 密码 + - `clientId?: string` - 客户端 ID + - `reconnectPeriod?: number` - 重连间隔(毫秒) + +### mqttService.disconnect() + +断开 MQTT 连接 + +### mqttService.send(topic, cmd, value) + +发送消息 + +**参数:** + +- `topic: string` - MQTT 主题 +- `cmd: string` - 命令类型 +- `value: any` - 消息内容 + +### mqttService.subscribe(topic, callback?) + +订阅主题 + +**参数:** + +- `topic: string` - 主题 +- `callback?: (message: MqttMessage) => void` - 消息回调 + +### mqttService.unsubscribe(topic) + +取消订阅 + +**参数:** + +- `topic: string` - 主题 + +### mqttService.registerService(name, service) + +注册服务 + +**参数:** + +- `name: string` - 服务名称 +- `service: any` - 服务对象 + +### mqttService.getClientStatus() + +获取客户端状态 + +**返回:** `{ connected: boolean; client: MqttClient | null }` + +## 注意事项 + +1. **连接时机**: 建议在应用启动时(main.ts)连接 MQTT +2. **服务注册**: 在组件的 `onMounted` 生命周期注册服务 +3. **清理资源**: 在组件卸载时根据需要断开连接或取消订阅 +4. **错误处理**: 服务已内置重连机制,无需手动处理 +5. **类型安全**: 所有方法都有 TypeScript 类型支持 + +## 迁移指南 + +从旧的 ExtJS 版本迁移: + +### 旧代码 + +```javascript +Rec.mqtt.connect(); +Rec.service.node = { ... }; +Rec.mqtt.send(cmd, value); +``` + +### 新代码 + +```typescript +import { mqttService } from '@/utils/mqttService'; + +mqttService.connect(); +mqttService.registerService('node', { ... }); +mqttService.send('/app/web/write', cmd, value); +``` + +## 示例项目结构 + +``` +src/ +├── utils/ +│ └── mqttService.ts # MQTT 服务 +├── services/ +│ ├── nodeService.ts # Node 服务 +│ ├── channelService.ts # 通道服务 +│ └── videoService.ts # 视频服务 +└── views/ + └── YourComponent.vue # 使用 MQTT 的组件 +``` + +## 故障排除 + +### 连接失败 + +- 检查 MQTT 服务器地址是否正确 +- 检查网络连接 +- 查看浏览器控制台日志 + +### 消息未收到 + +- 确认服务已注册 +- 检查主题订阅是否正确 +- 查看消息格式是否符合预期 + +### 重连问题 + +- 检查 `reconnectPeriod` 设置 +- 查看服务器是否在线 +- 检查认证信息是否正确 diff --git a/src/utils/STOMP_USAGE.md b/src/utils/STOMP_USAGE.md new file mode 100644 index 0000000..abb5010 --- /dev/null +++ b/src/utils/STOMP_USAGE.md @@ -0,0 +1,309 @@ +# STOMP 服务使用指南 + +## 简介 + +这是一个基于 `@stomp/stompjs` 库封装的 STOMP 服务,提供了完整的连接、订阅、发布和消息处理功能。 + +**注意:** 您的项目使用的是 **STOMP over WebSocket** 协议,而不是 MQTT。 + +## 主要功能 + +- ✅ 自动连接和重连 +- ✅ 服务注册机制 +- ✅ 消息自动分发 +- ✅ TypeScript 类型支持 +- ✅ 错误处理和日志记录 + +## 使用方法 + +### 1. 在 main.ts 中初始化 + +```typescript +import { stompService } from './utils/stompService'; + +// 应用启动时连接 STOMP +const app = createApp(App); + +// 连接 STOMP 服务器 +stompService.connect({ + // brokerUrl: 'ws://your-server:8080/stomp-endpoint', // 可选,默认使用当前域名 + // username: 'your-username', // 可选 + // password: 'your-password', // 可选 + reconnectDelay: 5000 // 重连间隔,默认 5000ms +}); + +app.mount('#app'); +``` + +### 2. 注册服务 + +```typescript +// 在您的组件或 store 中 +import { stompService } from '@/utils/stompService'; + +// 注册您的服务 +stompService.registerService('node', { + // 当 STOMP 连接成功时会调用此方法 + mqttReady() { + console.log('Node 服务已就绪'); + }, + + // 处理 Node 初始化消息 + msgNodeInit(data: any) { + console.log('收到 Node 初始化数据:', data); + }, + + // 处理 Node 更新消息 + msgNode(data: any) { + console.log('收到 Node 更新:', data); + }, + + // 运行时的数据 + runtimes: {} +}); + +// 注册其他服务 +stompService.registerService('channel', { + online(cid: string, isOnline: boolean) { + console.log(`通道 ${cid} 状态:`, isOnline); + } +}); + +stompService.registerService('video', { + initVideo(data: any) { + console.log('初始化视频:', data); + }, + playVideo(data: any) { + console.log('播放视频:', data); + } +}); +``` + +### 3. 发送消息 + +```typescript +import { stompService, MqttCmd } from '@/utils/stompService'; + +// 发送控制命令 +stompService.send('/app/web/write', MqttCmd.Control, { + nodeId: 'node-001', + action: 'turnOn' +}); + +// 发送 Node 更新 +stompService.send('/app/web/write', MqttCmd.Node, { + node: { + id: 'node-001', + status: 'active' + } +}); +``` + +### 4. 订阅主题 + +```typescript +import { stompService } from '@/utils/stompService'; + +// 订阅自定义主题 +stompService.subscribe('/topic/custom', (message) => { + console.log('收到自定义消息:', message); +}); + +// 取消订阅 +stompService.unsubscribe('/topic/custom'); +``` + +### 5. 在 Vue 组件中使用 + +```vue + + + 发送命令 + 连接状态:{{ isConnected ? '已连接' : '未连接' }} + + + + +``` + +## 消息命令类型 + +```typescript +export const MqttCmd = { + Channel: 'channel', // 通道状态 + NodeInit: 'nodeInit', // Node 初始化 + Node: 'node', // Node 更新 + User: 'user', // 用户登录 + Control: 'control', // 控制命令 + VideoInit: 'videoInit', // 视频初始化 + VideoCtl: 'videoCtl', // 视频控制 + Lock: 'lock', // 锁控 + Alarm: 'alarm', // 告警 + Info: 'info', // 通知提示 + SocketDebugger: 'socketDebugger' // 调试 +}; +``` + +## API 文档 + +### stompService.connect(config?) + +连接 STOMP 服务器 + +**参数:** + +- `config?: StompServiceConfig` - 连接配置 + - `brokerUrl?: string` - STOMP 服务器地址 + - `username?: string` - 用户名 + - `password?: string` - 密码 + - `reconnectDelay?: number` - 重连间隔(毫秒) + +### stompService.disconnect() + +断开 STOMP 连接 + +### stompService.send(destination, cmd, value) + +发送消息 + +**参数:** + +- `destination: string` - STOMP 目标地址 +- `cmd: string` - 命令类型 +- `value: any` - 消息内容 + +### stompService.subscribe(destination, callback?) + +订阅主题 + +**参数:** + +- `destination: string` - 目标地址 +- `callback?: (message: StompMessage) => void` - 消息回调 + +### stompService.unsubscribe(destination) + +取消订阅 + +**参数:** + +- `destination: string` - 目标地址 + +### stompService.registerService(name, service) + +注册服务 + +**参数:** + +- `name: string` - 服务名称 +- `service: any` - 服务对象 + +### stompService.getClientStatus() + +获取客户端状态 + +**返回:** `{ connected: boolean; client: Client | null }` + +## 注意事项 + +1. **连接时机**: 建议在应用启动时(main.ts)连接 STOMP +2. **服务注册**: 在组件的 `onMounted` 生命周期注册服务 +3. **清理资源**: 组件卸载时根据需要断开连接或取消订阅 +4. **错误处理**: 服务已内置重连机制,无需手动处理 +5. **类型安全**: 所有方法都有 TypeScript 类型支持 + +## 迁移指南 + +从旧的 ExtJS 版本迁移: + +### 旧代码 + +```javascript +Rec.mqtt.connect(); +Rec.service.node = { ... }; +Rec.mqtt.send(cmd, value); +``` + +### 新代码 + +```typescript +import { stompService } from '@/utils/stompService'; + +stompService.connect(); +stompService.registerService('node', { ... }); +stompService.send('/app/web/write', cmd, value); +``` + +## 示例项目结构 + +``` +src/ +├── utils/ +│ └── stompService.ts # STOMP 服务 +├── services/ +│ ├── nodeService.ts # Node 服务 +│ ├── channelService.ts # 通道服务 +│ └── videoService.ts # 视频服务 +└── views/ + └── YourComponent.vue # 使用 STOMP 的组件 +``` + +## 故障排除 + +### 连接失败 + +- 检查 STOMP 服务器地址是否正确 +- 检查 WebSocket 是否可用(某些浏览器需要 HTTPS) +- 查看浏览器控制台日志 + +### 消息未收到 + +- 确认服务已注册 +- 检查主题订阅是否正确 +- 查看消息格式是否符合预期 + +### 重连问题 + +- 检查 `reconnectDelay` 设置 +- 查看服务器是否在线 +- 检查认证信息是否正确 + +## 与 MQTT 的区别 + +STOMP 是一个简单的文本协议,基于 WebSocket: + +- 使用 `destination` 而不是 `topic` +- 使用 `send/publish` 发送消息 +- 使用 `subscribe` 订阅消息 +- 连接地址格式:`ws://host/stomp-endpoint` diff --git a/src/utils/globalUtils.ts b/src/utils/globalUtils.ts index 25de932..8e0ca0a 100644 --- a/src/utils/globalUtils.ts +++ b/src/utils/globalUtils.ts @@ -76,7 +76,6 @@ if (typeof window !== 'undefined') { // 获取所有节点 async function getData() { try { - debugger; const response = await modelApi.node_nrt_get(); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); @@ -111,31 +110,10 @@ if (typeof window !== 'undefined') { } // 监听页面刷新/加载事件 -// window.addEventListener('load', () => { -// getData(); -// }); +window.addEventListener('load', () => { + getData(); +}); // 也可以直接挂载到全局对象 // (window as any).myGlobalFunction = myGlobalFunction; -// (window as any).anotherGlobalFunction = anotherGlobalFunction; - -// 接收父页面消息 -window.addEventListener('message', function (event) { - if (event.data.type === 'SET_GLOBAL_DATA') { - // 接收父页面发送的数据 - window.globalData = event.data.payload; - console.log('接收到父页面数据:', window.globalData); - } -}); - -// 通知父页面 Vue 已准备就绪 -window.addEventListener('load', () => { - if (window.parent !== window) { - window.parent.postMessage( - { - type: 'VUE_APP_READY' - }, - '*' - ); - } -}); +(window as any).anotherGlobalFunction = anotherGlobalFunction; diff --git a/src/utils/stompService.ts b/src/utils/stompService.ts new file mode 100644 index 0000000..62a5702 --- /dev/null +++ b/src/utils/stompService.ts @@ -0,0 +1,455 @@ +import { Client, Message, IFrame } from '@stomp/stompjs'; +import type { StompConfig } from '@stomp/stompjs'; + +// 定义消息命令常量 +export const MqttCmd = { + Channel: 1, // 通道消息 + NodeInit: 2, // 节点初始化 + Node: 3, // 节点消息 + VideoInit: 4, // 视频初始化 + VideoCtl: 5, // PTZ 控制 + VideoPlay: 6, // 视频播放 + Lock: 7, // 锁控消息 + Control: 10, // 控制消息反馈 + Alarm: 20, // 告警消息 + Info: 100, // 通知 + Error: 101, // 错误 + DirVideo: 201, // 视频消息 + DirLock: 202, // 锁控消息 + DirLockWeb: 203, // 锁控 WEB 消息 + User: 1000, // 用户登陆 + SocketDebugger: 3001 // SocketDebugger 消息 +}; + +// 定义消息接口 +export interface StompMessage { + cmd: number; + value: any; +} + +// 定义服务注册表 +export interface ServiceRegistry { + [key: string]: any; +} + +// STOMP 配置接口 +export interface StompServiceConfig { + brokerUrl?: string; + username?: string; + password?: string; + reconnectDelay?: number; +} + +class StompService { + private client: Client | null = null; + private isConnected: boolean = false; + private services: ServiceRegistry = {}; + private config: StompServiceConfig = {}; + + /** + * 注册服务 + * @param name 服务名称 + * @param service 服务对象 + */ + public registerService(name: string, service: any): void { + this.services[name] = service; + console.log(`[STOMP] 服务已注册:${name}`); + } + + /** + * 连接 STOMP 服务器 + * @param config 连接配置 + */ + public connect(config: StompServiceConfig = {}): void { + if (this.client) { + console.warn('[STOMP] 客户端已存在,跳过连接'); + return; + } + + // 保存配置 + this.config = config; + + // 构建连接地址 + const protocol = location.protocol === 'https:' ? 'wss://' : 'ws://'; + // ${location.host} localhost:5173 + const host = 'localhost:8080'; + const brokerUrl = config.brokerUrl || `${protocol}${host}/stomp-endpoint`; + + // STOMP 配置 + const stompConfig: StompConfig = { + brokerURL: brokerUrl, + connectHeaders: { + login: config.username || '', + passcode: config.password || '' + }, + debug: (str: string) => {}, // 关闭调试日志 + reconnectDelay: config.reconnectDelay || 5000, // 重连延迟(毫秒) + + // 连接成功回调 + onConnect: (frame: IFrame) => { + this.onConnected(frame); + }, + + // 连接失败回调 + onStompError: (frame: IFrame) => { + console.error('[STOMP] 服务器错误:', frame); + }, + + // WebSocket 关闭回调 + onWebSocketClose: () => { + this.onConnectionLost(); + }, + + // WebSocket 错误回调 + onWebSocketError: (evt: Event) => { + console.error('[STOMP] WebSocket 错误:', evt); + this.handleConnectionError(evt); + } + }; + + try { + // 创建 STOMP 客户端 + this.client = new Client(stompConfig); + + // 激活连接 + this.client.activate(); + + console.log('[STOMP] 正在连接至:', brokerUrl); + } catch (error) { + console.error('[STOMP] 连接失败:', error); + this.handleConnectionError(error); + } + } + + /** + * 断开连接 + */ + public disconnect(): void { + if (this.client) { + this.client.deactivate(); + this.client = null; + this.isConnected = false; + console.log('[STOMP] 已断开连接'); + } + } + + /** + * 发送消息 + * @param destination 目标地址 + * @param cmd 命令 + * @param value 值 + */ + public send(destination: string, cmd: number, value: any): void { + if (!this.client || !this.isConnected) { + console.error('[STOMP] 客户端未连接,无法发送消息'); + return; + } + + const message: StompMessage = { + cmd, + value + }; + + try { + this.client.publish({ + destination, + body: JSON.stringify(message) + }); + // console.log(`[STOMP] 发送消息:${destination}`, message); + } catch (error) { + console.error('[STOMP] 发送消息失败:', error); + } + } + + /** + * 订阅主题 + * @param destination 目标地址 + * @param callback 回调函数 + */ + public subscribe(destination: string, callback?: (message: StompMessage) => void): void { + if (!this.client || !this.isConnected) { + console.error('[STOMP] 客户端未连接,无法订阅'); + return; + } + + try { + this.client.subscribe(destination, (message: Message) => { + this.onMessageArrived(message); + }); + + console.log(`[STOMP] 订阅成功:${destination}`); + + // 如果有自定义回调,添加到内部消息处理 + if (callback) { + this.services[`_custom_${destination}`] = { callback }; + } + } catch (error) { + console.error('[STOMP] 订阅失败:', error); + } + } + + /** + * 取消订阅 + * @param destination 目标地址 + */ + public unsubscribe(destination: string): void { + if (!this.client) { + return; + } + + try { + // STOMP 客户端会自动处理取消订阅 + delete this.services[`_custom_${destination}`]; + console.log(`[STOMP] 取消订阅:${destination}`); + } catch (error) { + console.error('[STOMP] 取消订阅失败:', error); + } + } + + /** + * 连接成功回调 + * @param frame STOMP 帧 + */ + private onConnected(frame: IFrame): void { + this.isConnected = true; + console.log('[STOMP] 连接成功!'); + + // 订阅默认主题 + this.subscribe('/topic/web/read'); + + // 通知所有服务 STOMP 已就绪 + for (const serviceName in this.services) { + if (this.services[serviceName].mqttReady || this.services[serviceName].stompReady) { + const callback = + this.services[serviceName].mqttReady || this.services[serviceName].stompReady; + callback.call(this.services[serviceName]); + } + } + } + + /** + * 连接丢失回调 + */ + private onConnectionLost(): void { + this.isConnected = false; + console.log('[STOMP] 连接丢失!'); + + // 通知所有服务 + for (const serviceName in this.services) { + if (this.services[serviceName].onConnectionLost) { + this.services[serviceName].onConnectionLost(); + } + } + } + + /** + * 消息到达处理 + * @param message STOMP 消息 + */ + private onMessageArrived(message: Message): void { + try { + const msgStr = message.body || ''; + const msgObject: StompMessage = JSON.parse(msgStr); + + // console.log('[STOMP] 收到消息:', message.destination, msgObject); + + // 根据命令类型分发到不同的服务 + this.dispatchMessage(msgObject); + + // 处理自定义回调 + const customService = this.services[`_custom_${message.destination}`]; + if (customService && customService.callback) { + customService.callback(msgObject); + } + } catch (error) { + console.error('[STOMP] 消息解析失败:', error); + } + } + + /** + * 消息分发 + * @param msgObject 消息对象 + */ + private dispatchMessage(msgObject: StompMessage): void { + switch (msgObject.cmd) { + case MqttCmd.Channel: + // 通道消息 + if (this.services.channel) { + this.services.channel.online(msgObject.value.cid, msgObject.value.online); + } + break; + + case MqttCmd.NodeInit: + // Node 初始化消息 + if (this.services.node) { + this.services.node.msgNodeInit(msgObject.value); + } + break; + + case MqttCmd.Node: + debugger + // 单一 Node 更新消息 + if (this.services.node) { + this.services.nod + console.log('[STOMP] 收到 Node 更新消息:', msgObject.value); + } + break; + + case MqttCmd.User: + // 用户登录消息 - 需要验证 + const uid = this.getCookie('uid'); + if (msgObject.value.uid !== uid) return; + const clientId = this.getCookie('client-id'); + if (msgObject.value.clientid === clientId) return; + // 退出登录 + window.location.href = window.location.origin + '/logout'; + break; + + case MqttCmd.Control: + // 控制返回 + this.showToast(msgObject.value); + break; + + case MqttCmd.VideoInit: + // 视频初始化 + if (this.services.video) { + this.services.video.initVideo(msgObject.value); + } + break; + + case MqttCmd.VideoCtl: + // 视频 PTZ 控制 + if (this.services.video) { + this.services.video.playVideo(msgObject.value); + } + break; + + case MqttCmd.Lock: + // 锁控消息 + if (this.services.lock) { + this.services.lock.onMsg(msgObject.value); + } + break; + + case MqttCmd.Alarm: + // 告警消息 + if (this.services.node) { + if (!this.services.node.runtimes) { + this.services.node.runtimes = {}; + } + this.services.node.runtimes[msgObject.value.node.id] = msgObject.value; + } + if (this.services.banner) { + this.services.banner.alarm(msgObject.value); + } + break; + + case MqttCmd.Info: + // 通知和提示 + this.showToast(msgObject.value); + break; + + case MqttCmd.SocketDebugger: + if (this.services.SocketDebugger) { + this.services.SocketDebugger.onMsg(msgObject.value); + } + break; + + default: + console.warn('[STOMP] 未知命令类型:', msgObject.cmd); + } + } + + /** + * 处理连接错误 + * @param error 错误对象 + */ + private handleConnectionError(error: any): void { + console.error('[STOMP] 连接错误:', error); + + // STOMP 客户端会自动重连,这里只需要通知服务 + for (const serviceName in this.services) { + if (this.services[serviceName].onConnectionError) { + this.services[serviceName].onConnectionError(error); + } + } + } + + /** + * 生成 UUID + */ + private generateUUID(): string { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + const r = (Math.random() * 16) | 0; + const v = c === 'x' ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); + } + + /** + * 获取 Cookie + */ + private getCookie(cname: string): string { + const name = cname + '='; + const ca = document.cookie.split(';'); + for (let i = 0; i < ca.length; i++) { + let c = ca[i].trim(); + if (c.indexOf(name) === 0) { + return c.substring(name.length, c.length); + } + } + return ''; + } + + /** + * 设置 Cookie + */ + private setCookie(cname: string, cvalue: string, exdays: number): void { + const d = new Date(); + d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000); + const expires = 'expires=' + d.toUTCString(); + document.cookie = cname + '=' + cvalue + '; ' + expires; + } + + /** + * 清除 Cookie + */ + private clearCookie(name: string): void { + this.setCookie(name, '', -1); + } + + /** + * 显示提示消息 + */ + private showToast(message: any): void { + console.log('[STOMP Toast]:', message); + + + + // 如果使用了 Element Plus + try { + import('element-plus').then(({ ElMessage }) => { + ElMessage.info(typeof message === 'string' ? message : JSON.stringify(message)); + }); + } catch (e) { + // 如果没有 Element Plus,使用 alert + alert(typeof message === 'string' ? message : JSON.stringify(message)); + } + } + + /** + * 获取客户端状态 + */ + public getClientStatus(): { connected: boolean; client: Client | null } { + return { + connected: this.isConnected, + client: this.client + }; + } +} + +// 导出单例 +export const stompService = new StompService(); + +// 默认导出 +export default stompService;