From 712bb7242dbb54439b275a63b7c6c9e46171fcca Mon Sep 17 00:00:00 2001 From: jingrow Date: Fri, 21 Nov 2025 13:02:11 +0800 Subject: [PATCH] implement dynamic route registration for tools --- apps/jingrow/frontend/src/app/main.ts | 9 + apps/jingrow/frontend/src/app/router/index.ts | 1 + .../frontend/src/shared/stores/tools.ts | 31 +++- .../src/shared/utils/dynamicRoutes.ts | 159 ++++++++++++++++++ apps/jingrow/frontend/src/views/Tools.vue | 6 +- 5 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 apps/jingrow/frontend/src/shared/utils/dynamicRoutes.ts diff --git a/apps/jingrow/frontend/src/app/main.ts b/apps/jingrow/frontend/src/app/main.ts index 1a95c68..309e181 100644 --- a/apps/jingrow/frontend/src/app/main.ts +++ b/apps/jingrow/frontend/src/app/main.ts @@ -29,4 +29,13 @@ import { useAuthStore } from '../shared/stores/auth' const authStore = useAuthStore() authStore.initAuth() +// 初始化动态工具路由 - 使用 router.isReady() 确保路由已完全初始化 +import { useToolsStore } from '../shared/stores/tools' +router.isReady().then(() => { + const toolsStore = useToolsStore() + toolsStore.initToolRoutes(router) +}).catch((error) => { + console.error('Failed to initialize tool routes:', error) +}) + app.mount('#app') diff --git a/apps/jingrow/frontend/src/app/router/index.ts b/apps/jingrow/frontend/src/app/router/index.ts index 0c88a54..e272dc9 100644 --- a/apps/jingrow/frontend/src/app/router/index.ts +++ b/apps/jingrow/frontend/src/app/router/index.ts @@ -12,6 +12,7 @@ const router = createRouter({ }, { path: '/', + name: 'AppLayout', component: () => import('../layouts/AppLayout.vue'), meta: { requiresAuth: true }, children: [ diff --git a/apps/jingrow/frontend/src/shared/stores/tools.ts b/apps/jingrow/frontend/src/shared/stores/tools.ts index 2b6bf83..845d018 100644 --- a/apps/jingrow/frontend/src/shared/stores/tools.ts +++ b/apps/jingrow/frontend/src/shared/stores/tools.ts @@ -1,6 +1,7 @@ import { defineStore } from 'pinia' import { computed, ref } from 'vue' import { t } from '../i18n' +import { registerToolRoute, unregisterToolRoute, syncToolRoutes, registerAllToolRoutes } from '../utils/dynamicRoutes' export interface Tool { id: string @@ -120,7 +121,7 @@ export const useToolsStore = defineStore('tools', () => { }) // 添加用户工具 - function addUserTool(tool: Tool) { + function addUserTool(tool: Tool, router?: any, componentPath?: string) { const defaultToolsCount = getDefaultTools().filter( t => !hiddenDefaultToolIds.value.includes(t.id) ).length @@ -129,6 +130,11 @@ export const useToolsStore = defineStore('tools', () => { tool.isDefault = false userTools.value.push(tool) saveUserTools(userTools.value) + + // 如果是路由类型工具,注册路由(如果提供了 router) + if (tool.type === 'route' && tool.routeName && router) { + registerToolRoute(router, tool, componentPath) + } } // 更新用户工具 @@ -141,7 +147,9 @@ export const useToolsStore = defineStore('tools', () => { } // 删除用户工具 - function deleteUserTool(toolId: string) { + function deleteUserTool(toolId: string, router?: any) { + const tool = userTools.value.find(t => t.id === toolId) + userTools.value = userTools.value.filter(t => t.id !== toolId) // 重新分配 order const defaultToolsCount = getDefaultTools().filter( @@ -151,6 +159,11 @@ export const useToolsStore = defineStore('tools', () => { t.order = defaultToolsCount + index + 1 }) saveUserTools(userTools.value) + + // 如果是路由类型工具,移除路由(如果提供了 router) + if (tool?.type === 'route' && tool.routeName && router) { + unregisterToolRoute(router, tool.routeName) + } } // 隐藏默认工具 @@ -181,6 +194,16 @@ export const useToolsStore = defineStore('tools', () => { saveUserTools(userTools.value) } + // 初始化工具路由(在应用启动时调用) + function initToolRoutes(router: any) { + registerAllToolRoutes(router, userTools.value) + } + + // 同步工具路由(移除不存在的,添加新的) + function syncRoutes(router: any) { + syncToolRoutes(router, userTools.value) + } + return { userTools, hiddenDefaultToolIds, @@ -192,7 +215,9 @@ export const useToolsStore = defineStore('tools', () => { hideDefaultTool, showDefaultTool, updateUserToolsOrder, - getDefaultTools + getDefaultTools, + initToolRoutes, + syncRoutes } }) diff --git a/apps/jingrow/frontend/src/shared/utils/dynamicRoutes.ts b/apps/jingrow/frontend/src/shared/utils/dynamicRoutes.ts new file mode 100644 index 0000000..eb581a5 --- /dev/null +++ b/apps/jingrow/frontend/src/shared/utils/dynamicRoutes.ts @@ -0,0 +1,159 @@ +/** + * 动态路由管理工具 + * 用于动态注册和移除工具路由 + */ + +import type { Router, RouteRecordRaw } from 'vue-router' +import type { Tool } from '../stores/tools' + +/** + * 动态注册工具路由 + * @param router Vue Router 实例 + * @param tool 工具定义 + * @param componentPath 组件路径(相对于 src/views) + */ +export function registerToolRoute( + router: Router, + tool: Tool, + componentPath?: string +): boolean { + if (tool.type !== 'route' || !tool.routeName) { + return false + } + + // 检查路由是否已存在 + if (router.hasRoute(tool.routeName)) { + console.warn(`Route ${tool.routeName} already exists, skipping registration`) + return false + } + + // 确定组件路径 + // 1. 如果提供了 componentPath,使用它 + // 2. 如果工具有 componentPath 属性,使用它 + // 3. 默认使用 tools/{routeName}.vue + const finalComponentPath = + componentPath || + (tool as any).componentPath || + `../../views/tools/${tool.routeName}.vue` + + // 构建路由路径 + // 默认使用 tools/{id},但可以通过 tool.routePath 自定义 + const routePath = (tool as any).routePath || `tools/${tool.id}` + + // 创建路由配置,添加组件加载错误处理 + const route: RouteRecordRaw = { + path: routePath, + name: tool.routeName, + component: () => import(finalComponentPath).catch((error) => { + console.error(`Failed to load tool component: ${finalComponentPath}`, error) + // 返回一个简单的错误组件 + return { + template: '

