Merge branch 'dev_xq_0.0.1' of http://web.ronyao.com:3000/xq/maotu-webtopo into dev_xq_0.0.1

# Conflicts:
#	src/components/vue-components/vue-characters.vue
#	src/utils/config.ts
dev_xq_0.0.1
刘政 9 hours ago
commit e580b6b6e5

93
package-lock.json generated

@ -26,18 +26,21 @@
"mqtt": "^5.15.0", "mqtt": "^5.15.0",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"sockjs-client": "^1.6.1", "sockjs-client": "^1.6.1",
"sortablejs": "^1.15.7",
"stats.js": "^0.17.0", "stats.js": "^0.17.0",
"three": "^0.182.0", "three": "^0.182.0",
"vue": "^3.3.4", "vue": "^3.3.4",
"vue-echarts": "^6.6.5", "vue-echarts": "^6.6.5",
"vue-router": "^4.2.5", "vue-router": "^4.2.5",
"vue3-ace-editor": "^2.2.4" "vue3-ace-editor": "^2.2.4",
"vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.3.3", "@rushstack/eslint-patch": "^1.3.3",
"@tsconfig/node18": "^18.2.2", "@tsconfig/node18": "^18.2.2",
"@types/jsdom": "^21.1.3", "@types/jsdom": "^21.1.3",
"@types/node": "^18.18.5", "@types/node": "^18.18.5",
"@types/sortablejs": "^1.15.9",
"@types/three": "^0.182.0", "@types/three": "^0.182.0",
"@vitejs/plugin-vue": "^4.4.0", "@vitejs/plugin-vue": "^4.4.0",
"@vue/eslint-config-prettier": "^8.0.0", "@vue/eslint-config-prettier": "^8.0.0",
@ -157,6 +160,7 @@
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.28.6", "@babel/code-frame": "^7.28.6",
"@babel/generator": "^7.28.6", "@babel/generator": "^7.28.6",
@ -1932,8 +1936,7 @@
"resolved": "https://registry.npmmirror.com/@rtsao/scc/-/scc-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/@rtsao/scc/-/scc-1.1.0.tgz",
"integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/@rushstack/eslint-patch": { "node_modules/@rushstack/eslint-patch": {
"version": "1.15.0", "version": "1.15.0",
@ -2138,7 +2141,8 @@
"resolved": "https://registry.npmmirror.com/@types/chai/-/chai-4.3.20.tgz", "resolved": "https://registry.npmmirror.com/@types/chai/-/chai-4.3.20.tgz",
"integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/@types/chai-subset": { "node_modules/@types/chai-subset": {
"version": "1.3.6", "version": "1.3.6",
@ -2181,8 +2185,7 @@
"resolved": "https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz", "resolved": "https://registry.npmmirror.com/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/@types/lodash": { "node_modules/@types/lodash": {
"version": "4.17.23", "version": "4.17.23",
@ -2195,6 +2198,7 @@
"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/lodash": "*" "@types/lodash": "*"
} }
@ -2230,6 +2234,13 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/sortablejs": {
"version": "1.15.9",
"resolved": "https://registry.npmmirror.com/@types/sortablejs/-/sortablejs-1.15.9.tgz",
"integrity": "sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/stats.js": { "node_modules/@types/stats.js": {
"version": "0.17.4", "version": "0.17.4",
"resolved": "https://registry.npmmirror.com/@types/stats.js/-/stats.js-0.17.4.tgz", "resolved": "https://registry.npmmirror.com/@types/stats.js/-/stats.js-0.17.4.tgz",
@ -2341,6 +2352,7 @@
"integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"peer": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/scope-manager": "6.21.0",
"@typescript-eslint/types": "6.21.0", "@typescript-eslint/types": "6.21.0",
@ -3196,6 +3208,7 @@
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.26.tgz", "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.26.tgz",
"integrity": "sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==", "integrity": "sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/reactivity": "3.5.26", "@vue/reactivity": "3.5.26",
"@vue/shared": "3.5.26" "@vue/shared": "3.5.26"
@ -3379,7 +3392,8 @@
"version": "1.43.5", "version": "1.43.5",
"resolved": "https://registry.npmmirror.com/ace-builds/-/ace-builds-1.43.5.tgz", "resolved": "https://registry.npmmirror.com/ace-builds/-/ace-builds-1.43.5.tgz",
"integrity": "sha512-iH5FLBKdB7SVn9GR37UgA/tpQS8OTWIxWAuq3Ofaw+Qbc69FfPXsXd9jeW7KRG2xKpKMqBDnu0tHBrCWY5QI7A==", "integrity": "sha512-iH5FLBKdB7SVn9GR37UgA/tpQS8OTWIxWAuq3Ofaw+Qbc69FfPXsXd9jeW7KRG2xKpKMqBDnu0tHBrCWY5QI7A==",
"license": "BSD-3-Clause" "license": "BSD-3-Clause",
"peer": true
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.15.0", "version": "8.15.0",
@ -3387,6 +3401,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@ -3593,7 +3608,6 @@
"integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"call-bind": "^1.0.8", "call-bind": "^1.0.8",
"call-bound": "^1.0.4", "call-bound": "^1.0.4",
@ -3643,7 +3657,6 @@
"integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"call-bind": "^1.0.8", "call-bind": "^1.0.8",
"call-bound": "^1.0.4", "call-bound": "^1.0.4",
@ -3666,7 +3679,6 @@
"integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"call-bind": "^1.0.8", "call-bind": "^1.0.8",
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
@ -3686,7 +3698,6 @@
"integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"call-bind": "^1.0.8", "call-bind": "^1.0.8",
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
@ -4014,6 +4025,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.9.0", "baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759", "caniuse-lite": "^1.0.30001759",
@ -5126,6 +5138,7 @@
"resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz", "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz",
"integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==", "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"dependencies": { "dependencies": {
"tslib": "2.3.0", "tslib": "2.3.0",
"zrender": "5.6.1" "zrender": "5.6.1"
@ -5361,7 +5374,6 @@
"integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"hasown": "^2.0.2" "hasown": "^2.0.2"
}, },
@ -5455,6 +5467,7 @@
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1", "@eslint-community/regexpp": "^4.6.1",
@ -5511,6 +5524,7 @@
"integrity": "sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==", "integrity": "sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"eslint-config-prettier": "bin/cli.js" "eslint-config-prettier": "bin/cli.js"
}, },
@ -5537,7 +5551,6 @@
"integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"debug": "^3.2.7", "debug": "^3.2.7",
"is-core-module": "^2.13.0", "is-core-module": "^2.13.0",
@ -5550,7 +5563,6 @@
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"ms": "^2.1.1" "ms": "^2.1.1"
} }
@ -5561,7 +5573,6 @@
"integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"debug": "^3.2.7" "debug": "^3.2.7"
}, },
@ -5580,7 +5591,6 @@
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"ms": "^2.1.1" "ms": "^2.1.1"
} }
@ -5591,7 +5601,6 @@
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@rtsao/scc": "^1.1.0", "@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9", "array-includes": "^3.1.9",
@ -5626,7 +5635,6 @@
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -5638,7 +5646,6 @@
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"ms": "^2.1.1" "ms": "^2.1.1"
} }
@ -5649,7 +5656,6 @@
"integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"dependencies": { "dependencies": {
"esutils": "^2.0.2" "esutils": "^2.0.2"
}, },
@ -5663,7 +5669,6 @@
"integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"peer": true,
"dependencies": { "dependencies": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
}, },
@ -5677,7 +5682,6 @@
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"peer": true,
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
@ -5719,6 +5723,7 @@
"integrity": "sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==", "integrity": "sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.4.0", "@eslint-community/eslint-utils": "^4.4.0",
"globals": "^13.24.0", "globals": "^13.24.0",
@ -7715,6 +7720,7 @@
"integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==", "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"abab": "^2.0.6", "abab": "^2.0.6",
"cssstyle": "^3.0.0", "cssstyle": "^3.0.0",
@ -7852,6 +7858,7 @@
"integrity": "sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==", "integrity": "sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"dependencies": { "dependencies": {
"copy-anything": "^2.0.1", "copy-anything": "^2.0.1",
"parse-node-version": "^1.0.1", "parse-node-version": "^1.0.1",
@ -7953,13 +7960,15 @@
"version": "4.17.21", "version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/lodash-es": { "node_modules/lodash-es": {
"version": "4.17.22", "version": "4.17.22",
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.22.tgz", "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.22.tgz",
"integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==", "integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==",
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/lodash-unified": { "node_modules/lodash-unified": {
"version": "1.0.3", "version": "1.0.3",
@ -8797,7 +8806,6 @@
"integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"call-bind": "^1.0.7", "call-bind": "^1.0.7",
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
@ -8817,7 +8825,6 @@
"integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"call-bind": "^1.0.7", "call-bind": "^1.0.7",
"define-properties": "^1.2.1", "define-properties": "^1.2.1",
@ -8856,7 +8863,6 @@
"integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"call-bind": "^1.0.8", "call-bind": "^1.0.8",
"call-bound": "^1.0.3", "call-bound": "^1.0.3",
@ -9249,6 +9255,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"nanoid": "^3.3.11", "nanoid": "^3.3.11",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
@ -9366,6 +9373,7 @@
"integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"prettier": "bin/prettier.cjs" "prettier": "bin/prettier.cjs"
}, },
@ -9857,6 +9865,7 @@
"integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
}, },
@ -10506,6 +10515,12 @@
"npm": ">= 3.0.0" "npm": ">= 3.0.0"
} }
}, },
"node_modules/sortablejs": {
"version": "1.15.7",
"resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.15.7.tgz",
"integrity": "sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==",
"license": "MIT"
},
"node_modules/source-map": { "node_modules/source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
@ -10896,7 +10911,6 @@
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=4" "node": ">=4"
} }
@ -11587,7 +11601,6 @@
"integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/json5": "^0.0.29", "@types/json5": "^0.0.29",
"json5": "^1.0.2", "json5": "^1.0.2",
@ -11601,7 +11614,6 @@
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"minimist": "^1.2.0" "minimist": "^1.2.0"
}, },
@ -11764,6 +11776,7 @@
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@ -12074,6 +12087,7 @@
"integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==", "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.18.10", "esbuild": "^0.18.10",
"postcss": "^8.4.27", "postcss": "^8.4.27",
@ -12337,6 +12351,7 @@
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.26.tgz", "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.26.tgz",
"integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==", "integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.26", "@vue/compiler-dom": "3.5.26",
"@vue/compiler-sfc": "3.5.26", "@vue/compiler-sfc": "3.5.26",
@ -12517,6 +12532,24 @@
"vue": "^3" "vue": "^3"
} }
}, },
"node_modules/vuedraggable": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-4.1.0.tgz",
"integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
"license": "MIT",
"dependencies": {
"sortablejs": "1.14.0"
},
"peerDependencies": {
"vue": "^3.0.1"
}
},
"node_modules/vuedraggable/node_modules/sortablejs": {
"version": "1.14.0",
"resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.14.0.tgz",
"integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==",
"license": "MIT"
},
"node_modules/w3c-xmlserializer": { "node_modules/w3c-xmlserializer": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", "resolved": "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",

@ -47,18 +47,21 @@
"mqtt": "^5.15.0", "mqtt": "^5.15.0",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"sockjs-client": "^1.6.1", "sockjs-client": "^1.6.1",
"sortablejs": "^1.15.7",
"stats.js": "^0.17.0", "stats.js": "^0.17.0",
"three": "^0.182.0", "three": "^0.182.0",
"vue": "^3.3.4", "vue": "^3.3.4",
"vue-echarts": "^6.6.5", "vue-echarts": "^6.6.5",
"vue-router": "^4.2.5", "vue-router": "^4.2.5",
"vue3-ace-editor": "^2.2.4" "vue3-ace-editor": "^2.2.4",
"vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@rushstack/eslint-patch": "^1.3.3", "@rushstack/eslint-patch": "^1.3.3",
"@tsconfig/node18": "^18.2.2", "@tsconfig/node18": "^18.2.2",
"@types/jsdom": "^21.1.3", "@types/jsdom": "^21.1.3",
"@types/node": "^18.18.5", "@types/node": "^18.18.5",
"@types/sortablejs": "^1.15.9",
"@types/three": "^0.182.0", "@types/three": "^0.182.0",
"@vitejs/plugin-vue": "^4.4.0", "@vitejs/plugin-vue": "^4.4.0",
"@vue/eslint-config-prettier": "^8.0.0", "@vue/eslint-config-prettier": "^8.0.0",

Binary file not shown.

@ -68,8 +68,13 @@ import VueTableFireHydrant from '@/components/vue-components/vue-table-fire-hydr
import VueTableFireHydrantInfo from '@/components/vue-components/vue-table-fire-hydrant-info.vue'; import VueTableFireHydrantInfo from '@/components/vue-components/vue-table-fire-hydrant-info.vue';
import VueThreeBuilding from '@/components/three-components/vue-three-building.vue'; import VueThreeBuilding from '@/components/three-components/vue-three-building.vue';
import VueExtinguisherTable from '@/components/vue-components/vue-extinguisher-table.vue'; import VueExtinguisherTable from '@/components/vue-components/vue-extinguisher-table.vue';
// F:\vue\workspace\maotu-webtopo\src\components\vue-components\vue-illumination-table.vue
import VueIlluminationTable from '@/components/vue-components/vue-illumination-table.vue'; import VueIlluminationTable from '@/components/vue-components/vue-illumination-table.vue';
import VueStrInfo from '@/components/vue-components/vue-str-info.vue';
import VueTestController from '@/components/vue-components/vue-test-controller.vue';
import VueCar from '@/components/vue-components/vue-car.vue';
import VueBaseInformation from '@/components/vue-components/vue-base-information.vue';
// F:\vue\workspace\maotu-webtopo\src\components\vue-components\vue-3d-base-information.vue
import Vue3dBaseInformation from '@/components/vue-components/vue-3d-base-information.vue';
// //
import VueGradeGauge from '@/components/vue-components/echarts-grade-gauge.vue'; import VueGradeGauge from '@/components/vue-components/echarts-grade-gauge.vue';
@ -127,6 +132,11 @@ instance?.appContext.app.component('vue-my-table-fire-hydrant-info', VueTableFir
instance?.appContext.app.component('vue-my-three-building', VueThreeBuilding); instance?.appContext.app.component('vue-my-three-building', VueThreeBuilding);
instance?.appContext.app.component('vue-my-extinguisher-table', VueExtinguisherTable); instance?.appContext.app.component('vue-my-extinguisher-table', VueExtinguisherTable);
instance?.appContext.app.component('vue-my-illumination-table', VueIlluminationTable); instance?.appContext.app.component('vue-my-illumination-table', VueIlluminationTable);
instance?.appContext.app.component('vue-my-str-info', VueStrInfo);
instance?.appContext.app.component('vue-my-test-controller', VueTestController);
instance?.appContext.app.component('vue-my-car', VueCar);
instance?.appContext.app.component('vue-my-base-information', VueBaseInformation);
instance?.appContext.app.component('vue-my-3d-base-information', Vue3dBaseInformation);
instance?.appContext.app.component('vue-grade-gauge', VueGradeGauge); instance?.appContext.app.component('vue-grade-gauge', VueGradeGauge);
instance?.appContext.app.component('vue-guage-line-chart', VueGuageLineChart); instance?.appContext.app.component('vue-guage-line-chart', VueGuageLineChart);
@ -322,7 +332,12 @@ leftAsideStore.registerConfig('vue四遥组件', [
showInfo: { showInfo: {
type: 'switch', type: 'switch',
val: false, val: false,
title: '显示名称' title: '显示详情'
},
showText: {
type: 'switch',
val: false,
title: '显示文字'
}, },
location: { location: {
type: 'select', type: 'select',
@ -349,6 +364,44 @@ leftAsideStore.registerConfig('vue四遥组件', [
} }
} }
}, },
{
id: 'vue-my-str-info',
title: 'vue遥测-模式',
type: 'vue',
thumbnail: '/svgs/num-info.svg',
props: {
moduleType: {
type: 'inputTypeTag',
val: '遥测',
title: '四遥类型'
},
moduleId: {
type: 'inputSelectId',
val: '--',
title: '绑定ID'
},
showStart: {
type: 'switch',
val: false,
title: '显示状态'
},
showModel: {
type: 'switch',
val: true,
title: '显示模式'
},
showVal: {
type: 'switch',
val: false,
title: '显示值'
},
showText: {
type: 'switch',
val: false,
title: '显示名称'
}
}
},
{ {
id: 'vue-my-regulator', id: 'vue-my-regulator',
title: 'vue遥调', title: 'vue遥调',
@ -366,6 +419,19 @@ leftAsideStore.registerConfig('vue四遥组件', [
title: '绑定ID' title: '绑定ID'
} }
} }
},
{
id: 'vue-my-test-controller',
title: '测试组件',
type: 'vue',
thumbnail: '/svgs/regulator.svg',
props: {
moduleId: {
type: 'inputSelectId',
val: '--',
title: '绑定ID'
}
}
} }
]); ]);
@ -398,8 +464,18 @@ leftAsideStore.registerConfig('vue公共组件', [
}, },
testColor: { testColor: {
type: 'color', type: 'color',
val: '#44B6E7', val: '#ffffff',
title: '文字颜色' title: '文字颜色'
},
fontSize: {
type: 'number',
val: 16,
title: '字体大小'
},
fontBold: {
type: 'switch',
val: false,
title: '加粗'
} }
} }
}, },
@ -671,6 +747,97 @@ leftAsideStore.registerConfig('vue公共组件', [
] ]
} }
} }
},
{
id: 'vue-my-car',
title: 'vue卡片组件',
type: 'vue',
thumbnail: '/svgs/table-base.svg',
props: {
fontFamily: {
title: '字体',
type: 'select',
val: 'Segoe UI',
options: [
{
value: 'Segoe UI',
label: 'Segoe UI'
},
{
value: '微软雅黑',
label: '微软雅黑'
},
{
value: '黑体',
label: '黑体'
},
{
value: '宋体',
label: '宋体'
}
]
},
testContent: {
type: 'input',
val: '标题内容',
title: '标题内容'
},
fontSize: {
type: 'number',
val: 14,
title: '标题大小'
},
testColor: {
type: 'color',
val: '#ffffff',
title: '文字颜色'
},
skeletonBool: {
type: 'switch',
val: false,
title: '辅助框'
},
skeletonRows: {
type: 'number',
val: 3,
title: '框数量'
}
}
},
{
id: 'vue-my-base-information',
title: 'vue基本信息',
type: 'vue',
thumbnail: '/svgs/table-base.svg',
props: {
dataSource: {
title: '数据源',
type: 'select',
val: '--',
options: [
{
value: 'fireAlarmHost',
label: '火灾报警系统主机'
},
{
value: 'host',
label: '主机'
},
{
value: 'oilChromatography',
label: '2号主变油色谱'
},
{
value: 'partialDischarge',
label: '2号主变局放'
},
{
value: 'switchGap',
label: '2215开关间隔'
}
]
}
}
} }
]); ]);
leftAsideStore.registerConfig('vue3D组件', [ leftAsideStore.registerConfig('vue3D组件', [
@ -816,6 +983,54 @@ leftAsideStore.registerConfig('vue3D组件', [
val: '--' val: '--'
} }
} }
},
{
id: 'vue-my-3d-base-information',
title: '3d-基本信息',
type: 'vue',
thumbnail: '/svgs/table-only.svg',
props: {
dataTitle: {
title: '标题',
type: 'input',
val: '单个设备基本信息'
},
dataSource: {
title: '数据源',
type: 'select',
val: '--',
options: [
{
value: 'smokeSiren',
label: '烟感报警器'
},
{
value: 'temperatureFireDetector',
label: '温感火灾探测器'
},
{
value: 'fireHydrant',
label: '消火栓'
},
{
value: 'fireExtinguisher',
label: '灭火器'
},
{
value: 'evacuationSign',
label: '疏散指示灯'
},
{
value: 'infraredBeam',
label: '红外对射'
},
{
value: 'accessController',
label: '门禁控制器'
}
]
}
}
} }
]); ]);
leftAsideStore.registerConfig('vue组件', [ leftAsideStore.registerConfig('vue组件', [

Binary file not shown.

@ -11,8 +11,10 @@
<!-- 连接控制 --> <!-- 连接控制 -->
<div class="control-section"> <div class="control-section">
<el-button type="primary" @click="connect" :disabled="isConnected">连接</el-button> <el-tag :type="isConnected ? 'success' : 'danger'" size="large">
<el-button type="danger" @click="disconnect" :disabled="!isConnected">断开</el-button> {{ isConnected ? 'MQTT 已连接' : 'MQTT 未连接' }}
</el-tag>
<el-button type="primary" @click="reconnect" :disabled="isConnected">重新连接</el-button>
</div> </div>
<!-- 消息发送 --> <!-- 消息发送 -->
@ -119,17 +121,14 @@ const clearLogs = () => {
}; };
// STOMP // STOMP
const connect = () => { const reconnect = () => {
stompService.connect({
reconnectDelay: 5000
});
ElMessage.info('正在连接 STOMP...');
};
//
const disconnect = () => {
stompService.disconnect(); stompService.disconnect();
ElMessage.info('已断开 STOMP 连接'); setTimeout(() => {
stompService.connect({
reconnectDelay: 5000
});
ElMessage.info('正在重新连接 MQTT...');
}, 1000);
}; };
// //
@ -151,17 +150,17 @@ const sendMessage = () => {
// //
onMounted(() => { onMounted(() => {
//
stompService.registerService('demoComponent', { stompService.registerService('demoComponent', {
mqttReady() { mqttReady() {
console.log('[Demo] STOMP 已就绪'); console.log('[Demo] MQTT 已就绪');
isConnected.value = true; isConnected.value = true;
ElMessage.success('STOMP 连接成功');
}, },
onConnectionLost() { onConnectionLost() {
console.log('[Demo] STOMP 连接丢失'); console.log('[Demo] MQTT 连接丢失');
isConnected.value = false; isConnected.value = false;
ElMessage.warning('STOMP 连接已断开,正在重连...'); ElMessage.warning('MQTT 连接已断开,正在重连...');
} }
}); });
@ -187,8 +186,8 @@ onMounted(() => {
// //
onUnmounted(() => { onUnmounted(() => {
// // MQTT
// stompService.disconnect(); console.log('[Demo] 组件卸载');
}); });
</script> </script>

@ -73,8 +73,8 @@
</el-icon> </el-button </el-icon> </el-button
></el-button-group> ></el-button-group>
<!-- <el-divider direction="vertical"></el-divider> --> <el-divider direction="vertical"></el-divider>
<!-- <el-popover <el-popover
placement="bottom" placement="bottom"
:width="240" :width="240"
trigger="hover" trigger="hover"
@ -131,7 +131,7 @@
</el-button> </el-button>
</el-button-group> </el-button-group>
</div> </div>
</el-popover> --> </el-popover>
<!-- <el-divider direction="vertical"></el-divider> --> <!-- <el-divider direction="vertical"></el-divider> -->
@ -332,6 +332,11 @@ const headerPanelProps = withDefaults(defineProps<HeaderPanelProps>(), {
useThumbnail: false, useThumbnail: false,
selectedItemsId: () => [] selectedItemsId: () => []
}); });
function leftJustify() {
emits('alignSelected', 'left');
}
const emits = defineEmits([ const emits = defineEmits([
'update:leftAside', 'update:leftAside',
'update:rightAside', 'update:rightAside',

@ -348,6 +348,7 @@ const onRenderCoreMouseDown = (item: IDoneJson, e: MouseEvent) => {
globalStore.refreshSelectedItemsId(); globalStore.refreshSelectedItemsId();
} }
}; };
const onMouseDown = (e: MouseEvent) => { const onMouseDown = (e: MouseEvent) => {
beginListenerKeyDown(); beginListenerKeyDown();
globalStore.cancelAllSelect(); globalStore.cancelAllSelect();
@ -376,6 +377,7 @@ const onMouseDown = (e: MouseEvent) => {
globalStore.setIntention('beginMulSelect'); globalStore.setIntention('beginMulSelect');
selectedAreaRef.value?.onMouseDown(e); selectedAreaRef.value?.onMouseDown(e);
}; };
/** /**
* 区域选择结束事件 之所以用getBoundingClientRect是为了处理旋转后的坐标 * 区域选择结束事件 之所以用getBoundingClientRect是为了处理旋转后的坐标
* @param area_binfo 区域选择的边界信息 * @param area_binfo 区域选择的边界信息

@ -108,6 +108,24 @@
height="80%" height="80%"
width="60%" width="60%"
> >
<div class="m-4">
<el-row>
<el-col :span="2"> 分区 </el-col>
<el-col :span="8">
<el-cascader
v-model="selectedPartition"
:options="options"
:props="props2"
clearable
@change="handlePartitionChange"
/>
</el-col>
<el-col :span="14">
<el-button type="primary" @click="getDataSource"></el-button>
</el-col>
</el-row>
</div>
<el-table :data="filteredData" :max-height="450"> <el-table :data="filteredData" :max-height="450">
<el-table-column type="index" label="序号" /> <el-table-column type="index" label="序号" />
<el-table-column prop="modeId" label="遥ID" /> <el-table-column prop="modeId" label="遥ID" />
@ -178,6 +196,7 @@ import {
ElColorPicker, ElColorPicker,
ElIcon, ElIcon,
ElButton, ElButton,
ElMessage,
type UploadFile type UploadFile
} from 'element-plus'; } from 'element-plus';
import { Search } from '@element-plus/icons-vue'; import { Search } from '@element-plus/icons-vue';
@ -237,16 +256,22 @@ function setInputTagVal() {
const item = selectItemPropsSettingProps.propsInfo[key]; const item = selectItemPropsSettingProps.propsInfo[key];
if (item.type === 'inputTypeTag') { if (item.type === 'inputTypeTag') {
inputTypeTagValue.value = item.val; inputTypeTagValue.value = item.val;
console.log('inputTypeTagValue:', inputTypeTagValue.value);
break;
} }
} }
} }
let attrItem: any; let attrItem: any;
const dialogTableVisible = ref(false); const dialogTableVisible = ref(false);
function handleIconClick(obj: any) {
assembleList(); async function handleIconClick(obj: any) {
console.log('inputTypeTagValue11:', inputTypeTagValue.value);
console.log('handleIconClick:', obj);
attrItem = obj; attrItem = obj;
dialogTableVisible.value = true; await getAllTree(); //
assembleList();
dialogTableVisible.value = true; //
} }
let attrImg: any; let attrImg: any;
@ -266,6 +291,22 @@ function bindingImg(obj: any) {
let modelIds = ref<string[]>([]); let modelIds = ref<string[]>([]);
//
const selectedPartition = ref<string[]>([]);
//
const handlePartitionChange = (value: string[]) => {
console.log('选中的分区数据:', value);
// value
// [' ID', ' ID', ...]
//
if (value && value.length > 0) {
const lastValue = value[value.length - 1];
console.log('最后选中的值:', lastValue);
//
}
};
function handleEdit(obj: any) { function handleEdit(obj: any) {
dialogTableVisible.value = false; dialogTableVisible.value = false;
attrItem.val = obj.modeId; attrItem.val = obj.modeId;
@ -310,20 +351,36 @@ const filterTag = (value: number, row: any) => {
// //
const filteredData = computed(() => { const filteredData = computed(() => {
debugger;
if (!search.value) { if (!search.value) {
return gridData; return gridData.value;
} }
return gridData.filter((item) => { return gridData.value.filter((item) => {
return item.name.toLowerCase().includes(search.value.toLowerCase()); return item.name.toLowerCase().includes(search.value.toLowerCase());
}); });
}); });
// //
function assembleList() { function assembleList() {
// debugger; debugger;
let code: number | undefined; let code: number | undefined;
setInputTagVal();
// globalDataRaw
gridData.value.splice(0, gridData.value.length);
if (inputTypeTagValue.value !== undefined) code = getNameForNode(inputTypeTagValue.value); if (inputTypeTagValue.value !== undefined) code = getNameForNode(inputTypeTagValue.value);
gridData.splice(0, gridData.length);
//
if (gridDataSource.value && gridDataSource.value.length > 0) {
if (code === undefined) {
// code
return;
} else {
// code
gridData.value = gridDataSource.value.filter((item) => item.bType === code);
console.log('new_gridData:', gridData.value);
return;
}
}
let globalDataRaw = (window as unknown as ExtendedParentWindow).globalData; let globalDataRaw = (window as unknown as ExtendedParentWindow).globalData;
// let globalDataRaw = (window.parent as ExtendedParentWindow).Rec?.service.node.runtimes; // let globalDataRaw = (window.parent as ExtendedParentWindow).Rec?.service.node.runtimes;
@ -340,7 +397,7 @@ function assembleList() {
let modeId = key; let modeId = key;
let name = data.node.name; let name = data.node.name;
let bType = data.bType; let bType = data.bType;
gridData.push({ modeId, name, bType }); gridData.value.push({ modeId, name, bType });
} }
}); });
} else { } else {
@ -352,7 +409,7 @@ function assembleList() {
let modeId = key; let modeId = key;
let name = data.node.name; let name = data.node.name;
let bType = data.bType; let bType = data.bType;
gridData.push({ modeId, name, bType }); gridData.value.push({ modeId, name, bType });
} }
} }
} }
@ -365,7 +422,73 @@ interface GridDataItem {
bType: number; bType: number;
} }
const gridData: GridDataItem[] = []; const gridData = ref<GridDataItem[]>([]);
const gridDataSource = ref<GridDataItem[]>([]);
const props2 = {
multiple: false,
checkStrictly: true
};
async function getDataSource() {
debugger;
let cls = '-1';
if (selectedPartition.value && selectedPartition.value.length > 0)
cls = selectedPartition.value[selectedPartition.value.length - 1];
let response = await modelApi.getNodeByCls_get(cls);
if (response.code != 200 || response.data == null) {
ElMessage.error(response.message);
} else {
let code: number | undefined;
setInputTagVal();
if (inputTypeTagValue.value !== undefined) code = getNameForNode(inputTypeTagValue.value);
gridDataSource.value.splice(0, gridDataSource.value.length);
gridData.value.splice(0, gridData.value.length);
response.data.forEach((item: any) => {
console.log('item:', item);
gridDataSource.value.push({
modeId: item.id,
name: item.name,
bType: item.btype
});
if (code == undefined || code == item.btype) {
gridData.value.push({
modeId: item.id,
name: item.name,
bType: item.btype
});
}
});
ElMessage.success('接口请求成功');
console.log('gridData:', gridData.value);
}
}
async function getAllTree() {
const response = await modelApi.allTree_get();
if (response.code != 200 || response.data == null) {
ElMessage.error(response.message);
} else {
// id value
options = convertTreeData(response.data);
ElMessage.success('树接口请求成功');
}
}
//
function convertTreeData(data: any[]): any[] {
return data.map((item) => ({
value: item.id.toString(), // id value
label: item.label,
children: item.children ? convertTreeData(item.children) : []
}));
}
let options: any[] = [];
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

@ -1,20 +1,50 @@
<template> <template>
<div style="width: 100%; height: 100%"> <div style="width: 100%; height: 100%">
<h2 class="my-button" style="width: 100%; height: 100%">{{ props.modelValue }}</h2> <!-- <h2 class="my-button" style="width: 100%; height: 100%">{{ props.modelValue }}</h2> -->
<el-text class="my-button">{{ props.modelValue }}</el-text>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue';
const props = defineProps({ const props = defineProps({
modelValue: String, modelValue: String,
fontFamily: String, fontFamily: String,
testColor: String testColor: String,
fontSize: Number,
fontBold: Boolean
});
let computedSize = computed({
//
get() {
return props.fontSize + 'px';
}, //
set(val) {}
});
let computedBold = computed({
//
get() {
return props.fontBold ? 'bold' : 'normal';
}, //
set(val) {}
}); });
</script> </script>
<style scoped> <style scoped>
.my-button { .my-button {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
display: flex;
font-weight: v-bind('computedBold');
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
text-align: center;
color: v-bind('props.testColor'); color: v-bind('props.testColor');
font-family: v-bind('props.fontFamily'); font-family: v-bind('props.fontFamily');
font-size: v-bind('computedSize');
} }
</style> </style>

@ -0,0 +1,262 @@
<template>
<el-dialog
:model-value="modelValue"
@update:model-value="handleUpdate"
class="elDialog"
top="10vh"
title="组件绑定"
height="80%"
width="60%"
>
<div class="m-4">
<el-row>
<el-col :span="2"> 分区 </el-col>
<el-col :span="8">
<el-cascader
v-model="selectedPartition"
:options="options"
:props="props2"
clearable
@change="handlePartitionChange"
/>
</el-col>
<el-col :span="14">
<el-button type="primary" @click="getDataSource"></el-button>
</el-col>
</el-row>
</div>
<el-table :data="filteredData" :max-height="450">
<el-table-column type="index" label="序号" />
<el-table-column prop="modeId" label="遥ID" />
<el-table-column prop="name" label="名称" />
<el-table-column
v-if="inputTypeTagValue == undefined"
prop="bType"
label="类型筛选"
:filters="[
{ text: '遥信', value: 1 },
{ text: '遥测', value: 2 },
{ text: '遥控', value: 3 },
{ text: '遥调', value: 4 },
{ text: '其他', value: 0 }
]"
:filter-method="filterTag"
filter-placement="bottom-end"
filter-confirm-button-text="确定"
filter-reset-button-text="重置"
>
<template #default="scope">
<el-tag type="primary" v-if="scope.row.bType === 1">{{ '' }}</el-tag>
<el-tag type="success" v-else-if="scope.row.bType === 2">{{ '遥测' }}</el-tag>
<el-tag type="info" v-else-if="scope.row.bType === 3">{{ '遥控' }}</el-tag>
<el-tag type="warning" v-else-if="scope.row.bType === 4">{{ '遥调' }}</el-tag>
<el-tag type="danger" v-else>{{ '' }}</el-tag>
</template>
</el-table-column>
<el-table-column v-if="inputTypeTagValue != undefined" label="类型">
<template #default="scope">
<el-tag type="primary" v-if="scope.row.bType === 1">{{ '' }}</el-tag>
<el-tag type="success" v-else-if="scope.row.bType === 2">{{ '遥测' }}</el-tag>
<el-tag type="info" v-else-if="scope.row.bType === 3">{{ '遥控' }}</el-tag>
<el-tag type="warning" v-else-if="scope.row.bType === 4">{{ '遥调' }}</el-tag>
</template>
</el-table-column>
<!-- <el-table-column label="操作">
<template #default="scope">
<el-button type="primary" size="small" @click="handleEdit(scope.row)"> </el-button>
</template>
</el-table-column> -->
<el-table-column align="center">
<template #header>
<el-input v-model="search" size="small" placeholder="节点名称" />
</template>
<template #default="scope">
<el-button size="small" @click="console.log(scope.row)"> </el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
</template>
<script setup lang="ts">
import {
ElInput,
ElButton,
ElMessage,
} from 'element-plus';
import { ref, computed, onMounted } from 'vue';
import { modelApi } from '@/utils/request';
interface GridDataItem {
modeId: string;
name: string;
bType: number;
}
const props = defineProps({
modelValue: {
type: Boolean,
required: true
}
});
const emit = defineEmits({
'update:modelValue': (value: boolean) => true
});
const handleUpdate = (value: boolean) => {
emit('update:modelValue', value);
};
//
const selectedPartition = ref<string[]>([]);
const gridDataSource = ref<GridDataItem[]>([]);
const gridData = ref<GridDataItem[]>([]);
const search = ref('');
let options: any[] = [];
const props2 = {
multiple: false,
checkStrictly: true
};
//
const handlePartitionChange = (value: string[]) => {
console.log('选中的分区数据:', value);
// value
// [' ID', ' ID', ...]
//
if (value && value.length > 0) {
const lastValue = value[value.length - 1];
console.log('最后选中的值:', lastValue);
//
}
};
//
const filteredData = computed(() => {
if (!search.value) {
return gridData.value;
}
return gridData.value.filter((item) => {
return item.name.toLowerCase().includes(search.value.toLowerCase());
});
});
let inputTypeTagValue = ref<string>();
async function getDataSource() {
let cls = '-1';
if (selectedPartition.value && selectedPartition.value.length > 0)
cls = selectedPartition.value[selectedPartition.value.length - 1];
let response = await modelApi.getNodeByCls_get(cls);
if (response.code != 200 || response.data == null) {
ElMessage.error(response.message);
} else {
let code: number | undefined;
setInputTagVal();
if (inputTypeTagValue.value !== undefined) code = getNameForNode(inputTypeTagValue.value);
gridDataSource.value.splice(0, gridDataSource.value.length);
gridData.value.splice(0, gridData.value.length);
response.data.forEach((item: any) => {
console.log('item:', item);
gridDataSource.value.push({
modeId: item.id,
name: item.name,
bType: item.btype
});
if (code == undefined || code == item.btype) {
gridData.value.push({
modeId: item.id,
name: item.name,
bType: item.btype
});
}
});
ElMessage.success('接口请求成功');
console.log('gridData:', gridData.value);
}
}
async function getAllTree() {
const response = await modelApi.allTree_get();
if (response.code != 200 || response.data == null) {
ElMessage.error(response.message);
} else {
// id value
options = convertTreeData(response.data);
ElMessage.success('树接口请求成功');
}
}
//
function convertTreeData(data: any[]): any[] {
return data.map((item) => ({
value: item.id.toString(), // id value
label: item.label,
children: item.children ? convertTreeData(item.children) : []
}));
}
//
function setInputTagVal() {
return '遥信';
// for (const key in selectItemPropsSettingProps.propsInfo) {
// const item = selectItemPropsSettingProps.propsInfo[key];
// if (item.type === 'inputTypeTag') {
// inputTypeTagValue.value = item.val;
// console.log('inputTypeTagValue:', inputTypeTagValue.value);
// break;
// }
// }
}
const filterTag = (value: number, row: any) => {
return row.bType === value;
};
function getNameForNode(nodeName: string): number {
switch (nodeName) {
case '遥信':
return 1;
case '遥测':
return 2;
case '遥控':
return 3;
case '遥调':
return 4;
default:
return 0;
}
}
onMounted(() => {
getAllTree();
});
</script>
<style lang="less" scoped>
.elDialog {
padding: 0;
margin: 0;
}
.demo-form-inline {
padding: 15px 0;
}
.demo-form-inline .el-input {
--el-input-width: 150px;
}
.demo-form-inline .el-select {
--el-select-width: 150px;
}
</style>

@ -0,0 +1,293 @@
<template>
<el-card class="card" shadow="hover" :body-style="{ padding: '10px' }">
<h2 class="cardHead">{{ dataTitle }}</h2>
<div class="cardBody">
<el-row :gutter="20" style="height: 100%">
<el-col :span="12" class="image-col">
<div ref="containerRef" class="container" />
</el-col>
<el-col :span="12">
<el-row v-for="item in dataObj" :key="item.field" class="info-row">
<el-col :span="10" class="info-label">{{ item.field }}</el-col>
<el-col :span="12"
><el-tag size="small">{{ item.value }}</el-tag></el-col
>
</el-row>
</el-col>
</el-row>
</div>
</el-card>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref, reactive, computed, watch } from 'vue';
import {
AxesHelper,
Color,
PerspectiveCamera,
Scene,
WebGLRenderer,
AmbientLight,
DirectionalLight,
PointLight
} from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { invariant3DObjMap, type Paragraph } from '@/utils/invariable';
const containerRef = ref<HTMLDivElement>();
//
const scene = new Scene();
//
const camera = new PerspectiveCamera(
45,
containerRef.value?.clientWidth! / containerRef.value?.clientHeight!,
0.1,
1000
);
camera.position.set(-40, 20, 35);
camera.lookAt(scene.position);
//
const renderer = new WebGLRenderer({
antialias: true, // 齿
alpha: true, //
preserveDrawingBuffer: true //
});
renderer.setClearColor(new Color('#131519'));
renderer.setSize(containerRef.value?.clientWidth!, containerRef.value?.clientHeight!);
renderer.shadowMap.enabled = true;
renderer.setPixelRatio(window.devicePixelRatio || 1); //
//
const orbitControls = new OrbitControls(camera, renderer.domElement);
orbitControls.autoRotate = false;
orbitControls.autoRotateSpeed = 5;
orbitControls.dampingFactor = 0.1;
orbitControls.enableDamping = true;
orbitControls.enableRotate = true;
orbitControls.minDistance = 10;
orbitControls.maxPolarAngle = Math.PI * 0.5;
let modelPath = '/models/default/default.glb';
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
loader.setDRACOLoader(dracoLoader);
//
let currentModel: any = null;
//
loader.load(modelPath, (gltf: any) => {
gltf.scene.scale.set(1, 1, 1);
scene.add(gltf.scene);
currentModel = gltf.scene;
});
//
const ambientLight = new AmbientLight('#ffffff', 1.8); // 0.6 1.0
scene.add(ambientLight);
//
const directionalLight = new DirectionalLight('#ffffff', 3.8); // 0.2 0.8
scene.add(directionalLight);
directionalLight.position.set(20, 20, 10);
//
const pointLight = new PointLight('#ffffff', 1.8, 1800); // 0.5 1.0
scene.add(pointLight);
pointLight.position.set(0, 40, 0);
function animate() {
requestAnimationFrame(animate);
orbitControls.update();
renderer.render(scene, camera);
}
// ResizeObserver
let resizeObserver: ResizeObserver | null = null;
onMounted(() => {
const container = containerRef.value!;
container.appendChild(renderer.domElement);
//
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
//
resizeObserver = new ResizeObserver(() => {
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
});
resizeObserver.observe(container);
animate();
});
function loadingModel() {
//
if (currentModel) {
scene.remove(currentModel);
//
currentModel.traverse((child: any) => {
if (child.geometry) child.geometry.dispose();
if (child.material) {
if (Array.isArray(child.material)) {
child.material.forEach((m: any) => m.dispose());
} else {
child.material.dispose();
}
}
});
currentModel = null;
}
//
loader.load(modelPath, (gltf: any) => {
gltf.scene.scale.set(1, 1, 1);
scene.add(gltf.scene);
currentModel = gltf.scene;
});
}
const props = defineProps({
dataTitle: {
type: String,
default: '--'
},
dataSource: {
type: String,
default: '--'
}
});
const dataObj = reactive<Paragraph[]>([]);
function getDataBySource() {
dataObj.length = 0;
modelPath = '/models/default/default.glb';
if (props.dataSource != null && props.dataSource !== '' && props.dataSource !== '--') {
const dataObjTemp = invariant3DObjMap[props.dataSource];
if (!dataObjTemp) {
loadingModel();
return;
}
dataObjTemp.forEach((item) => {
if (item.field == '3D模型') {
modelPath = item.value;
} else dataObj.push(item);
});
}
loadingModel();
}
watch(
() => props.dataSource,
(newVal, oldVal) => {
getDataBySource();
}
);
onUnmounted(() => {
//
if (currentModel) {
scene.remove(currentModel);
currentModel.traverse((child: any) => {
if (child.geometry) child.geometry.dispose();
if (child.material) {
if (Array.isArray(child.material)) {
child.material.forEach((m: any) => m.dispose());
} else {
child.material.dispose();
}
}
});
currentModel = null;
}
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
});
</script>
<style scoped>
.card {
flex: 1;
display: flex;
flex-direction: column;
height: 100%;
}
.card :deep(.el-card__body) {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 10px;
}
.cardHead {
margin: 0 0 15px 0;
padding: 0;
font-size: 16px;
font-weight: bold;
text-align: center;
color: #ffffff;
flex-shrink: 0;
}
.cardBody {
width: 100%;
height: 100%;
}
/* el-row 高度继承 */
.cardBody :deep(.el-row) {
/* height: 100%; */
}
/* el-col 高度继承 */
.cardBody :deep(.el-col) {
/* height: 100%;
display: flex;
align-items: stretch; */
}
/* 图片列样式 */
.image-col {
height: 100%;
display: flex;
}
/* 探测器图片样式 */
.detector-image {
width: 100%;
height: 100%;
object-fit: contain;
}
/* 信息行样式 */
.info-row {
margin-bottom: 1%;
}
/* 标签样式 */
.info-label {
font-size: 12px;
color: #ffffff;
}
/* el-tag 样式 */
.info-row :deep(.el-tag) {
font-size: 11px;
}
</style>

@ -0,0 +1,101 @@
<template>
<el-card class="card">
<h2 class="cardHead">基本信息</h2>
<div class="cardBody">
<el-scrollbar>
<el-row
v-for="item in dataObj"
:key="item.field"
:gutter="0"
style="display: flex; align-items: center; margin-bottom: 1%"
>
<el-col :span="8" :offset="4">{{ item.field }}</el-col>
<el-col :span="4">
<el-tag type="primary" size="small">{{ item.value }}</el-tag>
</el-col>
</el-row>
<!-- <el-row :gutter="0" style="display: flex; align-items: center;margin-bottom: 1%;">
<el-col :span="8" :offset="4"> 名称 </el-col>
<el-col :span="4">
<el-tag type="primary" size="small">{{ data.name }}</el-tag>
</el-col>
</el-row> -->
</el-scrollbar>
</div>
</el-card>
</template>
<script lang="ts" setup>
import { computed, ref, watch, onMounted, reactive } from 'vue';
import { invariantObjMap, type Paragraph } from '@/utils/invariable';
import { da } from 'element-plus/es/locales.mjs';
const props = defineProps({
dataSource: {
type: String,
default: '--'
}
});
const dataObj = reactive<Paragraph[]>([]);
function getDataBySource() {
dataObj.length = 0;
if (props.dataSource != null && props.dataSource !== '' && props.dataSource !== '--') {
const dataObjTemp = invariantObjMap[props.dataSource];
if (!dataObjTemp) return;
dataObj.push(...dataObjTemp);
}
}
onMounted(() => {
loadDataSource(props.dataSource);
});
watch(
() => props.dataSource,
(newVal, oldVal) => {
loadDataSource(newVal);
}
);
function loadDataSource(val: string) {
//
getDataBySource();
}
</script>
<style scoped>
:deep(.el-card__body) {
padding: 0;
padding-top: 10px;
padding-left: 2%;
}
.el-row .el-col {
margin-bottom: 6px; /* 设置上下间距 */
}
.card {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
color: #ffffff;
font-size: 12px;
}
.cardHead {
margin: 0;
padding: 0px;
padding-bottom: 5px;
text-align: center;
}
.cardBody {
width: 98%;
margin: 0;
padding: 0;
height: calc(100% - 40px);
overflow: hidden;
}
</style>

@ -0,0 +1,98 @@
<template>
<!-- <el-button @click="ccc">Default</el-button> -->
<el-card class="card">
<el-scrollbar height="100%">
<h2 class="cardHead">{{ testContent }}</h2>
<div class="cardBody" v-if="skeletonBool">
<el-skeleton :rows="rows" style="margin-top: 4%" />
</div>
</el-scrollbar>
</el-card>
</template>
<script lang="ts" setup>
import { computed, ref, watch, onMounted } from 'vue';
const props = defineProps({
fontFamily: {
type: String,
default: 'Segoe UI'
},
fontSize: {
type: Number,
default: 14
},
testColor: {
type: String,
default: '#000000'
},
testContent: {
type: String,
default: '标题内容'
},
skeletonBool: {
type: Boolean,
default: true
},
skeletonRows: {
type: Number,
default: 5
}
});
let computedSize = computed({
//
get() {
return props.fontSize + 'px';
}, //
set(val) {
console.log('有人修改了fullName', val);
}
});
let rows = computed({
//
get() {
if (props.skeletonRows >= 1) return props.skeletonRows - 1;
else return 0;
},
set(val) {}
});
</script>
<style scoped>
:deep(.el-card__body) {
padding: 0;
padding-top: 10px;
/* padding-left: 10px; */
}
.card {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
/* max-width: 480px; */
color: v-bind('props.testColor');
font-family: v-bind('props.fontFamily');
/* font-family: 'Segoe UI'; */
font-size: v-bind('computedSize');
}
.cardHead {
margin: 0;
padding: 0px;
padding-bottom: 5px;
text-align: center;
}
.cardBody {
width: 90%;
height: 100%;
margin-left: 5%;
}
/* 让骨架屏的段落更厚且宽度一致 */
:deep(.el-skeleton__item) {
height: 24px; /* 增加段落高度,默认约 16px */
width: 100%; /* 让所有段落宽度一致,填满容器 */
}
</style>

@ -0,0 +1,289 @@
<template>
<!-- 四遥遥测 -->
<!-- <button @click="console.log('data:', nodeByModelsStore.nodeMap)">点击</button> -->
<el-row :gutter="20" width="100%">
<el-col :span="24" justify="center" align="middle">
<el-tag :type="color" effect="plain" style="width: 100%">
<div style="display: flex; justify-content: space-around; align-items: center; width: 100%">
<el-divider direction="vertical"></el-divider>
<div v-if="showStart">
{{ nodeStatus }}
<el-divider direction="vertical"></el-divider>
</div>
<div v-if="showModel">
{{ nodeModel }}
<el-divider direction="vertical"></el-divider>
</div>
<div v-if="showVal">
{{ nodeValue }}
<el-divider direction="vertical"></el-divider>
</div>
</div>
</el-tag>
</el-col>
</el-row>
<el-row :gutter="20" v-if="showText">
<el-col :span="24" justify="center" align="middle"
><el-tag type="primary">{{ modeName }}</el-tag></el-col
>
</el-row>
</template>
<script lang="ts" setup>
import { onMounted, ref, computed, onUnmounted, watch } from 'vue';
import { useNodeByModelsStore } from '@/components/mt-edit/store/nodeByModels';
import emitter from '@/utils/emitter';
// F:\vue\workspace\maotu-webtopo\src\utils\config.ts
import { Rec, getIndex, getNodeStatus, getNodeColor } from '@/utils/config';
const nodeByModelsStore = useNodeByModelsStore();
onMounted(() => {
loadingModuleById();
saveodeByModels();
});
onUnmounted(() => {
console.log('组件卸载');
deleteByModels();
emitter.off(props.definitionItemJson.id);
});
const props = defineProps({
moduleType: {
type: String,
default: '--'
},
moduleId: {
type: String,
default: '--'
},
definitionItemJson: {
type: Object,
default: () => ({})
},
showStart: {
type: Boolean,
default: false
},
showModel: {
type: Boolean,
default: true
},
showVal: {
type: Boolean,
default: false
},
showText: {
type: Boolean,
default: false
}
});
console.log('itemjson:', props.definitionItemJson.id);
function saveodeByModels() {
if (props.moduleId == '' || props.moduleId == undefined || props.moduleId == '--') return;
nodeByModelsStore.saveOrUpdate(props.moduleId, props.definitionItemJson.id);
}
function deleteByModels() {
if (props.moduleId == '' || props.moduleId == undefined || props.moduleId == '--') return;
nodeByModelsStore.delete(props.moduleId, props.definitionItemJson.id);
}
watch(
() => props.moduleId,
(newVal, oldVal) => {
loadingModuleById();
nodeByModelsStore.change(newVal, oldVal, props.definitionItemJson.id);
}
);
emitter.on(props.definitionItemJson.id, (value) => {
console.log('触发事件', value);
loadingModuleById();
});
let moduleUnit = ref<string>();
let modeName = ref<string>();
let percentage = ref<number>();
let color = ref<string>('info');
let nodeStatus = ref<string>('未知状态');
let nodeModel = ref<string>('无模式');
let nodeValue = ref<string>('--');
function getModuleById(moduleId: string) {
const globalData = (window as any).globalData;
if (!globalData || moduleId == undefined || moduleId == '' || props.moduleId == '--') {
console.warn('globalData 未初始化');
return null;
}
return globalData.get(moduleId);
}
function loadingModuleById() {
// 使访 globalData
let module = getModuleById(props.moduleId);
if (props.moduleId !== '' && props.moduleId !== undefined && props.moduleId !== '--' && module) {
if (module) {
console.log('dddd');
modeName.value = module.node.name;
percentage.value = module.double;
moduleUnit.value = module.node.unit;
color.value = getNodeColor(module.sig);
nodeStatus.value = getNodeStatus(module.sig);
nodeModel.value = computedNodeModel();
nodeValue.value = computedVal.value;
}
}
}
function computedNodeModel(): string {
if (props.moduleId == '' || props.moduleId == undefined || props.moduleId == '--') return '--';
let runTimeNode = getModuleById(props.moduleId);
if (!runTimeNode) return '--';
//
if (runTimeNode.node.etype == 0 || runTimeNode.node.etype == 2) {
return '无模式';
}
console.log('runTimeNode.node.etype', runTimeNode.node.etype);
console.log('runTimeNode.valInt', runTimeNode.valInt);
console.log('getIndex(runTimeNode.bType)', getIndex(runTimeNode.bType));
let val = Rec.EnumTypeValFun![runTimeNode.node.etype](
runTimeNode.valInt,
getIndex(runTimeNode.bType)
);
return val;
}
//
let computedModel = computed({
//
get() {
if (props.moduleId == '' || props.moduleId == undefined || props.moduleId == '--') return '--';
let runTimeNode = getModuleById(props.moduleId);
if (!runTimeNode) return '--';
//
if (runTimeNode.node.etype == 0 || runTimeNode.node.etype == 2) {
return '无模式';
}
console.log('runTimeNode.node.etype', runTimeNode.node.etype);
console.log('runTimeNode.valInt', runTimeNode.valInt);
console.log('getIndex(runTimeNode.bType)', getIndex(runTimeNode.bType));
let val = Rec.EnumTypeValFun![runTimeNode.node.etype](
runTimeNode.valInt,
getIndex(runTimeNode.bType)
);
return val;
},
//
set() {}
});
//
let computedStatus = computed({
//
get() {
if (props.moduleId == '' || props.moduleId == undefined || props.moduleId == '--')
return '未知状态';
let runTimeNode = getModuleById(props.moduleId);
if (!runTimeNode) return '未知状态';
let val = getNodeStatus(runTimeNode.sig);
return val;
},
//
set() {}
});
//
let computedVal = computed({
//
get() {
if (percentage.value == undefined && moduleUnit.value == undefined) return '--';
//
const formattedValue =
percentage.value != null && !Number.isInteger(percentage.value)
? percentage.value.toFixed(2)
: (percentage.value?.toString() ?? '--');
return formattedValue + '' + moduleUnit.value;
},
//
set() {}
});
function test() {
console.log(getModuleById(props.moduleId));
}
const customFormat = () => {
//
if (percentage.value == undefined && moduleUnit.value == undefined) return '--';
return percentage.value + '' + moduleUnit.value;
};
</script>
<style scoped>
/* 设置固定宽度和文本省略 */
.ellipsis-tag {
width: 65px; /* 设置固定宽度 */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
align-items: center; /* 水平居中 */
}
.flex-center {
display: flex;
flex-direction: column; /* 垂直排列 */
justify-content: center; /* 垂直居中 */
align-items: center; /* 水平居中 */
}
.el-statistic {
/* 控制文字大小 */
--el-statistic-content-font-size: 16px;
}
.elColInfo {
text-align: center;
}
:deep(.el-progress__text) {
/* 控制文字大小 */
font-size: 16px !important;
}
.demo-progress .el-progress--line {
margin-bottom: 15px;
max-width: 500px;
}
.demo-progress .el-progress--circle {
margin-right: 15px;
}
.fontStyle {
color: rgb(51, 126, 204);
}
h3 {
margin: 0;
padding: 0;
text-align: center;
/* padding-top: 5px;
padding-bottom: 5px; */
}
.demo-radius .radius {
height: 28px;
min-width: 66px;
width: fit-content;
border: 2px solid rgba(51, 125, 204, 0.205);
border-radius: 0;
padding: 0 8px;
box-sizing: border-box;
}
</style>

@ -0,0 +1,32 @@
<template>
<!-- 四遥遥测 -->
<el-form :model="form" label-width="auto" style="max-width: 600px">
<el-form-item label="节点ID">
<el-input v-model="form.nodeId" />
</el-form-item>
<el-form-item label="值">
<el-input v-model="form.nodeValue" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">Send</el-button>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { reactive } from 'vue';
// do not use same name with ref
const form = reactive({
nodeId: '',
nodeValue: ''
});
const onSubmit = () => {
console.log('submit!');
};
</script>
<style scoped></style>

@ -1,7 +1,7 @@
<template> <template>
<el-row v-if="location === 'top'"> <el-row v-if="location === 'top'">
<el-col :span="24" class="flex-center"> <el-col :span="24" class="flex-center">
<el-tag type="primary" size="small" style="opacity: 0.9"> <el-tag v-if="showText" type="primary" size="small" style="opacity: 0.9">
<el-text type="primary" style="max-width: 65px" size="small" truncated> <el-text type="primary" style="max-width: 65px" size="small" truncated>
{{ modeName }} {{ modeName }}
</el-text> </el-text>
@ -11,7 +11,7 @@
<el-row justify="center"> <el-row justify="center">
<el-col :span="12" class="flex-center" v-if="location === 'left'"> <el-col :span="12" class="flex-center" v-if="location === 'left'">
<el-tag type="primary" size="small" style="opacity: 0.9"> <el-tag v-if="showText" type="primary" size="small" style="opacity: 0.9">
<el-text type="primary" style="max-width: 65px" size="small" truncated> <el-text type="primary" style="max-width: 65px" size="small" truncated>
{{ modeName }} {{ modeName }}
</el-text> </el-text>
@ -75,7 +75,7 @@
</el-col> </el-col>
<el-col :span="12" class="flex-center" v-if="location === 'right'"> <el-col :span="12" class="flex-center" v-if="location === 'right'">
<el-tag type="primary" size="small" style="opacity: 0.9"> <el-tag v-if="showText" type="primary" size="small" style="opacity: 0.9">
<el-text type="primary" style="max-width: 65px" size="small" truncated> <el-text type="primary" style="max-width: 65px" size="small" truncated>
{{ modeName }} {{ modeName }}
</el-text> </el-text>
@ -85,7 +85,7 @@
<el-row v-if="location === 'bottom'"> <el-row v-if="location === 'bottom'">
<el-col :span="24" class="flex-center"> <el-col :span="24" class="flex-center">
<el-tag type="primary" size="small" style="opacity: 0.9"> <el-tag v-if="showText" type="primary" size="small" style="opacity: 0.9">
<el-text type="primary" style="max-width: 65px; opacity: 1" size="small" truncated> <el-text type="primary" style="max-width: 65px; opacity: 1" size="small" truncated>
{{ modeName }} {{ modeName }}
</el-text> </el-text>
@ -132,6 +132,10 @@ const props = defineProps({
showInfo: { showInfo: {
type: Boolean, type: Boolean,
default: false default: false
},
showText: {
type: Boolean,
default: false
} }
}); });

@ -10,6 +10,7 @@ import { createPinia } from 'pinia';
import 'element-plus/dist/index.css'; import 'element-plus/dist/index.css';
import router from './router/newIndex'; import router from './router/newIndex';
import '@/utils/globalUtils'; import '@/utils/globalUtils';
import { stompService } from '@/utils/stompService';
// 导入 Element Plus 语言包 // 导入 Element Plus 语言包
// import enLocale from 'element-plus/dist/locale/en.mjs'; // import enLocale from 'element-plus/dist/locale/en.mjs';
@ -74,4 +75,11 @@ app.component('my-input', MyInput);
app.component('my-button', MyButton); app.component('my-button', MyButton);
app.component('custom-demo', CustomDemo); app.component('custom-demo', CustomDemo);
app.component('pie-charts', PieCharts); app.component('pie-charts', PieCharts);
// 项目启动时自动连接 STOMP/MQTT
stompService.connect({
reconnectDelay: 5000 // 5 秒后重连
});
console.log('[Main] STOMP/MQTT 服务已启动,正在连接...');
app.mount('#app'); app.mount('#app');

@ -813,6 +813,59 @@ export const constantRoutes: Readonly<RouteRecordRaw[]> = [
} }
] ]
}, },
{
path: '/classify', //F:\vue\workspace\maotu-webtopo\src\views\preview\classify\index.vue
name: 'classify',
component: () => import('@/layout/view_index.vue'),
meta: {
title: '设备分类项目',
hidden: false,
treeHidden: false,
bottomHidden: false,
menuIcon: 'Operation'
},
children: [
{
path: '/classify/index',
name: 'classifyIndex',
component: () => import('@/views/preview/classify/index.vue'),
meta: {
title: '设备分类',
hidden: false,
treeHidden: false,
bottomHidden: false,
menuIcon: 'Setting'
}
}
]
},
// F:\vue\workspace\maotu-webtopo\src\views\preview\lightBoard\index.vue
{
path: '/lightBoard', //F:\vue\workspace\maotu-webtopo\src\views\preview\classify\index.vue
name: 'lightBoard',
component: () => import('@/layout/view_index.vue'),
meta: {
title: '光字牌项目',
hidden: false,
treeHidden: false,
bottomHidden: false,
menuIcon: 'Operation'
},
children: [
{
path: '/lightBoard/index',
name: 'lightBoardIndex',
component: () => import('@/views/preview/lightBoard/index.vue'),
meta: {
title: '光字牌',
hidden: false,
treeHidden: false,
bottomHidden: false,
menuIcon: 'Setting'
}
}
]
},
{ {
path: '/test', //F:\vue\workspace\maotu-webtopo\src\views\preview\systemInfo\index.vue path: '/test', //F:\vue\workspace\maotu-webtopo\src\views\preview\systemInfo\index.vue
name: 'test', name: 'test',

@ -47,6 +47,58 @@ export const getIndex = (num: number) => {
return -1; return -1;
} }
}; };
/**
*
* @param num Nodesig
* @returns
*/
export const getNodeStatus = (sig: number) => {
switch (sig) {
case -2000:
return '未初始化状态';
case 0:
return '正常';
case 1:
return '开启';
case 5:
return '联动关';
case 10:
return '高联动';
case -10:
return '低联动';
case 20:
return '高限告警';
case -20:
return '低限告警';
default:
return '未知状态';
}
};
/**
*
* @param num Nodesig
* @returns
*/
export const getNodeColor = (sig: number) => {
switch (sig) {
case -2000:
return 'info';
case 0:
case 1:
return 'primary';
case 5:
case 10:
case -10:
return 'warning';
case 20:
case -20:
return 'danger';
default:
return 'info';
}
};
// 四遥基本类型 // 四遥基本类型
Rec.Node.TypeBase = [ Rec.Node.TypeBase = [
[1, '遥信'], [1, '遥信'],
@ -139,7 +191,7 @@ Rec.EnumTypeVal[14] = [
[2, '送风'] [2, '送风']
]; ];
Rec.EnumTypeValFun[14] = function (val: any) { Rec.EnumTypeValFun[14] = function (val: any, index: number) {
return Rec.trans!(val, Rec.EnumTypeVal![14]); return Rec.trans!(val, Rec.EnumTypeVal![14]);
}; };
@ -152,7 +204,7 @@ Rec.EnumTypeVal[11] = [
[4, '送风'] [4, '送风']
]; ];
Rec.EnumTypeValFun[11] = function (val: any) { Rec.EnumTypeValFun[11] = function (val: any, index: number) {
return Rec.trans!(val, Rec.EnumTypeVal![11]); return Rec.trans!(val, Rec.EnumTypeVal![11]);
}; };
@ -175,7 +227,7 @@ Rec.EnumTypeVal[12] = [
[30, '30℃'] [30, '30℃']
]; ];
Rec.EnumTypeValFun[12] = function (val: any) { Rec.EnumTypeValFun[12] = function (val: any, index: number) {
return Rec.trans!(val, Rec.EnumTypeVal![12]); return Rec.trans!(val, Rec.EnumTypeVal![12]);
}; };
@ -187,7 +239,7 @@ Rec.EnumTypeVal[13] = [
[3, '高'] [3, '高']
]; ];
Rec.EnumTypeValFun[13] = function (val: any) { Rec.EnumTypeValFun[13] = function (val: any, index: number) {
return Rec.trans!(val, Rec.EnumTypeVal![13]); return Rec.trans!(val, Rec.EnumTypeVal![13]);
}; };
@ -198,7 +250,7 @@ Rec.EnumTypeVal[21] = [
[2, '制热'] [2, '制热']
]; ];
Rec.EnumTypeValFun[21] = function (val: any) { Rec.EnumTypeValFun[21] = function (val: any, index: number) {
return Rec.trans!(val, Rec.EnumTypeVal![21]); return Rec.trans!(val, Rec.EnumTypeVal![21]);
}; };
// //

