refactor(layout, i18n, admin): 优化布局结构与多语言支持
调整 AdminShell 组件的子组件顺序,提升代码可读性。更新 admin-breadcrumb 组件,简化导航标签翻译逻辑,确保多语言支持的一致性。重构 admin-language-switcher 组件,优化语言切换的用户体验,增强界面交互性。更新多语言配置,新增登录界面的副标题,提升用户体验。
This commit is contained in:
@@ -2,40 +2,86 @@
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState, type ReactNode } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { AdminAuthCheckingScreen } from "@/components/admin/admin-auth-checking";
|
||||
import { verifyStoredAdminSession } from "@/lib/admin-session-verify";
|
||||
import { useAdminSessionStore } from "@/stores/admin-session";
|
||||
import { readToken } from "@/stores/admin-token";
|
||||
|
||||
type ShellAuthGateProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
type GateStatus = "pending" | "authed" | "guest";
|
||||
|
||||
function hasAdminToken(bearerToken: string | null): boolean {
|
||||
const token = bearerToken ?? readToken();
|
||||
return token != null && token.trim() !== "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Shell route guard. Reads the auth token from localStorage on the client and
|
||||
* redirects to the login page when no token is present.
|
||||
* Shell 路由守卫:无 Token 或 `/auth/me` 校验失败时跳转登录页。
|
||||
*/
|
||||
export function ShellAuthGate({ children }: ShellAuthGateProps) {
|
||||
const { t } = useTranslation("common");
|
||||
const router = useRouter();
|
||||
const [allowed, setAllowed] = useState(false);
|
||||
const bearerToken = useAdminSessionStore((s) => s.bearerToken);
|
||||
const [status, setStatus] = useState<GateStatus>("pending");
|
||||
|
||||
const setShellAuthPending = useAdminSessionStore((s) => s.setShellAuthPending);
|
||||
|
||||
useEffect(() => {
|
||||
const token = readToken();
|
||||
if (!token) {
|
||||
router.replace("/admin/login");
|
||||
return;
|
||||
}
|
||||
queueMicrotask(() => {
|
||||
setAllowed(true);
|
||||
});
|
||||
}, [router]);
|
||||
let cancelled = false;
|
||||
setShellAuthPending(true);
|
||||
|
||||
if (!allowed) {
|
||||
return (
|
||||
<div className="flex min-h-[50vh] w-full flex-1 items-center justify-center text-sm text-muted-foreground">
|
||||
{t("auth.checking", { defaultValue: "Checking sign-in status…" })}
|
||||
</div>
|
||||
);
|
||||
async function run() {
|
||||
if (!hasAdminToken(bearerToken)) {
|
||||
if (!cancelled) {
|
||||
setStatus("guest");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cancelled) {
|
||||
setStatus("pending");
|
||||
}
|
||||
|
||||
const ok = await verifyStoredAdminSession();
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
setStatus("authed");
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus("guest");
|
||||
}
|
||||
|
||||
void run().finally(() => {
|
||||
if (!cancelled) {
|
||||
setShellAuthPending(false);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
setShellAuthPending(false);
|
||||
};
|
||||
}, [bearerToken, setShellAuthPending]);
|
||||
|
||||
useEffect(() => {
|
||||
if (status === "guest") {
|
||||
router.replace("/admin/login");
|
||||
}
|
||||
}, [status, router]);
|
||||
|
||||
if (status === "pending") {
|
||||
return <AdminAuthCheckingScreen variant="shell" />;
|
||||
}
|
||||
|
||||
if (status === "guest") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return children;
|
||||
|
||||
Reference in New Issue
Block a user