fix(projects): 修复权限切换路由数据未更新的问题

This commit is contained in:
Soybean 2022-05-09 23:51:19 +08:00
parent 3590b65e22
commit 60f912508b
9 changed files with 159 additions and 73 deletions

View File

@ -77,7 +77,7 @@
"eslint-plugin-import": "^2.26.0", "eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.7.1", "eslint-plugin-vue": "^8.7.1",
"husky": "^8.0.0", "husky": "^8.0.1",
"lint-staged": "^12.4.1", "lint-staged": "^12.4.1",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"patch-package": "^6.4.7", "patch-package": "^6.4.7",
@ -89,7 +89,7 @@
"typescript": "^4.6.4", "typescript": "^4.6.4",
"unocss": "^0.33.2", "unocss": "^0.33.2",
"unplugin-icons": "^0.14.3", "unplugin-icons": "^0.14.3",
"unplugin-vue-components": "0.19.3", "unplugin-vue-components": "0.19.5",
"unplugin-vue-define-options": "^0.6.1", "unplugin-vue-define-options": "^0.6.1",
"vite": "^2.9.8", "vite": "^2.9.8",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",

View File

@ -38,7 +38,7 @@ specifiers:
eslint-plugin-prettier: ^4.0.0 eslint-plugin-prettier: ^4.0.0
eslint-plugin-vue: ^8.7.1 eslint-plugin-vue: ^8.7.1
form-data: ^4.0.0 form-data: ^4.0.0
husky: ^8.0.0 husky: ^8.0.1
lint-staged: ^12.4.1 lint-staged: ^12.4.1
lodash-es: ^4.17.21 lodash-es: ^4.17.21
mockjs: ^1.1.0 mockjs: ^1.1.0
@ -57,7 +57,7 @@ specifiers:
ua-parser-js: ^1.0.2 ua-parser-js: ^1.0.2
unocss: ^0.33.2 unocss: ^0.33.2
unplugin-icons: ^0.14.3 unplugin-icons: ^0.14.3
unplugin-vue-components: 0.19.3 unplugin-vue-components: 0.19.5
unplugin-vue-define-options: ^0.6.1 unplugin-vue-define-options: ^0.6.1
vditor: ^3.8.13 vditor: ^3.8.13
vite: ^2.9.8 vite: ^2.9.8
@ -123,7 +123,7 @@ devDependencies:
eslint-plugin-import: 2.26.0_eslint@8.15.0 eslint-plugin-import: 2.26.0_eslint@8.15.0
eslint-plugin-prettier: 4.0.0_440b30a60bbe5bb6e3ad0057150b2782 eslint-plugin-prettier: 4.0.0_440b30a60bbe5bb6e3ad0057150b2782
eslint-plugin-vue: 8.7.1_eslint@8.15.0 eslint-plugin-vue: 8.7.1_eslint@8.15.0
husky: 8.0.0 husky: 8.0.1
lint-staged: 12.4.1 lint-staged: 12.4.1
mockjs: 1.1.0 mockjs: 1.1.0
patch-package: 6.4.7 patch-package: 6.4.7
@ -135,7 +135,7 @@ devDependencies:
typescript: 4.6.4 typescript: 4.6.4
unocss: 0.33.2_vite@2.9.8 unocss: 0.33.2_vite@2.9.8
unplugin-icons: 0.14.3_vite@2.9.8 unplugin-icons: 0.14.3_vite@2.9.8
unplugin-vue-components: 0.19.3_vite@2.9.8+vue@3.2.33 unplugin-vue-components: 0.19.5_vite@2.9.8+vue@3.2.33
unplugin-vue-define-options: 0.6.1_vite@2.9.8+vue@3.2.33 unplugin-vue-define-options: 0.6.1_vite@2.9.8+vue@3.2.33
vite: 2.9.8_sass@1.51.0 vite: 2.9.8_sass@1.51.0
vite-plugin-compression: 0.5.1_vite@2.9.8 vite-plugin-compression: 0.5.1_vite@2.9.8
@ -3729,8 +3729,8 @@ packages:
engines: {node: '>=10.17.0'} engines: {node: '>=10.17.0'}
dev: true dev: true
/husky/8.0.0: /husky/8.0.1:
resolution: {integrity: sha512-4qbE/5dzNDNxFEkX9MNRPKl5+omTXQzdILCUWiqG/lWIAioiM5vln265/l6I2Zx8gpW8l1ukZwGQeCFbBZ6+6w==} resolution: {integrity: sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==}
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
dev: true dev: true
@ -5922,8 +5922,8 @@ packages:
- webpack - webpack
dev: true dev: true
/unplugin-vue-components/0.19.3_vite@2.9.8+vue@3.2.33: /unplugin-vue-components/0.19.5_vite@2.9.8+vue@3.2.33:
resolution: {integrity: sha512-z/kpYJnqrJuWglDNs7fy0YRHr41oLc07y2TkP3by6DqPb1GG9xGC9SFigeFwd4J7GVTqyFVsnjoeup7uK7I2dA==} resolution: {integrity: sha512-cIC+PdQEXmG+B1gmZGk4hws2xP+00C6pg3FD6ixEgRyW+WF+QXQW/60pc+hUhtDYs1PFE+23K3NY7yvYTnDDTA==}
engines: {node: '>=14'} engines: {node: '>=14'}
peerDependencies: peerDependencies:
'@babel/parser': ^7.15.8 '@babel/parser': ^7.15.8
@ -5944,7 +5944,7 @@ packages:
magic-string: 0.26.1 magic-string: 0.26.1
minimatch: 5.0.1 minimatch: 5.0.1
resolve: 1.22.0 resolve: 1.22.0
unplugin: 0.6.2_vite@2.9.8 unplugin: 0.6.3_vite@2.9.8
vue: 3.2.33 vue: 3.2.33
transitivePeerDependencies: transitivePeerDependencies:
- esbuild - esbuild
@ -5994,6 +5994,29 @@ packages:
webpack-virtual-modules: 0.4.3 webpack-virtual-modules: 0.4.3
dev: true dev: true
/unplugin/0.6.3_vite@2.9.8:
resolution: {integrity: sha512-CoW88FQfCW/yabVc4bLrjikN9HC8dEvMU4O7B6K2jsYMPK0l6iAnd9dpJwqGcmXJKRCU9vwSsy653qg+RK0G6A==}
peerDependencies:
esbuild: '>=0.13'
rollup: ^2.50.0
vite: ^2.3.0
webpack: 4 || 5
peerDependenciesMeta:
esbuild:
optional: true
rollup:
optional: true
vite:
optional: true
webpack:
optional: true
dependencies:
chokidar: 3.5.3
vite: 2.9.8_sass@1.51.0
webpack-sources: 3.2.3
webpack-virtual-modules: 0.4.3
dev: true
/uri-js/4.4.1: /uri-js/4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
dependencies: dependencies:

