fix:完成数据查看模块

This commit is contained in:
he-zhi-cheng
2025-08-11 14:21:39 +08:00
parent 5104d09674
commit a41a611975
30 changed files with 3988 additions and 690 deletions

258
package-lock.json generated
View File

@ -21,6 +21,7 @@
"maptalks": "^1.1.3",
"markdown-it": "^14.1.0",
"mitt": "^3.0.1",
"ol": "^10.6.1",
"serialport": "^12.0.0",
"vue": "^3.3.4",
"vue-drag-resize": "^2.0.3",
@ -687,6 +688,12 @@
"node": ">= 8"
}
},
"node_modules/@petamoriken/float16": {
"version": "3.9.2",
"resolved": "https://r.cnpmjs.org/@petamoriken/float16/-/float16-3.9.2.tgz",
"integrity": "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==",
"license": "MIT"
},
"node_modules/@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@ -1412,6 +1419,12 @@
"@types/lodash": "*"
}
},
"node_modules/@types/rbush": {
"version": "4.0.0",
"resolved": "https://r.cnpmjs.org/@types/rbush/-/rbush-4.0.0.tgz",
"integrity": "sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ==",
"license": "MIT"
},
"node_modules/@types/web-bluetooth": {
"version": "0.0.20",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
@ -1983,6 +1996,12 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/earcut": {
"version": "3.0.2",
"resolved": "https://r.cnpmjs.org/earcut/-/earcut-3.0.2.tgz",
"integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==",
"license": "ISC"
},
"node_modules/echart": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/echart/-/echart-0.1.3.tgz",
@ -2289,6 +2308,25 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/geotiff": {
"version": "2.1.3",
"resolved": "https://r.cnpmjs.org/geotiff/-/geotiff-2.1.3.tgz",
"integrity": "sha512-PT6uoF5a1+kbC3tHmZSUsLHBp2QJlHasxxxxPW47QIY1VBKpFB+FcDvX+MxER6UzgLQZ0xDzJ9s48B9JbOCTqA==",
"license": "MIT",
"dependencies": {
"@petamoriken/float16": "^3.4.7",
"lerc": "^3.0.0",
"pako": "^2.0.4",
"parse-headers": "^2.0.2",
"quick-lru": "^6.1.1",
"web-worker": "^1.2.0",
"xml-utils": "^1.0.2",
"zstddec": "^0.1.0"
},
"engines": {
"node": ">=10.19"
}
},
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
@ -2651,6 +2689,12 @@
"any-promise": "^1.1.0"
}
},
"node_modules/lerc": {
"version": "3.0.0",
"resolved": "https://r2.cnpmjs.org/lerc/-/lerc-3.0.0.tgz",
"integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==",
"license": "Apache-2.0"
},
"node_modules/less": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz",
@ -2985,6 +3029,38 @@
"integrity": "sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ==",
"dev": true
},
"node_modules/ol": {
"version": "10.6.1",
"resolved": "https://r.cnpmjs.org/ol/-/ol-10.6.1.tgz",
"integrity": "sha512-xp174YOwPeLj7c7/8TCIEHQ4d41tgTDDhdv6SqNdySsql5/MaFJEJkjlsYcvOPt7xA6vrum/QG4UdJ0iCGT1cg==",
"license": "BSD-2-Clause",
"dependencies": {
"@types/rbush": "4.0.0",
"earcut": "^3.0.0",
"geotiff": "^2.1.3",
"pbf": "4.0.1",
"rbush": "^4.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/openlayers"
}
},
"node_modules/ol/node_modules/quickselect": {
"version": "3.0.0",
"resolved": "https://r.cnpmjs.org/quickselect/-/quickselect-3.0.0.tgz",
"integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==",
"license": "ISC"
},
"node_modules/ol/node_modules/rbush": {
"version": "4.0.1",
"resolved": "https://r.cnpmjs.org/rbush/-/rbush-4.0.1.tgz",
"integrity": "sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ==",
"license": "MIT",
"dependencies": {
"quickselect": "^3.0.0"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@ -3001,6 +3077,18 @@
"resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz",
"integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ=="
},
"node_modules/pako": {
"version": "2.1.0",
"resolved": "https://r.cnpmjs.org/pako/-/pako-2.1.0.tgz",
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
"license": "(MIT AND Zlib)"
},
"node_modules/parse-headers": {
"version": "2.0.6",
"resolved": "https://r.cnpmjs.org/parse-headers/-/parse-headers-2.0.6.tgz",
"integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==",
"license": "MIT"
},
"node_modules/parse-node-version": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
@ -3038,6 +3126,18 @@
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
"dev": true
},
"node_modules/pbf": {
"version": "4.0.1",
"resolved": "https://r.cnpmjs.org/pbf/-/pbf-4.0.1.tgz",
"integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==",
"license": "BSD-3-Clause",
"dependencies": {
"resolve-protobuf-schema": "^2.1.0"
},
"bin": {
"pbf": "bin/pbf"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -3109,6 +3209,12 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/protocol-buffers-schema": {
"version": "3.6.0",
"resolved": "https://r2.cnpmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz",
"integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==",
"license": "MIT"
},
"node_modules/prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
@ -3152,6 +3258,18 @@
}
]
},
"node_modules/quick-lru": {
"version": "6.1.2",
"resolved": "https://r.cnpmjs.org/quick-lru/-/quick-lru-6.1.2.tgz",
"integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/quickselect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz",
@ -3202,6 +3320,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/resolve-protobuf-schema": {
"version": "2.1.0",
"resolved": "https://r2.cnpmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
"license": "MIT",
"dependencies": {
"protocol-buffers-schema": "^3.3.1"
}
},
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@ -3817,6 +3944,12 @@
"resolved": "https://registry.npmjs.org/vue3-konami-code/-/vue3-konami-code-1.0.0.tgz",
"integrity": "sha512-fhHAPFZA1jsgDHlYz5dnG52RmY9+vtfMthBaY1S5VBxNLdU4EWTbnJO0nfL8Uybw5uHsDqCGtTbGaX7nIvQ9Qw=="
},
"node_modules/web-worker": {
"version": "1.5.0",
"resolved": "https://r.cnpmjs.org/web-worker/-/web-worker-1.5.0.tgz",
"integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==",
"license": "Apache-2.0"
},
"node_modules/webpack-sources": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
@ -3832,6 +3965,12 @@
"integrity": "sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==",
"dev": true
},
"node_modules/xml-utils": {
"version": "1.10.2",
"resolved": "https://r.cnpmjs.org/xml-utils/-/xml-utils-1.10.2.tgz",
"integrity": "sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA==",
"license": "CC0-1.0"
},
"node_modules/ylru": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/ylru/-/ylru-1.4.0.tgz",
@ -3847,6 +3986,12 @@
"dependencies": {
"tslib": "2.3.0"
}
},
"node_modules/zstddec": {
"version": "0.1.0",
"resolved": "https://r.cnpmjs.org/zstddec/-/zstddec-0.1.0.tgz",
"integrity": "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==",
"license": "MIT AND BSD-3-Clause"
}
},
"dependencies": {
@ -4181,6 +4326,11 @@
"fastq": "^1.6.0"
}
},
"@petamoriken/float16": {
"version": "3.9.2",
"resolved": "https://r.cnpmjs.org/@petamoriken/float16/-/float16-3.9.2.tgz",
"integrity": "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog=="
},
"@popperjs/core": {
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
@ -4555,6 +4705,11 @@
"@types/lodash": "*"
}
},
"@types/rbush": {
"version": "4.0.0",
"resolved": "https://r.cnpmjs.org/@types/rbush/-/rbush-4.0.0.tgz",
"integrity": "sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ=="
},
"@types/web-bluetooth": {
"version": "0.0.20",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
@ -4977,6 +5132,11 @@
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
},
"earcut": {
"version": "3.0.2",
"resolved": "https://r.cnpmjs.org/earcut/-/earcut-3.0.2.tgz",
"integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ=="
},
"echart": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/echart/-/echart-0.1.3.tgz",
@ -5195,6 +5355,21 @@
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"dev": true
},
"geotiff": {
"version": "2.1.3",
"resolved": "https://r.cnpmjs.org/geotiff/-/geotiff-2.1.3.tgz",
"integrity": "sha512-PT6uoF5a1+kbC3tHmZSUsLHBp2QJlHasxxxxPW47QIY1VBKpFB+FcDvX+MxER6UzgLQZ0xDzJ9s48B9JbOCTqA==",
"requires": {
"@petamoriken/float16": "^3.4.7",
"lerc": "^3.0.0",
"pako": "^2.0.4",
"parse-headers": "^2.0.2",
"quick-lru": "^6.1.1",
"web-worker": "^1.2.0",
"xml-utils": "^1.0.2",
"zstddec": "^0.1.0"
}
},
"glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
@ -5483,6 +5658,11 @@
}
}
},
"lerc": {
"version": "3.0.0",
"resolved": "https://r2.cnpmjs.org/lerc/-/lerc-3.0.0.tgz",
"integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww=="
},
"less": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz",
@ -5724,6 +5904,33 @@
"integrity": "sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ==",
"dev": true
},
"ol": {
"version": "10.6.1",
"resolved": "https://r.cnpmjs.org/ol/-/ol-10.6.1.tgz",
"integrity": "sha512-xp174YOwPeLj7c7/8TCIEHQ4d41tgTDDhdv6SqNdySsql5/MaFJEJkjlsYcvOPt7xA6vrum/QG4UdJ0iCGT1cg==",
"requires": {
"@types/rbush": "4.0.0",
"earcut": "^3.0.0",
"geotiff": "^2.1.3",
"pbf": "4.0.1",
"rbush": "^4.0.0"
},
"dependencies": {
"quickselect": {
"version": "3.0.0",
"resolved": "https://r.cnpmjs.org/quickselect/-/quickselect-3.0.0.tgz",
"integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g=="
},
"rbush": {
"version": "4.0.1",
"resolved": "https://r.cnpmjs.org/rbush/-/rbush-4.0.1.tgz",
"integrity": "sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ==",
"requires": {
"quickselect": "^3.0.0"
}
}
}
},
"on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@ -5737,6 +5944,16 @@
"resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz",
"integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ=="
},
"pako": {
"version": "2.1.0",
"resolved": "https://r.cnpmjs.org/pako/-/pako-2.1.0.tgz",
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
},
"parse-headers": {
"version": "2.0.6",
"resolved": "https://r.cnpmjs.org/parse-headers/-/parse-headers-2.0.6.tgz",
"integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A=="
},
"parse-node-version": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
@ -5767,6 +5984,14 @@
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
"dev": true
},
"pbf": {
"version": "4.0.1",
"resolved": "https://r.cnpmjs.org/pbf/-/pbf-4.0.1.tgz",
"integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==",
"requires": {
"resolve-protobuf-schema": "^2.1.0"
}
},
"picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -5810,6 +6035,11 @@
"source-map-js": "^1.2.1"
}
},
"protocol-buffers-schema": {
"version": "3.6.0",
"resolved": "https://r2.cnpmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz",
"integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw=="
},
"prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
@ -5832,6 +6062,11 @@
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true
},
"quick-lru": {
"version": "6.1.2",
"resolved": "https://r.cnpmjs.org/quick-lru/-/quick-lru-6.1.2.tgz",
"integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ=="
},
"quickselect": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz",
@ -5871,6 +6106,14 @@
"supports-preserve-symlinks-flag": "^1.0.0"
}
},
"resolve-protobuf-schema": {
"version": "2.1.0",
"resolved": "https://r2.cnpmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
"requires": {
"protocol-buffers-schema": "^3.3.1"
}
},
"reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@ -6259,6 +6502,11 @@
"resolved": "https://registry.npmjs.org/vue3-konami-code/-/vue3-konami-code-1.0.0.tgz",
"integrity": "sha512-fhHAPFZA1jsgDHlYz5dnG52RmY9+vtfMthBaY1S5VBxNLdU4EWTbnJO0nfL8Uybw5uHsDqCGtTbGaX7nIvQ9Qw=="
},
"web-worker": {
"version": "1.5.0",
"resolved": "https://r.cnpmjs.org/web-worker/-/web-worker-1.5.0.tgz",
"integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw=="
},
"webpack-sources": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
@ -6271,6 +6519,11 @@
"integrity": "sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==",
"dev": true
},
"xml-utils": {
"version": "1.10.2",
"resolved": "https://r.cnpmjs.org/xml-utils/-/xml-utils-1.10.2.tgz",
"integrity": "sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA=="
},
"ylru": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/ylru/-/ylru-1.4.0.tgz",
@ -6283,6 +6536,11 @@
"requires": {
"tslib": "2.3.0"
}
},
"zstddec": {
"version": "0.1.0",
"resolved": "https://r.cnpmjs.org/zstddec/-/zstddec-0.1.0.tgz",
"integrity": "sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg=="
}
}
}

