xiaoxian521
3 years ago
25 changed files with 534 additions and 270 deletions
-
4.env.development
-
3.env.production
-
3build/index.ts
-
1mock/asyncRoutes.ts
-
2package.json
-
2pnpm-lock.yaml
-
1public/serverConfig.json
-
2src/directives/permission/index.ts
-
3src/layout/components/sidebar/horizontal.vue
-
3src/layout/components/sidebar/vertical.vue
-
2src/layout/components/tag/index.scss
-
86src/layout/components/tag/index.vue
-
15src/layout/routerView/parent.vue
-
3src/layout/types.ts
-
279src/router/index.ts
-
25src/router/modules/index.ts
-
9src/router/types.ts
-
289src/router/utils.ts
-
13src/store/modules/multiTags.ts
-
24src/store/modules/permission.ts
-
1src/store/modules/types.ts
-
2src/views/login.vue
-
6src/views/permission/button/index.vue
-
6src/views/permission/page/index.vue
-
2types/global.d.ts
@ -1,5 +1,8 @@ |
|||
# 线上环境项目打包路径 |
|||
VITE_PUBLIC_PATH = / |
|||
|
|||
# 线上环境路由历史模式 |
|||
VITE_ROUTER_HISTORY = "hash" |
|||
|
|||
# 线上环境后端地址 |
|||
VITE_PROXY_DOMAIN_REAL = "" |
@ -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> |
@ -0,0 +1,25 @@ |
|||
// 静态路由
|
|||
import homeRouter from "./home"; |
|||
import errorRouter from "./error"; |
|||
import externalLink from "./externalLink"; |
|||
import remainingRouter from "./remaining"; |
|||
import { RouteRecordRaw, RouteComponent } from "vue-router"; |
|||
|
|||
import { |
|||
ascending, |
|||
formatTwoStageRoutes, |
|||
formatFlatteningRoutes |
|||
} from "../utils"; |
|||
|
|||
// 原始静态路由(未做任何处理)
|
|||
const routes = [homeRouter, errorRouter, externalLink]; |
|||
|
|||
// 导出处理后的静态路由(三级及以上的路由全部拍成二级)
|
|||
export const constantRoutes: Array<RouteRecordRaw> = formatTwoStageRoutes( |
|||
formatFlatteningRoutes(ascending(routes)) |
|||
); |
|||
|
|||
// 用于渲染菜单,保持原始层级
|
|||
export const constantMenus: Array<RouteComponent> = ascending(routes).concat( |
|||
...remainingRouter |
|||
); |
@ -0,0 +1,9 @@ |
|||
import { RouteLocationNormalized } from "vue-router"; |
|||
|
|||
export interface toRouteType extends RouteLocationNormalized { |
|||
meta: { |
|||
keepAlive: boolean; |
|||
refreshRedirect: string; |
|||
realPath: string; |
|||
}; |
|||
} |
@ -0,0 +1,289 @@ |
|||
import { |
|||
RouterHistory, |
|||
RouteRecordRaw, |
|||
RouteComponent, |
|||
createWebHistory, |
|||
createWebHashHistory, |
|||
RouteRecordNormalized |
|||
} from "vue-router"; |
|||
import { router } from "./index"; |
|||
import { loadEnv } from "../../build"; |
|||
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); |
|||
if (!router.hasRoute(v?.name)) router.addRoute(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 { |
|||
if (v.meta.realPath) { |
|||
v.component = modulesRoutes[`/src/views${v.meta.realPath}/index.vue`]; |
|||
} else { |
|||
v.component = modulesRoutes[`/src/views${v.path}/index.vue`]; |
|||
} |
|||
} |
|||
if (v.children) { |
|||
addAsyncRoutes(v.children); |
|||
} |
|||
}); |
|||
return arrRoutes; |
|||
}; |
|||
|
|||
// 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html
|
|||
const getHistoryMode = (): RouterHistory => { |
|||
const routerHistory = loadEnv().VITE_ROUTER_HISTORY; |
|||
// len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1
|
|||
const historyMode = routerHistory.split(","); |
|||
const leftMode = historyMode[0]; |
|||
const rightMode = historyMode[1]; |
|||
// no param
|
|||
if (historyMode.length === 1) { |
|||
if (leftMode === "hash") { |
|||
return createWebHashHistory(""); |
|||
} else if (leftMode === "h5") { |
|||
return createWebHistory(""); |
|||
} |
|||
} //has param
|
|||
else if (historyMode.length === 2) { |
|||
if (leftMode === "hash") { |
|||
return createWebHashHistory(rightMode); |
|||
} else if (leftMode === "h5") { |
|||
return createWebHistory(rightMode); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
// 是否有权限
|
|||
const hasPermissions = (value: Array<string>): boolean => { |
|||
if (value && value instanceof Array && value.length > 0) { |
|||
const roles = usePermissionStoreHook().buttonAuth; |
|||
const permissionRoles = value; |
|||
|
|||
const hasPermission = roles.some(role => { |
|||
return permissionRoles.includes(role); |
|||
}); |
|||
|
|||
if (!hasPermission) { |
|||
return false; |
|||
} |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
}; |
|||
|
|||
export { |
|||
ascending, |
|||
filterTree, |
|||
initRouter, |
|||
resetRouter, |
|||
hasPermissions, |
|||
getHistoryMode, |
|||
addAsyncRoutes, |
|||
delAliveRoutes, |
|||
getParentPaths, |
|||
findRouteByPath, |
|||
handleAliveRoute, |
|||
formatTwoStageRoutes, |
|||
formatFlatteningRoutes |
|||
}; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue