Files
backend/frontend/src/router/index.ts
T
34047007@qq.com a6cd99a4ca
CI / backend (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
feat: initial commit - oncology literature search platform
OncoLit: a multi-tenant oncology literature search, feed, and
collaboration platform. Built with FastAPI + Vue 3 + PostgreSQL.
Includes PubMed pipeline, drug approvals, AI summaries, and
systematic review tools.
2026-07-27 07:59:18 +08:00

125 lines
7.5 KiB
TypeScript

import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '../stores/auth'
import { trackPageView } from '../composables/useAnalytics'
const routes = [
// 公开路由
{
path: '/',
component: () => import('../views/public/PublicLayout.vue'),
children: [
{ path: '', name: 'home', component: () => import('../views/public/HomeView.vue') },
{ path: 'feed', redirect: '/' },
{ path: 'literature/:pmid', name: 'public-detail', component: () => import('../views/public/LiteratureDetailView.vue') },
{ path: 'journals', name: 'journals', component: () => import('../views/public/JournalBrowseView.vue') },
{ path: 'cancers', name: 'cancers', component: () => import('../views/public/CancerBrowseView.vue') },
{ path: 'drugs', name: 'drugs', component: () => import('../views/public/DrugApprovalsView.vue') },
{ path: 'guidelines', name: 'guidelines', component: () => import('../views/public/GuidelinesView.vue') },
{ path: 'guidelines/compare', name: 'guideline-compare', component: () => import('../views/public/GuidelineCompareView.vue') },
{ path: 'guidelines/plans', name: 'treatment-plans', component: () => import('../views/public/TreatmentPlansView.vue') },
{ path: 'pricing', name: 'pricing', component: () => import('../views/public/PricingView.vue') },
{ path: 'about', name: 'about', component: () => import('../views/public/AboutView.vue') },
{ path: 'help', name: 'help', component: () => import('../views/public/HelpView.vue') },
],
},
// 认证路由(登录/注册)
{
path: '/auth',
component: () => import('../views/auth/AuthLayout.vue'),
children: [
{ path: 'login', name: 'login', component: () => import('../views/auth/LoginView.vue') },
{ path: 'admin-login', name: 'admin-login', component: () => import('../views/auth/AdminLoginView.vue') },
{ path: 'register', name: 'register', component: () => import('../views/auth/RegisterView.vue') },
{ path: 'accept', name: 'accept-invite', component: () => import('../views/auth/AcceptInviteView.vue') },
{ path: 'forgot-password', name: 'forgot-password', component: () => import('../views/auth/ForgotPasswordView.vue') },
{ path: 'reset-password', name: 'reset-password', component: () => import('../views/auth/ResetPasswordView.vue') },
// { path: 'wechat-login-callback', name: 'wechat-login-callback', component: () => import('../views/auth/WeChatLoginCallback.vue') }, // 微信登录(第一版已注释)
],
},
// 已登录应用路由
{
path: '/app',
component: () => import('../views/app/AppLayout.vue'),
meta: { requiresAuth: true },
children: [
{ path: '', redirect: '/app/feed' },
{ path: 'feed', name: 'feed', component: () => import('../views/app/FeedView.vue') },
{ path: 'profile', name: 'profile', component: () => import('../views/app/ProfileView.vue') },
{ path: 'search', name: 'search', component: () => import('../views/app/SearchView.vue') },
{ path: 'library', name: 'library', component: () => import('../views/app/LibraryView.vue') },
{ path: 'literature/:pmid', name: 'detail', component: () => import('../views/app/LiteratureDetailView.vue') },
{ path: 'team', name: 'team', component: () => import('../views/app/TeamView.vue') },
{ path: 'settings', name: 'settings', component: () => import('../views/app/SettingsView.vue') },
{ path: 'settings/interests', name: 'interests', component: () => import('../views/app/InterestSettingsView.vue') },
{ path: 'domains', name: 'domains', component: () => import('../views/app/DomainCenterView.vue') },
{ path: 'stats', redirect: '/app/profile' },
{ path: 'journal-club', name: 'journal-club', component: () => import('../views/app/JournalClubView.vue') },
{ path: 'mdt', name: 'mdt', component: () => import('../views/app/MdtView.vue') },
{ path: 'activity', name: 'activity', component: () => import('../views/app/ActivityHistoryView.vue') },
{ path: 'api', name: 'api-management', component: () => import('../views/app/ApiManagementView.vue') },
{ path: 'notifications', name: 'notifications', component: () => import('../views/app/NotificationsView.vue') },
{ path: 'notes', name: 'notes', component: () => import('../views/app/MyNotesView.vue') },
{ path: 'reviews', name: 'reviews', component: () => import('../views/app/ReviewsView.vue') },
{ path: 'reviews/:id', name: 'review-detail', component: () => import('../views/app/ReviewDetailView.vue') },
{ path: 'journals/:issn', name: 'journal-detail', component: () => import('../views/app/JournalDetailView.vue') },
],
},
// 管理后台
{
path: '/admin',
component: () => import('../views/admin/AdminLayout.vue'),
meta: { requiresAuth: true, requiresSuperuser: true },
children: [
{ path: '', redirect: '/admin/dashboard' },
{ path: 'dashboard', name: 'admin-dashboard', component: () => import('../views/admin/DashboardView.vue') },
{ path: 'tenants', name: 'admin-tenants', component: () => import('../views/admin/TenantsView.vue') },
{ path: 'users', name: 'admin-users', component: () => import('../views/admin/UsersView.vue') },
{ path: 'pipeline', name: 'admin-pipeline', component: () => import('../views/admin/PipelineView.vue') },
{ path: 'tags', name: 'admin-tags', component: () => import('../views/admin/TagsView.vue') },
{ path: 'journals', name: 'admin-journals', component: () => import('../views/admin/JournalsView.vue') },
{ path: 'drug-approvals', name: 'admin-drug-approvals', component: () => import('../views/admin/DrugApprovalsAdminView.vue') },
{ path: 'guidelines', name: 'admin-guidelines', component: () => import('../views/admin/GuidelinesAdminView.vue') },
{ path: 'notifications', name: 'admin-notifications', component: () => import('../views/admin/NotificationsView.vue') },
{ path: 'page-views', name: 'admin-page-views', component: () => import('../views/admin/PageViewsView.vue') },
{ path: 'feedback', name: 'admin-feedback', component: () => import('../views/admin/FeedbackView.vue') },
{ path: 'analytics', name: 'admin-analytics', component: () => import('../views/admin/AnalyticsView.vue') },
{ path: 'config', name: 'admin-config', component: () => import('../views/admin/ConfigView.vue') },
{ path: 'roles', name: 'admin-roles', component: () => import('../views/admin/PlatformRolesView.vue') },
],
},
]
export const router = createRouter({
history: createWebHistory(),
routes: [
...routes,
// 404 catch-all
{ path: '/:pathMatch(.*)*', name: 'not-found', component: () => import('../views/misc/NotFoundView.vue') },
],
})
router.beforeEach(async (to, _from, next) => {
const auth = useAuthStore()
// 确保会话初始化完成再判断登录态(避免路由守卫比 initialize 先触发)
if (!auth.initialized) {
await auth.initialize()
}
// 已登录用户访问公共详情页 → 重定向到 App 详情页(获得 AI 总结/收藏/标注等功能)
if (to.name === 'public-detail' && auth.isAuthenticated) {
next({ name: 'detail', params: { pmid: to.params.pmid }, query: to.query })
return
}
if (to.meta.requiresAuth && !auth.isAuthenticated) {
next({ name: 'login', query: { redirect: to.fullPath } })
} else if (to.meta.requiresSuperuser && !auth.user?.platform_role) {
next({ name: 'feed' })
} else {
next()
}
})
// 页面访问追踪(afterEach 不阻塞导航)
router.afterEach((to) => {
trackPageView(to)
})