From 60f912508b0e685957fb22ef0ed1f83272847263 Mon Sep 17 00:00:00 2001 From: Soybean Date: Mon, 9 May 2022 23:51:19 +0800 Subject: [PATCH] =?UTF-8?q?fix(projects):=20=E4=BF=AE=E5=A4=8D=E6=9D=83?= =?UTF-8?q?=E9=99=90=E5=88=87=E6=8D=A2=E8=B7=AF=E7=94=B1=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=9C=AA=E6=9B=B4=E6=96=B0=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 +- pnpm-lock.yaml | 41 +++++++--- src/layouts/common/GlobalContent/index.vue | 12 +-- src/router/guard/dynamic.ts | 7 +- src/router/guard/index.ts | 2 +- src/router/guard/permission.ts | 7 +- src/store/modules/auth/index.ts | 87 ++++++++++++++++------ src/store/modules/route/index.ts | 50 +++++++------ src/utils/router/helpers.ts | 22 +++++- 9 files changed, 159 insertions(+), 73 deletions(-) diff --git a/package.json b/package.json index 7cc1bf2e9..4dd19a722 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "eslint-plugin-import": "^2.26.0", "eslint-plugin-prettier": "^4.0.0", "eslint-plugin-vue": "^8.7.1", - "husky": "^8.0.0", + "husky": "^8.0.1", "lint-staged": "^12.4.1", "mockjs": "^1.1.0", "patch-package": "^6.4.7", @@ -89,7 +89,7 @@ "typescript": "^4.6.4", "unocss": "^0.33.2", "unplugin-icons": "^0.14.3", - "unplugin-vue-components": "0.19.3", + "unplugin-vue-components": "0.19.5", "unplugin-vue-define-options": "^0.6.1", "vite": "^2.9.8", "vite-plugin-compression": "^0.5.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ff5230795..5978e4798 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,7 +38,7 @@ specifiers: eslint-plugin-prettier: ^4.0.0 eslint-plugin-vue: ^8.7.1 form-data: ^4.0.0 - husky: ^8.0.0 + husky: ^8.0.1 lint-staged: ^12.4.1 lodash-es: ^4.17.21 mockjs: ^1.1.0 @@ -57,7 +57,7 @@ specifiers: ua-parser-js: ^1.0.2 unocss: ^0.33.2 unplugin-icons: ^0.14.3 - unplugin-vue-components: 0.19.3 + unplugin-vue-components: 0.19.5 unplugin-vue-define-options: ^0.6.1 vditor: ^3.8.13 vite: ^2.9.8 @@ -123,7 +123,7 @@ devDependencies: eslint-plugin-import: 2.26.0_eslint@8.15.0 eslint-plugin-prettier: 4.0.0_440b30a60bbe5bb6e3ad0057150b2782 eslint-plugin-vue: 8.7.1_eslint@8.15.0 - husky: 8.0.0 + husky: 8.0.1 lint-staged: 12.4.1 mockjs: 1.1.0 patch-package: 6.4.7 @@ -135,7 +135,7 @@ devDependencies: typescript: 4.6.4 unocss: 0.33.2_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 vite: 2.9.8_sass@1.51.0 vite-plugin-compression: 0.5.1_vite@2.9.8 @@ -3729,8 +3729,8 @@ packages: engines: {node: '>=10.17.0'} dev: true - /husky/8.0.0: - resolution: {integrity: sha512-4qbE/5dzNDNxFEkX9MNRPKl5+omTXQzdILCUWiqG/lWIAioiM5vln265/l6I2Zx8gpW8l1ukZwGQeCFbBZ6+6w==} + /husky/8.0.1: + resolution: {integrity: sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==} engines: {node: '>=14'} hasBin: true dev: true @@ -5922,8 +5922,8 @@ packages: - webpack dev: true - /unplugin-vue-components/0.19.3_vite@2.9.8+vue@3.2.33: - resolution: {integrity: sha512-z/kpYJnqrJuWglDNs7fy0YRHr41oLc07y2TkP3by6DqPb1GG9xGC9SFigeFwd4J7GVTqyFVsnjoeup7uK7I2dA==} + /unplugin-vue-components/0.19.5_vite@2.9.8+vue@3.2.33: + resolution: {integrity: sha512-cIC+PdQEXmG+B1gmZGk4hws2xP+00C6pg3FD6ixEgRyW+WF+QXQW/60pc+hUhtDYs1PFE+23K3NY7yvYTnDDTA==} engines: {node: '>=14'} peerDependencies: '@babel/parser': ^7.15.8 @@ -5944,7 +5944,7 @@ packages: magic-string: 0.26.1 minimatch: 5.0.1 resolve: 1.22.0 - unplugin: 0.6.2_vite@2.9.8 + unplugin: 0.6.3_vite@2.9.8 vue: 3.2.33 transitivePeerDependencies: - esbuild @@ -5994,6 +5994,29 @@ packages: webpack-virtual-modules: 0.4.3 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: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: diff --git a/src/layouts/common/GlobalContent/index.vue b/src/layouts/common/GlobalContent/index.vue index beb8f9be9..cec3f024f 100644 --- a/src/layouts/common/GlobalContent/index.vue +++ b/src/layouts/common/GlobalContent/index.vue @@ -4,11 +4,13 @@ class="h-full bg-[#f6f9f8] dark:bg-[#101014] transition duration-300 ease-in-out" > - - - - - +
+ + + + + +
diff --git a/src/router/guard/dynamic.ts b/src/router/guard/dynamic.ts index 8e44983cf..08d0cabea 100644 --- a/src/router/guard/dynamic.ts +++ b/src/router/guard/dynamic.ts @@ -1,4 +1,4 @@ -import type { Router, RouteLocationNormalized, NavigationGuardNext } from 'vue-router'; +import type { RouteLocationNormalized, NavigationGuardNext } from 'vue-router'; import { routeName } from '@/router'; import { useRouteStore } from '@/store'; import { getToken } from '@/utils'; @@ -9,8 +9,7 @@ import { getToken } from '@/utils'; export async function createDynamicRouteGuard( to: RouteLocationNormalized, _from: RouteLocationNormalized, - next: NavigationGuardNext, - router: Router + next: NavigationGuardNext ) { const route = useRouteStore(); const isLogin = Boolean(getToken()); @@ -28,7 +27,7 @@ export async function createDynamicRouteGuard( return false; } - await route.initAuthRoute(router); + await route.initAuthRoute(); if (to.name === routeName('not-found-page')) { // 动态路由没有加载导致被not-found-page路由捕获,等待权限路由加载好了,回到之前的路由 diff --git a/src/router/guard/index.ts b/src/router/guard/index.ts index 174227b6b..00bcccf01 100644 --- a/src/router/guard/index.ts +++ b/src/router/guard/index.ts @@ -11,7 +11,7 @@ export function createRouterGuard(router: Router) { // 开始 loadingBar window.$loadingBar?.start(); // 页面跳转权限处理 - await createPermissionGuard(to, from, next, router); + await createPermissionGuard(to, from, next); }); router.afterEach(to => { // 设置document title diff --git a/src/router/guard/permission.ts b/src/router/guard/permission.ts index 8105c1ee8..968e0f39d 100644 --- a/src/router/guard/permission.ts +++ b/src/router/guard/permission.ts @@ -1,4 +1,4 @@ -import type { Router, RouteLocationNormalized, NavigationGuardNext } from 'vue-router'; +import type { RouteLocationNormalized, NavigationGuardNext } from 'vue-router'; import { routeName } from '@/router'; import { useAuthStore } from '@/store'; import { exeStrategyActions, getToken } from '@/utils'; @@ -8,11 +8,10 @@ import { createDynamicRouteGuard } from './dynamic'; export async function createPermissionGuard( to: RouteLocationNormalized, from: RouteLocationNormalized, - next: NavigationGuardNext, - router: Router + next: NavigationGuardNext ) { // 动态路由 - const permission = await createDynamicRouteGuard(to, from, next, router); + const permission = await createDynamicRouteGuard(to, from, next); if (!permission) return; // 外链路由, 从新标签打开,返回上一个路由 diff --git a/src/store/modules/auth/index.ts b/src/store/modules/auth/index.ts index cfe3dbd55..6fea93933 100644 --- a/src/store/modules/auth/index.ts +++ b/src/store/modules/auth/index.ts @@ -1,6 +1,6 @@ -import { unref, nextTick } from 'vue'; +import { unref } from 'vue'; import { defineStore } from 'pinia'; -import { router as globalRouter } from '@/router'; +import { router } from '@/router'; import { useRouterPush } from '@/composables'; import { fetchLogin, fetchUserInfo } from '@/service'; import { getUserInfo, getToken, setUserInfo, setToken, setRefreshToken, clearAuthStorage } from '@/utils'; @@ -34,26 +34,50 @@ export const useAuthStore = defineStore('auth-store', { const { toLogin } = useRouterPush(false); const { resetTabStore } = useTabStore(); const { resetRouteStore } = useRouteStore(); - const route = unref(globalRouter.currentRoute); + const route = unref(router.currentRoute); clearAuthStorage(); this.$reset(); + resetTabStore(); + resetRouteStore(); + if (route.meta.requiresAuth) { toLogin(); } + }, + /** + * 处理登录后成功或失败的逻辑 + * @param backendToken - 返回的token + */ + async handleActionAfterLogin(backendToken: ApiAuth.Token) { + const { toLoginRedirect } = useRouterPush(false); - nextTick(() => { - resetTabStore(); - resetRouteStore(); - }); + const loginSuccess = await this.loginByToken(backendToken); + + if (loginSuccess) { + // 跳转登录后的地址 + toLoginRedirect(); + + // 登录成功弹出欢迎提示 + window.$notification?.success({ + title: '登录成功!', + content: `欢迎回来,${this.userInfo.userName}!`, + duration: 3000 + }); + + return; + } + + // 不成功则重置状态 + this.resetAuthStore(); }, /** * 根据token进行登录 * @param backendToken - 返回的token */ async loginByToken(backendToken: ApiAuth.Token) { - const { toLoginRedirect } = useRouterPush(false); + let successFlag = false; // 先把token存储到缓存中(后面接口的请求头需要token) const { token, refreshToken } = backendToken; @@ -70,19 +94,10 @@ export const useAuthStore = defineStore('auth-store', { this.userInfo = data; this.token = token; - // 跳转登录后的地址 - toLoginRedirect(); - - // 登录成功弹出欢迎提示 - window.$notification?.success({ - title: '登录成功!', - content: `欢迎回来,${data.userName}!`, - duration: 3000 - }); - } else { - // 不成功则重置状态 - this.resetAuthStore(); + successFlag = true; } + + return successFlag; }, /** * 登录 @@ -93,12 +108,38 @@ export const useAuthStore = defineStore('auth-store', { this.loginLoading = true; const { data } = await fetchLogin(userName, password); if (data) { - await this.loginByToken(data); + await this.handleActionAfterLogin(data); } 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 = { + 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(); + } } } }); diff --git a/src/store/modules/route/index.ts b/src/store/modules/route/index.ts index 41d361994..686402558 100644 --- a/src/store/modules/route/index.ts +++ b/src/store/modules/route/index.ts @@ -1,6 +1,5 @@ -import type { Router } from 'vue-router'; import { defineStore } from 'pinia'; -import { routes as staticRoutes } from '@/router'; +import { router, constantRoutes, routes as staticRoutes } from '@/router'; import { fetchUserRoutes } from '@/service'; import { getUserInfo, @@ -9,7 +8,8 @@ import { transformAuthRoutesToSearchMenus, getCacheRoutes, filterAuthRoutesByUserPermission, - transformRoutePathToRouteName + transformRoutePathToRouteName, + getConstantRouteNames } from '@/utils'; import { useAuthStore } from '../auth'; import { useTabStore } from '../tab'; @@ -44,14 +44,25 @@ export const useRouteStore = defineStore('route-store', { }), actions: { resetRouteStore() { + this.resetRoutes(); 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 router - 路由实例 */ - handleAuthRoutes(routes: AuthRoute.Route[], router: Router) { + handleAuthRoutes(routes: AuthRoute.Route[]) { this.menus = transformAuthRouteToMenu(routes); this.searchMenus = transformAuthRoutesToSearchMenus(routes); @@ -63,32 +74,23 @@ export const useRouteStore = defineStore('route-store', { this.cacheRoutes = getCacheRoutes(vueRoutes); }, - /** - * 初始化动态路由 - * @param router - 路由实例 - */ - async initDynamicRoute(router: Router) { + /** 初始化动态路由 */ + async initDynamicRoute() { const { userId } = getUserInfo(); const { data } = await fetchUserRoutes(userId); if (data) { this.routeHomeName = data.home; - this.handleAuthRoutes(data.routes, router); + this.handleAuthRoutes(data.routes); } }, - /** - * 初始化静态路由 - * @param router - 路由实例 - */ - async initStaticRoute(router: Router) { + /** 初始化静态路由 */ + async initStaticRoute() { const auth = useAuthStore(); const routes = filterAuthRoutesByUserPermission(staticRoutes, auth.userInfo.userRole); - this.handleAuthRoutes(routes, router); + this.handleAuthRoutes(routes); }, - /** - * 初始化权限路由 - * @param router - 路由实例 - */ - async initAuthRoute(router: Router) { + /** 初始化权限路由 */ + async initAuthRoute() { const { initHomeTab } = useTabStore(); const { userId } = getUserInfo(); @@ -96,9 +98,9 @@ export const useRouteStore = defineStore('route-store', { const isDynamicRoute = this.authRouteMode === 'dynamic'; if (isDynamicRoute) { - await this.initDynamicRoute(router); + await this.initDynamicRoute(); } else { - await this.initStaticRoute(router); + await this.initStaticRoute(); } initHomeTab(this.routeHomeName, router); diff --git a/src/utils/router/helpers.ts b/src/utils/router/helpers.ts index 4ed0aaa79..317447db2 100644 --- a/src/utils/router/helpers.ts +++ b/src/utils/router/helpers.ts @@ -2,7 +2,13 @@ import type { RouteRecordRaw } from 'vue-router'; import { consoleError } from '../common'; import { getLayoutComponent, getViewComponent } from './component'; -type ComponentAction = Record void>; +/** + * 获取所有固定路由的名称集合 + * @param routes - 固定路由 + */ +export function getConstantRouteNames(routes: AuthRoute.Route[]) { + return routes.map(route => getConstantRouteName(route)).flat(1); +} /** * 将权限路由转换成vue路由 @@ -59,6 +65,20 @@ export function transformRoutePathToRouteName( 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 void>; + /** * 将单个权限路由转换成vue路由 * @param item - 单个权限路由