diff --git a/src/api/login/index.ts b/src/api/login/index.ts index 96ccdb526..a676dfe69 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 5bfe16063..3151fb136 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 6293e5160..10d9a219d 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 start() { + NProgress.start() + } - function toggle() { - isLoading.value = !isLoading.value + function done() { + NProgress.done() } return { - toggle + start, + done } } diff --git a/src/layout/Layout.vue b/src/layout/Layout.vue index 2046fc050..ecbd19a73 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 4acc3e380..98bac0c38 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 3fbf63f45..1f70e8056 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 f97860fa1..de52166b4 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 39c087fa3..000000000 --- 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 12daa2dc2..ed4f15666 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 918ef5fef..a6a29f83e 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}') - -// /* Layout */ -// const Layout = () => import('@/layout/index.vue') - -// export interface PermissionState { -// routers: AppRouteRecordRaw[] -// addRouters: AppRouteRecordRaw[] -// isAddRouters: boolean -// activeTab: string -// menuTabRouters: AppRouteRecordRaw[] -// } - -// 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 -// } -// } -// }) - -// // 路由过滤,主要用于权限控制 -// 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 interface PermissionState { + routers: AppRouteRecordRaw[] + addRouters: AppRouteRecordRaw[] + isAddRouters: boolean + activeTab: string + menuTabRouters: AppRouteRecordRaw[] +} + +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) + // } + + // 不需要权限控制 + const routerMap: AppRouteRecordRaw[] = cloneDeep(asyncRouterMap) + + // 动态路由,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 + } + } +}) + +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 000000000..e69de29bb diff --git a/src/utils/routerHelper.ts b/src/utils/routerHelper.ts new file mode 100644 index 000000000..04015b279 --- /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 05fcc496f..f08e54358 100644 --- a/src/views/Level/Menu111.vue +++ b/src/views/Level/Menu111.vue @@ -1,5 +1,5 @@ diff --git a/src/views/Login/components/LoginForm.vue b/src/views/Login/components/LoginForm.vue index c6cf9a045..ce6dfaeac 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() {