优化了报告管理

This commit is contained in:
Xiaoyu 2025-02-06 03:11:48 +08:00
parent fcb6b77413
commit f7854c2462
6 changed files with 1035 additions and 284 deletions

501
package-lock.json generated
View File

@ -12,6 +12,7 @@
"axios": "^1.7.9",
"echarts": "^5.6.0",
"element-plus": "^2.9.3",
"json-server": "^1.0.0-beta.3",
"pinia": "^2.3.1",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
@ -856,6 +857,12 @@
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@polka/url": {
"version": "1.0.0-next.28",
"resolved": "https://registry.npmmirror.com/@polka/url/-/url-1.0.0-next.28.tgz",
"integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==",
"license": "MIT"
},
"node_modules/@popperjs/core": {
"name": "@sxzz/popperjs-es",
"version": "2.11.7",
@ -1133,6 +1140,244 @@
"win32"
]
},
"node_modules/@tinyhttp/accepts": {
"version": "2.2.3",
"resolved": "https://registry.npmmirror.com/@tinyhttp/accepts/-/accepts-2.2.3.tgz",
"integrity": "sha512-9pQN6pJAJOU3McmdJWTcyq7LLFW8Lj5q+DadyKcvp+sxMkEpktKX5sbfJgJuOvjk6+1xWl7pe0YL1US1vaO/1w==",
"license": "MIT",
"dependencies": {
"mime": "4.0.4",
"negotiator": "^0.6.3"
},
"engines": {
"node": ">=12.20.0"
},
"funding": {
"type": "individual",
"url": "https://github.com/tinyhttp/tinyhttp?sponsor=1"
}
},
"node_modules/@tinyhttp/app": {
"version": "2.5.0",
"resolved": "https://registry.npmmirror.com/@tinyhttp/app/-/app-2.5.0.tgz",
"integrity": "sha512-mwVY6RhTqF/s49tqNqpkRWbXAC7OmfpxR6YI6atp9xDauUOv3n1hv38AKy89Ga8HC8G2p1KR9vPD66VmF6VMnw==",
"license": "MIT",
"dependencies": {
"@tinyhttp/cookie": "2.1.1",
"@tinyhttp/proxy-addr": "2.2.0",
"@tinyhttp/req": "2.2.4",
"@tinyhttp/res": "2.2.4",
"@tinyhttp/router": "2.2.3",
"header-range-parser": "1.1.3",
"regexparam": "^2.0.2"
},
"engines": {
"node": ">=14.21.3"
},
"funding": {
"type": "individual",
"url": "https://github.com/tinyhttp/tinyhttp?sponsor=1"
}
},
"node_modules/@tinyhttp/content-disposition": {
"version": "2.2.2",
"resolved": "https://registry.npmmirror.com/@tinyhttp/content-disposition/-/content-disposition-2.2.2.tgz",
"integrity": "sha512-crXw1txzrS36huQOyQGYFvhTeLeG0Si1xu+/l6kXUVYpE0TjFjEZRqTbuadQLfKGZ0jaI+jJoRyqaWwxOSHW2g==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
},
"funding": {
"type": "individual",
"url": "https://github.com/tinyhttp/tinyhttp?sponsor=1"
}
},
"node_modules/@tinyhttp/content-type": {
"version": "0.1.4",
"resolved": "https://registry.npmmirror.com/@tinyhttp/content-type/-/content-type-0.1.4.tgz",
"integrity": "sha512-dl6f3SHIJPYbhsW1oXdrqOmLSQF/Ctlv3JnNfXAE22kIP7FosqJHxkz/qj2gv465prG8ODKH5KEyhBkvwrueKQ==",
"license": "MIT",
"engines": {
"node": ">=12.4"
}
},
"node_modules/@tinyhttp/cookie": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/@tinyhttp/cookie/-/cookie-2.1.1.tgz",
"integrity": "sha512-h/kL9jY0e0Dvad+/QU3efKZww0aTvZJslaHj3JTPmIPC9Oan9+kYqmh3M6L5JUQRuTJYFK2nzgL2iJtH2S+6dA==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
},
"funding": {
"type": "individual",
"url": "https://github.com/tinyhttp/tinyhttp?sponsor=1"
}
},
"node_modules/@tinyhttp/cookie-signature": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/@tinyhttp/cookie-signature/-/cookie-signature-2.1.1.tgz",
"integrity": "sha512-VDsSMY5OJfQJIAtUgeQYhqMPSZptehFSfvEEtxr+4nldPA8IImlp3QVcOVuK985g4AFR4Hl1sCbWCXoqBnVWnw==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/@tinyhttp/cors": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/@tinyhttp/cors/-/cors-2.0.1.tgz",
"integrity": "sha512-qrmo6WJuaiCzKWagv2yA/kw6hIISfF/hOqPWwmI6w0o8apeTMmRN3DoCFvQ/wNVuWVdU5J4KU7OX8aaSOEq51A==",
"license": "MIT",
"dependencies": {
"@tinyhttp/vary": "^0.1.3"
},
"engines": {
"node": ">=12.20 || 14.x || >=16"
}
},
"node_modules/@tinyhttp/encode-url": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/@tinyhttp/encode-url/-/encode-url-2.1.1.tgz",
"integrity": "sha512-AhY+JqdZ56qV77tzrBm0qThXORbsVjs/IOPgGCS7x/wWnsa/Bx30zDUU/jPAUcSzNOzt860x9fhdGpzdqbUeUw==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/@tinyhttp/etag": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/@tinyhttp/etag/-/etag-2.1.2.tgz",
"integrity": "sha512-j80fPKimGqdmMh6962y+BtQsnYPVCzZfJw0HXjyH70VaJBHLKGF+iYhcKqzI3yef6QBNa8DKIPsbEYpuwApXTw==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/@tinyhttp/forwarded": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/@tinyhttp/forwarded/-/forwarded-2.1.1.tgz",
"integrity": "sha512-nO3kq0R1LRl2+CAMlnggm22zE6sT8gfvGbNvSitV6F9eaUSurHP0A8YZFMihSkugHxK+uIegh1TKrqgD8+lyGQ==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/@tinyhttp/logger": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/@tinyhttp/logger/-/logger-2.0.0.tgz",
"integrity": "sha512-8DfLQjGDIaIJeivYamVrrpmwmsGwS8wt2DGvzlcY5HEBagdiI4QJy/veAFcUHuaJqufn4wLwmn4q5VUkW8BCpQ==",
"license": "MIT",
"dependencies": {
"colorette": "^2.0.20",
"dayjs": "^1.11.10",
"http-status-emojis": "^2.2.0"
},
"engines": {
"node": ">=14.18 || >=16.20"
}
},
"node_modules/@tinyhttp/proxy-addr": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/@tinyhttp/proxy-addr/-/proxy-addr-2.2.0.tgz",
"integrity": "sha512-WM/PPL9xNvrs7/8Om5nhKbke5FHrP3EfjOOR+wBnjgESfibqn0K7wdUTnzSLp1lBmemr88os1XvzwymSgaibyA==",
"license": "MIT",
"dependencies": {
"@tinyhttp/forwarded": "2.1.1",
"ipaddr.js": "^2.2.0"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/@tinyhttp/req": {
"version": "2.2.4",
"resolved": "https://registry.npmmirror.com/@tinyhttp/req/-/req-2.2.4.tgz",
"integrity": "sha512-lQAZIAo0NOeghxFOZS57tQzxpHSPPLs9T68Krq2BncEBImKwqaDKUt7M9Y5Kb+rvC/GwIL3LeErhkg7f5iG4IQ==",
"license": "MIT",
"dependencies": {
"@tinyhttp/accepts": "2.2.3",
"@tinyhttp/type-is": "2.2.4",
"@tinyhttp/url": "2.1.1",
"header-range-parser": "^1.1.3"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/@tinyhttp/res": {
"version": "2.2.4",
"resolved": "https://registry.npmmirror.com/@tinyhttp/res/-/res-2.2.4.tgz",
"integrity": "sha512-ETBRShnO19oJyIg2XQHQoofXPWeTXPAuwnIVYkU8WaftvXd/Vz4y5+WFQDHUzKlmdGOw5fAFnrEU7pIVMeFeVA==",
"license": "MIT",
"dependencies": {
"@tinyhttp/content-disposition": "2.2.2",
"@tinyhttp/cookie": "2.1.1",
"@tinyhttp/cookie-signature": "2.1.1",
"@tinyhttp/encode-url": "2.1.1",
"@tinyhttp/req": "2.2.4",
"@tinyhttp/send": "2.2.3",
"@tinyhttp/vary": "^0.1.3",
"es-escape-html": "^0.1.1",
"mime": "4.0.4"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/@tinyhttp/router": {
"version": "2.2.3",
"resolved": "https://registry.npmmirror.com/@tinyhttp/router/-/router-2.2.3.tgz",
"integrity": "sha512-O0MQqWV3Vpg/uXsMYg19XsIgOhwjyhTYWh51Qng7bxqXixxx2PEvZWnFjP7c84K7kU/nUX41KpkEBTLnznk9/Q==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/@tinyhttp/send": {
"version": "2.2.3",
"resolved": "https://registry.npmmirror.com/@tinyhttp/send/-/send-2.2.3.tgz",
"integrity": "sha512-o4cVHHGQ8WjVBS8UT0EE/2WnjoybrfXikHwsRoNlG1pfrC/Sd01u1N4Te8cOd/9aNGLr4mGxWb5qTm2RRtEi7g==",
"license": "MIT",
"dependencies": {
"@tinyhttp/content-type": "^0.1.4",
"@tinyhttp/etag": "2.1.2",
"mime": "4.0.4"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/@tinyhttp/type-is": {
"version": "2.2.4",
"resolved": "https://registry.npmmirror.com/@tinyhttp/type-is/-/type-is-2.2.4.tgz",
"integrity": "sha512-7F328NheridwjIfefBB2j1PEcKKABpADgv7aCJaE8x8EON77ZFrAkI3Rir7pGjopV7V9MBmW88xUQigBEX2rmQ==",
"license": "MIT",
"dependencies": {
"@tinyhttp/content-type": "^0.1.4",
"mime": "4.0.4"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/@tinyhttp/url": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/@tinyhttp/url/-/url-2.1.1.tgz",
"integrity": "sha512-POJeq2GQ5jI7Zrdmj22JqOijB5/GeX+LEX7DUdml1hUnGbJOTWDx7zf2b5cCERj7RoXL67zTgyzVblBJC+NJWg==",
"license": "MIT",
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/@tinyhttp/vary": {
"version": "0.1.3",
"resolved": "https://registry.npmmirror.com/@tinyhttp/vary/-/vary-0.1.3.tgz",
"integrity": "sha512-SoL83sQXAGiHN1jm2VwLUWQSQeDAAl1ywOm6T0b0Cg1CZhVsjoiZadmjhxF6FHCCY7OHHVaLnTgSMxTPIDLxMg==",
"license": "MIT",
"engines": {
"node": ">=12.20"
}
},
"node_modules/@types/estree": {
"version": "1.0.6",
"resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.6.tgz",
@ -1472,11 +1717,22 @@
"node": ">=8"
}
},
"node_modules/chalk": {
"version": "5.4.1",
"resolved": "https://registry.npmmirror.com/chalk/-/chalk-5.4.1.tgz",
"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
"license": "MIT",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chokidar": {
"version": "4.0.3",
"resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz",
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^4.0.1"
@ -1488,6 +1744,12 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/colorette": {
"version": "2.0.20",
"resolved": "https://registry.npmmirror.com/colorette/-/colorette-2.0.20.tgz",
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"license": "MIT"
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
@ -1542,6 +1804,21 @@
"node": ">=0.10"
}
},
"node_modules/dot-prop": {
"version": "9.0.0",
"resolved": "https://registry.npmmirror.com/dot-prop/-/dot-prop-9.0.0.tgz",
"integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==",
"license": "MIT",
"dependencies": {
"type-fest": "^4.18.2"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/echarts": {
"version": "5.6.0",
"resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz",
@ -1590,6 +1867,15 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/es-escape-html": {
"version": "0.1.1",
"resolved": "https://registry.npmmirror.com/es-escape-html/-/es-escape-html-0.1.1.tgz",
"integrity": "sha512-yUx1o+8RsG7UlszmYPtks+dm6Lho2m8lgHMOsLJQsFI0R8XwUJwiMhM1M4E/S8QLeGyf6MkDV/pWgjQ0tdTSyQ==",
"license": "MIT",
"engines": {
"node": ">=12.x"
}
},
"node_modules/esbuild": {
"version": "0.24.2",
"resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.24.2.tgz",
@ -1643,6 +1929,18 @@
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"license": "MIT"
},
"node_modules/eta": {
"version": "3.5.0",
"resolved": "https://registry.npmmirror.com/eta/-/eta-3.5.0.tgz",
"integrity": "sha512-e3x3FBvGzeCIHhF+zhK8FZA2vC5uFn6b4HJjegUbIWrDb4mJ7JjTGMJY9VGIbRVpmSwHopNiaJibhjIr+HfLug==",
"license": "MIT",
"engines": {
"node": ">=6.0.0"
},
"funding": {
"url": "https://github.com/eta-dev/eta?sponsor=1"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
@ -1716,6 +2014,21 @@
"he": "bin/he"
}
},
"node_modules/header-range-parser": {
"version": "1.1.3",
"resolved": "https://registry.npmmirror.com/header-range-parser/-/header-range-parser-1.1.3.tgz",
"integrity": "sha512-B9zCFt3jH8g09LR1vHL4pcAn8yMEtlSlOUdQemzHMRKMImNIhhszdeosYFfNW0WXKQtXIlWB+O4owHJKvEJYaA==",
"license": "MIT",
"engines": {
"node": ">=12.22.0"
}
},
"node_modules/http-status-emojis": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/http-status-emojis/-/http-status-emojis-2.2.0.tgz",
"integrity": "sha512-ompKtgwpx8ff0hsbpIB7oE4ax1LXoHmftsHHStMELX56ivG3GhofTX8ZHWlUaFKfGjcGjw6G3rPk7dJRXMmbbg==",
"license": "MIT"
},
"node_modules/immutable": {
"version": "5.0.3",
"resolved": "https://registry.npmmirror.com/immutable/-/immutable-5.0.3.tgz",
@ -1723,6 +2036,24 @@
"dev": true,
"license": "MIT"
},
"node_modules/inflection": {
"version": "3.0.2",
"resolved": "https://registry.npmmirror.com/inflection/-/inflection-3.0.2.tgz",
"integrity": "sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==",
"license": "MIT",
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/ipaddr.js": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
"integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==",
"license": "MIT",
"engines": {
"node": ">= 10"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
@ -1759,6 +2090,45 @@
"node": ">=0.12.0"
}
},
"node_modules/json-server": {
"version": "1.0.0-beta.3",
"resolved": "https://registry.npmmirror.com/json-server/-/json-server-1.0.0-beta.3.tgz",
"integrity": "sha512-DwE69Ep5ccwIJZBUIWEENC30Yj8bwr4Ax9W9VoIWAYnB8Sj4ReptscO8/DRHv/nXwVlmb3Bk73Ls86+VZdYkkA==",
"license": "SEE LICENSE IN ./LICENSE",
"dependencies": {
"@tinyhttp/app": "^2.4.0",
"@tinyhttp/cors": "^2.0.1",
"@tinyhttp/logger": "^2.0.0",
"chalk": "^5.3.0",
"chokidar": "^4.0.1",
"dot-prop": "^9.0.0",
"eta": "^3.5.0",
"inflection": "^3.0.0",
"json5": "^2.2.3",
"lowdb": "^7.0.1",
"milliparsec": "^4.0.0",
"sirv": "^2.0.4",
"sort-on": "^6.1.0"
},
"bin": {
"json-server": "lib/bin.js"
},
"engines": {
"node": ">=18.3"
}
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"license": "MIT",
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
@ -1782,6 +2152,21 @@
"lodash-es": "*"
}
},
"node_modules/lowdb": {
"version": "7.0.1",
"resolved": "https://registry.npmmirror.com/lowdb/-/lowdb-7.0.1.tgz",
"integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==",
"license": "MIT",
"dependencies": {
"steno": "^4.0.2"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
}
},
"node_modules/magic-string": {
"version": "0.30.17",
"resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz",
@ -1812,6 +2197,30 @@
"node": ">=8.6"
}
},
"node_modules/milliparsec": {
"version": "4.0.0",
"resolved": "https://registry.npmmirror.com/milliparsec/-/milliparsec-4.0.0.tgz",
"integrity": "sha512-/wk9d4Z6/9ZvoEH/6BI4TrTCgmkpZPuSRN/6fI9aUHOfXdNTuj/VhLS7d+NqG26bi6L9YmGXutVYvWC8zQ0qtA==",
"license": "MIT",
"engines": {
"node": ">=20"
}
},
"node_modules/mime": {
"version": "4.0.4",
"resolved": "https://registry.npmmirror.com/mime/-/mime-4.0.4.tgz",
"integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==",
"funding": [
"https://github.com/sponsors/broofa"
],
"license": "MIT",
"bin": {
"mime": "bin/cli.js"
},
"engines": {
"node": ">=16"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
@ -1849,6 +2258,15 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/mrmime": {
"version": "2.0.0",
"resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.0.tgz",
"integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/muggle-string": {
"version": "0.4.1",
"resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz",
@ -1874,6 +2292,15 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/negotiator": {
"version": "0.6.4",
"resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.4.tgz",
"integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz",
@ -1975,7 +2402,6 @@
"version": "4.1.1",
"resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.1.tgz",
"integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 14.18.0"
@ -1985,6 +2411,15 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/regexparam": {
"version": "2.0.2",
"resolved": "https://registry.npmmirror.com/regexparam/-/regexparam-2.0.2.tgz",
"integrity": "sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/rollup": {
"version": "4.34.3",
"resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.34.3.tgz",
@ -2045,6 +2480,35 @@
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/sirv": {
"version": "2.0.4",
"resolved": "https://registry.npmmirror.com/sirv/-/sirv-2.0.4.tgz",
"integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==",
"license": "MIT",
"dependencies": {
"@polka/url": "^1.0.0-next.24",
"mrmime": "^2.0.0",
"totalist": "^3.0.0"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/sort-on": {
"version": "6.1.0",
"resolved": "https://registry.npmmirror.com/sort-on/-/sort-on-6.1.0.tgz",
"integrity": "sha512-WTECP0nYNWO1n2g5bpsV0yZN9cBmZsF8ThHFbOqVN0HBFRoaQZLLEMvMmJlKHNPYQeVngeI5+jJzIfFqOIo1OA==",
"license": "MIT",
"dependencies": {
"dot-prop": "^9.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
@ -2054,6 +2518,18 @@
"node": ">=0.10.0"
}
},
"node_modules/steno": {
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/steno/-/steno-4.0.2.tgz",
"integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
@ -2068,12 +2544,33 @@
"node": ">=8.0"
}
},
"node_modules/totalist": {
"version": "3.0.1",
"resolved": "https://registry.npmmirror.com/totalist/-/totalist-3.0.1.tgz",
"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/tslib": {
"version": "2.3.0",
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
"license": "0BSD"
},
"node_modules/type-fest": {
"version": "4.33.0",
"resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-4.33.0.tgz",
"integrity": "sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g==",
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/typescript": {
"version": "5.6.3",
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.6.3.tgz",

View File

@ -13,6 +13,7 @@
"axios": "^1.7.9",
"echarts": "^5.6.0",
"element-plus": "^2.9.3",
"json-server": "^1.0.0-beta.3",
"pinia": "^2.3.1",
"vue": "^3.5.13",
"vue-router": "^4.5.0"

View File

@ -7,6 +7,7 @@ import {
Setting,
User,
DataAnalysis,
DataBoard,
Tools,
Document,
DataLine,
@ -43,7 +44,7 @@ const handleSelect = (key: string) => {
@select="handleSelect"
>
<el-menu-item index="/dashboard">
<el-icon><DataAnalysis /></el-icon>
<el-icon><DataBoard /></el-icon>
<template #title>控制台</template>
</el-menu-item>
@ -153,6 +154,7 @@ const handleSelect = (key: string) => {
}
.logo-title {
cursor: pointer;
margin: 0;
font-size: 18px;
font-weight: 500;

View File

@ -67,12 +67,12 @@ const router = createRouter({
{
path: 'report/daily',
name: 'DailyReports',
component: () => import('../views/reports/daily/index.vue')
component: () => import('../views/report/daily/index.vue')
},
{
path: 'report/analysis',
name: 'AnalysisReports',
component: () => import('../views/reports/analysis/index.vue')
component: () => import('../views/report/analysis/index.vue')
},
{
path: 'system/settings',

View File

@ -1,86 +1,88 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed } from 'vue';
import * as echarts from 'echarts';
import { ElMessageBox, ElMessage } from 'element-plus';
import { ref, onMounted, onUnmounted, computed } from "vue";
import * as echarts from "echarts";
import { ElMessageBox, ElMessage } from "element-plus";
import { Plus } from '@element-plus/icons-vue';
interface AnalysisReport {
id: number;
title: string;
type: 'species' | 'environment'; // /
timeRange: { //
type: "species" | "environment"; // /
timeRange: {
//
start: string;
end: string;
};
dataSource: { //
dataSource: {
//
type: string;
points: string[]; //
points: string[]; //
}[];
analysis: {
summary: string; //
trends: { //
indicator: string; //
trend: string; //
data: any[]; //
summary: string; //
trends: {
//
indicator: string; //
trend: string; //
data: any[]; //
}[];
abnormal: { //
abnormal: {
//
type: string;
description: string;
level: string;
}[];
};
recommendations: string[]; //
recommendations: string[]; //
}
//
const tableData = ref<AnalysisReport[]>([
{
id: 1,
title: '2024年第一季度水质监测分析报告',
type: 'environment',
title: "2024年第一季度水质监测分析报告",
type: "environment",
timeRange: {
start: '2024-01-01',
end: '2024-03-31'
start: "2024-01-01",
end: "2024-03-31",
},
dataSource: [
{
type: '水质监测',
points: ['A区-1号监测点', 'A区-2号监测点', 'B区-1号监测点']
}
type: "水质监测",
points: ["A区-1号监测点", "A区-2号监测点", "B区-1号监测点"],
},
],
analysis: {
summary: '第一季度水质总体保持稳定但3月份出现轻微波动',
summary: "第一季度水质总体保持稳定但3月份出现轻微波动",
trends: [
{
indicator: 'pH值',
trend: '稳定',
data: [7.1, 7.2, 7.0, 7.3]
indicator: "pH值",
trend: "稳定",
data: [7.1, 7.2, 7.0, 7.3],
},
{
indicator: '溶解氧',
trend: '下降',
data: [6.5, 6.3, 6.0, 5.8]
}
indicator: "溶解氧",
trend: "下降",
data: [6.5, 6.3, 6.0, 5.8],
},
],
abnormal: [
{
type: '溶解氧',
description: '3月底溶解氧水平略低于标准值',
level: '轻微'
}
]
type: "溶解氧",
description: "3月底溶解氧水平略低于标准值",
level: "轻微",
},
],
},
recommendations: [
'加强对B区-1号监测点的巡查频率',
'建议增加水体曝气设施'
]
}
recommendations: ["加强对B区-1号监测点的巡查频率", "建议增加水体曝气设施"],
},
]);
//
const filterForm = ref({
dateRange: [],
type: '',
indicator: ''
type: "",
indicator: "",
});
//
@ -94,17 +96,13 @@ const handleView = (row: AnalysisReport) => {
//
const handleExport = (row: AnalysisReport) => {
ElMessageBox.confirm(
'确认导出该分析报告?',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
}
)
ElMessageBox.confirm("确认导出该分析报告?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "info",
})
.then(() => {
ElMessage.success('导出成功,文件已下载');
ElMessage.success("导出成功,文件已下载");
})
.catch(() => {
//
@ -115,44 +113,44 @@ const handleExport = (row: AnalysisReport) => {
let myChart: echarts.ECharts | null = null;
const initChart = () => {
const chartDom = document.getElementById('trendChart');
const chartDom = document.getElementById("trendChart");
if (!chartDom) return;
myChart = echarts.init(chartDom);
const option = {
title: {
text: '监测指标趋势分析'
text: "监测指标趋势分析",
},
tooltip: {
trigger: 'axis'
trigger: "axis",
},
legend: {
data: ['pH值', '溶解氧', '水温']
data: ["pH值", "溶解氧", "水温"],
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月']
type: "category",
data: ["1月", "2月", "3月", "4月"],
},
yAxis: {
type: 'value'
type: "value",
},
series: [
{
name: 'pH值',
type: 'line',
data: [7.1, 7.2, 7.0, 7.3]
name: "pH值",
type: "line",
data: [7.1, 7.2, 7.0, 7.3],
},
{
name: '溶解氧',
type: 'line',
data: [6.5, 6.3, 6.0, 5.8]
name: "溶解氧",
type: "line",
data: [6.5, 6.3, 6.0, 5.8],
},
{
name: '水温',
type: 'line',
data: [15, 16, 18, 21]
}
]
name: "水温",
type: "line",
data: [15, 16, 18, 21],
},
],
};
myChart.setOption(option);
@ -160,11 +158,11 @@ const initChart = () => {
onMounted(() => {
initChart();
window.addEventListener('resize', handleResize);
window.addEventListener("resize", handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
window.removeEventListener("resize", handleResize);
myChart?.dispose();
});
@ -194,11 +192,11 @@ const handleCurrentChange = (val: number) => {
//
const dialogVisible = ref(false);
const formData = ref({
title: '',
type: '',
title: "",
type: "",
timeRange: [],
summary: '',
recommendations: ''
summary: "",
recommendations: "",
});
const handleCreate = () => {
@ -209,6 +207,12 @@ const handleSubmit = () => {
// TODO:
dialogVisible.value = false;
};
//
const handleRefresh = () => {
ElMessage.success('数据已更新');
// TODO:
};
</script>
<template>
@ -223,51 +227,43 @@ const handleSubmit = () => {
<template #header>
<div class="card-header">
<span>分析报告</span>
<el-button type="primary" @click="handleCreate">新建报告</el-button>
<el-button type="primary" :icon="Plus" @click="handleCreate">新建报告</el-button>
</div>
</template>
<!-- 筛选表单 -->
<el-form :model="filterForm" inline>
<el-form-item label="时间范围">
<el-date-picker
v-model="filterForm.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
<el-form-item label="分析类型">
<el-select v-model="filterForm.type" placeholder="请选择">
<el-option label="物种分析" value="species" />
<el-option label="环境分析" value="environment" />
</el-select>
</el-form-item>
<el-form-item label="监测指标">
<el-select v-model="filterForm.indicator" placeholder="请选择">
<el-option label="pH值" value="pH" />
<el-option label="溶解氧" value="oxygen" />
<el-option label="水温" value="temperature" />
</el-select>
</el-form-item>
</el-form>
<!-- 数据统计卡片 -->
<el-row :gutter="20" class="mb-20">
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-value">128</div>
<div class="stat-label">报告总数</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-value">45</div>
<div class="stat-label">本月新增</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" class="stat-card">
<div class="stat-value">24</div>
<div class="stat-label">待处理</div>
</el-card>
</el-col>
</el-row>
<!-- 数据表格 -->
<el-table :data="paginatedData" style="width: 100%">
<!-- 报告列表 -->
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="title" label="报告标题" min-width="200" />
<el-table-column prop="type" label="类型" width="120">
<template #default="{ row }">
{{ row.type === 'species' ? '物种分析' : '环境分析' }}
<el-tag :type="row.type === 'species' ? 'success' : 'primary'">
{{ row.type === 'species' ? '物种分析' : '环境分析' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="时间范围" width="200">
<template #default="{ row }">
{{ row.timeRange.start }} {{ row.timeRange.end }}
</template>
</el-table-column>
<el-table-column prop="analysis.summary" label="分析总结" min-width="300" />
<el-table-column label="操作" width="150" fixed="right">
<el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }">
<el-button type="primary" link @click="handleView(row)">查看</el-button>
<el-button type="warning" link @click="handleExport(row)">导出</el-button>
@ -275,26 +271,20 @@ const handleSubmit = () => {
</el-table-column>
</el-table>
<!-- 添加分页器 -->
<!-- 分页器 -->
<div class="pagination-container">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
:total="tableData.length"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-card>
<!-- 新建报告弹窗 -->
<el-dialog
v-model="dialogVisible"
title="新建分析报告"
width="800px"
>
<el-dialog v-model="dialogVisible" title="新建分析报告" width="800px">
<el-form :model="formData" label-width="100px">
<el-form-item label="报告标题" required>
<el-input v-model="formData.title" placeholder="请输入报告标题" />
@ -341,18 +331,14 @@ const handleSubmit = () => {
</el-dialog>
<!-- 添加详情弹窗 -->
<el-dialog
v-model="detailVisible"
title="分析报告详情"
width="900px"
>
<el-dialog v-model="detailVisible" title="分析报告详情" width="900px">
<template v-if="currentReport">
<el-descriptions :column="2" border>
<el-descriptions-item label="报告标题" :span="2">
{{ currentReport.title }}
</el-descriptions-item>
<el-descriptions-item label="分析类型">
{{ currentReport.type === 'species' ? '物种分析' : '环境分析' }}
{{ currentReport.type === "species" ? "物种分析" : "环境分析" }}
</el-descriptions-item>
<el-descriptions-item label="时间范围">
{{ currentReport.timeRange.start }} {{ currentReport.timeRange.end }}
@ -420,6 +406,8 @@ const handleSubmit = () => {
</template>
<style lang="scss" scoped>
@import "../../../styles/variables.scss";
.analysis-report {
.card-header {
display: flex;
@ -465,5 +453,27 @@ const handleSubmit = () => {
color: $text-regular;
}
}
.stat-card {
padding: 20px;
text-align: center;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
}
.stat-value {
font-size: 28px;
font-weight: 600;
color: $primary-color;
margin-bottom: 8px;
}
.stat-label {
font-size: 14px;
color: $text-secondary;
}
}
}
</style>

View File

@ -1,89 +1,151 @@
<script setup lang="ts">
import { ref } from 'vue';
import { ref, onMounted } from 'vue';
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
import { Plus, Document, Download, TrendCharts, Connection } from '@element-plus/icons-vue';
import * as echarts from 'echarts';
interface DailyReport {
id: number;
date: string; //
type: 'patrol' | 'monitor'; // /
area: string; //
personnel: string[]; //
content: {
patrolRoutes?: string[]; // 线
monitorPoints?: string[]; //
findings: string; //
actions: string; //
};
attachments: { //
title: string;
reporter: string;
department: string;
type: string;
content: string;
summary: string;
data: {
type: string;
url: string;
value: string | number;
trend?: string;
}[];
status: string; //
attachments: string[];
status: string;
createTime: string;
}
//
const tableData = ref<DailyReport[]>([
{
id: 1,
date: '2024-03-20',
type: 'patrol',
area: 'A区湿地',
personnel: ['张三', '李四'],
content: {
patrolRoutes: ['A区-1号点', 'A区-2号点', 'A区-3号点'],
findings: '发现2号监测点附近有水质异常',
actions: '已采集样本送检,并通知相关部门'
},
attachments: [
{ type: 'image', url: 'sample1.jpg' },
{ type: 'data', url: 'water_quality.xlsx' }
title: "A区巡护日报",
reporter: "张三",
department: "巡护部",
type: "巡护报告",
content: "今日巡护发现...",
summary: "今日巡护总体情况良好发现并处理2处异常情况",
data: [
{ type: "巡护路线", value: "3条" },
{ type: "巡护时长", value: "4小时" },
{ type: "发现问题", value: 2, trend: "down" },
{ type: "处理完成", value: 2 }
],
status: '已提交'
attachments: ["report1.pdf", "image1.jpg"],
status: "已提交",
createTime: "2024-03-20 10:30",
},
{
id: 2,
date: '2024-03-20',
type: 'monitor',
area: 'B区',
personnel: ['王五'],
content: {
monitorPoints: ['B区-水质监测点', 'B区-空气监测点'],
findings: '各项指标正常',
actions: '完成日常数据记录'
},
attachments: [
{ type: 'data', url: 'monitor_data.xlsx' }
],
status: '已审核'
}
// ...
]);
//
const filterForm = ref({
dateRange: [],
type: '',
area: '',
status: ''
});
//
const currentPage = ref(1);
const pageSize = ref(10);
//
const dialogVisible = ref(false);
const formData = ref({
type: '',
area: '',
personnel: [],
findings: '',
actions: '',
title: "",
type: "",
content: "",
attachments: []
});
//
const detailVisible = ref(false);
const currentReport = ref<DailyReport | null>(null);
const handleView = (row: DailyReport) => {
currentReport.value = row;
detailVisible.value = true;
};
const handleCreate = () => {
dialogVisible.value = true;
};
const handleSubmit = () => {
// TODO:
ElMessage.success('提交成功');
dialogVisible.value = false;
};
//
let trendChart: echarts.ECharts | null = null;
//
const initTrendChart = () => {
const chartDom = document.getElementById('trendChart');
if (!chartDom) return;
trendChart = echarts.init(chartDom);
const option = {
title: { text: '近7天报告提交趋势' },
tooltip: { trigger: 'axis' },
xAxis: { type: 'category', data: ['3-14', '3-15', '3-16', '3-17', '3-18', '3-19', '3-20'] },
yAxis: { type: 'value' },
series: [
{
name: '巡护报告',
type: 'line',
data: [5, 6, 4, 8, 7, 6, 5],
smooth: true
},
{
name: '监测报告',
type: 'line',
data: [3, 4, 3, 5, 4, 5, 4],
smooth: true
}
]
};
trendChart.setOption(option);
};
onMounted(() => {
initTrendChart();
});
//
const handleGenerate = () => {
ElMessageBox.confirm('是否生成今日巡护报告?系统将自动汇总数据。', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
}).then(() => {
//
const loadingInstance = ElLoading.service({
text: '正在汇总数据,请稍候...'
});
setTimeout(() => {
loadingInstance.close();
ElMessage.success('报告生成成功,已自动整合今日巡护记录和监测数据');
}, 2000);
});
};
//
const handleAnalyze = () => {
ElMessageBox.confirm('是否开始分析近期报告数据趋势?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
}).then(() => {
detailVisible.value = true;
currentReport.value = null;
showTrendAnalysis.value = true;
});
};
const showTrendAnalysis = ref(false);
</script>
<template>
@ -92,130 +154,118 @@ const handleSubmit = () => {
<template #header>
<div class="card-header">
<span>日常报告</span>
<el-button type="primary" @click="handleCreate">新建报告</el-button>
<div class="header-btns">
<el-button :icon="Document" @click="handleGenerate">生成报告</el-button>
<el-button :icon="TrendCharts" @click="handleAnalyze">趋势分析</el-button>
<el-button type="primary" :icon="Plus" @click="handleCreate">新建报告</el-button>
</div>
</div>
</template>
<!-- 筛选表单 -->
<el-form :model="filterForm" inline>
<el-form-item label="日期范围">
<el-date-picker
v-model="filterForm.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
<el-form-item label="报告类型">
<el-select v-model="filterForm.type" placeholder="请选择">
<el-option label="巡护报告" value="patrol" />
<el-option label="监测报告" value="monitor" />
</el-select>
</el-form-item>
<el-form-item label="区域">
<el-select v-model="filterForm.area" placeholder="请选择">
<el-option label="A区" value="A区" />
<el-option label="B区" value="B区" />
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="filterForm.status" placeholder="请选择">
<el-option label="已提交" value="已提交" />
<el-option label="已审核" value="已审核" />
</el-select>
</el-form-item>
</el-form>
<!-- 统计卡片 -->
<el-row :gutter="20" class="mb-20">
<el-col :span="6">
<el-card shadow="hover" class="stat-card">
<div class="stat-value">256</div>
<div class="stat-label">报告总数</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover" class="stat-card">
<div class="stat-value">68</div>
<div class="stat-label">本月新增</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover" class="stat-card">
<div class="stat-value">12</div>
<div class="stat-label">待审核</div>
</el-card>
</el-col>
<el-col :span="6">
<el-card shadow="hover" class="stat-card">
<div class="stat-value">98%</div>
<div class="stat-label">按时提交率</div>
</el-card>
</el-col>
</el-row>
<!-- 数据表格 -->
<!-- 趋势图表 -->
<el-card class="mb-20">
<template #header>
<div class="chart-header">
<span>报告趋势分析</span>
<el-tooltip content="系统自动分析近期报告数据,预测未来趋势">
<el-icon><Connection /></el-icon>
</el-tooltip>
</div>
</template>
<div id="trendChart" style="height: 300px"></div>
</el-card>
<!-- 报告列表 -->
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="日期" width="120" />
<el-table-column prop="title" label="报告标题" min-width="180" />
<el-table-column prop="reporter" label="提交人" width="100" />
<el-table-column prop="summary" label="报告摘要" min-width="200" show-overflow-tooltip />
<el-table-column prop="type" label="类型" width="100">
<template #default="{ row }">
{{ row.type === 'patrol' ? '巡护' : '监测' }}
<el-tag :type="row.type === '巡护报告' ? 'success' : 'primary'">
{{ row.type }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="area" label="区域" width="120" />
<el-table-column label="人员" width="150">
<template #default="{ row }">
{{ row.personnel.join(', ') }}
</template>
</el-table-column>
<el-table-column prop="content.findings" label="发现问题" min-width="200" />
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }">
<el-tag :type="row.status === '已审核' ? 'success' : 'warning'">
<el-tag :type="row.status === '已提交' ? 'success' : 'warning'">
{{ row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<el-table-column prop="createTime" label="提交时间" width="180" />
<el-table-column label="操作" width="200" fixed="right">
<template #default="{ row }">
<el-button type="primary" link>查看</el-button>
<el-button
type="success"
link
:disabled="row.status === '已审核'"
>
审核
</el-button>
<el-button type="primary" link @click="handleView(row)">查看</el-button>
<el-button type="success" link @click="handleDownload(row)">下载</el-button>
<el-button type="warning" link @click="handleExport(row)">导出</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页器 -->
<div class="pagination-container">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next"
:total="tableData.length"
/>
</div>
</el-card>
<!-- 新建报告弹窗 -->
<el-dialog
v-model="dialogVisible"
title="新建日常报告"
width="700px"
>
<el-dialog v-model="dialogVisible" title="新建日常报告" width="700px">
<el-form :model="formData" label-width="100px">
<el-form-item label="报告标题" required>
<el-input v-model="formData.title" placeholder="请输入报告标题" />
</el-form-item>
<el-form-item label="报告类型" required>
<el-radio-group v-model="formData.type">
<el-radio label="patrol">巡护报告</el-radio>
<el-radio label="monitor">监测报告</el-radio>
<el-radio label="巡护报告">巡护报告</el-radio>
<el-radio label="监测报告">监测报告</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="区域" required>
<el-select v-model="formData.area" placeholder="请选择区域">
<el-option label="A区" value="A区" />
<el-option label="B区" value="B区" />
</el-select>
</el-form-item>
<el-form-item label="相关人员" required>
<el-select
v-model="formData.personnel"
multiple
placeholder="请选择人员"
>
<el-option label="张三" value="张三" />
<el-option label="李四" value="李四" />
<el-option label="王五" value="王五" />
</el-select>
</el-form-item>
<el-form-item label="发现问题" required>
<el-form-item label="报告内容" required>
<el-input
v-model="formData.findings"
v-model="formData.content"
type="textarea"
rows="4"
placeholder="请描述发现的问题或异常情况"
/>
</el-form-item>
<el-form-item label="处理措施" required>
<el-input
v-model="formData.actions"
type="textarea"
rows="4"
placeholder="请描述采取的措施"
rows="6"
placeholder="请输入报告内容"
/>
</el-form-item>
<el-form-item label="附件">
<el-upload
action="#"
multiple
:auto-upload="false"
>
<el-upload action="#" multiple :auto-upload="false">
<el-button type="primary">选择文件</el-button>
</el-upload>
</el-form-item>
@ -227,15 +277,206 @@ const handleSubmit = () => {
</span>
</template>
</el-dialog>
<!-- 详情弹窗 -->
<el-dialog v-model="detailVisible" title="报告详情" width="800px">
<el-descriptions :column="2" border v-if="currentReport">
<el-descriptions-item label="报告标题" :span="2">
{{ currentReport.title }}
</el-descriptions-item>
<el-descriptions-item label="提交人">
{{ currentReport.reporter }}
</el-descriptions-item>
<el-descriptions-item label="所属部门">
{{ currentReport.department }}
</el-descriptions-item>
<el-descriptions-item label="报告类型">
{{ currentReport.type }}
</el-descriptions-item>
<el-descriptions-item label="提交时间">
{{ currentReport.createTime }}
</el-descriptions-item>
<el-descriptions-item label="报告内容" :span="2">
{{ currentReport.content }}
</el-descriptions-item>
<el-descriptions-item label="附件" :span="2">
<el-link
v-for="file in currentReport.attachments"
:key="file"
type="primary"
style="margin-right: 16px"
>
{{ file }}
</el-link>
</el-descriptions-item>
<el-descriptions-item label="数据统计" :span="2">
<el-row :gutter="20" class="stat-grid">
<el-col :span="6" v-for="item in currentReport.data" :key="item.type">
<div class="stat-item">
<div class="stat-item-label">{{ item.type }}</div>
<div class="stat-item-value">
{{ item.value }}
<el-icon v-if="item.trend" :class="item.trend">
<component :is="item.trend === 'up' ? 'ArrowUp' : 'ArrowDown'" />
</el-icon>
</div>
</div>
</el-col>
</el-row>
</el-descriptions-item>
</el-descriptions>
<template #footer>
<span class="dialog-footer">
<el-button @click="detailVisible = false">关闭</el-button>
<el-button type="warning" @click="handleExport(currentReport!)">导出</el-button>
</span>
</template>
</el-dialog>
<!-- 趋势分析弹窗 -->
<el-dialog v-model="showTrendAnalysis" title="报告趋势分析" width="900px">
<div class="trend-analysis">
<el-descriptions :column="2" border>
<el-descriptions-item label="数据周期" :span="2">
近30天报告数据
</el-descriptions-item>
<el-descriptions-item label="报告总量">
增长趋势 <el-tag type="success">上升 12%</el-tag>
</el-descriptions-item>
<el-descriptions-item label="提交及时率">
稳定趋势 <el-tag type="info">98%</el-tag>
</el-descriptions-item>
<el-descriptions-item label="问题发现率">
下降趋势 <el-tag type="success">-15%</el-tag>
</el-descriptions-item>
<el-descriptions-item label="处理及时率">
上升趋势 <el-tag type="success">+8%</el-tag>
</el-descriptions-item>
<el-descriptions-item label="预测分析" :span="2">
<div class="prediction">
<p>1. 根据当前趋势预计下月报告总量将增加约10%</p>
<p>2. 问题发现率持续下降表明整体环境质量改善</p>
<p>3. 建议继续保持当前的巡护频率和监测力度</p>
</div>
</el-descriptions-item>
</el-descriptions>
</div>
</el-dialog>
</div>
</template>
<style lang="scss" scoped>
@import "../../../styles/variables.scss";
.daily-report {
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.mb-20 {
margin-bottom: 20px;
}
.stat-card {
padding: 20px;
text-align: center;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
}
.stat-value {
font-size: 28px;
font-weight: 600;
color: $primary-color;
margin-bottom: 8px;
}
.stat-label {
font-size: 14px;
color: $text-secondary;
}
}
.pagination-container {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
.header-btns {
display: flex;
gap: 12px;
}
.stat-grid {
padding: 12px;
.stat-item {
text-align: center;
padding: 12px;
background: #f8f9fa;
border-radius: 4px;
.stat-item-label {
font-size: 13px;
color: $text-secondary;
margin-bottom: 8px;
}
.stat-item-value {
font-size: 16px;
font-weight: 500;
color: $text-primary;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
.up {
color: $success-color;
}
.down {
color: $danger-color;
}
}
}
}
.chart-header {
display: flex;
align-items: center;
gap: 8px;
.el-icon {
color: $text-secondary;
cursor: help;
}
}
.trend-analysis {
.prediction {
padding: 12px;
background: #f8f9fa;
border-radius: 4px;
p {
margin: 8px 0;
color: $text-regular;
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
}
}
}
</style>