From e6e283e41210bb0c2983da5ec9286757a386a187 Mon Sep 17 00:00:00 2001 From: 3452078359-xuexue <3452078359@qq.com> Date: Thu, 30 Jul 2026 11:14:05 +0800 Subject: [PATCH] feat: add relational data center entry --- admin-web/src/App.tsx | 5 +- .../panels/data-platform/DataCenterPanel.tsx | 156 +++++++++++++++++ admin-web/src/styles.css | 162 ++++++++++++++++++ 3 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 admin-web/src/panels/data-platform/DataCenterPanel.tsx diff --git a/admin-web/src/App.tsx b/admin-web/src/App.tsx index bb47908..9f0301b 100644 --- a/admin-web/src/App.tsx +++ b/admin-web/src/App.tsx @@ -5,7 +5,7 @@ import { HomeOutlined, ApartmentOutlined, ImportOutlined, CheckCircleOutlined, SendOutlined, ToolOutlined, SettingOutlined, LogoutOutlined, MenuFoldOutlined, MenuUnfoldOutlined, CompassOutlined, - ProjectOutlined, + ProjectOutlined, DatabaseOutlined, } from "@ant-design/icons"; import AdminLogin from "./AdminLogin"; import AdminLoginV2 from "./AdminLoginV2"; @@ -13,6 +13,7 @@ import Brand from "./Brand"; import { displayProjectName, getProjectContext, whoami, type KgProjectContext } from "./api"; import ProjectContextBar from "./ProjectContextBar"; import ProjectLanding from "./ProjectLanding"; +import DataCenterPanel from "./panels/data-platform/DataCenterPanel"; // Plaza panels (STEP 02) import PlazaOverview from "./panels/plaza/PlazaOverview"; @@ -70,6 +71,7 @@ const RUNTIME_CONTEXT = { const MENU_ITEMS = [ { key: "/projects", icon: , label: "项目工作区" }, + { key: "/data", icon: , label: "数据中心" }, { key: "/plaza", icon: , label: "知识广场", children: [ { key: "/plaza/overview", label: "馆藏概览" }, { key: "/plaza/user", icon: , label: "业务问答" }, @@ -227,6 +229,7 @@ export default function App() { {showProjectContextBar && } } /> + } /> {/* Plaza */} } /> } /> diff --git a/admin-web/src/panels/data-platform/DataCenterPanel.tsx b/admin-web/src/panels/data-platform/DataCenterPanel.tsx new file mode 100644 index 0000000..09d837b --- /dev/null +++ b/admin-web/src/panels/data-platform/DataCenterPanel.tsx @@ -0,0 +1,156 @@ +import { + ApartmentOutlined, + CheckCircleOutlined, + DatabaseOutlined, + ExportOutlined, + ImportOutlined, + SyncOutlined, + TableOutlined, +} from "@ant-design/icons"; +import { Alert, Card, Col, Row, Space, Tag, Typography } from "antd"; +import { displayProjectName, getProjectContext } from "../../api"; + +const { Paragraph, Text, Title } = Typography; + +const capabilityCards = [ + { + icon: , + title: "正式业务数据", + description: "统一管理酒店、美食、景区、交通及其明细表。", + status: "第一阶段", + }, + { + icon: , + title: "批量导入", + description: "接收处理完成的 CSV、XLSX 或多表 ZIP 数据包。", + status: "第二阶段", + }, + { + icon: , + title: "批量导出", + description: "按数据类型导出正式数据、明细表和数据字典。", + status: "第二阶段", + }, + { + icon: , + title: "图谱同步", + description: "把 PostgreSQL 正式数据同步为 FalkorDB 图谱投影。", + status: "第三阶段", + }, +]; + +const dataGroups = [ + { + icon: , + title: "正式实体", + tables: "poi_entities", + description: "保存酒店、美食、景区、交通等统一基础信息。", + }, + { + icon: , + title: "业务明细", + tables: "房型、设施、套餐、评论、公交线路与站序", + description: "一对多数据使用独立关系表,不再拼接成长文本。", + }, + { + icon: , + title: "必要运行记录", + tables: "导入、导出、修改日志", + description: "只记录正式数据操作结果,不保存原始采集和候选数据。", + }, + { + icon: , + title: "图谱投影", + tables: "graph_sync_queue", + description: "PostgreSQL 是权威数据源,FalkorDB 可按项目重新生成。", + }, +]; + +export default function DataCenterPanel() { + const context = getProjectContext(); + + return ( +
+
+
+ + + 关系数据中心 + + + 当前项目的 PostgreSQL 正式业务数据入口 + +
+ 项目 · {displayProjectName(context.projectId)} +
+ + + + + {capabilityCards.map((item) => ( + + +
{item.icon}
+
+ {item.title} + {item.status} +
+ {item.description} +
+ + ))} +
+ + PostgreSQL 权威数据} + > +
+ {dataGroups.map((group) => ( +
+
{group.icon}
+
+
+ {group.title} + {group.tables} +
+ {group.description} +
+
+ ))} +
+
+ + + + +
+ 进入 PostgreSQL +
    +
  • 处理完成的酒店、美食、景区、交通实体
  • +
  • 房型、设施、套餐、评论和公交站序
  • +
  • 已经确认的外部平台 ID、URL 和实体关系
  • +
