diff --git a/mock/api/route.ts b/mock/api/route.ts
index 66dec8a2..efedcfc8 100644
--- a/mock/api/route.ts
+++ b/mock/api/route.ts
@@ -19,7 +19,7 @@ const routes: AuthRoute.Route[] = [
path: '/dashboard/workbench',
component: 'self',
meta: {
- title: '分析页',
+ title: '工作台',
permissions: ['super', 'admin']
}
}
@@ -33,12 +33,12 @@ const routes: AuthRoute.Route[] = [
{
name: 'about',
path: '/about',
- component: 'layout',
+ component: 'self',
meta: {
title: '关于',
+ singleLayout: 'layout',
permissions: ['super', 'admin', 'test'],
- icon: 'fluent:book-information-24-regular',
- single: true
+ icon: 'fluent:book-information-24-regular'
}
},
{
diff --git a/package.json b/package.json
index 3d4dd80d..ebe9c704 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,7 @@
}
},
"dependencies": {
- "@vueuse/core": "^7.5.1",
+ "@vueuse/core": "^7.5.3",
"axios": "^0.24.0",
"colord": "^2.9.2",
"crypto-js": "^4.1.1",
@@ -40,7 +40,7 @@
"@iconify/json": "^1.1.452",
"@iconify/vue": "^3.1.1",
"@types/crypto-js": "^4.1.0",
- "@types/node": "^17.0.6",
+ "@types/node": "^17.0.8",
"@types/qs": "^6.9.7",
"@typescript-eslint/eslint-plugin": "^5.8.1",
"@typescript-eslint/parser": "^5.9.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 13a7f0ab..351d5685 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -6,14 +6,14 @@ specifiers:
'@iconify/json': ^1.1.452
'@iconify/vue': ^3.1.1
'@types/crypto-js': ^4.1.0
- '@types/node': ^17.0.6
+ '@types/node': ^17.0.8
'@types/qs': ^6.9.7
'@typescript-eslint/eslint-plugin': ^5.8.1
'@typescript-eslint/parser': ^5.9.0
'@vitejs/plugin-vue': ^2.0.1
'@vue/eslint-config-prettier': ^7.0.0
'@vue/eslint-config-typescript': ^10.0.0
- '@vueuse/core': ^7.5.1
+ '@vueuse/core': ^7.5.3
axios: ^0.24.0
colord: ^2.9.2
commitizen: ^4.2.4
@@ -55,7 +55,7 @@ specifiers:
windicss: ^3.4.2
dependencies:
- '@vueuse/core': registry.npmmirror.com/@vueuse/core/7.5.1_vue@3.2.26
+ '@vueuse/core': registry.npmmirror.com/@vueuse/core/7.5.3_vue@3.2.26
axios: registry.npmmirror.com/axios/0.24.0
colord: registry.npmmirror.com/colord/2.9.2
crypto-js: registry.npmmirror.com/crypto-js/4.1.1
@@ -69,21 +69,21 @@ dependencies:
vue-router: registry.npmmirror.com/vue-router/4.0.12_vue@3.2.26
devDependencies:
- '@commitlint/cli': registry.npmmirror.com/@commitlint/cli/16.0.1_@types+node@17.0.6
+ '@commitlint/cli': registry.npmmirror.com/@commitlint/cli/16.0.1_@types+node@17.0.8
'@commitlint/config-conventional': registry.npmmirror.com/@commitlint/config-conventional/16.0.0
'@iconify/json': registry.npmmirror.com/@iconify/json/1.1.452
'@iconify/vue': registry.npmmirror.com/@iconify/vue/3.1.1_vue@3.2.26
'@types/crypto-js': registry.npmmirror.com/@types/crypto-js/4.1.0
- '@types/node': registry.npmmirror.com/@types/node/17.0.6
+ '@types/node': registry.npmmirror.com/@types/node/17.0.8
'@types/qs': registry.npmmirror.com/@types/qs/6.9.7
'@typescript-eslint/eslint-plugin': registry.npmmirror.com/@typescript-eslint/eslint-plugin/5.8.1_bd2fd93dbcc607ad2f21b784bccfe0c8
'@typescript-eslint/parser': registry.npmmirror.com/@typescript-eslint/parser/5.9.0_eslint@8.6.0+typescript@4.5.4
'@vitejs/plugin-vue': registry.npmmirror.com/@vitejs/plugin-vue/2.0.1_vite@2.7.10+vue@3.2.26
'@vue/eslint-config-prettier': registry.npmmirror.com/@vue/eslint-config-prettier/7.0.0_eslint@8.6.0+prettier@2.5.1
'@vue/eslint-config-typescript': registry.npmmirror.com/@vue/eslint-config-typescript/10.0.0_57f850728139557a3a27f1248f77f964
- commitizen: registry.npmmirror.com/commitizen/4.2.4_@types+node@17.0.6
+ commitizen: registry.npmmirror.com/commitizen/4.2.4_@types+node@17.0.8
cross-env: registry.nlark.com/cross-env/7.0.3
- cz-conventional-changelog: registry.nlark.com/cz-conventional-changelog/3.3.0_@types+node@17.0.6
+ cz-conventional-changelog: registry.nlark.com/cz-conventional-changelog/3.3.0_@types+node@17.0.8
cz-customizable: registry.npmmirror.com/cz-customizable/6.3.0
eslint: registry.npmmirror.com/eslint/8.6.0
eslint-config-airbnb-base: registry.npmmirror.com/eslint-config-airbnb-base/15.0.0_b5a36b8c1535387c8dd00eff7ec6b551
@@ -624,7 +624,7 @@ packages:
which: registry.nlark.com/which/2.0.2
dev: true
- registry.nlark.com/cz-conventional-changelog/3.2.0_@types+node@17.0.6:
+ registry.nlark.com/cz-conventional-changelog/3.2.0_@types+node@17.0.8:
resolution: {integrity: sha1-au8fiS1kETND1+RVUpCJrJ8g5Hc=, registry: http://registry.npm.taobao.org/, tarball: https://registry.nlark.com/cz-conventional-changelog/download/cz-conventional-changelog-3.2.0.tgz}
id: registry.nlark.com/cz-conventional-changelog/3.2.0
name: cz-conventional-changelog
@@ -638,14 +638,14 @@ packages:
longest: registry.nlark.com/longest/2.0.1
word-wrap: registry.nlark.com/word-wrap/1.2.3
optionalDependencies:
- '@commitlint/load': registry.npmmirror.com/@commitlint/load/16.0.0_@types+node@17.0.6
+ '@commitlint/load': registry.npmmirror.com/@commitlint/load/16.0.0_@types+node@17.0.8
transitivePeerDependencies:
- '@swc/core'
- '@swc/wasm'
- '@types/node'
dev: true
- registry.nlark.com/cz-conventional-changelog/3.3.0_@types+node@17.0.6:
+ registry.nlark.com/cz-conventional-changelog/3.3.0_@types+node@17.0.8:
resolution: {integrity: sha1-kkaUfJBAQUmz/iz37pGsrTt9ItI=, registry: http://registry.npm.taobao.org/, tarball: https://registry.nlark.com/cz-conventional-changelog/download/cz-conventional-changelog-3.3.0.tgz}
id: registry.nlark.com/cz-conventional-changelog/3.3.0
name: cz-conventional-changelog
@@ -653,13 +653,13 @@ packages:
engines: {node: '>= 10'}
dependencies:
chalk: registry.npmmirror.com/chalk/2.4.2
- commitizen: registry.npmmirror.com/commitizen/4.2.4_@types+node@17.0.6
+ commitizen: registry.npmmirror.com/commitizen/4.2.4_@types+node@17.0.8
conventional-commit-types: registry.nlark.com/conventional-commit-types/3.0.0
lodash.map: registry.nlark.com/lodash.map/4.6.0
longest: registry.nlark.com/longest/2.0.1
word-wrap: registry.nlark.com/word-wrap/1.2.3
optionalDependencies:
- '@commitlint/load': registry.npmmirror.com/@commitlint/load/16.0.0_@types+node@17.0.6
+ '@commitlint/load': registry.npmmirror.com/@commitlint/load/16.0.0_@types+node@17.0.8
transitivePeerDependencies:
- '@swc/core'
- '@swc/wasm'
@@ -3086,7 +3086,7 @@ packages:
to-fast-properties: registry.nlark.com/to-fast-properties/2.0.0
dev: true
- registry.npmmirror.com/@commitlint/cli/16.0.1_@types+node@17.0.6:
+ registry.npmmirror.com/@commitlint/cli/16.0.1_@types+node@17.0.8:
resolution: {integrity: sha512-61gGRy65WiVDRsqP0dAR2fAgE3qrTBW3fgz9MySv32y5Ib3ZXXDDq6bGyQqi2dSaPuDYzNCRwwlC7mmQM73T/g==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@commitlint/cli/download/@commitlint/cli-16.0.1.tgz}
id: registry.npmmirror.com/@commitlint/cli/16.0.1
name: '@commitlint/cli'
@@ -3096,7 +3096,7 @@ packages:
dependencies:
'@commitlint/format': registry.npmmirror.com/@commitlint/format/16.0.0
'@commitlint/lint': registry.npmmirror.com/@commitlint/lint/16.0.0
- '@commitlint/load': registry.npmmirror.com/@commitlint/load/16.0.0_@types+node@17.0.6
+ '@commitlint/load': registry.npmmirror.com/@commitlint/load/16.0.0_@types+node@17.0.8
'@commitlint/read': registry.npmmirror.com/@commitlint/read/16.0.0
'@commitlint/types': registry.npmmirror.com/@commitlint/types/16.0.0
lodash: registry.npmmirror.com/lodash/4.17.21
@@ -3177,7 +3177,7 @@ packages:
'@commitlint/types': registry.npmmirror.com/@commitlint/types/16.0.0
dev: true
- registry.npmmirror.com/@commitlint/load/16.0.0_@types+node@17.0.6:
+ registry.npmmirror.com/@commitlint/load/16.0.0_@types+node@17.0.8:
resolution: {integrity: sha512-7WhrGCkP6K/XfjBBguLkkI2XUdiiIyMGlNsSoSqgRNiD352EiffhFEApMy1/XOU+viwBBm/On0n5p0NC7e9/4A==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@commitlint/load/download/@commitlint/load-16.0.0.tgz}
id: registry.npmmirror.com/@commitlint/load/16.0.0
name: '@commitlint/load'
@@ -3190,7 +3190,7 @@ packages:
'@commitlint/types': registry.npmmirror.com/@commitlint/types/16.0.0
chalk: registry.npmmirror.com/chalk/4.1.2
cosmiconfig: registry.nlark.com/cosmiconfig/7.0.1
- cosmiconfig-typescript-loader: registry.npmmirror.com/cosmiconfig-typescript-loader/1.0.2_646584a8d620b4d6f5eb4525e8655565
+ cosmiconfig-typescript-loader: registry.npmmirror.com/cosmiconfig-typescript-loader/1.0.2_faf01e1d5a40372a98081522dcafc186
lodash: registry.npmmirror.com/lodash/4.17.21
resolve-from: registry.nlark.com/resolve-from/5.0.0
typescript: registry.npmmirror.com/typescript/4.5.4
@@ -3403,7 +3403,7 @@ packages:
dependencies:
'@types/istanbul-lib-coverage': registry.npmmirror.com/@types/istanbul-lib-coverage/2.0.4
'@types/istanbul-reports': registry.npmmirror.com/@types/istanbul-reports/3.0.1
- '@types/node': registry.npmmirror.com/@types/node/17.0.6
+ '@types/node': registry.npmmirror.com/@types/node/17.0.8
'@types/yargs': registry.npmmirror.com/@types/yargs/16.0.4
chalk: registry.npmmirror.com/chalk/4.1.2
@@ -3522,10 +3522,10 @@ packages:
name: '@types/node'
version: 14.14.45
- registry.npmmirror.com/@types/node/17.0.6:
- resolution: {integrity: sha512-+XBAjfZmmivILUzO0HwBJoYkAyyySSLg5KCGBDFLomJo0sV6szvVLAf4ANZZ0pfWzgEds5KmGLG9D5hfEqOhaA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/node/download/@types/node-17.0.6.tgz}
+ registry.npmmirror.com/@types/node/17.0.8:
+ resolution: {integrity: sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/node/download/@types/node-17.0.8.tgz}
name: '@types/node'
- version: 17.0.6
+ version: 17.0.8
registry.npmmirror.com/@types/normalize-package-data/2.4.1:
resolution: {integrity: sha1-0zV0eaD9/dWQf+Z+F+CoXJBuEwE=, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/normalize-package-data/download/@types/normalize-package-data-2.4.1.tgz}
@@ -3550,7 +3550,7 @@ packages:
name: '@types/resolve'
version: 1.17.1
dependencies:
- '@types/node': registry.npmmirror.com/@types/node/17.0.6
+ '@types/node': registry.npmmirror.com/@types/node/17.0.8
dev: true
registry.npmmirror.com/@types/throttle-debounce/2.1.0:
@@ -3976,11 +3976,11 @@ packages:
name: '@vue/shared'
version: 3.2.26
- registry.npmmirror.com/@vueuse/core/7.5.1_vue@3.2.26:
- resolution: {integrity: sha512-GczfdTWqH483zkUHdDYiLm0Tn51OtsQXYc+eBKKIeolh0mgn682KbSYmkrjNytaF7qGc74YxMDAYjkPBW6V2Pg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vueuse/core/download/@vueuse/core-7.5.1.tgz}
- id: registry.npmmirror.com/@vueuse/core/7.5.1
+ registry.npmmirror.com/@vueuse/core/7.5.3_vue@3.2.26:
+ resolution: {integrity: sha512-D9j5ymHFMFRXQqCp0yZJkf/bvBGiz0MrKUa364p+L8dMyd5zyq2K1JmHyvoBd4xbTFRfmQ1h878u6YE5LCkDVQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vueuse/core/download/@vueuse/core-7.5.3.tgz}
+ id: registry.npmmirror.com/@vueuse/core/7.5.3
name: '@vueuse/core'
- version: 7.5.1
+ version: 7.5.3
peerDependencies:
'@vue/composition-api': ^1.1.0
vue: ^2.6.0 || ^3.2.0
@@ -3990,16 +3990,16 @@ packages:
vue:
optional: true
dependencies:
- '@vueuse/shared': registry.npmmirror.com/@vueuse/shared/7.5.1_vue@3.2.26
+ '@vueuse/shared': registry.npmmirror.com/@vueuse/shared/7.5.3_vue@3.2.26
vue: registry.npmmirror.com/vue/3.2.26
vue-demi: registry.npmmirror.com/vue-demi/0.12.1_vue@3.2.26
dev: false
- registry.npmmirror.com/@vueuse/shared/7.5.1_vue@3.2.26:
- resolution: {integrity: sha512-zMQEuYJyTmr5Hj2rYgSbb4H/cSI8mdaa9dUuw20j6rPV+xLV11y7vCyIkxo31uODDr0p77FMlProKzNDiK9rAA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vueuse/shared/download/@vueuse/shared-7.5.1.tgz}
- id: registry.npmmirror.com/@vueuse/shared/7.5.1
+ registry.npmmirror.com/@vueuse/shared/7.5.3_vue@3.2.26:
+ resolution: {integrity: sha512-BJ71cxHN5VByW1S58Gl85NFJaQu93F7Vs7K/MuAKsIIuHm9PBbkR5Vxkg9ko9cBdiKVt+FNoo13BhdbA+Vwycg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@vueuse/shared/download/@vueuse/shared-7.5.3.tgz}
+ id: registry.npmmirror.com/@vueuse/shared/7.5.3
name: '@vueuse/shared'
- version: 7.5.1
+ version: 7.5.3
peerDependencies:
'@vue/composition-api': ^1.1.0
vue: ^2.6.0 || ^3.2.0
@@ -4283,7 +4283,7 @@ packages:
hasBin: true
dependencies:
cachedir: registry.nlark.com/cachedir/2.2.0
- cz-conventional-changelog: registry.nlark.com/cz-conventional-changelog/3.2.0_@types+node@17.0.6
+ cz-conventional-changelog: registry.nlark.com/cz-conventional-changelog/3.2.0_@types+node@17.0.8
dedent: registry.nlark.com/dedent/0.7.0
detect-indent: registry.nlark.com/detect-indent/6.0.0
find-node-modules: registry.nlark.com/find-node-modules/2.1.2
@@ -4298,7 +4298,7 @@ packages:
strip-json-comments: registry.nlark.com/strip-json-comments/3.0.1
dev: true
- registry.npmmirror.com/commitizen/4.2.4_@types+node@17.0.6:
+ registry.npmmirror.com/commitizen/4.2.4_@types+node@17.0.8:
resolution: {integrity: sha512-LlZChbDzg3Ir3O2S7jSo/cgWp5/QwylQVr59K4xayVq8S4/RdKzSyJkghAiZZHfhh5t4pxunUoyeg0ml1q/7aw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/commitizen/download/commitizen-4.2.4.tgz}
id: registry.npmmirror.com/commitizen/4.2.4
name: commitizen
@@ -4307,7 +4307,7 @@ packages:
hasBin: true
dependencies:
cachedir: registry.nlark.com/cachedir/2.2.0
- cz-conventional-changelog: registry.nlark.com/cz-conventional-changelog/3.2.0_@types+node@17.0.6
+ cz-conventional-changelog: registry.nlark.com/cz-conventional-changelog/3.2.0_@types+node@17.0.8
dedent: registry.nlark.com/dedent/0.7.0
detect-indent: registry.nlark.com/detect-indent/6.0.0
find-node-modules: registry.nlark.com/find-node-modules/2.1.2
@@ -4358,7 +4358,7 @@ packages:
through2: registry.nlark.com/through2/4.0.2
dev: true
- registry.npmmirror.com/cosmiconfig-typescript-loader/1.0.2_646584a8d620b4d6f5eb4525e8655565:
+ registry.npmmirror.com/cosmiconfig-typescript-loader/1.0.2_faf01e1d5a40372a98081522dcafc186:
resolution: {integrity: sha512-27ZehvijYqAKVzta5xtZBS3PAliC8CmnWkGXN0vgxAZz7yqxpMjf3aG7flxF5rEiu8FAD7nZZXtOI+xUGn+bVg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/cosmiconfig-typescript-loader/download/cosmiconfig-typescript-loader-1.0.2.tgz}
id: registry.npmmirror.com/cosmiconfig-typescript-loader/1.0.2
name: cosmiconfig-typescript-loader
@@ -4368,9 +4368,9 @@ packages:
'@types/node': '*'
typescript: '>=3'
dependencies:
- '@types/node': registry.npmmirror.com/@types/node/17.0.6
+ '@types/node': registry.npmmirror.com/@types/node/17.0.8
cosmiconfig: registry.nlark.com/cosmiconfig/7.0.1
- ts-node: registry.npmmirror.com/ts-node/10.4.0_646584a8d620b4d6f5eb4525e8655565
+ ts-node: registry.npmmirror.com/ts-node/10.4.0_faf01e1d5a40372a98081522dcafc186
typescript: registry.npmmirror.com/typescript/4.5.4
transitivePeerDependencies:
- '@swc/core'
@@ -6100,7 +6100,7 @@ packages:
version: 0.3.9
dev: false
- registry.npmmirror.com/ts-node/10.4.0_646584a8d620b4d6f5eb4525e8655565:
+ registry.npmmirror.com/ts-node/10.4.0_faf01e1d5a40372a98081522dcafc186:
resolution: {integrity: sha1-aA+IlFiF9ObPRQ5/DWIj3UBIlfc=, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/ts-node/download/ts-node-10.4.0.tgz?cache=0&sync_timestamp=1636438781562&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fts-node%2Fdownload%2Fts-node-10.4.0.tgz}
id: registry.npmmirror.com/ts-node/10.4.0
name: ts-node
@@ -6122,7 +6122,7 @@ packages:
'@tsconfig/node12': registry.nlark.com/@tsconfig/node12/1.0.9
'@tsconfig/node14': registry.nlark.com/@tsconfig/node14/1.0.1
'@tsconfig/node16': registry.nlark.com/@tsconfig/node16/1.0.2
- '@types/node': registry.npmmirror.com/@types/node/17.0.6
+ '@types/node': registry.npmmirror.com/@types/node/17.0.8
acorn: registry.npmmirror.com/acorn/8.7.0
acorn-walk: registry.nlark.com/acorn-walk/8.2.0
arg: registry.npmmirror.com/arg/4.1.3
diff --git a/src/components/index.ts b/src/components/index.ts
index 814c5b36..76e372d7 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -1,4 +1,4 @@
-export * from './common';
-export * from './common';
export * from './custom';
+export * from './svg';
+export * from './common';
export * from './business';
diff --git a/src/components/svg/SvgEmptyData.vue b/src/components/svg/SvgEmptyData.vue
new file mode 100644
index 00000000..9cbf8fb1
--- /dev/null
+++ b/src/components/svg/SvgEmptyData.vue
@@ -0,0 +1,1447 @@
+
+
+
+
+
+
diff --git a/src/components/svg/SvgNetworkError.vue b/src/components/svg/SvgNetworkError.vue
new file mode 100644
index 00000000..b2882c76
--- /dev/null
+++ b/src/components/svg/SvgNetworkError.vue
@@ -0,0 +1,408 @@
+
+
+
+
+
+
diff --git a/src/components/svg/SvgNoPermission.vue b/src/components/svg/SvgNoPermission.vue
new file mode 100644
index 00000000..854b7760
--- /dev/null
+++ b/src/components/svg/SvgNoPermission.vue
@@ -0,0 +1,897 @@
+
+
+
+
+
+
diff --git a/src/components/svg/SvgNotFound.vue b/src/components/svg/SvgNotFound.vue
new file mode 100644
index 00000000..53f921c3
--- /dev/null
+++ b/src/components/svg/SvgNotFound.vue
@@ -0,0 +1,504 @@
+
+
+
+
+
+
diff --git a/src/components/svg/SvgServiceError.vue b/src/components/svg/SvgServiceError.vue
new file mode 100644
index 00000000..f9eee881
--- /dev/null
+++ b/src/components/svg/SvgServiceError.vue
@@ -0,0 +1,1337 @@
+
+
+
+
+
+
diff --git a/src/components/svg/index.ts b/src/components/svg/index.ts
new file mode 100644
index 00000000..38ca609f
--- /dev/null
+++ b/src/components/svg/index.ts
@@ -0,0 +1,7 @@
+import SvgNoPermission from './SvgNoPermission.vue';
+import SvgNotFound from './SvgNotFound.vue';
+import SvgServiceError from './SvgServiceError.vue';
+import SvgEmptyData from './SvgEmptyData.vue';
+import SvgNetworkError from './SvgNetworkError.vue';
+
+export { SvgNoPermission, SvgNotFound, SvgServiceError, SvgEmptyData, SvgNetworkError };
diff --git a/src/composables/common/index.ts b/src/composables/common/index.ts
index 7c20b36c..f2a9ec80 100644
--- a/src/composables/common/index.ts
+++ b/src/composables/common/index.ts
@@ -1,3 +1,2 @@
export * from './system';
export * from './router';
-export * from './route';
diff --git a/src/composables/common/route.ts b/src/composables/common/route.ts
deleted file mode 100644
index 860e9f36..00000000
--- a/src/composables/common/route.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { unref, computed } from 'vue';
-import { useRoute } from 'vue-router';
-import { router } from '@/router';
-import { useRouteStore } from '@/store';
-
-/**
- * 路由查询参数
- * @param inSetup - 是否在vue页面/组件的setup里面调用,在axios里面无法使用useRouter和useRoute
- */
-export function useRouteQuery(inSetup: boolean = true) {
- const { getRouteName } = useRouteStore();
-
- const route = getRouteInstance(inSetup);
-
- /** 登录后跳转的地址 */
- const loginRedirect = computed(() => {
- let url: string | undefined;
- if (route.name === getRouteName('login')) {
- url = (route.query?.redirect as string) || '';
- }
- return url;
- });
-
- return {
- loginRedirect
- };
-}
-
-function getRouteInstance(inSetup: boolean = true) {
- const route = inSetup ? useRoute() : unref(router.currentRoute);
- return route;
-}
diff --git a/src/composables/common/router.ts b/src/composables/common/router.ts
index 09b8d4d3..d1468075 100644
--- a/src/composables/common/router.ts
+++ b/src/composables/common/router.ts
@@ -1,8 +1,6 @@
-import { unref } from 'vue';
-import { useRouter, useRoute } from 'vue-router';
+import { useRouter } from 'vue-router';
import type { RouteLocationRaw } from 'vue-router';
-import { router as globalRouter } from '@/router';
-import { useRouteStore } from '@/store';
+import { router as globalRouter, routeName } from '@/router';
import { LoginModuleKey } from '@/interface';
/**
@@ -10,10 +8,8 @@ import { LoginModuleKey } from '@/interface';
* @param inSetup - 是否在vue页面/组件的setup里面调用,在axios里面无法使用useRouter和useRoute
*/
export function useRouterPush(inSetup: boolean = true) {
- const { getRouteName } = useRouteStore();
-
const router = inSetup ? useRouter() : globalRouter;
- const route = inSetup ? useRoute() : unref(globalRouter.currentRoute);
+ const route = globalRouter.currentRoute;
/**
* 路由跳转
@@ -39,7 +35,7 @@ export function useRouterPush(inSetup: boolean = true) {
* @param newTab - 在新的浏览器标签打开
*/
function toHome(newTab = false) {
- routerPush(getRouteName('root'), newTab);
+ routerPush({ name: routeName('root') }, newTab);
}
/**
@@ -50,10 +46,10 @@ export function useRouterPush(inSetup: boolean = true) {
function toLogin(loginModule?: LoginModuleKey, redirectUrl?: string) {
const module: LoginModuleKey = loginModule || 'pwd-login';
const routeLocation: RouteLocationRaw = {
- name: getRouteName('login'),
+ name: routeName('login'),
params: { module }
};
- const redirect = redirectUrl || route.fullPath;
+ const redirect = redirectUrl || route.value.fullPath;
Object.assign(routeLocation, { query: { redirect } });
routerPush(routeLocation);
}
@@ -63,17 +59,17 @@ export function useRouterPush(inSetup: boolean = true) {
* @param module - 切换后的登录模块
*/
function toLoginModule(module: LoginModuleKey) {
- const { query } = route;
- routerPush({ name: getRouteName('login'), params: { module }, query });
+ const { query } = route.value;
+ routerPush({ name: routeName('login'), params: { module }, query });
}
/**
* 登录成功后跳转重定向的地址
- * @param redirect - 重定向地址
*/
- function toLoginRedirect(redirect?: string) {
- if (redirect) {
- routerPush(redirect);
+ function toLoginRedirect() {
+ const { query } = route.value;
+ if (query?.redirect) {
+ routerPush(query.redirect as string);
} else {
toHome();
}
diff --git a/src/config/common/regexp.ts b/src/config/common/regexp.ts
index 847a5eb9..b94e15c4 100644
--- a/src/config/common/regexp.ts
+++ b/src/config/common/regexp.ts
@@ -14,3 +14,7 @@ export const REGEXP_CODE_SIX = /^\d{6}$/;
/** 4位数字验证码正则 */
export const REGEXP_CODE_FOUR = /^\d{4}$/;
+
+/** url链接正则 */
+export const REGEXP_URL =
+ /(((^https?:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
diff --git a/src/interface/index.ts b/src/interface/index.ts
index bdd505d9..0e406d1c 100644
--- a/src/interface/index.ts
+++ b/src/interface/index.ts
@@ -1 +1,2 @@
export * from './enum';
+export * from './system';
diff --git a/src/interface/system.ts b/src/interface/system.ts
new file mode 100644
index 00000000..5c3fc30f
--- /dev/null
+++ b/src/interface/system.ts
@@ -0,0 +1,7 @@
+import type { MenuOption } from 'naive-ui';
+
+/** 菜单项配置 */
+export type GlobalMenuOption = MenuOption & {
+ routeName: string;
+ routePath: string;
+};
diff --git a/src/layouts/Layout/index.vue b/src/layouts/Layout/index.vue
index 3acf7710..aba3fdc1 100644
--- a/src/layouts/Layout/index.vue
+++ b/src/layouts/Layout/index.vue
@@ -2,11 +2,15 @@
-
+
-
+
diff --git a/src/main.ts b/src/main.ts
index 5a8af4fc..57f109e6 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -6,6 +6,10 @@ import AppProvider from './AppProvider.vue';
import App from './App.vue';
async function setupApp() {
+ // 初始化加载的svg logo
+ setupInitSvgLogo('#loadingLogo');
+
+ // 引入静态资源
setupAssets();
// 挂载 appProvider 解决路由守卫,Axios中可使用,LoadingBar,Dialog,Message 等之类组件
@@ -13,9 +17,6 @@ async function setupApp() {
setupStore(appProvider);
appProvider.mount('#appProvider');
- // 初始化加载的svg logo
- setupInitSvgLogo('#loadingLogo');
-
const app = createApp(App);
setupStore(app);
diff --git a/src/router/guard/permission.ts b/src/router/guard/permission.ts
index 4eea1734..ce48e3d7 100644
--- a/src/router/guard/permission.ts
+++ b/src/router/guard/permission.ts
@@ -1,4 +1,5 @@
import type { Router, RouteLocationNormalized, NavigationGuardNext } from 'vue-router';
+import { routeName } from '@/router';
import { useAuthStore, useRouteStore } from '@/store';
import { exeStrategyActions } from '@/utils';
@@ -11,7 +12,7 @@ export async function handlePagePermission(
) {
const auth = useAuthStore();
const route = useRouteStore();
- const { initDynamicRoute, getRouteName } = useRouteStore();
+ const { initDynamicRoute } = useRouteStore();
const permissions = to.meta.permissions || [];
const needLogin = Boolean(to.meta?.requiresAuth) || Boolean(permissions.length);
@@ -21,19 +22,25 @@ export async function handlePagePermission(
// 添加动态路由
await initDynamicRoute(router);
- if (to.name === getRouteName('redirect-not-found')) {
- // 动态路由没有加载导致重定向到了redirect-not-found,等待动态路由加载好了,回到重定向之前的路由
+ if (to.name === routeName('not-found-page')) {
+ // 动态路由没有加载导致被not-found-page路由捕获,等待动态路由加载好了,回到之前的路由
next({ path: to.fullPath, replace: true, query: to.query });
return;
}
}
+ // 动态路由已经加载,仍然未找到,重定向到not-found
+ if (to.name === routeName('not-found-page')) {
+ next({ name: routeName('not-found'), replace: true });
+ return;
+ }
+
const actions: Common.StrategyAction[] = [
// 已登录状态跳转登录页,跳转至首页
[
- auth.isLogin && to.name === getRouteName('login'),
+ auth.isLogin && to.name === routeName('login'),
() => {
- next({ name: getRouteName('root') });
+ next({ name: routeName('root') });
}
],
// 不需要登录权限的页面直接通行
@@ -48,7 +55,7 @@ export async function handlePagePermission(
!auth.isLogin && needLogin,
() => {
const redirect = to.fullPath;
- next({ name: getRouteName('login'), query: { redirect } });
+ next({ name: routeName('login'), query: { redirect } });
}
],
// 登录状态进入需要登录权限的页面,有权限直接通行
@@ -62,7 +69,7 @@ export async function handlePagePermission(
// 登录状态进入需要登录权限的页面,无权限,重定向到无权限页面
auth.isLogin && needLogin && !hasPermission,
() => {
- next({ name: getRouteName('no-permission') });
+ next({ name: routeName('no-permission') });
}
]
];
diff --git a/src/router/index.ts b/src/router/index.ts
index 492a18b9..d33f55af 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,6 +1,6 @@
import type { App } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
-import { routes, constantRoutes } from './routes';
+import { routes } from './routes';
import { createRouterGuide } from './guard';
export const router = createRouter({
@@ -15,4 +15,4 @@ export async function setupRouter(app: App) {
await router.isReady();
}
-export { constantRoutes };
+export * from './routes';
diff --git a/src/router/routes/constant.ts b/src/router/routes/constant.ts
index d9ff75c3..c49933f9 100644
--- a/src/router/routes/constant.ts
+++ b/src/router/routes/constant.ts
@@ -13,8 +13,8 @@ const constantRoutes: AuthRoute.Route[] = [
},
{
name: 'login',
- path: `/login/:module(${getLoginModuleRegExp()})?`,
- component: 'blank',
+ path: '/login',
+ component: 'self',
props: route => {
const moduleType = (route.params.module as LoginModuleKey) || 'pwd-login';
return {
@@ -23,45 +23,45 @@ const constantRoutes: AuthRoute.Route[] = [
},
meta: {
title: '登录',
- single: true,
- singleOriginPath: '/login'
+ dynamicPath: `/login/:module(${getLoginModuleRegExp()})?`,
+ singleLayout: 'blank'
}
},
{
name: 'no-permission',
path: '/no-permission',
- component: 'blank',
+ component: 'self',
meta: {
title: '无权限',
- single: true
+ singleLayout: 'blank'
}
},
{
name: 'not-found',
path: '/not-found',
- component: 'blank',
+ component: 'self',
meta: {
title: '未找到',
- single: true
+ singleLayout: 'blank'
}
},
{
name: 'service-error',
path: '/service-error',
- component: 'blank',
+ component: 'self',
meta: {
title: '服务器错误',
- single: true
+ singleLayout: 'blank'
}
},
// 匹配无效的路径重定向not-found的页面
{
- name: 'redirect-not-found',
+ name: 'not-found-page',
path: '/:pathMatch(.*)*',
component: 'blank',
meta: {
title: '未找到',
- single: true
+ singleLayout: 'blank'
}
}
];
diff --git a/src/router/routes/index.ts b/src/router/routes/index.ts
index 3f133e4a..9327b3b2 100644
--- a/src/router/routes/index.ts
+++ b/src/router/routes/index.ts
@@ -5,4 +5,17 @@ import constantRoutes from './constant';
/** 所有路由 */
export const routes: RouteRecordRaw[] = constantRoutes.map(item => transformAuthRouteToVueRoute(item));
+/** 路由名称 */
+export const routeName = (key: AuthRoute.RouteKey) => key;
+
+/** 路由路径 */
+export function routePath(key: Exclude): AuthRoute.RoutePath {
+ const rootPath: AuthRoute.RoutePath = '/';
+ if (key === 'root') return rootPath;
+ const splitMark: AuthRoute.RouteSplitMark = '_';
+ const pathSplitMark = '/';
+ const path = key.split(splitMark).join(pathSplitMark);
+ return (pathSplitMark + path) as AuthRoute.RoutePath;
+}
+
export { constantRoutes };
diff --git a/src/service/api/auth.ts b/src/service/api/auth.ts
index 2e6e6f04..a7d7db9b 100644
--- a/src/service/api/auth.ts
+++ b/src/service/api/auth.ts
@@ -1,5 +1,4 @@
import { mockRequest } from '../request';
-import { userRoutesMiddleware } from '../middleware';
/**
* 获取验证码
@@ -33,7 +32,6 @@ export function fetchUserInfo() {
* @param userId - 用户id
* @description 后端根据用户id查询到对应的角色类型,并将路由筛选出对应角色的路由数据返回前端
*/
-export async function fetchUserRoutes(userId: string = 'soybean') {
- const { data } = await mockRequest.post('/getUserRoutes', { userId });
- return userRoutesMiddleware(data);
+export function fetchUserRoutes(userId: string = 'soybean') {
+ return mockRequest.post('/getUserRoutes', { userId });
}
diff --git a/src/service/middleware/auth.ts b/src/service/middleware/auth.ts
deleted file mode 100644
index 78302dd7..00000000
--- a/src/service/middleware/auth.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import type { RouteRecordRaw } from 'vue-router';
-import { transformAuthRouteToVueRoute } from '@/utils';
-
-export function userRoutesMiddleware(data: ApiRoute.Route | null) {
- if (!data) return [];
-
- const routes: RouteRecordRaw[] = data.routes.map(item => transformAuthRouteToVueRoute(item));
-
- return routes;
-}
diff --git a/src/service/middleware/index.ts b/src/service/middleware/index.ts
index 269586ee..cb0ff5c3 100644
--- a/src/service/middleware/index.ts
+++ b/src/service/middleware/index.ts
@@ -1 +1 @@
-export * from './auth';
+export {};
diff --git a/src/store/modules/auth/index.ts b/src/store/modules/auth/index.ts
index 31e86bea..99632bb8 100644
--- a/src/store/modules/auth/index.ts
+++ b/src/store/modules/auth/index.ts
@@ -2,7 +2,7 @@ import { ref, computed, reactive, unref } from 'vue';
import type { Ref, ComputedRef } from 'vue';
import { defineStore } from 'pinia';
import { router as globalRouter } from '@/router';
-import { useRouterPush, useRouteQuery } from '@/composables';
+import { useRouterPush } from '@/composables';
import { useLoading } from '@/hooks';
import { fetchLogin, fetchUserInfo } from '@/service';
import { getUserInfo, getToken, setUserInfo, setToken, setRefreshToken, clearAuthStorage } from '@/utils';
@@ -32,7 +32,6 @@ interface AuthStore {
export const useAuthStore = defineStore('auth-store', () => {
const { toLogin, toLoginRedirect } = useRouterPush(false);
- const { loginRedirect } = useRouteQuery(false);
const { loading: loginLoding, startLoading: startLoginLoading, endLoading: endLoginLoading } = useLoading();
const userInfo: Auth.UserInfo = reactive(getUserInfo());
@@ -82,7 +81,7 @@ export const useAuthStore = defineStore('auth-store', () => {
handleSetToken(token);
handleSetUserInfo(data);
// 3. 跳转登录后的地址
- toLoginRedirect(loginRedirect.value);
+ toLoginRedirect();
// 4.登录成功弹出欢迎提示
window.$notification?.success({
title: '登录成功!',
diff --git a/src/store/modules/route/index.ts b/src/store/modules/route/index.ts
index f7e2277d..a3caaab6 100644
--- a/src/store/modules/route/index.ts
+++ b/src/store/modules/route/index.ts
@@ -2,10 +2,10 @@ import { ref } from 'vue';
import type { Ref } from 'vue';
import type { Router } from 'vue-router';
import { defineStore } from 'pinia';
-import { constantRoutes } from '@/router';
import { useBoolean } from '@/hooks';
import { fetchUserRoutes } from '@/service';
-import { findAuthRouteByKey } from '@/utils';
+import { transformAuthRouteToMenu, transformAuthRouteToVueRoute } from '@/utils';
+import type { GlobalMenuOption } from '@/interface';
/** 路由状态 */
interface RouteStore {
@@ -17,18 +17,6 @@ interface RouteStore {
isAddedDynamicRoute: Ref;
/** 初始化动态路由 */
initDynamicRoute(router: Router): Promise;
- /**
- * 获取路由名称
- * @description getRouteName 和 getRoutePath 优先使用 getRouteName
- */
- getRouteName(key: AuthRoute.RouteKey): AuthRoute.RouteKey;
- /**
- * 获取路由路径
- * @description getRouteName 和 getRoutePath 优先使用 getRouteName
- */
- getRoutePath(key: AuthRoute.RouteKey): AuthRoute.RoutePath | undefined;
- /** 获取路由路径 */
- getRouteTitle(key: AuthRoute.RouteKey): string | undefined;
}
export const useRouteStore = defineStore('route-store', () => {
@@ -37,37 +25,32 @@ export const useRouteStore = defineStore('route-store', () => {
routes.value = data;
}
- const { bool: isAddedDynamicRoute, setTrue: setAddedDynamicRoute } = useBoolean();
- async function initDynamicRoute(router: Router) {
- const routes = await fetchUserRoutes();
- routes.forEach(route => {
- router.addRoute(route);
- });
- setAddedDynamicRoute();
+ const menus = ref([]) as Ref;
+ function getMenus(data: AuthRoute.Route[]) {
+ const transform = transformAuthRouteToMenu(data);
+ menus.value = transform;
}
- function getRouteName(key: AuthRoute.RouteKey) {
- return key;
- }
- function getRoutePath(key: AuthRoute.RouteKey) {
- const allRoutes = [...constantRoutes, ...routes.value];
- const item = findAuthRouteByKey(key, allRoutes);
- return item?.path;
- }
- function getRouteTitle(key: AuthRoute.RouteKey) {
- const allRoutes = [...constantRoutes, ...routes.value];
- const item = findAuthRouteByKey(key, allRoutes);
- return item?.meta?.title;
+ const { bool: isAddedDynamicRoute, setTrue: setAddedDynamicRoute } = useBoolean();
+ async function initDynamicRoute(router: Router) {
+ const { data } = await fetchUserRoutes();
+ if (data) {
+ getMenus(data.routes);
+
+ const vueRoutes = data.routes.map(route => transformAuthRouteToVueRoute(route));
+ vueRoutes.forEach(route => {
+ router.addRoute(route);
+ });
+
+ setAddedDynamicRoute();
+ }
}
const routeStore: RouteStore = {
routes,
setRoutes,
isAddedDynamicRoute,
- initDynamicRoute,
- getRouteName,
- getRoutePath,
- getRouteTitle
+ initDynamicRoute
};
return routeStore;
diff --git a/src/typings/common/route.d.ts b/src/typings/common/route.d.ts
index 40b0d2d0..6d69bb87 100644
--- a/src/typings/common/route.d.ts
+++ b/src/typings/common/route.d.ts
@@ -1,14 +1,17 @@
/** 权限路由相关类型 */
declare namespace AuthRoute {
+ /** 多级路由分割符号 */
+ type RouteSplitMark = '_';
+
/** 路由的key */
type RouteKey =
// 固定的路由
- | 'root'
+ | 'root' // 根路由
| 'login'
| 'not-found'
| 'no-permission'
| 'service-error'
- | 'redirect-not-found' // 重定向not-found
+ | 'not-found-page' // 捕获无效path的路由
// 自定义路由
| 'dashboard'
| 'dashboard_analysis'
@@ -18,18 +21,13 @@ declare namespace AuthRoute {
| 'multi-menu_first_second'
| 'about';
- /** 路由路径 */
- type RoutePath =
+ /** 路由的path */
+ type RoutePath =
| '/'
- | Exclude, '/root' | '/redirect'>
+ | Exclude, '/root' | 'not-found-page'>
| SingleRouteParentPath
- | Key
- | '/:path(.*)*'
| '/:pathMatch(.*)*';
- /** 多级路由分割符号 */
- type RouteSplitMark = '_';
-
/**
* 路由的组件
* - layout - 基础布局,具有公共部分的布局
@@ -43,6 +41,10 @@ declare namespace AuthRoute {
type RouteMeta = {
/** 路由标题(可用来作document.title或者菜单的名称) */
title: string;
+ /** 路由的动态路径 */
+ dynamicPath?: PathToDynamicPath<'/login'>;
+ /** 作为单独路由的父级路由布局组件 */
+ singleLayout?: Extract;
/** 需要登录权限 */
requiresAuth?: boolean;
/** 哪些类型的用户有权限才能访问的路由 */
@@ -55,23 +57,16 @@ declare namespace AuthRoute {
icon?: string;
/** 是否在菜单中隐藏 */
hide?: boolean;
- /** 是否作为单独的路由(作为菜单时只有自身,没有子菜单) */
- single?: boolean;
- /** 作为单独的路由且path为动态path的原始path */
- singleOriginPath?: SingleRoutePath;
/** 路由顺序,可用于菜单的排序 */
order?: number;
};
- /** 登录路由路径 */
- type LoginPath = `/login/:module(${string})?`;
-
/** 单个路由的类型结构(后端返回此类型结构的路由) */
- interface Route {
+ interface Route {
/** 路由名称(路由唯一标识) */
name: RouteKey;
/** 路由路径 */
- path: RoutePath;
+ path: RoutePath;
/** 路由重定向 */
redirect?: RoutePath;
/**
@@ -86,30 +81,41 @@ declare namespace AuthRoute {
children?: Route[];
/** 路由描述 */
meta: RouteMeta;
- /** 属性 */
+ /** 路由属性 */
props?: boolean | Record | ((to: any) => Record);
}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- type GetMultiRouteParentKey = Key extends `${infer Left}_${infer Right}` ? Left : never;
-
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- type GetSingleRouteKey = Key extends `${infer Left}_${infer Right}` ? never : Key;
-
- /** 单独一级路由的key (单独路由需要添加一个父路由用于应用布局组件) */
+ /** 单独一级路由的key (单独路由需要添加一个父级路由用于应用布局组件) */
type SingleRouteKey = Exclude<
GetSingleRouteKey,
- GetMultiRouteParentKey | 'root' | 'redirect-not-found'
+ GetMultiRouteParentKey | 'root' | 'not-found-page'
>;
-
- /** 单独路由需要添加一个父路由用于应用布局组件 */
+ /** 单独路由父级路由key */
type SingleRouteParentKey = `${SingleRouteKey}-parent`;
+ /** 单独路由path */
+ type SingleRoutePath = KeyToPath;
+
+ /** 单独路由父级路由path */
+ type SingleRouteParentPath = KeyToPath;
+
/** 路由key转换路由path */
- type KeyToPath = Key extends `${infer Left}_${infer Right}`
+ type KeyToPath = Key extends `${infer Left}_${infer Right}`
? KeyToPath<`${Left}/${Right}`>
: `/${Key}`;
- type SingleRoutePath = KeyToPath;
- type SingleRouteParentPath = KeyToPath;
+ /** 路由path转换动态路径 */
+ type PathToDynamicPath =
+ | `${Path}/:module`
+ | `${Path}/:module(${string})`
+ | `${Path}/:module(${string})?`;
+
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ type GetSingleRouteKey = Key extends `${infer Left}${RouteSplitMark}${infer Right}`
+ ? never
+ : Key;
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ type GetMultiRouteParentKey = Key extends `${infer Left}${RouteSplitMark}${infer Right}`
+ ? Left
+ : never;
}
diff --git a/src/utils/common/icon.ts b/src/utils/common/icon.ts
new file mode 100644
index 00000000..416a7087
--- /dev/null
+++ b/src/utils/common/icon.ts
@@ -0,0 +1,19 @@
+import { h } from 'vue';
+import { Icon } from '@iconify/vue';
+
+/**
+ * 动态渲染iconify
+ * @param icon - 图标名称
+ * @param color - 图标颜色
+ * @param size - 图标大小
+ */
+export function iconifyRender(icon: string, color?: string, size?: number) {
+ const style: { color?: string; size?: string } = {};
+ if (color) {
+ style.color = color;
+ }
+ if (size) {
+ style.size = `${size}px`;
+ }
+ return () => h(Icon, { icon, style });
+}
diff --git a/src/utils/common/index.ts b/src/utils/common/index.ts
index 9cd5747f..f2a056ae 100644
--- a/src/utils/common/index.ts
+++ b/src/utils/common/index.ts
@@ -2,4 +2,5 @@ export * from './typeof';
export * from './console';
export * from './color';
export * from './number';
+export * from './icon';
export * from './design-pattern';
diff --git a/src/utils/router/component.ts b/src/utils/router/component.ts
index 3d4b15e4..36ff4933 100644
--- a/src/utils/router/component.ts
+++ b/src/utils/router/component.ts
@@ -30,7 +30,7 @@ export function getViewComponent(routeKey: AuthRoute.RouteKey) {
'dashboard_workbench',
'about',
'multi-menu_first_second',
- 'redirect-not-found'
+ 'not-found-page'
];
const key = keys.includes(routeKey as ViewComponentKey) ? (routeKey as ViewComponentKey) : 'not-found';
@@ -44,7 +44,7 @@ export function getViewComponent(routeKey: AuthRoute.RouteKey) {
dashboard_workbench: DashboardWorkbench,
about: About,
'multi-menu_first_second': MultiMenuFirstSecond,
- 'redirect-not-found': NotFound
+ 'not-found-page': NotFound
};
return () => setViewComponentName(viewComponent[key], key) as Promise;
diff --git a/src/utils/router/helpers.ts b/src/utils/router/helpers.ts
index f4083ec1..c9746981 100644
--- a/src/utils/router/helpers.ts
+++ b/src/utils/router/helpers.ts
@@ -9,15 +9,12 @@ type ComponentAction = {
/** 将权限路由类型转换成vue路由类型 */
export function transformAuthRouteToVueRoute(item: AuthRoute.Route) {
- const { name, path, meta } = item;
- const itemRoute: Partial = {
- name,
- path,
- meta
- };
- if (hasRedirect(item)) {
- itemRoute.redirect = item.redirect;
+ const itemRoute = { ...item } as RouteRecordRaw;
+
+ if (hasDynamicPath(item)) {
+ Object.assign(itemRoute, { path: item.meta.dynamicPath });
}
+
if (hasComponent(item)) {
const action: ComponentAction = {
layout() {
@@ -29,7 +26,14 @@ export function transformAuthRouteToVueRoute(item: AuthRoute.Route) {
itemRoute.meta.blankLayout = true;
}
},
- multi() {},
+ multi() {
+ // 多级路由一定有子路由
+ if (hasChildren(item)) {
+ Object.assign(itemRoute, { component: Layout });
+ } else {
+ consoleError('多级路由缺少子路由: ', item);
+ }
+ },
self() {
itemRoute.component = getViewComponent(item.name);
}
@@ -41,116 +45,59 @@ export function transformAuthRouteToVueRoute(item: AuthRoute.Route) {
}
}
- if (hasProps(item)) {
- (itemRoute as any).props = item.props;
- }
-
+ // 注意:单独路由没有children
if (isSingleRoute(item)) {
- if (item.name === 'redirect-not-found') {
+ if (hasChildren(item)) {
+ consoleError('单独路由不应该有子路由: ', item);
+ }
+
+ // 捕获无效路由的需特殊处理
+ if (item.name === 'not-found-page') {
itemRoute.children = [
{
path: '',
name: item.name,
- component: getViewComponent('redirect-not-found')
+ component: getViewComponent('not-found-page')
}
];
- return itemRoute as RouteRecordRaw;
+ } else {
+ const parentPath = `${itemRoute.path}-parent` as AuthRoute.SingleRouteParentPath;
+
+ if (item.meta.singleLayout === 'blank') {
+ itemRoute.meta!.blankLayout = true;
+ }
+
+ const parentRoute: RouteRecordRaw = {
+ path: parentPath,
+ component: Layout,
+ redirect: item.path,
+ children: [itemRoute]
+ };
+
+ return parentRoute;
}
- const singleRoute = {
- ...itemRoute
- };
- Object.assign(singleRoute, { component: getViewComponent(item.name) });
-
- const singlePath = (
- hasSingleOriginPath(item) ? item.meta.singleOriginPath : item.path
- ) as AuthRoute.SingleRoutePath;
- const parenPath = `${singlePath}-parent` as AuthRoute.SingleRouteParentPath;
-
- const parentRoute: Partial = {
- path: parenPath,
- component: itemRoute.component,
- redirect: singlePath,
- children: [singleRoute as RouteRecordRaw]
- };
- return parentRoute as RouteRecordRaw;
}
+
if (hasChildren(item)) {
- itemRoute.children = item.children!.map(child => transformAuthRouteToVueRoute(child)) as RouteRecordRaw[];
+ itemRoute.redirect = item.children![0].path;
+ itemRoute.children = item.children!.map(child => transformAuthRouteToVueRoute(child));
}
- return itemRoute as RouteRecordRaw;
+ return itemRoute;
}
function hasComponent(item: AuthRoute.Route) {
return Boolean(item.component);
}
-function hasRedirect(item: AuthRoute.Route) {
- return Boolean(item.redirect);
-}
-
function hasChildren(item: AuthRoute.Route) {
return Boolean(item.children && item.children.length);
}
-function hasProps(item: AuthRoute.Route) {
- return Boolean(item.props);
+function hasDynamicPath(item: AuthRoute.Route) {
+ return Boolean(item.meta.dynamicPath);
}
function isSingleRoute(item: AuthRoute.Route) {
- return Boolean(item.meta.single);
-}
-
-function hasSingleOriginPath(item: AuthRoute.Route) {
- return Boolean(item.meta.singleOriginPath);
-}
-
-/**
- * 根据路由key获取AuthRoute数据
- * @param key - 路由key
- * @param routes - 路由
- */
-export function findAuthRouteByKey(key: AuthRoute.RouteKey, routes: AuthRoute.Route[]) {
- const paths = getRouteKeyPathsByKey(key);
- const route = recursiveFindRouteByPaths(paths, routes);
-
- return route;
-}
-
-/**
- * 根据路由key的paths获递归取路由
- * @param paths - 路由key的路径
- * @param routes - 路由
- */
-function recursiveFindRouteByPaths(
- paths: AuthRoute.RouteKey[],
- routes: AuthRoute.Route[]
-): AuthRoute.Route | undefined {
- const item = routes.find(route => paths.length && route.name === paths[0]);
-
- if (item && hasComponent(item)) {
- return recursiveFindRouteByPaths(paths.slice(1), item.children!);
- }
- return item;
-}
-
-/**
- * 根据路由key获取从第一级路由到当前路由key的paths
- * @param key - 路由key
- */
-function getRouteKeyPathsByKey(key: AuthRoute.RouteKey) {
- const splitMark: AuthRoute.RouteSplitMark = '_';
- const keys = key.split(splitMark);
- const keyPaths: AuthRoute.RouteKey[] = [];
-
- keys.forEach((itemKey, index) => {
- if (index === 0) {
- keyPaths.push(itemKey as AuthRoute.RouteKey);
- } else {
- const concatKey = keyPaths[index - 1] + splitMark + itemKey;
- keyPaths.push(concatKey as AuthRoute.RouteKey);
- }
- });
-
- return keyPaths;
+ return Boolean(item.meta.singleLayout);
}
diff --git a/src/utils/router/index.ts b/src/utils/router/index.ts
index a363a5bc..e659803c 100644
--- a/src/utils/router/index.ts
+++ b/src/utils/router/index.ts
@@ -1,2 +1,3 @@
export * from './helpers';
+export * from './menu';
export * from './regexp';
diff --git a/src/utils/router/menu.ts b/src/utils/router/menu.ts
new file mode 100644
index 00000000..07c208ae
--- /dev/null
+++ b/src/utils/router/menu.ts
@@ -0,0 +1,51 @@
+import type { GlobalMenuOption } from '@/interface';
+import { iconifyRender } from '../common';
+
+/** 路由不转换菜单 */
+function hideInMenu(route: AuthRoute.Route) {
+ return Boolean(route.meta.hide);
+}
+
+/** 给菜单添加可选属性 */
+function addPartialProps(menuItem: GlobalMenuOption, icon?: string, children?: GlobalMenuOption[]) {
+ const item = { ...menuItem };
+ if (icon) {
+ Object.assign(item, { icon: iconifyRender(icon) });
+ }
+ if (children) {
+ Object.assign(item, { children });
+ }
+ return item;
+}
+
+/**
+ * 将权限路由转换成菜单
+ * @param routes - 路由
+ */
+export function transformAuthRouteToMenu(routes: AuthRoute.Route[]) {
+ const globalMenu: GlobalMenuOption[] = [];
+ routes.forEach(route => {
+ const { name, path, meta } = route;
+ const routeName = name as string;
+ let menuChildren: GlobalMenuOption[] | undefined;
+ if (route.children) {
+ menuChildren = transformAuthRouteToMenu(route.children);
+ }
+ const menuItem: GlobalMenuOption = addPartialProps(
+ {
+ key: routeName,
+ label: meta.title,
+ routeName,
+ routePath: path
+ },
+ meta?.icon,
+ menuChildren
+ );
+
+ if (!hideInMenu(route)) {
+ globalMenu.push(menuItem);
+ }
+ });
+
+ return globalMenu;
+}
diff --git a/src/views/system/exception/403.vue b/src/views/system/exception/403.vue
deleted file mode 100644
index d77f1a3a..00000000
--- a/src/views/system/exception/403.vue
+++ /dev/null
@@ -1,6 +0,0 @@
-
- 403
-
-
-
-
diff --git a/src/views/system/exception/404.vue b/src/views/system/exception/404.vue
deleted file mode 100644
index 37e5be1e..00000000
--- a/src/views/system/exception/404.vue
+++ /dev/null
@@ -1,6 +0,0 @@
-
- NotFound
-
-
-
-
diff --git a/src/views/system/exception/500.vue b/src/views/system/exception/500.vue
deleted file mode 100644
index d8396bbf..00000000
--- a/src/views/system/exception/500.vue
+++ /dev/null
@@ -1,6 +0,0 @@
-
- 500
-
-
-
-
diff --git a/src/views/system/exception/components/ExceptionBase.vue b/src/views/system/exception/components/ExceptionBase.vue
new file mode 100644
index 00000000..c56e418a
--- /dev/null
+++ b/src/views/system/exception/components/ExceptionBase.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
diff --git a/src/views/system/exception/components/index.ts b/src/views/system/exception/components/index.ts
new file mode 100644
index 00000000..d8b1dedf
--- /dev/null
+++ b/src/views/system/exception/components/index.ts
@@ -0,0 +1,3 @@
+import ExceptionBase from './ExceptionBase.vue';
+
+export { ExceptionBase };
diff --git a/src/views/system/exception/no-permission/index.vue b/src/views/system/exception/no-permission/index.vue
new file mode 100644
index 00000000..04e5a44d
--- /dev/null
+++ b/src/views/system/exception/no-permission/index.vue
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/src/views/system/exception/not-found/index.vue b/src/views/system/exception/not-found/index.vue
new file mode 100644
index 00000000..65110f04
--- /dev/null
+++ b/src/views/system/exception/not-found/index.vue
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/src/views/system/exception/service-error/index.vue b/src/views/system/exception/service-error/index.vue
new file mode 100644
index 00000000..ad6eb1b3
--- /dev/null
+++ b/src/views/system/exception/service-error/index.vue
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/src/views/system/index.ts b/src/views/system/index.ts
index a86e490d..32aa8ffb 100644
--- a/src/views/system/index.ts
+++ b/src/views/system/index.ts
@@ -1,6 +1,6 @@
const Login = () => import('./login/index.vue');
-const NoPermission = () => import('./exception/403.vue');
-const NotFound = () => import('./exception/404.vue');
-const ServiceError = () => import('./exception/500.vue');
+const NoPermission = () => import('./exception/no-permission/index.vue');
+const NotFound = () => import('./exception/not-found/index.vue');
+const ServiceError = () => import('./exception/service-error/index.vue');
export { Login, NoPermission, NotFound, ServiceError };
diff --git a/src/views/system/login/components/CodeLogin/index.vue b/src/views/system/login/components/CodeLogin/index.vue
index 1176ec4b..92e3e284 100644
--- a/src/views/system/login/components/CodeLogin/index.vue
+++ b/src/views/system/login/components/CodeLogin/index.vue
@@ -18,7 +18,7 @@
-
+
-
+
确定
返回