diff --git a/mock/module/user.ts b/mock/module/user.ts index 874fc6b..8b06d59 100644 --- a/mock/module/user.ts +++ b/mock/module/user.ts @@ -85,85 +85,102 @@ const userInfo = { avatar: 'https://z3.ax1x.com/2021/10/29/5jnWgf.jpg', role: 'admin', password: '123456', - token, - userRoutes: [ - { - name: 'dashboard', - path: '/dashboard', - meta: { - title: '分析页', - requiresAuth: true, - icon: 'icon-park-outline:analysis', - }, - children: [ - { - name: 'dashboard_workbench', - path: '/dashboard/workbench', - meta: { - title: '工作台', - requiresAuth: true, - icon: 'icon-park-outline:alarm', - }, - }, - { - name: 'dashboard_monitor', - path: '/dashboard/monitor', - meta: { - title: '监控页', - requiresAuth: true, - icon: 'icon-park-outline:anchor', - }, - }, - ], - }, - { - name: 'test', - path: '/test', - meta: { - title: '测试专题', - requiresAuth: true, - icon: 'icon-park-outline:ambulance', - }, - children: [ - { - name: 'test1', - path: '/test/test1', - meta: { - title: '测试专题1', - requiresAuth: true, - icon: 'icon-park-outline:alarm', - }, - }, - { - name: 'test2', - path: '/test/test2', - meta: { - title: '测试专题2', - requiresAuth: true, - icon: 'icon-park-outline:pic', - }, - }, - { - name: 'test3', - path: '/test/test3', - meta: { - title: '测试专题3', - requiresAuth: true, - icon: 'icon-park-outline:tool', - }, - }, - ], - }, - ], }; +const userRoutes = [ + { + name: 'dashboard', + path: '/dashboard', + redirect: '/dashboard/workbench', + meta: { + title: '分析页', + requiresAuth: true, + icon: 'icon-park-outline:analysis', + }, + children: [ + { + name: 'dashboard_workbench', + path: '/dashboard/workbench', + meta: { + title: '工作台', + requiresAuth: true, + icon: 'icon-park-outline:alarm', + }, + }, + { + name: 'dashboard_monitor', + path: '/dashboard/monitor', + meta: { + title: '监控页', + requiresAuth: true, + icon: 'icon-park-outline:anchor', + }, + }, + ], + }, + { + name: 'test', + path: '/test', + redirect: '/test/test1', + meta: { + title: '测试专题', + requiresAuth: true, + icon: 'icon-park-outline:ambulance', + }, + children: [ + { + name: 'test1', + path: '/test/test1', + meta: { + title: '测试专题1', + requiresAuth: true, + icon: 'icon-park-outline:alarm', + }, + }, + { + name: 'test2', + path: '/test/test2', + meta: { + title: '测试专题2', + requiresAuth: true, + icon: 'icon-park-outline:pic', + }, + }, + { + name: 'test3', + path: '/test/test3', + meta: { + title: '测试专题3', + requiresAuth: true, + icon: 'icon-park-outline:tool', + }, + }, + ], + }, +]; export default [ { url: '/mock/login', timeout: 1000, method: 'post', + response: () => { + return resultSuccess({ token }); + }, + }, + { + url: '/mock/getUserInfo', + timeout: 1000, + method: 'get', response: () => { return resultSuccess(userInfo); }, }, + { + url: '/mock/getUserRoutes', + timeout: 1000, + method: 'post', + response: () => { + return resultSuccess(userRoutes); + }, + }, ]; diff --git a/src/router/guard/dynamic.ts b/src/router/guard/dynamic.ts index b887cad..644837e 100644 --- a/src/router/guard/dynamic.ts +++ b/src/router/guard/dynamic.ts @@ -1,77 +1,45 @@ import { RouteRecordRaw } from 'vue-router'; -import { router } from '@/router'; import { BasicLayout } from '@/layouts/index'; -import { useAuthStore } from '@/store'; -export async function setDynamicRoutes() { - const authStore = useAuthStore(); - const vueRootRoute: RouteRecordRaw = { - path: '/test', - name: 'test', - redirect: '/test/test1', +// 引入所有页面 +const modules = import.meta.glob('../../views/**/*.vue'); +/* 将路由树转换成一维数组 */ +function FlatAuthRoutes(routes: AppRoute.Route[]) { + let result: AppRoute.Route[] = []; + // 浅拷贝一层去降维,否则影响原数组 + const _routes = JSON.parse(JSON.stringify(routes)); + _routes.forEach((item: any) => { + if (item.children) { + const temp = item.children || []; + delete item.children; + result.push(item); + result = [...result, ...FlatAuthRoutes(temp)]; + } else { + result.push(item); + } + }); + return result; +} +export async function createDynamicRoutes(routes: AppRoute.Route[]) { + // 数组降维成一维数组,然后删除所有的childen + const flatRoutes = FlatAuthRoutes(routes); + // 生成路由,有redirect的不需要引入文件 + const mapRoutes = flatRoutes.map((item) => { + if (!item.redirect) { + // 动态加载对应页面 + item['component'] = modules[`../../views${item.path}/index.vue`]; + } + return item; + }); + + const appRootRoute: RouteRecordRaw = { + path: '/', + name: 'appRoot', + redirect: '/dashboard/workbench', component: BasicLayout, children: [], }; - const dynamicRoutes = [ - { - path: '/dashboard/workbench', - name: 'workbench', - component: () => import('@/views/dashboard/workbench/index.vue'), - meta: { - title: '工作台', - icon: 'icon-park-outline:music-list', - requiresAuth: true, - role: ['admin'], - }, - }, - { - path: '/dashboard/monitor', - name: 'monitor', - component: () => import('@/views/dashboard/monitor/index.vue'), - meta: { - title: '控制页', - icon: 'icon-park-outline:music-list', - requiresAuth: true, - role: ['admin'], - }, - }, - { - path: '/test/test1', - name: 'test1', - component: () => import(`@/views/test/test1.vue`), - meta: { - title: '测试1', - icon: 'icon-park-outline:game-three', - requiresAuth: true, - role: ['admin'], - }, - }, - { - path: '/test/test2', - name: 'test2', - component: () => import('@/views/test/test2.vue'), - meta: { - title: '测试2', - icon: 'carbon:aperture', - requiresAuth: true, - role: ['admin'], - }, - }, - { - path: '/test/test3', - name: 'test3', - component: () => import('@/views/test/test3.vue'), - meta: { - title: '测试3', - icon: 'icon-park-outline:music-list', - requiresAuth: true, - role: ['admin'], - }, - }, - ]; // 根据角色过滤后的插入根路由中 - vueRootRoute.children = dynamicRoutes.filter((route) => { - return route.meta.role.includes(authStore.userInfo.role); - }); - router.addRoute(vueRootRoute); + appRootRoute.children = mapRoutes as unknown as RouteRecordRaw[]; + return appRootRoute; } diff --git a/src/router/guard/permission.ts b/src/router/guard/permission.ts index fa8893f..d9b5208 100644 --- a/src/router/guard/permission.ts +++ b/src/router/guard/permission.ts @@ -1,7 +1,7 @@ import { RouteLocationNormalized, NavigationGuardNext } from 'vue-router'; import { getToken } from '@/utils/auth'; import { useRouteStore } from '@/store'; -import { setDynamicRoutes } from './dynamic'; +// import { setDynamicRoutes } from './dynamic'; export async function createPermissionGuard( to: RouteLocationNormalized, @@ -25,7 +25,6 @@ export async function createPermissionGuard( } return false; } - // debugger; // 有登录但是没有路由,初始化路由、侧边菜单等 // await setDynamicRoutes(); await routeStore.initAuthRoute(); diff --git a/src/service/api/login.ts b/src/service/api/login.ts index dff010c..13aeed2 100644 --- a/src/service/api/login.ts +++ b/src/service/api/login.ts @@ -7,3 +7,9 @@ interface Ilogin { export function fetchLogin(params: Ilogin) { return mockRequest.post('/login', params); } +export function fetchUserInfo() { + return mockRequest.get('/getUserInfo'); +} +export function fetchUserRoutes(params: string) { + return mockRequest.post('/getUserRoutes', params); +} diff --git a/src/store/modules/auth.ts b/src/store/modules/auth.ts index 8a32e71..a38439b 100644 --- a/src/store/modules/auth.ts +++ b/src/store/modules/auth.ts @@ -1,5 +1,5 @@ import { defineStore } from 'pinia'; -import { fetchLogin } from '@/service'; +import { fetchLogin, fetchUserInfo } from '@/service'; import { setUserInfo, getUserInfo, getToken, setToken, clearAuthStorage } from '@/utils/auth'; import { router } from '@/router'; import { useAppRouter } from '@/hook'; @@ -40,20 +40,19 @@ export const useAuthStore = defineStore('auth-store', { async login(userName: string, password: string) { this.loginLoading = true; const { data } = await fetchLogin({ userName, password }); - // 处理登录信息 - await this.handleAfterLogin(data as any); // TODO 避免any + await this.handleAfterLogin(data); // TODO 避免any this.loginLoading = false; }, /* 登录后的处理函数 */ - async handleAfterLogin(data: Auth.UserInfo) { - // 等待数据写入完成 + async handleAfterLogin(data: Auth.loginToken) { + // 将token和userInfo保存下来 const catchSuccess = await this.catchUserInfo(data); // 初始化侧边菜单 - const { initAuthRoute } = useRouteStore(); - await initAuthRoute(); + // const { initAuthRoute } = useRouteStore(); + // await initAuthRoute(); // 登录写入信息成功 if (catchSuccess) { @@ -75,14 +74,18 @@ export const useAuthStore = defineStore('auth-store', { }, /* 缓存用户信息 */ - async catchUserInfo(data: Auth.UserInfo) { + async catchUserInfo(userToken: Auth.loginToken) { let catchSuccess = false; + // 先存储token + const { token } = userToken; + setToken(token); - // 存储用户信息 + // 请求/存储用户信息 + const { data } = await fetchUserInfo(); setUserInfo(data); - setToken(data.token); + // 再将token和userInfo初始化 this.userInfo = data; - this.token = data.token; + this.token = token; catchSuccess = true; diff --git a/src/store/modules/route.ts b/src/store/modules/route.ts index 7da8ed9..9cbb6d5 100644 --- a/src/store/modules/route.ts +++ b/src/store/modules/route.ts @@ -1,8 +1,9 @@ import { defineStore } from 'pinia'; import { renderIcon, getUserInfo } from '@/utils'; -import { MenuOption, radioGroupProps } from 'naive-ui'; -import { setDynamicRoutes } from '@/router/guard/dynamic'; +import { MenuOption } from 'naive-ui'; +import { createDynamicRoutes } from '@/router/guard/dynamic'; import { router } from '@/router'; +import { fetchUserRoutes } from '@/service'; interface RoutesStatus { isInitAuthRoute: boolean; @@ -24,17 +25,25 @@ export const useRouteStore = defineStore('route-store', { }, resetRoutes() { /* 删除后面添加的路由 */ - router.removeRoute('test'); + router.removeRoute('appRoot'); }, - async setUserRoutes() { - this.userRoutes = getUserInfo().userRoutes; + createMenus(userRoutes: AppRoute.Route[]) { + this.userRoutes = userRoutes; + this.menus = this.transformAuthRoutesToMenus(userRoutes); }, - async setMenus() { - this.setUserRoutes(); - this.menus = this.transformAuthRoutesToMenus(this.userRoutes); + async initDynamicRoute() { + // 根据用户id来获取用户的路由 + const { userId } = getUserInfo(); + const { data } = await fetchUserRoutes(userId); + // 根据用户返回的路由表来生成真实路由 + const appRoutes = await createDynamicRoutes(data); + // 更具返回的生成侧边菜单 + await this.createMenus(data); + // 插入路由表 + router.addRoute(appRoutes); }, // 将返回的路由表渲染成侧边栏 - transformAuthRoutesToMenus(userRoutes: Auth.UserInfoPermissions[]): MenuOption[] { + transformAuthRoutesToMenus(userRoutes: AppRoute.Route[]): MenuOption[] { return userRoutes.map((item) => { const target: MenuOption = { label: item.meta.title, @@ -52,25 +61,8 @@ export const useRouteStore = defineStore('route-store', { }); }, - /* 将路由树转换成一维数组 */ - FlatAuthRoutes(routes: AppRoute.Route[]) { - let result: AppRoute.Route[] = []; - routes.forEach((item) => { - if (Object.prototype.hasOwnProperty.call(item, 'children')) { - const temp = item.children || []; - delete item.children; - result.push(item); - result = [...result, ...this.FlatAuthRoutes(temp)]; - } else { - result.push(item); - } - }); - return result; - }, - async initAuthRoute() { - await this.setMenus(); - await setDynamicRoutes(); + await this.initDynamicRoute(); this.isInitAuthRoute = true; }, }, diff --git a/src/types/business.d.ts b/src/types/business.d.ts index 97c5a56..d305023 100644 --- a/src/types/business.d.ts +++ b/src/types/business.d.ts @@ -10,6 +10,10 @@ declare namespace Auth { // type RoleType = keyof typeof import('@/enum').EnumUserRole; /** 用户信息 */ + interface loginToken { + token: string; + } + interface UserInfo { /** 用户id */ userId: string; @@ -23,16 +27,12 @@ declare namespace Auth { role: RoleType; /* 密码 */ password: string; - /* token */ - token: string; - /* 权限路由 */ - userRoutes: UserInfoPermissions[]; - } - interface UserInfoPermissions { - name: string; - path: string; - meta: AppRoute.RouteMeta; - children?: UserInfoPermissions[]; - redirect: string; } + // interface userRoutes { + // name: string; + // path: string; + // meta: AppRoute.RouteMeta; + // children?: userRoutes[]; + // redirect: string; + // } } diff --git a/src/types/route.d.ts b/src/types/route.d.ts index fcfe4cf..e5553b6 100644 --- a/src/types/route.d.ts +++ b/src/types/route.d.ts @@ -18,7 +18,7 @@ declare namespace AppRoute { /** 子路由 */ children?: Route[]; /** 路由描述 */ - meta?: RouteMeta; + meta: RouteMeta; /** 路由属性 */ // props?: boolean | Record | ((to: any) => Record); } diff --git a/src/views/test/test1.vue b/src/views/test/test1/index.vue similarity index 100% rename from src/views/test/test1.vue rename to src/views/test/test1/index.vue diff --git a/src/views/test/test2.vue b/src/views/test/test2/index.vue similarity index 100% rename from src/views/test/test2.vue rename to src/views/test/test2/index.vue diff --git a/src/views/test/test3.vue b/src/views/test/test3/index.vue similarity index 100% rename from src/views/test/test3.vue rename to src/views/test/test3/index.vue