Component Not Found

Failed to load: {{ path }}

', + data() { + return { path: finalComponentPath } + } + } + }), + meta: { + requiresAuth: true, + toolId: tool.id, + toolName: tool.name + } + } + + try { + // 将路由添加到 AppLayout 的 children 下 + // 如果 AppLayout 路由不存在,尝试添加到根路由 + if (router.hasRoute('AppLayout')) { + router.addRoute('AppLayout', route) + } else { + // 查找父路由(path 为 '/' 的路由) + const parentRoute = router.getRoutes().find((r: any) => r.path === '/' && r.name !== 'Login') + if (parentRoute) { + router.addRoute(parentRoute.name || parentRoute.path, route) + } else { + // 如果找不到父路由,直接添加到根路由 + router.addRoute(route) + } + } + console.log(`Tool route registered: ${tool.routeName} -> ${routePath}`) + return true + } catch (error) { + console.error(`Failed to register tool route ${tool.routeName}:`, error) + return false + } +} + +/** + * 移除工具路由 + * @param router Vue Router 实例 + * @param routeName 路由名称 + */ +export function unregisterToolRoute(router: Router, routeName: string): boolean { + if (!router.hasRoute(routeName)) { + return false + } + + try { + router.removeRoute(routeName) + console.log(`Tool route removed: ${routeName}`) + return true + } catch (error) { + console.error(`Failed to remove tool route ${routeName}:`, error) + return false + } +} + +/** + * 注册所有用户工具的路由 + * @param router Vue Router 实例 + * @param tools 工具列表 + */ +export function registerAllToolRoutes(router: Router, tools: Tool[]): void { + const routeTools = tools.filter(t => t.type === 'route' && t.routeName && !t.isDefault) + + routeTools.forEach(tool => { + registerToolRoute(router, tool) + }) + + console.log(`Registered ${routeTools.length} tool routes`) +} + +/** + * 同步工具路由(移除不存在的工具路由,添加新的工具路由) + * @param router Vue Router 实例 + * @param currentTools 当前工具列表 + */ +export function syncToolRoutes(router: Router, currentTools: Tool[]): void { + // 获取所有已注册的路由 + const registeredRoutes = router.getRoutes() + + // 找出所有工具路由(通过 meta.toolId 识别) + const toolRoutes = registeredRoutes.filter((route) => + route.meta?.toolId && !route.meta?.isDefault + ) + + // 当前应该存在的工具路由名称 + const currentRouteNames = new Set( + currentTools + .filter(t => t.type === 'route' && t.routeName && !t.isDefault) + .map(t => t.routeName!) + ) + + // 移除不再存在的工具路由 + toolRoutes.forEach((route) => { + if (route.name && typeof route.name === 'string' && !currentRouteNames.has(route.name)) { + unregisterToolRoute(router, route.name) + } + }) + + // 添加新的工具路由 + currentTools + .filter(t => t.type === 'route' && t.routeName && !t.isDefault) + .forEach(tool => { + if (tool.routeName && !router.hasRoute(tool.routeName)) { + registerToolRoute(router, tool) + } + }) +} + diff --git a/apps/jingrow/frontend/src/views/Tools.vue b/apps/jingrow/frontend/src/views/Tools.vue index c51da8b..cd40a1a 100644 --- a/apps/jingrow/frontend/src/views/Tools.vue +++ b/apps/jingrow/frontend/src/views/Tools.vue @@ -444,7 +444,8 @@ function handleSaveTool() { isDefault: false } - toolsStore.addUserTool(newTool) + // 添加工具并注册路由 + toolsStore.addUserTool(newTool, router) message.success(t('Tool added successfully')) } @@ -475,7 +476,8 @@ function handleDeleteTool(tool: Tool) { positiveText: t('Delete'), negativeText: t('Cancel'), onPositiveClick: () => { - toolsStore.deleteUserTool(tool.id) + // 删除工具并移除路由 + toolsStore.deleteUserTool(tool.id, router) message.success(t('Tool deleted successfully')) } })