fix:导出功能

zlx
zhoulexin 9 hours ago
parent 8ff1b05f22
commit 4bbb398c46

107
package-lock.json generated

@ -12,6 +12,8 @@
"axios": "^1.18.1", "axios": "^1.18.1",
"echarts": "^6.1.0", "echarts": "^6.1.0",
"element-plus": "^2.9.7", "element-plus": "^2.9.7",
"file-saver": "^2.0.5",
"jszip": "^3.10.1",
"pinia": "^2.3.0", "pinia": "^2.3.0",
"vue": "^3.5.38", "vue": "^3.5.38",
"vue-router": "^4.5.0" "vue-router": "^4.5.0"
@ -1226,6 +1228,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"license": "MIT"
},
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.2.3", "version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
@ -1458,6 +1466,12 @@
} }
} }
}, },
"node_modules/file-saver": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==",
"license": "MIT"
},
"node_modules/fill-range": { "node_modules/fill-range": {
"version": "7.1.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@ -1645,6 +1659,12 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
"license": "MIT"
},
"node_modules/immutable": { "node_modules/immutable": {
"version": "5.1.7", "version": "5.1.7",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.7.tgz", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.7.tgz",
@ -1652,6 +1672,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/is-binary-path": { "node_modules/is-binary-path": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@ -1698,6 +1724,12 @@
"node": ">=0.12.0" "node": ">=0.12.0"
} }
}, },
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"license": "MIT"
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "9.0.1", "version": "9.0.1",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
@ -1705,6 +1737,27 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/jszip": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
"license": "(MIT OR GPL-3.0-or-later)",
"dependencies": {
"lie": "~3.3.0",
"pako": "~1.0.2",
"readable-stream": "~2.3.6",
"setimmediate": "^1.0.5"
}
},
"node_modules/lie": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
"license": "MIT",
"dependencies": {
"immediate": "~3.0.5"
}
},
"node_modules/lightningcss": { "node_modules/lightningcss": {
"version": "1.32.0", "version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
@ -2167,6 +2220,12 @@
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==", "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==",
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"license": "(MIT AND Zlib)"
},
"node_modules/pathe": { "node_modules/pathe": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
@ -2255,6 +2314,12 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"license": "MIT"
},
"node_modules/proxy-from-env": { "node_modules/proxy-from-env": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
@ -2302,6 +2367,21 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"license": "MIT",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/readdirp": { "node_modules/readdirp": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
@ -2385,6 +2465,12 @@
"queue-microtask": "^1.2.2" "queue-microtask": "^1.2.2"
} }
}, },
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT"
},
"node_modules/sass": { "node_modules/sass": {
"version": "1.101.0", "version": "1.101.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz",
@ -2414,6 +2500,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
"license": "MIT"
},
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@ -2423,6 +2515,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/strip-literal": { "node_modules/strip-literal": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz",
@ -2704,6 +2805,12 @@
"node": ">=8.10.0" "node": ">=8.10.0"
} }
}, },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/vite": { "node_modules/vite": {
"version": "8.1.0", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz",

@ -13,6 +13,8 @@
"axios": "^1.18.1", "axios": "^1.18.1",
"echarts": "^6.1.0", "echarts": "^6.1.0",
"element-plus": "^2.9.7", "element-plus": "^2.9.7",
"file-saver": "^2.0.5",
"jszip": "^3.10.1",
"pinia": "^2.3.0", "pinia": "^2.3.0",
"vue": "^3.5.38", "vue": "^3.5.38",
"vue-router": "^4.5.0" "vue-router": "^4.5.0"

