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