From b218ccc9cce2ce1363c4a21d22b4d69c43c7b2dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=88=E5=87=AF=E9=BE=99?= <502431556@qq.com>
Date: Tue, 11 Jan 2022 10:47:10 +0800
Subject: [PATCH] feat(router): Add dynamic routing
---
src/api/login/index.ts | 5 +-
src/hooks/web/useAxios.ts | 10 +-
src/hooks/web/useNProgress.ts | 31 +--
src/layout/Layout.vue | 19 +-
src/locales/en.ts | 11 +-
src/locales/zh-CN.ts | 11 +-
src/permission.ts | 44 ++--
src/router/helper.ts | 27 ---
src/router/index.ts | 125 +++++------
src/store/modules/permission.ts | 254 ++++++++---------------
src/store/modules/tagsView.ts | 0
src/utils/routerHelper.ts | 192 +++++++++++++++++
src/views/Level/Menu111.vue | 2 +-
src/views/Login/components/LoginForm.vue | 64 +++++-
types/global.d.ts | 6 +-
15 files changed, 478 insertions(+), 323 deletions(-)
delete mode 100644 src/router/helper.ts
create mode 100644 src/store/modules/tagsView.ts
create mode 100644 src/utils/routerHelper.ts
diff --git a/src/api/login/index.ts b/src/api/login/index.ts
index 96ccdb5..a676dfe 100644
--- a/src/api/login/index.ts
+++ b/src/api/login/index.ts
@@ -4,5 +4,8 @@ import type { UserLoginType } from './types'
const { request } = useAxios()
export const loginApi = (data: UserLoginType) => {
- return request({ url: '/user/login', method: 'post', data })
+ return request({ url: '/user/login', method: 'post', data } as AxiosConfig<
+ Recordable,
+ UserLoginType
+ >)
}
diff --git a/src/hooks/web/useAxios.ts b/src/hooks/web/useAxios.ts
index 5bfe160..3151fb1 100644
--- a/src/hooks/web/useAxios.ts
+++ b/src/hooks/web/useAxios.ts
@@ -7,14 +7,8 @@ import { config } from '@/config/axios/config'
const { default_headers } = config
export function useAxios() {
- function request({
- url,
- method,
- params,
- data,
- headersType,
- responseType
- }: AxiosConfig): AxiosPromise {
+ function request(option: AxiosConfig): AxiosPromise {
+ const { url, method, params, data, headersType, responseType } = option
return service({
url: url,
method,
diff --git a/src/hooks/web/useNProgress.ts b/src/hooks/web/useNProgress.ts
index 6293e51..10d9a21 100644
--- a/src/hooks/web/useNProgress.ts
+++ b/src/hooks/web/useNProgress.ts
@@ -1,4 +1,4 @@
-import { watch, ref, nextTick, unref } from 'vue'
+import { nextTick, unref } from 'vue'
import type { NProgressOptions } from 'nprogress'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
@@ -7,26 +7,27 @@ import { useCssVar } from '@vueuse/core'
const primaryColor = useCssVar('--el-color-primary', document.documentElement)
export function useNProgress() {
- const isLoading = ref(false)
NProgress.configure({ showSpinner: false } as NProgressOptions)
+ initColor()
- watch(
- () => isLoading.value,
- async (loading: boolean) => {
- loading ? NProgress.start() : NProgress.done()
- await nextTick()
- const bar = document.getElementById('nprogress')?.getElementsByClassName('bar')[0] as ElRef
- if (bar) {
- bar.style.background = unref(primaryColor.value)
- }
+ async function initColor() {
+ await nextTick()
+ const bar = document.getElementById('nprogress')?.getElementsByClassName('bar')[0] as ElRef
+ if (bar) {
+ bar.style.background = unref(primaryColor.value)
}
- )
+ }
- function toggle() {
- isLoading.value = !isLoading.value
+ function start() {
+ NProgress.start()
+ }
+
+ function done() {
+ NProgress.done()
}
return {
- toggle
+ start,
+ done
}
}
diff --git a/src/layout/Layout.vue b/src/layout/Layout.vue
index 2046fc0..ecbd19a 100644
--- a/src/layout/Layout.vue
+++ b/src/layout/Layout.vue
@@ -1,9 +1,16 @@
-
+
-
+
+
+
+
+
+
+
diff --git a/src/locales/en.ts b/src/locales/en.ts
index 4acc3e3..98bac0c 100644
--- a/src/locales/en.ts
+++ b/src/locales/en.ts
@@ -20,12 +20,19 @@ export default {
login: 'Sign in',
otherLogin: 'Sign in with',
remember: 'Remember me',
- forgetPassword: 'Forget password'
+ forgetPassword: 'Forget password',
+ usernamePlaceholder: 'username is admin or test',
+ passwordPlaceholder: 'password is admin or test'
},
router: {
login: 'Login',
level: 'Multi level menu',
- menu: 'Menu'
+ menu: 'Menu',
+ menu1: 'Menu1',
+ menu11: 'Menu1-1',
+ menu111: 'Menu1-1-1',
+ menu12: 'Menu1-2',
+ menu2: 'Menu2'
},
mock: {
loginErr: 'Wrong account or password'
diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts
index 3fbf63f..1f70e80 100644
--- a/src/locales/zh-CN.ts
+++ b/src/locales/zh-CN.ts
@@ -20,12 +20,19 @@ export default {
login: '登录',
otherLogin: '其他登录方式',
remember: '记住我',
- forgetPassword: '忘记密码'
+ forgetPassword: '忘记密码',
+ usernamePlaceholder: '用户名为 admin 或者 test ',
+ passwordPlaceholder: '密码为 admin 或者 test '
},
router: {
login: '登录',
level: '多级菜单',
- menu: '菜单'
+ menu: '菜单',
+ menu1: '菜单1',
+ menu11: '菜单1-1',
+ menu111: '菜单1-1-1',
+ menu12: '菜单1-2',
+ menu2: '菜单2'
},
mock: {
loginErr: '账号或密码错误'
diff --git a/src/permission.ts b/src/permission.ts
index f97860f..de52166 100644
--- a/src/permission.ts
+++ b/src/permission.ts
@@ -1,40 +1,40 @@
import router from './router'
import { useAppStoreWithOut } from '@/store/modules/app'
import { useCache } from '@/hooks/web/useCache'
-// import type { RouteRecordRaw } from 'vue-router'
+import type { RouteRecordRaw } from 'vue-router'
import { useTitle } from '@/hooks/web/useTitle'
import { useNProgress } from '@/hooks/web/useNProgress'
+import { usePermissionStoreWithOut } from '@/store/modules/permission'
+
+const permissionStore = usePermissionStoreWithOut()
const appStore = useAppStoreWithOut()
const { wsCache } = useCache()
-const { toggle } = useNProgress()
+const { start, done } = useNProgress()
const whiteList = ['/login'] // 不重定向白名单
-router.beforeEach((to, from, next) => {
- console.log(from)
- toggle()
+router.beforeEach(async (to, from, next) => {
+ start()
if (wsCache.get(appStore.getUserInfo)) {
if (to.path === '/login') {
next({ path: '/' })
} else {
- // if (permissionStore.getIsAddRouters) {
- // next()
- // return
- // }
- // permissionStore.generateRoutes().then(() => {
- // permissionStore.addRouters.forEach(async (route) => {
- // await router.addRoute(route as RouteRecordRaw) // 动态添加可访问路由表
- // })
- // const redirectPath = from.query.redirect || to.path
- // const redirect = decodeURIComponent(redirectPath as string)
- // const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect }
- // permissionStore.setIsAddRouters(true)
- // next(nextData)
- // })
- next()
+ if (permissionStore.getIsAddRouters) {
+ next()
+ return
+ }
+ await permissionStore.generateRoutes()
+ permissionStore.getAddRouters.forEach((route) => {
+ router.addRoute(route as unknown as RouteRecordRaw) // 动态添加可访问路由表
+ })
+ const redirectPath = from.query.redirect || to.path
+ const redirect = decodeURIComponent(redirectPath as string)
+ const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect }
+ permissionStore.setIsAddRouters(true)
+ next(nextData)
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
@@ -45,7 +45,7 @@ router.beforeEach((to, from, next) => {
}
})
-router.afterEach(async (to) => {
+router.afterEach((to) => {
useTitle(to?.meta?.title as string)
- toggle() // 结束Progress
+ done() // 结束Progress
})
diff --git a/src/router/helper.ts b/src/router/helper.ts
deleted file mode 100644
index 39c087f..0000000
--- a/src/router/helper.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import ParentLayout from '@/components/ParentView/index.vue'
-import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router'
-
-export const getParentLayout = (name: string) => {
- return () =>
- new Promise((resolve) => {
- resolve({
- ...ParentLayout,
- name
- })
- })
-}
-
-export function getRoute(route: RouteLocationNormalized): RouteLocationNormalized {
- if (!route) return route
- const { matched, ...opt } = route
- return {
- ...opt,
- matched: (matched
- ? matched.map((item) => ({
- meta: item.meta,
- name: item.name,
- path: item.path
- }))
- : undefined) as RouteRecordNormalized[]
- }
-}
diff --git a/src/router/index.ts b/src/router/index.ts
index 12daa2d..ed4f156 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,8 +1,9 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
import type { App } from 'vue'
-// import { getParentLayout } from './helper'
+import { getParentLayout } from '@/utils/routerHelper'
import { useI18n } from '@/hooks/web/useI18n'
+
const { t } = useI18n()
/* Layout */
@@ -15,7 +16,7 @@ export const constantRouterMap: AppRouteRecordRaw[] = [
name: 'Redirect',
children: [
{
- path: '/redirect/:path*',
+ path: '/redirect/:path(.*)',
name: 'Redirect',
component: () => import('@/views/Redirect/Redirect.vue'),
meta: {}
@@ -37,66 +38,66 @@ export const constantRouterMap: AppRouteRecordRaw[] = [
}
]
-// export const asyncRouterMap: AppRouteRecordRaw[] = [
-// {
-// path: '/level',
-// component: Layout,
-// redirect: '/level/menu1/menu1-1/menu1-1-1',
-// name: 'Level',
-// meta: {
-// title: t('router.level')
-// },
-// children: [
-// {
-// path: 'menu1',
-// name: 'Menu1',
-// component: getParentLayout('Menu1'),
-// redirect: '/level/menu1/menu1-1/menu1-1-1',
-// meta: {
-// title: `${t('router.menu')}1`
-// },
-// children: [
-// {
-// path: 'menu1-1',
-// name: 'Menu11',
-// component: getParentLayout('Menu11Demo'),
-// redirect: '/level/menu1/menu1-1/menu1-1-1',
-// meta: {
-// title: `${t('router.menu')}1-1`,
-// alwaysShow: true
-// },
-// children: [
-// {
-// path: 'menu1-1-1',
-// name: 'Menu111',
-// component: () => import('@/views/Level/Menu111.vue'),
-// meta: {
-// title: `${t('router.menu')}1-1-1`
-// }
-// }
-// ]
-// },
-// {
-// path: 'menu1-2',
-// name: 'Menu12',
-// component: () => import('@/views/Level/Menu12.vue'),
-// meta: {
-// title: `${t('router.menu')}1-2`
-// }
-// }
-// ]
-// },
-// {
-// path: 'menu2',
-// name: 'Menu2Demo',
-// component: () => import('@/views/Level/Menu2.vue'),
-// meta: {
-// title: `${t('router.menu')}2`
-// }
-// }
-// ]
-// }
-// ]
+export const asyncRouterMap: AppRouteRecordRaw[] = [
+ {
+ path: '/level',
+ component: Layout,
+ redirect: '/level/menu1/menu1-1/menu1-1-1',
+ name: 'Level',
+ meta: {
+ title: t('router.level')
+ },
+ children: [
+ {
+ path: 'menu1',
+ name: 'Menu1',
+ component: getParentLayout(),
+ redirect: '/level/menu1/menu1-1/menu1-1-1',
+ meta: {
+ title: t('router.menu1')
+ },
+ children: [
+ {
+ path: 'menu1-1',
+ name: 'Menu11',
+ component: getParentLayout(),
+ redirect: '/level/menu1/menu1-1/menu1-1-1',
+ meta: {
+ title: t('router.menu11'),
+ alwaysShow: true
+ },
+ children: [
+ {
+ path: 'menu1-1-1',
+ name: 'Menu111',
+ component: () => import('@/views/Level/Menu111.vue'),
+ meta: {
+ title: t('router.menu111')
+ }
+ }
+ ]
+ },
+ {
+ path: 'menu1-2',
+ name: 'Menu12',
+ component: () => import('@/views/Level/Menu12.vue'),
+ meta: {
+ title: t('router.menu12')
+ }
+ }
+ ]
+ },
+ {
+ path: 'menu2',
+ name: 'Menu2Demo',
+ component: () => import('@/views/Level/Menu2.vue'),
+ meta: {
+ title: t('router.menu2')
+ }
+ }
+ ]
+ }
+]
const router = createRouter({
history: createWebHashHistory(),
diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts
index 918ef5f..a6a29f8 100644
--- a/src/store/modules/permission.ts
+++ b/src/store/modules/permission.ts
@@ -1,180 +1,96 @@
-// import { defineStore } from 'pinia'
-// import { asyncRouterMap, constantRouterMap } from '@/router'
+import { defineStore } from 'pinia'
+import { asyncRouterMap, constantRouterMap } from '@/router'
// import { useCache } from '@/hooks/web/useCache'
-// import { getParentLayout } from '@/router/helper'
-// import { store } from '../index'
+import { flatMultiLevelRoutes } from '@/utils/routerHelper'
+// import { generateRoutesFn1, generateRoutesFn2, flatMultiLevelRoutes } from '@/utils/routerHelper'
+import { store } from '../index'
// import { useAppStoreWithOut } from '@/store/modules/app'
-// import { isUrl } from '@/utils/is'
-// import { deepClone } from '@/utils'
+import { cloneDeep } from 'lodash-es'
// const { wsCache } = useCache()
// const appStore = useAppStoreWithOut()
-// const modules = import.meta.glob('../../views/**/*.{vue,tsx}')
+export interface PermissionState {
+ routers: AppRouteRecordRaw[]
+ addRouters: AppRouteRecordRaw[]
+ isAddRouters: boolean
+ activeTab: string
+ menuTabRouters: AppRouteRecordRaw[]
+}
-// /* Layout */
-// const Layout = () => import('@/layout/index.vue')
+export const usePermissionStore = defineStore({
+ id: 'permission',
+ state: (): PermissionState => ({
+ routers: [],
+ addRouters: [],
+ isAddRouters: false,
+ menuTabRouters: [],
+ activeTab: ''
+ }),
+ getters: {
+ getRouters(): AppRouteRecordRaw[] {
+ return this.routers
+ },
+ getAddRouters(): AppRouteRecordRaw[] {
+ return flatMultiLevelRoutes(cloneDeep(this.addRouters))
+ },
+ getIsAddRouters(): boolean {
+ return this.isAddRouters
+ },
+ getActiveTab(): string {
+ return this.activeTab
+ },
+ getMenuTabRouters(): AppRouteRecordRaw[] {
+ return this.menuTabRouters
+ }
+ },
+ actions: {
+ generateRoutes(): Promise {
+ return new Promise((resolve) => {
+ // 路由权限控制,如果不需要权限控制,请注释
+ // let routerMap: AppRouteRecordRaw[] = []
+ // if (wsCache.get(appStore.getUserInfo).username === 'admin') {
+ // // 模拟前端控制权限
+ // routerMap = generateRoutesFn1(cloneDeep(asyncRouterMap))
+ // } else {
+ // // 模拟后端控制权限
+ // routerMap = generateRoutesFn2(wsCache.get(appStore.getUserInfo).checkedNodes)
+ // }
-// export interface PermissionState {
-// routers: AppRouteRecordRaw[]
-// addRouters: AppRouteRecordRaw[]
-// isAddRouters: boolean
-// activeTab: string
-// menuTabRouters: AppRouteRecordRaw[]
-// }
+ // 不需要权限控制
+ const routerMap: AppRouteRecordRaw[] = cloneDeep(asyncRouterMap)
-// export const usePermissionStore = defineStore({
-// id: 'permission',
-// state: (): PermissionState => ({
-// routers: [],
-// addRouters: [],
-// isAddRouters: false,
-// menuTabRouters: [],
-// activeTab: ''
-// }),
-// getters: {
-// getRouters(): AppRouteRecordRaw[] {
-// return this.routers
-// },
-// getAddRouters(): AppRouteRecordRaw[] {
-// return this.addRouters
-// },
-// getIsAddRouters(): boolean {
-// return this.isAddRouters
-// },
-// getActiveTab(): string {
-// return this.activeTab
-// },
-// getMenuTabRouters(): AppRouteRecordRaw[] {
-// return this.menuTabRouters
-// }
-// },
-// actions: {
-// generateRoutes(): Promise {
-// return new Promise((resolve) => {
-// // 路由权限控制
-// let routerMap: AppRouteRecordRaw[] = []
-// if (wsCache.get(appStore.getUserInfo).roleName === 'admin') {
-// // 模拟前端控制权限
-// routerMap = generateRoutesFn(deepClone(asyncRouterMap, ['component']))
-// } else {
-// // 模拟后端控制权限
-// routerMap = getFilterRoutes(wsCache.get(appStore.getUserInfo).checkedNodes)
-// }
-// // const routerMap: AppRouteRecordRaw[] = generateRoutesFn(deepClone(asyncRouterMap, ['component']))
-// // 动态路由,404一定要放到最后面
-// this.addRouters = routerMap.concat([
-// {
-// path: '/:path(.*)*',
-// redirect: '/404',
-// name: '404',
-// meta: {
-// hidden: true,
-// breadcrumb: false
-// }
-// }
-// ])
-// // 渲染菜单的所有路由
-// this.routers = deepClone(constantRouterMap, ['component']).concat(routerMap)
-// resolve()
-// })
-// },
-// setIsAddRouters(state: boolean): void {
-// this.isAddRouters = state
-// },
-// setMenuTabRouters(routers: AppRouteRecordRaw[]): void {
-// this.menuTabRouters = routers
-// },
-// setAcitveTab(activeTab: string): void {
-// this.activeTab = activeTab
-// }
-// }
-// })
+ // 动态路由,404一定要放到最后面
+ this.addRouters = routerMap
+ // .concat([
+ // {
+ // path: '/:path(.*)*',
+ // redirect: '/404',
+ // name: '404',
+ // meta: {
+ // hidden: true,
+ // breadcrumb: false
+ // }
+ // }
+ // ])
+ // 渲染菜单的所有路由
+ this.routers = cloneDeep(constantRouterMap).concat(routerMap)
+ resolve()
+ })
+ },
+ setIsAddRouters(state: boolean): void {
+ this.isAddRouters = state
+ },
+ setMenuTabRouters(routers: AppRouteRecordRaw[]): void {
+ this.menuTabRouters = routers
+ },
+ setAcitveTab(activeTab: string): void {
+ this.activeTab = activeTab
+ }
+ }
+})
-// // 路由过滤,主要用于权限控制
-// function generateRoutesFn(routes: AppRouteRecordRaw[], basePath = '/'): AppRouteRecordRaw[] {
-// const res: AppRouteRecordRaw[] = []
-
-// for (const route of routes) {
-// // skip some route
-// if (route.meta && route.meta.hidden && !route.meta.showMainRoute) {
-// continue
-// }
-
-// let onlyOneChild: Nullable = null
-
-// if (route.children && route.children.length === 1 && !route.meta.alwaysShow) {
-// onlyOneChild = (
-// isUrl(route.children[0].path)
-// ? route.children[0].path
-// : path.resolve(path.resolve(basePath, route.path), route.children[0].path)
-// ) as string
-// }
-
-// let data: Nullable = null
-
-// // 如不需要路由权限,可注释以下逻辑
-// // 权限过滤,通过获取登录信息里面的角色权限,动态的渲染菜单。
-// const list = wsCache.get(appStore.getUserInfo).checkedNodes
-// // 开发者可以根据实际情况进行扩展
-// for (const item of list) {
-// // 通过路径去匹配
-// if (isUrl(item.path) && (onlyOneChild === item.path || route.path === item.path)) {
-// data = Object.assign({}, route)
-// } else {
-// const routePath = path.resolve(basePath, onlyOneChild || route.path)
-// if (routePath === item.path || (route.meta && route.meta.followRoute === item.path)) {
-// data = Object.assign({}, route)
-// }
-// }
-// }
-// // 如不需要路由权限,解注释下面一行
-// // data = Object.assign({}, route)
-
-// // recursive child routes
-// if (route.children && data) {
-// data.children = generateRoutesFn(route.children, path.resolve(basePath, data.path))
-// }
-// if (data) {
-// res.push(data as AppRouteRecordRaw)
-// }
-// }
-// return res
-// }
-
-// // 模拟后端过滤路由
-// function getFilterRoutes(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] {
-// const res: AppRouteRecordRaw[] = []
-
-// for (const route of routes) {
-// const data: AppRouteRecordRaw = {
-// path: route.path,
-// name: route.name,
-// redirect: route.redirect,
-// meta: {}
-// }
-// data.meta = Object.assign({}, route.meta || {}, { title: route.meta.title })
-// if (route.component) {
-// // 动态加载路由文件,可根据实际情况进行自定义逻辑
-// const component = route.component as string
-// data.component = (
-// component === '#'
-// ? Layout
-// : component.includes('##')
-// ? getParentLayout(component.split('##')[1])
-// : modules[`../../${route.component}.vue`] || modules[`../../${route.component}.tsx`]
-// )
-// }
-// // recursive child routes
-// if (route.children) {
-// data.children = getFilterRoutes(route.children)
-// }
-// res.push(data as AppRouteRecordRaw)
-// }
-// return res
-// }
-
-// export function usePermissionStoreWithOut() {
-// return usePermissionStore(store)
-// }
+export function usePermissionStoreWithOut() {
+ return usePermissionStore(store)
+}
diff --git a/src/store/modules/tagsView.ts b/src/store/modules/tagsView.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/utils/routerHelper.ts b/src/utils/routerHelper.ts
new file mode 100644
index 0000000..04015b2
--- /dev/null
+++ b/src/utils/routerHelper.ts
@@ -0,0 +1,192 @@
+import { createRouter, createWebHashHistory } from 'vue-router'
+import type { Router, RouteLocationNormalized, RouteRecordNormalized } from 'vue-router'
+import { isUrl } from '@/utils/is'
+import { useCache } from '@/hooks/web/useCache'
+import { useAppStoreWithOut } from '@/store/modules/app'
+import { omit, cloneDeep } from 'lodash-es'
+
+const appStore = useAppStoreWithOut()
+
+const { wsCache } = useCache()
+
+const modules = import.meta.glob('../../views/**/*.{vue,tsx}')
+
+/* Layout */
+const Layout = () => import('@/layout/index.vue')
+
+export const getParentLayout = () => {
+ return () =>
+ new Promise((resolve) => {
+ resolve({
+ name: 'ParentLayout'
+ })
+ })
+}
+
+export function getRoute(route: RouteLocationNormalized): RouteLocationNormalized {
+ if (!route) return route
+ const { matched, ...opt } = route
+ return {
+ ...opt,
+ matched: (matched
+ ? matched.map((item) => ({
+ meta: item.meta,
+ name: item.name,
+ path: item.path
+ }))
+ : undefined) as RouteRecordNormalized[]
+ }
+}
+
+// 前端控制路由生成
+export function generateRoutesFn1(
+ routes: AppRouteRecordRaw[],
+ basePath = '/'
+): AppRouteRecordRaw[] {
+ const res: AppRouteRecordRaw[] = []
+
+ for (const route of routes) {
+ // skip some route
+ if (route.meta && route.meta.hidden && !route.meta.showMainRoute) {
+ continue
+ }
+
+ let data: Nullable = null
+
+ let onlyOneChild: Nullable = null
+
+ if (route.children && route.children.length === 1 && !route.meta.alwaysShow) {
+ onlyOneChild = (
+ isUrl(route.children[0].path)
+ ? route.children[0].path
+ : pathResolve(pathResolve(basePath, route.path), route.children[0].path)
+ ) as string
+ }
+
+ // 权限过滤,通过获取登录信息里面的角色权限,动态的渲染菜单。
+ const list = wsCache.get(appStore.getUserInfo).checkedNodes
+ // 开发者可以根据实际情况进行扩展
+ for (const item of list) {
+ // 通过路径去匹配
+ if (isUrl(item.path) && (onlyOneChild === item.path || route.path === item.path)) {
+ data = Object.assign({}, route)
+ } else {
+ const routePath = pathResolve(basePath, onlyOneChild || route.path)
+ if (routePath === item.path || (route.meta && route.meta.followRoute === item.path)) {
+ data = Object.assign({}, route)
+ }
+ }
+ }
+
+ // recursive child routes
+ if (route.children && data) {
+ data.children = generateRoutesFn1(route.children, pathResolve(basePath, data.path))
+ }
+ if (data) {
+ res.push(data as AppRouteRecordRaw)
+ }
+ }
+ return res
+}
+
+// 后端控制路由生成
+export function generateRoutesFn2(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] {
+ const res: AppRouteRecordRaw[] = []
+
+ for (const route of routes) {
+ const data: AppRouteRecordRaw = {
+ path: route.path,
+ name: route.name,
+ redirect: route.redirect,
+ meta: route.meta
+ }
+ if (route.component) {
+ // 动态加载路由文件,可根据实际情况进行自定义逻辑
+ const component = route.component as string
+ data.component =
+ component === '#'
+ ? Layout
+ : component.includes('##')
+ ? getParentLayout()
+ : modules[`../../${route.component}.vue`] || modules[`../../${route.component}.tsx`]
+ }
+ // recursive child routes
+ if (route.children) {
+ data.children = generateRoutesFn2(route.children)
+ }
+ res.push(data as AppRouteRecordRaw)
+ }
+ return res
+}
+
+export function pathResolve(parentPath: string, path: string) {
+ return `${parentPath}/${path}`
+}
+
+// 路由降级
+export function flatMultiLevelRoutes(routes: AppRouteRecordRaw[]) {
+ const modules: AppRouteRecordRaw[] = cloneDeep(routes)
+ for (let index = 0; index < modules.length; index++) {
+ const route = modules[index]
+ if (!isMultipleRoute(route)) {
+ continue
+ }
+ promoteRouteLevel(route)
+ }
+ return modules
+}
+
+// 层级是否大于2
+function isMultipleRoute(route: AppRouteRecordRaw) {
+ if (!route || !Reflect.has(route, 'children') || !route.children?.length) {
+ return false
+ }
+
+ const children = route.children
+
+ let flag = false
+ for (let index = 0; index < children.length; index++) {
+ const child = children[index]
+ if (child.children?.length) {
+ flag = true
+ break
+ }
+ }
+ return flag
+}
+
+// 路由降级
+function promoteRouteLevel(route: AppRouteRecordRaw) {
+ let router: Router | null = createRouter({
+ routes: [route as unknown as RouteRecordNormalized],
+ history: createWebHashHistory()
+ })
+
+ const routes = router.getRoutes()
+ addToChildren(routes, route.children || [], route)
+ router = null
+
+ route.children = route.children?.map((item) => omit(item, 'children'))
+}
+
+// 添加所有子菜单
+function addToChildren(
+ routes: RouteRecordNormalized[],
+ children: AppRouteRecordRaw[],
+ routeModule: AppRouteRecordRaw
+) {
+ for (let index = 0; index < children.length; index++) {
+ const child = children[index]
+ const route = routes.find((item) => item.name === child.name)
+ if (!route) {
+ continue
+ }
+ routeModule.children = routeModule.children || []
+ if (!routeModule.children.find((item) => item.name === route.name)) {
+ routeModule.children?.push(route as unknown as AppRouteRecordRaw)
+ }
+ if (child.children?.length) {
+ addToChildren(routes, child.children, routeModule)
+ }
+ }
+}
diff --git a/src/views/Level/Menu111.vue b/src/views/Level/Menu111.vue
index 05fcc49..f08e543 100644
--- a/src/views/Level/Menu111.vue
+++ b/src/views/Level/Menu111.vue
@@ -1,5 +1,5 @@
- Menu11
+ Menu111
diff --git a/src/views/Login/components/LoginForm.vue b/src/views/Login/components/LoginForm.vue
index c6cf9a0..ce6dfae 100644
--- a/src/views/Login/components/LoginForm.vue
+++ b/src/views/Login/components/LoginForm.vue
@@ -1,5 +1,5 @@
@@ -125,16 +167,28 @@ async function signIn() {
-
-
+
+
diff --git a/types/global.d.ts b/types/global.d.ts
index bad1971..dfcb44a 100644
--- a/types/global.d.ts
+++ b/types/global.d.ts
@@ -25,9 +25,9 @@ declare type AxiosMethod = 'get' | 'post' | 'delete' | 'put'
declare type AxiosResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'
-declare type AxiosConfig = {
- params?: Recordable
- data?: Recordable
+declare type AxiosConfig = {
+ params?: T
+ data?: K
url?: string
method?: AxiosMethod
headersType?: string