refactor: 移除ant-design-vue和tailwindcss依赖并优化样式

style: 统一使用css替换scss并调整组件样式
style: 优化组件布局和属性顺序
chore: 更新.gitignore和.eslintrc配置
This commit is contained in:
张翔
2026-03-13 09:55:05 +08:00
parent 9f8bf041c3
commit 2555bf5f55
17 changed files with 901 additions and 1313 deletions
+25
View File
@@ -0,0 +1,25 @@
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true
},
extends: [
'eslint:recommended',
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended'
],
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module'
},
plugins: ['vue', '@typescript-eslint'],
rules: {
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }]
}
}
+8
View File
@@ -0,0 +1,8 @@
node_modules
dist
.DS_Store
*.log
.env.local
.env.*.local
coverage
.nyc_output
+64 -435
View File
@@ -9,7 +9,6 @@
"version": "1.0.0",
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"ant-design-vue": "^4.2.6",
"axios": "^1.6.2",
"dayjs": "^1.11.10",
"element-plus": "^2.13.5",
@@ -26,14 +25,10 @@
"@vitejs/plugin-vue": "^6.0.3",
"@vitest/ui": "^4.0.16",
"@vue/test-utils": "^2.4.3",
"autoprefixer": "^10.4.23",
"eslint": "^8.56.0",
"eslint-plugin-vue": "^9.19.2",
"jsdom": "^27.4.0",
"postcss": "^8.5.6",
"prettier": "^3.1.1",
"sass-embedded": "^1.98.0",
"tailwindcss": "^4.1.18",
"typescript": "^5.9.3",
"vite": "^7.3.1",
"vitest": "^4.0.16",
@@ -47,34 +42,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/@ant-design/colors": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz",
"integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==",
"license": "MIT",
"dependencies": {
"@ctrl/tinycolor": "^3.4.0"
}
},
"node_modules/@ant-design/icons-svg": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz",
"integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==",
"license": "MIT"
},
"node_modules/@ant-design/icons-vue": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-7.0.1.tgz",
"integrity": "sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==",
"license": "MIT",
"dependencies": {
"@ant-design/colors": "^6.0.0",
"@ant-design/icons-svg": "^4.2.1"
},
"peerDependencies": {
"vue": ">=3.0.3"
}
},
"node_modules/@asamuzakjp/css-color": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.2.tgz",
@@ -143,15 +110,6 @@
"node": ">=6.0.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
"integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/types": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
@@ -170,7 +128,9 @@
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.11.0.tgz",
"integrity": "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==",
"dev": true,
"license": "(Apache-2.0 AND BSD-3-Clause)"
"license": "(Apache-2.0 AND BSD-3-Clause)",
"optional": true,
"peer": true
},
"node_modules/@csstools/color-helpers": {
"version": "6.0.2",
@@ -304,15 +264,6 @@
"node": ">=20.19.0"
}
},
"node_modules/@ctrl/tinycolor": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz",
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/@element-plus/icons-vue": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz",
@@ -322,18 +273,6 @@
"vue": "^3.2.0"
}
},
"node_modules/@emotion/hash": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
"license": "MIT"
},
"node_modules/@emotion/unitless": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz",
"integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==",
"license": "MIT"
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
@@ -1118,6 +1057,7 @@
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"detect-libc": "^2.0.3",
"is-glob": "^4.0.3",
@@ -1160,6 +1100,7 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1181,6 +1122,7 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1202,6 +1144,7 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1223,6 +1166,7 @@
"os": [
"freebsd"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1244,6 +1188,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1265,6 +1210,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1286,6 +1232,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1307,6 +1254,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1328,6 +1276,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1349,6 +1298,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1370,6 +1320,7 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1391,6 +1342,7 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1412,6 +1364,7 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">= 10.0.0"
},
@@ -1427,6 +1380,7 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=12"
},
@@ -1836,16 +1790,6 @@
"win32"
]
},
"node_modules/@simonwep/pickr": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.8.2.tgz",
"integrity": "sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==",
"license": "MIT",
"dependencies": {
"core-js": "^3.15.1",
"nanopop": "^2.1.0"
}
},
"node_modules/@standard-schema/spec": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
@@ -2633,46 +2577,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/ant-design-vue": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-4.2.6.tgz",
"integrity": "sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==",
"license": "MIT",
"dependencies": {
"@ant-design/colors": "^6.0.0",
"@ant-design/icons-vue": "^7.0.0",
"@babel/runtime": "^7.10.5",
"@ctrl/tinycolor": "^3.5.0",
"@emotion/hash": "^0.9.0",
"@emotion/unitless": "^0.8.0",
"@simonwep/pickr": "~1.8.0",
"array-tree-filter": "^2.1.0",
"async-validator": "^4.0.0",
"csstype": "^3.1.1",
"dayjs": "^1.10.5",
"dom-align": "^1.12.1",
"dom-scroll-into-view": "^2.0.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.15",
"resize-observer-polyfill": "^1.5.1",
"scroll-into-view-if-needed": "^2.2.25",
"shallow-equal": "^1.0.0",
"stylis": "^4.1.3",
"throttle-debounce": "^5.0.0",
"vue-types": "^3.0.0",
"warning": "^4.0.0"
},
"engines": {
"node": ">=12.22.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/ant-design-vue"
},
"peerDependencies": {
"vue": ">=3.2.0"
}
},
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -2680,12 +2584,6 @@
"dev": true,
"license": "Python-2.0"
},
"node_modules/array-tree-filter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz",
"integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==",
"license": "MIT"
},
"node_modules/array-union": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
@@ -2718,43 +2616,6 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/autoprefixer": {
"version": "10.4.27",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz",
"integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"browserslist": "^4.28.1",
"caniuse-lite": "^1.0.30001774",
"fraction.js": "^5.3.4",
"picocolors": "^1.1.1",
"postcss-value-parser": "^4.2.0"
},
"bin": {
"autoprefixer": "bin/autoprefixer"
},
"engines": {
"node": "^10 || ^12 || >=14"
},
"peerDependencies": {
"postcss": "^8.1.0"
}
},
"node_modules/axios": {
"version": "1.13.6",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
@@ -2773,19 +2634,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
"integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.cjs"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/bidi-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
@@ -2835,40 +2683,6 @@
"node": ">=8"
}
},
"node_modules/browserslist": {
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
"electron-to-chromium": "^1.5.263",
"node-releases": "^2.0.27",
"update-browserslist-db": "^1.2.0"
},
"bin": {
"browserslist": "cli.js"
},
"engines": {
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -2892,27 +2706,6 @@
"node": ">=6"
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001777",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001777.tgz",
"integrity": "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "CC-BY-4.0"
},
"node_modules/chai": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
@@ -2947,6 +2740,7 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"readdirp": "^4.0.1"
},
@@ -2982,7 +2776,9 @@
"resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz",
"integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==",
"dev": true,
"license": "MIT"
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/combined-stream": {
"version": "1.0.8",
@@ -3006,12 +2802,6 @@
"node": ">=14"
}
},
"node_modules/compute-scroll-into-view": {
"version": "1.0.20",
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz",
"integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==",
"license": "MIT"
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -3045,17 +2835,6 @@
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/core-js": {
"version": "3.48.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz",
"integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==",
"hasInstallScript": true,
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
}
},
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -3198,6 +2977,7 @@
"dev": true,
"license": "Apache-2.0",
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
}
@@ -3228,18 +3008,6 @@
"node": ">=6.0.0"
}
},
"node_modules/dom-align": {
"version": "1.12.4",
"resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.4.tgz",
"integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==",
"license": "MIT"
},
"node_modules/dom-scroll-into-view": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-2.0.1.tgz",
"integrity": "sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==",
"license": "MIT"
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -3280,13 +3048,6 @@
"node": ">=14"
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.307",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz",
"integrity": "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==",
"dev": true,
"license": "ISC"
},
"node_modules/element-plus": {
"version": "2.13.5",
"resolved": "https://registry.npmjs.org/element-plus/-/element-plus-2.13.5.tgz",
@@ -3435,16 +3196,6 @@
"@esbuild/win32-x64": "0.27.3"
}
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -3862,20 +3613,6 @@
"node": ">= 6"
}
},
"node_modules/fraction.js": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
"integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "*"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/rawify"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -4162,7 +3899,9 @@
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz",
"integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==",
"dev": true,
"license": "MIT"
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/import-fresh": {
"version": "3.3.1",
@@ -4270,15 +4009,6 @@
"node": ">=8"
}
},
"node_modules/is-plain-object": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz",
"integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
@@ -4353,12 +4083,6 @@
"node": ">=14"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
@@ -4513,18 +4237,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/lru-cache": {
"version": "11.2.6",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz",
@@ -4685,12 +4397,6 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/nanopop": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/nanopop/-/nanopop-2.4.2.tgz",
"integrity": "sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==",
"license": "MIT"
},
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -4704,14 +4410,8 @@
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"dev": true,
"license": "MIT",
"optional": true
},
"node_modules/node-releases": {
"version": "2.0.36",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz",
"integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==",
"dev": true,
"license": "MIT"
"optional": true,
"peer": true
},
"node_modules/nopt": {
"version": "7.2.1",
@@ -5050,13 +4750,6 @@
"node": ">=4"
}
},
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true,
"license": "MIT"
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -5134,6 +4827,7 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">= 14.18.0"
},
@@ -5152,12 +4846,6 @@
"node": ">=0.10.0"
}
},
"node_modules/resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
"license": "MIT"
},
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -5323,6 +5011,8 @@
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"dev": true,
"license": "Apache-2.0",
"optional": true,
"peer": true,
"dependencies": {
"tslib": "^2.1.0"
}
@@ -5334,6 +5024,7 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.1.5",
@@ -5355,6 +5046,8 @@
"integrity": "sha512-Do7u6iRb6K+lrllcTkB1BXcHwOxcKe3rEfOF/GcCLE2w3WpddakRAosJOHFUR37DpsvimQXEt5abs3NzUjEIqg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"@bufbuild/protobuf": "^2.5.0",
"colorjs.io": "^0.5.0",
@@ -5404,6 +5097,7 @@
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"sass": "1.98.0"
}
@@ -5421,6 +5115,7 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5438,6 +5133,7 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5455,6 +5151,7 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5472,6 +5169,7 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5489,6 +5187,7 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5506,6 +5205,7 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5523,6 +5223,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5540,6 +5241,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5557,6 +5259,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5574,6 +5277,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5591,6 +5295,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5608,6 +5313,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5625,6 +5331,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5642,6 +5349,7 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5659,6 +5367,7 @@
"!linux",
"!win32"
],
"peer": true,
"dependencies": {
"sass": "1.98.0"
}
@@ -5676,6 +5385,7 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5693,6 +5403,7 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -5703,6 +5414,8 @@
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -5726,15 +5439,6 @@
"node": ">=v12.22.7"
}
},
"node_modules/scroll-into-view-if-needed": {
"version": "2.2.31",
"resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz",
"integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==",
"license": "MIT",
"dependencies": {
"compute-scroll-into-view": "^1.0.20"
}
},
"node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
@@ -5748,12 +5452,6 @@
"node": ">=10"
}
},
"node_modules/shallow-equal": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
"integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==",
"license": "MIT"
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -5964,12 +5662,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/stylis": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
"integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
"license": "MIT"
},
"node_modules/superjson": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz",
@@ -6008,6 +5700,8 @@
"integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"sync-message-port": "^1.0.0"
},
@@ -6021,17 +5715,12 @@
"integrity": "sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/tailwindcss": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz",
"integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==",
"dev": true,
"license": "MIT"
},
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -6039,15 +5728,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/throttle-debounce": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz",
"integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==",
"license": "MIT",
"engines": {
"node": ">=12.22"
}
},
"node_modules/tinybench": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
@@ -6210,7 +5890,9 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
"license": "0BSD",
"optional": true,
"peer": true
},
"node_modules/type-check": {
"version": "0.4.0",
@@ -6259,37 +5941,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"escalade": "^3.2.0",
"picocolors": "^1.1.1"
},
"bin": {
"update-browserslist-db": "cli.js"
},
"peerDependencies": {
"browserslist": ">= 4.21.0"
}
},
"node_modules/uri-js": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -6312,7 +5963,9 @@
"resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
"integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==",
"dev": true,
"license": "MIT"
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/vite": {
"version": "7.3.1",
@@ -6651,21 +6304,6 @@
"typescript": ">=5.0.0"
}
},
"node_modules/vue-types": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/vue-types/-/vue-types-3.0.2.tgz",
"integrity": "sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==",
"license": "MIT",
"dependencies": {
"is-plain-object": "3.0.1"
},
"engines": {
"node": ">=10.15.0"
},
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
@@ -6689,15 +6327,6 @@
"node": ">=18"
}
},
"node_modules/warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/webidl-conversions": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz",
-5
View File
@@ -19,7 +19,6 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"ant-design-vue": "^4.2.6",
"axios": "^1.6.2",
"dayjs": "^1.11.10",
"element-plus": "^2.13.5",
@@ -36,14 +35,10 @@
"@vitejs/plugin-vue": "^6.0.3",
"@vitest/ui": "^4.0.16",
"@vue/test-utils": "^2.4.3",
"autoprefixer": "^10.4.23",
"eslint": "^8.56.0",
"eslint-plugin-vue": "^9.19.2",
"jsdom": "^27.4.0",
"postcss": "^8.5.6",
"prettier": "^3.1.1",
"sass-embedded": "^1.98.0",
"tailwindcss": "^4.1.18",
"typescript": "^5.9.3",
"vite": "^7.3.1",
"vitest": "^4.0.16",
-678
View File
@@ -19,11 +19,6 @@
--el-color-info-light-9: #a6a9ad;
--el-color-info-light-3: #c8c9cc;
--el-color-info-dark-2: #73767a;
--border-radius-base: 8px;
--border-radius-large: 12px;
--border-radius-small: 4px;
--border-radius-circle: 50%;
}
* {
@@ -37,676 +32,3 @@ body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.el-card {
border-radius: var(--border-radius-base) !important;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.el-card:hover {
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.12);
transform: translateY(-2px);
}
.el-button {
border-radius: var(--border-radius-base) !important;
transition: all 0.3s ease;
}
.el-button--primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
}
.el-button--primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.el-input__wrapper {
border-radius: var(--border-radius-base) !important;
transition: all 0.3s ease;
}
.el-input__wrapper:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.el-select .el-input__wrapper {
border-radius: var(--border-radius-base) !important;
}
.el-table {
border-radius: var(--border-radius-base) !important;
overflow: hidden;
}
.el-table th {
background: linear-gradient(135deg, #f5f7fa 0%, #e4e7ed 100%);
font-weight: 600;
}
.el-table tr {
transition: all 0.3s ease;
}
.el-table tr:hover {
background-color: #f0f9ff !important;
}
.el-table .el-table__cell {
border-radius: var(--border-radius-small);
}
.el-pagination {
border-radius: var(--border-radius-base) !important;
}
.el-pagination .el-pager li {
border-radius: var(--border-radius-small) !important;
transition: all 0.3s ease;
}
.el-pagination .el-pager li:hover {
transform: scale(1.1);
}
.el-dialog {
border-radius: var(--border-radius-large) !important;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
}
.el-dialog__header {
border-radius: var(--border-radius-large) var(--border-radius-large) 0 0 !important;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.el-dialog__body {
padding: 24px;
}
.el-form-item__label {
font-weight: 500;
color: #606266;
}
.el-form-item {
margin-bottom: 20px;
}
.el-menu {
border-radius: var(--border-radius-base) !important;
border: none;
}
.el-menu-item {
border-radius: var(--border-radius-small) !important;
margin: 4px 8px;
transition: all 0.3s ease;
}
.el-menu-item:hover {
background: linear-gradient(135deg, #667eea20 0%, #764ba220 100%);
transform: translateX(4px);
}
.el-menu-item.is-active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.el-statistic {
border-radius: var(--border-radius-base) !important;
transition: all 0.3s ease;
}
.el-statistic:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
.el-timeline-item__tail {
border-radius: var(--border-radius-small);
}
.el-timeline-item__node {
border-radius: var(--border-radius-circle) !important;
}
.el-tag {
border-radius: var(--border-radius-small) !important;
padding: 4px 12px;
font-weight: 500;
}
.el-tag--success {
background: linear-gradient(135deg, #67c23a 0%, #5daf34 100%);
border: none;
}
.el-tag--warning {
background: linear-gradient(135deg, #e6a23c 0%, #d93026 100%);
border: none;
}
.el-tag--info {
background: linear-gradient(135deg, #909399 0%, #73767a 100%);
border: none;
}
.el-descriptions {
border-radius: var(--border-radius-base) !important;
}
.el-descriptions__label {
font-weight: 600;
background: #f5f7fa;
}
.el-message-box {
border-radius: var(--border-radius-large) !important;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
}
.el-notification {
border-radius: var(--border-radius-base) !important;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.el-drawer {
border-radius: var(--border-radius-large) var(--border-radius-large) 0 0 !important;
}
.el-switch {
border-radius: var(--border-radius-circle) !important;
}
.el-checkbox {
border-radius: var(--border-radius-small) !important;
}
.el-radio {
border-radius: var(--border-radius-small) !important;
}
.el-upload {
border-radius: var(--border-radius-base) !important;
}
.el-upload-dragger {
border-radius: var(--border-radius-base) !important;
border: 2px dashed #dcdfe6;
transition: all 0.3s ease;
}
.el-upload-dragger:hover {
border-color: #667eea;
background: #f0f9ff;
}
.el-progress-bar__inner {
border-radius: var(--border-radius-small) !important;
}
.el-progress-bar {
border-radius: var(--border-radius-base) !important;
}
.el-rate__icon {
border-radius: var(--border-radius-small) !important;
}
.el-slider__runway {
border-radius: var(--border-radius-base) !important;
}
.el-slider__button {
border-radius: var(--border-radius-circle) !important;
}
.el-avatar {
border-radius: var(--border-radius-circle) !important;
}
.el-badge__content {
border-radius: var(--border-radius-circle) !important;
}
.el-breadcrumb {
border-radius: var(--border-radius-base) !important;
padding: 8px 16px;
}
.el-breadcrumb__item {
transition: all 0.3s ease;
}
.el-breadcrumb__item:hover {
color: #667eea;
}
.el-divider {
border-radius: var(--border-radius-small) !important;
}
.el-empty {
border-radius: var(--border-radius-base) !important;
}
.el-result {
border-radius: var(--border-radius-base) !important;
}
.el-alert {
border-radius: var(--border-radius-base) !important;
border: none;
}
.el-alert--success {
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
}
.el-alert--warning {
background: linear-gradient(135deg, #fdf6ec 0%, #fef0f0 100%);
}
.el-alert--error {
background: linear-gradient(135deg, #fef0f0 0%, #fde2e2 100%);
}
.el-alert--info {
background: linear-gradient(135deg, #f4f4f5 0%, #e9e9eb 100%);
}
.el-tabs__item {
border-radius: var(--border-radius-base) var(--border-radius-base) 0 0 !important;
transition: all 0.3s ease;
}
.el-tabs__item:hover {
color: #667eea;
}
.el-tabs__item.is-active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.el-collapse {
border-radius: var(--border-radius-base) !important;
}
.el-collapse-item__header {
border-radius: var(--border-radius-base) !important;
transition: all 0.3s ease;
}
.el-collapse-item__header:hover {
background: #f5f7fa;
}
.el-popover {
border-radius: var(--border-radius-base) !important;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.el-tooltip__popper {
border-radius: var(--border-radius-base) !important;
}
.el-dropdown-menu {
border-radius: var(--border-radius-base) !important;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.el-dropdown-menu__item {
border-radius: var(--border-radius-small) !important;
transition: all 0.3s ease;
}
.el-dropdown-menu__item:hover {
background: linear-gradient(135deg, #667eea20 0%, #764ba220 100%);
transform: translateX(4px);
}
.el-tree-node__content {
border-radius: var(--border-radius-small) !important;
transition: all 0.3s ease;
}
.el-tree-node__content:hover {
background: #f0f9ff;
}
.el-transfer-panel {
border-radius: var(--border-radius-base) !important;
}
.el-cascader-panel {
border-radius: var(--border-radius-base) !important;
}
.el-color-picker__panel {
border-radius: var(--border-radius-base) !important;
}
.el-date-picker {
border-radius: var(--border-radius-base) !important;
}
.el-picker-panel {
border-radius: var(--border-radius-base) !important;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.el-calendar {
border-radius: var(--border-radius-base) !important;
}
.el-image {
border-radius: var(--border-radius-base) !important;
}
.el-skeleton {
border-radius: var(--border-radius-base) !important;
}
.el-backtop {
border-radius: var(--border-radius-circle) !important;
}
.el-affix {
border-radius: var(--border-radius-base) !important;
}
.el-space {
border-radius: var(--border-radius-base) !important;
}
.el-tour {
border-radius: var(--border-radius-base) !important;
}
.el-segmented {
border-radius: var(--border-radius-base) !important;
}
.el-timeline {
border-radius: var(--border-radius-base) !important;
}
.el-timeline-item {
border-radius: var(--border-radius-base) !important;
}
.el-scrollbar {
border-radius: var(--border-radius-base) !important;
}
.el-main {
border-radius: var(--border-radius-base) !important;
}
.el-header {
border-radius: var(--border-radius-base) !important;
}
.el-footer {
border-radius: var(--border-radius-base) !important;
}
.el-aside {
border-radius: var(--border-radius-base) !important;
}
.el-container {
border-radius: var(--border-radius-base) !important;
}
.el-row {
border-radius: var(--border-radius-base) !important;
}
.el-col {
border-radius: var(--border-radius-base) !important;
}
.el-form {
border-radius: var(--border-radius-base) !important;
}
.el-input-number {
border-radius: var(--border-radius-base) !important;
}
.el-select-dropdown {
border-radius: var(--border-radius-base) !important;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.el-option {
border-radius: var(--border-radius-small) !important;
transition: all 0.3s ease;
}
.el-option:hover {
background: #f0f9ff;
}
.el-option-group {
border-radius: var(--border-radius-base) !important;
}
.el-option-group__title {
border-radius: var(--border-radius-base) var(--border-radius-base) 0 0 !important;
}
.el-checkbox-group {
border-radius: var(--border-radius-base) !important;
}
.el-checkbox-button {
border-radius: var(--border-radius-base) !important;
}
.el-radio-group {
border-radius: var(--border-radius-base) !important;
}
.el-radio-button {
border-radius: var(--border-radius-base) !important;
}
.el-check-tag {
border-radius: var(--border-radius-small) !important;
}
.el-select-v2 {
border-radius: var(--border-radius-base) !important;
}
.el-select-dropdown-v2 {
border-radius: var(--border-radius-base) !important;
}
.el-select-dropdown-v2__item {
border-radius: var(--border-radius-small) !important;
}
.el-select-dropdown-v2__item:hover {
background: #f0f9ff;
}
.el-table-v2 {
border-radius: var(--border-radius-base) !important;
}
.el-table-column {
border-radius: var(--border-radius-base) !important;
}
.el-table-v2__row {
transition: all 0.3s ease;
}
.el-table-v2__row:hover {
background: #f0f9ff !important;
}
.el-page-header {
border-radius: var(--border-radius-base) !important;
}
.el-overlay {
border-radius: var(--border-radius-base) !important;
}
.el-notification__group {
border-radius: var(--border-radius-base) !important;
}
.el-notification__icon {
border-radius: var(--border-radius-circle) !important;
}
.el-notification__content {
border-radius: var(--border-radius-base) !important;
}
.el-notification__closeBtn {
border-radius: var(--border-radius-circle) !important;
}
.el-message {
border-radius: var(--border-radius-base) !important;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.el-message__icon {
border-radius: var(--border-radius-circle) !important;
}
.el-message__content {
border-radius: var(--border-radius-base) !important;
}
.el-message-box__btns {
border-radius: var(--border-radius-base) !important;
}
.el-menu-item-group {
border-radius: var(--border-radius-base) !important;
}
.el-menu-item-group__title {
border-radius: var(--border-radius-base) var(--border-radius-base) 0 0 !important;
}
.el-mention {
border-radius: var(--border-radius-base) !important;
}
.el-loading-spinner {
border-radius: var(--border-radius-circle) !important;
}
.el-link {
border-radius: var(--border-radius-small) !important;
}
.el-input-tag {
border-radius: var(--border-radius-small) !important;
}
.el-infinite-scroll {
border-radius: var(--border-radius-base) !important;
}
.el-image-viewer {
border-radius: var(--border-radius-base) !important;
}
.el-icon {
border-radius: var(--border-radius-small) !important;
}
.el-result__icon {
border-radius: var(--border-radius-circle) !important;
}
.el-result__title {
border-radius: var(--border-radius-base) !important;
}
.el-result__subtitle {
border-radius: var(--border-radius-base) !important;
}
.el-result__extra {
border-radius: var(--border-radius-base) !important;
}
.el-result__content {
border-radius: var(--border-radius-base) !important;
}
.el-descriptions-item {
border-radius: var(--border-radius-base) !important;
}
.el-descriptions-item__label {
border-radius: var(--border-radius-base) var(--border-radius-base) 0 0 !important;
}
.el-descriptions-item__content {
border-radius: var(--border-radius-base) !important;
}
.el-descriptions-item__cell {
border-radius: var(--border-radius-base) !important;
}
.el-date-picker__header {
border-radius: var(--border-radius-base) var(--border-radius-base) 0 0 !important;
}
.el-date-picker__header-label {
border-radius: var(--border-radius-small) !important;
}
.el-date-picker__time-header {
border-radius: var(--border-radius-base) var(--border-radius-base) 0 0 !important;
}
.el-date-picker__time-picker-option {
border-radius: var(--border-radius-small) !important;
}
.el-date-picker__time-picker-option:hover {
background: #f0f9ff;
}
.el-date-picker__time-picker-option.is-selected {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.el-date-picker__time-picker-option.is-disabled {
background: #f5f7fa;
color: #c0c4cc;
}
.el-date-picker__time-picker-option.is-disabled:hover {
background: #f5f7fa;
}
.el-date-picker__time-picker-option.is-disabled.is-selected {
background: #f5f7fa;
color: #c0c4cc;
}
.el-date-picker__time-picker-option.is-disabled.is-selected:hover {
background: #f5f7fa;
}
@@ -1,6 +1,9 @@
<template>
<el-container class="default-layout">
<el-aside :width="collapsed ? '64px' : '200px'" class="aside">
<el-aside
:width="collapsed ? '64px' : '200px'"
class="aside"
>
<div class="logo">
<span v-if="!collapsed">Novalon</span>
<span v-else>N</span>
@@ -9,8 +12,8 @@
:default-active="activeMenu"
class="menu"
:collapse="collapsed"
background-color="#304156"
text-color="#bfcbd9"
background-color="#f5f7fa"
text-color="#606266"
active-text-color="#409eff"
router
>
@@ -23,55 +26,85 @@
<el-icon><Setting /></el-icon>
<span>系统管理</span>
</template>
<el-menu-item index="/users">用户管理</el-menu-item>
<el-menu-item index="/roles">角色管理</el-menu-item>
<el-menu-item index="/menus">菜单管理</el-menu-item>
<el-menu-item index="/users">
用户管理
</el-menu-item>
<el-menu-item index="/roles">
角色管理
</el-menu-item>
<el-menu-item index="/menus">
菜单管理
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="config">
<template #title>
<el-icon><Tools /></el-icon>
<span>系统配置</span>
</template>
<el-menu-item index="/dict">字典管理</el-menu-item>
<el-menu-item index="/sysconfig">参数配置</el-menu-item>
<el-menu-item index="/dict">
字典管理
</el-menu-item>
<el-menu-item index="/sysconfig">
参数配置
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="audit">
<template #title>
<el-icon><Document /></el-icon>
<span>审计中心</span>
</template>
<el-menu-item index="/loginlog">登录日志</el-menu-item>
<el-menu-item index="/oplog">操作日志</el-menu-item>
<el-menu-item index="/loginlog">
登录日志
</el-menu-item>
<el-menu-item index="/oplog">
操作日志
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="notify">
<template #title>
<el-icon><Bell /></el-icon>
<span>通知中心</span>
</template>
<el-menu-item index="/notice">通知公告</el-menu-item>
<el-menu-item index="/notice">
通知公告
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="file">
<template #title>
<el-icon><Folder /></el-icon>
<span>文件管理</span>
</template>
<el-menu-item index="/files">文件列表</el-menu-item>
<el-menu-item index="/files">
文件列表
</el-menu-item>
</el-sub-menu>
</el-menu>
</el-aside>
<el-container>
<el-header class="header">
<el-icon class="trigger" @click="collapsed = !collapsed">
<el-icon
class="trigger"
@click="collapsed = !collapsed"
>
<Fold v-if="!collapsed" />
<Expand v-else />
</el-icon>
<div class="header-right">
<el-dropdown @command="handleCommand">
<el-avatar :size="32">{{ username }}</el-avatar>
<el-avatar :size="32">
{{ username }}
</el-avatar>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="profile">个人中心</el-dropdown-item>
<el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
<el-dropdown-item command="profile">
个人中心
</el-dropdown-item>
<el-dropdown-item
command="logout"
divided
>
退出登录
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@@ -114,13 +147,13 @@ onMounted(() => {
})
</script>
<style scoped lang="scss">
<style scoped lang="css">
.default-layout {
min-height: 100vh;
}
.aside {
background-color: #304156;
background-color: #f5f7fa;
transition: width 0.3s;
overflow: hidden;
}
@@ -130,7 +163,7 @@ onMounted(() => {
display: flex;
align-items: center;
justify-content: center;
color: #fff;
color: #303133;
font-size: 20px;
font-weight: bold;
}
+50 -12
View File
@@ -16,17 +16,51 @@
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button
type="primary"
@click="handleSearch"
>
搜索
</el-button>
</div>
</div>
</template>
<el-table :data="dataSource" v-loading="loading" style="width: 100%" @sort-change="handleSortChange">
<el-table-column prop="id" label="ID" sortable="custom" />
<el-table-column prop="username" label="用户名" sortable="custom" />
<el-table-column prop="ip" label="IP地址" sortable="custom" />
<el-table-column prop="location" label="登录地点" sortable="custom" />
<el-table-column prop="browser" label="浏览器" sortable="custom" />
<el-table-column prop="os" label="操作系统" sortable="custom" />
<el-table
v-loading="loading"
:data="dataSource"
style="width: 100%"
@sort-change="handleSortChange"
>
<el-table-column
prop="id"
label="ID"
sortable="custom"
/>
<el-table-column
prop="username"
label="用户名"
sortable="custom"
/>
<el-table-column
prop="ip"
label="IP地址"
sortable="custom"
/>
<el-table-column
prop="location"
label="登录地点"
sortable="custom"
/>
<el-table-column
prop="browser"
label="浏览器"
sortable="custom"
/>
<el-table-column
prop="os"
label="操作系统"
sortable="custom"
/>
<el-table-column label="状态">
<template #default="{ row }">
<el-tag :type="row.status === '0' ? 'success' : 'danger'">
@@ -34,17 +68,21 @@
</el-tag>
</template>
</el-table-column>
<el-table-column prop="loginTime" label="登录时间" sortable="custom" />
<el-table-column
prop="loginTime"
label="登录时间"
sortable="custom"
/>
</el-table>
<el-pagination
v-model:current-page="pagination.current"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
@current-change="handleTableChange"
@size-change="handleSizeChange"
layout="total, sizes, prev, pager, next, jumper"
style="margin-top: 16px; justify-content: flex-end"
@current-change="handleTableChange"
@size-change="handleSizeChange"
/>
</el-card>
</div>
@@ -111,7 +149,7 @@ const handleSortChange = ({ prop, order }: any) => {
onMounted(() => fetchData())
</script>
<style scoped lang="scss">
<style scoped lang="css">
.login-log {
.card-header {
display: flex;
@@ -16,16 +16,46 @@
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button
type="primary"
@click="handleSearch"
>
搜索
</el-button>
</div>
</div>
</template>
<el-table :data="dataSource" v-loading="loading" style="width: 100%" @sort-change="handleSortChange">
<el-table-column prop="id" label="ID" sortable="custom" />
<el-table-column prop="username" label="操作人" sortable="custom" />
<el-table-column prop="operation" label="操作模块" sortable="custom" />
<el-table-column prop="method" label="请求方法" sortable="custom" />
<el-table-column prop="params" label="请求参数" :show-overflow-tooltip="true" />
<el-table
v-loading="loading"
:data="dataSource"
style="width: 100%"
@sort-change="handleSortChange"
>
<el-table-column
prop="id"
label="ID"
sortable="custom"
/>
<el-table-column
prop="username"
label="操作人"
sortable="custom"
/>
<el-table-column
prop="operation"
label="操作模块"
sortable="custom"
/>
<el-table-column
prop="method"
label="请求方法"
sortable="custom"
/>
<el-table-column
prop="params"
label="请求参数"
:show-overflow-tooltip="true"
/>
<el-table-column label="状态">
<template #default="{ row }">
<el-tag :type="row.status === '0' ? 'success' : 'danger'">
@@ -33,18 +63,26 @@
</el-tag>
</template>
</el-table-column>
<el-table-column prop="duration" label="耗时(ms)" sortable="custom" />
<el-table-column prop="createdAt" label="操作时间" sortable="custom" />
<el-table-column
prop="duration"
label="耗时(ms)"
sortable="custom"
/>
<el-table-column
prop="createdAt"
label="操作时间"
sortable="custom"
/>
</el-table>
<el-pagination
v-model:current-page="pagination.current"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
@current-change="handleTableChange"
@size-change="handleSizeChange"
layout="total, sizes, prev, pager, next, jumper"
style="margin-top: 16px; justify-content: flex-end"
@current-change="handleTableChange"
@size-change="handleSizeChange"
/>
</el-card>
</div>
@@ -111,7 +149,7 @@ const handleSortChange = ({ prop, order }: any) => {
onMounted(() => fetchData())
</script>
<style scoped lang="scss">
<style scoped lang="css">
.operation-log {
.card-header {
display: flex;
@@ -4,14 +4,35 @@
<template #header>
<div class="card-title">
<span>参数配置</span>
<el-button type="primary" @click="handleAdd">新增配置</el-button>
<el-button
type="primary"
@click="handleAdd"
>
新增配置
</el-button>
</div>
</template>
<el-table :data="dataSource" v-loading="loading" style="width: 100%">
<el-table-column prop="id" label="ID" />
<el-table-column prop="configName" label="参数名称" />
<el-table-column prop="configKey" label="参数键名" />
<el-table-column prop="configValue" label="参数值" />
<el-table
v-loading="loading"
:data="dataSource"
style="width: 100%"
>
<el-table-column
prop="id"
label="ID"
/>
<el-table-column
prop="configName"
label="参数名称"
/>
<el-table-column
prop="configKey"
label="参数键名"
/>
<el-table-column
prop="configValue"
label="参数值"
/>
<el-table-column label="类型">
<template #default="{ row }">
<el-tag :type="row.configType === 'Y' ? '' : 'info'">
@@ -19,17 +40,41 @@
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<el-table-column
label="操作"
width="150"
>
<template #default="{ row }">
<el-button type="primary" link size="small" @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link size="small" @click="handleDelete(row)">删除</el-button>
<el-button
type="primary"
link
size="small"
@click="handleEdit(row)"
>
编辑
</el-button>
<el-button
type="danger"
link
size="small"
@click="handleDelete(row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="modalVisible" :title="modalTitle" width="500px">
<el-form :model="formState" label-width="80px">
<el-dialog
v-model="modalVisible"
:title="modalTitle"
width="500px"
>
<el-form
:model="formState"
label-width="80px"
>
<el-form-item label="参数名称">
<el-input v-model="formState.configName" />
</el-form-item>
@@ -41,8 +86,15 @@
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" @click="handleModalOk">确定</el-button>
<el-button @click="modalVisible = false">
取消
</el-button>
<el-button
type="primary"
@click="handleModalOk"
>
确定
</el-button>
</template>
</el-dialog>
</div>
@@ -91,7 +143,9 @@ const handleDelete = async (row: any) => {
await request.delete(`/config/${row.id}`)
ElMessage.success('删除成功')
fetchData()
} catch {}
} catch (error) {
console.error('删除配置失败:', error)
}
}
const handleModalOk = async () => {
@@ -112,7 +166,7 @@ const handleModalOk = async () => {
onMounted(() => fetchData())
</script>
<style scoped lang="scss">
<style scoped lang="css">
.config-management .card-title {
display: flex;
justify-content: space-between;
@@ -4,13 +4,31 @@
<template #header>
<div class="card-title">
<span>字典管理</span>
<el-button type="primary" @click="handleAdd">新增字典</el-button>
<el-button
type="primary"
@click="handleAdd"
>
新增字典
</el-button>
</div>
</template>
<el-table :data="dataSource" v-loading="loading" style="width: 100%">
<el-table-column prop="id" label="ID" />
<el-table-column prop="dictName" label="字典名称" />
<el-table-column prop="dictType" label="字典类型" />
<el-table
v-loading="loading"
:data="dataSource"
style="width: 100%"
>
<el-table-column
prop="id"
label="ID"
/>
<el-table-column
prop="dictName"
label="字典名称"
/>
<el-table-column
prop="dictType"
label="字典类型"
/>
<el-table-column label="状态">
<template #default="{ row }">
<el-tag :type="row.status === '0' ? 'success' : 'danger'">
@@ -18,18 +36,45 @@
</el-tag>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" />
<el-table-column label="操作" width="200">
<el-table-column
prop="remark"
label="备注"
/>
<el-table-column
label="操作"
width="200"
>
<template #default="{ row }">
<el-button type="primary" link size="small" @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link size="small" @click="handleDelete(row)">删除</el-button>
<el-button
type="primary"
link
size="small"
@click="handleEdit(row)"
>
编辑
</el-button>
<el-button
type="danger"
link
size="small"
@click="handleDelete(row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="modalVisible" :title="modalTitle" width="500px">
<el-form :model="formState" label-width="80px">
<el-dialog
v-model="modalVisible"
:title="modalTitle"
width="500px"
>
<el-form
:model="formState"
label-width="80px"
>
<el-form-item label="字典名称">
<el-input v-model="formState.dictName" />
</el-form-item>
@@ -38,17 +83,33 @@
</el-form-item>
<el-form-item label="状态">
<el-select v-model="formState.status">
<el-option value="0" label="正常" />
<el-option value="1" label="停用" />
<el-option
value="0"
label="正常"
/>
<el-option
value="1"
label="停用"
/>
</el-select>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="formState.remark" type="textarea" />
<el-input
v-model="formState.remark"
type="textarea"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" @click="handleModalOk">确定</el-button>
<el-button @click="modalVisible = false">
取消
</el-button>
<el-button
type="primary"
@click="handleModalOk"
>
确定
</el-button>
</template>
</el-dialog>
</div>
@@ -94,10 +155,12 @@ const handleDelete = async (row: any) => {
cancelButtonText: '取消',
type: 'warning'
})
await request.delete(`/dict/types/${row.id}`)
await request.delete(`/dict/${row.id}`)
ElMessage.success('删除成功')
fetchData()
} catch {}
} catch (error) {
console.error('删除字典失败:', error)
}
}
const handleModalOk = async () => {
@@ -118,7 +181,7 @@ const handleModalOk = async () => {
onMounted(() => fetchData())
</script>
<style scoped lang="scss">
<style scoped lang="css">
.dict-management .card-title {
display: flex;
justify-content: space-between;
@@ -4,17 +4,33 @@
<template #header>
<div class="card-title">
<span>文件管理</span>
<el-upload :before-upload="handleUpload" :show-file-list="false">
<el-upload
:before-upload="handleUpload"
:show-file-list="false"
>
<el-button type="primary">
<el-icon><Upload /></el-icon> 上传文件
</el-button>
</el-upload>
</div>
</template>
<el-table :data="dataSource" v-loading="loading" style="width: 100%">
<el-table-column prop="id" label="ID" />
<el-table-column prop="fileName" label="文件名" />
<el-table-column prop="fileSize" label="文件大小" />
<el-table
v-loading="loading"
:data="dataSource"
style="width: 100%"
>
<el-table-column
prop="id"
label="ID"
/>
<el-table-column
prop="fileName"
label="文件名"
/>
<el-table-column
prop="fileSize"
label="文件大小"
/>
<el-table-column label="文件类型">
<template #default="{ row }">
<el-tag :type="getFileTypeTag(row.fileType)">
@@ -22,12 +38,35 @@
</el-tag>
</template>
</el-table-column>
<el-table-column prop="storageType" label="存储方式" />
<el-table-column prop="createdAt" label="上传时间" />
<el-table-column label="操作" width="150">
<el-table-column
prop="storageType"
label="存储方式"
/>
<el-table-column
prop="createdAt"
label="上传时间"
/>
<el-table-column
label="操作"
width="150"
>
<template #default="{ row }">
<el-button type="primary" link size="small" @click="handleDownload(row)">下载</el-button>
<el-button type="danger" link size="small" @click="handleDelete(row)">删除</el-button>
<el-button
type="primary"
link
size="small"
@click="handleDownload(row)"
>
下载
</el-button>
<el-button
type="danger"
link
size="small"
@click="handleDelete(row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
@@ -83,7 +122,9 @@ const handleDelete = async (row: any) => {
await request.delete(`/files/${row.id}`)
ElMessage.success('删除成功')
fetchData()
} catch {}
} catch (error) {
console.error('删除文件失败:', error)
}
}
const getFileTypeName = (fileType: string) => {
@@ -111,7 +152,7 @@ const getFileTypeTag = (fileType: string): '' | 'success' | 'warning' | 'danger'
onMounted(() => fetchData())
</script>
<style scoped lang="scss">
<style scoped lang="css">
.file-management .card-title {
display: flex;
justify-content: space-between;
@@ -4,60 +4,131 @@
<template #header>
<div class="card-title">
<span>通知公告</span>
<el-button type="primary" @click="handleAdd">新增公告</el-button>
<el-button
type="primary"
@click="handleAdd"
>
新增公告
</el-button>
</div>
</template>
<el-table :data="dataSource" v-loading="loading" style="width: 100%">
<el-table-column prop="id" label="ID" />
<el-table-column prop="noticeTitle" label="公告标题" />
<el-table-column label="公告类型" width="100">
<el-table
v-loading="loading"
:data="dataSource"
style="width: 100%"
>
<el-table-column
prop="id"
label="ID"
/>
<el-table-column
prop="noticeTitle"
label="公告标题"
/>
<el-table-column
label="公告类型"
width="100"
>
<template #default="{ row }">
<el-tag :type="row.noticeType === '1' ? '' : 'success'">
{{ row.noticeType === '1' ? '通知' : '公告' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="状态" width="80">
<el-table-column
label="状态"
width="80"
>
<template #default="{ row }">
<el-tag :type="row.status === '0' ? 'success' : 'danger'">
{{ row.status === '0' ? '正常' : '停用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createdAt" label="发布时间" />
<el-table-column label="操作" width="150">
<el-table-column
prop="createdAt"
label="发布时间"
/>
<el-table-column
label="操作"
width="150"
>
<template #default="{ row }">
<el-button type="primary" link size="small" @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link size="small" @click="handleDelete(row)">删除</el-button>
<el-button
type="primary"
link
size="small"
@click="handleEdit(row)"
>
编辑
</el-button>
<el-button
type="danger"
link
size="small"
@click="handleDelete(row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="modalVisible" :title="modalTitle" width="500px">
<el-form :model="formState" label-width="80px">
<el-dialog
v-model="modalVisible"
:title="modalTitle"
width="500px"
>
<el-form
:model="formState"
label-width="80px"
>
<el-form-item label="公告标题">
<el-input v-model="formState.noticeTitle" />
</el-form-item>
<el-form-item label="公告类型">
<el-select v-model="formState.noticeType">
<el-option value="1" label="通知" />
<el-option value="2" label="公告" />
<el-option
value="1"
label="通知"
/>
<el-option
value="2"
label="公告"
/>
</el-select>
</el-form-item>
<el-form-item label="公告内容">
<el-input v-model="formState.noticeContent" type="textarea" :rows="4" />
<el-input
v-model="formState.noticeContent"
type="textarea"
:rows="4"
/>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="formState.status">
<el-option value="0" label="正常" />
<el-option value="1" label="停用" />
<el-option
value="0"
label="正常"
/>
<el-option
value="1"
label="停用"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" @click="handleModalOk">确定</el-button>
<el-button @click="modalVisible = false">
取消
</el-button>
<el-button
type="primary"
@click="handleModalOk"
>
确定
</el-button>
</template>
</el-dialog>
</div>
@@ -103,10 +174,12 @@ const handleDelete = async (row: any) => {
cancelButtonText: '取消',
type: 'warning'
})
await request.delete(`/notices/${row.id}`)
await request.delete(`/notice/${row.id}`)
ElMessage.success('删除成功')
fetchData()
} catch {}
} catch (error) {
console.error('删除通知失败:', error)
}
}
const handleModalOk = async () => {
@@ -127,7 +200,7 @@ const handleModalOk = async () => {
onMounted(() => fetchData())
</script>
<style scoped lang="scss">
<style scoped lang="css">
.notice-management .card-title {
display: flex;
justify-content: space-between;
@@ -3,7 +3,10 @@
<el-row :gutter="16">
<el-col :span="6">
<el-card v-loading="loading">
<el-statistic title="用户总数" :value="stats.userCount">
<el-statistic
title="用户总数"
:value="stats.userCount"
>
<template #prefix>
<el-icon><User /></el-icon>
</template>
@@ -12,7 +15,10 @@
</el-col>
<el-col :span="6">
<el-card v-loading="loading">
<el-statistic title="角色总数" :value="stats.roleCount">
<el-statistic
title="角色总数"
:value="stats.roleCount"
>
<template #prefix>
<el-icon><UserFilled /></el-icon>
</template>
@@ -21,7 +27,10 @@
</el-col>
<el-col :span="6">
<el-card v-loading="loading">
<el-statistic title="今日登录" :value="stats.todayLogin">
<el-statistic
title="今日登录"
:value="stats.todayLogin"
>
<template #prefix>
<el-icon><ArrowRight /></el-icon>
</template>
@@ -30,7 +39,10 @@
</el-col>
<el-col :span="6">
<el-card v-loading="loading">
<el-statistic title="操作日志" :value="stats.operationLog">
<el-statistic
title="操作日志"
:value="stats.operationLog"
>
<template #prefix>
<el-icon><Document /></el-icon>
</template>
@@ -38,9 +50,15 @@
</el-card>
</el-col>
</el-row>
<el-row :gutter="16" style="margin-top: 16px">
<el-row
:gutter="16"
style="margin-top: 16px"
>
<el-col :span="12">
<el-card title="最近登录" v-loading="loading">
<el-card
v-loading="loading"
title="最近登录"
>
<el-timeline>
<el-timeline-item
v-for="item in recentLogins"
@@ -54,12 +72,26 @@
</el-card>
</el-col>
<el-col :span="12">
<el-card title="系统信息" v-loading="loading">
<el-descriptions :column="1" border>
<el-descriptions-item label="系统版本">{{ systemInfo.version }}</el-descriptions-item>
<el-descriptions-item label="Java版本">{{ systemInfo.javaVersion }}</el-descriptions-item>
<el-descriptions-item label="前端框架">{{ systemInfo.frontendFramework }}</el-descriptions-item>
<el-descriptions-item label="数据库">{{ systemInfo.database }}</el-descriptions-item>
<el-card
v-loading="loading"
title="系统信息"
>
<el-descriptions
:column="1"
border
>
<el-descriptions-item label="系统版本">
{{ systemInfo.version }}
</el-descriptions-item>
<el-descriptions-item label="Java版本">
{{ systemInfo.javaVersion }}
</el-descriptions-item>
<el-descriptions-item label="前端框架">
{{ systemInfo.frontendFramework }}
</el-descriptions-item>
<el-descriptions-item label="数据库">
{{ systemInfo.database }}
</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
@@ -127,7 +159,7 @@ onMounted(() => {
})
</script>
<style scoped lang="scss">
<style scoped lang="css">
.dashboard {
padding: 16px;
}
+19 -6
View File
@@ -6,25 +6,38 @@
</template>
<el-form
:model="formState"
@submit.prevent="onFinish"
label-position="top"
@submit.prevent="onFinish"
>
<el-form-item
label="用户名"
prop="username"
:rules="[{ required: true, message: '请输入用户名', trigger: 'blur' }]"
>
<el-input v-model="formState.username" placeholder="请输入用户名" />
<el-input
v-model="formState.username"
placeholder="请输入用户名"
/>
</el-form-item>
<el-form-item
label="密码"
prop="password"
:rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]"
>
<el-input v-model="formState.password" type="password" placeholder="请输入密码" show-password />
<el-input
v-model="formState.password"
type="password"
placeholder="请输入密码"
show-password
/>
</el-form-item>
<el-form-item>
<el-button type="primary" native-type="submit" :loading="loading" style="width: 100%">
<el-button
type="primary"
native-type="submit"
:loading="loading"
style="width: 100%"
>
登录
</el-button>
</el-form-item>
@@ -63,13 +76,13 @@ const onFinish = async () => {
}
</script>
<style scoped lang="scss">
<style scoped lang="css">
.login-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background: var(--el-color-primary-light-9);
.login-card {
width: 400px;
@@ -4,56 +4,131 @@
<template #header>
<div class="card-title">
<span>菜单管理</span>
<el-button type="primary" @click="handleAdd">新增菜单</el-button>
<el-button
type="primary"
@click="handleAdd"
>
新增菜单
</el-button>
</div>
</template>
<el-table :data="dataSource" v-loading="loading" :pagination="false" row-key="id" style="width: 100%">
<el-table-column prop="menuName" label="菜单名称" />
<el-table-column label="菜单类型" width="100">
<el-table
v-loading="loading"
:data="dataSource"
:pagination="false"
row-key="id"
style="width: 100%"
>
<el-table-column
prop="menuName"
label="菜单名称"
/>
<el-table-column
label="菜单类型"
width="100"
>
<template #default="{ row }">
<el-tag :type="row.menuType === 'M' ? '' : row.menuType === 'C' ? 'success' : 'warning'">
{{ row.menuType === 'M' ? '目录' : row.menuType === 'C' ? '菜单' : '按钮' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="perms" label="权限标识" />
<el-table-column prop="component" label="组件" />
<el-table-column prop="orderNum" label="排序" width="80" />
<el-table-column label="状态" width="80">
<el-table-column
prop="perms"
label="权限标识"
/>
<el-table-column
prop="component"
label="组件"
/>
<el-table-column
prop="orderNum"
label="排序"
width="80"
/>
<el-table-column
label="状态"
width="80"
>
<template #default="{ row }">
<el-tag :type="row.status === '0' ? 'success' : 'danger'">
{{ row.status === '0' ? '显示' : '隐藏' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<el-table-column
label="操作"
width="150"
>
<template #default="{ row }">
<el-button type="primary" link size="small" @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link size="small" @click="handleDelete(row)">删除</el-button>
<el-button
type="primary"
link
size="small"
@click="handleEdit(row)"
>
编辑
</el-button>
<el-button
type="danger"
link
size="small"
@click="handleDelete(row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="modalVisible" :title="modalTitle" width="500px">
<el-form :model="formState" label-width="100px">
<el-dialog
v-model="modalVisible"
:title="modalTitle"
width="500px"
>
<el-form
:model="formState"
label-width="100px"
>
<el-form-item label="菜单名称">
<el-input v-model="formState.menuName" />
</el-form-item>
<el-form-item label="父级菜单">
<el-tree-select v-model="formState.parentId" :data="menuTree" placeholder="请选择父级菜单" clearable check-strictly />
<el-tree-select
v-model="formState.parentId"
:data="menuTree"
placeholder="请选择父级菜单"
clearable
check-strictly
/>
</el-form-item>
<el-form-item label="菜单类型">
<el-select v-model="formState.menuType">
<el-option value="M" label="目录" />
<el-option value="C" label="菜单" />
<el-option value="F" label="按钮" />
<el-option
value="M"
label="目录"
/>
<el-option
value="C"
label="菜单"
/>
<el-option
value="F"
label="按钮"
/>
</el-select>
</el-form-item>
<el-form-item label="路由地址" v-if="formState.menuType !== 'F'">
<el-form-item
v-if="formState.menuType !== 'F'"
label="路由地址"
>
<el-input v-model="formState.perms" />
</el-form-item>
<el-form-item label="组件路径" v-if="formState.menuType === 'C'">
<el-form-item
v-if="formState.menuType === 'C'"
label="组件路径"
>
<el-input v-model="formState.component" />
</el-form-item>
<el-form-item label="排序">
@@ -61,14 +136,27 @@
</el-form-item>
<el-form-item label="状态">
<el-select v-model="formState.status">
<el-option value="0" label="显示" />
<el-option value="1" label="隐藏" />
<el-option
value="0"
label="显示"
/>
<el-option
value="1"
label="隐藏"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" @click="handleModalOk">确定</el-button>
<el-button @click="modalVisible = false">
取消
</el-button>
<el-button
type="primary"
@click="handleModalOk"
>
确定
</el-button>
</template>
</el-dialog>
</div>
@@ -125,7 +213,9 @@ const handleDelete = async (row: any) => {
await request.delete(`/menus/${row.id}`)
ElMessage.success('删除成功')
fetchData()
} catch {}
} catch (error) {
console.error('删除菜单失败:', error)
}
}
const handleModalOk = async () => {
@@ -146,7 +236,7 @@ const handleModalOk = async () => {
onMounted(() => fetchData())
</script>
<style scoped lang="scss">
<style scoped lang="css">
.menu-management .card-title {
display: flex;
justify-content: space-between;
@@ -16,16 +16,47 @@
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button
type="primary"
@click="handleSearch"
>
搜索
</el-button>
</div>
<el-button type="primary" @click="handleAdd">新增角色</el-button>
<el-button
type="primary"
@click="handleAdd"
>
新增角色
</el-button>
</div>
</template>
<el-table :data="dataSource" v-loading="loading" style="width: 100%" @sort-change="handleSortChange">
<el-table-column prop="id" label="ID" sortable="custom" />
<el-table-column prop="roleName" label="角色名称" sortable="custom" />
<el-table-column prop="roleKey" label="角色标识" sortable="custom" />
<el-table-column prop="roleSort" label="排序" sortable="custom" />
<el-table
v-loading="loading"
:data="dataSource"
style="width: 100%"
@sort-change="handleSortChange"
>
<el-table-column
prop="id"
label="ID"
sortable="custom"
/>
<el-table-column
prop="roleName"
label="角色名称"
sortable="custom"
/>
<el-table-column
prop="roleKey"
label="角色标识"
sortable="custom"
/>
<el-table-column
prop="roleSort"
label="排序"
sortable="custom"
/>
<el-table-column label="状态">
<template #default="{ row }">
<el-tag :type="row.status === '0' ? 'success' : 'danger'">
@@ -33,11 +64,30 @@
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createdAt" label="创建时间" sortable="custom" />
<el-table-column label="操作" width="200">
<el-table-column
prop="createdAt"
label="创建时间"
sortable="custom"
/>
<el-table-column
label="操作"
width="200"
>
<template #default="{ row }">
<el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link @click="handleDelete(row)">删除</el-button>
<el-button
type="primary"
link
@click="handleEdit(row)"
>
编辑
</el-button>
<el-button
type="danger"
link
@click="handleDelete(row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
@@ -46,15 +96,22 @@
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
@current-change="handleTableChange"
@size-change="handleSizeChange"
layout="total, sizes, prev, pager, next, jumper"
style="margin-top: 16px; justify-content: flex-end"
@current-change="handleTableChange"
@size-change="handleSizeChange"
/>
</el-card>
<el-dialog v-model="modalVisible" :title="modalTitle" width="500px">
<el-form :model="formState" label-width="80px">
<el-dialog
v-model="modalVisible"
:title="modalTitle"
width="500px"
>
<el-form
:model="formState"
label-width="80px"
>
<el-form-item label="角色名称">
<el-input v-model="formState.roleName" />
</el-form-item>
@@ -66,14 +123,27 @@
</el-form-item>
<el-form-item label="状态">
<el-select v-model="formState.status">
<el-option value="0" label="正常" />
<el-option value="1" label="禁用" />
<el-option
value="0"
label="正常"
/>
<el-option
value="1"
label="禁用"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" @click="handleModalOk">确定</el-button>
<el-button @click="modalVisible = false">
取消
</el-button>
<el-button
type="primary"
@click="handleModalOk"
>
确定
</el-button>
</template>
</el-dialog>
</div>
@@ -164,7 +234,9 @@ const handleDelete = async (row: any) => {
await request.delete(`/roles/${row.id}`)
ElMessage.success('删除成功')
fetchData()
} catch {}
} catch (error) {
console.error('删除角色失败:', error)
}
}
const handleModalOk = async () => {
@@ -185,7 +257,7 @@ const handleModalOk = async () => {
onMounted(() => fetchData())
</script>
<style scoped lang="scss">
<style scoped lang="css">
.role-management {
.card-header {
display: flex;
@@ -16,33 +16,82 @@
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-button type="primary" @click="handleSearch">搜索</el-button>
<el-button
type="primary"
@click="handleSearch"
>
搜索
</el-button>
</div>
<el-button type="primary" @click="handleAdd">新增用户</el-button>
<el-button
type="primary"
@click="handleAdd"
>
新增用户
</el-button>
</div>
</template>
<el-table
:data="dataSource"
v-loading="loading"
:data="dataSource"
style="width: 100%"
@sort-change="handleSortChange"
>
<el-table-column prop="id" label="ID" width="80" sortable="custom" />
<el-table-column prop="username" label="用户名" sortable="custom" />
<el-table-column prop="email" label="邮箱" sortable="custom" />
<el-table-column prop="phone" label="手机号" sortable="custom" />
<el-table-column label="状态" width="100">
<el-table-column
prop="id"
label="ID"
width="80"
sortable="custom"
/>
<el-table-column
prop="username"
label="用户名"
sortable="custom"
/>
<el-table-column
prop="email"
label="邮箱"
sortable="custom"
/>
<el-table-column
prop="phone"
label="手机号"
sortable="custom"
/>
<el-table-column
label="状态"
width="100"
>
<template #default="{ row }">
<el-tag :type="row.status === '0' ? 'success' : 'danger'">
{{ row.status === '0' ? '正常' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createdAt" label="创建时间" sortable="custom" />
<el-table-column label="操作" width="200">
<el-table-column
prop="createdAt"
label="创建时间"
sortable="custom"
/>
<el-table-column
label="操作"
width="200"
>
<template #default="{ row }">
<el-button type="primary" link @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link @click="handleDelete(row)">删除</el-button>
<el-button
type="primary"
link
@click="handleEdit(row)"
>
编辑
</el-button>
<el-button
type="danger"
link
@click="handleDelete(row)"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
@@ -51,10 +100,10 @@
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="pagination.total"
@current-change="handleTableChange"
@size-change="handleSizeChange"
layout="total, sizes, prev, pager, next, jumper"
style="margin-top: 16px; justify-content: flex-end"
@current-change="handleTableChange"
@size-change="handleSizeChange"
/>
</el-card>
@@ -78,14 +127,27 @@
</el-form-item>
<el-form-item label="状态">
<el-select v-model="formState.status">
<el-option value="0" label="正常" />
<el-option value="1" label="禁用" />
<el-option
value="0"
label="正常"
/>
<el-option
value="1"
label="禁用"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="handleModalCancel">取消</el-button>
<el-button type="primary" @click="handleModalOk">确定</el-button>
<el-button @click="handleModalCancel">
取消
</el-button>
<el-button
type="primary"
@click="handleModalOk"
>
确定
</el-button>
</template>
</el-dialog>
</div>
@@ -212,7 +274,7 @@ onMounted(() => {
})
</script>
<style scoped lang="scss">
<style scoped lang="css">
.user-management {
.card-header {
display: flex;