diff --git a/apps/jingrow/frontend/src/app/layouts/AppHeader.vue b/apps/jingrow/frontend/src/app/layouts/AppHeader.vue index c90b2cb..06000bc 100644 --- a/apps/jingrow/frontend/src/app/layouts/AppHeader.vue +++ b/apps/jingrow/frontend/src/app/layouts/AppHeader.vue @@ -79,9 +79,75 @@ const appName = computed(() => localStorage.getItem('appName') || 'Jingrow') const isSystemUser = computed(() => authStore.user?.user_type === 'System User') +// 将路径段转换为可读标签 +function pathSegmentToLabel(segment: string): string { + // 将 kebab-case 或 snake_case 转换为标题格式 + return segment + .split(/[-_]/) + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(' ') +} + +// 从路由路径自动生成面包屑 +function generateBreadcrumbFromPath(path: string): Array<{ label: string; href?: string }> { + const items: Array<{ label: string; href?: string }> = [] + const segments = path.split('/').filter(Boolean) + + // 构建路径前缀,用于生成 href + let currentPath = '' + + segments.forEach((segment, index) => { + currentPath += `/${segment}` + const isLast = index === segments.length - 1 + + // 特殊处理:tools 路径 + if (segment === 'tools') { + items.push({ + label: t('Tools'), + href: isLast ? undefined : currentPath + }) + } else { + // 对于最后一项,不添加链接 + items.push({ + label: pathSegmentToLabel(segment), + href: isLast ? undefined : currentPath + }) + } + }) + + return items +} + const breadcrumbItems = computed(() => { const items: Array<{ label: string; href?: string }> = [] + // 优先检查路由 meta 中的 toolName(工具路由) + if (route.meta?.toolName) { + // 工具路由:显示 Tools / 工具名称 + // 自动解析 Tools 路由的路径(用于面包屑链接) + let toolsPath = '/app/tools' // 默认值 + try { + const resolved = router.resolve({ name: 'Tools' }) + if (resolved.matched.length > 0) { + toolsPath = resolved.path + } + } catch { + // 解析失败,使用默认值 + } + items.push({ + label: t('Tools'), + href: toolsPath + }) + // 使用 meta.toolName(工具的显示名称) + const toolLabel = route.meta.toolName as string + if (toolLabel) { + items.push({ + label: toolLabel + }) + } + return items + } + // 根据路由名称和参数生成面包屑 if (route.name === 'PageTypeList') { const entity = route.params.entity as string @@ -105,8 +171,15 @@ const breadcrumbItems = computed(() => { label: id === 'new' ? t('Create') : id }) } + } else if (route.name === 'WorkspacePage') { + const name = route.params.name as string + if (name) { + items.push({ + label: pathSegmentToLabel(name) + }) + } } else { - // 其他页面的标题映射 + // 其他页面的标题映射(保留向后兼容) const map: Record = { Dashboard: t('Dashboard'), AgentList: t('Agents'), @@ -123,8 +196,24 @@ const breadcrumbItems = computed(() => { SearchResults: t('Search Results') } const title = map[route.name as string] + if (title) { items.push({ label: title }) + } else { + // 如果没有匹配的标题,尝试从路径自动生成 + // 排除根路径和已知的特殊路径 + if (route.path !== '/' && !route.path.startsWith('/app/') && !route.path.startsWith('/page/')) { + const pathItems = generateBreadcrumbFromPath(route.path) + if (pathItems.length > 0) { + items.push(...pathItems) + } else { + // 最后的后备方案:使用路由名称 + const routeName = route.name as string + if (routeName) { + items.push({ label: pathSegmentToLabel(routeName) }) + } + } + } } } diff --git a/apps/jingrow/frontend/src/app/router/index.ts b/apps/jingrow/frontend/src/app/router/index.ts index e226c19..268819b 100644 --- a/apps/jingrow/frontend/src/app/router/index.ts +++ b/apps/jingrow/frontend/src/app/router/index.ts @@ -256,7 +256,7 @@ router.beforeEach(async (to, _from, next) => { return } else { // 路由不存在,重定向到工具列表页 - next('/tools') + next({ name: 'Tools' }) return } }