You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
5.4 KiB

import Layout from "@/layout"
import SkeletonPage from '@/view/_common/SkeletonPage'
import Page500 from '@/view/_app/500'
import {isEmpty} from "@/util"
import {isExternal, isString} from "@/util/validate"
//json字符串转路由配置
export function str2routeConfig(str) {
const parseOpt = (key, value) => {
if (key === 'dynamicTitle' && isString(value)) {
return isEmpty(value) ? undefined : eval(`(${value})`)
}
return value
}
return JSON.parse(str, parseOpt)
}
//根据json数组生成路由
export function generateRoutes(jsonTree) {
//将深度为2的节点合并为由其最末子节点组成的数组
function mergeChildren(routes) {
function fun(child, parent = {path: ''}, list = []) {
const {children} = child
if (!isEmpty(parent.path)) {
child.path = `${parent.path}/${child.path}`
}
if (!children || !children.length) {
list.push(child)
}
else {
children.forEach(i => fun(i, child, list))
}
return list
}
routes.forEach(root => {
const {children} = root
if (children && children.length) {
const list = []
children.forEach(route => {
list.push(...fun(route))
})
root.children = list
}
})
}
//数据转换,同时剔除不合法的路由
function transform(routes, depth = 1) {
if (!Array.isArray(routes)) return
for (let i = routes.length - 1; i >= 0; i--) {
const {path, redirect, component, children, meta = {}} = routes[i]
//剔除外链节点以及无component且非iframe的节点
if (isExternal(path) || !component && !meta.iframe) {
routes.splice(i, 1)
continue
}
//清理一些空属性,避免vue-router出错
['name', 'component', 'meta'].forEach(key => {
if (key in routes[i] && isEmpty(routes[i][key])) {
delete routes[i][key]
}
})
//设置路由组件,当component是vue组件声明对象时,不会进行懒加载
if (component) {
if (isString(component) && component.toUpperCase() === 'LAYOUT') {
routes[i].component = Layout
}
else if (isString(component) || typeof component === 'function') {
routes[i].component = lazyLoadView(component)
}
}
//先尝试递归
transform(children, depth + 1)
//叶子节点统一设置props = true,省的动态路由还要手动加
if (!children || children.length <= 0) {
routes[i].props = true
}
//仅在根节点未设置redirect时,将其第一个子节点的路径设为redirect
else if (depth === 1 && isEmpty(redirect)) {
const rootPath = path.endsWith('/') ? path : `${path}/`
routes[i].redirect = `${rootPath}${children[0].path}`
}
}
}
//路由页面懒加载,入参可以是字符串、返回值为Promise的函数
function lazyLoadView(component) {
if (isString(component)) {
if (component.endsWith('/')) {
component += 'index'
}
else if (component.endsWith('Page')) {
component = component.replace('Page', '')
}
else {
component = component.replace('Page.vue', '')
}
}
//这里注意一点,如果设置了超时时间,那么超时后只能刷新整个页面才能重新加载该组件
const AsyncHandler = () => ({
component: typeof component === 'function' ? component() : import(`@/view/${component}Page.vue`),
loading: SkeletonPage,
error: Page500,
delay: 200,
timeout: 10000
})
return () => Promise.resolve({
abstract: true,
functional: true,
render(h, {data, children}) {
return h(AsyncHandler, data, children)
}
})
}
mergeChildren(jsonTree)
transform(jsonTree)
return jsonTree
}
//子路由继承父路由meta上的{noAuth, noCache},优先使用子路由的值
export function metaExtend(routes, parentMeta) {
const keys = ['noAuth', 'noCache']
routes.forEach(route => {
if (parentMeta) {
Object.keys(parentMeta).forEach(key => {
const value = parentMeta[key]
if (keys.includes(key) && !isEmpty(value)) {
if (!route.meta) {
route.meta = {[key]: value}
}
else if (isEmpty(route.meta[key])) {
route.meta[key] = value
}
}
})
}
route.children && metaExtend(route.children, route.meta)
})
}
//将require.context导出的对象处理为数组
export function context2array(context) {
return context.keys().reduce((modules, modulePath) => {
const value = context(modulePath).default
Array.isArray(value) ? modules.push(...value) : modules.push(value)
return modules
}, [])
}