@ -20,6 +20,12 @@ importers:
element-plus: element-plus:
specifier: ^2.9.7 specifier: ^2.9.7
version: 2.14.2(vue@3.5.38) version: 2.14.2(vue@3.5.38)
file-saver:
specifier: ^2.0.5
version: 2.0.5
jszip:
specifier: ^3.10.1
version: 3.10.1
pinia: pinia:
specifier: ^2.3.0 specifier: ^2.3.0
version: 2.3.1(vue@3.5.38) version: 2.3.1(vue@3.5.38)
@ -455,6 +461,9 @@ packages:
confbox@0.2.4: confbox@0.2.4:
resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==}
core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
csstype@3.2.3: csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
@ -539,6 +548,9 @@ packages:
picomatch: picomatch:
optional: true optional: true
file-saver@2.0.5:
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
fill-range@7.1.1: fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -596,9 +608,15 @@ packages:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'} engines: {node: '>= 6'}
immediate@3.0.6:
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
immutable@5.1.7: immutable@5.1.7:
resolution: {integrity: sha512-47Xb+LFbZ/ZIjQMj6Q5J3IfK7PJFuqRdFOC9FpGgRTK6U2dAEVmkR9hp58qU4FpYux5YXpneDwkj2EP6lppzFA==} resolution: {integrity: sha512-47Xb+LFbZ/ZIjQMj6Q5J3IfK7PJFuqRdFOC9FpGgRTK6U2dAEVmkR9hp58qU4FpYux5YXpneDwkj2EP6lppzFA==}
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
is-binary-path@2.1.0: is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -615,9 +633,18 @@ packages:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'} engines: {node: '>=0.12.0'}
isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
js-tokens@9.0.1: js-tokens@9.0.1:
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
jszip@3.10.1:
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
lie@3.3.0:
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
lightningcss-android-arm64@1.32.0: lightningcss-android-arm64@1.32.0:
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
@ -764,6 +791,9 @@ packages:
normalize-wheel-es@1.2.0: normalize-wheel-es@1.2.0:
resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==} resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==}
pako@1.0.11:
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
pathe@2.0.3: pathe@2.0.3:
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
@ -797,6 +827,9 @@ packages:
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
proxy-from-env@2.1.0: proxy-from-env@2.1.0:
resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -807,6 +840,9 @@ packages:
queue-microtask@1.2.3: queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
readable-stream@2.3.8:
resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
readdirp@3.6.0: readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'} engines: {node: '>=8.10.0'}
@ -827,6 +863,9 @@ packages:
run-parallel@1.2.0: run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
sass@1.101.0: sass@1.101.0:
resolution: {integrity: sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==} resolution: {integrity: sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
@ -835,10 +874,16 @@ packages:
scule@1.3.0: scule@1.3.0:
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
setimmediate@1.0.5:
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
source-map-js@1.2.1: source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
string_decoder@1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
strip-literal@2.1.1: strip-literal@2.1.1:
resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==} resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==}
@ -895,6 +940,9 @@ packages:
resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==}
engines: {node: '>=18.12.0'} engines: {node: '>=18.12.0'}
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
vite@8.1.0: vite@8.1.0:
resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==} resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
@ -1339,6 +1387,8 @@ snapshots:
confbox@0.2.4: {} confbox@0.2.4: {}
core-util-is@1.0.3: {}
csstype@3.2.3: {} csstype@3.2.3: {}
dayjs@1.11.21: {} dayjs@1.11.21: {}
@ -1424,6 +1474,8 @@ snapshots:
optionalDependencies: optionalDependencies:
picomatch: 4.0.4 picomatch: 4.0.4
file-saver@2.0.5: {}
fill-range@7.1.1: fill-range@7.1.1:
dependencies: dependencies:
to-regex-range: 5.0.1 to-regex-range: 5.0.1
@ -1484,8 +1536,12 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
immediate@3.0.6: {}
immutable@5.1.7: {} immutable@5.1.7: {}
inherits@2.0.4: {}
is-binary-path@2.1.0: is-binary-path@2.1.0:
dependencies: dependencies:
binary-extensions: 2.3.0 binary-extensions: 2.3.0
@ -1498,8 +1554,21 @@ snapshots:
is-number@7.0.0: {} is-number@7.0.0: {}
isarray@1.0.0: {}
js-tokens@9.0.1: {} js-tokens@9.0.1: {}
jszip@3.10.1:
dependencies:
lie: 3.3.0
pako: 1.0.11
readable-stream: 2.3.8
setimmediate: 1.0.5
lie@3.3.0:
dependencies:
immediate: 3.0.6
lightningcss-android-arm64@1.32.0: lightningcss-android-arm64@1.32.0:
optional: true optional: true
@ -1613,6 +1682,8 @@ snapshots:
normalize-wheel-es@1.2.0: {} normalize-wheel-es@1.2.0: {}
pako@1.0.11: {}
pathe@2.0.3: {} pathe@2.0.3: {}
picocolors@1.1.1: {} picocolors@1.1.1: {}
@ -1647,12 +1718,24 @@ snapshots:
picocolors: 1.1.1 picocolors: 1.1.1
source-map-js: 1.2.1 source-map-js: 1.2.1
process-nextick-args@2.0.1: {}
proxy-from-env@2.1.0: {} proxy-from-env@2.1.0: {}
quansync@0.2.11: {} quansync@0.2.11: {}
queue-microtask@1.2.3: {} queue-microtask@1.2.3: {}
readable-stream@2.3.8:
dependencies:
core-util-is: 1.0.3
inherits: 2.0.4
isarray: 1.0.0
process-nextick-args: 2.0.1
safe-buffer: 5.1.2
string_decoder: 1.1.1
util-deprecate: 1.0.2
readdirp@3.6.0: readdirp@3.6.0:
dependencies: dependencies:
picomatch: 2.3.2 picomatch: 2.3.2
@ -1686,6 +1769,8 @@ snapshots:
dependencies: dependencies:
queue-microtask: 1.2.3 queue-microtask: 1.2.3
safe-buffer@5.1.2: {}
sass@1.101.0: sass@1.101.0:
dependencies: dependencies:
chokidar: 5.0.0 chokidar: 5.0.0
@ -1696,8 +1781,14 @@ snapshots:
scule@1.3.0: {} scule@1.3.0: {}
setimmediate@1.0.5: {}
source-map-js@1.2.1: {} source-map-js@1.2.1: {}
string_decoder@1.1.1:
dependencies:
safe-buffer: 5.1.2
strip-literal@2.1.1: strip-literal@2.1.1:
dependencies: dependencies:
js-tokens: 9.0.1 js-tokens: 9.0.1
@ -1782,6 +1873,8 @@ snapshots:
picomatch: 4.0.4 picomatch: 4.0.4
webpack-virtual-modules: 0.6.2 webpack-virtual-modules: 0.6.2
util-deprecate@1.0.2: {}
vite@8.1.0(sass@1.101.0): vite@8.1.0(sass@1.101.0):
dependencies: dependencies:
lightningcss: 1.32.0 lightningcss: 1.32.0

