import { useState, useEffect, useCallback } from "react"; import { Routes, Route, useNavigate, useLocation, Navigate } from "react-router-dom"; import { Layout, Menu, Button, Spin, Tag, Tooltip } from "antd"; import { HomeOutlined, ApartmentOutlined, ImportOutlined, CheckCircleOutlined, SendOutlined, ToolOutlined, SettingOutlined, LogoutOutlined, MenuFoldOutlined, MenuUnfoldOutlined, CompassOutlined, ProjectOutlined, } from "@ant-design/icons"; import AdminLogin from "./AdminLogin"; import AdminLoginV2 from "./AdminLoginV2"; import Brand from "./Brand"; import { displayProjectName, getProjectContext, whoami, type KgProjectContext } from "./api"; import ProjectContextBar from "./ProjectContextBar"; import ProjectLanding from "./ProjectLanding"; // Plaza panels (STEP 02) import PlazaOverview from "./panels/plaza/PlazaOverview"; import GraphCanvas from "./panels/plaza/GraphCanvas"; import ManualIngestPanel from "./panels/plaza/ManualIngestPanel"; import ProjectUserExperiencePanel from "./panels/plaza/ProjectUserExperiencePanel"; import AuditReport from "./panels/plaza/AuditReport"; import UsageStats from "./panels/plaza/UsageStats"; import HealthAlerts from "./panels/plaza/HealthAlerts"; // Modeling panels import VocabularyPanel from "./panels/modeling/VocabularyPanel"; import OntologySchemaPanel from "./panels/modeling/OntologySchemaPanel"; import SchemaHealth from "./panels/modeling/SchemaHealth"; // Acquisition panels (STEP 01) import BatchCenter from "./panels/acquisition/BatchCenter"; import SourceProfilesPanel from "./panels/acquisition/SourceProfilesPanel"; import ConflictDesk from "./panels/acquisition/ConflictDesk"; // Review panels (STEP 03) import AssetLibrary from "./panels/review/AssetLibrary"; import FieldLevelReview from "./panels/review/FieldLevelReview"; import AccessionView from "./panels/review/AccessionView"; import ExpertSignoff from "./panels/review/ExpertSignoff"; import PoiEvidenceQuality from "./panels/review/PoiEvidenceQuality"; // Publish panels (STEP 05) import PublishPanel from "./panels/publish/PublishPanel"; import RollbackPanel from "./panels/publish/RollbackPanel"; // Workbench panels (STEP 04) import InventoryPanel from "./panels/workbench/InventoryPanel"; import AcquisitionTasksPanel from "./panels/workbench/AcquisitionTasksPanel"; import TreatmentHistory from "./panels/workbench/TreatmentHistory"; // System panels import AgentTasksPanel from "./panels/system/AgentTasksPanel"; import LogsPanel from "./panels/system/LogsPanel"; import PermissionsAudit from "./panels/system/PermissionsAudit"; import UserManagement from "./panels/system/UserManagement"; import AreaManagement from "./panels/system/AreaManagement"; import Notifications from "./panels/system/Notifications"; import NotificationBell from "./panels/system/NotificationBell"; import AgentSettings from "./panels/system/AgentSettings"; const { Sider, Content } = Layout; const RUNTIME_CONTEXT = { tenant: "CityGraph-new2", project: "CityGraph-new2", dbSchema: "kg_admin_new2", graph: "guiyang_new2", port: "8102", }; const MENU_ITEMS = [ { key: "/projects", icon: , label: "项目工作区" }, { key: "/plaza", icon: , label: "知识广场", children: [ { key: "/plaza/overview", label: "馆藏概览" }, { key: "/plaza/user", icon: , label: "业务问答" }, { key: "/plaza/graph", label: "图谱浏览器" }, { key: "/plaza/manual-ingest", label: "人工录入" }, { key: "/plaza/audit", label: "AI 质量稽查" }, { key: "/plaza/usage", label: "使用统计" }, { key: "/plaza/alerts", label: "健康告警" }, ]}, { key: "/modeling", icon: , label: "本体建模", children: [ { key: "/modeling/schema", label: "本体 Schema" }, { key: "/modeling/vocabulary", label: "权威词表" }, { key: "/modeling/health", label: "Schema 健康" }, ]}, { key: "/acquisition", icon: , label: "数据采集", children: [ { key: "/acquisition/batches", label: "来源批次中心" }, { key: "/acquisition/sources", label: "来源档案" }, { key: "/acquisition/conflicts", label: "冲突处理台" }, ]}, { key: "/review", icon: , label: "审核入藏", children: [ { key: "/review/candidates", label: "候选审核" }, { key: "/review/poi-evidence", label: "POI 证据质检" }, { key: "/review/field-level", label: "字段级审核" }, { key: "/review/accession", label: "入藏记录" }, { key: "/review/expert-signoff", label: "专家会签" }, ]}, { key: "/publish", icon: , label: "图谱发布", children: [ { key: "/publish/jobs", label: "发布管理" }, { key: "/publish/rollback", label: "发布回滚" }, ]}, { key: "/workbench", icon: , label: "治理工作台", children: [ { key: "/workbench/inventory", label: "知识盘点" }, { key: "/workbench/tasks", label: "补藏队列" }, { key: "/workbench/history", label: "历史治理" }, ]}, { key: "/system", icon: , label: "系统", children: [ { key: "/system/agents", label: "Agent 任务" }, { key: "/system/logs", label: "调用日志" }, { key: "/system/users", label: "用户管理" }, { key: "/system/permissions", label: "角色权限" }, { key: "/system/areas", label: "城区责任" }, { key: "/system/notifications", label: "我的消息" }, { key: "/system/settings", label: "Agent 设置" }, ]}, ]; export default function App() { const [authed, setAuthed] = useState(false); const [checking, setChecking] = useState(true); const [collapsed, setCollapsed] = useState(false); const [projectContext, setProjectContextState] = useState(getProjectContext()); const navigate = useNavigate(); const location = useLocation(); const checkAuth = useCallback(async () => { const token = localStorage.getItem("token"); if (!token) { setChecking(false); return; } try { await whoami(); setAuthed(true); } catch { localStorage.removeItem("token"); } setChecking(false); }, []); useEffect(() => { checkAuth(); }, [checkAuth]); useEffect(() => { const onChange = () => setProjectContextState(getProjectContext()); window.addEventListener("znkg-project-context", onChange); return () => window.removeEventListener("znkg-project-context", onChange); }, []); const handleLogout = () => { localStorage.removeItem("token"); setAuthed(false); navigate("/admin/login"); }; if (checking) return ; if (!authed) { return ( { setAuthed(true); navigate("/admin/projects"); }} />} /> { setAuthed(true); navigate("/admin/projects"); }} />} /> } /> ); } const onMenuClick = ({ key }: { key: string }) => navigate(`/admin${key}`); const selectedKey = location.pathname.replace("/admin", "") || "/projects"; const openKeys = [selectedKey.split("/").slice(0, 2).join("/")]; const showProjectContextBar = location.pathname === "/admin/projects"; return (
{!collapsed && }
{!collapsed && (
new2 · {RUNTIME_CONTEXT.port} 项目 · {displayProjectName(projectContext.projectId)} Schema · {RUNTIME_CONTEXT.dbSchema}
)}
{showProjectContextBar && } } /> {/* Plaza */} } /> } /> } /> } /> } /> } /> } /> {/* Modeling */} } /> } /> } /> {/* Acquisition */} } /> } /> } /> {/* Review */} } /> } /> } /> } /> } /> {/* Publish */} } /> } /> {/* Workbench */} } /> } /> } /> {/* System */} } /> } /> } /> } /> } /> } /> } /> navigate("/admin/projects")} />} /> } />
); }