@ -85,7 +85,6 @@ async function getData() {
if (data && typeof data === 'object') { if (data && typeof data === 'object') {
// 判断当前 globalData 类型并相应处理 // 判断当前 globalData 类型并相应处理
const currentGlobalData = window.globalData; const currentGlobalData = window.globalData;
debugger;
currentGlobalData.clear(); currentGlobalData.clear();
Object.entries(data).forEach(([key, value]) => { Object.entries(data).forEach(([key, value]) => {

@ -0,0 +1,162 @@
export interface Paragraph {
field: string;
value: string;
}
export const invariantObjMap: Record<string, Paragraph[]> = {
// 火灾报警系统主机
fireAlarmHost: [
{ field: '名称', value: '火灾报警系统主机' },
{ field: '产品型号', value: 'JB-QB-GST200' },
{ field: '厂商', value: '海湾公司' },
{ field: '运行日期', value: '2023-04-01' },
{ field: '电源电压', value: '220V AC' },
{ field: '频率', value: '50Hz' },
{ field: '输出电压', value: '24V DC' },
{ field: '输出电流', value: '5A' },
{ field: '环境温度', value: '-5℃ ~ 45℃' },
{ field: '相对湿度', value: '5% ~ 95%' },
{ field: '维护单位', value: '陕西消防科技有限公司' },
{ field: '联系电话', value: '188*****00' }
],
// 主机
host: [
{ field: '名称', value: 'PAVLN排油注氮智能防护系统控制主机' },
{ field: '型号', value: 'JB-QB-GST200' },
{ field: '厂家', value: '海湾公司' },
{ field: '运行日期', value: '2015-10-10' },
{ field: '电源电压', value: 'DC 220V 5A/AC 220V 5A' },
{ field: '频率', value: '27~1600MHz' },
{ field: '输出电压', value: '24V DC' },
{ field: '输出电流', value: '5A' },
{ field: '环境温度', value: '-5℃ ~ 45℃' },
{ field: '相对湿度', value: '5% ~ 95%' },
{ field: '维护单位', value: '陕西消防科技有限公司' },
{ field: '联系电话', value: '188*****00' }
],
// 2号主变油色谱
oilChromatography: [
{ field: '厂家', value: '宁波理工' },
{ field: '型号', value: 'MGA2000-6H' },
{ field: '投运日期', value: '2015年10月27日' },
{ field: '工作电源', value: 'AC220V,50Hz' },
{ field: '工作环境温度', value: '-40°C~+80°C' },
{ field: '工作相对湿度', value: '5%~95%' }
],
//2号主变局放
partialDischarge: [
{ field: '厂家', value: '上海思瑞在线监测技术有限公司' },
{ field: '型号', value: 'MGA2000-6H' },
{ field: '投运日期', value: '2015年10月16日' },
{ field: '局放测量通道', value: '3个局放测量通道+1个噪音测量通道' },
{ field: '局放传感器频宽', value: '300MHZ~2000MHZ' },
{ field: '工作环境温度', value: '-25°C~+55°C' },
{ field: '工作相对湿度', value: '5%~95%' }
],
//2215开关间隔
switchGap: [
{ field: '厂家', value: '许继电气股份有限公司' },
{ field: '型号', value: 'DPD-801' },
{ field: '投运日期', value: '2015年11月17日' },
{ field: '局放测量通道', value: '3个局放测量通道+1个噪音测量通道' },
{ field: '工作环境温度', value: '-40°C~+70°C' },
{ field: '工作相对湿度', value: '5%~95%' }
]
};
const threeDPath = {
smokeSiren: '/models/smokeSiren/smokeSiren.glb',
default: '/models/default/default.glb'
};
export const invariant3DObjMap: Record<string, Paragraph[]> = {
// 烟感报警器
smokeSiren: [
{ field: '3D模型', value: threeDPath.smokeSiren },
{ field: '厂家', value: '海湾公司' },
{ field: '型号', value: 'VLP-666-CH标准型' },
{ field: '投运日期', value: '2014-06-08' },
{ field: '布设位置', value: '35kV高压室' },
{ field: '供电电压', value: '18-30VDC' },
{ field: 'IP等级', value: 'IP30' },
{ field: '接线方式', value: '两线制(L+、L-)' },
{ field: '尺寸(长x高x宽)', value: '350mmx225mmx125mm' },
{ field: '报警灵敏度范围', value: '0.005%至20%obs/m' },
{ field: '运行条件1', value: '探测器环境(-5°C至45°C)' },
{ field: '运行条件2', value: '采样空气(-20°C至60°C)' },
{ field: '运行条件3', value: '湿度(5%至95%RH)' }
],
// 温感火灾探测器
temperatureFireDetector: [
{ field: '3D模型', value: threeDPath.default },
{ field: '厂家', value: '海湾公司' },
{ field: '型号', value: 'CT1-155X' },
{ field: '投运日期', value: '2012-08-08' },
{ field: '使用环境', value: '温度-5°C~45°C' },
{ field: '火灾响应规模', value: '10mm' },
{ field: '测量温度精度', value: '≤1°C' },
{ field: '类型', value: '定温式' },
{ field: '报警温度', value: '85°C' },
{ field: '标准', value: 'GB16280-2014' }
],
// 消火栓
fireHydrant: [
{ field: '3D模型', value: threeDPath.default },
{ field: '厂家', value: '宏达消防设备制造有限公司' },
{ field: '型号', value: 'SN-65' },
{ field: '投运日期', value: '2016-10-10' },
{ field: '布设位置', value: '生产综合楼1楼走廊' },
{ field: '质量保证书类型', value: '型式认证' },
{ field: '质保证书', value: 'NO2015-2032' },
{ field: '维保单位', value: '陕西消防科技有限公司' },
{ field: '联系电话', value: '188*****00' }
],
// 灭火器
fireExtinguisher: [
{ field: '3D模型', value: threeDPath.default },
{ field: '厂家', value: '天河消防厂' },
{ field: '型号', value: 'MFZ35' },
{ field: '投运日期', value: '2012-08-08' },
{ field: '布设位置', value: '生产综合楼1楼35kV高压室' },
{ field: '名称', value: '推车式干粉灭火器' },
{ field: '品牌', value: '淮海' },
{ field: '灭火剂', value: '碳酸铵盐50%、硫酸铵25%、滑石粉25%' },
{ field: '灭火级别', value: 'ABCD' }
],
// 疏散指示灯
evacuationSign: [
{ field: '3D模型', value: threeDPath.default },
{ field: '厂家', value: 'XXX消防设备有限公司' },
{ field: '型号', value: 'BSD128F(自带应急照明)' },
{ field: '投运日期', value: '2013-08-06' },
{ field: '布设位置', value: '110kV保护室' },
{ field: '输入电压', value: 'AC220V(+10%)' },
{ field: '频率', value: '50Hz' },
{ field: '功率', value: '<3W' },
{ field: '应急时间', value: '>=90min' },
{ field: '转换时间', value: '<=10s' },
{ field: '充电时间', value: '>=24h' },
{ field: '电池类型', value: 'Ni-Cd 1.2v 800mAh' }
],
// 红外对射
infraredBeam: [
{ field: '3D模型', value: threeDPath.default },
{ field: '厂家', value: 'XX霍尼维尔' },
{ field: '生产型号', value: 'DT-8041' },
{ field: '供电电源', value: '9.0-15VDC' },
{ field: '微波频率', value: '10.525GHz' },
{ field: '工作温度', value: '-10°C~55°C' },
{ field: '相对湿度', value: '5至93%' },
{ field: '探测范围', value: '12m' }
],
// 门禁控制器
accessController: [
{ field: '3D模型', value: threeDPath.default },
{ field: '生产厂家', value: 'Pegasus' },
{ field: '生产型号', value: 'PP6750V' },
{ field: '投运日期', value: '2017-05-09' },
{ field: '电源电压', value: 'DC12V' },
{ field: '工作电流', value: '100mA' },
{ field: '管理门数', value: '4' }
]
};

@ -21,7 +21,102 @@ export const buildApiUrl = (path: string) => {
}; };
export const modelApi = { export const modelApi = {
// http://localhost:8080/monitor/info // http://localhost:8080/data/lightModel/getLightBoardList
getLightBoardList_post(endJson: any): Promise<ApiResponse> {
return fetch(buildApiUrl('/data/lightModel/getLightBoardList'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: '*/*'
},
body: JSON.stringify(endJson)
}).then((response) => {
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.json();
});
},
changeNodeClass_post(endJson: any): Promise<ApiResponse> {
return fetch(buildApiUrl('/data/tree/changeNodeClass'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: '*/*'
},
body: JSON.stringify(endJson)
}).then((response) => {
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.json();
});
},
getNodeByCls_get(clsId: string): Promise<ApiResponse> {
return fetch(buildApiUrl(`/data/tree/getNodeByCls?clsId=${clsId}`), {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Accept: '*/*'
}
}).then((response) => {
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.json();
});
},
deleteTreeNode_post(endJson: any): Promise<ApiResponse> {
return fetch(buildApiUrl('/data/tree/deleteTreeNode'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: '*/*'
},
body: JSON.stringify(endJson)
}).then((response) => {
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.json();
});
},
changeTreeNode_post(endJson: any): Promise<ApiResponse> {
return fetch(buildApiUrl('/data/tree/changeTreeNode'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: '*/*'
},
body: JSON.stringify(endJson)
}).then((response) => {
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.json();
});
},
addTreeNode_post(endJson: any): Promise<ApiResponse> {
return fetch(buildApiUrl('/data/tree/addTreeNode'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: '*/*'
},
body: JSON.stringify(endJson)
}).then((response) => {
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.json();
});
},
allTree_get(): Promise<ApiResponse> {
return fetch(buildApiUrl('/data/tree/allTree'), {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Accept: '*/*'
}
}).then((response) => {
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
return response.json();
});
},
monitor_info_get(): Promise<ApiResponse> { monitor_info_get(): Promise<ApiResponse> {
return fetch(buildApiUrl('/monitor/info'), { return fetch(buildApiUrl('/monitor/info'), {
method: 'GET', method: 'GET',

@ -287,7 +287,9 @@ class StompService {
break; break;
case MqttCmd.Node: case MqttCmd.Node:
debugger; console.log('[STOMP] 收到 Node 更新消息:', msgObject.value);
window.vueGlobalFunction(msgObject.value);
// 单一 Node 更新消息 // 单一 Node 更新消息
if (this.services.node) { if (this.services.node) {
this.services.nod; this.services.nod;

@ -0,0 +1,668 @@
<template>
<div class="layout-container">
<el-row :gutter="10">
<el-col :span="6">
<el-card style="height: 100%">
<template #header>节点分区</template>
<el-row :gutter="10" style="margin-bottom: 2%">
<el-col :span="24">
<el-input
v-model="filterText"
style="width: 80%; height: 30px"
placeholder="Filter keyword"
/>
</el-col>
</el-row>
<el-row :gutter="10" style="margin-bottom: 4%">
<el-col :span="6">
<el-button type="primary" size="small" @click="dialogFormVisible = true"
>添加</el-button
>
</el-col>
<el-col :span="6">
<el-button type="primary" size="small" @click="getAllTree"></el-button>
</el-col>
</el-row>
<div style="height: 65vh">
<el-scrollbar height="100%">
<el-tree
ref="treeRef"
style="max-width: 100%; height: 100%"
class="filter-tree"
:data="treeData"
:props="defaultProps"
:filter-node-method="filterNode"
highlight-current
:expand-on-click-node="false"
@node-click="handleNodeClick"
>
<template #default="{ node, data }">
<div class="tree-node">
<span class="tree-node-label">{{ node.label }}</span>
<span class="tree-node-actions">
<el-button link type="primary" size="small" @click.stop="handleAdd(data)"
>添加</el-button
>
<el-button link type="primary" size="small" @click.stop="handleEdit(data)"
>编辑</el-button
>
<el-button link type="primary" size="small" @click.stop="handleDelete(data)"
>删除</el-button
>
</span>
</div>
</template>
</el-tree>
</el-scrollbar>
</div>
</el-card>
</el-col>
<el-col :span="11">
<el-card style="height: 100%" class="center-car">
<template #header>节点列表</template>
<el-input
v-model="centerSearch"
style="width: 60%; height: 25px; margin-bottom: 2%"
placeholder="节点名称"
/>
<el-tag type="primary" v-if="treeNodeId">{{ treeNodeName }}</el-tag>
<el-tag type="info" v-else></el-tag>
<el-table
ref="centerTableRef"
@selection-change="handleCenterSelectionChange"
:data="centerFilteredData"
style="width: 100%; height: 73vh"
row-key="modeId"
class="center-table"
>
<el-table-column type="selection" width="30" />
<el-table-column prop="modeId" label="遥 ID" width="70" />
<el-table-column prop="name" label="名称" />
<el-table-column prop="bType" label="类型" width="100">
<template #default="scope">
<el-tag type="primary" v-if="scope.row.bType === 1">{{ '' }}</el-tag>
<el-tag type="success" v-else-if="scope.row.bType === 2">{{ '遥测' }}</el-tag>
<el-tag type="info" v-else-if="scope.row.bType === 3">{{ '遥控' }}</el-tag>
<el-tag type="warning" v-else-if="scope.row.bType === 4">{{ '遥调' }}</el-tag>
<el-tag type="danger" v-else>{{ '' }}</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
<!-- -->
<el-col :span="7">
<el-card style="height: 100%">
<template #header>所有节点</template>
<el-input
v-model="search"
style="width: 60%; height: 25px; margin-bottom: 2%"
placeholder="节点名称"
/>
<el-table
ref="rightTableRef"
@selection-change="handleSelectionChange"
:data="filteredData"
style="width: 100%; height: 73vh"
class="right-table"
>
<el-table-column type="selection" width="30" />
<el-table-column prop="modeId" label="遥ID" width="70" />
<el-table-column prop="name" label="名称" />
<el-table-column
v-if="inputTypeTagValue == undefined"
prop="bType"
label="类型筛选"
width="100"
:filters="[
{ text: '遥信', value: 1 },
{ text: '遥测', value: 2 },
{ text: '遥控', value: 3 },
{ text: '遥调', value: 4 },
{ text: '其他', value: 0 }
]"
:filter-method="filterTag"
filter-placement="bottom-end"
filter-confirm-button-text="确定"
filter-reset-button-text="重置"
>
<template #default="scope">
<el-tag type="primary" v-if="scope.row.bType === 1">{{ '' }}</el-tag>
<el-tag type="success" v-else-if="scope.row.bType === 2">{{ '遥测' }}</el-tag>
<el-tag type="info" v-else-if="scope.row.bType === 3">{{ '遥控' }}</el-tag>
<el-tag type="warning" v-else-if="scope.row.bType === 4">{{ '遥调' }}</el-tag>
<el-tag type="danger" v-else>{{ '' }}</el-tag>
</template>
</el-table-column>
<el-table-column v-if="inputTypeTagValue != undefined" label="类型">
<template #default="scope">
<el-tag type="primary" v-if="scope.row.bType === 1">{{ '' }}</el-tag>
<el-tag type="success" v-else-if="scope.row.bType === 2">{{ '遥测' }}</el-tag>
<el-tag type="info" v-else-if="scope.row.bType === 3">{{ '遥控' }}</el-tag>
<el-tag type="warning" v-else-if="scope.row.bType === 4">{{ '遥调' }}</el-tag>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
</el-row>
</div>
<el-dialog v-model="dialogFormVisible" title="添加树节点" width="500">
<el-form :model="form" :rules="rules" ref="formRef">
<el-form-item label="子节点名" prop="label">
<el-input v-model="form.label" placeholder="请输入子节点名称" />
</el-form-item>
<el-form-item label="父节点">
<el-input-tag
v-model="form.parentIdLable"
disabled
placeholder="默认根节点"
:tag-type="'primary'"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="form.label = ''">重置</el-button>
<el-button type="primary" @click="handleConfirmAdd"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, watch, onMounted, reactive, computed, nextTick } from 'vue';
import { modelApi } from '@/utils/request';
import { ElMessage } from 'element-plus';
import type { FilterNodeMethodFunction, TreeInstance } from 'element-plus';
import { type DataItem } from '@/components/mt-edit/store/types';
import type { AnyColumns } from 'element-plus/es/components/table-v2/src/types.mjs';
import Sortable from 'sortablejs';
import { fa } from 'element-plus/es/locales.mjs';
interface Tree {
[key: string]: any;
}
interface GridDataItem {
modeId: string;
name: string;
bType: number;
}
interface ExtendedParentWindow {
globalData: Map<string, DataItem> | Record<string, DataItem>;
}
interface ExtendedWindow extends Window {
draggingItems?: GridDataItem[];
}
let dialogFormVisible = ref(false);
const filterText = ref('');
const treeRef = ref<TreeInstance>();
const formRef = ref(); //
let inputTypeTagValue = ref<string>();
const search = ref('');
const centerSearch = ref(''); //
const rightTableRef = ref(); //
const centerTableRef = ref(); //
const gridData = ref<GridDataItem[]>([]);
const centerData = ref<GridDataItem[]>([]); //
let treeNodeId = ref<string | null>(null);
let treeNodeName = ref<string | null>(null);
const filterTag = (value: number, row: any) => {
return row.bType === value;
};
const defaultProps = {
children: 'children',
label: 'label',
parentId: 'parentId'
};
const form = reactive({
nodeId: null,
label: '',
parentIdLable: [] as string[], // 使 el-input-tag
parentId: '' as string
});
//
const rules = {
label: [
{ required: true, message: '请输入子节点名称', trigger: 'blur' },
{ min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' }
]
};
const multipleSelection = ref<GridDataItem[]>([]);
const handleSelectionChange = (val: GridDataItem[]) => {
multipleSelection.value = val;
console.log('右侧表格选中:', multipleSelection.value);
};
const centerSelection = ref<GridDataItem[]>([]);
const handleCenterSelectionChange = (val: GridDataItem[]) => {
centerSelection.value = val;
console.log('中间表格选中:', centerSelection.value);
};
//
const centerFilteredData = computed(() => {
if (!centerSearch.value) {
return centerData.value;
}
return centerData.value.filter((item) => {
return item.name.toLowerCase().includes(centerSearch.value.toLowerCase());
});
});
//
const filteredData = computed(() => {
if (!search.value) {
return gridData.value;
}
return gridData.value.filter((item) => {
return item.name.toLowerCase().includes(search.value.toLowerCase());
});
});
//
function assembleList() {
let code: number | undefined;
if (inputTypeTagValue.value !== undefined) code = getNameForNode(inputTypeTagValue.value);
gridData.value.splice(0, gridData.value.length);
let globalDataRaw = (window as unknown as ExtendedParentWindow).globalData;
if (!globalDataRaw) return;
// globalData Map
if (globalDataRaw instanceof Map) {
// Map
globalDataRaw.forEach((data, key) => {
if (code == undefined || code == data.bType) {
let modeId = key;
let name = data.node.name;
let bType = data.bType;
gridData.value.push({ modeId, name, bType });
}
});
} else {
//
for (const key in globalDataRaw) {
if (Object.prototype.hasOwnProperty.call(globalDataRaw, key)) {
const data = globalDataRaw[key];
if (data && (code == undefined || code == data.bType)) {
let modeId = key;
let name = data.node.name;
let bType = data.bType;
gridData.value.push({ modeId, name, bType });
}
}
}
}
}
function getNameForNode(nodeName: string): number {
switch (nodeName) {
case '遥信':
return 1;
case '遥测':
return 2;
case '遥控':
return 3;
case '遥调':
return 4;
default:
return 0;
}
}
watch(filterText, (val) => {
treeRef.value!.filter(val);
});
const filterNode: FilterNodeMethodFunction = (value: string, data: Tree) => {
if (!value) return true;
return data.label.includes(value);
};
//
const handleAdd = (data: Tree) => {
console.log('添加子节点:', data);
let tag = ' 父节点: ' + data.label;
form.parentIdLable = [tag]; //
form.parentId = data.id;
dialogFormVisible.value = true;
// addTreeNode(data.parentId, data.label);
};
const handleEdit = (data: Tree) => {
console.log('编辑节点:', data);
form.nodeId = data.id;
form.label = data.label;
dialogFormVisible.value = true;
};
//
const handleConfirmAdd = async () => {
if (!formRef.value) return;
await formRef.value.validate(async (valid: boolean) => {
if (!valid) return;
try {
// tag parentId
if (!form.nodeId) {
const parentId = form.parentId ? form.parentId : '0';
await addTreeNode(parentId, form.label);
} else {
await changeTreeNode(form.nodeId, form.label);
}
dialogFormVisible.value = false;
form.label = ''; //
form.parentId = '';
form.nodeId = null;
form.parentIdLable = [];
} catch (error) {
ElMessage.error('添加失败');
}
});
};
//
const handleDelete = (data: Tree) => {
deleteTreeNode(data.id);
};
async function getNodeByCls(nodeClassId: string) {
let response = await modelApi.getNodeByCls_get(nodeClassId);
if (response.code == 200 && response.data != null) {
centerData.value.splice(0, centerData.value.length);
let globalDataRaw = (window as unknown as ExtendedParentWindow).globalData as Map<
string,
DataItem
>;
response.data.forEach((item: any) => {
let dataItem = globalDataRaw.get(item.id.toString());
if (dataItem) {
let data: GridDataItem = {
modeId: dataItem.id.toString(),
name: dataItem.node.name,
bType: dataItem.bType
};
centerData.value.push(data);
}
});
}
}
const handleNodeClick = (data: Tree) => {
console.log('点击节点:', data);
treeNodeId.value = data.id;
treeNodeName.value = data.label;
getNodeByCls(data.id);
};
const treeData = ref<Tree[]>([]);
async function changeTreeNode(id: string, label: string) {
const response = await modelApi.changeTreeNode_post({ id, label });
if (response.code != 200 || response.data == null) {
ElMessage.error(response.message);
} else {
ElMessage.success('修改成功');
getAllTree();
}
}
async function deleteTreeNode(id: string) {
const response = await modelApi.deleteTreeNode_post({ id });
if (response.code != 200 || response.data == null) {
ElMessage.error(response.message);
} else {
ElMessage.success('删除成功');
getAllTree();
}
}
async function addTreeNode(parentId: string, label: string) {
const response = await modelApi.addTreeNode_post({ parentId, label });
if (response.code != 200 || response.data == null) {
ElMessage.error(response.message);
} else {
ElMessage.success('添加成功1');
getAllTree();
}
}
async function getAllTree() {
const response = await modelApi.allTree_get();
if (response.code != 200 || response.data == null) {
ElMessage.error(response.message);
} else {
treeData.value = response.data;
// ElMessage.success('');
}
}
async function changeNodeClass(nodeClassId: string, nodeIds: string[]) {
const response = await modelApi.changeNodeClass_post({ nodeClassId, nodeIds });
if (response.code != 200 || response.data == null) {
ElMessage.error(response.message);
} else {
getNodeByCls(nodeClassId);
ElMessage.success('操作成功');
}
}
onMounted(async () => {
assembleList();
getAllTree();
//
await nextTick();
initDraggable();
});
//
const initDraggable = () => {
// 使 class body
const rightTableBody = document.querySelector('.right-table .el-table__body-wrapper tbody');
const centerTableBody = document.querySelector('.center-table .el-table__body-wrapper tbody');
console.log('右侧表格 body:', rightTableBody);
console.log('中间表格 body:', centerTableBody);
if (!rightTableBody || !centerTableBody) {
console.error('未找到表格元素');
return;
}
//
Sortable.create(rightTableBody as HTMLElement, {
group: {
name: 'shared',
pull: 'clone', //
put: false //
},
animation: 150,
ghostClass: 'sortable-ghost',
chosenClass: 'sortable-chosen',
dragClass: 'sortable-drag',
onEnd: (evt: any) => {
console.log('右侧表格拖拽结束', evt);
if (!treeNodeId.value || treeNodeId.value == '') {
ElMessage.warning('请先选择树节点');
return;
}
// Sortable.js clone bug
// Sortable DOM
//
const { target, clone, item } = evt;
if (target && clone && item) {
//
target.insertBefore(item, clone);
//
target.removeChild(clone);
}
//
const isFromRightTable = evt.from === rightTableBody;
const isToCenterTable = evt.to === centerTableBody;
//
if (isFromRightTable && isToCenterTable) {
// 使使
const itemsToAdd =
multipleSelection.value.length > 0
? multipleSelection.value
: [gridData.value[evt.oldIndex]];
let addedCount = 0;
let nodeIds: string[] = [];
itemsToAdd.forEach((item) => {
if (!centerData.value.find((d) => d.modeId === item.modeId)) {
//
// centerData.value.push({ ...item });
nodeIds.push(item.modeId);
addedCount++;
}
});
if (addedCount > 0) {
// ID:
console.log('已添加的节点 ID:', nodeIds);
changeNodeClass(treeNodeId.value, nodeIds);
ElMessage.success(`已添加 ${addedCount} 项到节点列表`);
//
if (rightTableRef.value) {
rightTableRef.value.clearSelection();
}
} else {
ElMessage.info('所有选中项已存在');
}
}
}
});
//
Sortable.create(centerTableBody as HTMLElement, {
group: {
name: 'shared',
pull: false, //
put: true //
},
// animation: 150,
// ghostClass: 'sortable-ghost',
// chosenClass: 'sortable-chosen',
// dragClass: 'sortable-drag',
onAdd: (evt: any) => {
console.log('中间表格 onAdd', evt);
//
//
if (evt.from !== centerTableBody) {
// Sortable DOM
// Vue
const draggedElement = evt.item;
if (draggedElement && draggedElement.parentNode) {
draggedElement.parentNode.removeChild(draggedElement);
}
}
},
onEnd: (evt: any) => {
console.log('中间表格拖拽结束', evt);
if (evt.from === centerTableBody && evt.to !== centerTableBody) {
//
centerData.value.splice(evt.oldIndex, 1);
ElMessage.info('已移除');
}
}
});
};
</script>
<style scoped>
.layout-container {
width: 100%;
height: 100%;
overflow: hidden;
}
/* 修复 el-row gutter 导致的横向滚动条 */
:deep(.el-row) {
margin-left: 0 !important;
margin-right: 0 !important;
}
/* 树节点样式 */
.tree-node {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding-right: 8px;
}
.tree-node-label {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tree-node-actions {
display: flex;
gap: 4px;
opacity: 0;
transition: opacity 0.3s;
}
/* 鼠标悬停时显示操作按钮 */
.tree-node:hover .tree-node-actions {
opacity: 1;
}
/* 拖拽样式 */
.sortable-ghost {
opacity: 0.4;
background-color: #f5f7fa;
border: 2px dashed #409eff;
}
.sortable-chosen {
background-color: #ecf5ff;
cursor: grabbing !important;
}
.sortable-drag {
opacity: 0.8;
}
/* 表格行可拖拽样式 */
:deep(.right-table .el-table__row),
:deep(.center-car .el-table__row),
:deep(.center-table .el-table__row) {
cursor: grab;
}
:deep(.right-table .el-table__row:hover),
:deep(.center-car .el-table__row:hover),
:deep(.center-table .el-table__row:hover) {
/* background-color: #f5f7fa; */
}
</style>

@ -0,0 +1,247 @@
<template>
<div class="layout-container">
<el-row :gutter="10" style="margin-bottom: 2vh">
<el-col :span="24">
<el-card class="card-form">
<el-form :inline="true" class="form-inline" label-width="auto">
<el-form-item label="光字牌 ID">
<el-input v-model="formInline.lightBoardId" placeholder="光字牌 ID" clearable />
</el-form-item>
<el-form-item label="光字牌名称">
<el-input v-model="formInline.lightBoardName" placeholder="光字牌名称" clearable />
</el-form-item>
<el-form-item style="margin-left: auto">
<el-button type="primary">清除</el-button>
<el-button type="primary">查询</el-button>
</el-form-item>
</el-form>
</el-card>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="24">
<el-card class="card-table">
<el-button @click="getLightBoardList" style="margin-bottom: 10px">刷新</el-button>
<div style="height: calc(85%)">
<el-table
:data="lightBoardList"
:border="true"
:preserve-expanded-content="preserveExpanded"
:tooltip-formatter="tableRowFormatter"
show-overflow-tooltip
style="width: 100%; height: 500px"
>
<el-table-column type="expand">
<template #default="props">
<div m="4">
<p style="display: flex; align-items: center;">实际绑定节点<el-link style="margin: 0;padding: 0;" type="primary">{{ props.row.lightBoardStartNum-1 }}</el-link></p>
<el-button @click="bindStatus(props.row)"></el-button>
<h3>状态列表</h3>
<el-table :data="props.row.lightBoardStatusVOList" :border="false">
<el-table-column label="状态ID" prop="id" />
<el-table-column label="节点ID" prop="nodeId">
<template #default="scope">
<el-tag v-if="scope.row.nodeId" type="primary">{{
scope.row.nodeId
}}</el-tag>
<el-tag v-else type="info">无节点ID</el-tag>
</template>
</el-table-column>
<el-table-column label="状态名称" prop="lightBoardStart" />
<el-table-column label="状态权重" prop="weightNum" />
<el-table-column label="当前信号" prop="currentSignal" >
<template #default="item">
<el-tag v-if="item.row.currentSignal==false" type="success"></el-tag>
<el-tag v-else type="danger">出现信号</el-tag>
</template>
</el-table-column>
</el-table>
</div>
</template>
</el-table-column>
<el-table-column label="光字牌ID" prop="id" />
<el-table-column label="光字牌名称" prop="lightBoardName" />
<el-table-column label="绑定状态数量" prop="lightBoardStartNum" />
<el-table-column
prop="lightBoardTags"
label="绑定状态名称-tag"
width="240"
:tooltip-formatter="tableRowTooltipFormatter"
>
<template #default="{ row }">
<el-tag
v-for="tag in row.lightBoardTags"
:key="tag"
class="tag-item"
type="primary"
>
{{ tag }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="权重样式" prop="currentStatarTag" />
</el-table>
</div>
<div style="margin-top: 10px">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 15, 20]"
:pager-count="3"
size="default"
layout="total,sizes,-> ,prev, pager, next"
:total="pageTotal"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-card>
</el-col>
</el-row>
</div>
<NodeListDialog v-model="dialogTableVisible" />
</template>
<script setup lang="ts">
import { ref, reactive ,onMounted} from 'vue';
import { ElMessage, ElLink, type TableTooltipData } from 'element-plus';
import { modelApi } from '@/utils/request';
import NodeListDialog from '@/components/public-compoents/node-list-dialog.vue';
const dialogTableVisible = ref(false);
let currentPage = ref(1); //
let pageSize = ref(10); //
let pageTotal = ref(0); //
const handleSizeChange = (val: number) => {
console.log('size点击:', `${val} items per page`);
getLightBoardList();
};
const handleCurrentChange = (val: number) => {
console.log('Current点击:', `current page: ${val}`);
getLightBoardList();
};
const tableRowFormatter = (data: TableTooltipData<LightBoardList>) => {
return `${data.cellValue}: table formatter`;
};
const tableRowTooltipFormatter = ({ row }: { row: LightBoardList }) => {
return row.lightBoardTags.join(', ');
};
const bindStatus = (row: LightBoardList) => {
console.log('key:', dialogTableVisible.value);
console.log('绑定状态:', row);
dialogTableVisible.value=true
};
//
interface LightBoardList {
id: string;
lightBoardName: string; //
stalightBoardStartNumte: string; //
lightBoardTags: string[]; //
currentStatarTag: string; //
lightBoardStatusVOList: LightBoardStatusVO[]; //
}
interface LightBoardStatusVO {
id: string; //ID
lightBoardId: string; //ID
nodeId: string; //ID
lightBoardStart: string; //
weightNum: string; //
currentSignal: string; //
}
const preserveExpanded = ref(false);
const formInline = reactive({
lightBoardId: '', //ID
lightBoardName: '' //
});
const lightBoardList = ref<LightBoardList[]>([]);
async function getLightBoardList() {
let endJson = {
pageNum: currentPage.value,
pageSize: pageSize.value
};
const response = await modelApi.getLightBoardList_post(endJson);
if (response.code != 200 || response.data == null) {
ElMessage.error(response.message);
} else {
lightBoardList.value.length = 0;
lightBoardList.value = response.data.list;
pageTotal.value = response.data.total;
// pageSize.value = response.data.pageSize;
currentPage.value = response.data.current;
ElMessage.success('接口请求成功');
}
}
onMounted(() => {
getLightBoardList();
});
</script>
<style lang="less" scoped>
.layout-container {
width: 99%;
height: 94vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.form-inline {
width: 100%;
height: 45px;
display: flex;
align-items: center; /* 垂直居中 el-form-item */
}
/* 强制 el-form-item 垂直居中 */
:deep(.el-form-item) {
margin-top: 0 !important;
margin-bottom: 0 !important;
}
:deep(.el-card__body) {
padding: 5px;
}
.tag-item + .tag-item {
margin-left: 5px;
}
.card-form {
// margin-right: 1vh;
padding: 0;
}
.card-table {
height: calc(100% - 60px);
overflow: hidden;
}
/* 第二个卡片容器占满剩余空间 */
.el-row:nth-child(2) {
flex: 1;
height: 0;
}
/* 第二个卡片高度 100% */
.el-row:nth-child(2) .el-card {
height: 100%;
}
</style>
Loading…
Cancel
Save