mirror of
				https://github.com/pure-admin/vue-pure-admin.git
				synced 2025-11-03 13:44:47 +08:00 
			
		
		
		
	refactor: router (#145)
* refactor: router * perf: router * perf: router * perf: router Co-authored-by: lrl <742798240@qq.com>
This commit is contained in:
		
							parent
							
								
									7811f6bdeb
								
							
						
					
					
						commit
						d57e0e379e
					
				@ -62,7 +62,7 @@
 | 
			
		||||
    "sortablejs": "1.13.0",
 | 
			
		||||
    "typescript-cookie": "^1.0.0",
 | 
			
		||||
    "v-contextmenu": "3.0.0",
 | 
			
		||||
    "vue": "^3.2.21",
 | 
			
		||||
    "vue": "^3.2.24",
 | 
			
		||||
    "vue-i18n": "^9.2.0-beta.3",
 | 
			
		||||
    "vue-json-pretty": "^2.0.2",
 | 
			
		||||
    "vue-router": "^4.0.11",
 | 
			
		||||
@ -87,7 +87,7 @@
 | 
			
		||||
    "@typescript-eslint/parser": "4.31.0",
 | 
			
		||||
    "@vitejs/plugin-vue": "^1.10.2",
 | 
			
		||||
    "@vitejs/plugin-vue-jsx": "^1.3.1",
 | 
			
		||||
    "@vue/compiler-sfc": "^3.2.21",
 | 
			
		||||
    "@vue/compiler-sfc": "^3.2.24",
 | 
			
		||||
    "@vue/eslint-config-prettier": "6.0.0",
 | 
			
		||||
    "@vue/eslint-config-typescript": "7.0.0",
 | 
			
		||||
    "@zougt/vite-plugin-theme-preprocessor": "^1.3.12",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4613
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4613
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -13,7 +13,6 @@
 | 
			
		||||
  "HideTabs": false,
 | 
			
		||||
  "MapConfigure": {
 | 
			
		||||
    "amapKey": "97b3248d1553172e81f168cf94ea667e",
 | 
			
		||||
    "baiduKey": "wTHbkkEweiFqZLKunMIjcrb2RcqNXkhc",
 | 
			
		||||
    "options": {
 | 
			
		||||
      "resizeEnable": true,
 | 
			
		||||
      "center": [113.6401, 34.72468],
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,6 @@ const title =
 | 
			
		||||
  getCurrentInstance().appContext.config.globalProperties.$config?.Title;
 | 
			
		||||
 | 
			
		||||
const menuRef = templateRef<ElRef | null>("menu", null);
 | 
			
		||||
const routeStore = usePermissionStoreHook();
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const routers = useRouter().options.routes;
 | 
			
		||||
@ -133,7 +132,7 @@ onMounted(() => {
 | 
			
		||||
      @select="menuSelect"
 | 
			
		||||
    >
 | 
			
		||||
      <sidebar-item
 | 
			
		||||
        v-for="route in routeStore.wholeRoutes"
 | 
			
		||||
        v-for="route in usePermissionStoreHook().wholeMenus"
 | 
			
		||||
        :key="route.path"
 | 
			
		||||
        :item="route"
 | 
			
		||||
        :base-path="route.path"
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,6 @@ import { usePermissionStoreHook } from "/@/store/modules/permission";
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
const pureApp = useAppStoreHook();
 | 
			
		||||
const router = useRouter().options.routes;
 | 
			
		||||
const routeStore = usePermissionStoreHook();
 | 
			
		||||
const showLogo = ref(storageLocal.getItem("logoVal") || "1");
 | 
			
		||||
const isCollapse = computed(() => {
 | 
			
		||||
  return !pureApp.getSidebarStatus;
 | 
			
		||||
@ -72,7 +71,7 @@ onBeforeMount(() => {
 | 
			
		||||
        @select="menuSelect"
 | 
			
		||||
      >
 | 
			
		||||
        <sidebar-item
 | 
			
		||||
          v-for="route in routeStore.wholeRoutes"
 | 
			
		||||
          v-for="route in usePermissionStoreHook().wholeMenus"
 | 
			
		||||
          :key="route.path"
 | 
			
		||||
          :item="route"
 | 
			
		||||
          class="outer-most"
 | 
			
		||||
 | 
			
		||||
@ -22,11 +22,12 @@ import { transformI18n } from "/@/plugins/i18n";
 | 
			
		||||
import { storageLocal } from "/@/utils/storage";
 | 
			
		||||
import { useRoute, useRouter } from "vue-router";
 | 
			
		||||
import { RouteConfigs, tagsViewsType } from "../../types";
 | 
			
		||||
import { handleAliveRoute, delAliveRoutes } from "/@/router";
 | 
			
		||||
import { useSettingStoreHook } from "/@/store/modules/settings";
 | 
			
		||||
import { handleAliveRoute, delAliveRoutes } from "/@/router/utils";
 | 
			
		||||
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
 | 
			
		||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
 | 
			
		||||
import { toggleClass, removeClass, hasClass } from "/@/utils/operate";
 | 
			
		||||
 | 
			
		||||
import { templateRef, useResizeObserver, useDebounceFn } from "@vueuse/core";
 | 
			
		||||
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								src/layout/routerView/parent.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/layout/routerView/parent.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <router-view>
 | 
			
		||||
    <template #default="{ Component, route }">
 | 
			
		||||
      <transition appear name="fade-transform" mode="out-in">
 | 
			
		||||
        <component :is="Component" :key="route.fullPath" />
 | 
			
		||||
      </transition>
 | 
			
		||||
    </template>
 | 
			
		||||
  </router-view>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
export default {
 | 
			
		||||
  name: "layoutParentView"
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
@ -20,7 +20,9 @@ export type RouteConfigs = {
 | 
			
		||||
    icon?: string;
 | 
			
		||||
    showLink?: boolean;
 | 
			
		||||
    savedPosition?: boolean;
 | 
			
		||||
    authority?: Array<string>;
 | 
			
		||||
  };
 | 
			
		||||
  children?: RouteConfigs[];
 | 
			
		||||
  name?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,145 +2,30 @@ import {
 | 
			
		||||
  Router,
 | 
			
		||||
  RouteMeta,
 | 
			
		||||
  createRouter,
 | 
			
		||||
  RouteComponent,
 | 
			
		||||
  RouteRecordName,
 | 
			
		||||
  createWebHashHistory,
 | 
			
		||||
  RouteRecordNormalized
 | 
			
		||||
  createWebHashHistory
 | 
			
		||||
} from "vue-router";
 | 
			
		||||
import { toRouteType } from "./types";
 | 
			
		||||
import { openLink } from "/@/utils/link";
 | 
			
		||||
import NProgress from "/@/utils/progress";
 | 
			
		||||
import { split, uniqBy } from "lodash-es";
 | 
			
		||||
import { useTimeoutFn } from "@vueuse/core";
 | 
			
		||||
import { RouteConfigs } from "/@/layout/types";
 | 
			
		||||
import { constantRoutes } from "./modules";
 | 
			
		||||
import { transformI18n } from "/@/plugins/i18n";
 | 
			
		||||
import { storageSession, storageLocal } from "/@/utils/storage";
 | 
			
		||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
 | 
			
		||||
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
 | 
			
		||||
 | 
			
		||||
// 静态路由
 | 
			
		||||
import tabsRouter from "./modules/tabs";
 | 
			
		||||
import homeRouter from "./modules/home";
 | 
			
		||||
import Layout from "/@/layout/index.vue";
 | 
			
		||||
import errorRouter from "./modules/error";
 | 
			
		||||
import editorRouter from "./modules/editor";
 | 
			
		||||
import nestedRouter from "./modules/nested";
 | 
			
		||||
import externalLink from "./modules/externalLink";
 | 
			
		||||
import remainingRouter from "./modules/remaining";
 | 
			
		||||
import flowChartRouter from "./modules/flowchart";
 | 
			
		||||
import componentsRouter from "./modules/components";
 | 
			
		||||
 | 
			
		||||
// 动态路由
 | 
			
		||||
import { getAsyncRoutes } from "/@/api/routes";
 | 
			
		||||
 | 
			
		||||
// https://cn.vitejs.dev/guide/features.html#glob-import
 | 
			
		||||
const modulesRoutes = import.meta.glob("/src/views/*/*/*.vue");
 | 
			
		||||
 | 
			
		||||
const constantRoutes: Array<RouteComponent> = [
 | 
			
		||||
  tabsRouter,
 | 
			
		||||
  homeRouter,
 | 
			
		||||
  flowChartRouter,
 | 
			
		||||
  editorRouter,
 | 
			
		||||
  componentsRouter,
 | 
			
		||||
  nestedRouter,
 | 
			
		||||
  externalLink,
 | 
			
		||||
  errorRouter
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// 按照路由中meta下的rank等级升序来排序路由
 | 
			
		||||
export const ascending = arr => {
 | 
			
		||||
  return arr.sort((a: any, b: any) => {
 | 
			
		||||
    return a?.meta?.rank - b?.meta?.rank;
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 将所有静态路由导出
 | 
			
		||||
export const constantRoutesArr: Array<RouteComponent> = ascending(
 | 
			
		||||
  constantRoutes
 | 
			
		||||
).concat(...remainingRouter);
 | 
			
		||||
 | 
			
		||||
// 过滤meta中showLink为false的路由
 | 
			
		||||
export const filterTree = data => {
 | 
			
		||||
  const newTree = data.filter(v => v.meta.showLink);
 | 
			
		||||
  newTree.forEach(v => v.children && (v.children = filterTree(v.children)));
 | 
			
		||||
  return newTree;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 从路由中提取keepAlive为true的name组成数组(此处本项目中并没有用到,只是暴露个方法)
 | 
			
		||||
export const getAliveRoute = () => {
 | 
			
		||||
  const alivePageList = [];
 | 
			
		||||
  const recursiveSearch = treeLists => {
 | 
			
		||||
    if (!treeLists || !treeLists.length) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    for (let i = 0; i < treeLists.length; i++) {
 | 
			
		||||
      if (treeLists[i]?.meta?.keepAlive) alivePageList.push(treeLists[i].name);
 | 
			
		||||
      recursiveSearch(treeLists[i].children);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  recursiveSearch(router.options.routes);
 | 
			
		||||
  return alivePageList;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 批量删除缓存路由
 | 
			
		||||
export const delAliveRoutes = (delAliveRouteList: Array<RouteConfigs>) => {
 | 
			
		||||
  delAliveRouteList.forEach(route => {
 | 
			
		||||
    usePermissionStoreHook().cacheOperate({
 | 
			
		||||
      mode: "delete",
 | 
			
		||||
      name: route?.name
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 处理缓存路由(添加、删除、刷新)
 | 
			
		||||
export const handleAliveRoute = (
 | 
			
		||||
  matched: RouteRecordNormalized[],
 | 
			
		||||
  mode?: string
 | 
			
		||||
) => {
 | 
			
		||||
  switch (mode) {
 | 
			
		||||
    case "add":
 | 
			
		||||
      matched.forEach(v => {
 | 
			
		||||
        usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
 | 
			
		||||
      });
 | 
			
		||||
      break;
 | 
			
		||||
    case "delete":
 | 
			
		||||
      usePermissionStoreHook().cacheOperate({
 | 
			
		||||
        mode: "delete",
 | 
			
		||||
        name: matched[matched.length - 1].name
 | 
			
		||||
      });
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      usePermissionStoreHook().cacheOperate({
 | 
			
		||||
        mode: "delete",
 | 
			
		||||
        name: matched[matched.length - 1].name
 | 
			
		||||
      });
 | 
			
		||||
      useTimeoutFn(() => {
 | 
			
		||||
        matched.forEach(v => {
 | 
			
		||||
          usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
 | 
			
		||||
        });
 | 
			
		||||
      }, 100);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 过滤后端传来的动态路由 重新生成规范路由
 | 
			
		||||
export const addAsyncRoutes = (arrRoutes: Array<RouteComponent>) => {
 | 
			
		||||
  if (!arrRoutes || !arrRoutes.length) return;
 | 
			
		||||
  arrRoutes.forEach((v: any) => {
 | 
			
		||||
    if (v.redirect) {
 | 
			
		||||
      v.component = Layout;
 | 
			
		||||
    } else {
 | 
			
		||||
      v.component = modulesRoutes[`/src/views${v.path}/index.vue`];
 | 
			
		||||
    }
 | 
			
		||||
    if (v.children) {
 | 
			
		||||
      addAsyncRoutes(v.children);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  return arrRoutes;
 | 
			
		||||
};
 | 
			
		||||
import { storageSession, storageLocal } from "/@/utils/storage";
 | 
			
		||||
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
 | 
			
		||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
 | 
			
		||||
import {
 | 
			
		||||
  initRouter,
 | 
			
		||||
  getParentPaths,
 | 
			
		||||
  findRouteByPath,
 | 
			
		||||
  handleAliveRoute
 | 
			
		||||
} from "./utils";
 | 
			
		||||
 | 
			
		||||
// 创建路由实例
 | 
			
		||||
export const router: Router = createRouter({
 | 
			
		||||
  history: createWebHashHistory(),
 | 
			
		||||
  routes: ascending(constantRoutes).concat(...remainingRouter),
 | 
			
		||||
  routes: constantRoutes.concat(...remainingRouter),
 | 
			
		||||
  scrollBehavior(to, from, savedPosition) {
 | 
			
		||||
    return new Promise(resolve => {
 | 
			
		||||
      if (savedPosition) {
 | 
			
		||||
@ -156,96 +41,10 @@ export const router: Router = createRouter({
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 初始化路由
 | 
			
		||||
export const initRouter = name => {
 | 
			
		||||
  return new Promise(resolve => {
 | 
			
		||||
    getAsyncRoutes({ name }).then(({ info }) => {
 | 
			
		||||
      if (info.length === 0) {
 | 
			
		||||
        usePermissionStoreHook().changeSetting(info);
 | 
			
		||||
      } else {
 | 
			
		||||
        addAsyncRoutes(info).map((v: any) => {
 | 
			
		||||
          // 防止重复添加路由
 | 
			
		||||
          if (
 | 
			
		||||
            router.options.routes.findIndex(value => value.path === v.path) !==
 | 
			
		||||
            -1
 | 
			
		||||
          ) {
 | 
			
		||||
            return;
 | 
			
		||||
          } else {
 | 
			
		||||
            // 切记将路由push到routes后还需要使用addRoute,这样路由才能正常跳转
 | 
			
		||||
            router.options.routes.push(v);
 | 
			
		||||
            // 最终路由进行升序
 | 
			
		||||
            ascending(router.options.routes);
 | 
			
		||||
            router.addRoute(v.name, v);
 | 
			
		||||
            usePermissionStoreHook().changeSetting(info);
 | 
			
		||||
          }
 | 
			
		||||
          resolve(router);
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      router.addRoute({
 | 
			
		||||
        path: "/:pathMatch(.*)",
 | 
			
		||||
        redirect: "/error/404"
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 重置路由
 | 
			
		||||
export function resetRouter() {
 | 
			
		||||
  router.getRoutes().forEach(route => {
 | 
			
		||||
    const { name } = route;
 | 
			
		||||
    if (name) {
 | 
			
		||||
      router.hasRoute(name) && router.removeRoute(name);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findRouteByPath(path, routes) {
 | 
			
		||||
  let res = routes.find(item => item.path == path);
 | 
			
		||||
  if (res) {
 | 
			
		||||
    return res;
 | 
			
		||||
  } else {
 | 
			
		||||
    for (let i = 0; i < routes.length; i++) {
 | 
			
		||||
      if (
 | 
			
		||||
        routes[i].children instanceof Array &&
 | 
			
		||||
        routes[i].children.length > 0
 | 
			
		||||
      ) {
 | 
			
		||||
        res = findRouteByPath(path, routes[i].children);
 | 
			
		||||
        if (res) {
 | 
			
		||||
          return res;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getParentPaths(path, routes) {
 | 
			
		||||
  // 深度遍历查找
 | 
			
		||||
  function dfs(routes, path, parents) {
 | 
			
		||||
    for (let i = 0; i < routes.length; i++) {
 | 
			
		||||
      const item = routes[i];
 | 
			
		||||
      // 找到path则返回父级path
 | 
			
		||||
      if (item.path === path) return parents;
 | 
			
		||||
      // children不存在或为空则不递归
 | 
			
		||||
      if (!item.children || !item.children.length) continue;
 | 
			
		||||
      // 往下查找时将当前path入栈
 | 
			
		||||
      parents.push(item.path);
 | 
			
		||||
 | 
			
		||||
      if (dfs(item.children, path, parents).length) return parents;
 | 
			
		||||
      // 深度遍历查找未找到时当前path 出栈
 | 
			
		||||
      parents.pop();
 | 
			
		||||
    }
 | 
			
		||||
    // 未找到时返回空数组
 | 
			
		||||
    return [];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return dfs(routes, path, []);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 路由白名单
 | 
			
		||||
const whiteList = ["/login"];
 | 
			
		||||
 | 
			
		||||
router.beforeEach((to, _from, next) => {
 | 
			
		||||
router.beforeEach((to: toRouteType, _from, next) => {
 | 
			
		||||
  if (to.meta?.keepAlive) {
 | 
			
		||||
    const newMatched = to.matched;
 | 
			
		||||
    handleAliveRoute(newMatched, "add");
 | 
			
		||||
@ -277,7 +76,7 @@ router.beforeEach((to, _from, next) => {
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      // 刷新
 | 
			
		||||
      if (usePermissionStoreHook().wholeRoutes.length === 0)
 | 
			
		||||
      if (usePermissionStoreHook().wholeMenus.length === 0)
 | 
			
		||||
        initRouter(name.username).then((router: Router) => {
 | 
			
		||||
          if (!useMultiTagsStoreHook().getMultiTagsCache) {
 | 
			
		||||
            const handTag = (
 | 
			
		||||
@ -293,14 +92,27 @@ router.beforeEach((to, _from, next) => {
 | 
			
		||||
                meta
 | 
			
		||||
              });
 | 
			
		||||
            };
 | 
			
		||||
            const parentPath = to.matched[0]?.path;
 | 
			
		||||
            // 未开启标签页缓存,刷新页面重定向到顶级路由(参考标签页操作例子)
 | 
			
		||||
            if (to.meta?.realPath) {
 | 
			
		||||
              const { path, name, meta } = to.matched[0]?.children[0];
 | 
			
		||||
              handTag(path, parentPath, name, meta);
 | 
			
		||||
              return router.push(path);
 | 
			
		||||
              const routes = router.options.routes;
 | 
			
		||||
              const { refreshRedirect } = to.meta;
 | 
			
		||||
              const { name, meta } = findRouteByPath(refreshRedirect, routes);
 | 
			
		||||
              handTag(
 | 
			
		||||
                refreshRedirect,
 | 
			
		||||
                getParentPaths(refreshRedirect, routes)[1],
 | 
			
		||||
                name,
 | 
			
		||||
                meta
 | 
			
		||||
              );
 | 
			
		||||
              return router.push(refreshRedirect);
 | 
			
		||||
            } else {
 | 
			
		||||
              const { path } = to;
 | 
			
		||||
              const routes = router.options.routes;
 | 
			
		||||
              const index = remainingRouter.findIndex(v => {
 | 
			
		||||
                return v.path == path;
 | 
			
		||||
              });
 | 
			
		||||
              const routes =
 | 
			
		||||
                index === -1
 | 
			
		||||
                  ? router.options.routes[0].children
 | 
			
		||||
                  : router.options.routes;
 | 
			
		||||
              const route = findRouteByPath(path, routes);
 | 
			
		||||
              const routePartent = getParentPaths(path, routes);
 | 
			
		||||
              handTag(
 | 
			
		||||
@ -315,7 +127,7 @@ router.beforeEach((to, _from, next) => {
 | 
			
		||||
          router.push(to.path);
 | 
			
		||||
          // 刷新页面更新标签栏与页面路由匹配
 | 
			
		||||
          const localRoutes = storageLocal.getItem("responsive-tags");
 | 
			
		||||
          const optionsRoutes = router.options?.routes;
 | 
			
		||||
          const optionsRoutes = router.options?.routes[0].children;
 | 
			
		||||
          const newLocalRoutes = [];
 | 
			
		||||
          optionsRoutes.forEach(ors => {
 | 
			
		||||
            localRoutes.forEach(lrs => {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										39
									
								
								src/router/modules/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/router/modules/index.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
// 静态路由
 | 
			
		||||
import tabsRouter from "./tabs";
 | 
			
		||||
import homeRouter from "./home";
 | 
			
		||||
import errorRouter from "./error";
 | 
			
		||||
import editorRouter from "./editor";
 | 
			
		||||
import nestedRouter from "./nested";
 | 
			
		||||
import externalLink from "./externalLink";
 | 
			
		||||
import flowChartRouter from "./flowchart";
 | 
			
		||||
import remainingRouter from "./remaining";
 | 
			
		||||
import componentsRouter from "./components";
 | 
			
		||||
import { RouteRecordRaw, RouteComponent } from "vue-router";
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  ascending,
 | 
			
		||||
  formatTwoStageRoutes,
 | 
			
		||||
  formatFlatteningRoutes
 | 
			
		||||
} from "../utils";
 | 
			
		||||
 | 
			
		||||
// 原始静态路由(未做任何处理)
 | 
			
		||||
const routes = [
 | 
			
		||||
  tabsRouter,
 | 
			
		||||
  homeRouter,
 | 
			
		||||
  errorRouter,
 | 
			
		||||
  nestedRouter,
 | 
			
		||||
  externalLink,
 | 
			
		||||
  editorRouter,
 | 
			
		||||
  flowChartRouter,
 | 
			
		||||
  componentsRouter
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// 导出处理后的静态路由(三级及以上的路由全部拍成二级)
 | 
			
		||||
export const constantRoutes: Array<RouteRecordRaw> = formatTwoStageRoutes(
 | 
			
		||||
  formatFlatteningRoutes(ascending(routes))
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// 用于渲染菜单,保持原始层级
 | 
			
		||||
export const constantMenus: Array<RouteComponent> = ascending(routes).concat(
 | 
			
		||||
  ...remainingRouter
 | 
			
		||||
);
 | 
			
		||||
@ -15,7 +15,7 @@ const nestedRouter = {
 | 
			
		||||
  children: [
 | 
			
		||||
    {
 | 
			
		||||
      path: "/nested/menu1",
 | 
			
		||||
      component: () => import("/@/views/nested/menu1/index.vue"),
 | 
			
		||||
      component: () => import("/@/layout/routerView/parent.vue"),
 | 
			
		||||
      name: "Menu1",
 | 
			
		||||
      meta: {
 | 
			
		||||
        title: "message.hsmenu1",
 | 
			
		||||
@ -38,7 +38,7 @@ const nestedRouter = {
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          path: "/nested/menu1/menu1-2",
 | 
			
		||||
          component: () => import("/@/views/nested/menu1/menu1-2/index.vue"),
 | 
			
		||||
          component: () => import("/@/layout/routerView/parent.vue"),
 | 
			
		||||
          name: "Menu1-2",
 | 
			
		||||
          redirect: "/nested/menu1/menu1-2/menu1-2-1",
 | 
			
		||||
          meta: {
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ const tabsRouter = {
 | 
			
		||||
  path: "/tabs",
 | 
			
		||||
  name: "reTabs",
 | 
			
		||||
  component: Layout,
 | 
			
		||||
  redirect: "/tags/index",
 | 
			
		||||
  redirect: "/tabs/index",
 | 
			
		||||
  meta: {
 | 
			
		||||
    icon: "IF-team-icontabs",
 | 
			
		||||
    title: "message.hstabs",
 | 
			
		||||
@ -32,7 +32,8 @@ const tabsRouter = {
 | 
			
		||||
        showLink: false,
 | 
			
		||||
        i18n: false,
 | 
			
		||||
        dynamicLevel: 3,
 | 
			
		||||
        realPath: "/tabs/detail"
 | 
			
		||||
        realPath: "/tabs/detail",
 | 
			
		||||
        refreshRedirect: "/tabs/index"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								src/router/types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/router/types.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
import { RouteLocationNormalized } from "vue-router";
 | 
			
		||||
 | 
			
		||||
export interface toRouteType extends RouteLocationNormalized {
 | 
			
		||||
  meta: {
 | 
			
		||||
    keepAlive: boolean;
 | 
			
		||||
    refreshRedirect: string;
 | 
			
		||||
    realPath: string;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										236
									
								
								src/router/utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								src/router/utils.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,236 @@
 | 
			
		||||
import {
 | 
			
		||||
  RouteRecordRaw,
 | 
			
		||||
  RouteComponent,
 | 
			
		||||
  RouteRecordNormalized
 | 
			
		||||
} from "vue-router";
 | 
			
		||||
import { router } from "./index";
 | 
			
		||||
import Layout from "/@/layout/index.vue";
 | 
			
		||||
import { useTimeoutFn } from "@vueuse/core";
 | 
			
		||||
import { RouteConfigs } from "/@/layout/types";
 | 
			
		||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
 | 
			
		||||
// https://cn.vitejs.dev/guide/features.html#glob-import
 | 
			
		||||
const modulesRoutes = import.meta.glob("/src/views/*/*/*.vue");
 | 
			
		||||
 | 
			
		||||
// 动态路由
 | 
			
		||||
import { getAsyncRoutes } from "/@/api/routes";
 | 
			
		||||
 | 
			
		||||
// 按照路由中meta下的rank等级升序来排序路由
 | 
			
		||||
const ascending = (arr: any[]) => {
 | 
			
		||||
  return arr.sort(
 | 
			
		||||
    (a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
 | 
			
		||||
      return a?.meta?.rank - b?.meta?.rank;
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 过滤meta中showLink为false的路由
 | 
			
		||||
const filterTree = (data: RouteComponent[]) => {
 | 
			
		||||
  const newTree = data.filter(
 | 
			
		||||
    (v: { meta: { showLink: boolean } }) => v.meta.showLink
 | 
			
		||||
  );
 | 
			
		||||
  newTree.forEach(
 | 
			
		||||
    (v: { children }) => v.children && (v.children = filterTree(v.children))
 | 
			
		||||
  );
 | 
			
		||||
  return newTree;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 批量删除缓存路由(keepalive)
 | 
			
		||||
const delAliveRoutes = (delAliveRouteList: Array<RouteConfigs>) => {
 | 
			
		||||
  delAliveRouteList.forEach(route => {
 | 
			
		||||
    usePermissionStoreHook().cacheOperate({
 | 
			
		||||
      mode: "delete",
 | 
			
		||||
      name: route?.name
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 通过path获取父级路径
 | 
			
		||||
const getParentPaths = (path: string, routes: RouteRecordRaw[]) => {
 | 
			
		||||
  // 深度遍历查找
 | 
			
		||||
  function dfs(routes: RouteRecordRaw[], path: string, parents: string[]) {
 | 
			
		||||
    for (let i = 0; i < routes.length; i++) {
 | 
			
		||||
      const item = routes[i];
 | 
			
		||||
      // 找到path则返回父级path
 | 
			
		||||
      if (item.path === path) return parents;
 | 
			
		||||
      // children不存在或为空则不递归
 | 
			
		||||
      if (!item.children || !item.children.length) continue;
 | 
			
		||||
      // 往下查找时将当前path入栈
 | 
			
		||||
      parents.push(item.path);
 | 
			
		||||
 | 
			
		||||
      if (dfs(item.children, path, parents).length) return parents;
 | 
			
		||||
      // 深度遍历查找未找到时当前path 出栈
 | 
			
		||||
      parents.pop();
 | 
			
		||||
    }
 | 
			
		||||
    // 未找到时返回空数组
 | 
			
		||||
    return [];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return dfs(routes, path, []);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 查找对应path的路由信息
 | 
			
		||||
const findRouteByPath = (path: string, routes: RouteRecordRaw[]) => {
 | 
			
		||||
  let res = routes.find((item: { path: string }) => item.path == path);
 | 
			
		||||
  if (res) {
 | 
			
		||||
    return res;
 | 
			
		||||
  } else {
 | 
			
		||||
    for (let i = 0; i < routes.length; i++) {
 | 
			
		||||
      if (
 | 
			
		||||
        routes[i].children instanceof Array &&
 | 
			
		||||
        routes[i].children.length > 0
 | 
			
		||||
      ) {
 | 
			
		||||
        res = findRouteByPath(path, routes[i].children);
 | 
			
		||||
        if (res) {
 | 
			
		||||
          return res;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 重置路由
 | 
			
		||||
const resetRouter = (): void => {
 | 
			
		||||
  router.getRoutes().forEach(route => {
 | 
			
		||||
    const { name } = route;
 | 
			
		||||
    if (name) {
 | 
			
		||||
      router.hasRoute(name) && router.removeRoute(name);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 初始化路由
 | 
			
		||||
const initRouter = (name: string) => {
 | 
			
		||||
  return new Promise(resolve => {
 | 
			
		||||
    getAsyncRoutes({ name }).then(({ info }) => {
 | 
			
		||||
      if (info.length === 0) {
 | 
			
		||||
        usePermissionStoreHook().changeSetting(info);
 | 
			
		||||
      } else {
 | 
			
		||||
        formatFlatteningRoutes(addAsyncRoutes(info)).map(
 | 
			
		||||
          (v: RouteRecordRaw) => {
 | 
			
		||||
            // 防止重复添加路由
 | 
			
		||||
            if (
 | 
			
		||||
              router.options.routes[0].children.findIndex(
 | 
			
		||||
                value => value.path === v.path
 | 
			
		||||
              ) !== -1
 | 
			
		||||
            ) {
 | 
			
		||||
              return;
 | 
			
		||||
            } else {
 | 
			
		||||
              // 切记将路由push到routes后还需要使用addRoute,这样路由才能正常跳转
 | 
			
		||||
              router.options.routes[0].children.push(v);
 | 
			
		||||
              // 最终路由进行升序
 | 
			
		||||
              ascending(router.options.routes[0].children);
 | 
			
		||||
              router.addRoute("home", v);
 | 
			
		||||
            }
 | 
			
		||||
            resolve(router);
 | 
			
		||||
          }
 | 
			
		||||
        );
 | 
			
		||||
        usePermissionStoreHook().changeSetting(info);
 | 
			
		||||
      }
 | 
			
		||||
      router.addRoute({
 | 
			
		||||
        path: "/:pathMatch(.*)",
 | 
			
		||||
        redirect: "/error/404"
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 将多级嵌套路由处理成一维数组
 | 
			
		||||
 * @param routesList 传入路由
 | 
			
		||||
 * @returns 返回处理后的一维路由
 | 
			
		||||
 */
 | 
			
		||||
const formatFlatteningRoutes = (routesList: RouteRecordRaw[]) => {
 | 
			
		||||
  if (routesList.length <= 0) return routesList;
 | 
			
		||||
  for (let i = 0; i < routesList.length; i++) {
 | 
			
		||||
    if (routesList[i].children) {
 | 
			
		||||
      routesList = routesList
 | 
			
		||||
        .slice(0, i + 1)
 | 
			
		||||
        .concat(routesList[i].children, routesList.slice(i + 1));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return routesList;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 一维数组处理成多级嵌套数组(三级及以上的路由全部拍成二级,keep-alive 只支持到二级缓存)
 | 
			
		||||
 * https://github.com/xiaoxian521/vue-pure-admin/issues/67
 | 
			
		||||
 * @param routesList 处理后的一维路由菜单数组
 | 
			
		||||
 * @returns 返回将一维数组重新处理成规定路由的格式
 | 
			
		||||
 */
 | 
			
		||||
const formatTwoStageRoutes = (routesList: RouteRecordRaw[]) => {
 | 
			
		||||
  if (routesList.length <= 0) return routesList;
 | 
			
		||||
  const newRoutesList: RouteRecordRaw[] = [];
 | 
			
		||||
  routesList.forEach((v: RouteRecordRaw) => {
 | 
			
		||||
    if (v.path === "/") {
 | 
			
		||||
      newRoutesList.push({
 | 
			
		||||
        component: v.component,
 | 
			
		||||
        name: v.name,
 | 
			
		||||
        path: v.path,
 | 
			
		||||
        redirect: v.redirect,
 | 
			
		||||
        meta: v.meta,
 | 
			
		||||
        children: []
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      newRoutesList[0].children.push({ ...v });
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  return newRoutesList;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 处理缓存路由(添加、删除、刷新)
 | 
			
		||||
const handleAliveRoute = (matched: RouteRecordNormalized[], mode?: string) => {
 | 
			
		||||
  switch (mode) {
 | 
			
		||||
    case "add":
 | 
			
		||||
      matched.forEach(v => {
 | 
			
		||||
        usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
 | 
			
		||||
      });
 | 
			
		||||
      break;
 | 
			
		||||
    case "delete":
 | 
			
		||||
      usePermissionStoreHook().cacheOperate({
 | 
			
		||||
        mode: "delete",
 | 
			
		||||
        name: matched[matched.length - 1].name
 | 
			
		||||
      });
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      usePermissionStoreHook().cacheOperate({
 | 
			
		||||
        mode: "delete",
 | 
			
		||||
        name: matched[matched.length - 1].name
 | 
			
		||||
      });
 | 
			
		||||
      useTimeoutFn(() => {
 | 
			
		||||
        matched.forEach(v => {
 | 
			
		||||
          usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name });
 | 
			
		||||
        });
 | 
			
		||||
      }, 100);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 过滤后端传来的动态路由 重新生成规范路由
 | 
			
		||||
const addAsyncRoutes = (arrRoutes: Array<RouteRecordRaw>) => {
 | 
			
		||||
  if (!arrRoutes || !arrRoutes.length) return;
 | 
			
		||||
  arrRoutes.forEach((v: RouteRecordRaw) => {
 | 
			
		||||
    if (v.redirect) {
 | 
			
		||||
      v.component = Layout;
 | 
			
		||||
    } else {
 | 
			
		||||
      v.component = modulesRoutes[`/src/views${v.path}/index.vue`];
 | 
			
		||||
    }
 | 
			
		||||
    if (v.children) {
 | 
			
		||||
      addAsyncRoutes(v.children);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  return arrRoutes;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  ascending,
 | 
			
		||||
  filterTree,
 | 
			
		||||
  initRouter,
 | 
			
		||||
  resetRouter,
 | 
			
		||||
  addAsyncRoutes,
 | 
			
		||||
  delAliveRoutes,
 | 
			
		||||
  getParentPaths,
 | 
			
		||||
  findRouteByPath,
 | 
			
		||||
  handleAliveRoute,
 | 
			
		||||
  formatTwoStageRoutes,
 | 
			
		||||
  formatFlatteningRoutes
 | 
			
		||||
};
 | 
			
		||||
@ -1,28 +1,32 @@
 | 
			
		||||
import { defineStore } from "pinia";
 | 
			
		||||
import { store } from "/@/store";
 | 
			
		||||
import { cacheType } from "./types";
 | 
			
		||||
import { constantRoutesArr, ascending, filterTree } from "/@/router/index";
 | 
			
		||||
import { RouteConfigs } from "/@/layout/types";
 | 
			
		||||
import { constantMenus } from "/@/router/modules";
 | 
			
		||||
import { ascending, filterTree } from "/@/router/utils";
 | 
			
		||||
 | 
			
		||||
export const usePermissionStore = defineStore({
 | 
			
		||||
  id: "pure-permission",
 | 
			
		||||
  state: () => ({
 | 
			
		||||
    // 静态路由
 | 
			
		||||
    constantRoutes: constantRoutesArr,
 | 
			
		||||
    wholeRoutes: [],
 | 
			
		||||
    // 静态路由生成的菜单
 | 
			
		||||
    constantMenus,
 | 
			
		||||
    // 整体路由生成的菜单(静态、动态)
 | 
			
		||||
    wholeMenus: [],
 | 
			
		||||
    buttonAuth: [],
 | 
			
		||||
    // 缓存页面keepAlive
 | 
			
		||||
    cachePageList: []
 | 
			
		||||
  }),
 | 
			
		||||
  actions: {
 | 
			
		||||
    // 获取异步路由菜单
 | 
			
		||||
    asyncActionRoutes(routes) {
 | 
			
		||||
      if (this.wholeRoutes.length > 0) return;
 | 
			
		||||
      this.wholeRoutes = filterTree(
 | 
			
		||||
        ascending(this.constantRoutes.concat(routes))
 | 
			
		||||
      if (this.wholeMenus.length > 0) return;
 | 
			
		||||
      this.wholeMenus = filterTree(
 | 
			
		||||
        ascending(this.constantMenus.concat(routes))
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      const getButtonAuth = (arrRoutes: Array<string>) => {
 | 
			
		||||
      const getButtonAuth = (arrRoutes: Array<RouteConfigs>) => {
 | 
			
		||||
        if (!arrRoutes || !arrRoutes.length) return;
 | 
			
		||||
        arrRoutes.forEach((v: any) => {
 | 
			
		||||
        arrRoutes.forEach((v: RouteConfigs) => {
 | 
			
		||||
          if (v.meta && v.meta.authority) {
 | 
			
		||||
            this.buttonAuth.push(...v.meta.authority);
 | 
			
		||||
          }
 | 
			
		||||
@ -32,7 +36,7 @@ export const usePermissionStore = defineStore({
 | 
			
		||||
        });
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      getButtonAuth(this.wholeRoutes);
 | 
			
		||||
      getButtonAuth(this.wholeMenus);
 | 
			
		||||
    },
 | 
			
		||||
    async changeSetting(routes) {
 | 
			
		||||
      await this.asyncActionRoutes(routes);
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref, computed } from "vue";
 | 
			
		||||
import { useRouter } from "vue-router";
 | 
			
		||||
import { initRouter } from "/@/router";
 | 
			
		||||
import { initRouter } from "/@/router/utils";
 | 
			
		||||
import { storageSession } from "/@/utils/storage";
 | 
			
		||||
import { addClass, removeClass } from "/@/utils/operate";
 | 
			
		||||
import bg from "/@/assets/login/bg.png";
 | 
			
		||||
 | 
			
		||||
@ -1,35 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <p>{{ $t("message.hsmenu1") }}</p>
 | 
			
		||||
    <router-view>
 | 
			
		||||
      <template #default="{ Component, route }">
 | 
			
		||||
        <transition appear name="fade-transform" mode="out-in">
 | 
			
		||||
          <keep-alive
 | 
			
		||||
            v-if="keepAlive"
 | 
			
		||||
            :include="usePermissionStoreHook().cachePageList"
 | 
			
		||||
          >
 | 
			
		||||
            <component :is="Component" :key="route.fullPath" />
 | 
			
		||||
          </keep-alive>
 | 
			
		||||
          <component v-else :is="Component" :key="route.fullPath" />
 | 
			
		||||
        </transition>
 | 
			
		||||
      </template>
 | 
			
		||||
    </router-view>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { ref, getCurrentInstance } from "vue";
 | 
			
		||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
 | 
			
		||||
export default {
 | 
			
		||||
  name: "Menu1",
 | 
			
		||||
  setup() {
 | 
			
		||||
    const keepAlive: Boolean = ref(
 | 
			
		||||
      getCurrentInstance().appContext.config.globalProperties.$config?.KeepAlive
 | 
			
		||||
    );
 | 
			
		||||
    return {
 | 
			
		||||
      keepAlive,
 | 
			
		||||
      usePermissionStoreHook
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
@ -1,35 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <p style="text-indent: 2em">{{ $t("message.hsmenu1-2") }}</p>
 | 
			
		||||
    <router-view>
 | 
			
		||||
      <template #default="{ Component, route }">
 | 
			
		||||
        <transition appear name="fade-transform" mode="out-in">
 | 
			
		||||
          <keep-alive
 | 
			
		||||
            v-if="keepAlive"
 | 
			
		||||
            :include="usePermissionStoreHook().cachePageList"
 | 
			
		||||
          >
 | 
			
		||||
            <component :is="Component" :key="route.fullPath" />
 | 
			
		||||
          </keep-alive>
 | 
			
		||||
          <component v-else :is="Component" :key="route.fullPath" />
 | 
			
		||||
        </transition>
 | 
			
		||||
      </template>
 | 
			
		||||
    </router-view>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { ref, getCurrentInstance } from "vue";
 | 
			
		||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
 | 
			
		||||
export default {
 | 
			
		||||
  name: "Menu1-2",
 | 
			
		||||
  setup() {
 | 
			
		||||
    const keepAlive: Boolean = ref(
 | 
			
		||||
      getCurrentInstance().appContext.config.globalProperties.$config?.KeepAlive
 | 
			
		||||
    );
 | 
			
		||||
    return {
 | 
			
		||||
      keepAlive,
 | 
			
		||||
      usePermissionStoreHook
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										1
									
								
								types/global.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								types/global.d.ts
									
									
									
									
										vendored
									
									
								
							@ -85,7 +85,6 @@ declare global {
 | 
			
		||||
    HideTabs?: boolean;
 | 
			
		||||
    MapConfigure?: {
 | 
			
		||||
      amapKey?: string;
 | 
			
		||||
      baiduKey?: string;
 | 
			
		||||
      options: {
 | 
			
		||||
        resizeEnable?: boolean;
 | 
			
		||||
        center?: number[];
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user