mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
refactor: 动态路由重构
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
# 本地环境
|
# 本地环境
|
||||||
ENV = 'development'
|
ENV = 'development'
|
||||||
|
|
||||||
|
VITE_OPEN = true
|
||||||
|
|
||||||
# 本地环境接口地址
|
# 本地环境接口地址
|
||||||
VITE_API_URL = '/api'
|
VITE_API_URL = '/api'
|
||||||
|
|
||||||
|
|||||||
@@ -73,9 +73,9 @@ class SysSocket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
destory() {
|
destory() {
|
||||||
this.socket.close();
|
this.socket?.close();
|
||||||
this.socket = null;
|
this.socket = null;
|
||||||
this.categoryHandlers.clear();
|
this.categoryHandlers?.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import router from '../router';
|
import router from '../router';
|
||||||
import { clearUser, getClientId, getToken } from './utils/storage';
|
import { getClientId, getToken } from './utils/storage';
|
||||||
import { templateResolve } from './utils/string';
|
import { templateResolve } from './utils/string';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { createFetch } from '@vueuse/core';
|
import { createFetch } from '@vueuse/core';
|
||||||
@@ -7,6 +7,7 @@ import Api from './Api';
|
|||||||
import { Result, ResultEnum } from './request';
|
import { Result, ResultEnum } from './request';
|
||||||
import config from './config';
|
import config from './config';
|
||||||
import { unref } from 'vue';
|
import { unref } from 'vue';
|
||||||
|
import { URL_401 } from '../router/staticRouter';
|
||||||
|
|
||||||
const baseUrl: string = config.baseApiUrl;
|
const baseUrl: string = config.baseApiUrl;
|
||||||
|
|
||||||
@@ -125,11 +126,10 @@ export function useApiFetch<T>(api: Api, params: any = null, reqOptions: Request
|
|||||||
|
|
||||||
// 如果提示没有权限,则跳转至无权限页面
|
// 如果提示没有权限,则跳转至无权限页面
|
||||||
if (result.code === ResultEnum.NO_PERMISSION) {
|
if (result.code === ResultEnum.NO_PERMISSION) {
|
||||||
clearUser();
|
|
||||||
router.push({
|
router.push({
|
||||||
path: '/401',
|
path: URL_401,
|
||||||
});
|
});
|
||||||
return;
|
return Promise.reject(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果返回的code不为成功,则会返回对应的错误msg,则直接统一通知即可。忽略登录超时或没有权限的提示(直接跳转至401页面)
|
// 如果返回的code不为成功,则会返回对应的错误msg,则直接统一通知即可。忽略登录超时或没有权限的提示(直接跳转至401页面)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import Api from '@/common/Api';
|
import Api from '@/common/Api';
|
||||||
import { ElMessage } from 'element-plus';
|
|
||||||
import { reactive, toRefs, toValue } from 'vue';
|
import { reactive, toRefs, toValue } from 'vue';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,8 +53,6 @@ export const usePageTable = (
|
|||||||
} else {
|
} else {
|
||||||
state.tableData = res;
|
state.tableData = res;
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
|
||||||
ElMessage.error(error?.message);
|
|
||||||
} finally {
|
} finally {
|
||||||
state.loading = false;
|
state.loading = false;
|
||||||
}
|
}
|
||||||
|
|||||||
157
mayfly_go_web/src/router/dynamicRouter.ts
Normal file
157
mayfly_go_web/src/router/dynamicRouter.ts
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
import 'nprogress/nprogress.css';
|
||||||
|
import { clearSession, getToken } from '@/common/utils/storage';
|
||||||
|
import openApi from '@/common/openApi';
|
||||||
|
import { useUserInfo } from '@/store/userInfo';
|
||||||
|
import { useRoutesList } from '@/store/routesList';
|
||||||
|
import { useKeepALiveNames } from '@/store/keepAliveNames';
|
||||||
|
import router from '.';
|
||||||
|
import { RouteRecordRaw } from 'vue-router';
|
||||||
|
import { LAYOUT_ROUTE_NAME } from './staticRouter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取目录下的 .vue、.tsx 全部文件
|
||||||
|
* @method import.meta.glob
|
||||||
|
* @link 参考:https://cn.vitejs.dev/guide/features.html#json
|
||||||
|
*/
|
||||||
|
const viewsModules: Record<string, Function> = import.meta.glob(['../views/**/*.{vue,tsx}']);
|
||||||
|
const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...viewsModules });
|
||||||
|
|
||||||
|
// 后端控制路由:执行路由数据初始化
|
||||||
|
export async function initBackendRoutes() {
|
||||||
|
const token = getToken(); // 获取浏览器缓存 token 值
|
||||||
|
if (!token) {
|
||||||
|
// 无 token 停止执行下一步
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
useUserInfo().setUserInfo({});
|
||||||
|
// 获取路由
|
||||||
|
let menuRoute = await getBackEndControlRoutes();
|
||||||
|
|
||||||
|
const cacheList: Array<string> = [];
|
||||||
|
// 处理路由(component)
|
||||||
|
const routes = backEndRouterConverter(menuRoute, (router: any) => {
|
||||||
|
// 可能为false时不存在isKeepAlive属性
|
||||||
|
if (!router.meta.isKeepAlive) {
|
||||||
|
router.meta.isKeepAlive = false;
|
||||||
|
}
|
||||||
|
if (router.meta.isKeepAlive) {
|
||||||
|
cacheList.push(router.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
routes.forEach((item: any) => {
|
||||||
|
if (item.meta.isFull) {
|
||||||
|
// 菜单为全屏展示 (示例:数据大屏页面等)
|
||||||
|
router.addRoute(item as RouteRecordRaw);
|
||||||
|
} else {
|
||||||
|
// 要将嵌套路由添加到现有的路由中,可以将路由的 name 作为第一个参数传递给 router.addRoute(),这将有效地添加路由,就像通过 children 添加的一样
|
||||||
|
router.addRoute(LAYOUT_ROUTE_NAME, item as RouteRecordRaw);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useKeepALiveNames().setCacheKeepAlive(cacheList);
|
||||||
|
useRoutesList().setRoutesList(routes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
||||||
|
export async function getBackEndControlRoutes() {
|
||||||
|
try {
|
||||||
|
const menuAndPermission = await openApi.getPermissions();
|
||||||
|
// 赋值权限码,用于控制按钮等
|
||||||
|
useUserInfo().userInfo.permissions = menuAndPermission.permissions;
|
||||||
|
return menuAndPermission.menus;
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error('获取菜单权限信息失败', e);
|
||||||
|
clearSession();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RouterConvCallbackFunc = (router: any) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后端控制路由,后端返回路由 转换为vue route
|
||||||
|
*
|
||||||
|
* @description routes参数配置简介
|
||||||
|
* @param code(path) ==> route.path -> 路由菜单访问路径
|
||||||
|
* @param name ==> title,路由标题 相当于route.meta.title
|
||||||
|
*
|
||||||
|
* @param meta ==> 路由菜单元信息
|
||||||
|
* @param meta.routeName ==> route.name -> 路由 name (对应页面组件 name, 可用作 KeepAlive 缓存标识 && 按钮权限筛选)
|
||||||
|
* @param meta.redirect ==> route.redirect -> 路由重定向地址
|
||||||
|
* @param meta.component ==> 文件路径
|
||||||
|
* @param meta.icon ==> 菜单和面包屑对应的图标
|
||||||
|
* @param meta.isHide ==> 是否在菜单中隐藏 (通常列表详情页需要隐藏)
|
||||||
|
* @param meta.isFull ==> 菜单是否全屏 (示例:数据大屏页面)
|
||||||
|
* @param meta.isAffix ==> 菜单是否固定在标签页中 (首页通常是固定项)
|
||||||
|
* @param meta.isKeepAlive ==> 当前路由是否缓存
|
||||||
|
* @param meta.linkType ==> 外链类型, 内嵌: 以iframe展示、外链: 新标签打开
|
||||||
|
* @param meta.link ==> 外链地址
|
||||||
|
* */
|
||||||
|
export function backEndRouterConverter(routes: any, callbackFunc: RouterConvCallbackFunc = null as any, parentPath: string = '/') {
|
||||||
|
if (!routes) return [];
|
||||||
|
return routes.map((item: any) => {
|
||||||
|
if (!item.meta) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
// 将json字符串的meta转为对象
|
||||||
|
item.meta = JSON.parse(item.meta);
|
||||||
|
// 将meta.comoponet 解析为route.component
|
||||||
|
if (item.meta.component) {
|
||||||
|
item.component = dynamicImport(dynamicViewsModules, item.meta.component);
|
||||||
|
delete item.meta['component'];
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = item.code;
|
||||||
|
// 如果不是以 / 开头,则路径需要拼接父路径
|
||||||
|
if (!path.startsWith('/')) {
|
||||||
|
path = parentPath + '/' + path;
|
||||||
|
}
|
||||||
|
item.path = path;
|
||||||
|
delete item['code'];
|
||||||
|
|
||||||
|
// route.meta.title == resource.name
|
||||||
|
item.meta.title = item.name;
|
||||||
|
delete item['name'];
|
||||||
|
|
||||||
|
// route.name == resource.meta.routeName
|
||||||
|
item.name = item.meta.routeName;
|
||||||
|
delete item.meta['routeName'];
|
||||||
|
|
||||||
|
// route.redirect == resource.meta.redirect
|
||||||
|
if (item.meta.redirect) {
|
||||||
|
item.redirect = item.meta.redirect;
|
||||||
|
delete item.meta['redirect'];
|
||||||
|
}
|
||||||
|
// 存在回调,则执行回调
|
||||||
|
callbackFunc && callbackFunc(item);
|
||||||
|
item.children && backEndRouterConverter(item.children, callbackFunc, item.path);
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后端路由 component 转换函数
|
||||||
|
* @param dynamicViewsModules 获取目录下的 .vue、.tsx 全部文件
|
||||||
|
* @param component 当前要处理项 component
|
||||||
|
* @returns 返回处理成函数后的 component
|
||||||
|
*/
|
||||||
|
export function dynamicImport(dynamicViewsModules: Record<string, Function>, component: string) {
|
||||||
|
const keys = Object.keys(dynamicViewsModules);
|
||||||
|
const matchKeys = keys.filter((key) => {
|
||||||
|
const k = key.replace(/..\/views|../, '');
|
||||||
|
return k.startsWith(`${component}`) || k.startsWith(`/${component}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matchKeys?.length === 1) {
|
||||||
|
return dynamicViewsModules[matchKeys[0]];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchKeys?.length > 1) {
|
||||||
|
console.error('匹配到多个相似组件路径, 可添加后缀.vue或.tsx进行区分或者重命名组件名, 请调整...', matchKeys);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(`未匹配到[${component}]组件名对应的组件文件`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1,30 +1,21 @@
|
|||||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
|
import { createRouter, createWebHashHistory } from 'vue-router';
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress';
|
||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
import { clearSession, getToken } from '@/common/utils/storage';
|
import { getToken } from '@/common/utils/storage';
|
||||||
import { templateResolve } from '@/common/utils/string';
|
import { templateResolve } from '@/common/utils/string';
|
||||||
import { NextLoading } from '@/common/utils/loading';
|
import { NextLoading } from '@/common/utils/loading';
|
||||||
import { dynamicRoutes, staticRoutes, pathMatch } from './route';
|
import { staticRoutes, URL_LOGIN, URL_401, ROUTER_WHITE_LIST, errorRoutes } from './staticRouter';
|
||||||
import openApi from '@/common/openApi';
|
|
||||||
import syssocket from '@/common/syssocket';
|
import syssocket from '@/common/syssocket';
|
||||||
import pinia from '@/store/index';
|
import pinia from '@/store/index';
|
||||||
import { useThemeConfig } from '@/store/themeConfig';
|
import { useThemeConfig } from '@/store/themeConfig';
|
||||||
import { useUserInfo } from '@/store/userInfo';
|
import { useUserInfo } from '@/store/userInfo';
|
||||||
import { useRoutesList } from '@/store/routesList';
|
import { useRoutesList } from '@/store/routesList';
|
||||||
import { useKeepALiveNames } from '@/store/keepAliveNames';
|
import { initBackendRoutes } from './dynamicRouter';
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取目录下的 .vue、.tsx 全部文件
|
|
||||||
* @method import.meta.glob
|
|
||||||
* @link 参考:https://cn.vitejs.dev/guide/features.html#json
|
|
||||||
*/
|
|
||||||
const viewsModules: Record<string, Function> = import.meta.glob(['../views/**/*.{vue,tsx}']);
|
|
||||||
const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...viewsModules });
|
|
||||||
|
|
||||||
// 添加静态路由
|
// 添加静态路由
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHashHistory(),
|
history: createWebHashHistory(),
|
||||||
routes: staticRoutes,
|
routes: [...staticRoutes, ...errorRoutes],
|
||||||
});
|
});
|
||||||
|
|
||||||
// 前端控制路由:初始化方法,防止刷新时丢失
|
// 前端控制路由:初始化方法,防止刷新时丢失
|
||||||
@@ -35,133 +26,15 @@ export function initAllFun() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
useUserInfo().setUserInfo({});
|
useUserInfo().setUserInfo({});
|
||||||
router.addRoute(pathMatch); // 添加404界面
|
|
||||||
resetRoute(); // 删除/重置路由
|
resetRoute(); // 删除/重置路由
|
||||||
router.addRoute(dynamicRoutes[0]);
|
// router.addRoute(dynamicRoutes[0]);
|
||||||
|
// // 过滤权限菜单
|
||||||
// 过滤权限菜单
|
// useRoutesList().setRoutesList(dynamicRoutes[0].children);
|
||||||
useRoutesList().setRoutesList(dynamicRoutes[0].children);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 后端控制路由:执行路由数据初始化
|
|
||||||
export async function initBackEndControlRoutesFun() {
|
|
||||||
const token = getToken(); // 获取浏览器缓存 token 值
|
|
||||||
if (!token) {
|
|
||||||
// 无 token 停止执行下一步
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
useUserInfo().setUserInfo({});
|
|
||||||
// 获取路由
|
|
||||||
let menuRoute = await getBackEndControlRoutes();
|
|
||||||
|
|
||||||
const cacheList: Array<string> = [];
|
|
||||||
// 处理路由(component)
|
|
||||||
dynamicRoutes[0].children = backEndRouterConverter(menuRoute, (router: any) => {
|
|
||||||
// 可能为false时不存在isKeepAlive属性
|
|
||||||
if (!router.meta.isKeepAlive) {
|
|
||||||
router.meta.isKeepAlive = false;
|
|
||||||
}
|
|
||||||
if (router.meta.isKeepAlive) {
|
|
||||||
cacheList.push(router.name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
useKeepALiveNames().setCacheKeepAlive(cacheList);
|
|
||||||
|
|
||||||
// 添加404界面
|
|
||||||
router.addRoute(pathMatch);
|
|
||||||
resetRoute(); // 删除/重置路由
|
|
||||||
router.addRoute(dynamicRoutes[0] as unknown as RouteRecordRaw);
|
|
||||||
|
|
||||||
useRoutesList().setRoutesList(dynamicRoutes[0].children);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
|
||||||
export async function getBackEndControlRoutes() {
|
|
||||||
try {
|
|
||||||
const menuAndPermission = await openApi.getPermissions();
|
|
||||||
// 赋值权限码,用于控制按钮等
|
|
||||||
useUserInfo().userInfo.permissions = menuAndPermission.permissions;
|
|
||||||
return menuAndPermission.menus;
|
|
||||||
} catch (e: any) {
|
|
||||||
console.error(e);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type RouterConvCallbackFunc = (router: any) => void;
|
|
||||||
|
|
||||||
// 后端控制路由,后端返回路由 转换为vue route
|
|
||||||
export function backEndRouterConverter(routes: any, callbackFunc: RouterConvCallbackFunc = null as any, parentPath: string = '/') {
|
|
||||||
if (!routes) return;
|
|
||||||
return routes.map((item: any) => {
|
|
||||||
if (!item.meta) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
// 将json字符串的meta转为对象
|
|
||||||
item.meta = JSON.parse(item.meta);
|
|
||||||
// 将meta.comoponet 解析为route.component
|
|
||||||
if (item.meta.component) {
|
|
||||||
item.component = dynamicImport(dynamicViewsModules, item.meta.component);
|
|
||||||
delete item.meta['component'];
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = item.code;
|
|
||||||
// 如果不是以 / 开头,则路径需要拼接父路径
|
|
||||||
if (!path.startsWith('/')) {
|
|
||||||
path = parentPath + '/' + path;
|
|
||||||
}
|
|
||||||
item.path = path;
|
|
||||||
delete item['code'];
|
|
||||||
|
|
||||||
// route.meta.title == resource.name
|
|
||||||
item.meta.title = item.name;
|
|
||||||
delete item['name'];
|
|
||||||
|
|
||||||
// route.name == resource.meta.routeName
|
|
||||||
item.name = item.meta.routeName;
|
|
||||||
delete item.meta['routeName'];
|
|
||||||
|
|
||||||
// route.redirect == resource.meta.redirect
|
|
||||||
if (item.meta.redirect) {
|
|
||||||
item.redirect = item.meta.redirect;
|
|
||||||
delete item.meta['redirect'];
|
|
||||||
}
|
|
||||||
// 存在回调,则执行回调
|
|
||||||
callbackFunc && callbackFunc(item);
|
|
||||||
item.children && backEndRouterConverter(item.children, callbackFunc, item.path);
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 后端路由 component 转换函数
|
|
||||||
* @param dynamicViewsModules 获取目录下的 .vue、.tsx 全部文件
|
|
||||||
* @param component 当前要处理项 component
|
|
||||||
* @returns 返回处理成函数后的 component
|
|
||||||
*/
|
|
||||||
export function dynamicImport(dynamicViewsModules: Record<string, Function>, component: string) {
|
|
||||||
const keys = Object.keys(dynamicViewsModules);
|
|
||||||
const matchKeys = keys.filter((key) => {
|
|
||||||
const k = key.replace(/..\/views|../, '');
|
|
||||||
return k.startsWith(`${component}`) || k.startsWith(`/${component}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (matchKeys?.length === 1) {
|
|
||||||
return dynamicViewsModules[matchKeys[0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matchKeys?.length > 1) {
|
|
||||||
console.error('匹配到多个相似组件路径, 可添加后缀.vue或.tsx进行区分或者重命名组件名, 请调整...', matchKeys);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error(`未匹配到[${component}]组件名对应的组件文件`);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除/重置路由
|
// 删除/重置路由
|
||||||
export function resetRoute() {
|
export function resetRoute() {
|
||||||
useRoutesList().routesList.forEach((route: any) => {
|
useRoutesList().routesList?.forEach((route: any) => {
|
||||||
const { name } = route;
|
const { name } = route;
|
||||||
router.hasRoute(name) && router.removeRoute(name);
|
router.hasRoute(name) && router.removeRoute(name);
|
||||||
});
|
});
|
||||||
@@ -177,19 +50,17 @@ export async function initRouter() {
|
|||||||
initAllFun();
|
initAllFun();
|
||||||
} else if (isRequestRoutes) {
|
} else if (isRequestRoutes) {
|
||||||
// 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
// 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
||||||
await initBackEndControlRoutesFun();
|
await initBackendRoutes();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
NextLoading.done();
|
NextLoading.done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let loadRouter = false;
|
|
||||||
|
|
||||||
// 路由加载前
|
// 路由加载前
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
NProgress.configure({ showSpinner: false });
|
NProgress.configure({ showSpinner: false });
|
||||||
if (to.meta.title) NProgress.start();
|
NProgress.start();
|
||||||
|
|
||||||
// 如果有标题参数,则再原标题后加上参数来区别
|
// 如果有标题参数,则再原标题后加上参数来区别
|
||||||
if (to.meta.titleRename && to.meta.title) {
|
if (to.meta.titleRename && to.meta.title) {
|
||||||
@@ -197,24 +68,27 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const token = getToken();
|
const token = getToken();
|
||||||
if ((to.path === '/login' || to.path == '/oauth2/callback') && !token) {
|
|
||||||
next();
|
|
||||||
NProgress.done();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!token) {
|
|
||||||
next(`/login?redirect=${to.path}`);
|
|
||||||
clearSession();
|
|
||||||
resetRoute();
|
|
||||||
NProgress.done();
|
|
||||||
|
|
||||||
|
const toPath = to.path;
|
||||||
|
// 判断是访问登陆页,有token就在当前页面,没有token重置路由与用户信息到登陆页
|
||||||
|
if (toPath === URL_LOGIN) {
|
||||||
|
if (token) {
|
||||||
|
return next(from.fullPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetRoute();
|
||||||
syssocket.destory();
|
syssocket.destory();
|
||||||
return;
|
return next();
|
||||||
}
|
}
|
||||||
if (token && to.path === '/login') {
|
|
||||||
next('/');
|
// 判断访问页面是否在路由白名单地址(静态路由)中,如果存在直接放行
|
||||||
NProgress.done();
|
if (ROUTER_WHITE_LIST.includes(toPath)) {
|
||||||
return;
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否有token,没有重定向到 login 页面
|
||||||
|
if (!token) {
|
||||||
|
return next(`${URL_LOGIN}?redirect=${toPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 终端不需要连接系统websocket消息
|
// 终端不需要连接系统websocket消息
|
||||||
@@ -222,14 +96,18 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
syssocket.init();
|
syssocket.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 不存在路由(避免刷新页面找不到路由)并且未加载过(避免token过期,导致获取权限接口报权限不足,无限获取),则重新初始化路由
|
// 不存在路由(避免刷新页面找不到路由),则重新初始化路由
|
||||||
if (useRoutesList().routesList?.length == 0 && !loadRouter) {
|
if (useRoutesList().routesList?.length == 0) {
|
||||||
await initRouter();
|
try {
|
||||||
loadRouter = true;
|
// 可能token过期无法获取菜单权限信息等
|
||||||
next({ path: to.path, query: to.query });
|
await initRouter();
|
||||||
} else {
|
} catch (e) {
|
||||||
next();
|
return next(`${URL_401}?redirect=${toPath}`);
|
||||||
|
}
|
||||||
|
return next({ path: toPath, query: to.query });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 路由加载后
|
// 路由加载后
|
||||||
@@ -237,5 +115,13 @@ router.afterEach(() => {
|
|||||||
NProgress.done();
|
NProgress.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 路由跳转错误
|
||||||
|
* */
|
||||||
|
router.onError((error) => {
|
||||||
|
NProgress.done();
|
||||||
|
console.warn('路由错误', error.message);
|
||||||
|
});
|
||||||
|
|
||||||
// 导出路由
|
// 导出路由
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -1,171 +0,0 @@
|
|||||||
import { RouteRecordRaw } from 'vue-router';
|
|
||||||
import Layout from '@/layout/index.vue';
|
|
||||||
|
|
||||||
// 定义动态路由
|
|
||||||
export const dynamicRoutes = [
|
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
name: '/',
|
|
||||||
component: Layout,
|
|
||||||
redirect: '/home',
|
|
||||||
meta: {
|
|
||||||
isKeepAlive: true,
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
// children: [
|
|
||||||
// {
|
|
||||||
// path: '/home',
|
|
||||||
// name: 'home',
|
|
||||||
// component: () => import('@/views/home/index.vue'),
|
|
||||||
// meta: {
|
|
||||||
// title: '首页',
|
|
||||||
// // iframe链接
|
|
||||||
// link: '',
|
|
||||||
// // 是否在菜单栏显示,默认显示
|
|
||||||
// isHide: false,
|
|
||||||
// isKeepAlive: true,
|
|
||||||
// // tag标签是否不可删除
|
|
||||||
// isAffix: true,
|
|
||||||
// // 是否为iframe
|
|
||||||
// isIframe: false,
|
|
||||||
// icon: 'el-icon-s-home',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: '/sys',
|
|
||||||
// name: 'Resource',
|
|
||||||
// redirect: '/sys/resources',
|
|
||||||
// meta: {
|
|
||||||
// title: '系统管理',
|
|
||||||
// // 资源code,用于校验用户是否拥有该资源权限
|
|
||||||
// code: 'sys',
|
|
||||||
// // isKeepAlive: true,
|
|
||||||
// icon: 'el-icon-monitor',
|
|
||||||
// },
|
|
||||||
// children: [
|
|
||||||
// {
|
|
||||||
// path: 'sys/resources',
|
|
||||||
// name: 'ResourceList',
|
|
||||||
// component: () => import('@/views/system/resource'),
|
|
||||||
// meta: {
|
|
||||||
// title: '资源管理',
|
|
||||||
// code: 'resource:list',
|
|
||||||
// isKeepAlive: true,
|
|
||||||
// icon: 'el-icon-menu',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'sys/roles',
|
|
||||||
// name: 'RoleList',
|
|
||||||
// component: () => import('@/views/system/role'),
|
|
||||||
// meta: {
|
|
||||||
// title: '角色管理',
|
|
||||||
// code: 'role:list',
|
|
||||||
// isKeepAlive: true,
|
|
||||||
// icon: 'el-icon-menu',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: 'sys/accounts',
|
|
||||||
// name: 'ResourceList',
|
|
||||||
// component: () => import('@/views/system/account'),
|
|
||||||
// meta: {
|
|
||||||
// title: '账号管理',
|
|
||||||
// code: 'account:list',
|
|
||||||
// isKeepAlive: true,
|
|
||||||
// icon: 'el-icon-menu',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: '/machine',
|
|
||||||
// name: 'Machine',
|
|
||||||
// redirect: '/machine/list',
|
|
||||||
// meta: {
|
|
||||||
// title: '机器管理',
|
|
||||||
// // 资源code,用于校验用户是否拥有该资源权限
|
|
||||||
// code: 'machine',
|
|
||||||
// // isKeepAlive: true,
|
|
||||||
// icon: 'el-icon-monitor',
|
|
||||||
// },
|
|
||||||
// children: [
|
|
||||||
// {
|
|
||||||
// path: '/list',
|
|
||||||
// name: 'MachineList',
|
|
||||||
// component: () => import('@/views/ops/machine'),
|
|
||||||
// meta: {
|
|
||||||
// title: '机器列表',
|
|
||||||
// code: 'machine:list',
|
|
||||||
// isKeepAlive: true,
|
|
||||||
// icon: 'el-icon-menu',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// path: '/personal',
|
|
||||||
// name: 'personal',
|
|
||||||
// component: () => import('@/views/personal/index.vue'),
|
|
||||||
// meta: {
|
|
||||||
// title: '个人中心',
|
|
||||||
// isKeepAlive: true,
|
|
||||||
// icon: 'el-icon-user',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// 定义静态路由
|
|
||||||
export const staticRoutes: Array<RouteRecordRaw> = [
|
|
||||||
{
|
|
||||||
path: '/login',
|
|
||||||
name: 'login',
|
|
||||||
component: () => import('@/views/login/index.vue'),
|
|
||||||
meta: {
|
|
||||||
title: '登录',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/404',
|
|
||||||
name: 'notFound',
|
|
||||||
component: () => import('@/views/error/404.vue'),
|
|
||||||
meta: {
|
|
||||||
title: '找不到此页面',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/401',
|
|
||||||
name: 'noPower',
|
|
||||||
component: () => import('@/views/error/401.vue'),
|
|
||||||
meta: {
|
|
||||||
title: '没有权限',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/oauth2/callback',
|
|
||||||
name: 'oauth2Callback',
|
|
||||||
component: () => import('@/views/oauth/Oauth2Callback.vue'),
|
|
||||||
meta: {
|
|
||||||
title: 'oauth2回调',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/machine/terminal',
|
|
||||||
name: 'machineTerminal',
|
|
||||||
component: () => import('@/views/ops/machine/SshTerminalPage.vue'),
|
|
||||||
meta: {
|
|
||||||
// 将路径 'xxx?name=名字' 里的name字段值替换到title里
|
|
||||||
title: '终端 | {name}',
|
|
||||||
// 是否根据query对标题名进行参数替换,即最终显示为‘终端_机器名’
|
|
||||||
titleRename: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// 定义404界面
|
|
||||||
export const pathMatch = {
|
|
||||||
path: '/:path(.*)*',
|
|
||||||
redirect: '/404',
|
|
||||||
};
|
|
||||||
82
mayfly_go_web/src/router/staticRouter.ts
Normal file
82
mayfly_go_web/src/router/staticRouter.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { RouteRecordRaw } from 'vue-router';
|
||||||
|
|
||||||
|
export const URL_HOME: string = '/home';
|
||||||
|
|
||||||
|
// 登录页地址(默认)
|
||||||
|
export const URL_LOGIN: string = '/login';
|
||||||
|
|
||||||
|
export const URL_401: string = '/401';
|
||||||
|
|
||||||
|
export const URL_404: string = '/404';
|
||||||
|
|
||||||
|
export const LAYOUT_ROUTE_NAME: string = 'layout';
|
||||||
|
|
||||||
|
// 路由白名单地址(本地存在的路由 staticRouter.ts 中)
|
||||||
|
export const ROUTER_WHITE_LIST: string[] = [URL_404, URL_401, '/oauth2/callback'];
|
||||||
|
|
||||||
|
// 静态路由
|
||||||
|
export const staticRoutes: Array<RouteRecordRaw> = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
redirect: URL_HOME,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: URL_LOGIN,
|
||||||
|
name: 'login',
|
||||||
|
component: () => import('@/views/login/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '登录',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/layout',
|
||||||
|
name: LAYOUT_ROUTE_NAME,
|
||||||
|
component: () => import('@/layout/index.vue'),
|
||||||
|
redirect: URL_HOME,
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/oauth2/callback',
|
||||||
|
name: 'oauth2Callback',
|
||||||
|
component: () => import('@/views/oauth/Oauth2Callback.vue'),
|
||||||
|
meta: {
|
||||||
|
title: 'oauth2回调',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/machine/terminal',
|
||||||
|
name: 'machineTerminal',
|
||||||
|
component: () => import('@/views/ops/machine/SshTerminalPage.vue'),
|
||||||
|
meta: {
|
||||||
|
// 将路径 'xxx?name=名字' 里的name字段值替换到title里
|
||||||
|
title: '终端 | {name}',
|
||||||
|
// 是否根据query对标题名进行参数替换,即最终显示为‘终端_机器名’
|
||||||
|
titleRename: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 错误页面路由
|
||||||
|
export const errorRoutes: Array<RouteRecordRaw> = [
|
||||||
|
{
|
||||||
|
path: URL_404,
|
||||||
|
name: 'notFound',
|
||||||
|
component: () => import('@/views/error/404.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '找不到此页面',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: URL_401,
|
||||||
|
name: 'noPower',
|
||||||
|
component: () => import('@/views/error/401.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '没有权限',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Resolve refresh page, route warnings
|
||||||
|
{
|
||||||
|
path: '/:pathMatch(.*)*',
|
||||||
|
component: () => import('@/views/error/404.vue'),
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -11,7 +11,7 @@ export const useRoutesList = defineStore('routesList', {
|
|||||||
routesList: [],
|
routesList: [],
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
async setRoutesList(data: Array<string>) {
|
async setRoutesList(data: Array<any>) {
|
||||||
this.routesList = data;
|
this.routesList = data;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,16 +19,21 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
import { clearSession } from '@/common/utils/storage';
|
import { clearSession } from '@/common/utils/storage';
|
||||||
|
import { URL_LOGIN } from '@/router/staticRouter';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: '401',
|
name: '401',
|
||||||
setup() {
|
setup() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
const onSetAuth = () => {
|
const onSetAuth = () => {
|
||||||
clearSession();
|
clearSession();
|
||||||
router.push('/login');
|
router.push({ path: URL_LOGIN, query: route.query });
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onSetAuth,
|
onSetAuth,
|
||||||
};
|
};
|
||||||
@@ -93,3 +98,4 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@/router/staticRouter
|
||||||
|
|||||||
Reference in New Issue
Block a user