UNPKG

@truenewx/tnxvue3

Version:

互联网技术解决方案:Vue3扩展支持

184 lines (167 loc) 6.66 kB
/** * 基于Vue的路由器构建函数 */ import { createRouter, createWebHashHistory, Router, RouteRecordRaw, RouterHistory, RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteLocationNormalizedLoadedGeneric, NavigationGuardNext, } from 'vue-router'; import * as NetUtil from '../../tnxcore/src/util/net.ts'; export type RouteItem = { caption: string; path: string; icon?: string; page?: string; component?: () => Promise<any>; redirect?: never; alias?: string; subs?: RouteItem[]; } function addRoute(routes: RouteRecordRaw[], superiorPath: string, item: RouteItem, fnImportPage?: (page: string) => Promise<any>): void { if (item && item.path) { let page = item.page || item.path.replace(/\/:[a-zA-Z0-9_]+/g, ''); let route: RouteRecordRaw = { path: item.path, meta: { superiorPath: superiorPath, page: page, cache: {}, // 路由级缓存 isHistory() { // 通过setTimeout()方式调用才能确保获得正确结果 return this.historyFrom !== undefined; }, }, component: item.component, }; if (!route.component && fnImportPage) { route.component = () => { return fnImportPage(page); }; } // 如果直接定义route的redirect/alias字段,则item的redirect/alias为undefined时,route仍然有redirect/alias字段,只是其值为undefined,这将导致VueRouter报错 if (item.redirect) { route.redirect = item.redirect; } if (item.alias) { route.alias = item.alias; } routes.push(route); } } function applyItemsToRoutes(superiorPath: string, items: RouteItem[], routes: RouteRecordRaw[], fnImportPage?: (page: string) => Promise<any>): void { if (items && items.length) { items.forEach(item => { if (item) { addRoute(routes, superiorPath, item, fnImportPage); applyItemsToRoutes(item.path, item.subs, routes, fnImportPage); } }); } } function instantiatePath(path: string, params?: Record<string, any>): string { if (path && path.includes('/:')) { if (params) { Object.keys(params).forEach(key => { path = path.replace('/:' + key + '/', '/' + params[key] + '/'); }); } if (path.includes('/:')) { // 参数替换完之后,还有路径参数,则为无效路径,返回首页 console.warn( '路径中的参数无法获得参数值,请确保具有参数的路径所属菜单项的下级菜单路径包含相同的参数:' + path); return '/'; } } return path; } function getCurrentRoute(router: Router): RouteLocationNormalizedLoadedGeneric { return router.currentRoute.value; } export type VueRouter = Router & { history: RouterHistory; prev?: RouteLocationNormalizedLoaded; $beforeLeaveHandlers: Record<string, (to: RouteLocationNormalized) => boolean>; beforeLeave: (handler: (to: RouteLocationNormalized) => boolean) => void; pushState: (path: string) => boolean; replaceState: (path: string) => boolean; backTo: (path?: string) => void; } export default function (items: RouteItem[], fnImportPage?: (page: string) => Promise<any>): VueRouter { const routes: RouteRecordRaw[] = []; applyItemsToRoutes('', items, routes, fnImportPage); const routerHistory = createWebHashHistory(); const router: VueRouter = createRouter({ history: routerHistory, routes, }) as VueRouter; router.history = routerHistory; // 浏览器的返回事件触发位于VueRouter的钩子执行和页面渲染之后,这意味着$route.meta.historyFrom必须在页面渲染完之后才具有正确的值 // if (window.history && window.history.pushState) { // window.history.pushState(null, null, document.URL); // } window.addEventListener('popstate', () => { let $route = getCurrentRoute(router); if ($route) { $route.meta.historyFrom = router.history.state.forward; } }, false); // 注册离开页面前事件处理支持 router.$beforeLeaveHandlers = {}; router.beforeLeave = function (handler: (to: RouteLocationNormalized) => boolean): void { let $route = getCurrentRoute(router); let path = $route.path; router.$beforeLeaveHandlers[path] = handler; }; router.beforeEach((to: RouteLocationNormalized, from: RouteLocationNormalizedLoaded, next: NavigationGuardNext): void => { let allow = true; let beforeLeaveHandler = router.$beforeLeaveHandlers[from.path]; if (beforeLeaveHandler) { if (beforeLeaveHandler(to) === false) { allow = false; } } if (allow) { next(); } }); router.afterEach((to: RouteLocationNormalized, from: RouteLocationNormalizedLoaded): void => { router.prev = from; // 前后路径相同,但全路径不同(意味着参数不同),则需要刷新页面,否则页面不会刷新 if (to.path === from.path && to.fullPath !== from.fullPath) { window.location.reload(); } }); router.backTo = function (path?: string): void { if (!router.prev?.path) { // 没有path,说明当前页面为刷新后进入的第一个页面,无法简单返回 let $route = getCurrentRoute(router); if (!path) { // 未指定默认返回路径,则返回上一级页面 path = $route.meta.superiorPath as string; } path = instantiatePath(path, $route.params); if (path) { router.replace(path); return; } } router.back.call(router); } router.pushState = function (path: string): boolean { let success = NetUtil.pushState('#' + path); if (!success) { this.push(path); } return success; }; router.replaceState = function (path: string): boolean { let success = NetUtil.replaceState('#' + path); if (!success) { this.replace(path); } return success; }; return router; }