| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- import type { Route } from "#/global";
- import type { RouteRecordRaw } from "vue-router";
- import { systemRoutes } from "@/router/routes";
- import { resolveRoutePath } from "@/utils";
- import useUserStore from "@/store/modules/user";
- import { cloneDeep } from "lodash-es";
- import useSettingsStore from "./settings";
- import api from "@/api";
- import { ElMessage } from "element-plus";
- const useRouteStore = defineStore(
- // 唯一ID
- "route",
- () => {
- const settingsStore = useSettingsStore();
- const isGenerate = ref(false);
- const routesRaw = ref<Route.recordMainRaw[]>([]);
- const filesystemRoutesRaw = ref<RouteRecordRaw[]>([]);
- const currentRemoveRoutes = ref<(() => void)[]>([]);
- // 将多层嵌套路由处理成两层,保留顶层和最子层路由,中间层级将被拍平
- function flatAsyncRoutes<T extends RouteRecordRaw>(route: T): T {
- if (route.children) {
- route.children = flatAsyncRoutesRecursive(
- route.children,
- [
- {
- path: route.path,
- title: route.meta?.title,
- icon: route.meta?.icon,
- hide: !route.meta?.breadcrumb && route.meta?.breadcrumb === false,
- },
- ],
- route.path
- );
- }
- return route;
- }
- function flatAsyncRoutesRecursive(
- routes: RouteRecordRaw[],
- breadcrumb: Route.breadcrumb[] = [],
- baseUrl = ""
- ): RouteRecordRaw[] {
- const res: RouteRecordRaw[] = [];
- routes.forEach((route) => {
- if (route.children) {
- const childrenBaseUrl = resolveRoutePath(baseUrl, route.path);
- const tmpBreadcrumb = cloneDeep(breadcrumb);
- tmpBreadcrumb.push({
- path: childrenBaseUrl,
- title: route.meta?.title,
- icon: route.meta?.icon,
- hide: !route.meta?.breadcrumb && route.meta?.breadcrumb === false,
- });
- const tmpRoute = cloneDeep(route);
- tmpRoute.path = childrenBaseUrl;
- if (!tmpRoute.meta) {
- tmpRoute.meta = {};
- }
- tmpRoute.meta.breadcrumbNeste = tmpBreadcrumb;
- delete tmpRoute.children;
- res.push(tmpRoute);
- const childrenRoutes = flatAsyncRoutesRecursive(
- route.children,
- tmpBreadcrumb,
- childrenBaseUrl
- );
- childrenRoutes.forEach((item) => {
- // 如果 path 一样则覆盖,因为子路由的 path 可能设置为空,导致和父路由一样,直接注册会提示路由重复
- if (res.some((v) => v.path === item.path)) {
- res.forEach((v, i) => {
- if (v.path === item.path) {
- res[i] = item;
- }
- });
- } else {
- res.push(item);
- }
- });
- } else {
- const tmpRoute = cloneDeep(route);
- tmpRoute.path = resolveRoutePath(baseUrl, tmpRoute.path);
- // 处理面包屑导航
- const tmpBreadcrumb = cloneDeep(breadcrumb);
- tmpBreadcrumb.push({
- path: tmpRoute.path,
- title: tmpRoute.meta?.title,
- icon: tmpRoute.meta?.icon,
- hide:
- !tmpRoute.meta?.breadcrumb && tmpRoute.meta?.breadcrumb === false,
- });
- if (!tmpRoute.meta) {
- tmpRoute.meta = {};
- }
- tmpRoute.meta.breadcrumbNeste = tmpBreadcrumb;
- res.push(tmpRoute);
- }
- });
- return res;
- }
- // 扁平化路由(将三级及以上路由数据拍平成二级)
- const flatRoutes = computed(() => {
- const returnRoutes: RouteRecordRaw[] = [];
- if (settingsStore.settings.app.routeBaseOn !== "filesystem") {
- if (routesRaw.value) {
- routesRaw.value.forEach((item) => {
- const tmpRoutes = cloneDeep(item.children) as RouteRecordRaw[];
- tmpRoutes.map((v) => {
- if (!v.meta) {
- v.meta = {};
- }
- v.meta.auth = item.meta?.auth ?? v.meta?.auth;
- return v;
- });
- returnRoutes.push(...tmpRoutes);
- });
- returnRoutes.forEach((item) => flatAsyncRoutes(item));
- }
- } else {
- returnRoutes.push(
- ...(cloneDeep(filesystemRoutesRaw.value) as RouteRecordRaw[])
- );
- }
- return returnRoutes;
- });
- const flatSystemRoutes = computed(() => {
- const routes = [...systemRoutes];
- routes.forEach((item) => flatAsyncRoutes(item));
- return routes;
- });
- // TODO 将设置 meta.sidebar 的属性转换成 meta.menu ,过渡处理,未来将被弃用
- let isUsedDeprecatedAttribute = false;
- function converDeprecatedAttribute<T extends Route.recordMainRaw[]>(
- routes: T
- ): T {
- routes.forEach((route) => {
- route.children = converDeprecatedAttributeRecursive(route.children);
- });
- if (isUsedDeprecatedAttribute) {
- // turbo-console-disable-next-line
- console.warn(
- '[Fantastic-admin] 路由配置中的 "sidebar" 属性即将被弃用, 请尽快替换为 "menu" 属性'
- );
- }
- return routes;
- }
- function converDeprecatedAttributeRecursive(routes: RouteRecordRaw[]) {
- if (routes) {
- routes.forEach((route) => {
- if (typeof route.meta?.sidebar === "boolean") {
- isUsedDeprecatedAttribute = true;
- route.meta.menu = route.meta.sidebar;
- delete route.meta.sidebar;
- }
- if (route.children) {
- converDeprecatedAttributeRecursive(route.children);
- }
- });
- }
- return routes;
- }
- // 生成路由(前端生成)
- function generateRoutesAtFront(asyncRoutes: Route.recordMainRaw[]) {
- // 设置 routes 数据
- routesRaw.value = converDeprecatedAttribute(
- cloneDeep(asyncRoutes) as any
- );
- isGenerate.value = true;
- }
- // 格式化后端路由数据
- // function formatBackRoutes(routes: any, views = import.meta.glob('../../views/**/*.vue')): Route.recordMainRaw[] {
- // return routes.map((route: any) => {
- // switch (route.component) {
- // case 'Layout':
- // route.component = () => import('@/layouts/index.vue')
- // break
- // default:
- // if (route.component) {
- // route.component = views[`../../views/${route.component}`]
- // }
- // else {
- // delete route.component
- // }
- // }
- // if (route.children) {
- // route.children = formatBackRoutes(route.children, views)
- // }
- // return route
- // })
- // }
- // 格式化后端路由数据
- function formatBackRoutes(
- routes: any,
- views = import.meta.glob("../../views/**/*.vue")
- ): Route.recordMainRaw[] {
- // 需要从外层移动到meta对象的字段列表
- const metaFields = [
- "title",
- "icon",
- "auth",
- "menu",
- "breadcrumb",
- "activeMenu",
- "cache",
- "noCache",
- "link",
- ]; // 从外层移动到meta对象的字段列表
- return routes
- .filter((route: any) => route.type != 2)
- .map((route: any) => {
- // 处理组件引用
- switch (route.component) {
- case "Layout":
- route.component = () => import("@/layouts/index.vue");
- break;
- default:
- if (route.component) {
- route.component = views[`../../views${route.component}.vue`];
- if (route.keepAlive === "1") route.cache = true;
- if (route.menu == 0) {
- route.menu = false;
- }
- } else {
- delete route.component;
- }
- }
- // 处理元数据格式转换:将指定字段从外层移动到meta对象中
- const hasMetaFields = metaFields.some(
- (field) => route[field] !== undefined
- );
- if (hasMetaFields) {
- // 确保meta对象存在
- if (!route.meta) {
- route.meta = {};
- }
- // 批量移动字段
- metaFields.forEach((field) => {
- if (route[field] !== undefined) {
- route.meta[field] = route[field];
- delete route[field];
- }
- });
- }
- // 递归处理子路由
- if (route.children) {
- route.children = formatBackRoutes(route.children, views);
- }
- return route;
- });
- }
- // 生成路由(后端获取)
- async function generateRoutesAtBack() {
- await api.menuApi
- .getRoleMenuTree()
- .then((res) => {
- if (res.data.length > 0) {
- // 设置 routes 数据
- routesRaw.value = converDeprecatedAttribute(
- formatBackRoutes(res.data) as any
- );
- isGenerate.value = true;
- } else {
- // 退出登录
- useUserStore().logout();
- // 路由获取失败
- ElMessage.error("路由获取失败");
- }
- })
- .catch(() => {});
- }
- // 生成路由(文件系统生成)
- function generateRoutesAtFilesystem(asyncRoutes: RouteRecordRaw[]) {
- // 设置 routes 数据
- filesystemRoutesRaw.value = cloneDeep(asyncRoutes) as any;
- isGenerate.value = true;
- }
- // 记录 accessRoutes 路由,用于登出时删除路由
- function setCurrentRemoveRoutes(routes: (() => void)[]) {
- currentRemoveRoutes.value = routes;
- }
- // 清空动态路由
- function removeRoutes() {
- isGenerate.value = false;
- routesRaw.value = [];
- filesystemRoutesRaw.value = [];
- currentRemoveRoutes.value.forEach((removeRoute) => {
- removeRoute();
- });
- currentRemoveRoutes.value = [];
- }
- return {
- isGenerate,
- routesRaw,
- currentRemoveRoutes,
- flatRoutes,
- flatSystemRoutes,
- generateRoutesAtFront,
- generateRoutesAtBack,
- generateRoutesAtFilesystem,
- setCurrentRemoveRoutes,
- removeRoutes,
- };
- }
- );
- export default useRouteStore;
|