feat(projects): 权限完善及权限示例页面

This commit is contained in:
Soybean 2022-04-23 02:21:02 +08:00
parent b9c5c34979
commit 807448aec5
27 changed files with 287 additions and 114 deletions

4
.env
View File

@ -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

View File

@ -8,7 +8,7 @@ import mock from './mock';
import visualizer from './visualizer';
/**
* vite插件
* vite插件
* @param configEnv -
* @param srcPath - src路径
* @param viteEnv -

15
components.d.ts vendored
View File

@ -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']

View File

@ -62,7 +62,7 @@ const apis: MockMethod[] = [
{
url: '/mock/testToken',
method: 'post',
response: (option: any): Service.MockServiceResult<true | null> => {
response: (option: Service.MockOption): Service.MockServiceResult<true | null> => {
if (option.headers?.authorization !== token.token) {
return {
code: 66666,

View File

@ -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
}
}
];

View File

@ -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",

View File

@ -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

View File

@ -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);
}

View File

@ -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<HTMLElement, Auth.RoleType | undefined> = {
const permissionDirective: Directive<HTMLElement, Auth.RoleType | Auth.RoleType[]> = {
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);
}

View File

@ -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')) {

View File

@ -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<AuthRoute.RouteKey, 'not-found-page'>) => transformRouteNameToRoutePath(key);
export * from './routes';
export * from './modules';

View File

@ -8,7 +8,7 @@ const about: AuthRoute.Route = {
singleLayout: 'basic',
permissions: ['super', 'admin', 'test'],
icon: 'fluent:book-information-24-regular',
order: 7
order: 8
}
};

View File

@ -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;

View File

@ -37,7 +37,7 @@ const exception: AuthRoute.Route = {
meta: {
title: '异常页',
icon: 'ant-design:exception-outlined',
order: 5
order: 6
}
};

View File

@ -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.RouteKey, 'not-found-page'>): 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;
}

View File

@ -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;
}
}
});

View File

@ -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;
}
}
});

View File

@ -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);
}

View File

@ -27,8 +27,11 @@ interface ImportMetaEnv {
/**
* :
* - static -
* - dynamic - */
* - dynamic -
*/
readonly VITE_AUTH_ROUTE_MODE: 'static' | 'dynamic';
/** 路由首页的路径 */
readonly VITE_ROUTE_HOME_PATH: Exclude<AuthRoute.RoutePath, '/not-found-page' | '/:pathMatch(.*)*'>;
/** vite环境类型 */
readonly VITE_ENV_TYPE?: EnvType;
/** 开启请求代理 */

View File

@ -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'

View File

@ -84,6 +84,14 @@ declare namespace Service {
/** 接口消息 */
message: string;
}
/** mock的响应option */
interface MockOption {
url: Record<string, any>;
body: Record<string, any>;
query: Record<string, any>;
headers: Record<string, any>;
}
}
/** 主题相关类型 */

View File

@ -37,7 +37,7 @@ export function getUserInfo() {
userId: '',
userName: '',
userPhone: '',
userRole: 'test'
userRole: 'normal'
};
const userInfo: Auth.UserInfo = getLocal<Auth.UserInfo>(EnumStorageKey['user-info']) || emptyInfo;
return userInfo;

View File

@ -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] : [];
}

View File

@ -31,6 +31,34 @@ export function transformAuthRoutesToSearchMenus(routes: AuthRoute.Route[], tree
}, treeMap);
}
/** 将路由名字转换成路由路径 */
export function transformRouteNameToRoutePath(
name: Exclude<AuthRoute.RouteKey, 'not-found-page'>
): 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.RoutePath, '/not-found-page' | '/:pathMatch(.*)*'>
): 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 -

View File

@ -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';

View File

@ -0,0 +1,46 @@
<template>
<div class="h-full">
<n-card title="权限指令 v-permission" class="h-full shadow-sm rounded-16px">
<div class="pb-12px">
<n-gradient-text type="primary" :size="20">当前用户的权限{{ auth.userInfo.userRole }}</n-gradient-text>
</div>
<n-space>
<n-button v-permission="`super`">super可见</n-button>
<n-button v-permission="`admin`">admin可见</n-button>
<n-button v-permission="['admin', 'test']">admin和test可见</n-button>
</n-space>
<div class="py-12px">
<n-gradient-text type="primary" :size="20">切换用户权限</n-gradient-text>
</div>
<n-select
:value="auth.userInfo.userRole"
class="w-120px"
size="small"
:options="roleList"
@update:value="auth.updateUserRole"
/>
</n-card>
</div>
</template>
<script setup lang="ts">
import { watch } from 'vue';
import { useAppStore, useAuthStore } from '@/store';
const app = useAppStore();
const auth = useAuthStore();
const roleList = [
{ label: '超级管理员', value: 'super' },
{ label: '管理员', value: 'admin' },
{ label: '测试', value: 'test' }
];
watch(
() => auth.userInfo.userRole,
async () => {
app.reloadPage();
}
);
</script>
<style scoped></style>

View File

@ -0,0 +1,8 @@
<template>
<div class="h-full">
<n-card title="当前页面只有super才能看到" class="h-full shadow-sm rounded-16px"> </n-card>
</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>