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 }, []) }