import { ConfigProvider, Dropdown, Tooltip } from "antd"; import type { MenuProps } from "antd"; import { ArrowLeft, BarChart3, BellRing, ChevronLeft, ChevronRight, ChevronDown, CloudUpload, Coins, Database, FileClock, HardDrive, Home, Infinity as InfinityIcon, KeyRound, Layers3, Mail, MessageSquareText, Moon, Paintbrush, PlugZap, RadioTower, RefreshCw, Settings2, ShieldAlert, ShieldCheck, Sun, TicketCheck, ToggleLeft, UsersRound, } from "lucide-react"; import { useEffect, useState, type ReactNode } from "react"; import { Link, NavLink, Outlet, useLocation } from "react-router"; import { AppChangelogButton } from "@/components/layout/app-changelog-modal"; import { WorkspacePage } from "@/components/layout/workspace-page"; import { publishWorkspaceSidebarCollapsed, readWorkspaceSidebarCollapsed, subscribeWorkspaceSidebarCollapsed } from "@/components/layout/workspace-sidebar-state"; import { getAdminAntThemeConfig } from "@/lib/app-theme"; import { cn } from "@/lib/utils"; import "@/styles/admin-ui.css"; import { useThemeStore } from "@/stores/use-theme-store"; import { useUserStore } from "@/stores/use-user-store"; type AdminNavigationItem = { path: string; label: string; description: string; icon: ReactNode; requireFeature?: "frontendModelsEnabled"; }; const adminNavigation: Array<{ label: string; items: AdminNavigationItem[] }> = [ { label: "概览", items: [{ path: "/admin", label: "数据概览", description: "活跃、调用与成本趋势", icon: }], }, { label: "平台资源", items: [ { path: "/admin/users", label: "用户管理", description: "账号、角色与状态", icon: }, { path: "/admin/channels", label: "系统渠道", description: "渠道、模型与售价", icon: }, { path: "/admin/models", label: "前台模型", description: "展示、线路与用户价格", icon: , requireFeature: "frontendModelsEnabled" }, { path: "/admin/plugins", label: "插件管理", description: "平台可用性、上传与卸载", icon: }, { path: "/admin/prompt-templates", label: "提示词模板", description: "平台创作策略版本", icon: }, { path: "/admin/resources", label: "存储资源", description: "资源列表、容量与预览", icon: }, ], }, { label: "运营", items: [ { path: "/admin/announcements", label: "系统公告", description: "发布、关闭与历史公告", icon: }, { path: "/admin/credit-operations", label: "积分运营", description: "人工调账与异常计费", icon: }, { path: "/admin/redemption-codes", label: "兑换码", description: "生成与查看兑换码批次", icon: }, { path: "/admin/logs", label: "请求明细", description: "上游调用与费用", icon: }, ], }, { label: "系统配置", items: [ { path: "/admin/settings/features", label: "功能开放", description: "工作台、插件与模型能力", icon: }, { path: "/admin/settings/drawing-engine", label: "绘图工具", description: "画布绘图节点默认引擎", icon: }, { path: "/admin/settings/runtime-policy", label: "资源与策略", description: "配额、并发、频控与超时", icon: }, { path: "/admin/settings/access", label: "登录与注册", description: "账号创建与第三方登录", icon: }, { path: "/admin/settings/email", label: "邮件服务", description: "注册验证码与 SMTP", icon: }, { path: "/admin/settings/storage", label: "存储服务", description: "对象存储与资源存储", icon: }, { path: "/admin/settings/ark-private-assets", label: "方舟素材库", description: "Seedance 可信参考素材", icon: }, { path: "/admin/settings/response-interception", label: "模型响应拦截", description: "先启用策略,再配置替换规则", icon: }, { path: "/admin/settings/third-party", label: "第三方参数配置", description: "先配置凭据,再开放用户入口", icon: }, { path: "/admin/settings/system-update", label: "系统更新", description: "检查版本、备份与安全更新", icon: }, ], }, ]; function isAdminNavigationPath(pathname: string, navigationPath: string) { if (navigationPath === "/admin") { return pathname === navigationPath; } return pathname === navigationPath || pathname.startsWith(`${navigationPath}/`); } export function AdminShell() { const [collapsed, setCollapsed] = useState(readWorkspaceSidebarCollapsed); const dark = useThemeStore((state) => state.theme === "dark"); useEffect(() => { document.body.classList.add("admin-overlays"); return () => document.body.classList.remove("admin-overlays"); }, []); useEffect(() => { return subscribeWorkspaceSidebarCollapsed(setCollapsed); }, []); const toggleCollapsed = () => { const next = !collapsed; setCollapsed(next); publishWorkspaceSidebarCollapsed(next); }; return ( {collapsed ? : } ); } export function AdminPageFrame({ title, description, actions, back, scroll = false, children }: { title: string; description?: string; actions?: ReactNode; back?: { label: string; onClick: () => void }; scroll?: boolean; children: ReactNode }) { const location = useLocation(); const currentSection = adminNavigation.find((section) => section.items.some((item) => isAdminNavigationPath(location.pathname, item.path))); const currentItem = currentSection?.items.find((item) => isAdminNavigationPath(location.pathname, item.path)); const sectionLabel = back ? (currentItem?.label ?? currentSection?.label ?? "管理后台") : (currentSection?.label ?? "管理后台"); const sectionPath = back ? (currentItem?.path ?? currentSection?.items[0]?.path ?? "/admin") : (currentSection?.items[0]?.path ?? "/admin"); return ( {back ? ( ) : null} {sectionLabel} / {title} {description ? {description} : null} {actions} {children} ); } function AdminThemeButton() { const theme = useThemeStore((state) => state.theme); const setTheme = useThemeStore((state) => state.setTheme); const dark = theme === "dark"; return ( setTheme(dark ? "light" : "dark")} aria-label={dark ? "切换到浅色主题" : "切换到深色主题"}> {dark ? : } ); } function MobileAdminNavigation() { const features = useUserStore((state) => state.features); const location = useLocation(); const visibleGroups = adminNavigation.map((group) => ({ ...group, items: group.items.filter((item) => !item.requireFeature || features[item.requireFeature]) })).filter((group) => group.items.length > 0); const visibleItems = visibleGroups.flatMap((group) => group.items); const currentItem = visibleItems.find((item) => item.path === location.pathname) || visibleItems[0]; const menuItems: MenuProps["items"] = visibleGroups.map((group) => ({ type: "group", key: `group-${group.label}`, label: group.label, children: group.items.map((item) => ({ key: item.path, label: ( {item.icon} {item.label} ), })), })); return ( {currentItem?.icon} 管理后台 {currentItem?.label || "选择页面"} ); } function AdminNavigation({ collapsed }: { collapsed: boolean }) { const features = useUserStore((state) => state.features); return ( {adminNavigation.map((group) => { const visibleItems = group.items.filter((item) => !item.requireFeature || features[item.requireFeature]); if (visibleItems.length === 0) return null; return ( {!collapsed ? ( {group.label} ) : ( )} {visibleItems.map((item) => ( cn( "app-workspace-nav-link flex h-8 items-center rounded-md text-[var(--fs-body)] transition-colors", collapsed ? "justify-center px-0" : "gap-2.5 px-2.5", isActive ? "is-active font-medium" : "text-foreground/62 hover:bg-surface-hover hover:text-foreground", ) } > {item.icon} {!collapsed ? {item.label} : null} ))} ); })} ); }
{description}