diff --git a/apps/jingrow/frontend/src/app/main.ts b/apps/jingrow/frontend/src/app/main.ts index 309e181..19163cf 100644 --- a/apps/jingrow/frontend/src/app/main.ts +++ b/apps/jingrow/frontend/src/app/main.ts @@ -29,7 +29,6 @@ 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() diff --git a/apps/jingrow/frontend/src/app/router/index.ts b/apps/jingrow/frontend/src/app/router/index.ts index e272dc9..de0e33f 100644 --- a/apps/jingrow/frontend/src/app/router/index.ts +++ b/apps/jingrow/frontend/src/app/router/index.ts @@ -190,15 +190,39 @@ const router = createRouter({ ] }) -// 路由守卫 router.beforeEach(async (to, _from, next) => { const authStore = useAuthStore() - // 初始化认证状态(内部会检查cookie是否过期) if (!authStore.isAuthenticated) { await authStore.initAuth() } + if (to.path.startsWith('/tools/') && to.name !== 'Tools' && to.name !== 'RemoveBackground') { + const routeExists = to.matched.length > 0 || router.getRoutes().some(r => { + const fullPath = r.path.startsWith('/') ? r.path : `/${r.path}` + return fullPath === to.path + }) + + if (!routeExists) { + const { useToolsStore } = await import('../../shared/stores/tools') + const toolsStore = useToolsStore() + toolsStore.initToolRoutes(router) + + const routeExistsAfter = router.getRoutes().some(r => { + const fullPath = r.path.startsWith('/') ? r.path : `/${r.path}` + return fullPath === to.path + }) + + if (routeExistsAfter) { + next({ ...to, replace: true }) + return + } else { + next('/tools') + return + } + } + } + if (to.meta.requiresAuth && !authStore.isLoggedIn) { next('/login') } else if (to.path === '/login' && authStore.isLoggedIn) { diff --git a/apps/jingrow/frontend/src/shared/stores/tools.ts b/apps/jingrow/frontend/src/shared/stores/tools.ts index a6f587c..026d58c 100644 --- a/apps/jingrow/frontend/src/shared/stores/tools.ts +++ b/apps/jingrow/frontend/src/shared/stores/tools.ts @@ -101,25 +101,20 @@ export const useToolsStore = defineStore('tools', () => { const userTools = ref(loadUserTools()) const hiddenDefaultToolIds = ref(loadHiddenDefaultTools()) - // 合并显示所有工具:默认工具 + 用户工具 const allTools = computed(() => { - // 获取默认工具(排除隐藏的) const defaultTools = getDefaultTools() .filter(tool => !hiddenDefaultToolIds.value.includes(tool.id)) .map(tool => ({ ...tool, isDefault: true })) - // 用户工具 const userToolsList = [...userTools.value] .map(tool => ({ ...tool, isDefault: false })) - // 合并并排序:默认工具在前,用户工具在后,各自按order排序 return [ ...defaultTools.sort((a, b) => (a.order ?? 0) - (b.order ?? 0)), ...userToolsList.sort((a, b) => (a.order ?? 0) - (b.order ?? 0)) ] }) - // 获取隐藏的默认工具列表 const hiddenTools = computed(() => { return getDefaultTools() .filter(tool => hiddenDefaultToolIds.value.includes(tool.id)) @@ -148,7 +143,6 @@ export const useToolsStore = defineStore('tools', () => { } } - // 更新用户工具 function updateUserTool(toolId: string, updates: Partial) { const index = userTools.value.findIndex(t => t.id === toolId) if (index >= 0) { @@ -161,18 +155,12 @@ export const useToolsStore = defineStore('tools', () => { async function deleteUserTool(toolId: string, router?: Router) { const tool = userTools.value.find(t => t.id === toolId) - console.log('删除工具:', { toolId, tool, fromMarketplace: tool?.fromMarketplace, marketplaceId: tool?.marketplaceId, toolName: tool?.toolName, componentPath: tool?.componentPath }) - - // 确定工具名称(tool_name):优先使用 toolName,否则从 componentPath 提取 let toolName: string | null = null if (tool) { - // 优先使用 toolName(实际的 tool_name,用于文件系统) if (tool.toolName) { toolName = tool.toolName - } - // 否则从 componentPath 提取工具名称(格式:tools/{tool_name}/{tool_name}.vue) - else if (tool.componentPath) { + } else if (tool.componentPath) { const match = tool.componentPath.match(/tools\/([^\/]+)\//) if (match && match[1]) { toolName = match[1] @@ -180,27 +168,19 @@ export const useToolsStore = defineStore('tools', () => { } } - // 如果找到了工具名称,尝试删除文件系统 if (toolName) { try { - console.log('调用后端API删除工具文件,工具名称:', toolName) - // 调用后端API删除工具文件 - const response = await axios.post(`/jingrow/uninstall-tool/${toolName}`, {}, { + await axios.post(`/jingrow/uninstall-tool/${toolName}`, {}, { headers: { ...get_session_api_headers() } }) - console.log('删除工具文件成功:', response.data) } catch (error: any) { console.error('删除工具文件失败:', error.response?.data || error.message || '未知错误') - // 即使删除文件失败,也继续删除store中的工具 } - } else { - console.log('无法确定工具名称,跳过文件删除') } userTools.value = userTools.value.filter(t => t.id !== toolId) - // 重新分配 order const defaultToolsCount = getDefaultTools().filter( t => !hiddenDefaultToolIds.value.includes(t.id) ).length @@ -209,13 +189,11 @@ export const useToolsStore = defineStore('tools', () => { }) saveUserTools(userTools.value) - // 移除路由(如果提供了 router) if (tool && tool.routeName && router) { unregisterToolRoute(router, tool.routeName) } } - // 隐藏默认工具 function hideDefaultTool(toolId: string) { if (!hiddenDefaultToolIds.value.includes(toolId)) { hiddenDefaultToolIds.value.push(toolId) @@ -223,13 +201,11 @@ export const useToolsStore = defineStore('tools', () => { } } - // 显示隐藏的默认工具 function showDefaultTool(toolId: string) { hiddenDefaultToolIds.value = hiddenDefaultToolIds.value.filter(id => id !== toolId) saveHiddenDefaultTools(hiddenDefaultToolIds.value) } - // 更新用户工具顺序 function updateUserToolsOrder(newOrder: Tool[]) { const defaultToolsCount = getDefaultTools().filter( t => !hiddenDefaultToolIds.value.includes(t.id) @@ -243,7 +219,6 @@ export const useToolsStore = defineStore('tools', () => { saveUserTools(userTools.value) } - // 初始化工具路由(在应用启动时调用) function initToolRoutes(router: Router) { registerAllToolRoutes(router, userTools.value) } diff --git a/apps/jingrow/frontend/src/shared/utils/dynamicRoutes.ts b/apps/jingrow/frontend/src/shared/utils/dynamicRoutes.ts index 5dccde7..39b2917 100644 --- a/apps/jingrow/frontend/src/shared/utils/dynamicRoutes.ts +++ b/apps/jingrow/frontend/src/shared/utils/dynamicRoutes.ts @@ -1,115 +1,65 @@ -/** - * 动态路由管理工具 - * 用于动态注册和移除工具路由 - */ - import type { Router, RouteRecordRaw } from 'vue-router' import type { Tool } from '../stores/tools' -/** - * 将 snake_case 转换为 PascalCase - */ function snakeToPascal(snakeStr: string): string { const components = snakeStr.split('_') return components.map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('') } -/** - * 基于 tool_name 生成 routeName(PascalCase) - */ function generateRouteName(toolName: string): string { return snakeToPascal(toolName) } -/** - * 基于 tool_name 生成 routePath(约定:tools/{tool_name}) - */ function generateRoutePath(toolName: string): string { return `tools/${toolName}` } -/** - * 确保工具具有 routeName 和 routePath(如果缺失则自动生成) - * 优先使用 tool.marketplaceId(即 tool_name),如果没有则使用 tool.name(工具显示名称) - * 这样从市场安装的工具会使用 tool_name,手动添加的工具会使用 name - */ export function ensureToolRoutes(tool: Tool): Tool { - // 确定基础名称:优先使用 marketplaceId(即 tool_name,稳定标识符),如果没有则使用 name(工具显示名称),最后回退到 id - const baseName = tool.marketplaceId || tool.name || tool.id + const routeNameBase = tool.marketplaceId || tool.name || tool.id + const routePathBase = tool.toolName || tool.marketplaceId || tool.name || tool.id - // 如果没有 routeName,基于基础名称生成 if (!tool.routeName) { - tool.routeName = generateRouteName(baseName) + tool.routeName = generateRouteName(routeNameBase) } - // 如果没有 routePath,基于基础名称生成 if (!tool.routePath) { - tool.routePath = generateRoutePath(baseName) + tool.routePath = generateRoutePath(routePathBase) } return tool } - -/** - * 动态注册工具路由 - * @param router Vue Router 实例 - * @param tool 工具定义 - * @param componentPath 组件路径(相对于 src/views) - */ export function registerToolRoute( router: Router, tool: Tool, componentPath?: string ): boolean { - // 确保工具具有 routeName 和 routePath const toolWithRoutes = ensureToolRoutes({ ...tool }) - // 如果没有 routeName,无法注册路由 if (!toolWithRoutes.routeName) { return false } - // 检查路由是否已存在 if (router.hasRoute(toolWithRoutes.routeName)) { - console.warn(`Route ${toolWithRoutes.routeName} already exists, skipping registration`) return false } - // 确定组件路径和路由路径 - // componentPath 格式:tools/{tool_name}/{tool_name}.vue(相对于 src/views) - // 需要转换为相对于当前文件的路径:../../views/tools/{tool_name}/{tool_name}.vue let finalComponentPath: string if (componentPath) { - // 如果 componentPath 已经是相对于 src/views 的路径(如 tools/test_tool/test_tool.vue) - // 需要转换为相对于当前文件的路径 finalComponentPath = `../../views/${componentPath}` } else if (toolWithRoutes.componentPath) { - // 如果 tool 对象中有 componentPath finalComponentPath = `../../views/${toolWithRoutes.componentPath}` } else { - // 默认路径:使用 toolName 或 id const toolDirName = toolWithRoutes.toolName || toolWithRoutes.id finalComponentPath = `../../views/tools/${toolDirName}/${toolDirName}.vue` } const routePath = toolWithRoutes.routePath || `tools/${toolWithRoutes.toolName || toolWithRoutes.id}` - // 创建路由配置,添加组件加载错误处理 - console.log(`Registering tool route:`, { - routeName: toolWithRoutes.routeName, - routePath, - componentPath: finalComponentPath, - toolName: toolWithRoutes.toolName, - id: toolWithRoutes.id - }) - const route: RouteRecordRaw = { path: routePath, name: toolWithRoutes.routeName, component: () => { - console.log(`Loading tool component from: ${finalComponentPath}`) return import(finalComponentPath).catch((error) => { console.error(`Failed to load tool component: ${finalComponentPath}`, error) - // 返回一个简单的错误组件,显示详细错误信息 return { name: 'ToolComponentError', template: ` @@ -130,10 +80,7 @@ export function registerToolRoute( } try { - // 将路由添加到 AppLayout 的 children 下 - // AppLayout 路由应该在应用启动时已存在 router.addRoute('AppLayout', route) - console.log(`Tool route registered: ${toolWithRoutes.routeName} -> ${routePath}`) return true } catch (error) { console.error(`Failed to register tool route ${toolWithRoutes.routeName}:`, error) @@ -141,11 +88,6 @@ export function registerToolRoute( } } -/** - * 移除工具路由 - * @param router Vue Router 实例 - * @param routeName 路由名称 - */ export function unregisterToolRoute(router: Router, routeName: string): boolean { if (!router.hasRoute(routeName)) { return false @@ -153,7 +95,6 @@ export function unregisterToolRoute(router: Router, routeName: string): boolean try { router.removeRoute(routeName) - console.log(`Tool route removed: ${routeName}`) return true } catch (error) { console.error(`Failed to remove tool route ${routeName}:`, error) @@ -161,19 +102,10 @@ export function unregisterToolRoute(router: Router, routeName: string): boolean } } -/** - * 注册所有用户工具的路由 - * @param router Vue Router 实例 - * @param tools 工具列表 - */ export function registerAllToolRoutes(router: Router, tools: Tool[]): void { - // 过滤出用户工具(routeName 会在 registerToolRoute 中自动生成) const routeTools = tools.filter(t => !t.isDefault) - routeTools.forEach(tool => { - registerToolRoute(router, tool) + registerToolRoute(router, tool, tool.componentPath) }) - - console.log(`Registered ${routeTools.length} tool routes`) }