增加管理运行状态界面

This commit is contained in:
zy 2026-03-30 16:27:45 +08:00
parent 4388a6ef49
commit a9866d71e8
19 changed files with 2315 additions and 152 deletions

View File

@ -6,7 +6,7 @@ VITE_PUBLIC_PATH = /
# 跨域代理,您可以配置多个 ,请注意,没有换行符 # 跨域代理,您可以配置多个 ,请注意,没有换行符
VITE_PROXY = [["/jeecgboot","http://192.168.2.110:9999"],["/upload","http://192.168.2.110:3300/upload"]] VITE_PROXY = [["/jeecgboot","http://192.168.2.110:9999"],["/upload","http://192.168.2.110:3300/upload"]]
VITE_PROXY = [["/jeecgboot","https://fxnsp.sxcooh.com/lh-api/"],["/upload","http://192.168.2.111:3300/upload"]] #VITE_PROXY = [["/jeecgboot","https://fxnsp.sxcooh.com/lh-api/"],["/upload","http://192.168.2.111:3300/upload"]]
# 控制台不输出 # 控制台不输出
VITE_DROP_CONSOLE = false VITE_DROP_CONSOLE = false
@ -23,3 +23,8 @@ VITE_GLOB_API_URL_PREFIX=
#微前端qiankun应用,命名必须以VITE_APP_SUB_开头,jeecg-app-1为子应用的项目名称,也是子应用的路由父路径 #微前端qiankun应用,命名必须以VITE_APP_SUB_开头,jeecg-app-1为子应用的项目名称,也是子应用的路由父路径
VITE_APP_SUB_jeecg-app-1 = '//localhost:8092' VITE_APP_SUB_jeecg-app-1 = '//localhost:8092'
# mqtt
VITE_MQTT_BROKER_URL=ws://mqtt.ilhzn.cn:80/mqtt
VITE_MQTT_USERNAME=lhzn.mqtt
VITE_MQTT_PASSWORD=lhzn.2025

View File

@ -36,3 +36,8 @@ VITE_USE_PWA = false
# 是否兼容旧浏览器 # 是否兼容旧浏览器
VITE_LEGACY = false VITE_LEGACY = false
# mqtt
VITE_MQTT_BROKER_URL=ws://mqtt.ilhzn.cn:80/mqtt
VITE_MQTT_USERNAME=lhzn.mqtt
VITE_MQTT_PASSWORD=lhzn.2025

View File

@ -48,7 +48,6 @@
"@vueuse/shared": "^8.3.0", "@vueuse/shared": "^8.3.0",
"@zxcvbn-ts/core": "^2.0.1", "@zxcvbn-ts/core": "^2.0.1",
"ant-design-vue": "^3.2.12", "ant-design-vue": "^3.2.12",
"html2canvas": "1.4.1",
"axios": "^0.26.1", "axios": "^0.26.1",
"china-area-data": "^5.0.1", "china-area-data": "^5.0.1",
"clipboard": "^2.0.8", "clipboard": "^2.0.8",
@ -62,11 +61,13 @@
"emoji-mart-vue-fast": "^11.1.1", "emoji-mart-vue-fast": "^11.1.1",
"enquire.js": "^2.1.6", "enquire.js": "^2.1.6",
"ezuikit-js": "^7.6.8", "ezuikit-js": "^7.6.8",
"html2canvas": "1.4.1",
"intro.js": "^5.1.0", "intro.js": "^5.1.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
"md5": "^2.3.0", "md5": "^2.3.0",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"mqtt": "^5.15.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"path-to-regexp": "^6.2.0", "path-to-regexp": "^6.2.0",
"pinia": "2.0.12", "pinia": "2.0.12",
@ -80,6 +81,9 @@
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"tinymce": "^5.10.3", "tinymce": "^5.10.3",
"vditor": "^3.8.13", "vditor": "^3.8.13",
"video.js": "^8.21.1",
"videojs-contrib-hls": "^5.15.0",
"videojs-vtt.js": "^0.15.5",
"vue": "^3.2.33", "vue": "^3.2.33",
"vue-cropper": "^0.5.6", "vue-cropper": "^0.5.6",
"vue-cropperjs": "^5.0.0", "vue-cropperjs": "^5.0.0",
@ -93,10 +97,7 @@
"vxe-table": "4.1.0", "vxe-table": "4.1.0",
"vxe-table-plugin-antd": "3.0.5", "vxe-table-plugin-antd": "3.0.5",
"xe-utils": "^3.3.1", "xe-utils": "^3.3.1",
"xss": "^1.0.13", "xss": "^1.0.13"
"video.js": "^8.21.1",
"videojs-contrib-hls": "^5.15.0",
"videojs-vtt.js": "^0.15.5"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^16.2.3", "@commitlint/cli": "^16.2.3",

View File

