2021-06-07 17:22:07 +08:00
|
|
|
|
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
|
|
|
|
|
|
import NProgress from 'nprogress';
|
|
|
|
|
|
import 'nprogress/nprogress.css';
|
2023-03-15 11:41:03 +08:00
|
|
|
|
import { getSession, clearSession } from '@/common/utils/storage';
|
2023-07-06 20:59:22 +08:00
|
|
|
|
import { templateResolve } from '@/common/utils/string';
|
2023-03-15 11:41:03 +08:00
|
|
|
|
import { NextLoading } from '@/common/utils/loading';
|
2023-07-06 20:59:22 +08:00
|
|
|
|
import { dynamicRoutes, staticRoutes, pathMatch } from './route';
|
2021-06-07 17:22:07 +08:00
|
|
|
|
import openApi from '@/common/openApi';
|
2021-11-11 15:56:02 +08:00
|
|
|
|
import sockets from '@/common/sockets';
|
2023-03-15 11:41:03 +08:00
|
|
|
|
import pinia from '@/store/index';
|
|
|
|
|
|
import { useThemeConfig } from '@/store/themeConfig';
|
|
|
|
|
|
import { useUserInfo } from '@/store/userInfo';
|
|
|
|
|
|
import { useRoutesList } from '@/store/routesList';
|
|
|
|
|
|
import { useKeepALiveNames } from '@/store/keepAliveNames';
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取目录下的 .vue、.tsx 全部文件
|
|
|
|
|
|
* @method import.meta.glob
|
|
|
|
|
|
* @link 参考:https://cn.vitejs.dev/guide/features.html#json
|
|
|
|
|
|
*/
|
2023-08-16 17:37:33 +08:00
|
|
|
|
const viewsModules: any = import.meta.glob(['../views/**/*.{vue,tsx}', '!../views/layout/**/*.{vue,tsx}']);
|
2023-03-15 11:41:03 +08:00
|
|
|
|
const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...viewsModules });
|
2021-06-07 17:22:07 +08:00
|
|
|
|
|
|
|
|
|
|
// 添加静态路由
|
|
|
|
|
|
const router = createRouter({
|
|
|
|
|
|
history: createWebHashHistory(),
|
|
|
|
|
|
routes: staticRoutes,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 前端控制路由:初始化方法,防止刷新时丢失
|
|
|
|
|
|
export function initAllFun() {
|
|
|
|
|
|
NextLoading.start(); // 界面 loading 动画开始执行
|
|
|
|
|
|
const token = getSession('token'); // 获取浏览器缓存 token 值
|
|
|
|
|
|
if (!token) {
|
|
|
|
|
|
// 无 token 停止执行下一步
|
2023-07-06 20:59:22 +08:00
|
|
|
|
return false;
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
2023-03-15 11:41:03 +08:00
|
|
|
|
useUserInfo().setUserInfo({});
|
2021-06-07 17:22:07 +08:00
|
|
|
|
router.addRoute(pathMatch); // 添加404界面
|
|
|
|
|
|
resetRoute(); // 删除/重置路由
|
|
|
|
|
|
// 添加动态路由
|
|
|
|
|
|
setFilterRouteEnd().forEach((route: any) => {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
router.addRoute(route as unknown as RouteRecordRaw);
|
2021-06-07 17:22:07 +08:00
|
|
|
|
});
|
|
|
|
|
|
// 过滤权限菜单
|
2023-07-06 20:59:22 +08:00
|
|
|
|
useRoutesList().setRoutesList(setFilterMenuFun(dynamicRoutes[0].children, useUserInfo().userInfo.menus));
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-13 20:11:22 +08:00
|
|
|
|
// 后端控制路由:执行路由数据初始化
|
|
|
|
|
|
export async function initBackEndControlRoutesFun() {
|
2021-06-07 17:22:07 +08:00
|
|
|
|
NextLoading.start(); // 界面 loading 动画开始执行
|
|
|
|
|
|
const token = getSession('token'); // 获取浏览器缓存 token 值
|
|
|
|
|
|
if (!token) {
|
|
|
|
|
|
// 无 token 停止执行下一步
|
2023-07-06 20:59:22 +08:00
|
|
|
|
return false;
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
2023-03-15 11:41:03 +08:00
|
|
|
|
useUserInfo().setUserInfo({});
|
2023-04-13 20:11:22 +08:00
|
|
|
|
// 获取路由
|
|
|
|
|
|
let menuRoute = await getBackEndControlRoutes();
|
2021-06-07 17:22:07 +08:00
|
|
|
|
dynamicRoutes[0].children = backEndRouterConverter(menuRoute); // 处理路由(component)
|
|
|
|
|
|
// 添加404界面
|
|
|
|
|
|
router.addRoute(pathMatch);
|
|
|
|
|
|
resetRoute(); // 删除/重置路由
|
|
|
|
|
|
// 添加动态路由
|
|
|
|
|
|
formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes)).forEach((route: any) => {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
router.addRoute(route as unknown as RouteRecordRaw);
|
2021-06-07 17:22:07 +08:00
|
|
|
|
});
|
2023-07-06 20:59:22 +08:00
|
|
|
|
useRoutesList().setRoutesList(dynamicRoutes[0].children);
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
2023-04-13 20:11:22 +08:00
|
|
|
|
export async function getBackEndControlRoutes() {
|
|
|
|
|
|
try {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
const menuAndPermission = await openApi.getPermissions();
|
2023-04-13 20:11:22 +08:00
|
|
|
|
// 赋值权限码,用于控制按钮等
|
|
|
|
|
|
useUserInfo().userInfo.permissions = menuAndPermission.permissions;
|
|
|
|
|
|
return menuAndPermission.menus;
|
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
|
console.error(e);
|
2023-07-06 20:59:22 +08:00
|
|
|
|
return [];
|
2023-04-13 20:11:22 +08:00
|
|
|
|
}
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 后端控制路由,后端返回路由 转换为vue route
|
2023-07-06 20:59:22 +08:00
|
|
|
|
export function backEndRouterConverter(routes: any, parentPath: string = '/') {
|
2021-06-07 17:22:07 +08:00
|
|
|
|
if (!routes) return;
|
|
|
|
|
|
return routes.map((item: any) => {
|
|
|
|
|
|
if (!item.meta) {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
return item;
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
// 将json字符串的meta转为对象
|
2023-07-06 20:59:22 +08:00
|
|
|
|
item.meta = JSON.parse(item.meta);
|
2021-06-07 17:22:07 +08:00
|
|
|
|
// 将meta.comoponet 解析为route.component
|
|
|
|
|
|
if (item.meta.component) {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
item.component = dynamicImport(dynamicViewsModules, item.meta.component);
|
|
|
|
|
|
delete item.meta['component'];
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
// route.path == resource.code
|
2023-07-06 20:59:22 +08:00
|
|
|
|
let path = item.code;
|
2021-06-07 17:22:07 +08:00
|
|
|
|
// 如果不是以 / 开头,则路径需要拼接父路径
|
2023-07-06 20:59:22 +08:00
|
|
|
|
if (!path.startsWith('/')) {
|
|
|
|
|
|
path = parentPath + '/' + path;
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
2023-07-06 20:59:22 +08:00
|
|
|
|
item.path = path;
|
|
|
|
|
|
delete item['code'];
|
2021-06-07 17:22:07 +08:00
|
|
|
|
|
|
|
|
|
|
// route.meta.title == resource.name
|
2023-07-06 20:59:22 +08:00
|
|
|
|
item.meta.title = item.name;
|
|
|
|
|
|
delete item['name'];
|
2021-06-07 17:22:07 +08:00
|
|
|
|
|
|
|
|
|
|
// route.name == resource.meta.routeName
|
2023-07-06 20:59:22 +08:00
|
|
|
|
item.name = item.meta.routeName;
|
|
|
|
|
|
delete item.meta['routeName'];
|
2021-06-07 17:22:07 +08:00
|
|
|
|
|
|
|
|
|
|
// route.redirect == resource.meta.redirect
|
|
|
|
|
|
if (item.meta.redirect) {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
item.redirect = item.meta.redirect;
|
|
|
|
|
|
delete item.meta['redirect'];
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
item.children && backEndRouterConverter(item.children, item.path);
|
|
|
|
|
|
return item;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-03-15 11:41:03 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 后端路由 component 转换函数
|
|
|
|
|
|
* @param dynamicViewsModules 获取目录下的 .vue、.tsx 全部文件
|
|
|
|
|
|
* @param component 当前要处理项 component
|
|
|
|
|
|
* @returns 返回处理成函数后的 component
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function dynamicImport(dynamicViewsModules: Record<string, Function>, component: string) {
|
2023-04-13 20:11:22 +08:00
|
|
|
|
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) {
|
|
|
|
|
|
const matchKey = matchKeys[0];
|
|
|
|
|
|
return dynamicViewsModules[matchKey];
|
|
|
|
|
|
}
|
|
|
|
|
|
if (matchKeys?.length > 1) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2023-03-15 11:41:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-07 17:22:07 +08:00
|
|
|
|
// 多级嵌套数组处理成一维数组
|
|
|
|
|
|
export function formatFlatteningRoutes(arr: any) {
|
|
|
|
|
|
if (arr.length <= 0) return false;
|
|
|
|
|
|
for (let i = 0; i < arr.length; i++) {
|
|
|
|
|
|
if (arr[i].children) {
|
|
|
|
|
|
arr = arr.slice(0, i + 1).concat(arr[i].children, arr.slice(i + 1));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return arr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 多级嵌套数组处理后的一维数组,再处理成 `定义动态路由` 的格式
|
|
|
|
|
|
// 只保留二级:也就是二级以上全部处理成只有二级,keep-alive 支持二级缓存
|
|
|
|
|
|
// isKeepAlive 处理 `name` 值,进行缓存。顶级关闭,全部不缓存
|
|
|
|
|
|
export function formatTwoStageRoutes(arr: any) {
|
|
|
|
|
|
if (arr.length <= 0) return false;
|
|
|
|
|
|
const newArr: any = [];
|
|
|
|
|
|
const cacheList: Array<string> = [];
|
|
|
|
|
|
arr.forEach((v: any) => {
|
|
|
|
|
|
if (v.path === '/') {
|
|
|
|
|
|
newArr.push({ component: v.component, name: v.name, path: v.path, redirect: v.redirect, meta: v.meta, children: [] });
|
|
|
|
|
|
} else {
|
|
|
|
|
|
newArr[0].children.push({ ...v });
|
|
|
|
|
|
if (newArr[0].meta.isKeepAlive && v.meta.isKeepAlive) {
|
|
|
|
|
|
cacheList.push(v.name);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2023-03-15 11:41:03 +08:00
|
|
|
|
useKeepALiveNames().setCacheKeepAlive(cacheList);
|
2021-06-07 17:22:07 +08:00
|
|
|
|
return newArr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 判断路由code 是否包含当前登录用户menus字段中,menus为字符串code数组
|
|
|
|
|
|
export function hasAnth(menus: any, route: any) {
|
|
|
|
|
|
if (route.meta && route.meta.code) {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
return menus.includes(route.meta.code);
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
2023-07-06 20:59:22 +08:00
|
|
|
|
return true;
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 递归过滤有权限的路由
|
|
|
|
|
|
export function setFilterMenuFun(routes: any, menus: any) {
|
|
|
|
|
|
const menu: any = [];
|
|
|
|
|
|
routes.forEach((route: any) => {
|
|
|
|
|
|
const item = { ...route };
|
|
|
|
|
|
if (hasAnth(menus, item)) {
|
|
|
|
|
|
if (item.children) {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
item.children = setFilterMenuFun(item.children, menus);
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
menu.push(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return menu;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前用户的权限去比对路由表,用于动态路由的添加
|
|
|
|
|
|
export function setFilterRoute(chil: any) {
|
|
|
|
|
|
let filterRoute: any = [];
|
|
|
|
|
|
chil.forEach((route: any) => {
|
|
|
|
|
|
// 如果路由需要拥有指定code才可访问,则校验该用户菜单是否存在该code
|
|
|
|
|
|
if (route.meta.code) {
|
2023-03-15 11:41:03 +08:00
|
|
|
|
useUserInfo().userInfo.menus.forEach((m: any) => {
|
2021-06-07 17:22:07 +08:00
|
|
|
|
if (route.meta.code == m) {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
filterRoute.push({ ...route });
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
2023-07-06 20:59:22 +08:00
|
|
|
|
});
|
2021-06-07 17:22:07 +08:00
|
|
|
|
} else {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
filterRoute.push({ ...route });
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return filterRoute;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 比对后的路由表,进行重新赋值
|
|
|
|
|
|
export function setFilterRouteEnd() {
|
|
|
|
|
|
let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes));
|
|
|
|
|
|
filterRouteEnd[0].children = setFilterRoute(filterRouteEnd[0].children);
|
|
|
|
|
|
return filterRouteEnd;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 删除/重置路由
|
|
|
|
|
|
export function resetRoute() {
|
2023-03-15 11:41:03 +08:00
|
|
|
|
useRoutesList().routesList.forEach((route: any) => {
|
2021-06-07 17:22:07 +08:00
|
|
|
|
const { name } = route;
|
|
|
|
|
|
router.hasRoute(name) && router.removeRoute(name);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-13 20:11:22 +08:00
|
|
|
|
export async function initRouter() {
|
|
|
|
|
|
// 初始化方法执行
|
|
|
|
|
|
const { isRequestRoutes } = useThemeConfig(pinia).themeConfig;
|
|
|
|
|
|
if (!isRequestRoutes) {
|
|
|
|
|
|
// 未开启后端控制路由
|
|
|
|
|
|
initAllFun();
|
|
|
|
|
|
} else if (isRequestRoutes) {
|
|
|
|
|
|
// 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
|
|
|
|
|
await initBackEndControlRoutesFun();
|
|
|
|
|
|
}
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-11-11 15:56:02 +08:00
|
|
|
|
let SysWs: any;
|
2023-04-16 00:50:36 +08:00
|
|
|
|
let loadRouter = false;
|
2021-11-11 15:56:02 +08:00
|
|
|
|
|
2021-06-07 17:22:07 +08:00
|
|
|
|
// 路由加载前
|
2023-04-13 20:11:22 +08:00
|
|
|
|
router.beforeEach(async (to, from, next) => {
|
2021-06-07 17:22:07 +08:00
|
|
|
|
NProgress.configure({ showSpinner: false });
|
|
|
|
|
|
if (to.meta.title) NProgress.start();
|
|
|
|
|
|
|
|
|
|
|
|
// 如果有标题参数,则再原标题后加上参数来区别
|
|
|
|
|
|
if (to.meta.titleRename) {
|
2023-07-06 20:59:22 +08:00
|
|
|
|
to.meta.title = templateResolve(to.meta.title as string, to.query);
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const token = getSession('token');
|
2023-07-24 22:36:07 +08:00
|
|
|
|
if ((to.path === '/login' || to.path == '/oauth2/callback') && !token) {
|
2021-06-07 17:22:07 +08:00
|
|
|
|
next();
|
|
|
|
|
|
NProgress.done();
|
2022-05-17 20:23:08 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!token) {
|
|
|
|
|
|
next(`/login?redirect=${to.path}`);
|
|
|
|
|
|
clearSession();
|
|
|
|
|
|
resetRoute();
|
|
|
|
|
|
NProgress.done();
|
|
|
|
|
|
|
|
|
|
|
|
if (SysWs) {
|
|
|
|
|
|
SysWs.close();
|
|
|
|
|
|
SysWs = null;
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
2022-05-17 20:23:08 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (token && to.path === '/login') {
|
|
|
|
|
|
next('/');
|
|
|
|
|
|
NProgress.done();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 终端不需要连接系统websocket消息
|
|
|
|
|
|
if (!SysWs && to.path != '/machine/terminal') {
|
|
|
|
|
|
SysWs = sockets.sysMsgSocket();
|
|
|
|
|
|
}
|
2023-04-16 00:50:36 +08:00
|
|
|
|
// 不存在路由(避免刷新页面找不到路由)并且未加载过(避免token过期,导致获取权限接口报权限不足,无限获取),则重新初始化路由
|
|
|
|
|
|
if (useRoutesList().routesList.length == 0 && !loadRouter) {
|
2023-04-13 20:11:22 +08:00
|
|
|
|
await initRouter();
|
2023-04-16 00:50:36 +08:00
|
|
|
|
loadRouter = true;
|
2023-04-13 20:11:22 +08:00
|
|
|
|
next({ path: to.path, query: to.query });
|
|
|
|
|
|
} else {
|
2022-05-17 20:23:08 +08:00
|
|
|
|
next();
|
2021-06-07 17:22:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 路由加载后
|
|
|
|
|
|
router.afterEach(() => {
|
|
|
|
|
|
NProgress.done();
|
|
|
|
|
|
NextLoading.done();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 导出路由
|
|
|
|
|
|
export default router;
|