@ -0,0 +1,41 @@
import JSZip from 'jszip'
import { saveAs } from 'file-saver'
/**
* URL 获取文件数据返回 ArrayBuffer
*/
async function fetchFileAsArrayBuffer(url) {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`下载文件失败: ${url} (${response.status})`)
}
return await response.arrayBuffer()
}
/**
* 将多个文件打包成 ZIP 并触发下载
* @param {Array<{name: string, url: string}>} files 文件名和远程 URL
* @param {string} outputFilename 输出的 ZIP 文件名 "检测结果_20260628.zip"
*/
export async function downloadAsZip(files, outputFilename) {
const zip = new JSZip()
// 并行下载所有文件
const results = await Promise.allSettled(
files.map(async (file) => {
const data = await fetchFileAsArrayBuffer(file.url)
zip.file(file.name, data, { binary: true })
})
)
// 检查是否有下载失败
const failures = results.filter(r => r.status === 'rejected')
if (failures.length > 0) {
const errors = failures.map(r => r.reason.message).join('; ')
throw new Error(`部分文件下载失败: ${errors}`)
}
// 生成 ZIP 并触发下载
const blob = await zip.generateAsync({ type: 'blob' })
saveAs(blob, outputFilename)
}

@ -4,7 +4,7 @@
:type="streamRunning ? 'danger' : 'primary'" :type="streamRunning ? 'danger' : 'primary'"
size="default" size="default"
@click="$emit('toggleStream')" @click="$emit('toggleStream')"
:disabled="streamAddressEmpty || (algorithmRunning && !streamRunning)" :disabled="streamAddressEmpty || (algorithmRunning && !streamRunning) || hasOfflineVideo"
> >
{{ streamRunning ? '关闭视频流' : '启动视频流' }} {{ streamRunning ? '关闭视频流' : '启动视频流' }}
</el-button> </el-button>
@ -12,7 +12,7 @@
:type="algorithmRunning ? 'danger' : 'success'" :type="algorithmRunning ? 'danger' : 'success'"
size="default" size="default"
@click="$emit('toggleAlgorithm')" @click="$emit('toggleAlgorithm')"
:disabled="offlineVideoMissing" :disabled="offlineVideoMissing || isLiveMode"
> >
{{ algorithmRunning ? '关闭算法' : '启动算法' }} {{ algorithmRunning ? '关闭算法' : '启动算法' }}
</el-button> </el-button>
@ -24,7 +24,9 @@ defineProps({
streamRunning: Boolean, streamRunning: Boolean,
algorithmRunning: Boolean, algorithmRunning: Boolean,
streamAddressEmpty: Boolean, streamAddressEmpty: Boolean,
offlineVideoMissing: Boolean offlineVideoMissing: Boolean,
isLiveMode: Boolean,
hasOfflineVideo: Boolean
}) })
defineEmits(['toggleStream', 'toggleAlgorithm']) defineEmits(['toggleStream', 'toggleAlgorithm'])