View File

@ -24,6 +24,7 @@
"maptalks": "^1.1.3",
"markdown-it": "^14.1.0",
"mitt": "^3.0.1",
"ol": "^10.6.1",
"serialport": "^12.0.0",
"vue": "^3.3.4",
"vue-drag-resize": "^2.0.3",

View File

@ -1 +1 @@
{"pathofsave":"C:\\Program Files\\lucamtool","Filename":"testaa","caijiavgNumber":"1","useSG":false,"usehighpass":false,"Dispatcher":{"isenable":true,"begin":"06:25","end":"23:59"},"sensor_typeforset":"IS3"}
{"pathofsave":null,"Filename":"testaa","caijiavgNumber":"1","useSG":false,"usehighpass":false,"Dispatcher":{"isenable":true,"begin":"06:25","end":"23:59"},"sensor_typeforset":"IRIS-IS11"}

BIN
src-tauri/data46.iris Normal file

Binary file not shown.

View File

@ -1,6 +1,4 @@
<script>
// This starter template is using Vue 3 <script setup> SFCs
// Check out https://vuejs.org/api/sfc-script-setup.html#script-setup
import Greet from "./components/Greet.vue";
import AppHyperSpectral from "./AppHyperSpectral.vue";
import APPDataview from "./DataView/APPDataview.vue";
@ -13,34 +11,35 @@ export default {
},
data() {
return {
modalcomponent: 'AppHyperSpectral',
compomentlist: ['AppHyperSpectral', 'APPDataview'],
currentView: 'AppHyperSpectral', // 当前显示的视图
viewList: ['AppHyperSpectral', 'APPDataview'],
indexofcomponent: 0,
};
},
mounted() {
EventBus.on('changemainvue', this.onchangemainvue);
console.log(window.__TAURI__);
},
methods: {
onchangemainvue(){
// 切换到下一个视图
this.indexofcomponent++;
if (this.indexofcomponent >= this.compomentlist.length) {
if (this.indexofcomponent >= this.viewList.length) {
this.indexofcomponent = 0;
}
this.modalcomponent = this.compomentlist[this.indexofcomponent];
this.currentView = this.viewList[this.indexofcomponent];
}
}
};
</script>
<template>
<component :is="modalcomponent" ref="Commancompent" style="user-select: none;"></component>
<keep-alive>
<component
:is="currentView"
style="user-select: none; width: 100vw; height: 100vh;"
/>
</keep-alive>
</template>
<style scoped>

View File