+
+ + +
+ 不进入正式业务库 +
    +
  • 未清洗的接口原始响应和爬虫缓存
  • +
  • 候选匹配中间结果和重复计算过程
  • +
  • “仅剩几间”等无长期价值的实时文案
  • +
+
+ +
+
+
+ ); +} diff --git a/admin-web/src/styles.css b/admin-web/src/styles.css index b77dac6..63ed8d7 100644 --- a/admin-web/src/styles.css +++ b/admin-web/src/styles.css @@ -79,6 +79,168 @@ body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Ro font-weight: 700; } +/* ── Relational data center ── */ +.data-center-page { + max-width: 1440px; + margin: 0 auto; +} + +.data-center-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 20px; + margin-bottom: 20px; +} + +.data-center-header .ant-typography { + margin: 0; +} + +.data-center-header h2.ant-typography { + font-size: 24px; +} + +.data-center-header p.ant-typography { + margin-top: 6px; +} + +.data-center-title-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 38px; + height: 38px; + border-radius: 10px; + color: #1677ff; + background: #eaf3ff; + font-size: 20px; +} + +.data-center-capabilities { + margin-top: 20px; +} + +.data-center-capability-card { + height: 100%; + border-color: #e6edf7; + border-radius: 12px; +} + +.data-center-capability-card .ant-card-body { + min-height: 170px; +} + +.data-center-capability-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + margin-bottom: 18px; + border-radius: 10px; + color: #1677ff; + background: #f0f6ff; + font-size: 19px; +} + +.data-center-capability-heading { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 8px; + font-size: 16px; +} + +.data-center-capability-card p.ant-typography { + margin-bottom: 0; + line-height: 1.7; +} + +.data-center-structure-card, +.data-center-boundary-card { + margin-top: 20px; + border-color: #e6edf7; + border-radius: 12px; +} + +.data-center-groups { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px; +} + +.data-center-group { + display: flex; + gap: 14px; + min-width: 0; + padding: 16px; + border: 1px solid #edf1f7; + border-radius: 10px; + background: #fbfcff; +} + +.data-center-group-icon { + display: inline-flex; + align-items: center; + justify-content: center; + flex: 0 0 34px; + width: 34px; + height: 34px; + border-radius: 9px; + color: #5b6b82; + background: #fff; + box-shadow: 0 1px 4px rgba(31, 55, 88, 0.08); +} + +.data-center-group-content { + min-width: 0; +} + +.data-center-group-title { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; + margin-bottom: 6px; +} + +.data-center-boundary { + height: 100%; + padding: 16px 18px; + border-radius: 10px; +} + +.data-center-boundary ul { + margin: 10px 0 0; + padding-left: 20px; + color: #5b6678; + line-height: 1.9; +} + +.data-center-boundary-include { + border: 1px solid #d9f0df; + background: #f7fcf8; +} + +.data-center-boundary-exclude { + border: 1px solid #f0e6d8; + background: #fffaf4; +} + +@media (max-width: 900px) { + .data-center-groups { + grid-template-columns: 1fr; + } +} + +@media (max-width: 640px) { + .data-center-header { + flex-direction: column; + } +} + /* ── Plaza Overview ── */ .plaza-overview { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 24px; } .plaza-overview .stat-card { text-align: center; padding: 24px; }