refactor(navigate): 重构路由查找逻辑以支持任意深度嵌套

- 实现递归查找路由函数,支持多层嵌套路由的定位
- 新增通过路径和名称两种方式查找路由的功能
- 添加构建完整路由路径的工具函数
- 完善路由查找相关单元测试覆盖
- 优化路径规范化处理,去除多余斜杠
- 改进错误处理逻辑,提升代码健壮性
This commit is contained in:
hz
2025-12-17 17:59:58 +08:00
parent 43596c8003
commit 4d684c2797
2 changed files with 469 additions and 28 deletions

View File

@@ -20,43 +20,31 @@ export function navigateRouter({ path = '', name = '', title = '', params = {} }
routerInfo: { query: params }
}
if (path) {
const route = routes.find(item => {
if (item.children) {
// 可能后面 children 存在多个,目前暂时先判定一层。 待优化
return item.children.find(childItem => `${item.path}/${childItem.path}` === path)
}
return item.path === path
})
if (!route) {
// 查找路由支持任意深度的嵌套
const routeInfo = findRouteByPath(routes, path)
if (!routeInfo) {
console.error('在 routers 中无法找到该路径,请检查路径是否正确')
return
return false
}
options.extra = {
title: route.meta && route.meta.title ? route.meta.title : '',
title: routeInfo.route.meta && routeInfo.route.meta.title ? routeInfo.route.meta.title : '',
url: location.origin + '/#' + path + '?' + processJson(params)
}
options.routerInfo = { ...options.routerInfo, path }
} else if (name) {
const route = routes.find(item => {
if (item.children) {
// 可能后面 children 存在多个,目前暂时先判定一层。 待优化
return item.children.find(childItem => childItem.name === name)
}
return item.name === name
})
if (!route) {
console.error('在 routers 中无法找到该路径,请检查路径是否正确')
return
// 通过名称查找路由也支持任意深度嵌套
const routeInfo = findRouteByName(routes, name)
if (!routeInfo) {
console.error('在 routers 中无法找到该名称,请检查名称是否正确')
return false
}
// 如何存在子元素, 需要重新更改 path 地址
if (route.children) {
route.path = `${route.path}/${route.children.find(childItem => childItem.name === name).path}`
}
console.log(route)
// 构建完整的路径
const fullPath = buildFullPath(routeInfo.routeStack)
options.extra = {
title: route.meta && route.meta.title ? route.meta.title : '',
url: location.origin + '/#' + route.path + '?' + processJson(params)
title: routeInfo.route.meta && routeInfo.route.meta.title ? routeInfo.route.meta.title : '',
url: location.origin + '/#' + fullPath + '?' + processJson(params)
}
options.routerInfo = { ...options.routerInfo, name }
@@ -65,6 +53,130 @@ export function navigateRouter({ path = '', name = '', title = '', params = {} }
jump(options)
}
/**
* 递归查找具有指定路径的路由(支持多层嵌套)
* @param routes {import("vue-router").RouteConfig[]} 路由数组
* @param targetPath {String}目标路径
* @param parentPath {String}父级路径
* @return {undefined| {
* route: import("vue-router").RouteConfig,
* fullPath: String
* }}
*/
function findRouteByPath(routes, targetPath, parentPath = '') {
if (!routes || !Array.isArray(routes)) return undefined
// 规范化目标路径,移除多余的斜杠
targetPath = targetPath.replace(/\/+/g, '/')
for (const route of routes) {
// 构造当前路由的完整路径
let currentPath = parentPath
if (currentPath && !currentPath.endsWith('/') && route.path && !route.path.startsWith('/')) {
currentPath += '/' + route.path
} else {
currentPath += route.path
}
// 移除多余的斜杠
currentPath = currentPath.replace(/\/+/g, '/')
// 检查当前路由是否匹配
if (currentPath === targetPath) {
return {
route: route,
fullPath: currentPath
}
}
// 递归检查子路由
if (route.children && route.children.length > 0) {
const matchedChild = findRouteByPath(route.children, targetPath, currentPath)
if (matchedChild) {
return matchedChild
}
}
}
return undefined
}
/**
* 递归查找具有指定名称的路由(支持多层嵌套)
* @param routes 路由数组
* @param targetName 目标名称
* @param routeStack 用于追踪路由层级的堆栈
* @return {undefined| {
* route: import("vue-router").RouteConfig,
* routeStack: import("vue-router").RouteConfig[]
* }}
*/
function findRouteByName(routes, targetName, routeStack = []) {
if (!routes || !Array.isArray(routes)) return undefined
for (const route of routes) {
// 创建当前路由的堆栈副本
const currentStack = [...routeStack, route]
// 检查当前路由是否匹配
if (route.name === targetName) {
return {
route: route,
routeStack: currentStack
}
}
// 递归检查子路由
if (route.children && route.children.length > 0) {
const matchedChild = findRouteByName(route.children, targetName, currentStack)
if (matchedChild) {
return matchedChild
}
}
}
return undefined
}
/**
* 根据路由堆栈构建完整路径
* @param routeStack 路由堆栈
* @returns string
*/
function buildFullPath(routeStack) {
if (!routeStack || routeStack.length === 0) return ''
// 提取所有路由的路径并正确拼接
let fullPath = ''
routeStack.forEach((route, index) => {
// 特殊处理第一个路由
if (index === 0) {
fullPath += route.path
} else {
if (fullPath && !fullPath.endsWith('/') && route.path && !route.path.startsWith('/')) {
fullPath += '/' + route.path
} else if (fullPath.endsWith('/') && route.path.startsWith('/')) {
// 如果fullPath以/结尾而route.path以/开头,则需要去掉一个/
fullPath += route.path.substring(1)
} else {
fullPath += route.path
}
}
})
// 移除多余的斜杠
fullPath = fullPath.replace(/\/+/g, '/')
// 确保不会在末尾留下斜杠(除非是根路径)
if (fullPath.length > 1 && fullPath.endsWith('/')) {
fullPath = fullPath.slice(0, -1)
}
return fullPath
}
/**
* 处理 json 键值对,到 url 中
*/
@@ -79,4 +191,4 @@ function processJson(json) {
}
}
return url.substring(0, url.length - 1)
}
}