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 - 单个权限路由