View File

@ -4,11 +4,13 @@
class="h-full bg-[#f6f9f8] dark:bg-[#101014] transition duration-300 ease-in-out" class="h-full bg-[#f6f9f8] dark:bg-[#101014] transition duration-300 ease-in-out"
> >
<router-view v-slot="{ Component, route }"> <router-view v-slot="{ Component, route }">
<transition :name="theme.page.animate ? theme.page.animateMode : undefined" mode="out-in" appear> <div class="h-full">
<keep-alive :include="routeStore.cacheRoutes"> <transition :name="theme.page.animate ? theme.page.animateMode : undefined" mode="out-in" appear>
<component :is="Component" v-if="app.reloadFlag" :key="route.path" /> <keep-alive :include="routeStore.cacheRoutes">
</keep-alive> <component :is="Component" v-if="app.reloadFlag" :key="route.path" />
</transition> </keep-alive>
</transition>
</div>
</router-view> </router-view>
</div> </div>
</template> </template>

View File

@ -1,4 +1,4 @@
import type { Router, RouteLocationNormalized, NavigationGuardNext } from 'vue-router'; import type { RouteLocationNormalized, NavigationGuardNext } from 'vue-router';
import { routeName } from '@/router'; import { routeName } from '@/router';
import { useRouteStore } from '@/store'; import { useRouteStore } from '@/store';
import { getToken } from '@/utils'; import { getToken } from '@/utils';
@ -9,8 +9,7 @@ import { getToken } from '@/utils';
export async function createDynamicRouteGuard( export async function createDynamicRouteGuard(
to: RouteLocationNormalized, to: RouteLocationNormalized,
_from: RouteLocationNormalized, _from: RouteLocationNormalized,
next: NavigationGuardNext, next: NavigationGuardNext
router: Router
) { ) {
const route = useRouteStore(); const route = useRouteStore();
const isLogin = Boolean(getToken()); const isLogin = Boolean(getToken());
@ -28,7 +27,7 @@ export async function createDynamicRouteGuard(
return false; return false;
} }
await route.initAuthRoute(router); await route.initAuthRoute();
if (to.name === routeName('not-found-page')) { if (to.name === routeName('not-found-page')) {
// 动态路由没有加载导致被not-found-page路由捕获等待权限路由加载好了回到之前的路由 // 动态路由没有加载导致被not-found-page路由捕获等待权限路由加载好了回到之前的路由

View File

@ -11,7 +11,7 @@ export function createRouterGuard(router: Router) {
// 开始 loadingBar // 开始 loadingBar
window.$loadingBar?.start(); window.$loadingBar?.start();
// 页面跳转权限处理 // 页面跳转权限处理
await createPermissionGuard(to, from, next, router); await createPermissionGuard(to, from, next);
}); });
router.afterEach(to => { router.afterEach(to => {
// 设置document title // 设置document title

View File

@ -1,4 +1,4 @@
import type { Router, RouteLocationNormalized, NavigationGuardNext } from 'vue-router'; import type { RouteLocationNormalized, NavigationGuardNext } from 'vue-router';
import { routeName } from '@/router'; import { routeName } from '@/router';
import { useAuthStore } from '@/store'; import { useAuthStore } from '@/store';
import { exeStrategyActions, getToken } from '@/utils'; import { exeStrategyActions, getToken } from '@/utils';
@ -8,11 +8,10 @@ import { createDynamicRouteGuard } from './dynamic';
export async function createPermissionGuard( export async function createPermissionGuard(
to: RouteLocationNormalized, to: RouteLocationNormalized,
from: RouteLocationNormalized, from: RouteLocationNormalized,
next: NavigationGuardNext, next: NavigationGuardNext
router: Router
) { ) {
// 动态路由 // 动态路由
const permission = await createDynamicRouteGuard(to, from, next, router); const permission = await createDynamicRouteGuard(to, from, next);
if (!permission) return; if (!permission) return;
// 外链路由, 从新标签打开,返回上一个路由 // 外链路由, 从新标签打开,返回上一个路由

View File

@ -1,6 +1,6 @@
import { unref, nextTick } from 'vue'; import { unref } from 'vue';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { router as globalRouter } from '@/router'; import { router } from '@/router';
import { useRouterPush } from '@/composables'; import { useRouterPush } from '@/composables';
import { fetchLogin, fetchUserInfo } from '@/service'; import { fetchLogin, fetchUserInfo } from '@/service';
import { getUserInfo, getToken, setUserInfo, setToken, setRefreshToken, clearAuthStorage } from '@/utils'; import { getUserInfo, getToken, setUserInfo, setToken, setRefreshToken, clearAuthStorage } from '@/utils';
@ -34,26 +34,50 @@ export const useAuthStore = defineStore('auth-store', {
const { toLogin } = useRouterPush(false); const { toLogin } = useRouterPush(false);
const { resetTabStore } = useTabStore(); const { resetTabStore } = useTabStore();
const { resetRouteStore } = useRouteStore(); const { resetRouteStore } = useRouteStore();
const route = unref(globalRouter.currentRoute); const route = unref(router.currentRoute);
clearAuthStorage(); clearAuthStorage();
this.$reset(); this.$reset();
resetTabStore();
resetRouteStore();
if (route.meta.requiresAuth) { if (route.meta.requiresAuth) {
toLogin(); toLogin();
} }
},
/**
*
* @param backendToken - token
*/
async handleActionAfterLogin(backendToken: ApiAuth.Token) {
const { toLoginRedirect } = useRouterPush(false);
nextTick(() => { const loginSuccess = await this.loginByToken(backendToken);
resetTabStore();
resetRouteStore(); if (loginSuccess) {
}); // 跳转登录后的地址
toLoginRedirect();
// 登录成功弹出欢迎提示
window.$notification?.success({
title: '登录成功!',
content: `欢迎回来,${this.userInfo.userName}!`,
duration: 3000
});
return;
}
// 不成功则重置状态
this.resetAuthStore();
}, },
/** /**
* token进行登录 * token进行登录
* @param backendToken - token * @param backendToken - token
*/ */
async loginByToken(backendToken: ApiAuth.Token) { async loginByToken(backendToken: ApiAuth.Token) {
const { toLoginRedirect } = useRouterPush(false); let successFlag = false;
// 先把token存储到缓存中(后面接口的请求头需要token) // 先把token存储到缓存中(后面接口的请求头需要token)
const { token, refreshToken } = backendToken; const { token, refreshToken } = backendToken;
@ -70,19 +94,10 @@ export const useAuthStore = defineStore('auth-store', {
this.userInfo = data; this.userInfo = data;
this.token = token; this.token = token;
// 跳转登录后的地址 successFlag = true;
toLoginRedirect();
// 登录成功弹出欢迎提示
window.$notification?.success({
title: '登录成功!',
content: `欢迎回来,${data.userName}!`,
duration: 3000
});
} else {
// 不成功则重置状态
this.resetAuthStore();
} }
return successFlag;
}, },
/** /**
* *
@ -93,12 +108,38 @@ export const useAuthStore = defineStore('auth-store', {
this.loginLoading = true; this.loginLoading = true;
const { data } = await fetchLogin(userName, password); const { data } = await fetchLogin(userName, password);
if (data) { if (data) {
await this.loginByToken(data); await this.handleActionAfterLogin(data);
} }
this.loginLoading = false; this.loginLoading = false;
}, },
updateUserRole(userRole: Auth.RoleType) { /**
this.userInfo.userRole = userRole; * ()
* @param userRole
*/
async updateUserRole(userRole: Auth.RoleType) {
const { resetRouteStore, initAuthRoute } = useRouteStore();
const accounts: Record<Auth.RoleType, { userName: string; password: string }> = {
super: {
userName: 'Super',
password: 'super123'
},
admin: {
userName: 'Admin',
password: 'admin123'
},
user: {
userName: 'User01',
password: 'user01123'
}
};
const { userName, password } = accounts[userRole];
const { data } = await fetchLogin(userName, password);
if (data) {
await this.loginByToken(data);
resetRouteStore();
initAuthRoute();
}
} }
} }
}); });

