// 路由配置文件:定义应用的所有路由及导航守卫 import { createRouter, createWebHistory } from 'vue-router' import { useUserStore } from '../store/user' import { useDatabaseStore } from '../store/database' import { useRegistrationStore } from '../store/registration' import { migrateKey, readString } from '../utils/storage' import { LEGACY_STORAGE_KEYS, STORAGE_KEYS } from '../utils/storageKeys' import { loadPageSizeOptions } from '../composables/usePageSizeOptions' // 创建路由实例,使用 HTML5 History 模式 const router = createRouter({ history: createWebHistory(), routes: [ { path: '/database-setup', name: 'DatabaseSetup', component: () => import('../views/DatabaseSettings.vue'), // 首次数据库配置引导页(无需登录) }, { path: '/login', name: 'Login', component: () => import('../views/Login.vue'), // 登录页 }, { path: '/register', name: 'Register', component: () => import('../views/Register.vue'), // 注册页 }, { path: '/bill-import', name: 'BillImport', component: () => import('../views/BillImport.vue'), // 账单导入(独立页,无侧边栏) }, { path: '/', component: () => import('../views/Layout.vue'), // 主布局(带侧边栏) redirect: '/dashboard', // 默认跳转到统计面板 children: [ { path: 'dashboard', name: 'Dashboard', component: () => import('../views/Dashboard.vue'), // 统计面板 }, { path: 'transactions', name: 'Transactions', component: () => import('../views/TransactionList.vue'), // 收支记录 }, { path: 'categories', name: 'Categories', component: () => import('../views/CategoryManage.vue'), // 分类管理 }, { path: 'tags', name: 'Tags', component: () => import('../views/TagManage.vue'), // 标签管理 }, { path: 'rules', name: 'Rules', component: () => import('../views/RuleManage.vue'), // 规则管理(自定义分类与标签规则) }, { path: 'channels', name: 'Channels', component: () => import('../views/ChannelManage.vue'), // 交易渠道管理(独立页,不加入侧边栏) }, { path: 'email-bills', // 跳转到上次访问的标签页,无记录时默认邮箱配置 redirect: () => { const tabs = ['configs', 'senderRules', 'messages'] // 读取前迁移旧键,保证老用户标签页偏好不丢失 migrateKey(LEGACY_STORAGE_KEYS.emailBillTab, STORAGE_KEYS.emailBillTab) const saved = readString(STORAGE_KEYS.emailBillTab) || null return `/email-bills/${saved && tabs.includes(saved) ? saved : 'configs'}` }, }, { path: 'email-bills/:tab(configs|senderRules|messages)', name: 'EmailBills', component: () => import('../views/EmailBillManage.vue'), // 邮箱账单(邮箱配置/发件人规则/待处理账单) }, { path: 'settings', name: 'Settings', component: () => import('../views/Settings.vue'), // 系统设置(数据库 + AI 模型) }, { path: 'ai-usage', name: 'AiUsage', component: () => import('../views/AiUsage.vue'), // 模型使用情况 }, { path: 'logs', // 跳转到上次访问的日志标签页,无记录时默认访问日志 redirect: () => { const tabs = ['http', 'login', 'app'] // 读取前迁移旧键,保证老用户标签页偏好不丢失 migrateKey(LEGACY_STORAGE_KEYS.logsTab, STORAGE_KEYS.logsTab) const saved = readString(STORAGE_KEYS.logsTab) || null return `/logs/${saved && tabs.includes(saved) ? saved : 'http'}` }, }, { path: 'logs/:tab(http|login|app)', name: 'Logs', component: () => import('../views/Logs.vue'), // 系统日志(访问日志/登录日志/运行日志) }, ], }, ], }) // 全局前置守卫:优先校验数据库配置,再校验登录状态 router.beforeEach(async (to, _from, next) => { const databaseStore = useDatabaseStore() // 首次进入时从后端加载数据库配置状态 if (!databaseStore.loaded) { await databaseStore.load() } // 数据库未配置:强制引导到配置页,避免用户卡在无法登录的状态 if (!databaseStore.configured) { if (to.path === '/database-setup') { next() } else { next('/database-setup') } return } // 已配置数据库:配置引导页不再需要,跳转到登录或首页 if (to.path === '/database-setup') { const userStore = useUserStore() next(userStore.token ? '/dashboard' : '/login') return } // 已登录用户访问登录/注册页时,自动跳转到回跳地址或首页 const userStore = useUserStore() if ((to.path === '/login' || to.path === '/register') && userStore.token) { // 仅允许站内路径回跳,避免开放重定向 const redirect = Array.isArray(to.query.redirect) ? to.query.redirect[0] : to.query.redirect const target = typeof redirect === 'string' && redirect.startsWith('/') && !redirect.startsWith('//') ? redirect : '/dashboard' next(target) return } // 注册功能关闭且已有用户时,不允许访问注册页 if (to.path === '/register') { const registrationStore = useRegistrationStore() if (!registrationStore.loaded) { await registrationStore.load() } if (!registrationStore.status?.allowed) { next('/login') return } } // 如果目标页面不是登录/注册页,且没有 token,则重定向到登录页并记录来源,登录后可回跳 if (to.path !== '/login' && to.path !== '/register' && !userStore.token) { next({ path: '/login', query: { redirect: to.fullPath } }) return } // 已登录:预加载表格分页偏好,保证表格页首次渲染即拿到默认每页条数 if (userStore.token) { await loadPageSizeOptions() } next() }) export default router