feat(web): 前端逻辑骨架——http/router/stores/queries 与薄壳路由
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
import AdminLayout from "@/layouts/admin-layout.vue";
|
||||
|
||||
/**
|
||||
* 管理面路由 `/admin/*`(契约:页面路由永不进入 `/api`)。
|
||||
* 侧边栏六项 + 账号设置(交互共性 §2)。
|
||||
*/
|
||||
export const adminRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/admin/login",
|
||||
name: "admin-login",
|
||||
component: () => import("@/views/admin/login-view.vue"),
|
||||
meta: { public: true, world: "admin" },
|
||||
},
|
||||
{
|
||||
path: "/admin",
|
||||
component: AdminLayout,
|
||||
meta: { requiresAdmin: true, world: "admin" },
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
redirect: { name: "admin-overview" },
|
||||
},
|
||||
{
|
||||
path: "overview",
|
||||
name: "admin-overview",
|
||||
component: () => import("@/views/admin/overview-view.vue"),
|
||||
},
|
||||
{
|
||||
path: "keys",
|
||||
name: "admin-keys",
|
||||
component: () => import("@/views/admin/keys-view.vue"),
|
||||
},
|
||||
{
|
||||
path: "payments",
|
||||
name: "admin-payments",
|
||||
component: () => import("@/views/admin/payments-view.vue"),
|
||||
},
|
||||
{
|
||||
path: "members",
|
||||
name: "admin-members",
|
||||
component: () => import("@/views/admin/members-view.vue"),
|
||||
},
|
||||
{
|
||||
path: "audit",
|
||||
name: "admin-audit",
|
||||
component: () => import("@/views/admin/audit-view.vue"),
|
||||
},
|
||||
{
|
||||
path: "settings",
|
||||
name: "admin-settings",
|
||||
component: () => import("@/views/admin/settings-view.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,59 @@
|
||||
import { createRouter, createWebHistory, type Router } from "vue-router";
|
||||
import { adminRoutes } from "./admin.ts";
|
||||
import { memberRoutes } from "./member.ts";
|
||||
import { setAuthRedirect } from "@/api/auth-redirect.ts";
|
||||
import { useAdminSessionStore } from "@/stores/admin-session.ts";
|
||||
import { useMemberTokenStore } from "@/stores/member-token.ts";
|
||||
|
||||
declare module "vue-router" {
|
||||
interface RouteMeta {
|
||||
public?: boolean;
|
||||
requiresAdmin?: boolean;
|
||||
requiresMember?: boolean;
|
||||
world?: "admin" | "member";
|
||||
}
|
||||
}
|
||||
|
||||
export const router: Router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{ path: "/", redirect: "/admin/overview" },
|
||||
...adminRoutes,
|
||||
...memberRoutes,
|
||||
{ path: "/:pathMatch(.*)*", redirect: "/admin/overview" },
|
||||
],
|
||||
});
|
||||
|
||||
router.beforeEach((to) => {
|
||||
if (to.meta.public) return true;
|
||||
|
||||
const memberToken = useMemberTokenStore();
|
||||
|
||||
// 管理面:硬刷新后 Pinia 为空但会话 cookie 仍在——交给 GET /session 与 401 分流裁决
|
||||
if (to.meta.requiresMember && !memberToken.hasToken) {
|
||||
return { name: "member-login" };
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
/** 401 分流接入 router(由 main.ts 调用一次) */
|
||||
export function bindAuthRedirectToRouter(): void {
|
||||
setAuthRedirect((world, reason) => {
|
||||
const adminSession = useAdminSessionStore();
|
||||
const memberToken = useMemberTokenStore();
|
||||
if (world === "admin") {
|
||||
adminSession.signOut();
|
||||
void router.push({
|
||||
name: "admin-login",
|
||||
query: reason === "expired" ? { reason: "expired" } : {},
|
||||
});
|
||||
} else {
|
||||
memberToken.clearToken();
|
||||
void router.push({
|
||||
name: "member-login",
|
||||
query: reason === "expired" ? { reason: "expired" } : {},
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
import MemberLayout from "@/layouts/member-layout.vue";
|
||||
|
||||
/** 成员视图 `/member/*`:登录壳 → 单页(交互共性 §1) */
|
||||
export const memberRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/member/login",
|
||||
name: "member-login",
|
||||
component: () => import("@/views/member/login-view.vue"),
|
||||
meta: { public: true, world: "member" },
|
||||
},
|
||||
{
|
||||
path: "/member",
|
||||
component: MemberLayout,
|
||||
meta: { requiresMember: true, world: "member" },
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
name: "member-home",
|
||||
component: () => import("@/views/member/home-view.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user