View File

@ -1,6 +1,5 @@
import type { Router } from 'vue-router';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { routes as staticRoutes } from '@/router'; import { router, constantRoutes, routes as staticRoutes } from '@/router';
import { fetchUserRoutes } from '@/service'; import { fetchUserRoutes } from '@/service';
import { import {
getUserInfo, getUserInfo,
@ -9,7 +8,8 @@ import {
transformAuthRoutesToSearchMenus, transformAuthRoutesToSearchMenus,
getCacheRoutes, getCacheRoutes,
filterAuthRoutesByUserPermission, filterAuthRoutesByUserPermission,
transformRoutePathToRouteName transformRoutePathToRouteName,
getConstantRouteNames
} from '@/utils'; } from '@/utils';
import { useAuthStore } from '../auth'; import { useAuthStore } from '../auth';
import { useTabStore } from '../tab'; import { useTabStore } from '../tab';
@ -44,14 +44,25 @@ export const useRouteStore = defineStore('route-store', {
}), }),
actions: { actions: {
resetRouteStore() { resetRouteStore() {
this.resetRoutes();
this.$reset(); this.$reset();
}, },
/** 重置路由数据,保留固定路由 */
resetRoutes() {
const routes = router.getRoutes();
const constantRouteNames = getConstantRouteNames(constantRoutes);
routes.forEach(route => {
const name: AuthRoute.RouteKey = (route.name || 'root') as AuthRoute.RouteKey;
if (!constantRouteNames.includes(name)) {
router.removeRoute(name);
}
});
},
/** /**
* *
* @param routes - * @param routes -
* @param router -
*/ */
handleAuthRoutes(routes: AuthRoute.Route[], router: Router) { handleAuthRoutes(routes: AuthRoute.Route[]) {
this.menus = transformAuthRouteToMenu(routes); this.menus = transformAuthRouteToMenu(routes);
this.searchMenus = transformAuthRoutesToSearchMenus(routes); this.searchMenus = transformAuthRoutesToSearchMenus(routes);
@ -63,32 +74,23 @@ export const useRouteStore = defineStore('route-store', {
this.cacheRoutes = getCacheRoutes(vueRoutes); this.cacheRoutes = getCacheRoutes(vueRoutes);
}, },
/** /** 初始化动态路由 */
* async initDynamicRoute() {
* @param router -
*/
async initDynamicRoute(router: Router) {
const { userId } = getUserInfo(); const { userId } = getUserInfo();
const { data } = await fetchUserRoutes(userId); const { data } = await fetchUserRoutes(userId);
if (data) { if (data) {
this.routeHomeName = data.home; this.routeHomeName = data.home;
this.handleAuthRoutes(data.routes, router); this.handleAuthRoutes(data.routes);
} }
}, },
/** /** 初始化静态路由 */
* async initStaticRoute() {
* @param router -
*/
async initStaticRoute(router: Router) {
const auth = useAuthStore(); const auth = useAuthStore();
const routes = filterAuthRoutesByUserPermission(staticRoutes, auth.userInfo.userRole); const routes = filterAuthRoutesByUserPermission(staticRoutes, auth.userInfo.userRole);
this.handleAuthRoutes(routes, router); this.handleAuthRoutes(routes);
}, },
/** /** 初始化权限路由 */
* async initAuthRoute() {
* @param router -
*/
async initAuthRoute(router: Router) {
const { initHomeTab } = useTabStore(); const { initHomeTab } = useTabStore();
const { userId } = getUserInfo(); const { userId } = getUserInfo();
@ -96,9 +98,9 @@ export const useRouteStore = defineStore('route-store', {
const isDynamicRoute = this.authRouteMode === 'dynamic'; const isDynamicRoute = this.authRouteMode === 'dynamic';
if (isDynamicRoute) { if (isDynamicRoute) {
await this.initDynamicRoute(router); await this.initDynamicRoute();
} else { } else {
await this.initStaticRoute(router); await this.initStaticRoute();
} }
initHomeTab(this.routeHomeName, router); initHomeTab(this.routeHomeName, router);

View File

@ -2,7 +2,13 @@ import type { RouteRecordRaw } from 'vue-router';
import { consoleError } from '../common'; import { consoleError } from '../common';
import { getLayoutComponent, getViewComponent } from './component'; import { getLayoutComponent, getViewComponent } from './component';
type ComponentAction = Record<AuthRoute.RouteComponent, () => void>; /**
*
* @param routes -
*/
export function getConstantRouteNames(routes: AuthRoute.Route[]) {
return routes.map(route => getConstantRouteName(route)).flat(1);
}
/** /**
* vue路由 * vue路由
@ -59,6 +65,20 @@ export function transformRoutePathToRouteName(
return name; return name;
} }
/**
*
* @param route -
*/
function getConstantRouteName(route: AuthRoute.Route) {
const names = [route.name];
if (hasChildren(route)) {
names.push(...route.children!.map(item => getConstantRouteName(item)).flat(1));
}
return names;
}
type ComponentAction = Record<AuthRoute.RouteComponent, () => void>;
/** /**
* vue路由 * vue路由
* @param item - * @param item -