From 807448aec5b041535fe4fbac90eca1138b2f439c Mon Sep 17 00:00:00 2001 From: Soybean Date: Sat, 23 Apr 2022 02:21:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(projects):=20=E6=9D=83=E9=99=90=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E5=8F=8A=E6=9D=83=E9=99=90=E7=A4=BA=E4=BE=8B=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 4 +- build/plugins/index.ts | 2 +- components.d.ts | 15 ---- mock/api/auth.ts | 2 +- mock/api/route.ts | 39 ++++++++++- package.json | 6 +- pnpm-lock.yaml | 89 ++++++++++++------------ src/directives/index.ts | 2 + src/directives/permission.ts | 20 ++++-- src/router/guard/dynamic.ts | 2 +- src/router/index.ts | 7 +- src/router/modules/about.ts | 2 +- src/router/modules/auth-demo.ts | 35 ++++++++++ src/router/modules/exception.ts | 2 +- src/router/routes/index.ts | 15 +--- src/store/modules/auth/index.ts | 3 + src/store/modules/route/index.ts | 21 +++--- src/store/modules/tab/index.ts | 17 +++-- src/typings/env.d.ts | 5 +- src/typings/route.d.ts | 3 + src/typings/system.d.ts | 8 +++ src/utils/auth/user.ts | 2 +- src/utils/router/auth.ts | 17 ++--- src/utils/router/helpers.ts | 28 ++++++++ src/utils/router/index.ts | 1 + src/views/auth-demo/permission/index.vue | 46 ++++++++++++ src/views/auth-demo/super/index.vue | 8 +++ 27 files changed, 287 insertions(+), 114 deletions(-) create mode 100644 src/router/modules/auth-demo.ts create mode 100644 src/views/auth-demo/permission/index.vue create mode 100644 src/views/auth-demo/super/index.vue diff --git a/.env b/.env index 8b79cb391..34e263ca8 100644 --- a/.env +++ b/.env @@ -7,6 +7,8 @@ VITE_APP_TITLE=Soybean管理系统 VITE_APP_DESC=SoybeanAdmin是一个中后台管理系统模版 # 权限路由模式: static | dynamic -VITE_AUTH_ROUTE_MODE=dynamic +VITE_AUTH_ROUTE_MODE=static VITE_VISUALIZER=false + +VITE_ROUTE_HOME_PATH=/dashboard/analysis diff --git a/build/plugins/index.ts b/build/plugins/index.ts index d9bda12e6..0a8bf6dde 100644 --- a/build/plugins/index.ts +++ b/build/plugins/index.ts @@ -8,7 +8,7 @@ import mock from './mock'; import visualizer from './visualizer'; /** - * vite插件 + * vite插件 * @param configEnv - 环境 * @param srcPath - src路径 * @param viteEnv - 环境变量配置 diff --git a/components.d.ts b/components.d.ts index 01abe2188..47f42fc2c 100644 --- a/components.d.ts +++ b/components.d.ts @@ -13,14 +13,9 @@ declare module 'vue' { IconAntDesignCloseOutlined: typeof import('~icons/ant-design/close-outlined')['default'] IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default'] IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default'] - IconCustomActivity: typeof import('~icons/custom/activity')['default'] IconCustomAvatar: typeof import('~icons/custom/avatar')['default'] - IconCustomBanner: typeof import('~icons/custom/banner')['default'] - IconCustomCast: typeof import('~icons/custom/cast')['default'] - IconCustomEmptyData: typeof import('~icons/custom/empty-data')['default'] IconCustomLogo: typeof import('~icons/custom/logo')['default'] IconCustomLogoFill: typeof import('~icons/custom/logo-fill')['default'] - IconCustomNetworkError: typeof import('~icons/custom/network-error')['default'] IconCustomNoPermission: typeof import('~icons/custom/no-permission')['default'] IconCustomNotFound: typeof import('~icons/custom/not-found')['default'] IconCustomServiceError: typeof import('~icons/custom/service-error')['default'] @@ -55,8 +50,6 @@ declare module 'vue' { NColorPicker: typeof import('naive-ui')['NColorPicker'] NConfigProvider: typeof import('naive-ui')['NConfigProvider'] NDataTable: typeof import('naive-ui')['NDataTable'] - NDescriptions: typeof import('naive-ui')['NDescriptions'] - NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem'] NDialogProvider: typeof import('naive-ui')['NDialogProvider'] NDivider: typeof import('naive-ui')['NDivider'] NDrawer: typeof import('naive-ui')['NDrawer'] @@ -69,26 +62,18 @@ declare module 'vue' { NGrid: typeof import('naive-ui')['NGrid'] NGridItem: typeof import('naive-ui')['NGridItem'] NInput: typeof import('naive-ui')['NInput'] - NInputGroup: typeof import('naive-ui')['NInputGroup'] NInputNumber: typeof import('naive-ui')['NInputNumber'] - NList: typeof import('naive-ui')['NList'] - NListItem: typeof import('naive-ui')['NListItem'] NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider'] NMenu: typeof import('naive-ui')['NMenu'] NMessageProvider: typeof import('naive-ui')['NMessageProvider'] NModal: typeof import('naive-ui')['NModal'] NNotificationProvider: typeof import('naive-ui')['NNotificationProvider'] - NPopover: typeof import('naive-ui')['NPopover'] NScrollbar: typeof import('naive-ui')['NScrollbar'] NSelect: typeof import('naive-ui')['NSelect'] NSpace: typeof import('naive-ui')['NSpace'] - NSpin: typeof import('naive-ui')['NSpin'] - NStatistic: typeof import('naive-ui')['NStatistic'] NSwitch: typeof import('naive-ui')['NSwitch'] NTabPane: typeof import('naive-ui')['NTabPane'] NTabs: typeof import('naive-ui')['NTabs'] - NTag: typeof import('naive-ui')['NTag'] - NThing: typeof import('naive-ui')['NThing'] NTimeline: typeof import('naive-ui')['NTimeline'] NTimelineItem: typeof import('naive-ui')['NTimelineItem'] NTooltip: typeof import('naive-ui')['NTooltip'] diff --git a/mock/api/auth.ts b/mock/api/auth.ts index 7bec62bcb..941c78e6c 100644 --- a/mock/api/auth.ts +++ b/mock/api/auth.ts @@ -62,7 +62,7 @@ const apis: MockMethod[] = [ { url: '/mock/testToken', method: 'post', - response: (option: any): Service.MockServiceResult => { + response: (option: Service.MockOption): Service.MockServiceResult => { if (option.headers?.authorization !== token.token) { return { code: 66666, diff --git a/mock/api/route.ts b/mock/api/route.ts index cf61fc6a2..dbafc7bfc 100644 --- a/mock/api/route.ts +++ b/mock/api/route.ts @@ -231,6 +231,39 @@ const routes: AuthRoute.Route[] = [ order: 4 } }, + { + name: 'auth-demo', + path: '/auth-demo', + component: 'basic', + children: [ + { + name: 'auth-demo_permission', + path: '/auth-demo/permission', + component: 'self', + meta: { + title: '指令和权限切换', + requiresAuth: true, + icon: 'ic:round-construction' + } + }, + { + name: 'auth-demo_super', + path: '/auth-demo/super', + component: 'self', + meta: { + title: '超级管理员可见', + requiresAuth: true, + permissions: ['super'], + icon: 'ic:round-supervisor-account' + } + } + ], + meta: { + title: '权限示例', + icon: 'ic:baseline-security', + order: 5 + } + }, { name: 'exception', path: '/exception', @@ -270,7 +303,7 @@ const routes: AuthRoute.Route[] = [ meta: { title: '异常页', icon: 'ant-design:exception-outlined', - order: 5 + order: 6 } }, { @@ -324,7 +357,7 @@ const routes: AuthRoute.Route[] = [ meta: { title: '多级菜单', icon: 'carbon:menu', - order: 6 + order: 7 } }, { @@ -337,7 +370,7 @@ const routes: AuthRoute.Route[] = [ singleLayout: 'basic', permissions: ['super', 'admin', 'test'], icon: 'fluent:book-information-24-regular', - order: 7 + order: 8 } } ]; diff --git a/package.json b/package.json index 1ac17be92..7567cd0e2 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ } }, "dependencies": { - "@antv/g2plot": "^2.4.15", + "@antv/g2plot": "^2.4.16", "@better-scroll/core": "^2.4.2", "@vueuse/core": "^8.3.1", "axios": "^0.26.1", @@ -54,7 +54,7 @@ "@amap/amap-jsapi-types": "^0.0.8", "@commitlint/cli": "^16.2.3", "@commitlint/config-conventional": "^16.2.1", - "@iconify/json": "^2.1.30", + "@iconify/json": "^2.1.31", "@iconify/vue": "^3.2.1", "@types/bmapgl": "^0.0.5", "@types/crypto-js": "^4.1.1", @@ -77,7 +77,7 @@ "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-prettier": "^4.0.0", - "eslint-plugin-vue": "^8.6.0", + "eslint-plugin-vue": "^8.7.1", "husky": "^7.0.4", "lint-staged": "^12.4.0", "mockjs": "^1.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec02e656d..b755658da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2,11 +2,11 @@ lockfileVersion: 5.3 specifiers: '@amap/amap-jsapi-types': ^0.0.8 - '@antv/g2plot': ^2.4.15 + '@antv/g2plot': ^2.4.16 '@better-scroll/core': ^2.4.2 '@commitlint/cli': ^16.2.3 '@commitlint/config-conventional': ^16.2.1 - '@iconify/json': ^2.1.30 + '@iconify/json': ^2.1.31 '@iconify/vue': ^3.2.1 '@types/bmapgl': ^0.0.5 '@types/crypto-js': ^4.1.1 @@ -35,7 +35,7 @@ specifiers: eslint-config-prettier: ^8.5.0 eslint-plugin-import: ^2.26.0 eslint-plugin-prettier: ^4.0.0 - eslint-plugin-vue: ^8.6.0 + eslint-plugin-vue: ^8.7.1 form-data: ^4.0.0 husky: ^7.0.4 lint-staged: ^12.4.0 @@ -72,7 +72,7 @@ specifiers: xgplayer: ^2.31.6 dependencies: - '@antv/g2plot': 2.4.15 + '@antv/g2plot': 2.4.16 '@better-scroll/core': registry.nlark.com/@better-scroll/core/2.4.2 '@vueuse/core': 8.3.1_vue@3.2.33 axios: 0.26.1 @@ -100,7 +100,7 @@ devDependencies: '@amap/amap-jsapi-types': 0.0.8 '@commitlint/cli': 16.2.3 '@commitlint/config-conventional': 16.2.1 - '@iconify/json': 2.1.30 + '@iconify/json': 2.1.31 '@iconify/vue': 3.2.1_vue@3.2.33 '@types/bmapgl': 0.0.5 '@types/crypto-js': 4.1.1 @@ -112,7 +112,7 @@ devDependencies: '@vitejs/plugin-vue': 2.3.1_vite@2.9.5+vue@3.2.33 '@vitejs/plugin-vue-jsx': 1.3.10 '@vue/eslint-config-prettier': 7.0.0_eslint@8.13.0+prettier@2.6.2 - '@vue/eslint-config-typescript': 10.0.0_a62cbc2f4797496d74696b1f6538012a + '@vue/eslint-config-typescript': 10.0.0_72c1a3548548335704ca8b66d8a11662 '@vue/tsconfig': 0.1.3_@types+node@17.0.25 commitizen: 4.2.4 cross-env: registry.nlark.com/cross-env/7.0.3 @@ -123,7 +123,7 @@ devDependencies: eslint-config-prettier: 8.5.0_eslint@8.13.0 eslint-plugin-import: 2.26.0_eslint@8.13.0 eslint-plugin-prettier: 4.0.0_1815ac95b7fb26c13c7d48a8eef62d0f - eslint-plugin-vue: 8.6.0_eslint@8.13.0 + eslint-plugin-vue: 8.7.1_eslint@8.13.0 husky: 7.0.4 lint-staged: 12.4.0 mockjs: 1.1.0 @@ -307,8 +307,8 @@ packages: tslib: 2.3.1 dev: false - /@antv/g2plot/2.4.15: - resolution: {integrity: sha512-VDE1w8iOCuxgWmowGIHRoDNjQ/WDD31wx/wjMaGcaV1xHFOccG+nVycPaAV21Pjb3jwzDlqAWqvwHpT1lOBKFw==} + /@antv/g2plot/2.4.16: + resolution: {integrity: sha512-uuO08uyN4WvHHt0f3o6qVuIrUiARhIwebFh/JvMTnWEqoH3MbVo3e6vvcLhBrqE2J+WZeQyqrtyAxl92C95wqw==} dependencies: '@antv/event-emitter': 0.1.2 '@antv/g2': 4.1.37 @@ -887,8 +887,8 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true - /@iconify/json/2.1.30: - resolution: {integrity: sha512-mxaB9gr4sSAKUq6GJdXV42YC7tkJ9WOZW6/8zGXciiA1cjLLyHe73GJl6qpnc53G8SDSSXWClQC5RHWNz/HIBQ==} + /@iconify/json/2.1.31: + resolution: {integrity: sha512-peG8fjKAOe8w9xMrYo0kkDdF9AcDPxz89P516TTsC3EK2MVTaRuvuXEgMG3trqfU0Hur/n6ZZ/LXGE+/qS4aFg==} dependencies: '@iconify/types': 1.1.0 pathe: 0.2.0 @@ -1520,7 +1520,7 @@ packages: prettier: 2.6.2 dev: true - /@vue/eslint-config-typescript/10.0.0_a62cbc2f4797496d74696b1f6538012a: + /@vue/eslint-config-typescript/10.0.0_72c1a3548548335704ca8b66d8a11662: resolution: {integrity: sha512-F94cL8ug3FaYXlCfU5/wiGjk1qeadmoBpRGAOBq+qre3Smdupa59dd6ZJrsfRODpsMPyTG7330juMDsUvpZ3Rw==, registry: http://registry.npm.taobao.org/, tarball: http://registry.npm.taobao.org/@vue/eslint-config-typescript/download/@vue/eslint-config-typescript-10.0.0.tgz} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -1530,7 +1530,7 @@ packages: '@typescript-eslint/eslint-plugin': 5.20.0_b9ac9b5656ce5dffade639fcf5e491bf '@typescript-eslint/parser': 5.20.0_eslint@8.13.0+typescript@4.6.3 eslint: 8.13.0 - eslint-plugin-vue: 8.6.0_eslint@8.13.0 + eslint-plugin-vue: 8.7.1_eslint@8.13.0 vue-eslint-parser: 8.0.1_eslint@8.13.0 transitivePeerDependencies: - supports-color @@ -1704,7 +1704,7 @@ packages: dev: false /amdefine/1.0.1: - resolution: {integrity: sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/amdefine/-/amdefine-1.0.1.tgz} + resolution: {integrity: sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==} engines: {node: '>=0.4.2'} dev: false @@ -1849,7 +1849,7 @@ packages: dev: true /boolbase/1.0.0: - resolution: {integrity: sha1-aN/1++YMUes3cl6p4+0xDcwed24=} + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} dev: true /brace-expansion/1.1.11: @@ -2283,6 +2283,12 @@ packages: engines: {node: '>= 6'} dev: true + /cssesc/3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + /csstype/2.6.19: resolution: {integrity: sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ==} dev: false @@ -2421,7 +2427,7 @@ packages: dev: true /decamelize/1.2.0: - resolution: {integrity: sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=} + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} /deep-equal/1.1.1: @@ -2963,8 +2969,8 @@ packages: prettier-linter-helpers: registry.nlark.com/prettier-linter-helpers/1.0.0 dev: true - /eslint-plugin-vue/8.6.0_eslint@8.13.0: - resolution: {integrity: sha512-abXiF2J18n/7ZPy9foSlJyouKf54IqpKlNvNmzhM93N0zs3QUxZG/oBd3tVPOJTKg7SlhBUtPxugpqzNbgGpQQ==} + /eslint-plugin-vue/8.7.1_eslint@8.13.0: + resolution: {integrity: sha512-28sbtm4l4cOzoO1LtzQPxfxhQABararUb1JtqusQqObJpWX2e/gmVyeYVfepizPFne0Q5cILkYGiBoV36L12Wg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 @@ -2972,6 +2978,8 @@ packages: eslint: 8.13.0 eslint-utils: 3.0.0_eslint@8.13.0 natural-compare: 1.4.0 + nth-check: 2.0.1 + postcss-selector-parser: 6.0.10 semver: 7.3.5 vue-eslint-parser: 8.0.1_eslint@8.13.0 transitivePeerDependencies: @@ -3297,7 +3305,7 @@ packages: dev: false /fs.realpath/1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/fs.realpath/-/fs.realpath-1.0.0.tgz} + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} /fsevents/2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} @@ -3585,13 +3593,13 @@ packages: dev: true /inflight/1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/inflight/-/inflight-1.0.6.tgz} + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} dependencies: once: 1.4.0 wrappy: 1.0.2 /inherits/2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==, registry: http://registry.npm.taobao.org/, tarball: http://registry.npm.taobao.org/inherits/-/inherits-2.0.4.tgz} + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} /ini/1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} @@ -3647,7 +3655,6 @@ packages: /is-callable/1.2.4: resolution: {integrity: sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==} engines: {node: '>= 0.4'} - dev: false /is-ci/2.0.0: resolution: {integrity: sha1-a8YzQYGBDgS1wis9WJ/cpVAmQEw=, registry: http://registry.npm.taobao.org/, tarball: http://registry.npm.taobao.org/is-ci/download/is-ci-2.0.0.tgz} @@ -3666,7 +3673,6 @@ packages: engines: {node: '>= 0.4'} dependencies: has-tostringtag: 1.0.0 - dev: false /is-docker/2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} @@ -4130,13 +4136,8 @@ packages: kind-of: 6.0.3 dev: true - /minimist/1.2.5: - resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==} - dev: false - /minimist/1.2.6: resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==} - dev: true /mkdirp/0.5.5: resolution: {integrity: sha1-2Rzv1i0UNsoPQWIOJRKI1CAJne8=, registry: http://registry.npm.taobao.org/, tarball: http://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.5.tgz} @@ -4307,7 +4308,7 @@ packages: dev: true /once/1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/once/-/once-1.4.0.tgz} + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 @@ -4455,7 +4456,7 @@ packages: dev: true /path-is-absolute/1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz} + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} /path-key/2.0.1: @@ -4520,6 +4521,14 @@ packages: vue-demi: 0.12.1_vue@3.2.33 dev: false + /postcss-selector-parser/6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + /postcss/8.4.12: resolution: {integrity: sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==} engines: {node: ^10 || ^12 || >=14} @@ -4778,7 +4787,7 @@ packages: hasBin: true dependencies: chalk: 1.1.3 - minimist: 1.2.5 + minimist: 1.2.6 source-map-support: 0.3.3 dev: false @@ -5204,7 +5213,7 @@ packages: has: 1.0.3 inherits: 2.0.4 is-regex: 1.1.4 - minimist: 1.2.5 + minimist: 1.2.6 object-inspect: 1.11.1 resolve: 1.20.0 resumer: 0.0.0 @@ -5573,7 +5582,7 @@ packages: dev: true /util-deprecate/1.0.2: - resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true /v8-compile-cache/2.3.0: @@ -5851,7 +5860,7 @@ packages: dev: true /wrappy/1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/wrappy/-/wrappy-1.0.2.tgz} + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} /xgplayer-subtitles/1.0.22: resolution: {integrity: sha512-+m1fo+FFynL+2ymO4UQBUSnJAz3WuIthpJFCrKBjd+ukcLRoy3vTcsylXEOo5AZN1xT28fpHmxvZQKEIEYA+6w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/xgplayer-subtitles/-/xgplayer-subtitles-1.0.22.tgz} @@ -6150,8 +6159,8 @@ packages: version: 1.2.1 engines: {node: '>= 0.4'} dependencies: - is-callable: registry.nlark.com/is-callable/1.2.4 - is-date-object: registry.nlark.com/is-date-object/1.0.5 + is-callable: 1.2.4 + is-date-object: 1.0.5 is-symbol: registry.nlark.com/is-symbol/1.0.4 registry.nlark.com/escape-html/1.0.3: @@ -6394,14 +6403,6 @@ packages: version: 1.2.4 engines: {node: '>= 0.4'} - registry.nlark.com/is-date-object/1.0.5: - resolution: {integrity: sha1-CEHVU25yTCVZe/bqYuG9OCmN8x8=, registry: http://registry.npm.taobao.org/, tarball: https://registry.nlark.com/is-date-object/download/is-date-object-1.0.5.tgz} - name: is-date-object - version: 1.0.5 - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - registry.nlark.com/is-docker/2.2.1: resolution: {integrity: sha1-M+6r4jz+hvFL3kQIoCwM+4U6zao=, registry: http://registry.npm.taobao.org/, tarball: https://registry.nlark.com/is-docker/download/is-docker-2.2.1.tgz} name: is-docker diff --git a/src/directives/index.ts b/src/directives/index.ts index 732fa9e41..7b7e7499c 100644 --- a/src/directives/index.ts +++ b/src/directives/index.ts @@ -1,9 +1,11 @@ import type { App } from 'vue'; import setupNetworkDirective from './network'; import setupLoginDirective from './login'; +import setupPermissionDirective from './permission'; /** setup custom vue directives. - [安装自定义的vue指令] */ export function setupDirectives(app: App) { setupNetworkDirective(app); setupLoginDirective(app); + setupPermissionDirective(app); } diff --git a/src/directives/permission.ts b/src/directives/permission.ts index bd0b383cf..7b90806c2 100644 --- a/src/directives/permission.ts +++ b/src/directives/permission.ts @@ -1,16 +1,28 @@ import type { App, Directive } from 'vue'; import { useAuthStore } from '@/store'; +import { isArray, isString } from '@/utils'; -export default function setupLoginDirective(app: App) { +export default function setupPermissionDirective(app: App) { const auth = useAuthStore(); - const loginDirective: Directive = { + const permissionDirective: Directive = { mounted(el: HTMLElement, binding) { - if (binding.value !== auth.userInfo.userRole) { + const { userRole } = auth.userInfo; + const elPermission = binding.value; + let hasPermission = userRole === 'super'; + if (!hasPermission) { + if (isArray(elPermission)) { + hasPermission = (elPermission as Auth.RoleType[]).includes(userRole); + } + if (isString(elPermission)) { + hasPermission = (elPermission as Auth.RoleType) === userRole; + } + } + if (!hasPermission) { el.remove(); } } }; - app.directive('login', loginDirective); + app.directive('permission', permissionDirective); } diff --git a/src/router/guard/dynamic.ts b/src/router/guard/dynamic.ts index 40c6de4e7..8e44983cf 100644 --- a/src/router/guard/dynamic.ts +++ b/src/router/guard/dynamic.ts @@ -16,7 +16,7 @@ export async function createDynamicRouteGuard( const isLogin = Boolean(getToken()); // 初始化权限路由 - if (!route.isInitedAuthRoute) { + if (!route.isInitAuthRoute) { // 未登录情况下直接回到登录页,登录成功后再加载权限路由 if (!isLogin) { if (to.name === routeName('login')) { diff --git a/src/router/index.ts b/src/router/index.ts index 196f91996..0efbb1371 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,6 +1,6 @@ import type { App } from 'vue'; import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'; -import { transformAuthRoutesToVueRoutes } from '@/utils'; +import { transformAuthRoutesToVueRoutes, transformRouteNameToRoutePath } from '@/utils'; import { constantRoutes } from './routes'; import { scrollBehavior } from './helpers'; import { createRouterGuard } from './guard'; @@ -20,5 +20,10 @@ export async function setupRouter(app: App) { await router.isReady(); } +/** 路由名称 */ +export const routeName = (key: AuthRoute.RouteKey) => key; +/** 路由路径 */ +export const routePath = (key: Exclude) => transformRouteNameToRoutePath(key); + export * from './routes'; export * from './modules'; diff --git a/src/router/modules/about.ts b/src/router/modules/about.ts index 9f5608611..47fea40b0 100644 --- a/src/router/modules/about.ts +++ b/src/router/modules/about.ts @@ -8,7 +8,7 @@ const about: AuthRoute.Route = { singleLayout: 'basic', permissions: ['super', 'admin', 'test'], icon: 'fluent:book-information-24-regular', - order: 7 + order: 8 } }; diff --git a/src/router/modules/auth-demo.ts b/src/router/modules/auth-demo.ts new file mode 100644 index 000000000..07640bca6 --- /dev/null +++ b/src/router/modules/auth-demo.ts @@ -0,0 +1,35 @@ +const authDemo: AuthRoute.Route = { + name: 'auth-demo', + path: '/auth-demo', + component: 'basic', + children: [ + { + name: 'auth-demo_permission', + path: '/auth-demo/permission', + component: 'self', + meta: { + title: '指令和权限切换', + requiresAuth: true, + icon: 'ic:round-construction' + } + }, + { + name: 'auth-demo_super', + path: '/auth-demo/super', + component: 'self', + meta: { + title: '超级管理员可见', + requiresAuth: true, + permissions: ['super'], + icon: 'ic:round-supervisor-account' + } + } + ], + meta: { + title: '权限示例', + icon: 'ic:baseline-security', + order: 5 + } +}; + +export default authDemo; diff --git a/src/router/modules/exception.ts b/src/router/modules/exception.ts index 3d57898b2..f7cd56225 100644 --- a/src/router/modules/exception.ts +++ b/src/router/modules/exception.ts @@ -37,7 +37,7 @@ const exception: AuthRoute.Route = { meta: { title: '异常页', icon: 'ant-design:exception-outlined', - order: 5 + order: 6 } }; diff --git a/src/router/routes/index.ts b/src/router/routes/index.ts index f0cd6bcca..00a0adc9c 100644 --- a/src/router/routes/index.ts +++ b/src/router/routes/index.ts @@ -5,7 +5,7 @@ export const constantRoutes: AuthRoute.Route[] = [ { name: 'root', path: '/', - redirect: '/dashboard/analysis', + redirect: import.meta.env.VITE_ROUTE_HOME_PATH, meta: { title: 'Root' } @@ -64,16 +64,3 @@ export const constantRoutes: AuthRoute.Route[] = [ } } ]; - -/** 路由名称 */ -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; -} diff --git a/src/store/modules/auth/index.ts b/src/store/modules/auth/index.ts index aee952d1e..af09fd198 100644 --- a/src/store/modules/auth/index.ts +++ b/src/store/modules/auth/index.ts @@ -87,6 +87,9 @@ export const useAuthStore = defineStore('auth-store', { await this.loginByToken(data); } this.loginLoading = false; + }, + updateUserRole(userRole: Auth.RoleType) { + this.userInfo.userRole = userRole; } } }); diff --git a/src/store/modules/route/index.ts b/src/store/modules/route/index.ts index bc2e7b8d5..0184efa97 100644 --- a/src/store/modules/route/index.ts +++ b/src/store/modules/route/index.ts @@ -7,8 +7,11 @@ import { transformAuthRouteToMenu, transformAuthRoutesToVueRoutes, transformAuthRoutesToSearchMenus, - getCacheRoutes + getCacheRoutes, + filterAuthRoutesByUserPermission, + transformRoutePathToRouteName } from '@/utils'; +import { useAuthStore } from '../auth'; import { useTabStore } from '../tab'; interface RouteState { @@ -19,7 +22,7 @@ interface RouteState { */ authRouteMode: ImportMetaEnv['VITE_AUTH_ROUTE_MODE']; /** 是否初始化了权限路由 */ - isInitedAuthRoute: boolean; + isInitAuthRoute: boolean; /** 路由首页name(前端静态路由时生效,后端动态路由该值会被后端返回的值覆盖) */ routeHomeName: AuthRoute.RouteKey; /** 菜单 */ @@ -33,8 +36,8 @@ interface RouteState { export const useRouteStore = defineStore('route-store', { state: (): RouteState => ({ authRouteMode: import.meta.env.VITE_AUTH_ROUTE_MODE, - isInitedAuthRoute: false, - routeHomeName: 'dashboard_analysis', + isInitAuthRoute: false, + routeHomeName: transformRoutePathToRouteName(import.meta.env.VITE_ROUTE_HOME_PATH), menus: [], searchMenus: [], cacheRoutes: [] @@ -73,9 +76,9 @@ export const useRouteStore = defineStore('route-store', { * @param router - 路由实例 */ async initStaticRoute(router: Router) { - // 先根据用户权限过滤一下staticRoutes - - this.handleAuthRoutes(staticRoutes, router); + const auth = useAuthStore(); + const routes = filterAuthRoutesByUserPermission(staticRoutes, auth.userInfo.userRole); + this.handleAuthRoutes(routes, router); }, /** * 初始化权限路由 @@ -84,6 +87,7 @@ export const useRouteStore = defineStore('route-store', { async initAuthRoute(router: Router) { const { initHomeTab } = useTabStore(); const { userId } = getUserInfo(); + if (!userId) return; const isDynamicRoute = this.authRouteMode === 'dynamic'; @@ -94,7 +98,8 @@ export const useRouteStore = defineStore('route-store', { } initHomeTab(this.routeHomeName, router); - this.isInitedAuthRoute = true; + + this.isInitAuthRoute = true; } } }); diff --git a/src/store/modules/tab/index.ts b/src/store/modules/tab/index.ts index 6872ca9b5..cdd58343c 100644 --- a/src/store/modules/tab/index.ts +++ b/src/store/modules/tab/index.ts @@ -21,7 +21,7 @@ export const useTabStore = defineStore('tab-store', { name: 'root', path: '/', meta: { - title: 'root' + title: 'Root' }, scrollPosition: { left: 0, @@ -53,7 +53,8 @@ export const useTabStore = defineStore('tab-store', { initHomeTab(routeHomeName: string, router: Router) { const routes = router.getRoutes(); const findHome = routes.find(item => item.name === routeHomeName); - if (findHome) { + if (findHome && !findHome.children) { + // 有子路由的不能作为Tab this.homeTab = getTabRouteByVueRoute(findHome); } }, @@ -165,16 +166,20 @@ export const useTabStore = defineStore('tab-store', { iniTabStore(currentRoute: RouteLocationNormalizedLoaded) { const theme = useThemeStore(); - const isHome = currentRoute.path === this.homeTab.path; const tabs: GlobalTabRoute[] = theme.tab.isCache ? getTabRoutes() : []; + const hasHome = isInTabRoutes(tabs, this.homeTab.path); - const hasCurrent = isInTabRoutes(tabs, currentRoute.path); - if (!hasHome) { + if (!hasHome && this.homeTab.name !== 'root') { tabs.unshift(this.homeTab); } + + const isHome = currentRoute.path === this.homeTab.path; + const hasCurrent = isInTabRoutes(tabs, currentRoute.path); if (!isHome && !hasCurrent) { - tabs.push(getTabRouteByVueRoute(currentRoute)); + const currentTab = getTabRouteByVueRoute(currentRoute); + tabs.push(currentTab); } + this.tabs = tabs; this.setActiveTab(currentRoute.path); } diff --git a/src/typings/env.d.ts b/src/typings/env.d.ts index 55ad248a8..0a088f101 100644 --- a/src/typings/env.d.ts +++ b/src/typings/env.d.ts @@ -27,8 +27,11 @@ interface ImportMetaEnv { /** * 权限路由模式: * - static - 前端声明的静态 - * - dynamic - 后端返回的动态 */ + * - dynamic - 后端返回的动态 + */ readonly VITE_AUTH_ROUTE_MODE: 'static' | 'dynamic'; + /** 路由首页的路径 */ + readonly VITE_ROUTE_HOME_PATH: Exclude; /** vite环境类型 */ readonly VITE_ENV_TYPE?: EnvType; /** 开启请求代理 */ diff --git a/src/typings/route.d.ts b/src/typings/route.d.ts index a1753a6cc..09e00643f 100644 --- a/src/typings/route.d.ts +++ b/src/typings/route.d.ts @@ -36,6 +36,9 @@ declare namespace AuthRoute { | 'plugin_icon' | 'plugin_print' | 'plugin_swiper' + | 'auth-demo' + | 'auth-demo_permission' + | 'auth-demo_super' | 'exception' | 'exception_403' | 'exception_404' diff --git a/src/typings/system.d.ts b/src/typings/system.d.ts index 64d658374..0d8c5e483 100644 --- a/src/typings/system.d.ts +++ b/src/typings/system.d.ts @@ -84,6 +84,14 @@ declare namespace Service { /** 接口消息 */ message: string; } + + /** mock的响应option */ + interface MockOption { + url: Record; + body: Record; + query: Record; + headers: Record; + } } /** 主题相关类型 */ diff --git a/src/utils/auth/user.ts b/src/utils/auth/user.ts index b6e96c692..632f859c4 100644 --- a/src/utils/auth/user.ts +++ b/src/utils/auth/user.ts @@ -37,7 +37,7 @@ export function getUserInfo() { userId: '', userName: '', userPhone: '', - userRole: 'test' + userRole: 'normal' }; const userInfo: Auth.UserInfo = getLocal(EnumStorageKey['user-info']) || emptyInfo; return userInfo; diff --git a/src/utils/router/auth.ts b/src/utils/router/auth.ts index 7c0bade71..52943addd 100644 --- a/src/utils/router/auth.ts +++ b/src/utils/router/auth.ts @@ -1,16 +1,10 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ /** * 根据用户权限过滤路由 * @param routes - 权限路由 * @param permission - 权限 */ export function filterAuthRoutesByUserPermission(routes: AuthRoute.Route[], permission: Auth.RoleType) { - const filters: AuthRoute.Route[] = []; - - routes.forEach(route => { - filterAuthRouteByUserPermission(route, permission); - }); - return filters; + return routes.map(route => filterAuthRouteByUserPermission(route, permission)).flat(1); } /** @@ -19,5 +13,12 @@ export function filterAuthRoutesByUserPermission(routes: AuthRoute.Route[], perm * @param permission - 权限 */ function filterAuthRouteByUserPermission(route: AuthRoute.Route, permission: Auth.RoleType): AuthRoute.Route[] { - return []; + const hasPermission = + !route.meta.permissions || permission === 'super' || route.meta.permissions.includes(permission); + + if (route.children) { + const filterChildren = route.children.map(item => filterAuthRouteByUserPermission(item, permission)).flat(1); + Object.assign(route, { children: filterChildren }); + } + return hasPermission ? [route] : []; } diff --git a/src/utils/router/helpers.ts b/src/utils/router/helpers.ts index e9fed067f..4ed0aaa79 100644 --- a/src/utils/router/helpers.ts +++ b/src/utils/router/helpers.ts @@ -31,6 +31,34 @@ export function transformAuthRoutesToSearchMenus(routes: AuthRoute.Route[], tree }, treeMap); } +/** 将路由名字转换成路由路径 */ +export function transformRouteNameToRoutePath( + name: Exclude +): AuthRoute.RoutePath { + const rootPath: AuthRoute.RoutePath = '/'; + if (name === 'root') return rootPath; + + const splitMark: AuthRoute.RouteSplitMark = '_'; + const pathSplitMark = '/'; + const path = name.split(splitMark).join(pathSplitMark); + + return (pathSplitMark + path) as AuthRoute.RoutePath; +} + +/** 将路由路径转换成路由名字 */ +export function transformRoutePathToRouteName( + path: Exclude +): AuthRoute.RouteKey { + if (path === '/') return 'root'; + + const pathSplitMark = '/'; + const routeSplitMark: AuthRoute.RouteSplitMark = '_'; + + const name = path.split(pathSplitMark).slice(1).join(routeSplitMark) as AuthRoute.RouteKey; + + return name; +} + /** * 将单个权限路由转换成vue路由 * @param item - 单个权限路由 diff --git a/src/utils/router/index.ts b/src/utils/router/index.ts index d20270c2b..72b575bf2 100644 --- a/src/utils/router/index.ts +++ b/src/utils/router/index.ts @@ -1,6 +1,7 @@ export * from './module'; export * from './helpers'; export * from './cache'; +export * from './auth'; export * from './menu'; export * from './breadcrumb'; export * from './tab'; diff --git a/src/views/auth-demo/permission/index.vue b/src/views/auth-demo/permission/index.vue new file mode 100644 index 000000000..ccc169cda --- /dev/null +++ b/src/views/auth-demo/permission/index.vue @@ -0,0 +1,46 @@ + + + + diff --git a/src/views/auth-demo/super/index.vue b/src/views/auth-demo/super/index.vue new file mode 100644 index 000000000..8b8da6cc2 --- /dev/null +++ b/src/views/auth-demo/super/index.vue @@ -0,0 +1,8 @@ + + + +