diff --git a/package.json b/package.json
index b54ff3f..89a9711 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,7 @@
"unocss": "^0.49.8",
"unplugin-auto-import": "^0.13.0",
"unplugin-vue-components": "^0.23.0",
+ "unplugin-vue-router": "^0.7.0",
"vite": "^4.4.9",
"vite-plugin-pages": "^0.28.0",
"vite-plugin-style-import": "^2.0.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 025cab8..26f5407 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -85,6 +85,9 @@ devDependencies:
unplugin-vue-components:
specifier: ^0.23.0
version: 0.23.0(vue@3.3.4)
+ unplugin-vue-router:
+ specifier: ^0.7.0
+ version: 0.7.0(vue-router@4.2.4)(vue@3.3.4)
vite:
specifier: ^4.4.9
version: 4.4.9(less@4.2.0)
@@ -170,10 +173,10 @@ packages:
'@babel/helper-compilation-targets': 7.22.15
'@babel/helper-module-transforms': 7.22.17(@babel/core@7.22.17)
'@babel/helpers': 7.22.15
- '@babel/parser': 7.22.16
+ '@babel/parser': 7.23.6
'@babel/template': 7.22.15
'@babel/traverse': 7.22.17
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
convert-source-map: 1.9.0
debug: 4.3.4
gensync: 1.0.0-beta.2
@@ -187,7 +190,7 @@ packages:
resolution: {integrity: sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
'@jridgewell/gen-mapping': 0.3.3
'@jridgewell/trace-mapping': 0.3.19
jsesc: 2.5.2
@@ -197,7 +200,7 @@ packages:
resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
dev: true
/@babel/helper-compilation-targets@7.22.15:
@@ -239,28 +242,28 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/template': 7.22.15
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
dev: true
/@babel/helper-hoist-variables@7.22.5:
resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
dev: true
/@babel/helper-member-expression-to-functions@7.22.15:
resolution: {integrity: sha512-qLNsZbgrNh0fDQBCPocSL8guki1hcPvltGDv/NxvUoABwFq7GkKSu1nRXeJkVZc+wJvne2E0RKQz+2SQrz6eAA==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
dev: true
/@babel/helper-module-imports@7.22.15:
resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
dev: true
/@babel/helper-module-transforms@7.22.17(@babel/core@7.22.17):
@@ -281,7 +284,7 @@ packages:
resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
dev: true
/@babel/helper-plugin-utils@7.22.5:
@@ -305,21 +308,21 @@ packages:
resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
dev: true
/@babel/helper-skip-transparent-expression-wrappers@7.22.5:
resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
dev: true
/@babel/helper-split-export-declaration@7.22.6:
resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
engines: {node: '>=6.9.0'}
dependencies:
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
dev: true
/@babel/helper-string-parser@7.22.5:
@@ -327,6 +330,11 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
+ /@babel/helper-string-parser@7.23.4:
+ resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
/@babel/helper-validator-identifier@7.22.20:
resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
engines: {node: '>=6.9.0'}
@@ -343,7 +351,7 @@ packages:
dependencies:
'@babel/template': 7.22.15
'@babel/traverse': 7.22.17
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
transitivePeerDependencies:
- supports-color
dev: true
@@ -365,6 +373,14 @@ packages:
'@babel/types': 7.23.0
dev: true
+ /@babel/parser@7.23.6:
+ resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+ dependencies:
+ '@babel/types': 7.23.6
+ dev: true
+
/@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.17):
resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==}
engines: {node: '>=6.9.0'}
@@ -408,8 +424,8 @@ packages:
engines: {node: '>=6.9.0'}
dependencies:
'@babel/code-frame': 7.22.13
- '@babel/parser': 7.22.16
- '@babel/types': 7.23.0
+ '@babel/parser': 7.23.6
+ '@babel/types': 7.23.6
dev: true
/@babel/traverse@7.22.17:
@@ -422,8 +438,8 @@ packages:
'@babel/helper-function-name': 7.22.5
'@babel/helper-hoist-variables': 7.22.5
'@babel/helper-split-export-declaration': 7.22.6
- '@babel/parser': 7.22.16
- '@babel/types': 7.23.0
+ '@babel/parser': 7.23.6
+ '@babel/types': 7.23.6
debug: 4.3.4
globals: 11.12.0
transitivePeerDependencies:
@@ -439,6 +455,15 @@ packages:
to-fast-properties: 2.0.0
dev: true
+ /@babel/types@7.23.6:
+ resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-string-parser': 7.23.4
+ '@babel/helper-validator-identifier': 7.22.20
+ to-fast-properties: 2.0.0
+ dev: true
+
/@esbuild-kit/cjs-loader@2.4.2:
resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==}
dependencies:
@@ -993,6 +1018,20 @@ packages:
picomatch: 2.3.1
dev: true
+ /@rollup/pluginutils@5.1.0:
+ resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+ dependencies:
+ '@types/estree': 1.0.1
+ estree-walker: 2.0.2
+ picomatch: 2.3.1
+ dev: true
+
/@sindresorhus/is@5.6.0:
resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==}
engines: {node: '>=14.16'}
@@ -1106,7 +1145,7 @@ packages:
hasBin: true
dependencies:
'@ampproject/remapping': 2.2.1
- '@rollup/pluginutils': 5.0.5
+ '@rollup/pluginutils': 5.1.0
'@unocss/config': 0.49.8
'@unocss/core': 0.49.8
'@unocss/preset-uno': 0.49.8
@@ -1236,7 +1275,7 @@ packages:
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0
dependencies:
'@ampproject/remapping': 2.2.1
- '@rollup/pluginutils': 5.0.5
+ '@rollup/pluginutils': 5.1.0
'@unocss/config': 0.49.8
'@unocss/core': 0.49.8
'@unocss/inspector': 0.49.8
@@ -1295,6 +1334,26 @@ packages:
'@volar/language-core': 1.10.1
dev: true
+ /@vue-macros/common@1.10.0(vue@3.3.4):
+ resolution: {integrity: sha512-4DZsPeQA/nBQDw2RkYAmH7KrFjJVrMdAhJhO1JCl1bbbFXCGeoGjXfkg9wHPppj47s2HpAB3GrqNwqVGbi12NQ==}
+ engines: {node: '>=16.14.0'}
+ peerDependencies:
+ vue: ^2.7.0 || ^3.2.25
+ peerDependenciesMeta:
+ vue:
+ optional: true
+ dependencies:
+ '@babel/types': 7.23.6
+ '@rollup/pluginutils': 5.1.0
+ '@vue/compiler-sfc': 3.3.11
+ ast-kit: 0.11.3
+ local-pkg: 0.5.0
+ magic-string-ast: 0.3.0
+ vue: 3.3.4
+ transitivePeerDependencies:
+ - rollup
+ dev: true
+
/@vue/babel-helper-vue-transform-on@1.1.5:
resolution: {integrity: sha512-SgUymFpMoAyWeYWLAY+MkCK3QEROsiUnfaw5zxOVD/M64KQs8D/4oK6Q5omVA2hnvEOE0SCkH2TZxs/jnnUj7w==}
dev: true
@@ -1309,7 +1368,7 @@ packages:
'@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.17)
'@babel/template': 7.22.15
'@babel/traverse': 7.22.17
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
'@vue/babel-helper-vue-transform-on': 1.1.5
camelcase: 6.3.0
html-tags: 3.3.1
@@ -1318,15 +1377,31 @@ packages:
- supports-color
dev: true
+ /@vue/compiler-core@3.3.11:
+ resolution: {integrity: sha512-h97/TGWBilnLuRaj58sxNrsUU66fwdRKLOLQ9N/5iNDfp+DZhYH9Obhe0bXxhedl8fjAgpRANpiZfbgWyruQ0w==}
+ dependencies:
+ '@babel/parser': 7.23.6
+ '@vue/shared': 3.3.11
+ estree-walker: 2.0.2
+ source-map-js: 1.0.2
+ dev: true
+
/@vue/compiler-core@3.3.4:
resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==}
dependencies:
- '@babel/parser': 7.22.16
+ '@babel/parser': 7.23.6
'@vue/shared': 3.3.4
estree-walker: 2.0.2
source-map-js: 1.0.2
dev: true
+ /@vue/compiler-dom@3.3.11:
+ resolution: {integrity: sha512-zoAiUIqSKqAJ81WhfPXYmFGwDRuO+loqLxvXmfUdR5fOitPoUiIeFI9cTTyv9MU5O1+ZZglJVTusWzy+wfk5hw==}
+ dependencies:
+ '@vue/compiler-core': 3.3.11
+ '@vue/shared': 3.3.11
+ dev: true
+
/@vue/compiler-dom@3.3.4:
resolution: {integrity: sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==}
dependencies:
@@ -1334,21 +1409,43 @@ packages:
'@vue/shared': 3.3.4
dev: true
+ /@vue/compiler-sfc@3.3.11:
+ resolution: {integrity: sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA==}
+ dependencies:
+ '@babel/parser': 7.23.6
+ '@vue/compiler-core': 3.3.11
+ '@vue/compiler-dom': 3.3.11
+ '@vue/compiler-ssr': 3.3.11
+ '@vue/reactivity-transform': 3.3.11
+ '@vue/shared': 3.3.11
+ estree-walker: 2.0.2
+ magic-string: 0.30.5
+ postcss: 8.4.32
+ source-map-js: 1.0.2
+ dev: true
+
/@vue/compiler-sfc@3.3.4:
resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==}
dependencies:
- '@babel/parser': 7.22.16
+ '@babel/parser': 7.23.6
'@vue/compiler-core': 3.3.4
'@vue/compiler-dom': 3.3.4
'@vue/compiler-ssr': 3.3.4
'@vue/reactivity-transform': 3.3.4
'@vue/shared': 3.3.4
estree-walker: 2.0.2
- magic-string: 0.30.3
- postcss: 8.4.29
+ magic-string: 0.30.5
+ postcss: 8.4.32
source-map-js: 1.0.2
dev: true
+ /@vue/compiler-ssr@3.3.11:
+ resolution: {integrity: sha512-Zd66ZwMvndxRTgVPdo+muV4Rv9n9DwQ4SSgWWKWkPFebHQfVYRrVjeygmmDmPewsHyznCNvJ2P2d6iOOhdv8Qg==}
+ dependencies:
+ '@vue/compiler-dom': 3.3.11
+ '@vue/shared': 3.3.11
+ dev: true
+
/@vue/compiler-ssr@3.3.4:
resolution: {integrity: sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==}
dependencies:
@@ -1370,23 +1467,33 @@ packages:
dependencies:
'@volar/language-core': 1.10.1
'@volar/source-map': 1.10.1
- '@vue/compiler-dom': 3.3.4
+ '@vue/compiler-dom': 3.3.11
'@vue/reactivity': 3.3.4
- '@vue/shared': 3.3.4
+ '@vue/shared': 3.3.11
minimatch: 9.0.3
muggle-string: 0.3.1
typescript: 4.9.5
vue-template-compiler: 2.7.14
dev: true
+ /@vue/reactivity-transform@3.3.11:
+ resolution: {integrity: sha512-fPGjH0wqJo68A0wQ1k158utDq/cRyZNlFoxGwNScE28aUFOKFEnCBsvyD8jHn+0kd0UKVpuGuaZEQ6r9FJRqCg==}
+ dependencies:
+ '@babel/parser': 7.23.6
+ '@vue/compiler-core': 3.3.11
+ '@vue/shared': 3.3.11
+ estree-walker: 2.0.2
+ magic-string: 0.30.5
+ dev: true
+
/@vue/reactivity-transform@3.3.4:
resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==}
dependencies:
- '@babel/parser': 7.22.16
+ '@babel/parser': 7.23.6
'@vue/compiler-core': 3.3.4
'@vue/shared': 3.3.4
estree-walker: 2.0.2
- magic-string: 0.30.3
+ magic-string: 0.30.5
dev: true
/@vue/reactivity@3.3.4:
@@ -1420,6 +1527,10 @@ packages:
vue: 3.3.4
dev: true
+ /@vue/shared@3.3.11:
+ resolution: {integrity: sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw==}
+ dev: true
+
/@vue/shared@3.3.4:
resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==}
dev: true
@@ -1616,6 +1727,28 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
+ /ast-kit@0.11.3:
+ resolution: {integrity: sha512-qdwwKEhckRk0XE22/xDdmU3v/60E8Edu4qFhgTLIhGGDs/PAJwLw9pQn8Rj99PitlbBZbYpx0k/lbir4kg0SuA==}
+ engines: {node: '>=16.14.0'}
+ dependencies:
+ '@babel/parser': 7.23.6
+ '@rollup/pluginutils': 5.1.0
+ pathe: 1.1.1
+ transitivePeerDependencies:
+ - rollup
+ dev: true
+
+ /ast-kit@0.9.5:
+ resolution: {integrity: sha512-kbL7ERlqjXubdDd+szuwdlQ1xUxEz9mCz1+m07ftNVStgwRb2RWw+U6oKo08PAvOishMxiqz1mlJyLl8yQx2Qg==}
+ engines: {node: '>=16.14.0'}
+ dependencies:
+ '@babel/parser': 7.22.16
+ '@rollup/pluginutils': 5.0.5
+ pathe: 1.1.1
+ transitivePeerDependencies:
+ - rollup
+ dev: true
+
/ast-types@0.13.4:
resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==}
engines: {node: '>=4'}
@@ -1623,6 +1756,16 @@ packages:
tslib: 2.6.2
dev: true
+ /ast-walker-scope@0.5.0:
+ resolution: {integrity: sha512-NsyHMxBh4dmdEHjBo1/TBZvCKxffmZxRYhmclfu0PP6Aftre47jOHYaYaNqJcV0bxihxFXhDkzLHUwHc0ocd0Q==}
+ engines: {node: '>=16.14.0'}
+ dependencies:
+ '@babel/parser': 7.22.16
+ ast-kit: 0.9.5
+ transitivePeerDependencies:
+ - rollup
+ dev: true
+
/async-retry@1.3.3:
resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==}
dependencies:
@@ -4182,6 +4325,14 @@ packages:
engines: {node: '>=14'}
dev: true
+ /local-pkg@0.5.0:
+ resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
+ engines: {node: '>=14'}
+ dependencies:
+ mlly: 1.4.2
+ pkg-types: 1.0.3
+ dev: true
+
/locate-path@2.0.0:
resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==}
engines: {node: '>=4'}
@@ -4290,6 +4441,13 @@ packages:
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dev: true
+ /magic-string-ast@0.3.0:
+ resolution: {integrity: sha512-0shqecEPgdFpnI3AP90epXyxZy9g6CRZ+SZ7BcqFwYmtFEnZ1jpevcV5HoyVnlDS9gCnc1UIg3Rsvp3Ci7r8OA==}
+ engines: {node: '>=16.14.0'}
+ dependencies:
+ magic-string: 0.30.3
+ dev: true
+
/magic-string@0.25.9:
resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
dependencies:
@@ -4317,6 +4475,13 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.15
dev: true
+ /magic-string@0.30.5:
+ resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==}
+ engines: {node: '>=12'}
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.4.15
+ dev: true
+
/make-dir@2.1.0:
resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
engines: {node: '>=6'}
@@ -4569,8 +4734,8 @@ packages:
hasBin: true
dev: true
- /nanoid@3.3.6:
- resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
+ /nanoid@3.3.7:
+ resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
dev: true
@@ -5281,7 +5446,16 @@ packages:
resolution: {integrity: sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
- nanoid: 3.3.6
+ nanoid: 3.3.7
+ picocolors: 1.0.0
+ source-map-js: 1.0.2
+ dev: true
+
+ /postcss@8.4.32:
+ resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==}
+ engines: {node: ^10 || ^12 || >=14}
+ dependencies:
+ nanoid: 3.3.7
picocolors: 1.0.0
source-map-js: 1.0.2
dev: true
@@ -6377,14 +6551,14 @@ packages:
dependencies:
acorn: 8.10.0
estree-walker: 3.0.3
- magic-string: 0.30.3
+ magic-string: 0.30.5
unplugin: 1.5.0
dev: true
/unimport@2.2.4:
resolution: {integrity: sha512-qMgmeEGqqrrmEtm0dqxMG37J6xBtrriqxq9hILvDb+e6l2F0yTnJomLoCCp0eghLR7bYGeBsUU5Y0oyiUYhViw==}
dependencies:
- '@rollup/pluginutils': 5.0.5
+ '@rollup/pluginutils': 5.1.0
escape-string-regexp: 5.0.0
fast-glob: 3.3.1
local-pkg: 0.4.3
@@ -6402,11 +6576,11 @@ packages:
/unimport@3.3.0:
resolution: {integrity: sha512-3jhq3ZG5hFZzrWGDCpx83kjPzefP/EeuKkIO1T0MA4Zwj+dO/Og1mFvZ4aZ5WSDm0FVbbdVIRH1zKBG7c4wOpg==}
dependencies:
- '@rollup/pluginutils': 5.0.5
+ '@rollup/pluginutils': 5.1.0
escape-string-regexp: 5.0.0
fast-glob: 3.3.1
local-pkg: 0.4.3
- magic-string: 0.30.3
+ magic-string: 0.30.5
mlly: 1.4.2
pathe: 1.1.1
pkg-types: 1.0.3
@@ -6517,6 +6691,33 @@ packages:
- supports-color
dev: true
+ /unplugin-vue-router@0.7.0(vue-router@4.2.4)(vue@3.3.4):
+ resolution: {integrity: sha512-ddRreGq0t5vlSB7OMy4e4cfU1w2AwBQCwmvW3oP/0IHQiokzbx4hd3TpwBu3eIAFVuhX2cwNQwp1U32UybTVCw==}
+ peerDependencies:
+ vue-router: ^4.1.0
+ peerDependenciesMeta:
+ vue-router:
+ optional: true
+ dependencies:
+ '@babel/types': 7.23.0
+ '@rollup/pluginutils': 5.0.5
+ '@vue-macros/common': 1.10.0(vue@3.3.4)
+ ast-walker-scope: 0.5.0
+ chokidar: 3.5.3
+ fast-glob: 3.3.1
+ json5: 2.2.3
+ local-pkg: 0.4.3
+ mlly: 1.4.2
+ pathe: 1.1.1
+ scule: 1.0.0
+ unplugin: 1.5.0
+ vue-router: 4.2.4(vue@3.3.4)
+ yaml: 2.3.2
+ transitivePeerDependencies:
+ - rollup
+ - vue
+ dev: true
+
/unplugin@1.4.0:
resolution: {integrity: sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==}
dependencies:
@@ -6546,7 +6747,7 @@ packages:
dependencies:
'@babel/core': 7.22.17
'@babel/standalone': 7.22.17
- '@babel/types': 7.23.0
+ '@babel/types': 7.23.6
defu: 6.1.2
jiti: 1.20.0
mri: 1.2.0
diff --git a/src/api/instance/api.ts b/src/api/instance/api.ts
index e162ae7..fa9558a 100644
--- a/src/api/instance/api.ts
+++ b/src/api/instance/api.ts
@@ -9,6 +9,7 @@ import { env } from '@/config/env';
* @see src/api/instance/instance.ts
*/
export const api = new Service({
+ timeout: 2000,
baseURL: env.apiPrefix,
});
@@ -17,12 +18,12 @@ export const api = new Service({
*/
addToastInterceptor(api.instance);
+/**
+ * 添加异常处理拦截器
+ */
+addExceptionInterceptor(api.instance, () => api.expireHandler?.());
/**
* 添加登陆令牌拦截器
*/
addAuthInterceptor(api.instance);
-/**
- * 添加异常处理拦截器
- */
-addExceptionInterceptor(api.instance, () => api.expireHandler?.());
diff --git a/src/api/interceptors/auth.ts b/src/api/interceptors/auth.ts
index c7aceb8..9af5bf4 100644
--- a/src/api/interceptors/auth.ts
+++ b/src/api/interceptors/auth.ts
@@ -11,6 +11,7 @@ export function addAuthInterceptor(axios: AxiosInstance) {
if (userStore.accessToken) {
config.headers.Authorization = `Bearer ${userStore.accessToken}`;
}
+ // throw Error('dd');
return config;
});
}
diff --git a/src/api/interceptors/exception.ts b/src/api/interceptors/exception.ts
index 00e7ddd..20e4e61 100644
--- a/src/api/interceptors/exception.ts
+++ b/src/api/interceptors/exception.ts
@@ -33,6 +33,7 @@ export function addExceptionInterceptor(axios: AxiosInstance, exipreHandler?: (.
return res;
},
error => {
+ console.log('res error', error);
if (error.response) {
const code = error.response.data?.code;
if (expiredCodes.includes(code)) {
diff --git a/src/api/interceptors/toast.ts b/src/api/interceptors/toast.ts
index f50acb3..4df00da 100644
--- a/src/api/interceptors/toast.ts
+++ b/src/api/interceptors/toast.ts
@@ -22,7 +22,7 @@ export function addToastInterceptor(axios: AxiosInstance) {
return config;
},
error => {
- error.config.closeToast?.();
+ error.config?.closeToast?.();
return Promise.reject(error);
}
);
@@ -37,7 +37,7 @@ export function addToastInterceptor(axios: AxiosInstance) {
return response;
},
error => {
- error.config.closeToast?.();
+ error.config?.closeToast?.();
return Promise.reject(error);
}
);
diff --git a/src/components/AnForbidden/AnForbidden.vue b/src/components/AnForbidden/AnForbidden.vue
index 7854858..8ec1227 100644
--- a/src/components/AnForbidden/AnForbidden.vue
+++ b/src/components/AnForbidden/AnForbidden.vue
@@ -5,8 +5,8 @@
-
403
-
权限不足,如需访问请联系管理员!
+
403
+
权限不足,如需访问请联系管理员!
diff --git a/src/components/AnForm/components/Form.tsx b/src/components/AnForm/components/Form.tsx
index f83eb3f..fb45352 100644
--- a/src/components/AnForm/components/Form.tsx
+++ b/src/components/AnForm/components/Form.tsx
@@ -1,12 +1,29 @@
-import { Form, FormInstance } from '@arco-design/web-vue';
+import { Form, FormInstance, Message } from '@arco-design/web-vue';
import { useVModel } from '@vueuse/core';
-import { PropType } from 'vue';
-import { FormContextKey } from './useFormContext';
-import { useFormItems } from './useFormItems';
-import { useFormModel } from './useFormModel';
-import { useFormRef } from './useFormRef';
-import { useFormSubmit } from './useFormSubmit';
+import { ComputedRef, InjectionKey, PropType, Ref } from 'vue';
+import { initFormItems } from '../utils/useFormItems';
+import { FormRef, useFormRef } from '../utils/useFormRef';
import { AnFormItem, AnFormItemProps } from './FormItem';
+import { cloneDeep, isFunction, isObject, merge } from 'lodash-es';
+import { getModel } from '../utils/useFormModel';
+
+const SUBMIT_ITEM = {
+ field: 'id',
+ setter: 'submit' as const,
+ itemProps: {
+ hideLabel: true,
+ },
+};
+
+export type FormContextInterface = FormRef & {
+ model: Ref;
+ items: ComputedRef;
+ loading: Ref;
+ submitForm: any;
+ resetForm: any;
+};
+
+export const FormContextKey = Symbol('FormContextKey') as InjectionKey;
/**
* 表单组件
@@ -50,7 +67,7 @@ export const AnForm = defineComponent({
* ```
*/
submit: {
- type: [String, Function, Object] as PropType,
+ type: [Function, Object] as PropType,
},
/**
* 传给Form组件的参数
@@ -69,25 +86,61 @@ export const AnForm = defineComponent({
setup(props, { slots, emit }) {
const model = useVModel(props, 'model', emit);
const items = computed(() => props.items);
- const formRefes = useFormRef();
- const formModel = useFormModel(model, formRefes.clearValidate);
- const formItems = useFormItems(items, model);
- const formSubmit = useFormSubmit(props, formRefes.validate, formModel.getModel);
- const context = { slots, ...formModel, ...formItems, ...formRefes, ...formSubmit };
+ const initModel = cloneDeep(model.value);
+ const loading = ref(false);
+ const { formRef, ...formMethods } = useFormRef();
+ const submitItem = () => {
+ if (!props.submit) {
+ return null;
+ }
+ if (isFunction(props.submit)) {
+ return SUBMIT_ITEM;
+ }
+ if (isObject(props.submit)) {
+ return merge({}, SUBMIT_ITEM, props.submit);
+ }
+ };
+
+ const resetForm = () => {
+ model.value = cloneDeep(initModel);
+ formRef.value?.clearValidate();
+ };
+
+ const submitForm = async () => {
+ if (await formRef.value?.validate()) {
+ return;
+ }
+ const submit: any = typeof props.submit === 'object' ? props.submit.visible : props.submit;
+ try {
+ loading.value = true;
+ const data = getModel(model.value);
+ const res = await submit?.(data, props.items);
+ const msg = res?.data?.message;
+ msg && Message.success(`提示: ${msg}`);
+ } catch (e) {
+ console.log(e);
+ } finally {
+ loading.value = false;
+ }
+ };
+
+ const context = { slots, loading, resetForm, submitForm, submitItem, model, items, formRef, ...formMethods };
provide(FormContextKey, context);
+
+ onMounted(() => {
+ initFormItems(props.items, model.value);
+ });
+
return context;
},
render() {
return (
-
);
},
@@ -99,4 +152,4 @@ export type AnFormProps = Pick any;
-export type AnFormSubmit = string | AnFormSubmitFn;
+export type AnFormSubmit = AnFormSubmitFn | AnFormItemProps;
diff --git a/src/components/AnForm/components/FormModal.tsx b/src/components/AnForm/components/FormModal.tsx
index 4691873..dacd9ba 100644
--- a/src/components/AnForm/components/FormModal.tsx
+++ b/src/components/AnForm/components/FormModal.tsx
@@ -1,19 +1,17 @@
-import { useVisible } from '@/hooks/useVisible';
-import { Button, ButtonInstance, FormInstance, Modal } from '@arco-design/web-vue';
+import { Button, ButtonInstance, FormInstance, Message, Modal } from '@arco-design/web-vue';
import { InjectionKey, PropType, Ref } from 'vue';
-import { useModalSubmit } from './useModalSubmit';
-import { useModalTrigger } from './useModalTrigger';
-import { AnForm, AnFormInstance, AnFormProps, AnFormSubmit } from './Form';
+import { AnForm, AnFormInstance, AnFormSubmit } from './Form';
import { AnFormItemProps } from './FormItem';
import { useVModel } from '@vueuse/core';
+import { getModel, setModel } from '../utils/useFormModel';
export interface AnFormModalContext {
visible: Ref;
loading: Ref;
- formRef: Ref;
+ anFormRef: Ref;
+ submitForm: () => any | Promise;
open: (data: Recordable) => void;
close: () => void;
- submitForm: () => any | Promise;
modalTitle: () => any;
modalTrigger: () => any;
onClose: () => void;
@@ -97,7 +95,7 @@ export const AnFormModal = defineComponent({
* ```
*/
submit: {
- type: [String, Function] as PropType,
+ type: [Object, Function] as PropType,
},
/**
* 传给Form组件的参数
@@ -114,25 +112,10 @@ export const AnFormModal = defineComponent({
},
emits: ['update:model', 'submited'],
setup(props, { emit }) {
- const formRef = ref(null);
const model = useVModel(props, 'model', emit);
+ const anFormRef = ref(null);
const visible = ref(false);
- const show = () => (visible.value = true);
- const hide = () => (visible.value = false);
- const modalTrigger = useModalTrigger(props, show);
- const { loading, setLoading, submitForm } = useModalSubmit(props, formRef, visible, emit, model);
-
- const open = (data: Recordable = {}) => {
- formRef.value?.setModel(data);
- visible.value = true;
- };
-
- const close = () => {
- setLoading(false);
- hide();
- };
-
- const onClose = () => {};
+ const loading = ref(false);
const modalTitle = () => {
if (typeof props.title === 'string') {
@@ -141,10 +124,71 @@ export const AnFormModal = defineComponent({
return ;
};
+ const modalTrigger = () => {
+ if (!props.trigger) {
+ return null;
+ }
+ if (typeof props.trigger === 'function') {
+ return ;
+ }
+ const internal = {
+ text: '新增',
+ buttonProps: {},
+ buttonSlots: {},
+ };
+ if (typeof props.trigger === 'string') {
+ internal.text = props.trigger;
+ }
+ if (typeof props.trigger === 'object') {
+ Object.assign(internal, props.trigger);
+ }
+ return (
+
+ );
+ };
+
+ const submitForm = async () => {
+ if (await anFormRef.value?.validate()) {
+ return;
+ }
+ try {
+ loading.value = true;
+ const data = getModel(model.value);
+ const res = await (props as any).submit?.(data, props.items);
+ const msg = res?.data?.message;
+ msg && Message.success(msg);
+ visible.value = false;
+ emit('submited', res);
+ } catch {
+ // todo
+ } finally {
+ loading.value = false;
+ }
+ };
+
+ const open = async (data: Recordable = {}) => {
+ visible.value = true;
+ await nextTick();
+ anFormRef.value && setModel(model.value, data);
+ };
+
+ const close = () => {
+ loading.value = false;
+ visible.value = false;
+ };
+
+ const onClose = () => {};
+
const context: AnFormModalContext = {
visible,
loading,
- formRef,
+ anFormRef,
open,
close,
onClose,
@@ -155,7 +199,9 @@ export const AnFormModal = defineComponent({
provide(AnFormModalContextKey, context);
- return context;
+ return {
+ ...context
+ };
},
render() {
return (
@@ -164,11 +210,11 @@ export const AnFormModal = defineComponent({
{{
diff --git a/src/components/AnForm/components/useFormContext.tsx b/src/components/AnForm/components/useFormContext.tsx
deleted file mode 100644
index cbefe1a..0000000
--- a/src/components/AnForm/components/useFormContext.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { InjectionKey } from "vue";
-import { FormItems } from "./useFormItems";
-import { FormModel } from "./useFormModel";
-import { FormRef } from "./useFormRef";
-import { FormSubmit } from "./useFormSubmit";
-
-export type FormContextInterface = FormModel &
- FormItems &
- FormRef &
- FormSubmit & {
- slots: Recordable;
- };
-
-export const FormContextKey = Symbol("FormContextKey") as InjectionKey;
diff --git a/src/components/AnForm/components/useFormItems.tsx b/src/components/AnForm/components/useFormItems.tsx
deleted file mode 100644
index eef76d3..0000000
--- a/src/components/AnForm/components/useFormItems.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { Ref } from 'vue';
-import { AnFormItemProps } from './FormItem';
-import { setterMap } from './FormSetter';
-
-export function useFormItems(items: Ref, model: Ref) {
- const getItem = (field: string) => {
- return items.value.find(i => i.field === field);
- };
-
- const getItemOptions = (field: string) => {
- const item = getItem(field);
- if (item) {
- return (item.setterProps as any)?.options;
- }
- };
-
- const initItemOptions = (field: string) => {
- const item = getItem(field);
- if (!item) {
- return;
- }
- const setter = setterMap[item.setter!];
- if (!setter) {
- return;
- }
- setter.onSetup?.({ item, items: items.value, model: model.value });
- };
-
- const initItems = () => {
- for (const item of items.value) {
- const setter = setterMap[item?.setter!];
- setter.onSetup?.({ item, items: items.value, model: model.value });
- }
- };
-
- const initItem = (field: string) => {
- const item = getItem(field);
- if (!item) {
- return;
- }
- const setter = setterMap[item?.setter!];
- setter.onSetup?.({ item, items: items.value, model: model.value });
- };
-
- onMounted(() => {
- initItems();
- });
-
- return {
- items,
- getItem,
- initItem,
- initItems,
- getItemOptions,
- initItemOptions,
- };
-}
-
-export type FormItems = ReturnType;
diff --git a/src/components/AnForm/components/useFormModel.tsx b/src/components/AnForm/components/useFormModel.tsx
deleted file mode 100644
index cd18784..0000000
--- a/src/components/AnForm/components/useFormModel.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import { cloneDeep } from 'lodash-es';
-import { Ref } from 'vue';
-
-/**
- * 表单数据管理
- * @param initial 初始值
- * @returns
- */
-export function useFormModel(model: Ref, clearValidate: any) {
- const initial = cloneDeep(model.value);
-
- const resetModel = () => {
- model.value = cloneDeep(initial);
- clearValidate();
- };
-
- const getInitialModel = () => {
- return initial;
- };
-
- const setModel = (data: Recordable) => {
- for (const key of Object.keys(model.value)) {
- model.value[key] = data[key];
- }
- };
-
- const getModel = () => {
- return formatModel(model.value);
- };
-
- return {
- model,
- getInitialModel,
- resetModel,
- setModel,
- getModel,
- };
-}
-
-export type FormModel = ReturnType;
-
-export function formatModel(model: Recordable) {
- const data: Recordable = {};
-
- for (const [key, value] of Object.entries(model)) {
- if (value === '') {
- continue;
- }
- if (/^\[.+\]$/.test(key)) {
- formatModelArray(key, value, data);
- continue;
- }
- if (/^\{.+\}$/.test(key)) {
- formatModelObject(key, value, data);
- continue;
- }
- data[key] = value;
- }
-
- return data;
-}
-
-function formatModelArray(key: string, value: any, data: Recordable) {
- let field = key.replaceAll(/\s/g, '');
- field = field.match(/^\[(.+)\]$/)?.[1] ?? '';
-
- if (!field) {
- data[key] = value;
- return;
- }
-
- field.split(',').forEach((key, index) => {
- data[key] = value?.[index];
- });
-
- return data;
-}
-
-function formatModelObject(key: string, value: any, data: Recordable) {
- let field = key.replaceAll(/\s/g, '');
- field = field.match(/^\{(.+)\}$/)?.[1] ?? '';
-
- if (!field) {
- data[key] = value;
- return;
- }
-
- for (const key of field.split(',')) {
- data[key] = value?.[key];
- }
-
- return data;
-}
diff --git a/src/components/AnForm/components/useFormSubmit.tsx b/src/components/AnForm/components/useFormSubmit.tsx
deleted file mode 100644
index 01da7d6..0000000
--- a/src/components/AnForm/components/useFormSubmit.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { Message } from '@arco-design/web-vue';
-import { AnFormProps } from './Form';
-import { AnFormItemProps } from './FormItem';
-import { cloneDeep } from 'lodash-es';
-
-const SUBMIT_ITEM = {
- field: 'id',
- setter: 'submit' as const,
- itemProps: {
- hideLabel: true,
- },
-};
-
-export function useFormSubmit(props: AnFormProps, validate: any, getModel: any) {
- const loading = ref(false);
- const submitItem = ref(null);
-
- if (props.submit) {
- submitItem.value = cloneDeep(SUBMIT_ITEM);
- }
-
- /**
- * 设置loading
- * @param value 值
- */
- const setLoading = (value: boolean) => {
- loading.value = value;
- };
-
- /**
- * 提交表单
- */
- const submitForm = async () => {
- if (await validate()) {
- return;
- }
- const submit = typeof props.submit === 'string' ? () => null : props.submit;
- try {
- loading.value = true;
- const data = getModel();
- const res = await submit?.(data, props.items ?? []);
- const msg = res?.data?.message;
- msg && Message.success(`提示: ${msg}`);
- } catch {
- console.log();
- } finally {
- loading.value = false;
- }
- };
-
- /**
- * 取消提交
- */
- const cancelForm = () => {};
-
- return {
- loading,
- submitItem,
- setLoading,
- submitForm,
- cancelForm,
- };
-}
-
-export type FormSubmit = ReturnType;
diff --git a/src/components/AnForm/components/useModalSubmit.tsx b/src/components/AnForm/components/useModalSubmit.tsx
deleted file mode 100644
index 7933c7d..0000000
--- a/src/components/AnForm/components/useModalSubmit.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import { sleep } from '@/utils';
-import { Message } from '@arco-design/web-vue';
-import { cloneDeep } from 'lodash-es';
-import { Ref } from 'vue';
-
-export function useModalSubmit(props: any, formRef: any, visible: Ref, emit?: any, model?: Ref) {
- const loading = ref(false);
- const origin = cloneDeep(props.model);
-
- const submitForm = async () => {
- if (await formRef.value?.validate()) {
- return;
- }
- try {
- loading.value = true;
- const data = formRef.value?.getModel() ?? {};
- const res = await props.submit?.(data, props.items);
- const msg = res?.data?.message;
- msg && Message.success(msg);
- emit('submited', res);
- visible.value = false;
- if (model) {
- model.value = cloneDeep(origin);
- }
- } catch {
- // todo
- } finally {
- loading.value = false;
- }
- };
-
- const setLoading = (value: boolean) => {
- loading.value = value;
- };
-
- return {
- loading,
- setLoading,
- submitForm,
- };
-}
diff --git a/src/components/AnForm/components/useModalTrigger.tsx b/src/components/AnForm/components/useModalTrigger.tsx
deleted file mode 100644
index b307cca..0000000
--- a/src/components/AnForm/components/useModalTrigger.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Button } from '@arco-design/web-vue';
-
-export function useModalTrigger(props: any, open: () => void) {
- const modalTrigger = () => {
- if (!props.trigger) {
- return null;
- }
- if (typeof props.trigger === 'function') {
- return ;
- }
- const internal = {
- text: '新增',
- buttonProps: {},
- buttonSlots: {},
- };
- if (typeof props.trigger === 'string') {
- internal.text = props.trigger;
- }
- if (typeof props.trigger === 'object') {
- Object.assign(internal, props.trigger);
- }
- return (
-
- );
- };
- return modalTrigger;
-}
diff --git a/src/components/AnForm/hooks/useForm.tsx b/src/components/AnForm/hooks/useForm.tsx
index 5b4792d..797655b 100644
--- a/src/components/AnForm/hooks/useForm.tsx
+++ b/src/components/AnForm/hooks/useForm.tsx
@@ -1,6 +1,6 @@
import { merge } from 'lodash-es';
import { AnForm, AnFormInstance, AnFormProps } from '../components/Form';
-import { FormItem, useItems } from './useItems';
+import { FormItem, useFormItems } from './useFormItems';
export type FormUseOptions = Partial> & {
/**
@@ -20,7 +20,7 @@ export type FormUseOptions = Partial> & {
export function useFormProps(options: FormUseOptions): Required {
const { model: _model = {}, items: _items = [], submit = () => null, formProps = {} } = options;
const model = merge({ id: undefined }, _model);
- const items = useItems(_items ?? [], model);
+ const items = useFormItems(_items ?? [], model);
return {
model,
items,
diff --git a/src/components/AnForm/hooks/useItems.tsx b/src/components/AnForm/hooks/useFormItems.tsx
similarity index 53%
rename from src/components/AnForm/hooks/useItems.tsx
rename to src/components/AnForm/hooks/useFormItems.tsx
index 770fa11..8327edd 100644
--- a/src/components/AnForm/hooks/useItems.tsx
+++ b/src/components/AnForm/hooks/useFormItems.tsx
@@ -1,7 +1,7 @@
-import { defaultsDeep, merge, omit } from 'lodash-es';
+import { defaultsDeep, has, merge, omit } from 'lodash-es';
import { AnFormItemProps, AnFormItemPropsBase } from '../components/FormItem';
import { SetterItem, setterMap } from '../components/FormSetter';
-import { Rule, useRules } from './useRules';
+import { Rule, useFormRules } from './useFormRules';
/**
* 表单项数据
@@ -34,17 +34,26 @@ export type FormItem = Omit &
* ```
*/
rules?: Rule[];
+
+ /**
+ * 参数 `setterProps.placeholder` 的快捷语法
+ * @example
+ * ```ts
+ * '请输入用户名称'
+ * ```
+ */
+ placeholder?: string | string[];
};
const ITEM: Partial = {
setter: 'input',
};
-export function useItems(list: FormItem[], model: Recordable) {
- const items: AnFormItemProps[] = [];
+export function useFormItems(items: FormItem[], model: Recordable) {
+ const data: AnFormItemProps[] = [];
- for (const item of list) {
- let target: any = defaultsDeep({}, ITEM);
+ for (const item of items) {
+ let target: AnFormItemProps = defaultsDeep({}, ITEM);
if (!item.setter || typeof item.setter === 'string') {
const setter = setterMap[item.setter ?? 'input'];
@@ -53,16 +62,23 @@ export function useItems(list: FormItem[], model: Recordable) {
}
}
- target = merge(target, omit(item, ['required', 'rules', 'value']));
+ target = merge(target, omit(item, ['required', 'rules', 'value', 'placeholder']));
- const rules = useRules(item);
- if (rules) {
+ if (item.required || item.rules) {
+ const rules = useFormRules(item)!;
target.rules = rules;
}
- model[item.field] = model[item.field] ?? item.value;
- items.push(target);
+ if (target.setterProps && has(item, 'placeholder')) {
+ (target.setterProps as Recordable).placholder = item.placeholder;
+ }
+
+ if (has(item, 'value')) {
+ model[item.field] = item.value;
+ }
+
+ data.push(target);
}
- return items;
+ return data;
}
diff --git a/src/components/AnForm/hooks/useFormModal.tsx b/src/components/AnForm/hooks/useFormModal.tsx
index 6825427..9104915 100644
--- a/src/components/AnForm/hooks/useFormModal.tsx
+++ b/src/components/AnForm/hooks/useFormModal.tsx
@@ -1,7 +1,7 @@
import { merge } from 'lodash-es';
import { AnFormModal, AnFormModalProps } from '../components/FormModal';
import { useFormProps } from './useForm';
-import { FormItem } from './useItems';
+import { FormItem } from './useFormItems';
export type FormModalUseOptions = Partial> & {
/**
@@ -13,6 +13,10 @@ export type FormModalUseOptions = Partial> & {
* ```
*/
width?: number;
+ /**
+ * modal宽度
+ */
+ modalWidth?: number;
/**
* 表单类名
* @description 参数 `formProps.class` 的便捷语法
@@ -58,7 +62,7 @@ export function useFormModalProps(options: FormModalUseOptions): AnFormModalProp
export function useFormModal(options: FormModalUseOptions) {
const modalRef = ref | null>(null);
- const formRef = computed(() => modalRef.value?.formRef);
+ const formRef = computed(() => modalRef.value?.anFormRef);
const open = (data: Recordable = {}) => modalRef.value?.open(data);
const rawProps = useFormModalProps(options);
const props = reactive(rawProps);
diff --git a/src/components/AnForm/hooks/useRules.tsx b/src/components/AnForm/hooks/useFormRules.tsx
similarity index 95%
rename from src/components/AnForm/hooks/useRules.tsx
rename to src/components/AnForm/hooks/useFormRules.tsx
index 9273776..fda9abe 100644
--- a/src/components/AnForm/hooks/useRules.tsx
+++ b/src/components/AnForm/hooks/useFormRules.tsx
@@ -70,7 +70,7 @@ function defineRuleMap>(ruleMap: T) {
* @param item 表单项
* @returns
*/
-export const useRules = (item: T) => {
+export const useFormRules = (item: T) => {
const data: AnFormItemRule[] = [];
const { required, rules } = item;
diff --git a/src/components/AnForm/index.ts b/src/components/AnForm/index.ts
index 0a38872..f60fea7 100644
--- a/src/components/AnForm/index.ts
+++ b/src/components/AnForm/index.ts
@@ -2,15 +2,11 @@ export * from './components/Form';
export * from './components/FormItem';
export * from './components/FormModal';
export * from './components/FormSetter';
-export * from './components/useFormContext';
-export * from './components/useFormItems';
-export * from './components/useFormModel';
-export * from './components/useFormRef';
-export * from './components/useFormSubmit';
-export * from './components/useModalSubmit';
-export * from './components/useModalTrigger';
+export * from './utils/useFormItems';
+export * from './utils/useFormModel';
+export * from './utils/useFormRef';
export * from './hooks/useForm';
export * from './hooks/useFormModal';
-export * from './hooks/useItems';
-export * from './hooks/useRules';
+export * from './hooks/useFormItems';
+export * from './hooks/useFormRules';
export * from './setters';
diff --git a/src/components/AnForm/setters/Submit.tsx b/src/components/AnForm/setters/Submit.tsx
index 46b625a..6eaf335 100644
--- a/src/components/AnForm/setters/Submit.tsx
+++ b/src/components/AnForm/setters/Submit.tsx
@@ -1,18 +1,16 @@
import { Button } from '@arco-design/web-vue';
-import { FormContextKey } from '../components/useFormContext';
+import { FormContextKey } from '../components/Form';
import { defineSetter } from './util';
export default defineSetter<{}, 'none'>({
setter() {
- const { loading, submitForm, resetModel } = inject(FormContextKey)!;
+ const { submitForm, resetForm } = inject(FormContextKey)!;
return (
<>
-
预览
-
+
diff --git a/src/components/editor/components/EditorRight.vue b/src/components/editor/components/EditorRight.vue
index 5cc145c..c7ebe0a 100644
--- a/src/components/editor/components/EditorRight.vue
+++ b/src/components/editor/components/EditorRight.vue
@@ -1,11 +1,11 @@
-
+
-
+
- 组件属性({{ BlockerMap[model.type].title }})
+ {{ BlockerMap[model.type].title }}属性
diff --git a/src/components/editor/components/EditorSetting.vue b/src/components/editor/components/EditorSetting.vue
index 30af625..fdb8480 100644
--- a/src/components/editor/components/EditorSetting.vue
+++ b/src/components/editor/components/EditorSetting.vue
@@ -1,10 +1,10 @@
-
+
- 画布设置
+ 画布属性
@@ -31,6 +31,22 @@
+
+
+
+ 中文(cn)
+ 英语(en)
+ 俄语(ru)
+
+
+
+
+
+
+ 秒(s)
+
+
+
diff --git a/src/components/editor/core/blocker.ts b/src/components/editor/core/blocker.ts
index fe7e006..1f572b8 100644
--- a/src/components/editor/core/blocker.ts
+++ b/src/components/editor/core/blocker.ts
@@ -1,5 +1,6 @@
-import { Component } from "vue";
-import { Block } from "./block";
+import { Component } from 'vue';
+import { Block } from './block';
+import { Container } from './container';
/**
* 组件配置
@@ -38,11 +39,63 @@ export interface Blocker {
*/
viewer?: Component;
/**
- * 初始化钩子
- * @param block 组件
- * @returns
+ * 将实际格式转换为内部格式
*/
- onInit?: (block: Block) => void;
+ onLoadContainer?: (container: Container) => void;
+ /**
+ * 将内部格式转换为实际格式
+ */
+ onSaveContainer?: (container: Container) => any;
+ /**
+ * 将实际格式转换为内部格式
+ */
+ onLoadBlock?: (data: any) => Block;
+ /**
+ * 将内部格式转换为实际格式
+ */
+ onSaveBlock?: (block: Block) => any;
+ /**
+ * 在左侧添加选项卡
+ */
+ addLeftTab?: () => {
+ title: string;
+ icon: string | Component;
+ component: Component;
+ };
+ addBlock?: () => {
+ /**
+ * 唯一标识符
+ */
+ name: string;
+ /**
+ * 显示标题
+ */
+ title: string;
+ /**
+ * 显示图标
+ */
+ icon: string;
+ /**
+ * 显示描述
+ */
+ description: string;
+ /**
+ * 默认初始值
+ */
+ initial: Block;
+ /**
+ * 预览时的渲染组件
+ */
+ render: Component;
+ /**
+ * 编辑参数时的渲染组件
+ */
+ optionRender: Component<{ modelValue: Block }>;
+ /**
+ * 编辑时的渲染组件
+ */
+ modifyRender: Component;
+ }
}
/**
diff --git a/src/components/editor/core/container.ts b/src/components/editor/core/container.ts
index 710544c..5677f3c 100644
--- a/src/components/editor/core/container.ts
+++ b/src/components/editor/core/container.ts
@@ -42,6 +42,8 @@ export interface Container {
* 背景颜色
*/
bgColor: string;
+ langList: string[];
+ langSwitch: number;
}
/**
@@ -58,4 +60,6 @@ export const defaultContainer: Container = {
height: 1080,
bgImage: "",
bgColor: "#ffffff",
+ langList: ['ch', 'en'],
+ langSwitch: 0
};
diff --git a/src/pages/_layout/components/userDropdown.vue b/src/pages/_layout/components/userDropdown.vue
index ff7911f..298eebb 100644
--- a/src/pages/_layout/components/userDropdown.vue
+++ b/src/pages/_layout/components/userDropdown.vue
@@ -31,10 +31,16 @@
-
+
个人设置
+
+
+
+
+ 系统设置
+
diff --git a/src/pages/_layout/index.vue b/src/pages/_layout/index.vue
index b45d651..d5e49f7 100644
--- a/src/pages/_layout/index.vue
+++ b/src/pages/_layout/index.vue
@@ -4,9 +4,9 @@
class="h-13 overflow-hidden flex justify-between items-center gap-4 px-2 pr-4 border-b border-slate-200 bg-white dark:bg-slate-800 dark:border-slate-700"
>
-
-
-
+
+
+
{{ appStore.title }}
v0.0.1
@@ -16,6 +16,15 @@
+
+ null" class="!bg-transparent !hover:bg-gray-100">
+
+
+
+
+
+
+
diff --git a/src/pages/_login/index.vue b/src/pages/_login/index.vue
index 6580eeb..ae6ac9c 100644
--- a/src/pages/_login/index.vue
+++ b/src/pages/_login/index.vue
@@ -19,9 +19,9 @@
class="login-left relative hidden md:block w-full h-full overflow-hidden bg-[rgb(var(--primary-6))] px-4"
>
-
欢迎登陆
-
{{ meridiem }}好,欢迎登陆{{ appStore.title }}!
-
+ 用户登陆
+ {{ meridiem }}好,欢迎访问 {{ appStore.title }} 系统!
+
diff --git a/src/pages/content/media/components/AnGroup.vue b/src/pages/content/material/components/AnGroup.vue
similarity index 84%
rename from src/pages/content/media/components/AnGroup.vue
rename to src/pages/content/material/components/AnGroup.vue
index 9dfdc7f..ea0d7b5 100644
--- a/src/pages/content/media/components/AnGroup.vue
+++ b/src/pages/content/material/components/AnGroup.vue
@@ -9,9 +9,19 @@
-
-
+
+
-
-
+
+
@@ -75,7 +85,6 @@ const updateFileCategories = async () => {
loading.value = true;
const res = await api.fileCategory.getFileCategorys({ size: 0 });
list.value = res.data.data ?? [];
- list.value.unshift({ id: undefined, name: '全部' } as any);
list.value.length && emit('change', list.value[0]);
} catch {
// nothing to do
diff --git a/src/pages/content/material/components/AnPreview.vue b/src/pages/content/material/components/AnPreview.vue
new file mode 100644
index 0000000..13a84fb
--- /dev/null
+++ b/src/pages/content/material/components/AnPreview.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 抱歉,此文件类型暂不支持预览!
+
+
+
+
+
+
+
diff --git a/src/pages/content/media/components/AnUpload.vue b/src/pages/content/material/components/AnUpload.vue
similarity index 67%
rename from src/pages/content/media/components/AnUpload.vue
rename to src/pages/content/material/components/AnUpload.vue
index 27a7c97..5b6565f 100644
--- a/src/pages/content/media/components/AnUpload.vue
+++ b/src/pages/content/material/components/AnUpload.vue
@@ -38,66 +38,68 @@
-
-
- -
-
-
-
-
-
-
- {{ item.name }}
-
{{ numeral(item.file?.size).format('0 b') }}
+
+
+
+ -
+
+
+
+
+
+
+ {{ item.name }}
+ {{ numeral(item.file?.size).format('0 b') }}
+
+
-
-
停止
-
重试
-
- 删除
-
+
+
+
+
+
+ 等待上传
+
+
+
+ 正在上传
+
+
+
+ 上传成功
+
+
+
+ 上传失败
+
+
+
+
+
+ 速度:{{ formatSpeed(item.uid) }}/s, 进度:{{ formatProgress(item) }} %
+
+
+ 耗时:{{ fileMap.get(item.uid)?.cost || 0 }} 秒, 平均:{{ formatAspeed(item.uid) }}/s
+
+ 原因:{{ fileMap.get(item.uid)?.error }}
+
-
-
-
-
-
- 等待上传
-
-
-
- 正在上传
-
-
-
- 上传成功
-
-
-
- 上传失败
-
-
-
-
-
- 速度:{{ formatSpeed(item.uid) }}/s, 进度:{{ formatProgress(item) }} %
-
-
- 耗时:{{ fileMap.get(item.uid)?.cost || 0 }} 秒, 平均:{{ formatAspeed(item.uid) }}/s
-
- 原因:{{ fileMap.get(item.uid)?.error }}
-
-
-
-
-
-
-
-
+
已上传 {{ stat.doneCount }}/{{ fileList.length }} 项
diff --git a/src/pages/content/material/components/util.ts b/src/pages/content/material/components/util.ts
new file mode 100644
index 0000000..0313ff6
--- /dev/null
+++ b/src/pages/content/material/components/util.ts
@@ -0,0 +1,45 @@
+const typeIconMap: Record
= {
+ video: 'icon-park-outline-video-file',
+ audio: 'icon-park-outline-audio-file',
+ image: 'icon-park-outline-file-pdf',
+ text: 'icon-park-outline-file-txt',
+ application: 'icon-park-outline-file-code',
+ unknown: 'icon-park-outline-file-question',
+};
+
+function getIconnameByMimetype(mimetype: string) {
+ const [type, subtype] = mimetype.split('/');
+ return typeIconMap[type] || typeIconMap.unknown;
+}
+
+enum MIME {
+ IMAGE = 'image',
+ VIDEO = 'video',
+ AUDIO = 'audio',
+ TEXT = 'text',
+ APPLICATION = 'application',
+}
+
+function getIcon(mimetype: string) {
+ const [type, subtype] = mimetype.split('/');
+ if (type === MIME.IMAGE) {
+ return 'icon-file-iimage';
+ }
+ if (type === MIME.VIDEO) {
+ return 'icon-file-ivideo';
+ }
+ if (type === MIME.TEXT) {
+ return 'icon-file-itxt';
+ }
+ if (type === MIME.AUDIO) {
+ return 'icon-file-iaudio';
+ }
+ if (type === MIME.APPLICATION) {
+ if (subtype === 'zip') {
+ return 'icon-file-izip';
+ }
+ }
+ return 'icon-file-iunknown';
+}
+
+export { getIcon, getIconnameByMimetype };
diff --git a/src/pages/content/media/index.vue b/src/pages/content/material/index.vue
similarity index 89%
rename from src/pages/content/media/index.vue
rename to src/pages/content/material/index.vue
index 321df7d..2bcf351 100644
--- a/src/pages/content/media/index.vue
+++ b/src/pages/content/material/index.vue
@@ -9,7 +9,7 @@
tableRef?.refresh()">
-
+
@@ -23,19 +23,17 @@ import { Message } from '@arco-design/web-vue';
import numeral from 'numeral';
import AnGroup from './components/AnGroup.vue';
import AnUpload from './components/AnUpload.vue';
+import AnPreview from './components/AnPreview.vue';
import { getIcon } from './components/util';
-const visible = ref(false);
const current = ref
();
-const image = ref('');
+const viewer = reactive({ visible: false, url: undefined, type: undefined });
const preview = (record: any) => {
- if (!record.mimetype.startsWith('image')) {
- window.open(record.path, '_blank');
- return;
- }
- image.value = record.path;
- visible.value = true;
+ const [type] = record.mimetype.split('/');
+ viewer.url = record.path;
+ viewer.type = type;
+ viewer.visible = true;
};
const onCategoryChange = (category: FileCategory) => {
@@ -48,7 +46,7 @@ const onCategoryChange = (category: FileCategory) => {
const copyLink = (record: Recordable) => {
window.navigator.clipboard.writeText(record.path);
- Message.success(`提示:已复制 ${record.name} 的地址!`);
+ Message.success(`已复制 ${record.name} 的地址!`);
};
const {
@@ -63,10 +61,10 @@ const {
dataIndex: 'name',
render: ({ record }) => {
return (
-
+
{record.mimetype.startsWith('image') ? (
-
+
) : (
diff --git a/src/pages/content/media/components/util.ts b/src/pages/content/media/components/util.ts
deleted file mode 100644
index 0c3b874..0000000
--- a/src/pages/content/media/components/util.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-const typeIconMap: Record = {
- video: "icon-park-outline-video-file",
- audio: "icon-park-outline-audio-file",
- image: "icon-park-outline-file-pdf",
- text: "icon-park-outline-file-txt",
- application: "icon-park-outline-file-code",
- unknown: "icon-park-outline-file-question",
-};
-
-function getIconnameByMimetype(mimetype: string) {
- const [type, subtype] = mimetype.split("/");
- return typeIconMap[type] || typeIconMap.unknown;
-}
-
-function getIcon(mimetype: string) {
- if (mimetype.startsWith("image")) {
- return "icon-fmt-png";
- }
- if (mimetype.startsWith("video")) {
- return "icon-fmt-video";
- }
- if (mimetype.startsWith("text")) {
- return "icon-fmt-txt";
- }
- if (mimetype.startsWith("audio")) {
- return "icon-fmt-mp";
- }
- return "icon-fmt-visio";
-}
-
-export { getIcon, getIconnameByMimetype };
-
diff --git a/src/pages/system/index.vue b/src/pages/system/index.vue
index cb84ee2..b4e977e 100644
--- a/src/pages/system/index.vue
+++ b/src/pages/system/index.vue
@@ -1,4 +1,6 @@
-
+
+
+
{
diff --git a/src/pages/system/logl/index.vue b/src/pages/system/logl/index.vue
deleted file mode 100644
index 67fd6b3..0000000
--- a/src/pages/system/logl/index.vue
+++ /dev/null
@@ -1,140 +0,0 @@
-
-
-
-
- 添加
-
-
-
-
-
-
-
-
-
-
-
-{
- "meta": {
- "name": "SystemLoglPage",
- "sort": 10303,
- "title": "登陆日志",
- "icon": "icon-park-outline-log"
- }
-}
-
diff --git a/src/pages/system/logo/index.vue b/src/pages/system/logo/index.vue
deleted file mode 100644
index 7b8d23b..0000000
--- a/src/pages/system/logo/index.vue
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-{
- "meta": {
- "name": "SystemLogoPage",
- "sort": 10304,
- "title": "操作日志",
- "icon": "icon-park-outline-doc-detail"
- }
-}
-
diff --git a/src/pages/system/user/index.vue b/src/pages/system/user/index.vue
index 3a9b93f..9bd8043 100644
--- a/src/pages/system/user/index.vue
+++ b/src/pages/system/user/index.vue
@@ -7,7 +7,7 @@