@ -114,6 +114,9 @@ importers:
mockjs: mockjs:
specifier: ^1.1.0 specifier: ^1.1.0
version: 1.1.0 version: 1.1.0
mqtt:
specifier: ^5.15.1
version: 5.15.1
nprogress: nprogress:
specifier: ^0.2.0 specifier: ^0.2.0
version: 0.2.0 version: 0.2.0
@ -1061,6 +1064,10 @@ packages:
resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/runtime@7.29.2':
resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==}
engines: {node: '>=6.9.0'}
'@babel/standalone@7.28.5': '@babel/standalone@7.28.5':
resolution: {integrity: sha512-1DViPYJpRU50irpGMfLBQ9B4kyfQuL6X7SS7pwTeWeZX0mNkjzPi0XFqxCjSdddZXUQy4AhnQnnesA/ZHnvAdw==} resolution: {integrity: sha512-1DViPYJpRU50irpGMfLBQ9B4kyfQuL6X7SS7pwTeWeZX0mNkjzPi0XFqxCjSdddZXUQy4AhnQnnesA/ZHnvAdw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
@ -1899,6 +1906,9 @@ packages:
'@types/raf@3.4.3': '@types/raf@3.4.3':
resolution: {integrity: sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==} resolution: {integrity: sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==}
'@types/readable-stream@4.0.23':
resolution: {integrity: sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==}
'@types/resolve@1.17.1': '@types/resolve@1.17.1':
resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==} resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
@ -1932,6 +1942,9 @@ packages:
'@types/trusted-types@2.0.7': '@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
'@types/ws@8.18.1':
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
'@types/yargs-parser@21.0.3': '@types/yargs-parser@21.0.3':
resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
@ -2254,6 +2267,10 @@ packages:
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
deprecated: Use your platform's native atob() and btoa() methods instead deprecated: Use your platform's native atob() and btoa() methods instead
abort-controller@3.0.0:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
acorn-globals@6.0.0: acorn-globals@6.0.0:
resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==} resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==}
@ -2600,6 +2617,9 @@ packages:
bl@4.1.0: bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
bl@6.1.6:
resolution: {integrity: sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==}
bluebird@3.7.2: bluebird@3.7.2:
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
@ -2620,6 +2640,9 @@ packages:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'} engines: {node: '>=8'}
broker-factory@3.1.14:
resolution: {integrity: sha512-L45k5HMbPIrMid0nTOZ/UPXG/c0aRuQKVrSDFIb1zOkvfiyHgYmIjc3cSiN1KwQIvRDOtKE0tfb3I9EZ3CmpQQ==}
browser-process-hrtime@1.0.0: browser-process-hrtime@1.0.0:
resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==}
@ -2658,6 +2681,9 @@ packages:
buffer@5.7.1: buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
builtin-modules@3.3.0: builtin-modules@3.3.0:
resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
engines: {node: '>=6'} engines: {node: '>=6'}
@ -2917,6 +2943,9 @@ packages:
resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
engines: {node: ^12.20.0 || >=14} engines: {node: ^12.20.0 || >=14}
commist@3.2.0:
resolution: {integrity: sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==}
commitizen@4.2.4: commitizen@4.2.4:
resolution: {integrity: sha512-LlZChbDzg3Ir3O2S7jSo/cgWp5/QwylQVr59K4xayVq8S4/RdKzSyJkghAiZZHfhh5t4pxunUoyeg0ml1q/7aw==} resolution: {integrity: sha512-LlZChbDzg3Ir3O2S7jSo/cgWp5/QwylQVr59K4xayVq8S4/RdKzSyJkghAiZZHfhh5t4pxunUoyeg0ml1q/7aw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
@ -2938,6 +2967,10 @@ packages:
concat-map@0.0.1: concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
concat-stream@2.0.0:
resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
engines: {'0': node >= 6.0}
config-chain@1.1.13: config-chain@1.1.13:
resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
@ -3854,9 +3887,17 @@ packages:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
event-target-shim@5.0.1:
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
engines: {node: '>=6'}
eventemitter3@4.0.7: eventemitter3@4.0.7:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
events@3.3.0:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
engines: {node: '>=0.8.x'}
exec-buffer@3.2.0: exec-buffer@3.2.0:
resolution: {integrity: sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA==} resolution: {integrity: sha512-wsiD+2Tp6BWHoVv3B+5Dcx6E7u5zky+hUwOHjuH2hKSLR3dvRmX8fk8UD8uqQixHs4Wk6eDmiegVrMPjKj7wpA==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -3948,6 +3989,10 @@ packages:
fast-levenshtein@2.0.6: fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
fast-unique-numbers@9.0.27:
resolution: {integrity: sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A==}
engines: {node: '>=18.2.0'}
fast-uri@3.1.0: fast-uri@3.1.0:
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
@ -4416,6 +4461,9 @@ packages:
header-case@2.0.4: header-case@2.0.4:
resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==}
help-me@5.0.0:
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
homedir-polyfill@1.0.3: homedir-polyfill@1.0.3:
resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -4618,6 +4666,10 @@ packages:
intro.js@5.1.0: intro.js@5.1.0:
resolution: {integrity: sha512-zwWl/duTh00eeNcZRU4o4/xxloNYPFKs4n4lMRDNx59jZr+qRI0jSOnzqYMOuVftD4beGrmxBHz4k8qp9/dCMA==} resolution: {integrity: sha512-zwWl/duTh00eeNcZRU4o4/xxloNYPFKs4n4lMRDNx59jZr+qRI0jSOnzqYMOuVftD4beGrmxBHz4k8qp9/dCMA==}
ip-address@10.1.0:
resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==}
engines: {node: '>= 12'}
is-accessor-descriptor@1.0.1: is-accessor-descriptor@1.0.1:
resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==} resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==}
engines: {node: '>= 0.10'} engines: {node: '>= 0.10'}
@ -5112,6 +5164,9 @@ packages:
js-base64@2.6.4: js-base64@2.6.4:
resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==} resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==}
js-sdsl@4.3.0:
resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==}
js-tokens@4.0.0: js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@ -5368,6 +5423,9 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
hasBin: true hasBin: true
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
lru-cache@4.1.5: lru-cache@4.1.5:
resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
@ -5573,6 +5631,14 @@ packages:
resolution: {integrity: sha512-1FuyEWI5k2HcmhS1HkKnUAQV7yFPfXPht2DnRRGtoiiAAW+ESTbtEXIDpRkwdU+XyrQuwrIym7UkoPKsZ0SyFw==} resolution: {integrity: sha512-1FuyEWI5k2HcmhS1HkKnUAQV7yFPfXPht2DnRRGtoiiAAW+ESTbtEXIDpRkwdU+XyrQuwrIym7UkoPKsZ0SyFw==}
hasBin: true hasBin: true
mqtt-packet@9.0.2:
resolution: {integrity: sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==}
mqtt@5.15.1:
resolution: {integrity: sha512-V1WnkGuJh3ec9QXzy5Iylw8OOBK+Xu1WhxcQ9mMpLThG+/JZIMV1PgLNRgIiqXhZnvnVLsuyxHl5A/3bHHbcAA==}
engines: {node: '>=16.0.0'}
hasBin: true
mri@1.2.0: mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -5693,6 +5759,9 @@ packages:
nth-check@2.1.1: nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
number-allocator@1.0.14:
resolution: {integrity: sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==}
nwsapi@2.2.22: nwsapi@2.2.22:
resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==}
@ -6283,6 +6352,10 @@ packages:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
readable-stream@4.7.0:
resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
readdirp@3.6.0: readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'} engines: {node: '>=8.10.0'}
@ -6654,6 +6727,10 @@ packages:
resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
smart-buffer@4.2.0:
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
snake-case@3.0.4: snake-case@3.0.4:
resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
@ -6669,6 +6746,10 @@ packages:
resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==} resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
socks@2.8.7:
resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==}
engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
sort-keys-length@1.0.1: sort-keys-length@1.0.1:
resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==} resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -6749,6 +6830,10 @@ packages:
split2@3.2.2: split2@3.2.2:
resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==}
split2@4.2.0:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
split@1.0.1: split@1.0.1:
resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==}
@ -7262,6 +7347,9 @@ packages:
resolution: {integrity: sha512-q7QNVDGTdl702bVFiI5eY4l/HkgCM6at9KhcFbgUAzezHFbOVy4+0O/lCjsABEQwbZPravVfBIiBVGo89yzHFg==} resolution: {integrity: sha512-q7QNVDGTdl702bVFiI5eY4l/HkgCM6at9KhcFbgUAzezHFbOVy4+0O/lCjsABEQwbZPravVfBIiBVGo89yzHFg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
typedarray@0.0.6:
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
typescript@4.6.3: typescript@4.6.3:
resolution: {integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==} resolution: {integrity: sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==}
engines: {node: '>=4.2.0'} engines: {node: '>=4.2.0'}
@ -7836,6 +7924,18 @@ packages:
workbox-window@6.6.0: workbox-window@6.6.0:
resolution: {integrity: sha512-L4N9+vka17d16geaJXXRjENLFldvkWy7JyGxElRD0JvBxvFEd8LOhr+uXCcar/NzAmIBRv9EZ+M+Qr4mOoBITw==} resolution: {integrity: sha512-L4N9+vka17d16geaJXXRjENLFldvkWy7JyGxElRD0JvBxvFEd8LOhr+uXCcar/NzAmIBRv9EZ+M+Qr4mOoBITw==}
worker-factory@7.0.49:
resolution: {integrity: sha512-lW7tpgy6aUv2dFsQhv1yv+XFzdkCf/leoKRTGMPVK5/die6RrUjqgJHJf556qO+ZfytNG6wPXc17E8zzsOLUDw==}
worker-timers-broker@8.0.16:
resolution: {integrity: sha512-JyP3AvUGyPGbBGW7XiUewm2+0pN/aYo1QpVf5kdXAfkDZcN3p7NbWrG6XnyDEpDIvfHk/+LCnOW/NsuiU9riYA==}
worker-timers-worker@9.0.14:
resolution: {integrity: sha512-/qF06C60sXmSLfUl7WglvrDIbspmPOM8UrG63Dnn4bi2x4/DfqHS/+dxF5B+MdHnYO5tVuZYLHdAodrKdabTIg==}
worker-timers@8.0.31:
resolution: {integrity: sha512-ngkq5S6JuZyztom8tDgBzorLo9byhBMko/sXfgiUD945AuzKGg1GCgDMCC3NaYkicLpGKXutONM36wEX8UbBCA==}
wrap-ansi@6.2.0: wrap-ansi@6.2.0:
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -7866,6 +7966,18 @@ packages:
utf-8-validate: utf-8-validate:
optional: true optional: true
ws@8.20.0:
resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
xe-utils@3.3.1: xe-utils@3.3.1:
resolution: {integrity: sha512-OdQgl9WPV9dK3/djneFPrGX8z1M4neX+VOkzra5oONjoNsCKQhwdiut99WlxceNMQ5vXDv4EQ/wKA2fux3Gdug==} resolution: {integrity: sha512-OdQgl9WPV9dK3/djneFPrGX8z1M4neX+VOkzra5oONjoNsCKQhwdiut99WlxceNMQ5vXDv4EQ/wKA2fux3Gdug==}
@ -8709,6 +8821,8 @@ snapshots:
'@babel/runtime@7.28.4': {} '@babel/runtime@7.28.4': {}
'@babel/runtime@7.29.2': {}
'@babel/standalone@7.28.5': {} '@babel/standalone@7.28.5': {}
'@babel/template@7.27.2': '@babel/template@7.27.2':
@ -9795,6 +9909,10 @@ snapshots:
'@types/raf@3.4.3': '@types/raf@3.4.3':
optional: true optional: true
'@types/readable-stream@4.0.23':
dependencies:
'@types/node': 17.0.25
'@types/resolve@1.17.1': '@types/resolve@1.17.1':
dependencies: dependencies:
'@types/node': 17.0.25 '@types/node': 17.0.25
@ -9827,6 +9945,10 @@ snapshots:
'@types/trusted-types@2.0.7': {} '@types/trusted-types@2.0.7': {}
'@types/ws@8.18.1':
dependencies:
'@types/node': 17.0.25
'@types/yargs-parser@21.0.3': {} '@types/yargs-parser@21.0.3': {}
'@types/yargs@16.0.11': '@types/yargs@16.0.11':
@ -10284,6 +10406,10 @@ snapshots:
abab@2.0.6: {} abab@2.0.6: {}
abort-controller@3.0.0:
dependencies:
event-target-shim: 5.0.1
acorn-globals@6.0.0: acorn-globals@6.0.0:
dependencies: dependencies:
acorn: 7.4.1 acorn: 7.4.1
@ -10671,6 +10797,13 @@ snapshots:
inherits: 2.0.4 inherits: 2.0.4
readable-stream: 3.6.2 readable-stream: 3.6.2
bl@6.1.6:
dependencies:
'@types/readable-stream': 4.0.23
buffer: 6.0.3
inherits: 2.0.4
readable-stream: 4.7.0
bluebird@3.7.2: {} bluebird@3.7.2: {}
boolbase@1.0.0: {} boolbase@1.0.0: {}
@ -10703,6 +10836,13 @@ snapshots:
dependencies: dependencies:
fill-range: 7.1.1 fill-range: 7.1.1
broker-factory@3.1.14:
dependencies:
'@babel/runtime': 7.29.2
fast-unique-numbers: 9.0.27
tslib: 2.8.1
worker-factory: 7.0.49
browser-process-hrtime@1.0.0: {} browser-process-hrtime@1.0.0: {}
browserslist@4.28.0: browserslist@4.28.0:
@ -10741,6 +10881,11 @@ snapshots:
base64-js: 1.5.1 base64-js: 1.5.1
ieee754: 1.2.1 ieee754: 1.2.1
buffer@6.0.3:
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1
builtin-modules@3.3.0: {} builtin-modules@3.3.0: {}
builtins@4.1.0: builtins@4.1.0:
@ -11034,6 +11179,8 @@ snapshots:
commander@9.5.0: {} commander@9.5.0: {}
commist@3.2.0: {}
commitizen@4.2.4(@types/node@17.0.25)(typescript@4.6.3): commitizen@4.2.4(@types/node@17.0.25)(typescript@4.6.3):
dependencies: dependencies:
cachedir: 2.2.0 cachedir: 2.2.0
@ -11067,6 +11214,13 @@ snapshots:
concat-map@0.0.1: {} concat-map@0.0.1: {}
concat-stream@2.0.0:
dependencies:
buffer-from: 1.1.2
inherits: 2.0.4
readable-stream: 3.6.2
typedarray: 0.0.6
config-chain@1.1.13: config-chain@1.1.13:
dependencies: dependencies:
ini: 1.3.8 ini: 1.3.8
@ -12089,8 +12243,12 @@ snapshots:
etag@1.8.1: {} etag@1.8.1: {}
event-target-shim@5.0.1: {}
eventemitter3@4.0.7: {} eventemitter3@4.0.7: {}
events@3.3.0: {}
exec-buffer@3.2.0: exec-buffer@3.2.0:
dependencies: dependencies:
execa: 0.7.0 execa: 0.7.0
@ -12233,6 +12391,11 @@ snapshots:
fast-levenshtein@2.0.6: {} fast-levenshtein@2.0.6: {}
fast-unique-numbers@9.0.27:
dependencies:
'@babel/runtime': 7.29.2
tslib: 2.8.1
fast-uri@3.1.0: {} fast-uri@3.1.0: {}
fast-xml-parser@4.5.3: fast-xml-parser@4.5.3:
@ -12767,6 +12930,8 @@ snapshots:
capital-case: 1.0.4 capital-case: 1.0.4
tslib: 2.8.1 tslib: 2.8.1
help-me@5.0.0: {}
homedir-polyfill@1.0.3: homedir-polyfill@1.0.3:
dependencies: dependencies:
parse-passwd: 1.0.0 parse-passwd: 1.0.0
@ -13031,6 +13196,8 @@ snapshots:
intro.js@5.1.0: {} intro.js@5.1.0: {}
ip-address@10.1.0: {}
is-accessor-descriptor@1.0.1: is-accessor-descriptor@1.0.1:
dependencies: dependencies:
hasown: 2.0.2 hasown: 2.0.2
@ -13712,6 +13879,8 @@ snapshots:
js-base64@2.6.4: {} js-base64@2.6.4: {}
js-sdsl@4.3.0: {}
js-tokens@4.0.0: {} js-tokens@4.0.0: {}
js-yaml@3.14.2: js-yaml@3.14.2:
@ -14002,6 +14171,8 @@ snapshots:
longest: 1.0.1 longest: 1.0.1
meow: 3.7.0 meow: 3.7.0
lru-cache@10.4.3: {}
lru-cache@4.1.5: lru-cache@4.1.5:
dependencies: dependencies:
pseudomap: 1.0.2 pseudomap: 1.0.2
@ -14233,6 +14404,37 @@ snapshots:
'@xmldom/xmldom': 0.8.11 '@xmldom/xmldom': 0.8.11
global: 4.4.0 global: 4.4.0
mqtt-packet@9.0.2:
dependencies:
bl: 6.1.6
debug: 4.4.3(supports-color@9.4.0)
process-nextick-args: 2.0.1
transitivePeerDependencies:
- supports-color
mqtt@5.15.1:
dependencies:
'@types/readable-stream': 4.0.23
'@types/ws': 8.18.1
commist: 3.2.0
concat-stream: 2.0.0
debug: 4.4.3(supports-color@9.4.0)
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.7
split2: 4.2.0
worker-timers: 8.0.31
ws: 8.20.0
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
mri@1.2.0: {} mri@1.2.0: {}
ms@2.0.0: {} ms@2.0.0: {}
@ -14368,6 +14570,13 @@ snapshots:
dependencies: dependencies:
boolbase: 1.0.0 boolbase: 1.0.0
number-allocator@1.0.14:
dependencies:
debug: 4.4.3(supports-color@9.4.0)
js-sdsl: 4.3.0
transitivePeerDependencies:
- supports-color
nwsapi@2.2.22: {} nwsapi@2.2.22: {}
object-assign@4.1.1: {} object-assign@4.1.1: {}
@ -14922,6 +15131,14 @@ snapshots:
string_decoder: 1.3.0 string_decoder: 1.3.0
util-deprecate: 1.0.2 util-deprecate: 1.0.2
readable-stream@4.7.0:
dependencies:
abort-controller: 3.0.0
buffer: 6.0.3
events: 3.3.0
process: 0.11.10
string_decoder: 1.3.0
readdirp@3.6.0: readdirp@3.6.0:
dependencies: dependencies:
picomatch: 2.3.1 picomatch: 2.3.1
@ -15305,6 +15522,8 @@ snapshots:
ansi-styles: 6.2.3 ansi-styles: 6.2.3
is-fullwidth-code-point: 4.0.0 is-fullwidth-code-point: 4.0.0
smart-buffer@4.2.0: {}
snake-case@3.0.4: snake-case@3.0.4:
dependencies: dependencies:
dot-case: 3.0.4 dot-case: 3.0.4
@ -15333,6 +15552,11 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
socks@2.8.7:
dependencies:
ip-address: 10.1.0
smart-buffer: 4.2.0
sort-keys-length@1.0.1: sort-keys-length@1.0.1:
dependencies: dependencies:
sort-keys: 1.1.2 sort-keys: 1.1.2
@ -15404,6 +15628,8 @@ snapshots:
dependencies: dependencies:
readable-stream: 3.6.2 readable-stream: 3.6.2
split2@4.2.0: {}
split@1.0.1: split@1.0.1:
dependencies: dependencies:
through: 2.3.8 through: 2.3.8
@ -16002,6 +16228,8 @@ snapshots:
typed-array-buffer: 1.0.3 typed-array-buffer: 1.0.3
typed-array-byte-offset: 1.0.4 typed-array-byte-offset: 1.0.4
typedarray@0.0.6: {}
typescript@4.6.3: {} typescript@4.6.3: {}
uglify-js@3.19.3: uglify-js@3.19.3:
@ -16754,6 +16982,33 @@ snapshots:
'@types/trusted-types': 2.0.7 '@types/trusted-types': 2.0.7
workbox-core: 6.6.0 workbox-core: 6.6.0
worker-factory@7.0.49:
dependencies:
'@babel/runtime': 7.29.2
fast-unique-numbers: 9.0.27
tslib: 2.8.1
worker-timers-broker@8.0.16:
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
worker-timers-worker@9.0.14:
dependencies:
'@babel/runtime': 7.29.2
tslib: 2.8.1
worker-factory: 7.0.49
worker-timers@8.0.31:
dependencies:
'@babel/runtime': 7.29.2
tslib: 2.8.1
worker-timers-broker: 8.0.16
worker-timers-worker: 9.0.14
wrap-ansi@6.2.0: wrap-ansi@6.2.0:
dependencies: dependencies:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
@ -16782,6 +17037,8 @@ snapshots:
ws@7.5.10: {} ws@7.5.10: {}
ws@8.20.0: {}
xe-utils@3.3.1: {} xe-utils@3.3.1: {}
xhr@2.4.0: xhr@2.4.0:

View File

@ -0,0 +1,312 @@
import { ref, onMounted, onUnmounted } from 'vue';
import mqtt, { MqttClient, IClientOptions } from 'mqtt';
import { mqttConfig } from '@/config/mqtt';
export function useMqtt() {
// 状态
const client = ref<MqttClient | null>(null);
const isConnected = ref(false);
const isConnecting = ref(false);
const error = ref<string | null>(null);
const messages = ref<Array<{ topic: string; payload: string; timestamp: number }>>([]);
// 消息处理器映射
const messageHandlers = new Map<string, (payload: string, topic: string) => void>();
/**
* MQTT
*/
const connect = () => {
if (client.value && isConnected.value) {
console.warn('MQTT 已经连接');
return;
}
if (isConnecting.value) {
console.warn('MQTT 正在连接中');
return;
}
isConnecting.value = true;
error.value = null;
try {
console.log('开始连接 MQTT...', mqttConfig.brokerUrl);
// 创建客户端
client.value = mqtt.connect(mqttConfig.brokerUrl, mqttConfig.options);
// 连接成功
client.value.on('connect', () => {
console.log('MQTT 连接成功');
isConnected.value = true;
isConnecting.value = false;
error.value = null;
// 订阅主题
subscribeTopics();
});
// 连接错误
client.value.on('error', (err) => {
console.error('MQTT 连接错误:', err);
error.value = err.message;
isConnected.value = false;
isConnecting.value = false;
});
// 连接关闭
client.value.on('close', () => {
console.log('MQTT 连接关闭');
isConnected.value = false;
isConnecting.value = false;
});
// 收到消息
client.value.on('message', (topic, message) => {
const payload = message.toString();
console.log('收到消息:', topic, payload);
// 存储消息
messages.value.unshift({
topic,
payload,
timestamp: Date.now(),
});
// 限制消息数量
if (messages.value.length > 100) {
messages.value.pop();
}
// 调用对应的处理器
const handler = messageHandlers.get(topic);
if (handler) {
handler(payload, topic);
}
// 通配符匹配
for (const [pattern, handler] of messageHandlers.entries()) {
if (pattern.includes('+') || pattern.includes('#')) {
if (matchTopic(pattern, topic)) {
handler(payload, topic);
}
}
}
});
// 重连
client.value.on('reconnect', () => {
console.log('MQTT 正在重连...');
});
// 离线
client.value.on('offline', () => {
console.log('MQTT 离线');
isConnected.value = false;
});
} catch (err: any) {
console.error('创建 MQTT 客户端失败:', err);
error.value = err.message;
isConnecting.value = false;
}
};
/**
*
*/
const subscribeTopics = () => {
if (!client.value || !isConnected.value) {
console.warn('MQTT 未连接,无法订阅');
return;
}
mqttConfig.topics.forEach(({ topic, qos }) => {
client.value!.subscribe(topic, { qos }, (err) => {
if (err) {
console.error(`订阅主题失败: ${topic}`, err);
} else {
console.log(`订阅主题成功: ${topic} (QoS: ${qos})`);
}
});
});
};
/**
*
*/
const subscribe = (topic: string, qos: number = 1): Promise<void> => {
return new Promise((resolve, reject) => {
if (!client.value || !isConnected.value) {
reject(new Error('MQTT 未连接'));
return;
}
client.value.subscribe(topic, { qos }, (err) => {
if (err) {
console.error(`订阅主题失败: ${topic}`, err);
reject(err);
} else {
console.log(`订阅主题成功: ${topic}`);
resolve();
}
});
});
};
/**
*
*/
const unsubscribe = (topic: string): Promise<void> => {
return new Promise((resolve, reject) => {
if (!client.value || !isConnected.value) {
reject(new Error('MQTT 未连接'));
return;
}
client.value.unsubscribe(topic, (err) => {
if (err) {
console.error(`取消订阅失败: ${topic}`, err);
reject(err);
} else {
console.log(`取消订阅成功: ${topic}`);
resolve();
}
});
});
};
/**
*
*/
const publish = (topic: string, payload: string | object, qos: number = 1, retain: boolean = false): Promise<void> => {
return new Promise((resolve, reject) => {
if (!client.value || !isConnected.value) {
reject(new Error('MQTT 未连接'));
return;
}
let message: string;
if (typeof payload === 'object') {
message = JSON.stringify(payload);
} else {
message = payload;
}
client.value.publish(topic, message, { qos, retain }, (err) => {
if (err) {
console.error(`发布消息失败: ${topic}`, err);
reject(err);
} else {
console.log(`发布消息成功: ${topic}`);
resolve();
}
});
});
};
/**
*
*/
const sendDeviceCommand = (deviceId: string, command: string, params?: any): Promise<void> => {
const topic = `lanhai/device/${deviceId}/command`;
const payload = {
command,
params: params || {},
timestamp: Date.now(),
requestId: generateRequestId(),
};
return publish(topic, payload, 1, false);
};
/**
*
*/
const onMessage = (topic: string, handler: (payload: string, topic: string) => void) => {
messageHandlers.set(topic, handler);
};
/**
*
*/
const offMessage = (topic: string) => {
messageHandlers.delete(topic);
};
/**
*
*/
const clearMessages = () => {
messages.value = [];
};
/**
*
*/
const disconnect = () => {
if (client.value) {
client.value.end();
client.value = null;
isConnected.value = false;
messageHandlers.clear();
console.log('MQTT 已断开连接');
}
};
/**
* ID
*/
const generateRequestId = (): string => {
return `${Date.now()}-${Math.random().toString(36).substring(2, 10)}`;
};
/**
*
*/
const matchTopic = (pattern: string, topic: string): boolean => {
const patternParts = pattern.split('/');
const topicParts = topic.split('/');
if (patternParts.length > topicParts.length && patternParts[patternParts.length - 1] !== '#') {
return false;
}
for (let i = 0; i < patternParts.length; i++) {
if (patternParts[i] === '+') continue;
if (patternParts[i] === '#') return true;
if (patternParts[i] !== topicParts[i]) return false;
}
return patternParts.length === topicParts.length;
};
// 自动连接
onMounted(() => {
connect();
});
// 断开连接
onUnmounted(() => {
disconnect();
});
return {
// 状态
client,
isConnected,
isConnecting,
error,
messages,
// 方法
connect,
disconnect,
publish,
subscribe,
unsubscribe,
sendDeviceCommand,
onMessage,
offMessage,
clearMessages,
};
}

View File

@ -0,0 +1,203 @@
import { ref, onMounted, onUnmounted, type Ref } from 'vue';
import mqtt, { MqttClient } from 'mqtt';
interface MqttOptions {
brokerUrl: string;
clientId?: string;
username?: string;
password?: string;
keepalive?: number;
reconnectPeriod?: number;
}
export function usePageMqtt(options: MqttOptions) {
// 状态
const client = ref<MqttClient | null>(null);
const isConnected = ref(false);
const isConnecting = ref(false);
const error = ref<string | null>(null);
// 消息处理器
let messageHandler: ((payload: string, topic: string) => void) | null = null;
let currentTopic: string | null = null;
/**
* MQTT
*/
const connectAndSubscribe = (topic: string, handler: (payload: string, topic: string) => void) => {
if (client.value && isConnected.value) {
console.warn('MQTT 已连接,直接订阅');
subscribeTopic(topic, handler);
return;
}
if (isConnecting.value) {
console.warn('MQTT 正在连接中');
return;
}
currentTopic = topic;
messageHandler = handler;
isConnecting.value = true;
error.value = null;
try {
// 生成唯一客户端ID
const clientId = options.clientId || `page-${Date.now()}-${Math.random().toString(16).substring(2, 8)}`;
// 连接配置
const connectOptions = {
clientId,
username: options.username,
password: options.password,
keepalive: options.keepalive || 60,
clean: true,
reconnectPeriod: options.reconnectPeriod || 5000,
connectTimeout: 30 * 1000,
};
console.log('开始连接 MQTT...', options.brokerUrl);
// 创建客户端
client.value = mqtt.connect(options.brokerUrl, connectOptions);
// 连接成功
client.value.on('connect', () => {
console.log('MQTT 连接成功');
isConnected.value = true;
isConnecting.value = false;
// 订阅主题
if (currentTopic && messageHandler) {
subscribeTopic(currentTopic, messageHandler);
}
});
// 连接错误
client.value.on('error', (err) => {
console.error('MQTT 连接错误:', err);
error.value = err.message;
isConnected.value = false;
isConnecting.value = false;
});
// 连接关闭
client.value.on('close', () => {
console.log('MQTT 连接关闭');
isConnected.value = false;
});
// 收到消息
client.value.on('message', (topic, message) => {
const payload = message.toString();
console.log('收到消息:', topic, payload);
// 调用消息处理器
if (messageHandler) {
messageHandler(payload, topic);
}
});
// 重连
client.value.on('reconnect', () => {
console.log('MQTT 正在重连...');
});
// 离线
client.value.on('offline', () => {
console.log('MQTT 离线');
isConnected.value = false;
});
} catch (err: any) {
console.error('创建 MQTT 客户端失败:', err);
error.value = err.message;
isConnecting.value = false;
}
};
/**
*
*/
const subscribeTopic = (topic: string, handler: (payload: string, topic: string) => void) => {
if (!client.value || !isConnected.value) {
console.warn('MQTT 未连接,无法订阅');
return;
}
messageHandler = handler;
currentTopic = topic;
client.value.subscribe(topic, { qos: 1 }, (err) => {
if (err) {
console.error(`订阅主题失败: ${topic}`, err);
error.value = `订阅失败: ${err.message}`;
} else {
console.log(`订阅主题成功: ${topic}`);
}
});
};
/**
*
*/
const publish = (topic: string, payload: string | object, qos: number = 1): Promise<void> => {
return new Promise((resolve, reject) => {
if (!client.value || !isConnected.value) {
reject(new Error('MQTT 未连接'));
return;
}
const message = typeof payload === 'object' ? JSON.stringify(payload) : payload;
client.value.publish(topic, message, { qos }, (err) => {
if (err) {
console.error(`发布消息失败: ${topic}`, err);
reject(err);
} else {
console.log(`发布消息成功: ${topic}`);
resolve();
}
});
});
};
/**
*
*/
const disconnect = () => {
if (client.value) {
// 取消订阅
if (currentTopic) {
client.value.unsubscribe(currentTopic, (err) => {
if (err) {
console.error(`取消订阅失败: ${currentTopic}`, err);
} else {
console.log(`取消订阅成功: ${currentTopic}`);
}
});
}
// 断开连接
client.value.end(true, () => {
console.log('MQTT 连接已关闭');
});
client.value = null;
isConnected.value = false;
messageHandler = null;
currentTopic = null;
}
};
return {
// 状态
isConnected,
isConnecting,
error,
// 方法
connectAndSubscribe,
publish,
disconnect,
};
}

34
src/config/mqtt.ts Normal file
View File

@ -0,0 +1,34 @@
export const mqttConfig = {
// MQTT 服务器地址
brokerUrl: 'ws://mqtt.ilhzn.cn:10007', // WebSocket 连接
// brokerUrl: 'wss://mqtt.example.com:8084/mqtt', // 使用 SSL
// 连接选项
options: {
clientId: `vue3-mqtt-${Math.random().toString(16).substring(2, 8)}`,
username: 'lhzn.mqtt',
password: 'lhzn.2025',
keepalive: 60,
clean: true,
reconnectPeriod: 5000, // 自动重连间隔(ms)
connectTimeout: 30 * 1000,
// SSL 配置(如果使用 wss
// rejectUnauthorized: false,
// 遗嘱消息
will: {
topic: 'app/status',
payload: 'offline',
qos: 1,
retain: true,
},
},
// 订阅主题
topics: [
{ topic: 'app/receive', qos: 1 },
{ topic: 'app/status', qos: 0 },
{ topic: 'lanhai/device/+/command', qos: 1 },
],
};

View File

@ -4,45 +4,44 @@
<div class="jeecg-basic-table-form-container"> <div class="jeecg-basic-table-form-container">
<a-form ref="formRef" @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol"> <a-form ref="formRef" @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :xs="24" :sm="8" :md="6" :lg="8" :xl="6" :xxl="6" style="margin-left: -10px; margin-top: 16px">
<a-col :xs="24" :sm="8" :md="6" :lg="8" :xl="6" :xxl="6" style="margin-left:-10px;margin-top: 16px;"> <a-form-item label="选择监测站" name="stationCode">
<a-form-item label="选择监测站" name="stationCode"> <ApiSelect
<ApiSelect :api="getStationList"
:api="getStationList" showSearch
showSearch v-model:value="queryParam.stationCode"
v-model:value="queryParam.stationCode" optionFilterProp="label"
optionFilterProp="label" resultField="list"
resultField="list" labelField="stationName"
labelField="stationName" valueField="stationCode"
valueField="stationCode" placeholder="请选择"
placeholder="请选择" />
/>
</a-form-item>
</a-col>
<a-col :xs="24" :sm="8" :md="6" :lg="8" :xl="6" :xxl="6" style="margin-left:-10px;margin-top: 16px;">
<a-form-item label="选择设备类型" name="deployType">
<ApiSelect
:api="getDeviceCateList"
showSearch
v-model:value="queryParam.deployType"
optionFilterProp="label"
resultField="list"
labelField="cateName"
valueField="deployType"
placeholder="请选择"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :xs="24" :sm="8" :md="6" :lg="8" :xl="6" :xxl="6" style="margin-left:-10px;margin-top: 16px;">
<a-form-item label="设备部署编号" name="deployCode">
<a-input placeholder="请输入部署编号查询" v-model:value="queryParam.deployCode"></a-input>
</a-form-item> </a-form-item>
</a-col> </a-col>
</template>
<a-col :xs="24" :sm="8" :md="6" :lg="8" :xl="6" :xxl="6" style="margin-left: -10px; margin-top: 16px">
<a-form-item label="选择设备类型" name="deployType">
<ApiSelect
:api="getDeviceCateList"
showSearch
v-model:value="queryParam.deployType"
optionFilterProp="label"
resultField="list"
labelField="cateName"
valueField="deployType"
placeholder="请选择"
/>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :xs="24" :sm="8" :md="6" :lg="8" :xl="6" :xxl="6" style="margin-left: -10px; margin-top: 16px">
<a-form-item label="设备部署编号" name="deployCode">
<a-input placeholder="请输入部署编号查询" v-model:value="queryParam.deployCode"></a-input>
</a-form-item>
</a-col>
</template>
<span style="float: left; overflow: hidden" class="table-page-search-submitButtons"> <span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
<a-col :xs="24" :sm="8" :md="6" :lg="8" :xl="6" :xxl="6" style="margin-left:-10px;margin-top: 16px;"> <a-col :xs="24" :sm="8" :md="6" :lg="8" :xl="6" :xxl="6" style="margin-left: -10px; margin-top: 16px">
<a-button type="primary" preIcon="ant-design:search-outlined" @click="searchQuery">查询</a-button> <a-button type="primary" preIcon="ant-design:search-outlined" @click="searchQuery">查询</a-button>
<a-button type="primary" preIcon="ant-design:reload-outlined" @click="searchReset" style="margin-left: 8px">重置</a-button> <a-button type="primary" preIcon="ant-design:reload-outlined" @click="searchReset" style="margin-left: 8px">重置</a-button>
<a @click="toggleSearchStatus = !toggleSearchStatus" style="margin-left: 8px"> <a @click="toggleSearchStatus = !toggleSearchStatus" style="margin-left: 8px">
@ -59,7 +58,7 @@
<!--插槽:table标题--> <!--插槽:table标题-->
<template #tableTitle> <template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button> <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button> <a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<!-- <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button> --> <!-- <j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button> -->
<a-dropdown v-if="selectedRowKeys.length > 0"> <a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay> <template #overlay>
@ -70,27 +69,27 @@
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
</template> </template>
<a-button>批量操作 <a-button
>批量操作
<Icon icon="mdi:chevron-down"></Icon> <Icon icon="mdi:chevron-down"></Icon>
</a-button> </a-button>
</a-dropdown> </a-dropdown>
</template> </template>
<!--操作栏--> <!--操作栏-->
<template #action="{ record }"> <template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/> <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template> </template>
<!--字段回显插槽--> <!--字段回显插槽-->
<template #htmlSlot="{text}"> <template #htmlSlot="{ text }">
<div v-html="text"></div> <div v-html="text"></div>
</template> </template>
<!--省市区字段回显插槽--> <!--省市区字段回显插槽-->
<template #pcaSlot="{text}"> <template #pcaSlot="{ text }">
{{ getAreaTextByCode(text) }} {{ getAreaTextByCode(text) }}
</template> </template>
<template #fileSlot="{text}"> <template #fileSlot="{ text }">
<span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span> <span v-if="!text" style="font-size: 12px; font-style: italic">无文件</span>
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button> <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
</template> </template>
</BasicTable> </BasicTable>
@ -98,10 +97,12 @@
<SurvDeviceDeployModal ref="registerModal" @success="handleSuccess"></SurvDeviceDeployModal> <SurvDeviceDeployModal ref="registerModal" @success="handleSuccess"></SurvDeviceDeployModal>
<ControlModal ref="qiuFaModal" @success="handleSuccess"></ControlModal> <ControlModal ref="qiuFaModal" @success="handleSuccess"></ControlModal>
<!--字典配置抽屉--> <!--字典配置抽屉-->
<ZhiBiaoList @register="registerDrawer" /> <ZhiBiaoList @register="registerDrawer" />
<SetupList @register="registerSetupDrawer" /> <SetupList @register="registerSetupDrawer" />
<RelayGroupList @register="registerGroupDrawer" /> <RelayGroupList @register="registerGroupDrawer" />
<SetupRunList @register="registerSetupRunDrawer" />
<RunGroupList @register="registerRunGroupDrawer" />
</div> </div>
</template> </template>
@ -110,19 +111,21 @@
import { BasicTable, useTable, TableAction } from '/@/components/Table'; import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
import { columns } from './SurvDeviceDeploy.data'; import { columns } from './SurvDeviceDeploy.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl,deviceInit,getDeviceCateList,relaySync} from './SurvDeviceDeploy.api'; import { list, deleteOne, batchDelete, getImportUrl, getExportUrl, deviceInit, getDeviceCateList, relaySync } from './SurvDeviceDeploy.api';
import { downloadFile } from '/@/utils/common/renderUtils'; import { downloadFile } from '/@/utils/common/renderUtils';
import SurvDeviceDeployModal from './components/SurvDeviceDeployModal.vue' import SurvDeviceDeployModal from './components/SurvDeviceDeployModal.vue';
import ControlModal from './components/ControlModal.vue' import ControlModal from './components/ControlModal.vue';
import { ApiSelect} from '/@/components/Form/index'; import { ApiSelect } from '/@/components/Form/index';
import { getStationList } from '../station/SurvStationInfo.api' import { getStationList } from '../station/SurvStationInfo.api';
import { useDrawer } from '/@/components/Drawer'; import { useDrawer } from '/@/components/Drawer';
import ZhiBiaoList from './components/ZhiBiaoList.vue'; import ZhiBiaoList from './components/ZhiBiaoList.vue';
import SetupList from './components/SetupList.vue'; import SetupList from './components/SetupList.vue';
import RelayGroupList from './components/RelayGroupList.vue'; import RelayGroupList from './components/RelayGroupList.vue';
import SetupRunList from './components/SetupRunList.vue';
import RunGroupList from './components/RunGroupList.vue';
//drawer //drawer
const [registerDrawer, { openDrawer }] = useDrawer(); const [registerDrawer, { openDrawer }] = useDrawer();
const formRef = ref(); const formRef = ref();
const queryParam = reactive<any>({}); const queryParam = reactive<any>({});
const toggleSearchStatus = ref<boolean>(false); const toggleSearchStatus = ref<boolean>(false);
@ -131,6 +134,8 @@
const open = ref<boolean>(false); const open = ref<boolean>(false);
const [registerSetupDrawer, { openDrawer: openDrawerSetup }] = useDrawer(); const [registerSetupDrawer, { openDrawer: openDrawerSetup }] = useDrawer();
const [registerGroupDrawer, { openDrawer: openDrawerGroup }] = useDrawer(); const [registerGroupDrawer, { openDrawer: openDrawerGroup }] = useDrawer();
const [registerSetupRunDrawer, { openDrawer: openDrawerRunSetup }] = useDrawer();
const [registerRunGroupDrawer, { openDrawer: openDrawerRunGroup }] = useDrawer();
//table //table
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({ const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
@ -138,7 +143,7 @@
title: 'surv_device_deploy', title: 'surv_device_deploy',
api: list, api: list,
columns, columns,
canResize:false, canResize: false,
useSearchForm: false, useSearchForm: false,
actionColumn: { actionColumn: {
width: 260, width: 260,
@ -149,16 +154,17 @@
}, },
}, },
exportConfig: { exportConfig: {
name: "surv_device_deploy", name: 'surv_device_deploy',
url: getExportUrl, url: getExportUrl,
params: queryParam, params: queryParam,
}, },
importConfig: { importConfig: {
url: getImportUrl, url: getImportUrl,
success: handleSuccess success: handleSuccess,
}, },
}); });
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext; const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] =
tableContext;
const labelCol = reactive({ const labelCol = reactive({
xs: { span: 24 }, xs: { span: 24 },
sm: { span: 7 }, sm: { span: 7 },
@ -213,27 +219,27 @@
(selectedRowKeys.value = []) && reload(); (selectedRowKeys.value = []) && reload();
} }
/** /**
* 协议配置 * 协议配置
*/ */
function handleItem(record) { function handleItem(record) {
openDrawer(true, { openDrawer(true, {
id: record.id, id: record.id,
}); });
} }
/**
/**
* 球阀控制 * 球阀控制
*/ */
function handleControl(record) { function handleControl(record) {
qiuFaModal.value.disableSubmit = false; qiuFaModal.value.disableSubmit = false;
qiuFaModal.value.showup({ qiuFaModal.value.showup({
deployId: record.id, deployId: record.id,
receiveTopic: record.deviceReverseIotUrl,
sendTopic: record.deviceIotUrl,
}); });
} }
/** /**
* 初始化设备 * 初始化设备
*/ */
@ -241,46 +247,44 @@
await deviceInit({ ids: record.id }, handleSuccess); await deviceInit({ ids: record.id }, handleSuccess);
} }
/** /**
* 操作栏 * 操作栏
*/ */
function getTableAction(record) { function getTableAction(record) {
if (record.deployType == 'water_orient' || record.deployType == 'water_live') {
if(record.deployType =="water_orient" || record.deployType =="water_live"){
return [ return [
{ {
label: '编辑', label: '编辑',
onClick: handleEdit.bind(null, record), onClick: handleEdit.bind(null, record),
},{ },
label: '指标配置', {
onClick: handleItem.bind(null, record), label: '指标配置',
},{ onClick: handleItem.bind(null, record),
label: '球阀控制', },
onClick: handleControl.bind(null, record), {
}, label: '球阀控制',
]; onClick: handleControl.bind(null, record),
}else{ },
];
} else {
return [ return [
{ {
label: '编辑', label: '编辑',
onClick: handleEdit.bind(null, record), onClick: handleEdit.bind(null, record),
},{ },
label: '指标配置', {
onClick: handleItem.bind(null, record), label: '指标配置',
} onClick: handleItem.bind(null, record),
]; },
];
} }
} }
/** /**
* 下拉操作栏 * 下拉操作栏
*/ */
function getDropDownAction(record) { function getDropDownAction(record) {
if (record.deployType == 'air' || record.deployType == 'soil' || record.deployType == '6_water') {
if(record.deployType == 'air'||record.deployType == 'soil'||record.deployType == '6_water'){
return [ return [
{ {
label: '初始化设备', label: '初始化设备',
@ -289,17 +293,17 @@
{ {
label: '详情', label: '详情',
onClick: handleDetail.bind(null, record), onClick: handleDetail.bind(null, record),
}, { },
{
label: '删除', label: '删除',
popConfirm: { popConfirm: {
title: '是否确认删除', title: '是否确认删除',
placement: "leftBottom", placement: 'leftBottom',
confirm: handleDelete.bind(null, record), confirm: handleDelete.bind(null, record),
} },
} },
] ];
} } else if (record.deployType == 'control_cab') {
else if(record.deployType == 'control_cab' ){
return [ return [
{ {
label: '继电器同步', label: '继电器同步',
@ -312,17 +316,17 @@
{ {
label: '详情', label: '详情',
onClick: handleDetail.bind(null, record), onClick: handleDetail.bind(null, record),
}, { },
{
label: '删除', label: '删除',
popConfirm: { popConfirm: {
title: '是否确认删除', title: '是否确认删除',
placement: "leftBottom", placement: 'leftBottom',
confirm: handleDelete.bind(null, record), confirm: handleDelete.bind(null, record),
} },
} },
] ];
} } else if (record.deployType == 'water_orient' || record.deployType == 'water_live') {
else if(record.deployType =="water_orient" || record.deployType =="water_live"){
return [ return [
{ {
label: '控制配置', label: '控制配置',
@ -333,37 +337,45 @@
// onClick: handleRelay.bind(null, record), // onClick: handleRelay.bind(null, record),
// }, // },
{ {
label: '继电器分组', label: '控制分组',
onClick: handleRelayGroup.bind(null, record), onClick: handleRelayGroup.bind(null, record),
}, },
{
label: '运行状态配置',
onClick: handleRunDetail.bind(null, record),
},
{
label: '运行状态分组',
onClick: handleRunGroup.bind(null, record),
},
{ {
label: '详情', label: '详情',
onClick: handleDetail.bind(null, record), onClick: handleDetail.bind(null, record),
}, { },
{
label: '删除', label: '删除',
popConfirm: { popConfirm: {
title: '是否确认删除', title: '是否确认删除',
placement: "leftBottom", placement: 'leftBottom',
confirm: handleDelete.bind(null, record), confirm: handleDelete.bind(null, record),
} },
} },
] ];
} } else {
else{
return [ return [
{ {
label: '详情', label: '详情',
onClick: handleDetail.bind(null, record), onClick: handleDetail.bind(null, record),
}, { },
{
label: '删除', label: '删除',
popConfirm: { popConfirm: {
title: '是否确认删除', title: '是否确认删除',
confirm: handleDelete.bind(null, record), confirm: handleDelete.bind(null, record),
} },
} },
] ];
} }
} }
/** /**
@ -384,6 +396,24 @@
}); });
} }
/**
* 继电器明细
*/
async function handleRunDetail(record: Recordable) {
openDrawerRunSetup(true, {
id: record.id,
});
}
/**
* 继电器分组
*/
async function handleRunGroup(record: Recordable) {
openDrawerRunGroup(true, {
id: record.id,
});
}
/** /**
* 继电器同步 * 继电器同步
*/ */
@ -407,10 +437,6 @@
// //
reload(); reload();
} }
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -420,14 +446,14 @@
margin-bottom: 24px; margin-bottom: 24px;
white-space: nowrap; white-space: nowrap;
} }
.query-group-cust{ .query-group-cust {
width: calc(50% - 15px); width: calc(50% - 15px);
min-width: 100px !important; min-width: 100px !important;
} }
.query-group-split-cust{ .query-group-split-cust {
width: 30px; width: 30px;
display: inline-block; display: inline-block;
text-align: center text-align: center;
} }
} }
</style> </style>