@ -1,47 +1,56 @@
<template>
<a-layout style="height: 100vh; width: 100vw;">
<a-layout-header>
<menubardatavue></menubardatavue>
</a-layout-header>
<a-layout>
<a-layout-sider :resize-directions="['right']" style=" min-width: 200px;max-width: 50vw;">
<a-layout-sider :resize-directions="['right']" style=" min-width: 20vw;max-width: 50vw;">
<a-dropdown trigger="contextMenu" alignPoint :style="{ display: 'block' }">
<GuiLeftSider class="lefttree" v-on:NodeClicked="ononeFilechoese"></GuiLeftSider>
<GuiLeftSider class="lefttree" v-on:NodeClicked="ononeFilechoese"
v-on:NodeDblClicked="onFileDblClick" v-on:FilesSelected="onFilesSelected"
v-on:FolderClicked="onFolderClicked" v-on:FolderDblClicked="onFolderDblClick"
v-on:SaveFilesRequested="onSaveFilesRequested" v-on:reset="resetTreeData">
</GuiLeftSider>
<template #content>
<a-doption>Option 1</a-doption>
<a-doption>Option 2</a-doption>
<a-doption>Option 3</a-doption>
<a-doption @click="onShowCurvesClick">显示曲线</a-doption>
<!-- <a-doption>Option 2</a-doption> -->
<!-- <a-doption>Option 3</a-doption> -->
</template>
</a-dropdown>
</a-layout-sider>
<a-layout-content class="right">
<GuiForDataShow ref="GuiForDataShow"></GuiForDataShow>
</a-layout-content>
</a-layout>
<a-layout-footer>Footer</a-layout-footer>
</a-layout>
<!-- <a-layout-footer>Footer</a-layout-footer> -->
<!-- 保存文件弹窗 -->
<SaveFileDialog v-model:visible="showSaveDialog" :files="filesToSave" @save="handleSaveFiles"
@cancel="handleCancelSave" />
</a-layout>
</template>
<script>
import menubardatavue from './menuvue/menubar.vue';
import GuiForDataShow from './vuecomponents/GuiForDataShow.vue';
import GuiLeftSider from './vuecomponents/GuiLeftSider.vue';
import SaveFileDialog from './vuecomponents/SaveFileDialog.vue';
import { fs } from '@tauri-apps/api';
export default {
name: 'APPDataview',
components: {
menubardatavue,
GuiForDataShow,
GuiLeftSider
GuiLeftSider,
SaveFileDialog
},
data() {
return {
// 初始化数据
message: '欢迎使用 Vue!',
selectedFilePath: null, // 存储当前选中的文件路径
selectedFilePaths: [], // 存储多选的文件路径
treeData: [
{
title: 'Trunk 0-0',
@ -73,13 +82,113 @@ export default {
},
],
},
]
],
showSaveDialog: false,
filesToSave: [],
}
},
methods: {
// 方法定义
}
// 处理单个文件选择(单击)
async ononeFilechoese(filePath) {
// console.log('选中文件路径:', filePath);
// 只记录选中的文件路径,不立即加载数据
this.selectedFilePath = filePath;
},
// 处理文件双击事件
async onFileDblClick(filePath) {
// console.log('双击文件路径:', filePath);
// 双击时加载数据
this.$refs.GuiForDataShow.onloaddata([filePath]);
},
// 处理多个文件选择当使用Shift或Ctrl多选时
async onFilesSelected(filePaths) {
// console.log('多选文件路径:', filePaths);
// 存储多选的文件路径
this.selectedFilePaths = filePaths;
// 多选时,可以选择不自动加载数据,等待用户进一步操作
if (filePaths && filePaths.length > 0) {
this.selectedFilePath = filePaths[0];
}
},
// 处理文件夹点击事件
async onFolderClicked(folderPath, childFilePaths) {
// console.log('选中文件夹:', folderPath);
// console.log('文件夹下的文件:', childFilePaths);
// 存储文件夹下的文件路径
this.selectedFilePaths = childFilePaths;
},
// 处理文件夹双击事件
async onFolderDblClick(folderPath, childFilePaths) {
// console.log('双击文件夹:', folderPath);
// console.log('文件夹下的文件:', childFilePaths);
// 如果文件夹下有文件,则加载所有文件
if (childFilePaths && childFilePaths.length > 0) {
// 将所有文件路径传递给 GuiForDataShow 组件
this.$refs.GuiForDataShow.onloaddata(childFilePaths);
}
},
// 处理"显示曲线"菜单项点击事件
async onShowCurvesClick() {
// console.log('点击显示曲线菜单项');
// 如果有多选文件,则加载所有选中的文件
if (this.selectedFilePaths && this.selectedFilePaths.length > 0) {
// console.log('加载多选文件:', this.selectedFilePaths);
this.$refs.GuiForDataShow.onloaddata(this.selectedFilePaths);
} else if (this.selectedFilePath) {
// 如果只有单选文件,则加载单个文件
// console.log('加载单选文件:', this.selectedFilePath);
this.$refs.GuiForDataShow.onloaddata(this.selectedFilePath);
}
},
resetTreeData() {
this.selectedFilePaths = []
this.$refs.GuiForDataShow.onloaddata(this.selectedFilePaths);
},
// 处理保存文件请求
onSaveFilesRequested(selectedFiles) {
this.filesToSave = selectedFiles
this.showSaveDialog = true
},
// 处理保存文件
async handleSaveFiles(saveData) {
try {
const { files, location } = saveData
for (const file of files) {
// 构建目标文件路径
const fileName = file.label
const targetPath = `${location}/${fileName}`
// 复制文件
await fs.copyFile(file.path, targetPath)
}
this.$message.success(`成功保存 ${files.length} 个文件到 ${location}`)
} catch (error) {
console.error('保存文件失败:', error)
alert('保存文件失败: ' + error.message)
}
},
// 取消保存
handleCancelSave() {
this.showSaveDialog = false
this.filesToSave = []
}
},
}
</script>
<style scoped>
@ -87,12 +196,12 @@ export default {
h1 {
color: #42b983;
}
.right {
flex: 1;
background-color: #fff;
padding: 20px;
overflow: auto;
height: 100%;
width: 100%;
flex: 1;
background-color: #f3f5fa;
overflow: auto;
height: 100%;
width: 100%;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 B

View File

@ -1,7 +1,7 @@
<script >
import { BDropdownItem,BDropdown,BDropdownDivider,BButtonGroup,BButton,BModal,BNavbar,BNavbarBrand,BNavbarNav,BNavItem,BNavItemDropdown,BToast,useToast } from 'bootstrap-vue-next';
<script>
import { BDropdownItem, BDropdown, BDropdownDivider, BButtonGroup, BButton, BModal, BNavbar, BNavbarBrand, BNavbarNav, BNavItem, BNavItemDropdown, BToast, useToast } from 'bootstrap-vue-next';
import { ref,Teleport} from 'vue';
import { ref, Teleport } from 'vue';
import EventBus from "../../eventBus.js";
@ -26,13 +26,13 @@ export default {
data() {
return {
msg: 'Welcome to Your Vue.js App',
modal:false,
DCbutton:{
state:"init",
modal: false,
DCbutton: {
state: "init",
},
WRbutton:{
state:"init",
WRbutton: {
state: "init",
}
@ -45,26 +45,26 @@ export default {
return {active}
return { active }
},
mounted() {
window.addEventListener("keydown",this.handlekeydown)
EventBus.on('SetMenubutton',this.setbutton);
window.addEventListener("keydown", this.handlekeydown)
EventBus.on('SetMenubutton', this.setbutton);
},
methods: {
setbutton(command){
if (command.name == "DC"){
setbutton(command) {
if (command.name == "DC") {
this.DCbutton.state = command.state;
}
if (command.name == "WR"){
if (command.name == "WR") {
this.WRbutton.state = command.state;
}
},
showbox(){
EventBus.emit('showbox',"hello","提示11")
showbox() {
EventBus.emit('showbox', "hello", "提示11")
},
changemainvue() {
EventBus.emit('changemainvue');
@ -100,7 +100,15 @@ export default {
}
}
},
openFolder() {
EventBus.emit('triggerOpenFolder');
},
saveFiles() {
EventBus.emit('triggerSaveFiles');
},
}
@ -122,17 +130,17 @@ export default {
<BNavbarNav>
<BNavItemDropdown text="文件" right>
<BDropdownItem href="#">新建</BDropdownItem>
<BDropdownItem href="#" >打开</BDropdownItem>
<BDropdownItem href="#">保存</BDropdownItem>
<BDropdownItem href="#">新建</BDropdownItem>
<BDropdownItem href="#" @click="openFolder">打开</BDropdownItem>
<BDropdownItem href="#" @click="saveFiles">保存</BDropdownItem>
<BDropdownItem href="#">另存为</BDropdownItem>
<BDropdownDivider></BDropdownDivider>
<BDropdownItem href="#">退出</BDropdownItem>
<BDropdownItem href="#" @click="onmenuclick('File','Advance')">高级</BDropdownItem>
<BDropdownItem @click="onmenuclick('info','help')">帮助</BDropdownItem>
<BDropdownItem href="#" @click="onmenuclick('File', 'Advance')">高级</BDropdownItem>
<BDropdownItem @click="onmenuclick('info', 'help')">帮助</BDropdownItem>
</BNavItemDropdown>
<!-- &lt;!&ndash; Navbar dropdowns &ndash;&gt;-->
<!-- &lt;!&ndash; Navbar dropdowns &ndash;&gt;-->
<BNavItemDropdown text="设置" right>
@ -145,31 +153,31 @@ export default {
</BNavItemDropdown>
<BNavItemDropdown text="窗口" right>
<BDropdownItem @click="changemainvue()">数据采集</BDropdownItem>
<!-- <BDropdownItem >ES</BDropdownItem>-->
<!-- <BDropdownItem href="#">RU</BDropdownItem>-->
<!-- <BDropdownItem href="#">FA</BDropdownItem>-->
</BNavItemDropdown>
<BDropdownItem @click="changemainvue()">数据采集</BDropdownItem>
<!-- <BDropdownItem >ES</BDropdownItem>-->
<!-- <BDropdownItem href="#">RU</BDropdownItem>-->
<!-- <BDropdownItem href="#">FA</BDropdownItem>-->
</BNavItemDropdown>
<!-- <Teleport to="body">-->
<!-- <div class="toast-container position-fixed " style="top:0px;right: 0px;width: 300px" >-->
<!-- <BToast v-model="active" variant="info" interval="10" value="100" progress-props="{-->
<!-- variant: 'danger',-->
<!-- },">-->
<!-- <template #title>-->
<!-- Title-->
<!-- </template>-->
<!-- 你好-->
<!-- </BToast>-->
<!-- </div>-->
<!-- </Teleport>-->
<!-- <BButton @click="active = !active">Toggle</BButton>-->
<!-- <Teleport to="body">-->
<!-- <div class="toast-container position-fixed " style="top:0px;right: 0px;width: 300px" >-->
<!-- <BToast v-model="active" variant="info" interval="10" value="100" progress-props="{-->
<!-- variant: 'danger',-->
<!-- },">-->
<!-- <template #title>-->
<!-- Title-->
<!-- </template>-->
<!-- 你好-->
<!-- </BToast>-->
<!-- </div>-->
<!-- </Teleport>-->
<!-- <BButton @click="active = !active">Toggle</BButton>-->
</BNavbarNav>
<!-- <div class="btgroup">
<b-button variant="secondary" pill class="siglebt" @click="onmenuclick('Work','OPT')">OPT</b-button>
@ -188,11 +196,13 @@ export default {
background-color: #f8f9fa;
width: 100vw;
}
.btgroup{
.btgroup {
position: absolute;
right: 15%;
}
.siglebt{
.siglebt {
radio: 50%;
font-size: 10px;
height: 40px;

View File

@ -1,73 +1,172 @@
<template>
<el-main style="height: 100%; width: 100%; overflow: auto;">
<el-row class="secondhang">
<GuiForPlotShow ref="ASDPlotShow" class="plotcontainer"></GuiForPlotShow>
</el-row>
<el-row class="firsthang">
<el-col :span="10" class="diveinfo">
设备信息
<GuiForDivesInfo ref="GuiForDivesInforef"></GuiForDivesInfo>
</el-col>
<el-col :span="14">
地图
<!-- <MapContainer></MapContainer> -->
<a-image
width="200"
src="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/a8c8cdb109cb051163646151a4a5083b.png~tplv-uwbnlip3yd-webp.webp"
/>
</el-col>
</el-row>
<el-main style="height: 100%; width: 100%; overflow: auto;display: flex
;
flex-direction: column;
justify-content: space-between;">
<el-row class="secondhang">
<GuiForPlotShow @onComBox1="onComBox1" @onComBox2="onComBox2" @legendselectchanged="legendselectchanged"
ref="ASDPlotShow" class="plotcontainer">
</GuiForPlotShow>
</el-row>
<el-row class="firsthang">
<el-col :span="8" class="diveinfo">
<GuiForDivesInfo ref="GuiForDivesInforef"></GuiForDivesInfo>
</el-col>
<el-col :span="16">
<PictureDisplayInterface ref="PictureDisplayInterfaceRef"></PictureDisplayInterface>
<!-- <div class="imageAndMap">
</el-main>
</div>
地图 -->
<!-- <MapContainer></MapContainer> -->
<!-- <a-image v-if="imgeList.length > 0" width="200" :src="imgeList[0].url" /> -->
</el-col>
</el-row>
</el-main>
</template>
<script>
<script setup>
import GuiForPlotShow from "./GuiForPlotShow.vue";
import MapContainer from "./MapContainer.vue";
import GuiForDivesInfo from "./GuiForDivesInfo.vue";
import {invoke} from "@tauri-apps/api/tauri";
export default {
name: "GuiForDataShow",
components: {
GuiForPlotShow,
MapContainer,
GuiForDivesInfo
},
methods: {
async onloaddata(data){
// console.log(data);
let jsonfiletem = await this.$MyGetJsonData(data);
this.$refs.ASDPlotShow.onloaddata(jsonfiletem);
this.$refs.GuiForDivesInforef.onloaddata(jsonfiletem["ASDInfo"]);
import GuiForDivesInfo from "./GuiForDivesInfo.vue";
import PictureDisplayInterface from "./PictureDisplayInterface.vue";
import { ref } from 'vue';
import { spectralTypeList } from '../../utils/irisDataDispose';
import { SpectralDataService } from '../../utils/spectralDataService';
}
},
async mounted(){
let aaa=await invoke("getoneirisfile",{path:"iris_data_example.iris"});
console.log(aaa);
defineOptions({
name: "GuiForDataShow"
});
const fromData = ref({
comBox1: spectralTypeList[0].value,
comBox2: '',
});
const ASDPlotShow = ref(null);
const GuiForDivesInforef = ref(null);
const PictureDisplayInterfaceRef = ref(null);
const spectralDataList = ref([]);
const imgeList = ref([]);
const fileData = ref([]);
async function onloaddata(data) {
// 重置数据
spectralDataList.value = [];
imgeList.value = [];
fileData.value = [];
if (!data || data.length === 0) {
updateChildComponents([], [], []);
return;
}
try {
// 加载文件数据
fileData.value = await SpectralDataService.loadFileData(data);
// 处理数据
await processSpectralData();
} catch (error) {
console.error('加载数据失败:', error);
updateChildComponents([], [], []);
}
}
async function processSpectralData() {
try {
const result = SpectralDataService.processSpectralData(
fileData.value,
fromData.value.comBox1,
fromData.value.comBox2
);
spectralDataList.value = result.spectralDataList;
imgeList.value = result.imageList;
updateChildComponents(
result.processedData,
result.spectralDataList,
result.imageList
);
} catch (error) {
console.error('处理光谱数据失败:', error);
}
}
function updateChildComponents(processedData, spectralData, imageData) {
ASDPlotShow.value?.onloaddata(processedData, fileData.value);
GuiForDivesInforef.value?.onloaddata(spectralData);
PictureDisplayInterfaceRef.value?.onloaddata(imageData, spectralData);
}
const onComBox1 = (e) => {
fromData.value.comBox1 = e;
processSpectralData();
};
const onComBox2 = (val) => {
fromData.value.comBox2 = val;
if (fromData.value.comBox1) {
processSpectralData();
}
};
const legendselectchanged = (nameTable) => {
if (nameTable) {
const filteredImages = imgeList.value.filter(element => {
if (!element || element.length === 0) return false;
return element.some(item => {
const str = item.name.split('.')[0];
return nameTable.includes(str);
});
});
const selectedSpectral = spectralDataList.value.filter(e =>
nameTable.includes(e.name)
);
if (selectedSpectral.length > 0) {
GuiForDivesInforef.value?.onloaddata(selectedSpectral);
PictureDisplayInterfaceRef.value?.onloaddata(filteredImages, selectedSpectral);
}
} else {
GuiForDivesInforef.value?.onloaddata(spectralDataList.value);
PictureDisplayInterfaceRef.value?.onloaddata(imgeList.value, spectralDataList.value);
}
};
defineExpose({
onloaddata
});
</script>
<style scoped>
.plotcontainer{
.plotcontainer {
height: 100%;
width: 100%;
overflow: hidden;
}
.firsthang{
height: 30%;
.firsthang {
height: 42%;
width: 100%;
overflow: hidden;
}
.secondhang{
height: 70%;
.secondhang {
height: 56%;
width: 100%;
text-align: center;
overflow: hidden;
position: relative;
}
.diveinfo{
.imageAndMap {
width: 100%;
height: 100%;
background: #FDFDFD;
border-radius: 4px;
margin-left: 24px;
}
</style>
</style>

View File

@ -1,74 +1,264 @@
<template>
<div class="maincontemer">
<el-row>
<el-col :span="24">
<el-card>
<div slot="header" class="clearfix">
<span>设备信息</span>
</div>
<el-table
:data="tableData"
style="width: 100%">
<el-table-column
prop="name"
<div class="maincontainer">
<div class="container_item">
<!-- 文件名显示 -->
<div class="filename_display">
<img src="../assets/文件图标-面.png">
{{ currentItem?.environmentData?.fileName || '暂无文件' }}
</div>
width="100px">
</el-table-column>
<el-table-column
prop="Value"
<!-- 轮播内容区域 -->
<div class="carousel-container">
<!-- 左侧导航按钮 -->
<button class="nav-button left" @click="prevItem">
<svg t="1754038374827" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="5034" width="200" height="200">
<path
d="M481.233 904c8.189 0 16.379-3.124 22.628-9.372 12.496-12.497 12.496-32.759 0-45.256L166.488 512l337.373-337.373c12.496-12.497 12.496-32.758 0-45.255-12.498-12.497-32.758-12.497-45.256 0l-360 360c-12.496 12.497-12.496 32.758 0 45.255l360 360c6.249 6.249 14.439 9.373 22.628 9.373z"
p-id="5035"></path>
</svg>
</button>
width="100px">
</el-table-column>
</el-table>
</el-card>
</el-col>
</el-row>
<!-- 主要内容展示 -->
<div class="contemer_content">
<ul style="width:175px">
<li>时间</li>
<li>温度</li>
<li>湿度</li>
<li>距离</li>
</ul>
<ul style="flex:1">
<li>{{ currentItem?.environmentData?.date || '--' }}</li>
<li>{{ currentItem?.environmentData?.temperature || '--' }}</li>
<li>{{ currentItem?.environmentData?.humidity || '--' }}</li>
<li>{{ currentItem?.environmentData?.height == 0 ? 0 : currentItem?.environmentData?.height || '--' }}</li>
</ul>
</div>
<!-- 右侧导航按钮 -->
<button class="nav-button right" @click="nextItem">
<svg style="transform: rotateZ(180deg);left: 10px;" t="1754038374827" class="icon" viewBox="0 0 1024 1024"
version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5034" width="200" height="200">
<path
d="M481.233 904c8.189 0 16.379-3.124 22.628-9.372 12.496-12.497 12.496-32.759 0-45.256L166.488 512l337.373-337.373c12.496-12.497 12.496-32.758 0-45.255-12.498-12.497-32.758-12.497-45.256 0l-360 360c-12.496 12.497-12.496 32.758 0 45.255l360 360c6.249 6.249 14.439 9.373 22.628 9.373z"
p-id="5035"></path>
</svg>
</button>
</div>
<!-- 轮播指示器 -->
<!-- <div class="carousel-indicators" v-if="tableData.length > 1">
<span
v-for="(item, index) in tableData"
:key="index"
:class="{ active: currentIndex === index }"
@click="goToItem(index)"
></span>
</div> -->
</div>
</div>
</template>
<script>
export default {
name: "GuiForDivesInfo",
data(){
return {
tableData: [{
name: '设备名称',
Value: 'ARS'
} ]
}
<script setup>
import { ref, computed } from 'vue'
},
methods: {
onloaddata(jsondata){
//if (jsondata.isObject){
this.tableData=[];
this.tableData.push(...convertObjectToArray(jsondata));
// }
const tableData = ref([])
const currentIndex = ref(0)
}
// 计算当前显示的项目
const currentItem = computed(() => {
return tableData.value[currentIndex.value] || {}
})
// 加载数据方法
const onloaddata = (jsondata) => {
tableData.value = jsondata
currentIndex.value = 0 // 重置到第一项
}
// 上一项
const prevItem = () => {
if (tableData.value.length <= 1) return
currentIndex.value = (currentIndex.value - 1 + tableData.value.length) % tableData.value.length
}
// 下一项
const nextItem = () => {
if (tableData.value.length <= 1) return
currentIndex.value = (currentIndex.value + 1) % tableData.value.length
}
// 跳转到指定项
const goToItem = (index) => {
if (index >= 0 && index < tableData.value.length) {
currentIndex.value = index
}
}
function convertObjectToArray(jsonObject) {
const jsonArray = [];
for (const key in jsonObject) {
if (jsonObject.hasOwnProperty(key)) {
const keyValueObject = {
name: key,
Value: jsonObject[key]
};
jsonArray.push(keyValueObject);
}
}
return jsonArray;
}
defineExpose({
onloaddata
})
</script>
<style scoped>
.maincontemer{
height: 100px;
<style scoped lang="less">
.maincontainer {
width: 100%;
height: 100%;
background: #FDFDFD;
position: relative;
border-radius: 4px;
.container_item {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.filename_display {
width: 100%;
height: 70px;
font-size: 20px;
color: #434959;
font-weight: 600;
display: flex;
justify-content: center;
align-items: center;
&>img {
width: 16px;
height: 20px;
margin-right: 6px;
}
}
.carousel-container {
display: flex;
align-items: center;
justify-content: space-between;
height: calc(100% - 100px);
position: relative;
.contemer_content {
flex: 1;
padding: 0 20px;
font-size: 16px;
color: #6B7181;
width: 100%;
display: flex;
padding-top: 20px;
&>ul {
&>li {
text-align: right;
margin-bottom: 20px;
}
}
&>ul:nth-child(2) {
&>li {
text-align: left;
}
}
ul,
li {
margin: 0;
padding: 0;
list-style: none;
}
div {
margin: 10px 0;
&>span {
color: #434959;
}
}
}
.nav-button {
width: 60px;
height: 90px;
background: rgba(243, 245, 250, 1);
font-size: 24px;
cursor: pointer;
display: flex;
align-items: center;
border-radius: 4px;
justify-content: center;
color: #6B7181;
box-shadow: none;
cursor: pointer;
position: absolute;
&>svg {
position: absolute;
width: 30px;
height: 30px;
left: 20px;
&>path {
fill: rgba(107, 113, 129, 1);
}
}
&:hover:not(:disabled) {
background: rgba(66, 113, 238, 0.10);
border: 1.4px solid #4271EE;
&>svg {
position: absolute;
width: 30px;
height: 30px;
left: 20px;
&>path {
fill: rgba(66, 113, 238, 1);
}
}
}
&:disabled {
opacity: 0.3;
cursor: not-allowed;
}
&.left {
left: 16px;
}
&.right {
right: 16px;
}
}
}
.carousel-indicators {
display: flex;
justify-content: center;
height: 40px;
align-items: center;
span {
width: 10px;
height: 10px;
border-radius: 50%;
background: #ccc;
margin: 0 5px;
cursor: pointer;
&.active {
background: #434959;
}
}
}
}
}
</style>
</style>

File diff suppressed because it is too large Load Diff

View File

@ -1,78 +1,447 @@
<template>
<div class="el-treecontain">
<el-tree :data="data" :props="defaultProps" class="el-treemain" @node-click="handleNodeClick"/>
<div class="GuiLeftSider">
<p class="nameList">文件列表</p>
<div class="el-treecontain" v-if="data && data.length > 0">
<el-tree ref="treeRef" :data="data" :props="defaultProps" class="el-treemain" highlight-current
:expand-on-click-node="false" :default-expanded-keys="defaultExpandedKeys" @node-click="handleNodeClick"
node-key="id">
<template #default="{ node, data }">
<div class="fileitem1" v-if="data.isFolder">
<img src="../assets/文件夹图标.png">
{{ node.label }}
</div>
<div class="fileitem2" v-else-if="data.isLeaf">
<img src="../assets/文件图标-线.png">
{{ node.label }}
</div>
</template>
</el-tree>
</div>
<!-- <div class="defaultList" v-else>
<button @click="onopendata">选择文件夹</button>
</div> -->
</div>
<button @click="onopendata">click</button>
</template>
<script>
import EventBus from "../../eventBus.js"
export default {
name: "GuiLeftSider",
data() {
return {
data: [{
label: '请选择',
children: [],
},
],
data: [],
defaultProps: {
children: 'children',
label: 'label',
},
DefualtPath: "\\ARS_data"
DefualtPath: "",
showButton: true,
ctrlKeyPressed: false,
shiftKeyPressed: false,
shiftKeyField: [],
selectNodes: [],
includeChildren: true,
allFiles: [], // 存储所有解析出的文件
lastClickedNode: null, // 记录最后点击的节点
lastClickTime: 0, // 记录最后点击时间
defaultExpandedKeys: [],
}
},
async mounted() {
//if(this.$isElectron()) {
this.DefualtPath="";
return
// };
await this.onopendata();
window.addEventListener("keydown", this.handleKeyDown);
window.addEventListener("keyup", this.handleKeyUp);
EventBus.on('triggerOpenFolder', this.onopendata);
EventBus.on('triggerSaveFiles', this.handleSaveFiles);
},
beforeUnmount() {
window.removeEventListener("keydown", this.handleKeyDown);
window.removeEventListener("keyup", this.handleKeyUp);
EventBus.off('triggerOpenFolder', this.onopendata);
EventBus.off('triggerSaveFiles', this.handleSaveFiles);
},
methods: {
handleNodeClick(data) {
if (data.isLeaf)
// console.log(getParentPath(data));
this.$emit("NodeClicked", this.DefualtPath+getParentPath(data));
handleKeyDown(event) {
if (event.key == "Control") {
this.ctrlKeyPressed = true;
}
if (event.key == "Shift") {
this.shiftKeyPressed = true;
}
},
handleKeyUp(event) {
if (event.key == "Control") {
this.ctrlKeyPressed = false;
}
if (event.key == "Shift") {
this.shiftKeyPressed = false;
}
},
collectAllFiles(node) {
const files = [];
if (node.isLeaf) {
files.push({
id: node.id,
path: node.path,
label: node.label
});
return files;
}
if (node.children && node.children.length > 0) {
for (const child of node.children) {
const childFiles = this.collectAllFiles(child);
files.push(...childFiles);
}
}
return files;
},
isFolder(nodeData) {
return nodeData.isFolder === true || (nodeData.children && Array.isArray(nodeData.children));
},
// 添加双击事件处理
handleNodeDblClick(nodeData, node) {
if (nodeData.isLeaf && !this.isFolder(nodeData)) {
// 双击文件时发送NodeDblClicked事件
this.$emit("NodeDblClicked", nodeData.path);
} else if (this.isFolder(nodeData)) {
// 双击文件夹时收集所有文件并发送FolderDblClicked事件
this.allFiles = this.collectAllFiles(nodeData);
const allPaths = this.allFiles.map(file => file.path);
this.$emit("FolderDblClicked", nodeData.path, allPaths);
}
},
// 节点点击事件处理
handleNodeClick(nodeData, node) {
const treeRef = this.$refs.treeRef;
if (!treeRef) return;
const nodes = treeRef.store._getAllNodes(); // 所有node节点
const ishas = this.selectNodes.includes(node.id);
const isSameLevel = (node1, node2) => node1.parent === node2.parent;
const isNodeFolder = this.isFolder(nodeData);
// 实现双击检测
const now = new Date().getTime();
const isDoubleClick = this.lastClickedNode === nodeData.path && (now - this.lastClickTime) < 300; // 300ms内的点击视为双击
// 更新最后点击的节点和时间
this.lastClickedNode = nodeData.path;
this.lastClickTime = now;
// 如果是双击,则发送双击事件
if (isDoubleClick) {
if (nodeData.isLeaf && !isNodeFolder) {
this.$emit("NodeDblClicked", nodeData.path);
return; // 双击时不执行单击逻辑
} else if (isNodeFolder) {
// 双击文件夹时收集所有文件并发送FolderDblClicked事件
this.allFiles = this.collectAllFiles(nodeData);
const allPaths = this.allFiles.map(file => file.path);
this.$emit("FolderDblClicked", nodeData.path, allPaths);
return; // 双击时不执行单击逻辑
}
}
// 单选模式
if (!this.ctrlKeyPressed && !this.shiftKeyPressed) {
// 清空之前的选择
this.shiftKeyField = [];
this.selectNodes = [];
// 如果是文件,直接选中
if (nodeData.isLeaf && !isNodeFolder) {
this.selectNodes = [node.id];
this.$emit("NodeClicked", nodeData.path);
}
// 如果是文件夹,解析所有子文件
else if (isNodeFolder) {
this.selectNodes = [node.id];
// 收集文件夹下所有文件
this.allFiles = this.collectAllFiles(nodeData);
// console.log('文件夹下所有文件:', this.allFiles);
// 可以发送所有文件路径给父组件
const allPaths = this.allFiles.map(file => file.path);
this.$emit("FolderClicked", nodeData.path, allPaths);
}
}
// Shift多选模式
else if (this.shiftKeyPressed) {
if (isNodeFolder) {
this.shiftKeyField = [];
this.selectNodes = [];
for (const item of nodes) {
item.isCurrent = false;
}
return;
}
// 如果selectNodes有值但shiftKeyField为空说明是从单选模式切换到Shift多选模式
// 需要将已选中的节点ID添加到shiftKeyField中
if (this.selectNodes.length > 0 && this.shiftKeyField.length === 0) {
const firstSelectedNodeId = this.selectNodes[0];
this.shiftKeyField.push(firstSelectedNodeId);
}
if (this.selectNodes.length > 0) {
const firstSelectedNodeId = this.selectNodes[0];
const firstSelectedNode = nodes.find(x => x.id == firstSelectedNodeId);
if (firstSelectedNode && !isSameLevel(firstSelectedNode, node)) {
this.shiftKeyField = [];
this.selectNodes = [];
for (const item of nodes) {
item.isCurrent = false;
}
return;
}
}
this.shiftKeyField.push(node.id);
if (this.shiftKeyField.length > 1) {
const firstNodeId = this.shiftKeyField[0];
const firstNode = nodes.find(x => x.id == firstNodeId);
if (!isSameLevel(firstNode, node)) {
this.shiftKeyField = [];
this.selectNodes = [];
for (const item of nodes) {
item.isCurrent = false;
}
return;
}
const sIndex = nodes.findIndex(x => x.id == this.shiftKeyField[0]);
const eIndex = nodes.findIndex(x => x.id == this.shiftKeyField[this.shiftKeyField.length - 1]);
const s = sIndex < eIndex ? sIndex : eIndex; // 取小值当开头索引
const e = sIndex < eIndex ? eIndex : sIndex; // 取大值当结尾索引
for (let i = s; i <= e; i++) {
const currentNode = nodes[i];
if (!this.isFolder(currentNode.data) && isSameLevel(currentNode, firstNode)) {
if (!this.selectNodes.includes(currentNode.id)) {
this.selectNodes.push(currentNode.id);
}
}
}
} else {
if (!isNodeFolder) {
if (!this.selectNodes.includes(node.id)) {
this.selectNodes.push(node.id);
}
} else {
this.shiftKeyField = [];
this.selectNodes = [];
}
}
}
// ctrl多选模式
else if (this.ctrlKeyPressed) {
if (isNodeFolder) {
this.shiftKeyField = [];
this.selectNodes = [];
for (const item of nodes) {
item.isCurrent = false;
}
return;
}
if (ishas) {
const index = this.selectNodes.findIndex(x => x == node.id);
this.selectNodes.splice(index, 1);
} else {
this.selectNodes.push(node.id);
}
}
for (const item of nodes) {
if (this.selectNodes.includes(item.id)) {
item.isCurrent = true;
} else {
item.isCurrent = false;
}
}
const selectedNodesInfo = nodes
.filter(item => this.selectNodes.includes(item.id))
.map(item => ({
id: item.id,
label: item.label,
path: item.data.path || '',
isFolder: this.isFolder(item.data)
}));
const selectedFiles = selectedNodesInfo.filter(item => !item.isFolder);
if (selectedFiles.length > 0) {
const filePaths = selectedFiles.map(file => file.path);
this.$emit("FilesSelected", filePaths);
}
},
async onopendata() {
let cfiles = await this.$MyGetFolderlist(this.DefualtPath);
this.data = [];
this.data.push(cfiles);
try {
// 获取文件夹列表
this.$emit("reset");
this.data = []
const folderTree = await this.$tauriApi.getFolderList(this.DefualtPath);
// 添加唯一ID
let idCounter = 1;
const addUniqueIdsAndFilter = (node) => {
node.id = idCounter++;
if (node.isFolder) {
if (Array.isArray(node.children) && node.children.length) {
node.children = node.children.filter(child => {
if (child.isFolder) return true;
if (child.isLeaf && typeof child.label === 'string') {
return child.label.endsWith('.iris');
}
return false;
});
}
node.children.forEach(child => addUniqueIdsAndFilter(child));
}
} else {
}
};
addUniqueIdsAndFilter(folderTree);
if (folderTree && folderTree.label != "请选择") {
this.data = [folderTree];
}
// 收集所有id用于默认展开
const expandedKeys = [];
this.collectAllNodeIds(folderTree, expandedKeys);
this.defaultExpandedKeys = expandedKeys;
} catch (error) {
console.error("打开文件夹失败:", error);
}
},
// 整合所有节点id默认显示使用
collectAllNodeIds(node, arr) {
arr.push(node.id);
if (node.children && node.children.length > 0) {
node.children.forEach(child => this.collectAllNodeIds(child, arr));
}
},
// 处理保存文件事件
handleSaveFiles() {
const selectedFiles = this.getSelectedFiles();
if (selectedFiles.length === 0) {
alert('请先打开文件后再保存')
return;
}
// 触发保存文件请求事件,传递选中的文件
this.$emit('SaveFilesRequested', selectedFiles);
},
// 获取文件
getSelectedFiles() {
const treeRef = this.$refs.treeRef;
if (!treeRef) return [];
const nodes = treeRef.store._getAllNodes();
const allFiles = [];
// 遍历所有节点,收集所有文件
nodes.forEach(node => {
if (node.data && node.data.isLeaf && !node.data.isFolder) {
allFiles.push({
id: node.id,
label: node.data.label,
path: node.data.path,
name: node.data.label
});
}
});
return allFiles;
},
}
}
function getParentPath(node) {
if (!node) {
return '';
}
if (node.parent) {
return getParentPath(node.parent) + '\\' + node.label;
} else {
return '';
}
}
</script>
<style scoped>
el-tree{
<style scoped lang="less">
el-tree {
height: 100vh;
overflow: auto;
}
.el-treemain {
min-width: 100%;
white-space: nowrap; /* 不换行,使树节点水平排列 */
display: inline-block; /* 横向排列 */
white-space: nowrap;
/* 不换行,使树节点水平排列 */
display: inline-block;
/* 横向排列 */
text-align: left;
}
.el-treecontain{
.el-treecontain {
width: 100%;
text-align: left;
max-height: 96vh;
overflow-y: auto;
}
</style>
.fileitem1 {
width: 100%;
height: 20px;
display: flex;
align-items: center;
&>img {
width: 18px;
height: 18px;
margin-right: 5px;
}
}
.fileitem2 {
width: 100%;
height: 18px;
display: flex;
align-items: center;
&>img {
margin-right: 5px;
width: 13px;
height: 13px;
}
}
.fileitem:nth-child(2) {
width: 13px;
height: 13px;
}
.GuiLeftSider {
padding: 17px 24px;
}
.GuiLeftSider p {
font-size: 18px;
color: #434959;
text-align: left;
font-weight: 600;
}
.defaultList button {
width: 100%;
height: 40px;
border-radius: 4px;
border: 1.4px solid #4271EE;
box-shadow: none;
font-size: 16px;
color: #4271EE;
}
</style>

View File

@ -1,78 +1,236 @@
<template>
<div ref="mapContainer" id="map" style="width: 100%; height: 400px;"></div>
<div ref="mapContainer" class="ol-map-wrapper">
<div class="ol-map" ref="olMap"></div>
<button class="fullscreen-btn" @click="toggleFullScreen">
{{ isFullscreen ? '退出全屏' : '全屏显示' }}
</button>
</div>
</template>
<script>
import "maptalks/dist/maptalks.css";
import * as maptalks from "maptalks";
export default {
name: "MapContainer",
data() {
return {
map: null,
layer: null
};
},
methods: {
addpointtomap(lon,lat){
var marker1 = new maptalks.Marker(
[116.255535,40.204654],
{
'symbol': {
'markerType': 'ellipse',
'markerFill': 'rgb(135,196,240)',
'markerFillOpacity': 1,
'markerLineColor': '#34495e',
'markerLineWidth': 3,
'markerLineOpacity': 1,
'markerLineDasharray': [],
'markerWidth': 40,
'markerHeight': 40,
'markerDx': 0,
'markerDy': 0,
'markerOpacity': 1
}
}
)
marker1.addTo(this.layer);
},
initializeMap() {
this.map = new maptalks.Map('map', {
center: [-0.113049,51.498568],
zoom: 14,
compassControl: {
position: 'top-left',
},
<script setup>
import { ref, onMounted, onBeforeUnmount, defineProps, watch } from 'vue';
import 'ol/ol.css';
import { Map, View } from 'ol';
import TileLayer from 'ol/layer/Tile';
import XYZ from 'ol/source/XYZ';
import { Feature } from 'ol';
import Point from 'ol/geom/Point';
import VectorSource from 'ol/source/Vector';
import VectorLayer from 'ol/layer/Vector';
import { Icon, Style, Text, Fill, Stroke } from 'ol/style';
import { fromLonLat } from 'ol/proj';
import pinIcon from '../assets/图钉.png';
import { boundingExtent } from 'ol/extent';
baseLayer: new maptalks.TileLayer('base', {
urlTemplate: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
subdomains: ['a','b','c','d'],
attribution: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>'
})
const props = defineProps({
dataListMap: {
type: Array,
default: () => []
}
});
const mapContainer = ref(null);
const olMap = ref(null)
const map = ref(null);
const vectorLayer = ref(null);
const isMapReady = ref(false);
const isFullscreen = ref(false);
const initializeMap = () => {
const center = fromLonLat([116.255535, 40.204654]);
const view = new View({
center,
zoom: 14
});
const tiandituKey = 'a155b662f47ceee8d82a67bfbb3a0825';
const tileLayers = [
new TileLayer({
source: new XYZ({
url: `http://t{0-7}.tianditu.gov.cn/DataServer/wmts?T=img_w&x={x}&y={y}&l={z}&tk=${tiandituKey}`
}),
isBaseLayer: true
}),
new TileLayer({
source: new XYZ({
url: `http://t{0-7}.tianditu.gov.cn/DataServer/wmts?T=cia_w&x={x}&y={y}&l={z}&tk=${tiandituKey}`
}),
isBaseLayer: true
})
];
const vectorSource = new VectorSource();
vectorLayer.value = new VectorLayer({
source: vectorSource
});
map.value = new Map({
target: olMap.value,
layers: [...tileLayers, vectorLayer.value],
view
});
isMapReady.value = true;
};
const getRandomCoordinateInBeijing = () => {
const minLon = 115.7;
const maxLon = 117.4;
const minLat = 40.2;
const maxLat = 40.5;
const lon = Math.random() * (maxLon - minLon) + minLon;
const lat = Math.random() * (maxLat - minLat) + minLat;
return {
longitude: parseFloat(lon.toFixed(6)),
latitude: parseFloat(lat.toFixed(6))
};
};
const handleTagging = (data) => {
if (!isMapReady.value || !Array.isArray(data)) return;
const features = [];
const coords = [];
data.forEach(item => {
const { longitude, latitude } = getRandomCoordinateInBeijing(); // 模拟
if (longitude && latitude) {
const feature = new Feature({
geometry: new Point(fromLonLat([longitude, latitude])),
name: item.environmentData.fileName || ''
});
this.map.setCenter([116.255535,40.204654]);
this.layer=new maptalks.VectorLayer('vector').addTo(this.map);
this.addpointtomap(0,0)
}
},
mounted() {
this.initializeMap();
// const mapOptions = {
// center: [0, 0],
// zoom: 10
// };
//
// const map = new Map(this.$refs.mapContainer, mapOptions);
feature.setStyle(
new Style({
image: new Icon({
src: pinIcon,
anchor: [0.5, 1],
scale: 1
}),
text: new Text({
text: item.environmentData.fileName || '',
offsetY: 14,
font: '300 14px sans-serif',
fill: new Fill({
color: '#fff'
}),
stroke: new Stroke({
color: '#fff',
width: 0
}),
textAlign: 'center',
backgroundFill: new Fill({
color: 'rgba(0, 0, 0, 0.5)'
}),
backgroundStroke: new Stroke({
color: 'rgba(0, 0, 0, 0.5)',
width: 0
}),
padding: [1, 10, 1, 10],
radius: 4
})
})
);
features.push(feature);
coords.push(fromLonLat([longitude, latitude]));
}
});
if (features.length > 0) {
vectorLayer.value.getSource().clear();
vectorLayer.value.getSource().addFeatures(features);
const extent = boundingExtent(coords);
map.value.getView().fit(extent, {
padding: [50, 50, 50, 50],
maxZoom: 16,
duration: 1000
});
}
};
const toggleFullScreen = () => {
const container = mapContainer.value;
if (!document.fullscreenElement) {
container?.requestFullscreen?.();
} else {
document.exitFullscreen?.();
}
};
const fullscreenChangeHandler = () => {
isFullscreen.value = !!document.fullscreenElement;
};
watch(
() => props.dataListMap,
(newVal) => {
if (!Array.isArray(newVal) || newVal.length === 0) return;
const tryAdd = () => {
if (isMapReady.value) {
handleTagging(newVal);
} else {
setTimeout(tryAdd, 100);
}
};
tryAdd();
},
{ deep: true, immediate: true }
);
onMounted(() => {
try {
initializeMap();
} catch (error) {
alert('初始化地图出错,', error)
}
document.addEventListener('fullscreenchange', fullscreenChangeHandler);
});
onBeforeUnmount(() => {
document.removeEventListener('fullscreenchange', fullscreenChangeHandler);
});
</script>
<style scoped>
.ol-map-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.ol-map {
width: 100%;
height: 100%;
}
.fullscreen-btn {
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
padding: 6px 12px;
font-size: 14px;
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
user-select: none;
}
</style>

View File

@ -0,0 +1,240 @@
<template>
<div class="maincontainer">
<div class="container_item">
<!-- 文件名显示 -->
<div class="filename_display" v-if="!isLastPage && currentItem?.url">
<img src="../assets/文件图标-面.png">
{{ currentItem?.name || '暂无文件' }}
</div>
<div class="filename_display" v-else>
地图
</div>
<!-- 轮播内容区域 -->
<div class="carousel-container">
<!-- 左侧导航按钮 -->
<button class="nav-button left" @click="prevItem">
<svg t="1754038374827" class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" p-id="5034" width="200" height="200">
<path
d="M481.233 904c8.189 0 16.379-3.124 22.628-9.372 12.496-12.497 12.496-32.759 0-45.256L166.488 512l337.373-337.373c12.496-12.497 12.496-32.758 0-45.255-12.498-12.497-32.758-12.497-45.256 0l-360 360c-12.496 12.497-12.496 32.758 0 45.255l360 360c6.249 6.249 14.439 9.373 22.628 9.373z"
p-id="5035"></path>
</svg>
</button>
<div class="contemer_content">
<img v-if="!isLastPage && currentItem?.url" :src="currentItem.url" />
<div v-else class="last-page-box">
<MapContainer :dataListMap="dataListMap"></MapContainer>
</div>
</div>
<!-- 右侧导航按钮 -->
<button class="nav-button right" @click="nextItem">
<svg style="transform: rotateZ(180deg);left: 10px;" t="1754038374827" class="icon"
viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5034" width="200"
height="200">
<path
d="M481.233 904c8.189 0 16.379-3.124 22.628-9.372 12.496-12.497 12.496-32.759 0-45.256L166.488 512l337.373-337.373c12.496-12.497 12.496-32.758 0-45.255-12.498-12.497-32.758-12.497-45.256 0l-360 360c-12.496 12.497-12.496 32.758 0 45.255l360 360c6.249 6.249 14.439 9.373 22.628 9.373z"
p-id="5035"></path>
</svg>
</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import MapContainer from "./MapContainer.vue";
const imgeList = ref([])
const currentIndex = ref(0)
const dataListMap = ref([])
const isLastPage = computed(() => currentIndex.value === imgeList.value.length)
const currentItem = computed(() => {
if (isLastPage.value) {
return null
}
return imgeList.value[currentIndex.value] || {}
})
const onloaddata = async (jsondata, dataList) => {
if (jsondata && jsondata.length > 0 && dataList && dataList.length > 0) {
dataListMap.value = dataList
imgeList.value = jsondata.flat()
currentIndex.value = 0
}
}
// 上一项
const prevItem = () => {
if (imgeList.value.length === 0) return
currentIndex.value =
(currentIndex.value - 1 + (imgeList.value.length + 1)) %
(imgeList.value.length + 1)
}
// 下一项
const nextItem = () => {
if (imgeList.value.length === 0) return
currentIndex.value = (currentIndex.value + 1) % (imgeList.value.length + 1)
}
// 跳转到指定项
const goToItem = (index) => {
if (index >= 0 && index < imgeList.value.length) {
currentIndex.value = index
}
}
defineExpose({
onloaddata
})
</script>
<style scoped lang="less">
.maincontainer {
width: 97.5%;
height: 100%;
background: #FDFDFD;
position: relative;
border-radius: 4px;
float: right;
.container_item {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.filename_display {
width: 100%;
height: 70px;
font-size: 20px;
color: #434959;
font-weight: 600;
display: flex;
justify-content: center;
align-items: center;
&>img {
width: 16px;
height: 20px;
margin-right: 6px;
}
}
.carousel-container {
display: flex;
align-items: center;
justify-content: space-between;
height: calc(100% - 100px);
position: relative;
.contemer_content {
flex: 1;
padding: 0 20px;
font-size: 18px;
color: #6B7181;
width: 100%;
height: 30vh;
display: flex;
padding-top: 20px;
&>img {
width: 100%;
height: 100%;
object-fit: contain;
}
&>.last-page-box {
width: 100%;
height: 100%;
}
}
.nav-button {
width: 60px;
height: 90px;
background: rgba(243, 245, 250, 1);
font-size: 24px;
cursor: pointer;
display: flex;
align-items: center;
border-radius: 4px;
justify-content: center;
color: #6B7181;
box-shadow: none;
cursor: pointer;
position: relative;
&>svg {
position: absolute;
width: 30px;
height: 30px;
left: 20px;
&>path {
fill: rgba(107, 113, 129, 1);
}
}
&:hover:not(:disabled) {
background: rgba(66, 113, 238, 0.10);
border: 1.4px solid #4271EE;
&>svg {
position: absolute;
width: 30px;
height: 30px;
left: 20px;
&>path {
fill: rgba(66, 113, 238, 1);
}
}
}
&:disabled {
opacity: 0.3;
cursor: not-allowed;
}
&.left {
margin-left: 16px;
}
&.right {
margin-right: 16px;
}
}
}
.carousel-indicators {
display: flex;
justify-content: center;
height: 40px;
align-items: center;
span {
width: 10px;
height: 10px;
border-radius: 50%;
background: #ccc;
margin: 0 5px;
cursor: pointer;
&.active {
background: #434959;
}
}
}
}
}
</style>

View File

@ -0,0 +1,301 @@
<template>
<el-dialog style="padding: 0 ; border-radius: 12px; overflow: hidden; min-width:600px; max-width:800px"
class="legend_my_el_dialog" v-model="showModal" width="24%" @close="handleCancel">
<template #title>
<div class="legend-dialog-title">保存文件</div>
</template>
<div class="save-dialog-content">
<div class="file-selection-section">
<div class="select-all-section">
<h6>选择要保存的文件</h6>
<el-checkbox class="my_save_checkbox select-all-checkbox" v-model="selectAll"
:indeterminate="isIndeterminate" @change="handleSelectAll">
全选
</el-checkbox>
</div>
<div class="file-list">
<div>
<div v-for="file in selectedFiles" :key="file.path" class="file-item">
<el-checkbox class="my_save_checkbox" v-model="file.selected" :label="file.label"
@change="handleFileSelect">
{{ file.label }}
</el-checkbox>
</div>
</div>
</div>
</div>
<div class="save-location-section">
<h6>保存位置</h6>
<div class="location-input">
<el-input v-model="saveLocation" placeholder="请选择保存位置">
</el-input>
<el-button @click="selectSaveLocation" class="input-group-button">浏览</el-button>
</div>
</div>
<div class="save-actions">
<el-button type="primary" @click="handleSave" class="input-group-button-primary">保存</el-button>
<el-button @click="handleCancel" class="input-group-button">取消</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import { dialog } from '@tauri-apps/api'
export default {
name: 'SaveFileDialog',
props: {
visible: {
type: Boolean,
default: false
},
files: {
type: Array,
default: () => []
}
},
emits: ['update:visible', 'save', 'cancel'],
data() {
return {
saveLocation: '',
selectedFiles: [],
selectAll: false,
isIndeterminate: false
}
},
computed: {
showModal: {
get() {
return this.visible
},
set(value) {
this.$emit('update:visible', value)
}
}
},
watch: {
files: {
handler(newFiles) {
this.selectedFiles = newFiles.map(file => ({
...file,
selected: true
}))
this.updateSelectAllState()
},
immediate: true
}
},
methods: {
async selectSaveLocation() {
try {
const selected = await dialog.open({
directory: true,
title: '选择保存位置'
})
if (selected) {
this.saveLocation = selected
}
} catch (error) {
console.error('选择保存位置失败:', error)
}
},
handleSave() {
const filesToSave = this.selectedFiles.filter(file => file.selected)
if (filesToSave.length === 0) {
// this.$message.warning('请至少选择一个文件')
alert('请至少选择一个文件')
return
}
if (!this.saveLocation) {
alert('请选择保存位置')
// this.$message.warning('请选择保存位置')
return
}
this.$emit('save', {
files: filesToSave,
location: this.saveLocation
})
this.showModal = false
},
handleCancel() {
this.$emit('cancel')
this.showModal = false
},
handleSelectAll(value) {
this.selectedFiles.forEach(file => {
file.selected = value
})
this.updateSelectAllState()
},
handleFileSelect() {
this.updateSelectAllState()
},
updateSelectAllState() {
const selectedCount = this.selectedFiles.filter(file => file.selected).length
const totalCount = this.selectedFiles.length
if (selectedCount === 0) {
this.selectAll = false
this.isIndeterminate = false
} else if (selectedCount === totalCount) {
this.selectAll = true
this.isIndeterminate = false
} else {
this.selectAll = false
this.isIndeterminate = true
}
},
}
}
</script>
<style lang="less" scoped>
.file-selection-section,
.save-location-section {
margin-bottom: 20px;
h6 {
text-align: left;
}
.select-all-section {
display: flex;
align-items: center;
justify-content: space-between;
}
}
.file-list {
max-height: 300px;
overflow-y: auto;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 10px;
}
.file-item {
margin-bottom: 8px;
padding: 5px;
border-radius: 3px;
text-align: left;
}
.file-item:hover {
background-color: #f5f7fa;
}
.file-path {
color: #606266;
font-size: 12px;
margin-left: 8px;
}
.no-files-message {
text-align: center;
color: #909399;
padding: 20px;
}
.location-input {
width: 100%;
display: flex;
}
</style>
<style lang="less">
.legend_my_el_dialog {
.el-dialog__header.show-close {
padding: 0 !important;
font-size: 16px;
color: #333333;
.el-dialog__headerbtn {
box-shadow: none;
top: 4px;
right: 14px;
font-size: 24px;
color: #000;
.el-icon {
fill: #000;
path {
fill: #000;
}
}
}
}
}
.legend-dialog-title {
text-align: left;
height: 50px;
line-height: 50px;
background: #F3F5FA;
border-radius: 12px 12px 0px 0px;
padding: 0;
font-weight: 600;
padding-left: 30px;
}
.save-dialog-content {
padding: 20px 0;
.my_save_checkbox {
.el-checkbox__input.is-checked+.el-checkbox__label {
color: #4271EE;
}
.el-checkbox__input.is-checked .el-checkbox__inner {
border-color: #4271EE;
background-color: #4271EE;
}
.el-checkbox__input.is-indeterminate .el-checkbox__inner {
background-color: #4271EE;
border-color: #4271EE;
}
}
.el-input__wrapper.is-focus {
box-shadow: 0 0 0 1px #dcdfe6;
}
.input-group-button {
margin-left: 10px;
box-shadow: none;
background-color: #EDF1FB;
color: #6B7181;
border: 1px solid #D3D8E3;
}
.input-group-button-primary {
box-shadow: none;
color: #fff;
background-color: #4271EE;
}
.el-button:hover {
color: #6B7181;
border-color: #aaabad;
}
.input-group-button-primary:hover {
color: #fff;
border-color: #4271EE;
}
}
</style>

View File

@ -2,54 +2,64 @@ import { createApp } from "vue";
import "./styles.css";
//import App from "./AppHyperSpectral.vue";
import App from "./App.vue";
import { appWindow } from '@tauri-apps/api/window';
import { LogicalSize } from '@tauri-apps/api/window';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css'
import {createBootstrap} from 'bootstrap-vue-next'
import { appWindow } from "@tauri-apps/api/window";
import { LogicalSize } from "@tauri-apps/api/window";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { createBootstrap } from "bootstrap-vue-next";
//
// // Add the necessary CSS
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue-next/dist/bootstrap-vue-next.css'
import ArcoVue from '@arco-design/web-vue';
import "bootstrap/dist/css/bootstrap.css";
import "bootstrap-vue-next/dist/bootstrap-vue-next.css";
import ArcoVue from "@arco-design/web-vue";
import '@arco-design/web-vue/dist/arco.css';
import { Draggable,DraggablePlugin, DraggableDirective } from '@braks/revue-draggable';
import KonamiCode from 'vue3-konami-code';
import "@arco-design/web-vue/dist/arco.css";
import {
Draggable,
DraggablePlugin,
DraggableDirective,
} from "@braks/revue-draggable";
import KonamiCode from "vue3-konami-code";
import EventBus from "./eventBus.js";
import tauriApi from "./utils/tauriApi.js";
async function setWindowSize() {
// const primaryMonitor = await screen.primaryMonitor();
// const screenSize = primaryMonitor.size;
// const primaryMonitor = await screen.primaryMonitor();
// const screenSize = primaryMonitor.size;
const newSize = new LogicalSize(800, 600);
await appWindow.setSize(newSize);
// appWindow.setSize(new Size(1000, 1000));
const newSize = new LogicalSize(800, 600);
await appWindow.setSize(newSize);
// appWindow.setSize(new Size(1000, 1000));
}
var app = createApp(App);
app.use(ElementPlus);
app.use(createBootstrap( {components: true, directives: true,plugins:true,icons: true,}));
app.use(ElementPlus, {
locale: zhCn,
});
app.use(
createBootstrap({
components: true,
directives: true,
plugins: true,
icons: true,
})
);
app.use(ArcoVue);
app.use(DraggablePlugin);
app.use(KonamiCode, {
onKonamiCodeEntered: () => {
// 用户输入Konami Code后执行的代码
console.log('Konami Code 已输入!');
EventBus.emit('konamiactive');
}
onKonamiCodeEntered: () => {
// 用户输入Konami Code后执行的代码
console.log("Konami Code 已输入!");
EventBus.emit("konamiactive");
},
});
// or
app.directive('draggable', DraggableDirective)
app.component('Draggable', Draggable);
app.directive("draggable", DraggableDirective);
app.component("Draggable", Draggable);
// 注册全局 API
app.config.globalProperties.$tauriApi = tauriApi;
// app.use(BootstrapVueIcons);
app.mount("#app");

View File

@ -0,0 +1,112 @@
import { ref } from 'vue';
export function useHyperspectralDataParser() {
const parsedData = ref(null);
const error = ref(null);
// 解析时间结构
const parseTimeStruct = (timeData) => {
if (!timeData) return null;
return {
timezone: timeData.time_zone || 0,
year: timeData.year || 0,
month: timeData.month || 0,
day: timeData.day || 0,
hour: timeData.hour || 0,
minute: timeData.minute || 0,
second: timeData.second || 0,
millisecond: timeData.millisecond || 0
};
};
// 解析设备元数据
const parseDeviceInfo = (metaData) => {
if (!metaData || metaData.info_type !== "devinfo") {
return null;
}
return {
infoType: metaData.info_type,
sensorId: metaData.sensor_id || '',
bandNum: metaData.bandnum || 0,
waveCoeff: {
a1: metaData.wave_coeff?.a1 || 0,
a2: metaData.wave_coeff?.a2 || 0,
a3: metaData.wave_coeff?.a3 || 0,
a4: metaData.wave_coeff?.a4 || 0
}
};
};
// 解析光谱数据
const parseSpectralData = (uint8Array, bands, dataType) => {
try {
switch (dataType) {
case 0x20: // float32
return new Float32Array(uint8Array.buffer, 0, bands);
case 0x21: // float64
return new Float64Array(uint8Array.buffer, 0, bands);
case 0x10: // uint8
return new Uint8Array(uint8Array.buffer, 0, bands);
case 0x11: // int16
return new Int16Array(uint8Array.buffer, 0, bands);
case 0x12: // uint16
return new Uint16Array(uint8Array.buffer, 0, bands);
case 0x13: // int32
return new Int32Array(uint8Array.buffer, 0, bands);
case 0x14: // uint32
return new Uint32Array(uint8Array.buffer, 0, bands);
default:
throw new Error(`Unsupported data type: 0x${dataType.toString(16)}`);
}
} catch (e) {
error.value = `Failed to parse spectral data: ${e.message}`;
return null;
}
};
// 主解析函数
const parseHyperspectralData = (rawData, metaData) => {
try {
if (!rawData || !rawData.spectral_data || !rawData.bands) {
throw new Error('Invalid data format: missing required fields');
}
const uint8Array = new Uint8Array(rawData.spectral_data);
const deviceInfo = metaData ? parseDeviceInfo(metaData) : null;
const result = {
deviceInfo,
dataInfo: {
name: rawData.name || '',
sensorId: rawData.sensor_id || '',
fiberId: rawData.fiber_id || 0,
collectionTime: rawData.collection_time ? parseTimeStruct(rawData.collection_time) : null,
exposure: rawData.exposure || 0,
gain: rawData.gain || 0,
dataType: rawData.data_type || 0,
pixelSize: rawData.pixel_size || 0,
groundType: rawData.ground_type || 0,
validFlag: rawData.valid_flag || 0,
bands: rawData.bands || 0
},
spectralData: parseSpectralData(uint8Array, rawData.bands, rawData.data_type)
};
parsedData.value = result;
return result;
} catch (e) {
error.value = `Data parsing error: ${e.message}`;
return null;
}
};
// 重置状态
const reset = () => {
parsedData.value = null;
error.value = null;
};
return {
parsedData,
error,
parseHyperspectralData,
reset
};
}

View File

@ -0,0 +1,455 @@
class getIrisDataDispose {
spectralName;
irisData;
spectralInfoData;
spectral_data;
image_info;
devinfoData;
environmentData;
constructor(irisData, spectralName) {
this.irisData = irisData;
this.spectralName = spectralName;
this.spectral_data = irisData.spectral_data_section;
this.image_info = irisData.image_info_section;
for (const element of irisData.spectral_info_section) {
if (element.info_type == "devinfo") {
this.devinfoData = element;
} else if (element.info_type == "environment") {
this.environmentData = element;
}
}
}
initData() {
const basicTypes = ["ground_dn", "flat_dn", "dark_dn", "gain"];
const specialTypes = [
"flat_ref",
"radiance_ground",
"radiance_flat",
"refrad",
];
if (basicTypes.includes(this.spectralName)) {
return this.getFileSpectralData();
} else if (specialTypes.includes(this.spectralName)) {
return this.getSpecialFileSpectralData();
}
}
//获取一般后端直接返回的类型
getFileSpectralData() {
for (const element of this.spectral_data) {
const typedArray = manageSpectralData(element, this.devinfoData);
const normalArray = Array.from(typedArray);
if (element.name.toLowerCase().includes(this.spectralName)) {
return { ...element, normalArray };
}
}
}
//获取需要计算的类型
getSpecialFileSpectralData() {
let spectralDataMap = new Map([
["ground_dn", null],
["flat_dn", null],
["dark_dn", null],
["flat_ref", null],
["gain", null],
["radiance_ground", null],
["radiance_flat", null],
["refrad", null],
]);
for (const element of this.spectral_data) {
const typedArray = manageSpectralData(element, this.devinfoData);
const normalArray = Array.from(typedArray);
if (element.name.toLowerCase().includes("ground_dn")) {
spectralDataMap.set("ground_dn", { ...element, normalArray });
} else if (element.name.toLowerCase().includes("flat_dn")) {
spectralDataMap.set("flat_dn", { ...element, normalArray });
} else if (element.name.toLowerCase().includes("dark_dn")) {
spectralDataMap.set("dark_dn", { ...element, normalArray });
} else if (element.name.toLowerCase().includes("gain")) {
spectralDataMap.set("gain", { ...element, normalArray });
}
}
if (
spectralDataMap.get("ground_dn") &&
spectralDataMap.get("flat_dn") &&
spectralDataMap.get("dark_dn") &&
spectralDataMap.get("gain")
) {
const obj1 = {
ground_dn: spectralDataMap.get("ground_dn").normalArray,
flat_dn: spectralDataMap.get("flat_dn").normalArray,
dark_dn: spectralDataMap.get("dark_dn").normalArray,
gain: spectralDataMap.get("gain").normalArray,
};
const arrAll = mergeObjectArrays(obj1);
const name = spectralDataMap.get("ground_dn").name.split("_")[0];
// 通用的数值验证函数
const validateAndPush = (array, value, type, invalidCount) => {
if (isFinite(value) && !isNaN(value)) {
array.push(value);
return true;
} else {
invalidCount.invalidResult++;
return false;
}
};
// 通用的除零检查函数
const checkDivision = (numerator, denominator, type, invalidCount) => {
if (Math.abs(denominator) < 1e-10) {
invalidCount.zeroDiv++;
return null;
}
return numerator / denominator;
};
// 通用的结果验证和返回函数
const validateResult = (array, name, type) => {
if (array.length === 0) {
console.error(`${type}计算错误: 没有有效的数据点,无法生成图表`);
return { normalArray: [], name: name };
}
return { normalArray: array, name: name };
};
if (this.spectralName == "radiance_ground") {
const radiance_ground = [];
const invalidCount = { zeroDiv: 0, invalidResult: 0 };
for (const element of arrAll) {
const gainExposure = spectralDataMap.get("gain").exposure;
const groundExposure = spectralDataMap.get("ground_dn").exposure;
// 检查曝光时间除零 - 改为补充0值
if (Math.abs(groundExposure) < 1e-10) {
invalidCount.zeroDiv++;
radiance_ground.push(0);
continue;
}
const exposureRatio = gainExposure / groundExposure;
const dnDiff = element.ground_dn - element.dark_dn;
const a = exposureRatio * (element.gain * dnDiff);
// 验证结果无效时补充0值
if (isFinite(a) && !isNaN(a)) {
radiance_ground.push(a);
} else {
invalidCount.invalidResult++;
radiance_ground.push(0);
}
}
// 记录统计信息
if (invalidCount.zeroDiv > 0 || invalidCount.invalidResult > 0) {
console.warn(`radiance_ground计算统计: 除零错误 ${invalidCount.zeroDiv} 个,无效结果 ${invalidCount.invalidResult} 个,有效数据 ${radiance_ground.length}`);
}
return validateResult(radiance_ground, name + "_radiance_ground", 'radiance_ground');
} else if (this.spectralName == "radiance_flat") {
const radiance_flat = [];
const invalidCount = { zeroDiv: 0, invalidResult: 0 };
for (const element of arrAll) {
const gainExposure = spectralDataMap.get("gain").exposure;
const flatExposure = spectralDataMap.get("flat_dn").exposure;
// 检查曝光时间除零 - 改为补充0值
if (Math.abs(flatExposure) < 1e-10) {
invalidCount.zeroDiv++;
radiance_flat.push(0);
continue;
}
const exposureRatio = gainExposure / flatExposure;
const dnDiff = element.flat_dn - element.dark_dn;
const b = exposureRatio * (element.gain * dnDiff);
// 验证结果无效时补充0值
if (isFinite(b) && !isNaN(b)) {
radiance_flat.push(b);
} else {
invalidCount.invalidResult++;
radiance_flat.push(0);
}
}
// 记录统计信息
if (invalidCount.zeroDiv > 0 || invalidCount.invalidResult > 0) {
console.warn(`radiance_flat计算统计: 除零错误 ${invalidCount.zeroDiv} 个,无效结果 ${invalidCount.invalidResult} 个,有效数据 ${radiance_flat.length}`);
}
return validateResult(radiance_flat, name + "_radiance_flat", 'radiance_flat');
} else if (this.spectralName == "refrad") {
const refrad = [];
const invalidCount = { zeroDiv: 0, invalidResult: 0 };
for (const element of arrAll) {
const gainExposure = spectralDataMap.get("gain").exposure;
const groundExposure = spectralDataMap.get("ground_dn").exposure;
const flatExposure = spectralDataMap.get("flat_dn").exposure;
// 检查曝光时间除零 - 改为补充0值
if (Math.abs(groundExposure) < 1e-10 || Math.abs(flatExposure) < 1e-10) {
invalidCount.zeroDiv++;
refrad.push(0);
continue;
}
const groundRatio = gainExposure / groundExposure;
const flatRatio = gainExposure / flatExposure;
const groundDnDiff = element.ground_dn - element.dark_dn;
const flatDnDiff = element.flat_dn - element.dark_dn;
const a = groundRatio * (element.gain * groundDnDiff);
const b = flatRatio * (element.gain * flatDnDiff);
// 检查分母b是否为零 - 改为补充0值
if (Math.abs(b) < 1e-10) {
invalidCount.zeroDiv++;
refrad.push(0);
} else {
const c = a / b;
if (isFinite(c) && !isNaN(c)) {
refrad.push(c);
} else {
invalidCount.invalidResult++;
refrad.push(0);
}
}
}
// 记录统计信息
if (invalidCount.zeroDiv > 0 || invalidCount.invalidResult > 0) {
console.warn(`refrad计算统计: 除零错误 ${invalidCount.zeroDiv} 个,无效结果 ${invalidCount.invalidResult} 个,有效数据 ${refrad.length}`);
}
return validateResult(refrad, name + "_refrad", 'refrad');
} else if (this.spectralName == "flat_ref") {
const validData = [];
const invalidCount = { zeroDiv: 0, invalidResult: 0 };
for (const element of arrAll) {
const denominator = element.flat_dn - element.dark_dn;
const numerator = element.ground_dn - element.dark_dn;
// 检查分母是否为零或接近零 - 改为补充0值
if (Math.abs(denominator) < 1e-10) {
invalidCount.zeroDiv++;
validData.push(0);
} else {
const b = numerator / denominator;
if (isFinite(b) && !isNaN(b)) {
validData.push(b);
} else {
invalidCount.invalidResult++;
validData.push(0);
}
}
}
// 记录统计信息
if (invalidCount.zeroDiv > 0 || invalidCount.invalidResult > 0) {
console.warn(`flat_ref计算统计: 除零错误 ${invalidCount.zeroDiv} 个,无效结果 ${invalidCount.invalidResult} 个,有效数据 ${validData.length}`);
}
return validateResult(validData, name + "_flat_ref", 'flat_ref');
}
}
}
getSpectralInfoData() {
let spectralInfoData = [];
for (let i = 0; i < this.devinfoData.bandnum; i++) {
const a =
this.devinfoData.wave_coeff.a1 * i ** 3 +
this.devinfoData.wave_coeff.a2 * i * i +
this.devinfoData.wave_coeff.a3 * i +
this.devinfoData.wave_coeff.a4;
spectralInfoData.push(a);
}
return spectralInfoData;
}
parseImageData(image_info) {
if (!image_info || image_info.length === 0) {
console.log("No image data found");
return [];
}
const imageInfos = image_info;
const images = [];
for (const imageInfo of imageInfos) {
// 获取图像类型
const imageType = imageInfo.info_type;
let mimeType;
switch (imageType) {
case 0:
mimeType = "image/jpeg";
break;
case 1:
mimeType = "image/png";
break;
case 2:
mimeType = "image/tiff";
break;
case 3:
mimeType = "application/octet-stream"; // 原始数据
break;
default:
mimeType = "application/octet-stream";
}
// 创建Blob对象
const imageBlob = new Blob([new Uint8Array(imageInfo.image_data)], {
type: mimeType,
});
// 创建URL
const imageUrl = URL.createObjectURL(imageBlob);
images.push({
name: imageInfo.name,
type: imageType,
url: imageUrl,
time: imageInfo.collection_time,
});
}
return images;
}
}
function mergeObjectArrays(obj) {
const keys = Object.keys(obj);
const length = obj[keys[0]].length;
const result = [];
for (let i = 0; i < length; i++) {
const item = {};
for (const key of keys) {
item[key] = obj[key][i];
}
result.push(item);
}
return result;
}
const manageSpectralData = (rawData, devinfoData) => {
const bands = devinfoData.bandnum;
const uint8Array = new Uint8Array(rawData.spectral_data);
try {
switch (rawData.data_type) {
case 0x20: // float32
return new Float32Array(uint8Array.buffer, 0, bands);
case 0x21: // float64
return new Float64Array(uint8Array.buffer, 0, bands);
case 0x10: // uint8
return new Uint8Array(uint8Array.buffer, 0, bands);
case 0x11: // int16
return new Int16Array(uint8Array.buffer, 0, bands);
case 0x12: // uint16
return new Uint16Array(uint8Array.buffer, 0, bands);
case 0x13: // int32
return new Int32Array(uint8Array.buffer, 0, bands);
case 0x14: // uint32
return new Uint32Array(uint8Array.buffer, 0, bands);
default:
throw new Error(`Unsupported data type: 0x${dataType.toString(16)}`);
}
} catch (e) {
error.value = `Failed to parse spectral data: ${e.message}`;
return null;
}
};
const spectralTypeList = [
{
value: "ground_dn",
label: "地物DN",
},
{
value: "flat_dn",
label: "参考DN",
},
{
value: "dark_dn",
label: "暗噪DN",
},
{
value: "flat_ref",
label: "反射率",
},
{
value: "gain",
label: "能量校准文件",
},
{
value: "radiance_ground",
label: "地物辐射亮度",
},
{
value: "radiance_flat",
label: "参考辐射亮度",
},
{
value: "refrad",
label: "反射率能量",
},
{
value: "jdfs",
label: "绝对反射率",
},
{
value: "ckbzwj",
label: "参考校准文件",
},
{
value: "fszd",
label: "辐射照度",
},
];
const spectralProcessTypeList = [
{
value: "D1",
label: "一阶导数",
},
{
value: "D2",
label: "二阶导数",
},
{
value: "MA",
label: "移动平均平滑",
},
];
function D1(arr) {
const result = [];
for (let i = 1; i < arr.length; i++) {
result.push(arr[i] - arr[i - 1]);
}
result.push(arr[arr.length - 1]);
return result;
}
function D2(arr) {
const arr2 = D1(arr);
return D1(arr2);
}
export {
spectralTypeList,
getIrisDataDispose,
spectralProcessTypeList,
D1,
D2,
};

View File

@ -0,0 +1,106 @@
import { getIrisDataDispose, D1, D2 } from '../utils/irisDataDispose';
import { invoke } from "@tauri-apps/api/tauri";
/**
* 光谱数据处理服务
*/
export class SpectralDataService {
/**
* 提取文件名
*/
static extractFileName(filePath) {
const match = filePath.match(/[^\\\/]+$/);
return match ? match[0] : '';
}
/**
* 加载文件数据
*/
static async loadFileData(filePaths) {
const fileData = [];
for (const path of filePaths) {
try {
const src = await invoke("getoneirisfile", { path });
const fileName = this.extractFileName(path);
fileData.push({ data: src, name: fileName });
} catch (error) {
console.error(`加载文件失败: ${path}`, error);
}
}
return fileData;
}
/**
* 处理光谱数据
*/
static processSpectralData(fileData, spectralType, processType = '') {
const spectralDataList = [];
const imageList = [];
for (const element of fileData) {
if (!element.data) continue;
try {
const dispose = new getIrisDataDispose(element.data, spectralType);
const datay = dispose.initData();
const datax = dispose.getSpectralInfoData();
const environmentData = dispose.environmentData;
const img = dispose.parseImageData(element.data.image_info_section);
if (datay?.normalArray && datax) {
spectralDataList.push({
name: datay?.name || '',
datax: datax,
datay: datay.normalArray,
environmentData: { ...environmentData, fileName: element.name }
});
imageList.push(img);
}
} catch (error) {
console.error(`处理光谱数据失败: ${element.name}`, error);
}
}
return {
spectralDataList,
imageList,
processedData: this.applyProcessing(spectralDataList, processType)
};
}
/**
* 应用数据处理D1/D2等
*/
static applyProcessing(spectralDataList, processType) {
if (!processType) return spectralDataList;
switch (processType) {
case 'D1':
return spectralDataList.map(item => ({
name: item.name + '_D1',
datax: item.datax,
datay: D1(item.datay),
environmentData: item.environmentData
}));
case 'D2':
return spectralDataList.map(item => ({
name: item.name + '_D2',
datax: item.datax,
datay: D2(item.datay),
environmentData: item.environmentData
}));
default:
return spectralDataList;
}
}
/**
* 批量处理选定文件的光谱数据
*/
static processSelectedFiles(fileData, selectedFileNames, spectralType, processType = '') {
const filteredData = fileData.filter(file =>
selectedFileNames.includes(file.name)
);
return this.processSpectralData(filteredData, spectralType, processType);
}
}

118
src/utils/tauriApi.js Normal file
View File

@ -0,0 +1,118 @@
import { invoke } from "@tauri-apps/api/tauri";
import { dialog, fs } from "@tauri-apps/api";
export default {
/**
* 打开文件夹选择对话框并获取文件夹内容
* @param {string} defaultPath 默认路径
* @returns {Promise<Object>} 文件夹树形结构
*/
async getFolderList(defaultPath = "") {
try {
// 打开文件夹选择对话框
const options = {
defaultPath: defaultPath || "../",
directory: true,
title: "请选择文件夹",
};
const selectedPath = await dialog.open(options);
if (!selectedPath) {
return {
label: '请选择',
children: [],
};
}
// 读取文件夹内容并构建树形结构
const tree = await this.buildFolderTree(selectedPath);
return tree;
} catch (error) {
console.error("获取文件夹列表失败:", error);
return {
label: '请选择',
children: [],
};
}
},
/**
* 构建文件夹树形结构
* @param {string} folderPath 文件夹路径
* @returns {Promise<Object>} 树形结构对象
*/
async buildFolderTree(folderPath) {
try {
const entries = await fs.readDir(folderPath, { recursive: true });
// 获取文件夹名称
const pathParts = folderPath.split(/[\\\/]/);
const folderName = pathParts[pathParts.length - 1] || folderPath;
const obj = {
name: folderName,
path: folderPath,
children: entries,
isFolder: true,
isLeaf: false
}
const data = this.getTreeData(obj);
return data
} catch (error) {
console.error("构建文件夹树失败:", error);
return {
label: folderPath,
children: [],
path: folderPath,
isFolder: true,
isLeaf: false
};
}
},
getTreeData(entries) {
if (!entries) {
return null;
}
const obj = {
label: entries.name,
path: entries.path,
}
if (entries?.children && Array.isArray(entries.children)) {
// 处理数组
obj.children = entries.children.map((item) => {
return this.getTreeData(item);
});
obj.isFolder = true;
obj.isLeaf = false;
} else {
obj.isLeaf = true;
obj.isFolder = false;
}
return obj;
}
};
function deepTraverse(data) {
if (Array.isArray(data)) {
// 处理数组
data.forEach((item, index) => {
console.log(`数组项 ${index}:`, item);
deepTraverse(item); // 递归处理数组元素
});
} else if (typeof data === 'object' && data !== null) {
// 处理对象
Object.entries(data).forEach(([key, value]) => {
console.log(`属性 ${key}:`, value);
deepTraverse(value); // 递归处理对象属性
});
} else {
// 基本类型值
console.log('值:', data);
}
}

106
yarn.lock
View File

@ -124,6 +124,11 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
"@petamoriken/float16@^3.4.7":
version "3.9.2"
resolved "https://r.cnpmjs.org/@petamoriken/float16/-/float16-3.9.2.tgz"
integrity sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==
"@popperjs/core@^2.11.8", "@popperjs/core@npm:@sxzz/popperjs-es@^2.11.7":
version "2.11.8"
resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz"
@ -287,6 +292,11 @@
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.4.tgz"
integrity sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==
"@types/rbush@4.0.0":
version "4.0.0"
resolved "https://r.cnpmjs.org/@types/rbush/-/rbush-4.0.0.tgz"
integrity sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ==
"@types/web-bluetooth@^0.0.16":
version "0.0.16"
resolved "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz"
@ -694,6 +704,11 @@ destroy@^1.0.4:
resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
earcut@^3.0.0:
version "3.0.2"
resolved "https://r.cnpmjs.org/earcut/-/earcut-3.0.2.tgz"
integrity sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==
echart@^0.1.3:
version "0.1.3"
resolved "https://registry.npmjs.org/echart/-/echart-0.1.3.tgz"
@ -854,6 +869,20 @@ function-bind@^1.1.2:
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
geotiff@^2.1.3:
version "2.1.3"
resolved "https://r.cnpmjs.org/geotiff/-/geotiff-2.1.3.tgz"
integrity sha512-PT6uoF5a1+kbC3tHmZSUsLHBp2QJlHasxxxxPW47QIY1VBKpFB+FcDvX+MxER6UzgLQZ0xDzJ9s48B9JbOCTqA==
dependencies:
"@petamoriken/float16" "^3.4.7"
lerc "^3.0.0"
pako "^2.0.4"
parse-headers "^2.0.2"
quick-lru "^6.1.1"
web-worker "^1.2.0"
xml-utils "^1.0.2"
zstddec "^0.1.0"
glob-parent@^5.1.2, glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
@ -1096,6 +1125,11 @@ koa@^2.2.0:
type-is "^1.6.16"
vary "^1.1.2"
lerc@^3.0.0:
version "3.0.0"
resolved "https://r2.cnpmjs.org/lerc/-/lerc-3.0.0.tgz"
integrity sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==
less@*, less@^4.2.0:
version "4.2.0"
resolved "https://registry.npmjs.org/less/-/less-4.2.0.tgz"
@ -1339,6 +1373,17 @@ number-precision@^1.6.0:
resolved "https://registry.npmjs.org/number-precision/-/number-precision-1.6.0.tgz"
integrity sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ==
ol@^10.6.1:
version "10.6.1"
resolved "https://r.cnpmjs.org/ol/-/ol-10.6.1.tgz"
integrity sha512-xp174YOwPeLj7c7/8TCIEHQ4d41tgTDDhdv6SqNdySsql5/MaFJEJkjlsYcvOPt7xA6vrum/QG4UdJ0iCGT1cg==
dependencies:
"@types/rbush" "4.0.0"
earcut "^3.0.0"
geotiff "^2.1.3"
pbf "4.0.1"
rbush "^4.0.0"
on-finished@^2.3.0:
version "2.4.1"
resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz"
@ -1351,6 +1396,16 @@ only@~0.0.2:
resolved "https://registry.npmjs.org/only/-/only-0.0.2.tgz"
integrity sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==
pako@^2.0.4:
version "2.1.0"
resolved "https://r.cnpmjs.org/pako/-/pako-2.1.0.tgz"
integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==
parse-headers@^2.0.2:
version "2.0.6"
resolved "https://r.cnpmjs.org/parse-headers/-/parse-headers-2.0.6.tgz"
integrity sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==
parse-node-version@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz"
@ -1378,6 +1433,13 @@ pathe@^1.1.2:
resolved "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz"
integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==
pbf@4.0.1:
version "4.0.1"
resolved "https://r.cnpmjs.org/pbf/-/pbf-4.0.1.tgz"
integrity sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==
dependencies:
resolve-protobuf-schema "^2.1.0"
picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz"
@ -1421,6 +1483,11 @@ postcss@^8.4.35, postcss@^8.5.3:
picocolors "^1.1.1"
source-map-js "^1.2.1"
protocol-buffers-schema@^3.3.1:
version "3.6.0"
resolved "https://r2.cnpmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz"
integrity sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==
prr@~1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz"
@ -1441,11 +1508,21 @@ queue-microtask@^1.2.2:
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
quick-lru@^6.1.1:
version "6.1.2"
resolved "https://r.cnpmjs.org/quick-lru/-/quick-lru-6.1.2.tgz"
integrity sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==
quickselect@^1.0.1:
version "1.1.1"
resolved "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz"
integrity sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ==
quickselect@^3.0.0:
version "3.0.0"
resolved "https://r.cnpmjs.org/quickselect/-/quickselect-3.0.0.tgz"
integrity sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==
rbush@^2.0.2:
version "2.0.2"
resolved "https://registry.npmjs.org/rbush/-/rbush-2.0.2.tgz"
@ -1453,6 +1530,13 @@ rbush@^2.0.2:
dependencies:
quickselect "^1.0.1"
rbush@^4.0.0:
version "4.0.1"
resolved "https://r.cnpmjs.org/rbush/-/rbush-4.0.1.tgz"
integrity sha512-IP0UpfeWQujYC8Jg162rMNc01Rf0gWMMAb2Uxus/Q0qOFw4lCcq6ZnQEZwUoJqWyUGJ9th7JjwI4yIWo+uvoAQ==
dependencies:
quickselect "^3.0.0"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz"
@ -1465,6 +1549,13 @@ resize-observer-polyfill@^1.5.1:
resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz"
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
resolve-protobuf-schema@^2.1.0:
version "2.1.0"
resolved "https://r2.cnpmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz"
integrity sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==
dependencies:
protocol-buffers-schema "^3.3.1"
resolve@^1.22.8:
version "1.22.8"
resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz"
@ -1789,6 +1880,11 @@ vue3-konami-code@^1.0.0:
resolved "https://registry.npmjs.org/vue3-konami-code/-/vue3-konami-code-1.0.0.tgz"
integrity sha512-fhHAPFZA1jsgDHlYz5dnG52RmY9+vtfMthBaY1S5VBxNLdU4EWTbnJO0nfL8Uybw5uHsDqCGtTbGaX7nIvQ9Qw==
web-worker@^1.2.0:
version "1.5.0"
resolved "https://r.cnpmjs.org/web-worker/-/web-worker-1.5.0.tgz"
integrity sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==
webpack-sources@^3.2.3:
version "3.2.3"
resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz"
@ -1799,6 +1895,11 @@ webpack-virtual-modules@^0.6.1:
resolved "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.1.tgz"
integrity sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==
xml-utils@^1.0.2:
version "1.10.2"
resolved "https://r.cnpmjs.org/xml-utils/-/xml-utils-1.10.2.tgz"
integrity sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA==
ylru@^1.2.0:
version "1.4.0"
resolved "https://registry.npmjs.org/ylru/-/ylru-1.4.0.tgz"
@ -1810,3 +1911,8 @@ zrender@5.5.0:
integrity sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==
dependencies:
tslib "2.3.0"
zstddec@^0.1.0:
version "0.1.0"
resolved "https://r.cnpmjs.org/zstddec/-/zstddec-0.1.0.tgz"
integrity sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==