feat: merge admin routes into unified frontend
This commit is contained in:
117
frontend-web/src/components/admin-shell.tsx
Normal file
117
frontend-web/src/components/admin-shell.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
"use client";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import clsx from "clsx";
|
||||
import {
|
||||
Blocks,
|
||||
ChartColumnBig,
|
||||
Coins,
|
||||
KeySquare,
|
||||
Link2,
|
||||
LogOut,
|
||||
Package2,
|
||||
Settings2,
|
||||
Users,
|
||||
Workflow,
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { api } from "@/lib/api";
|
||||
|
||||
const navigation = [
|
||||
{ href: "/admin/dashboard", label: "仪表盘", icon: ChartColumnBig },
|
||||
{ href: "/admin/users", label: "用户管理", icon: Users },
|
||||
{ href: "/admin/recharge-orders", label: "充值订单", icon: Coins },
|
||||
{ href: "/admin/redeem-codes", label: "兑换密钥", icon: KeySquare },
|
||||
{ href: "/admin/growth-rules", label: "增长奖励", icon: Link2 },
|
||||
{ href: "/admin/invite-relations", label: "邀请关系", icon: Link2 },
|
||||
{ href: "/admin/provider-accounts", label: "供应商账号", icon: Workflow },
|
||||
{ href: "/admin/provider-models", label: "供应商模型", icon: Blocks },
|
||||
{ href: "/admin/video-models", label: "平台模型", icon: Package2 },
|
||||
{ href: "/admin/video-model-bindings", label: "模型绑定", icon: Workflow },
|
||||
{ href: "/admin/pricing-rules", label: "价格规则", icon: Coins },
|
||||
{ href: "/admin/video-tasks", label: "视频任务", icon: Workflow },
|
||||
{ href: "/admin/callback-logs", label: "回调日志", icon: ChartColumnBig },
|
||||
{ href: "/admin/system-config", label: "系统配置", icon: Settings2 },
|
||||
];
|
||||
|
||||
export function AdminShell({ children }: { children: React.ReactNode }) {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const meQuery = useQuery({
|
||||
queryKey: ["admin-me"],
|
||||
queryFn: () => api.get("/api/v1/admin/auth/me"),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (meQuery.error) {
|
||||
router.replace("/admin/login");
|
||||
}
|
||||
}, [meQuery.error, router]);
|
||||
|
||||
if (meQuery.isLoading || !meQuery.data) {
|
||||
return (
|
||||
<div className="fullscreen-shell">
|
||||
<div className="pulse-card">正在校验管理员身份...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="shell-grid">
|
||||
<aside className="shell-sidebar">
|
||||
<div className="brand-block">
|
||||
<span className="brand-kicker">AIVIDEO ADMIN</span>
|
||||
<h1>控制后台</h1>
|
||||
<p>围绕模型、价格、奖励、订单和任务链路进行统一配置与审计。</p>
|
||||
</div>
|
||||
|
||||
<nav className="sidebar-nav">
|
||||
{navigation.map((item) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={clsx("nav-item", {
|
||||
active: pathname.startsWith(item.href),
|
||||
})}
|
||||
>
|
||||
<Icon size={18} />
|
||||
<span>{item.label}</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
|
||||
<div className="profile-card">
|
||||
<div>
|
||||
<div className="profile-name">
|
||||
{(meQuery.data as { nickname: string }).nickname}
|
||||
</div>
|
||||
<div className="profile-meta">
|
||||
{(meQuery.data as { username: string }).username}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="ghost-button"
|
||||
onClick={async () => {
|
||||
await api.post("/api/v1/admin/auth/logout");
|
||||
router.replace("/admin/login");
|
||||
}}
|
||||
>
|
||||
<LogOut size={16} />
|
||||
退出
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main className="shell-main">
|
||||
<section className="shell-content">{children}</section>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ const statusMap: Record<string, string> = {
|
||||
unused: "success",
|
||||
used: "ghost",
|
||||
disabled: "danger",
|
||||
rewarded: "success",
|
||||
};
|
||||
|
||||
const statusLabelMap: Record<string, string> = {
|
||||
@@ -26,6 +27,7 @@ const statusLabelMap: Record<string, string> = {
|
||||
unused: "未使用",
|
||||
used: "已使用",
|
||||
disabled: "已停用",
|
||||
rewarded: "已奖励",
|
||||
};
|
||||
|
||||
export function StatusBadge({ value }: { value: string }) {
|
||||
|
||||
Reference in New Issue
Block a user