View File

@ -7,8 +7,10 @@
:labelStyle="{ fontWeight: 'bold', textAlign: 'center' }" :labelStyle="{ fontWeight: 'bold', textAlign: 'center' }"
:contentStyle="{ textAlign: 'center' }" :contentStyle="{ textAlign: 'center' }"
> >
<a-descriptions-item :label="item.moduleName" v-for="item in formState.runList" :key="item.id"> <a-descriptions-item :label="item.runName" v-for="item in formState.runList" :key="item.id">
<span style="color: red">停止</span> <span style="color: red" v-if="item.value == '0'">停止</span>
<span style="color: green" v-else-if="item.value == '1'">运行</span>
<span style="color: orange" v-else>待同步</span>
</a-descriptions-item> </a-descriptions-item>
</a-descriptions> </a-descriptions>
@ -23,11 +25,13 @@
<a-col :span="8" v-for="group in formState.moduleList" :key="group.id" class="card-col"> <a-col :span="8" v-for="group in formState.moduleList" :key="group.id" class="card-col">
<a-card :title="group.groupName" :bordered="true"> <a-card :title="group.groupName" :bordered="true">
<!-- <template #extra><a href="#">一键启动</a></template>--> <!-- <template #extra><a href="#">一键启动</a></template>-->
<a-form-item :label="item.moduleName" v-for="item in group.modules" :key="item.id"> <a-form-item :label="item.relayName" v-for="item in group.relayList" :key="item.id">
<a-switch <a-switch
v-model:checked="formState[item.id]" v-model:checked="item.value"
checked-children="打开" checked-children="打开"
un-checked-children="关闭" un-checked-children="关闭"
:checked-value="'1'"
:un-checked-value="'0'"
@change="(checked) => switchChange(checked, item)" @change="(checked) => switchChange(checked, item)"
/> />
</a-form-item> </a-form-item>
@ -42,10 +46,138 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref, reactive, nextTick, defineExpose } from 'vue'; import { onMounted, onUnmounted, ref, reactive, nextTick, defineExpose } from 'vue';
import type { UnwrapRef } from 'vue'; import type { UnwrapRef } from 'vue';
import { getValveStatus, sendDeviceCmd, getRelayList } from '../SurvDeviceDeploy.api'; import { getValveStatus, sendDeviceCmd, getRelayList } from '../SurvDeviceDeploy.api';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { usePageMqtt } from '/@/components/mqtt/usePageMqtt';
// MQTT
const mqttOptions = {
brokerUrl: import.meta.env.VITE_MQTT_BROKER_URL || 'ws://localhost:8083/mqtt',
username: import.meta.env.VITE_MQTT_USERNAME || 'admin',
password: import.meta.env.VITE_MQTT_PASSWORD || 'admin123',
keepalive: 60,
reconnectPeriod: 5000,
};
// 使 MQTT
const { isConnected, isConnecting, error, connectAndSubscribe, publish, disconnect } = usePageMqtt(mqttOptions);
//
const deviceData = reactive({
temperature: 0,
humidity: 0,
status: 'offline',
lastUpdate: 0,
});
//
interface LogEntry {
id: number;
topic: string;
message: string;
timestamp: number;
}
const logs = ref<LogEntry[]>([]);
let logId = 0;
//
const formatTime = (timestamp: number) => {
if (!timestamp) return '--:--:--';
return new Date(timestamp).toLocaleTimeString();
};
//
const addLog = (topic: string, message: string) => {
logs.value.unshift({
id: logId++,
topic,
message: typeof message === 'object' ? JSON.stringify(message) : message,
timestamp: Date.now(),
});
// 100
if (logs.value.length > 100) {
logs.value.pop();
}
};
//
const handleMessage = (payload: string, topic: string) => {
// console.log(':', topic, payload);
try {
const data = JSON.parse(payload);
//
addLog(topic, payload);
if (data.rw_prot !== undefined) {
if (data.rw_prot.r_data !== undefined) {
// Map
const sourceMap = data.rw_prot.r_data.reduce((map, item) => {
map[item.name] = item.value;
return map;
}, {});
//
// 1.
formState.runList.forEach((target) => {
const source = sourceMap[target.runKey];
if (source) {
Object.assign(target, {
value: source,
});
}
});
// 2.
formState.moduleList.forEach((target) => {
if (target.relayList) {
//
target.relayList.forEach((member) => {
const source = sourceMap[member.relayKey];
if (source) {
Object.assign(member, {
value: source,
});
}
});
}
});
}
console.log('check-----', formState.moduleList);
deviceData.temperature = data.temperature;
}
} catch (e) {
// JSON
addLog(topic, payload);
}
};
//
const sendCommand = async (topic: string, command: string) => {
if (!isConnected.value) {
addLog('system', 'MQTT未连接无法发送指令');
return;
}
// const payload = {
// command,
// timestamp: Date.now(),
// requestId: `${Date.now()}-${Math.random().toString(36).substring(2, 10)}`,
// };
const payload = command;
console.log('sendCommand===', topic, command);
try {
await publish(topic, payload);
addLog(topic, `发送指令: ${command}`);
} catch (err) {
console.error('发送指令失败:', err);
addLog(topic, `指令发送失败: ${command}`);
}
};
const bodystyle = { const bodystyle = {
height: '1000px', height: '1000px',
@ -60,6 +192,7 @@
dataTime: string; dataTime: string;
moduleList: []; moduleList: [];
runList: []; runList: [];
deployInfo: {};
} }
const formState: UnwrapRef<FormState> = reactive({ const formState: UnwrapRef<FormState> = reactive({
deployCode: '', deployCode: '',
@ -68,6 +201,7 @@
dataTime: '', dataTime: '',
moduleList: [], moduleList: [],
runList: [], runList: [],
deployInfo: {},
}); });
const labelCol = { style: { width: '150px' } }; const labelCol = { style: { width: '150px' } };
@ -84,22 +218,36 @@
message.loading({ content: '发送指令中', key, duration: 7 }); message.loading({ content: '发送指令中', key, duration: 7 });
}; };
// MQTT
onMounted(() => {});
//
onUnmounted(() => {
console.log('页面卸载,断开 MQTT...');
disconnect();
});
function switchChange(checked, relay) { function switchChange(checked, relay) {
openMessage(); openMessage();
let ops = ''; let ops = checked;
if (checked === true) { // if (checked === true) {
ops = '1'; // ops = '1';
} else if (checked === false) { // } else if (checked === false) {
ops = '0'; // ops = '0';
// }
// let parmas = { relayId: relay.id, ops: ops };
// sendDeviceCmd(parmas).then((res) => {
// if (res.code == 500) {
// //
// formState[relay.id] = false;
// message.error({ content: res.message, key, duration: 2 });
// }
// });
if ('1' == ops) {
sendCommand(formState.deployInfo.sendTopic, relay.registerCmdOn);
} else if ('0' == ops) {
sendCommand(formState.deployInfo.sendTopic, relay.registerCmdOff);
} }
let parmas = { relayId: relay.id, ops: ops };
sendDeviceCmd(parmas).then((res) => {
if (res.code == 500) {
//
formState[relay.id] = false;
message.error({ content: res.message, key, duration: 2 });
}
});
} }
function on1SwitchChange(checked) { function on1SwitchChange(checked) {
@ -151,6 +299,15 @@
formState.moduleList = []; formState.moduleList = [];
formState.runList = []; formState.runList = [];
formState.dataTime = ''; formState.dataTime = '';
formState.deployInfo = record;
console.log(record);
console.log('页面加载,连接 MQTT...', formState.deployInfo.receiveTopic);
//
const subscribeTopic = formState.deployInfo.receiveTopic; //
//
connectAndSubscribe(subscribeTopic, handleMessage);
getRelayList({ deployId: record.deployId }).then((res) => { getRelayList({ deployId: record.deployId }).then((res) => {
if (res.code == 200) { if (res.code == 200) {
formState.moduleList = res.result.moduleList; formState.moduleList = res.result.moduleList;
@ -211,6 +368,7 @@
*/ */
function handleCancel() { function handleCancel() {
visible.value = false; visible.value = false;
disconnect();
} }
defineExpose({ defineExpose({

View File

@ -228,7 +228,7 @@ export const dictItemColumns: BasicColumn[] = [
export const dictItemSearchFormSchema: FormSchema[] = [ export const dictItemSearchFormSchema: FormSchema[] = [
{ {
label: '名称', label: '名称',
field: 'relayName', field: 'groupName',
component: 'Input', component: 'Input',
}, },
]; ];

View File

@ -1,5 +1,5 @@
<template> <template>
<BasicDrawer v-bind="$attrs" @register="registerDrawer" title="指标列表" width="800px"> <BasicDrawer v-bind="$attrs" @register="registerDrawer" title="分组列表" width="800px">
<BasicTable @register="registerTable" :rowClassName="getRowClassName"> <BasicTable @register="registerTable" :rowClassName="getRowClassName">
<template #tableTitle> <template #tableTitle>
<a-button type="primary" @click="handleCreate"> 新增</a-button> <a-button type="primary" @click="handleCreate"> 新增</a-button>

View File

@ -0,0 +1,121 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from "/@/hooks/web/useMessage";
const { createConfirm } = useMessage();
enum Api {
list = '/appmana/survDeviceDeployRungroup/list',
save='/appmana/survDeviceDeployRungroup/add',
edit='/appmana/survDeviceDeployRungroup/edit',
deleteOne = '/appmana/survDeviceDeployRungroup/delete',
deleteBatch = '/appmana/survDeviceDeployRungroup/deleteBatch',
importExcel = '/appmana/survDeviceDeployRungroup/importExcel',
exportXls = '/appmana/survDeviceDeployRungroup/exportXls',
itemList = '/appmana/survDeviceDeployRungroup/list',
deleteItem = '/appmana/survDeviceDeployRungroup/delete',
itemSave = '/appmana/survDeviceDeployRungroup/add',
itemEdit = '/appmana/survDeviceDeployRungroup/edit',
itemCheck ='/appmana/survDeviceDeployRungroup/itemCheck',
}
/**
* api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* api
*/
export const getImportUrl = Api.importExcel;
/**
*
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
*
* @param params
* @param handleSuccess
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
*
* @param params
* @param handleSuccess
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
*
* @param params
* @param isUpdate
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params }, { isTransformResponse: false });
}
/**
*
* @param params
*/
export const saveOrUpdateDictItem = (params, isUpdate) => {
let url = isUpdate ? Api.itemEdit : Api.itemSave;
return defHttp.post({ url: url, params });
};
/**
*
* @param params
*/
export const saveOrUpdateDict = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
/**
*
* @param params
*/
export const itemCheck = (params) => defHttp.get({ url: Api.itemCheck, params }, { isTransformResponse: false });
/**
*
* @param params
*/
export const itemList = (params) => defHttp.get({ url: Api.itemList, params });
/**
*
* @param params
*/
export const deleteItem = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteItem, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};

View File

@ -0,0 +1,234 @@
import {BasicColumn} from '/@/components/Table';
import {FormSchema} from '/@/components/Table';
import { rules} from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
import { itemCheck} from './RunGroup.api';
//列表数据
export const columns: BasicColumn[] = [
{
title: '创建者',
align: "center",
dataIndex: 'createId'
},
{
title: '更新者',
align: "center",
dataIndex: 'updateId'
},
{
title: '设备主键',
align: "center",
dataIndex: 'equId'
},
{
title: '指标code',
align: "center",
dataIndex: 'code'
},
{
title: '指标名称',
align: "center",
dataIndex: 'name'
},
{
title: '低阈值',
align: "center",
dataIndex: 'valLow'
},
{
title: '高阈值',
align: "center",
dataIndex: 'valHeight'
},
{
title: '计量单位',
align: "center",
dataIndex: 'nuit'
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '创建者',
field: 'createId',
component: 'Input',
},
{
label: '更新者',
field: 'updateId',
component: 'Input',
},
{
label: '设备主键',
field: 'equId',
component: 'Input',
},
{
label: '指标code',
field: 'code',
component: 'Input',
},
{
label: '指标名称',
field: 'name',
component: 'Input',
},
{
label: '低阈值',
field: 'valLow',
component: 'InputNumber',
},
{
label: '高阈值',
field: 'valHeight',
component: 'InputNumber',
},
{
label: '计量单位',
field: 'nuit',
component: 'Input',
},
{
label: '排序',
field: 'sortNo',
component: 'InputNumber',
},
// TODO 主键隐藏字段目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];
export const itemFormSchema: FormSchema[] = [
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
{
label: '',
field: 'deployId',
component: 'Input',
show: false,
},
{
label: '分组名称',
field: 'groupName',
component: 'Input',
dynamicRules: ({ values, model }) => {
return [
{
required: true,
validator: (_, value) => {
if (!value) {
return Promise.reject('请输入属性编码');
}
if (new RegExp("[`~!@#$^&*()=|{}'.<>《》/?!¥()—【】‘;:”“。,、?]").test(value)) {
return Promise.reject('数据值不能包含特殊字符!');
}
return new Promise<void>((resolve, reject) => {
console.log('wwwwwww',model)
let params = {
deployId: model.deployId,
groupName: value,
id:model.id
};
itemCheck(params)
.then((res) => {
res.success ? resolve() : reject(res.message || '校验失败');
})
.catch((err) => {
reject(err.message || '验证失败');
});
});
},
},
];
},
},
{
label: '分组种类',
field: 'groupType',
required: true,
component: 'JDictSelectTag',
componentProps: {
dictCode: 'iot_run_group_type',
placeholder: '请选择分组种类',
stringToNumber: false,
},
},
{
field: 'isEnable',
label: '是否启用',
defaultValue: 1,
component: 'JDictSelectTag',
componentProps: {
type: 'radioButton',
dictCode: 'yn',
stringToNumber: true,
},
},
{
label: '备注',
field: 'groupNotes',
required: false,
component: 'Input',
},
{
label: '排序',
field: 'sortNo',
required: false,
component: 'InputNumber',
defaultValue: 1
},
];
export const dictItemColumns: BasicColumn[] = [
{
title: '分组名称',
dataIndex: 'groupName',
width: 80,
},
{
title: '分组类别',
dataIndex: 'groupTypeName',
width: 80,
},
{
title: '是否启用',
dataIndex: 'isEnable',
width: 80,
customRender: ({ text }) => {
const color = text == '1' ? 'green' : text == '0' ? 'red' : 'gray';
return render.renderTag(render.renderDict(text, 'dict_item_status'), color);
},
},
{
title: '排序',
dataIndex: 'sortNo',
width: 80,
},
];
export const dictItemSearchFormSchema: FormSchema[] = [
{
label: '名称',
field: 'groupName',
component: 'Input',
},
];

View File

@ -0,0 +1,132 @@
<template>
<BasicDrawer v-bind="$attrs" @register="registerDrawer" title="状态分组列表" width="800px">
<BasicTable @register="registerTable" :rowClassName="getRowClassName">
<template #tableTitle>
<a-button type="primary" @click="handleCreate"> 新增</a-button>
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
</BasicDrawer>
<RelayModal @register="registerModal" @success="reload" :deployId="deployId" />
</template>
<script lang="ts" setup>
import { ref, unref } from 'vue';
import { BasicDrawer, useDrawerInner } from '/src/components/Drawer';
import { BasicTable, useTable, TableAction } from '/src/components/Table';
import { useModal } from '/src/components/Modal';
import { useDesign } from '/@/hooks/web/useDesign';
import RelayModal from './RunGroupModal.vue';
import { dictItemColumns, dictItemSearchFormSchema } from './RunGroup.data';
import { itemList, deleteItem } from './RunGroup.api';
import { ColEx } from '/@/components/Form/src/types';
const { prefixCls } = useDesign('row-invalid');
const deployId = ref('');
//model
const [registerModal, { openModal }] = useModal();
const [registerDrawer] = useDrawerInner(async (data) => {
deployId.value = data.id;
setProps({ searchInfo: { deployId: unref(deployId) } });
reload();
});
//
const adaptiveColProps: Partial<ColEx> = {
xs: 24, // <576px
sm: 24, // 576px
md: 24, // 768px
lg: 12, // 992px
xl: 12, // 1200px
xxl: 8, // 1600px
};
const [registerTable, { reload, setProps }] = useTable({
api: itemList,
columns: dictItemColumns,
formConfig: {
baseColProps: adaptiveColProps,
labelAlign: 'right',
labelCol: {
offset: 1,
xs: 24,
sm: 24,
md: 24,
lg: 9,
xl: 7,
xxl: 4,
},
wrapperCol: {},
schemas: dictItemSearchFormSchema,
autoSubmitOnEnter: true,
},
striped: true,
useSearchForm: true,
bordered: true,
showIndexColumn: false,
canResize: false,
immediate: false,
actionColumn: {
width: 100,
title: '操作',
dataIndex: 'action',
slots: { customRender: 'action' },
fixed: undefined,
},
});
/**
* 新增
*/
function handleCreate() {
openModal(true, {
isUpdate: false,
});
}
/**
* 编辑
*/
function handleEdit(record) {
openModal(true, {
record,
isUpdate: true,
});
}
/**
* 删除
*/
async function handleDelete(record) {
await deleteItem({ id: record.id }, reload);
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
},
];
}
function getRowClassName(record) {
return record.status == 0 ? prefixCls : '';
}
</script>
<style scoped lang="less">
@prefix-cls: ~'@{namespace}-row-invalid';
:deep(.@{prefix-cls}) {
background: #f4f4f4;
color: #bababa;
}
</style>

View File

@ -0,0 +1,64 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit" width="800px">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import { defineProps, ref, computed, unref, reactive } from 'vue';
import { BasicModal, useModalInner } from '/src/components/Modal';
import { BasicForm, useForm } from '/src/components/Form';
import { itemFormSchema } from './RunGroup.data';
import { saveOrUpdateDictItem } from './RunGroup.api';
import { stringify } from 'querystring';
// Emits
const emit = defineEmits(['success', 'register']);
const props = defineProps({ deployId: String });
const isUpdate = ref(true);
//
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
schemas: itemFormSchema,
showActionButtonGroup: false,
mergeDynamicData: props,
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 18 },
},
});
//
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//
await resetFields();
setModalProps({ confirmLoading: false });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//
await setFieldsValue({
...data.record,
});
}
});
//
const getTitle = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//
async function handleSubmit() {
try {
const values = await validate();
values.deployId = props.deployId;
setModalProps({ confirmLoading: true });
//
await saveOrUpdateDictItem(values, isUpdate.value);
//
closeModal();
//
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>

View File

@ -0,0 +1,127 @@
import { defHttp } from '/@/utils/http/axios';
import { useMessage } from "/@/hooks/web/useMessage";
const { createConfirm } = useMessage();
enum Api {
list = '/appmana/survDeviceDeployRun/list',
save='/appmana/survDeviceDeployRun/add',
edit='/appmana/survDeviceDeployRun/edit',
deleteOne = '/appmana/survDeviceDeployRun/delete',
deleteBatch = '/appmana/survDeviceDeployRun/deleteBatch',
importExcel = '/appmana/survDeviceDeployRun/importExcel',
exportXls = '/appmana/survDeviceDeployRun/exportXls',
itemList = '/appmana/survDeviceDeployRun/list',
deleteItem = '/appmana/survDeviceDeployRun/delete',
itemSave = '/appmana/survDeviceDeployRun/add',
itemEdit = '/appmana/survDeviceDeployRun/edit',
relayItemCheck ='/appmana/survDeviceDeployRun/relayItemCheck',
allRelay = '/appmana/survDeviceDeployRun/getRelayType'
}
/**
* api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* api
*/
export const getImportUrl = Api.importExcel;
/**
*
* @param params
*/
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
*
* @param params
* @param handleSuccess
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
*
* @param params
* @param handleSuccess
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
*
* @param params
* @param isUpdate
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params }, { isTransformResponse: false });
}
/**
*
* @param params
*/
export const saveOrUpdateDictItem = (params, isUpdate) => {
let url = isUpdate ? Api.itemEdit : Api.itemSave;
return defHttp.post({ url: url, params });
};
/**
*
* @param params
*/
export const saveOrUpdateDict = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({ url: url, params });
};
/**
*
* @param params
*/
export const relayItemCheck = (params) => defHttp.get({ url: Api.relayItemCheck, params }, { isTransformResponse: false });
/**
*
* @param params
*/
export const itemList = (params) => defHttp.get({ url: Api.itemList, params });
/**
*
* @param params
*/
export const deleteItem = (params, handleSuccess) => {
return defHttp.delete({ url: Api.deleteItem, params }, { joinParamsToUrl: true }).then(() => {
handleSuccess();
});
};
/**
*
* @param params
*/
export const relayTypeList = (params) => defHttp.get({ url: Api.allRelay, params });

View File

@ -0,0 +1,288 @@
import {BasicColumn} from '/@/components/Table';
import {FormSchema} from '/@/components/Table';
import { rules} from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
import { relayItemCheck,relayTypeList} from './SetupRun.api';
//列表数据
export const columns: BasicColumn[] = [
{
title: '创建者',
align: "center",
dataIndex: 'createId'
},
{
title: '更新者',
align: "center",
dataIndex: 'updateId'
},
{
title: '设备主键',
align: "center",
dataIndex: 'equId'
},
{
title: '指标code',
align: "center",
dataIndex: 'code'
},
{
title: '指标名称',
align: "center",
dataIndex: 'name'
},
{
title: '低阈值',
align: "center",
dataIndex: 'valLow'
},
{
title: '高阈值',
align: "center",
dataIndex: 'valHeight'
},
{
title: '计量单位',
align: "center",
dataIndex: 'nuit'
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '创建者',
field: 'createId',
component: 'Input',
},
{
label: '更新者',
field: 'updateId',
component: 'Input',
},
{
label: '设备主键',
field: 'equId',
component: 'Input',
},
{
label: '指标code',
field: 'code',
component: 'Input',
},
{
label: '指标名称',
field: 'name',
component: 'Input',
},
{
label: '低阈值',
field: 'valLow',
component: 'InputNumber',
},
{
label: '高阈值',
field: 'valHeight',
component: 'InputNumber',
},
{
label: '计量单位',
field: 'nuit',
component: 'Input',
},
{
label: '排序',
field: 'sortNo',
component: 'InputNumber',
},
// TODO 主键隐藏字段目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
];
export const itemFormSchema: FormSchema[] = [
{
label: '',
field: 'id',
component: 'Input',
show: false,
},
{
label: '',
field: 'deployId',
component: 'Input',
show: false,
},
{
label: '状态名称',
field: 'runName',
required: true,
component: 'Input',
},
{
label: '状态编码',
field: 'runKey',
component: 'Input',
dynamicRules: ({ values, model }) => {
return [
{
required: true,
validator: (_, value) => {
if (!value) {
return Promise.reject('请输入属性编码');
}
if (new RegExp("[`~!@#$^&*()=|{}'.<>《》/?!¥()—【】‘;:”“。,、?]").test(value)) {
return Promise.reject('数据值不能包含特殊字符!');
}
return new Promise<void>((resolve, reject) => {
let params = {
deployId: model.deployId,
relayKey: value,
id: model.id,
};
relayItemCheck(params)
.then((res) => {
res.success ? resolve() : reject(res.message || '校验失败');
})
.catch((err) => {
reject(err.message || '验证失败');
});
});
},
},
];
},
},
{
label: '状态种类',
field: 'runType',
required: true,
component: 'JDictSelectTag',
componentProps: {
dictCode: 'iot_run_type',
placeholder: '请选择状态种类',
stringToNumber: true,
},
},
{
label: '状态类别',
field: 'runCate',
required: true,
component: 'JDictSelectTag',
componentProps: {
dictCode: 'iot_run_cate',
placeholder: '请选择状态类别',
stringToNumber: true,
},
},
{
label: '所属分组',
field: 'groupId',
defaultValue: '',
component: 'JDictSelectTag',
componentProps: ({ schema, formModel }) => {
return {
dictCode: 'surv_device_deploy_rungroup,GROUP_NAME,ID,IS_ENABLE = 1 and DEPLOY_ID=' + formModel.deployId,
dict: 'surv_device_deploy_rungroup,GROUP_NAME,ID,IS_ENABLE = 1 and DEPLOY_ID=' + formModel.deployId,
};
},
},
// {
// label: '分组编码',
// field: 'groupCode',
// required: false,
// component: 'Input',
// },
{
label: '开状态值',
field: 'registerOn',
required: false,
component: 'Input',
},
{
label: '关状态值',
field: 'registerOff',
required: false,
component: 'Input',
},
{
label: '停状态值',
field: 'registerStop',
required: false,
component: 'Input',
},
{
field: 'isEnable',
label: '是否启用',
defaultValue: 1,
component: 'JDictSelectTag',
componentProps: {
type: 'radioButton',
dictCode: 'yn',
stringToNumber: true,
},
},
{
label: '排序',
field: 'sortNo',
required: false,
component: 'InputNumber',
defaultValue: 1,
},
];
export const dictItemColumns: BasicColumn[] = [
{
title: '状态编号',
dataIndex: 'runKey',
width: 80,
},
{
title: '状态名称',
dataIndex: 'runName',
width: 80,
},
{
title: '分组名称',
dataIndex: 'groupNameStr',
width: 80,
},
{
title: '状态类别',
dataIndex: 'runCateName',
width: 80,
},
{
title: '状态种类',
dataIndex: 'runTypeName',
width: 80,
},
{
title: '排序',
dataIndex: 'sortNo',
width: 80,
},
];
export const dictItemSearchFormSchema: FormSchema[] = [
{
label: '名称',
field: 'runName',
component: 'Input',
},
];

View File

@ -0,0 +1,132 @@
<template>
<BasicDrawer v-bind="$attrs" @register="registerDrawer" title="运行状态列表" width="800px">
<BasicTable @register="registerTable" :rowClassName="getRowClassName">
<template #tableTitle>
<a-button type="primary" @click="handleCreate"> 新增</a-button>
</template>
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" />
</template>
</BasicTable>
</BasicDrawer>
<SetupModal @register="registerModal" @success="reload" :deployId="deployId" />
</template>
<script lang="ts" setup>
import { ref, unref } from 'vue';
import { BasicDrawer, useDrawerInner } from '/src/components/Drawer';
import { BasicTable, useTable, TableAction } from '/src/components/Table';
import { useModal } from '/src/components/Modal';
import { useDesign } from '/@/hooks/web/useDesign';
import SetupModal from './SetupRunModal.vue';
import { dictItemColumns, dictItemSearchFormSchema } from './SetupRun.data';
import { itemList, deleteItem } from './SetupRun.api';
import { ColEx } from '/@/components/Form/src/types';
const { prefixCls } = useDesign('row-invalid');
const deployId = ref('');
//model
const [registerModal, { openModal }] = useModal();
const [registerDrawer] = useDrawerInner(async (data) => {
deployId.value = data.id;
setProps({ searchInfo: { deployId: unref(deployId) } });
reload();
});
//
const adaptiveColProps: Partial<ColEx> = {
xs: 24, // <576px
sm: 24, // 576px
md: 24, // 768px
lg: 12, // 992px
xl: 12, // 1200px
xxl: 8, // 1600px
};
const [registerTable, { reload, setProps }] = useTable({
api: itemList,
columns: dictItemColumns,
formConfig: {
baseColProps: adaptiveColProps,
labelAlign: 'right',
labelCol: {
offset: 1,
xs: 24,
sm: 24,
md: 24,
lg: 9,
xl: 7,
xxl: 4,
},
wrapperCol: {},
schemas: dictItemSearchFormSchema,
autoSubmitOnEnter: true,
},
striped: true,
useSearchForm: true,
bordered: true,
showIndexColumn: false,
canResize: false,
immediate: false,
actionColumn: {
width: 100,
title: '操作',
dataIndex: 'action',
slots: { customRender: 'action' },
fixed: undefined,
},
});
/**
* 新增
*/
function handleCreate() {
openModal(true, {
isUpdate: false,
});
}
/**
* 编辑
*/
function handleEdit(record) {
openModal(true, {
record,
isUpdate: true,
});
}
/**
* 删除
*/
async function handleDelete(record) {
await deleteItem({ id: record.id }, reload);
}
/**
* 操作栏
*/
function getTableAction(record) {
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
},
{
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
},
];
}
function getRowClassName(record) {
return record.status == 0 ? prefixCls : '';
}
</script>
<style scoped lang="less">
@prefix-cls: ~'@{namespace}-row-invalid';
:deep(.@{prefix-cls}) {
background: #f4f4f4;
color: #bababa;
}
</style>

View File

@ -0,0 +1,64 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit" width="800px">
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import { defineProps, ref, computed, unref, reactive } from 'vue';
import { BasicModal, useModalInner } from '/src/components/Modal';
import { BasicForm, useForm } from '/src/components/Form';
import { itemFormSchema } from './SetupRun.data';
import { saveOrUpdateDictItem } from './SetupRun.api';
import { stringify } from 'querystring';
// Emits
const emit = defineEmits(['success', 'register']);
const props = defineProps({ deployId: String });
const isUpdate = ref(true);
//
const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
schemas: itemFormSchema,
showActionButtonGroup: false,
mergeDynamicData: props,
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 18 },
},
});
//
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//
await resetFields();
setModalProps({ confirmLoading: false });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//
await setFieldsValue({
...data.record,
});
}
});
//
const getTitle = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
//
async function handleSubmit() {
try {
const values = await validate();
values.deployId = props.deployId;
setModalProps({ confirmLoading: true });
//
await saveOrUpdateDictItem(values, isUpdate.value);
//
closeModal();
//
emit('success');
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>