56 lines
1.5 KiB
TypeScript
56 lines
1.5 KiB
TypeScript
// src/router/guards.ts
|
|
import type { Router } from 'vue-router'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
|
|
function hasAll(required: string[], owned: string[]) {
|
|
return required.every((x) => owned.includes(x))
|
|
}
|
|
|
|
export function registerGuards(router: Router) {
|
|
router.beforeEach(async (to) => {
|
|
const skipLogin = import.meta.env.VITE_SKIP_LOGIN === 'true'
|
|
if (skipLogin) {
|
|
if (to.name === 'login' || to.meta.guestOnly) {
|
|
return { name: 'home' }
|
|
}
|
|
return true
|
|
}
|
|
|
|
const auth = useAuthStore()
|
|
|
|
// Requires auth:未登入導去 login,附 redirect
|
|
if (to.meta.requiresAuth && !auth.isAuthenticated) {
|
|
if (to.name === 'login') return true
|
|
return { name: 'login', query: { redirect: to.fullPath } }
|
|
}
|
|
|
|
// Role-based access control (RBAC)
|
|
if (to.meta.roles?.length && !hasAll(to.meta.roles, auth.roles)) {
|
|
return { name: 'forbidden' }
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
router.beforeResolve(() => {
|
|
// 適合放:進頁前最後一步的「輕量」工作(例如開始進度條)
|
|
// NProgress.start() 之類
|
|
return true
|
|
})
|
|
|
|
router.afterEach((to) => {
|
|
// 1) Document title
|
|
const base = 'Demo App'
|
|
document.title = to.meta.title ? `${to.meta.title} - ${base}` : base
|
|
|
|
// 2) 追蹤(Analytics)可放這裡:pageview
|
|
// 3) NProgress.done()
|
|
})
|
|
|
|
router.onError((err) => {
|
|
// Chunk load 失敗、動態 import 失敗等
|
|
// 可考慮:router.replace(current) 或導到錯誤頁
|
|
console.error('Router error:', err)
|
|
})
|
|
}
|