@ -6,6 +6,8 @@
:algorithm-running="algorithmRunning" :algorithm-running="algorithmRunning"
:stream-address-empty="!streamAddress" :stream-address-empty="!streamAddress"
:offline-video-missing="!offlineVideoUrl" :offline-video-missing="!offlineVideoUrl"
:is-live-mode="isLiveMode"
:has-offline-video="!!offlineVideoUrl"
@toggle-stream="toggleStream" @toggle-stream="toggleStream"
@toggle-algorithm="toggleAlgorithm" @toggle-algorithm="toggleAlgorithm"
/> />
@ -44,6 +46,14 @@
:ending="ending" :ending="ending"
@toggle-pause="togglePause" @toggle-pause="togglePause"
/> />
<div v-if="canExport && !algorithmRunning" class="export-bar">
<span class="export-label">检测已完成</span>
<el-button type="primary" size="small" :loading="exporting" @click="exportResults">
<el-icon :size="14"><Download /></el-icon>
导出结果
</el-button>
</div>
</div> </div>
<div class="right-section"> <div class="right-section">
<ChartsPanel :process-stats="processStats" :counts="detectCounts" /> <ChartsPanel :process-stats="processStats" :counts="detectCounts" />
@ -54,6 +64,7 @@
<script setup> <script setup>
import { ref, reactive, onMounted, onUnmounted, computed } from 'vue' import { ref, reactive, onMounted, onUnmounted, computed } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { Download } from '@element-plus/icons-vue'
import WebSocketClient from '@/utils/websocket' import WebSocketClient from '@/utils/websocket'
import VideoToolbar from './components/VideoToolbar.vue' import VideoToolbar from './components/VideoToolbar.vue'
@ -63,6 +74,7 @@ import ControlButtons from './components/ControlButtons.vue'
import FlowLog from './components/FlowLog.vue' import FlowLog from './components/FlowLog.vue'
import ChartsPanel from './components/ChartsPanel.vue' import ChartsPanel from './components/ChartsPanel.vue'
import fileHttp from '@/utils/fileHttp.js' import fileHttp from '@/utils/fileHttp.js'
import { downloadAsZip } from '@/utils/zip.js'
// ============ ============ // ============ ============
const videoMode = ref('idle') const videoMode = ref('idle')
@ -71,6 +83,7 @@ const webrtcUrl = computed(() => {
return import.meta.env.VITE_WEBRTC_URL || `http://${streamAddress.value}/stream` return import.meta.env.VITE_WEBRTC_URL || `http://${streamAddress.value}/stream`
}) })
const streamRunning = ref(false) const streamRunning = ref(false)
const isLiveMode = computed(() => videoMode.value === 'live')
function switchStream() { function switchStream() {
if (!streamAddress.value) { if (!streamAddress.value) {
@ -78,7 +91,7 @@ function switchStream() {
return return
} }
videoMode.value = 'live' videoMode.value = 'live'
ElMessage.success('已切换到实时视频流') clearAllLiveData()
} }
function toggleStream() { function toggleStream() {
@ -93,7 +106,7 @@ function toggleStream() {
addLog('已清空离线视频文件,如需检测请重新上传', 'warning') addLog('已清空离线视频文件,如需检测请重新上传', 'warning')
} }
videoMode.value = 'live' videoMode.value = 'live'
ElMessage.success('视频流已启动') clearAllLiveData()
} else { } else {
videoMode.value = 'idle' videoMode.value = 'idle'
ElMessage.info('视频流已关闭') ElMessage.info('视频流已关闭')
@ -116,7 +129,8 @@ function handleVideoUpload(file) {
detectionFile.value = file.raw detectionFile.value = file.raw
videoMode.value = 'offline' videoMode.value = 'offline'
resetDetectionState() resetDetectionState()
ElMessage.success('离线视频已加载') flowLogs.value = []
addLog('离线视频已加载','warning')
} }
// ============ 线WebSocket ============ // ============ 线WebSocket ============
@ -157,6 +171,12 @@ function resetDetectionState() {
processStats.incomplete = 0 processStats.incomplete = 0
} }
function clearAllLiveData() {
flowLogs.value = []
resetDetectionState()
addLog('已切换到实时流模式', 'warning')
}
async function sendFileInChunks(socket, file) { async function sendFileInChunks(socket, file) {
const chunkSize = 1024 * 1024 const chunkSize = 1024 * 1024
let offset = 0 let offset = 0
@ -175,6 +195,37 @@ const ending = ref(false)
let wsClient = null let wsClient = null
const videoAutoplayTrigger = ref(0) const videoAutoplayTrigger = ref(0)
// ============ ============
const canExport = ref(false)
const exportVideoUrl = ref('')
const exportReportUrl = ref('')
const exporting = ref(false)
async function exportResults() {
exporting.value = true
try {
const files = []
if (exportVideoUrl.value) {
files.push({ name: '检测结果视频.mp4', url: exportVideoUrl.value })
}
if (exportReportUrl.value) {
files.push({ name: '检测报告.txt', url: exportReportUrl.value })
}
if (files.length === 0) {
ElMessage.warning('没有可导出的文件')
return
}
const timestamp = new Date().toISOString().slice(0, 19).replace(/[:-]/g, '')
await downloadAsZip(files, `检测结果_${timestamp}.zip`)
ElMessage.success('导出成功')
} catch (err) {
ElMessage.error('导出失败: ' + (err.message || ''))
console.error('[导出错误]', err)
} finally {
exporting.value = false
}
}
function toggleAlgorithm() { function toggleAlgorithm() {
if (algorithmRunning.value && videoMode.value === 'offline' && detectionFile.value) { if (algorithmRunning.value && videoMode.value === 'offline' && detectionFile.value) {
endDetection() endDetection()
@ -292,6 +343,11 @@ function toggleAlgorithm() {
ending.value = false ending.value = false
wsClient.close() wsClient.close()
videoAutoplayTrigger.value++ videoAutoplayTrigger.value++
//
exportVideoUrl.value = videoUrl
exportReportUrl.value = msg.report_url ? (HTTP_BASE.replace(/\/$/, '') + msg.report_url) : ''
canExport.value = true
addLog('检测结果已就绪,可点击导出按钮下载', 'success')
break break
case 'error': case 'error':
@ -449,4 +505,26 @@ onUnmounted(() => {
} }
} }
} }
.export-bar {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
background: $bg-white;
padding: 10px 16px;
border-radius: $radius-md;
box-shadow: $shadow-sm;
.export-label {
font-size: 14px;
color: $text-secondary;
}
.el-button {
.el-icon {
margin-right: 4px;
}
}
}
</style> </style>

Loading…
Cancel
Save