From 02eee4bd3eecbb206831c0ff73f11a767fa2a1a0 Mon Sep 17 00:00:00 2001 From: fengxiangdebaozi <10845145+fengxiangdebaozi@user.noreply.gitee.com> Date: Tue, 2 Jun 2026 21:25:44 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/multi-agent-business-chain-dev.md | 1 + .../pages/BusinessChainEditorPage.tsx | 124 +++++++++--------- 2 files changed, 63 insertions(+), 62 deletions(-) diff --git a/frontend-web/docs/multi-agent-business-chain-dev.md b/frontend-web/docs/multi-agent-business-chain-dev.md index 6a9a5338..f2946bc4 100644 --- a/frontend-web/docs/multi-agent-business-chain-dev.md +++ b/frontend-web/docs/multi-agent-business-chain-dev.md @@ -299,6 +299,7 @@ chain 模式的 Step 2 init **完全复用** `initMultiAgent`:`agentNames = ch - **验收**:✅ typecheck 0 新增错误(76 个预存量错误均在无关模块)。**人工验收待跑**:新建「情报→方案→风险→总结」链保存 → 刷新后下拉重载 → 右栏拖拽排序 → 左栏拖入指定位 → 2–8 边界禁用保存。 - **切换成本备注**:若改全屏 Dialog 方案,编辑器组件原样复用,仅去掉路由、外面套 `Dialog`。 - **入口说明**:圆桌本身未在侧栏(`sidebar-menu.ts` 入口被注释),本页暂经直链 `/page/workspace/roundtable/chains` 或顶栏「返回圆桌」往返;阶段 3 推荐弹窗「去配置」按钮会导航到此。 +- **视觉(后续重画)**:本页**不**套圆桌 `roundtable-planning` scope,直接遵循《浅红色/深蓝色 UI 规范》——用 app 语义 token(光 `--primary` 红 hue27 / 暗 蓝 hue259,已内置自适应)+ 规范小圆角(输入/按钮/tag/卡片内项 2px = `R_CTRL`,分栏/面板 4px = `R_CARD`)+ 克制阴影(`PANEL_SHADOW`)。筛选框复用通用 `TagSearchBar`,配色随 app 中性 token,不再被红 scope 染色。 ### 阶段 2 · 推荐弹窗「模式选择」外壳(1 天) ✅ 已完成 **目标**:弹窗顶部模式切换,智能推荐行为不变。 diff --git a/frontend-web/src/roundtable-planning/pages/BusinessChainEditorPage.tsx b/frontend-web/src/roundtable-planning/pages/BusinessChainEditorPage.tsx index 57d7d306..6144b8fb 100644 --- a/frontend-web/src/roundtable-planning/pages/BusinessChainEditorPage.tsx +++ b/frontend-web/src/roundtable-planning/pages/BusinessChainEditorPage.tsx @@ -11,8 +11,9 @@ * 拖拽用 `@dnd-kit`:右栏是 `SortableContext`(席位互相排序),左栏卡片是 * `useDraggable`(pool→右栏插入)。落点计算在 `onDragEnd`。 * - * 视觉:套 `roundtable-planning`(+`--dark`) 作用域,与圆桌主页面同一套浅红/深蓝皮肤 - * (`styles/roundtable-planning.css`,对齐 docs/浅红色UI规范 + ui规范-深蓝)。 + * 视觉:直接遵循《浅红色 UI 规范》/《深蓝色 UI 规范》—— 用 app 语义 token(光红暗蓝 + * 已内置)+ 规范小圆角(输入/按钮/tag 2px、卡片/分栏 4px)+ 克制阴影。**不**套圆桌页 + * 的 `roundtable-planning` scope。 * * 持久化走 `api/chains.ts`(后端 MySQL `/api/roundtable-chains`)。席位数约束 * 2 ≤ N ≤ 8、agent 去重,保存时校验(后端 `_validate_seats` 也兜底)。 @@ -56,7 +57,6 @@ import { import { CSS } from "@dnd-kit/utilities"; import { cn } from "@/lib/utils"; -import { useTheme } from "@/contexts/ThemeContext"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { TagSearchBar } from "@/components/workspace/tags/tag-search-bar"; @@ -85,6 +85,13 @@ const POOL_PREFIX = "pool::"; const SEAT_PREFIX = "seat::"; const SEATS_DROPPABLE_ID = "seats-droppable"; +// 规范圆角(§4):输入/按钮/tag 2px,卡片/分栏 4px。app 默认 --radius=12px 偏大, +// 这里用 arbitrary 值精确命中规范。 +const R_CARD = "rounded-[4px]"; +const R_CTRL = "rounded-[2px]"; +// 规范阴影(§3):克制的浅投影,深色下几乎不可见。 +const PANEL_SHADOW = "shadow-[0_1px_2px_rgba(15,23,42,0.05)]"; + type ActiveDrag = | { kind: "pool"; agentId: string; name: string } | { kind: "seat"; agentId: string; name: string } @@ -92,8 +99,6 @@ type ActiveDrag = export default function BusinessChainEditorPage() { const navigate = useNavigate(); - const { theme } = useTheme(); - const isDark = theme === "dark"; // ─── 数据 ──────────────────────────────────────────────────────────── const [pool, setPool] = useState([]); @@ -117,8 +122,6 @@ export default function BusinessChainEditorPage() { useSensor(PointerSensor, { activationConstraint: { distance: 5 } }), ); - // 候选池按 agent_id 取元数据。注意 pool 是「当前筛选视图」——已加入但被筛掉 - // 的席位名称回退用 seat 自带快照(loadChain 写入),所以 poolById 缺项不影响保存。 const poolById = useMemo(() => { const m = new Map(); for (const a of pool) m.set(a.agent_id, a); @@ -235,7 +238,6 @@ export default function BusinessChainEditorPage() { const overId = e.over ? String(e.over.id) : null; if (!overId) return; - // over 落在第几个席位之前(over 是席位则取其索引,否则追加到末尾)。 const overSeatIndex = overId.startsWith(SEAT_PREFIX) ? seats.findIndex((s) => SEAT_PREFIX + s.agent_id === overId) : -1; @@ -344,31 +346,26 @@ export default function BusinessChainEditorPage() { const seatItemIds = seats.map((s) => SEAT_PREFIX + s.agent_id); return ( -
+
{/* 顶栏 */} -
+
-
+
- +
-
业务链条配置
-
编排有序的智能体研讨链路
+
业务链条配置
+
编排有序的智能体研讨链路
@@ -385,7 +382,10 @@ export default function BusinessChainEditorPage() { const chain = chains.find((c) => c.id === id); if (chain) loadChain(chain); }} - className="h-8 rounded-md border border-input bg-card px-2.5 text-sm font-medium shadow-sm outline-none focus:border-primary/60 focus:ring-2 focus:ring-primary/20" + className={cn( + "h-8 border border-input bg-card px-2.5 text-sm text-foreground outline-none transition-colors focus:border-primary focus:ring-1 focus:ring-primary/30", + R_CTRL, + )} > {chains.map((c) => ( @@ -394,7 +394,7 @@ export default function BusinessChainEditorPage() { ))} - @@ -406,7 +406,7 @@ export default function BusinessChainEditorPage() { size="sm" onClick={handleDelete} disabled={!currentChainId} - className="gap-1.5 text-destructive hover:text-destructive" + className={cn("h-8 gap-1.5 text-destructive hover:text-destructive", R_CTRL)} > 删除 @@ -415,7 +415,7 @@ export default function BusinessChainEditorPage() { size="sm" onClick={handleSave} disabled={!canSave} - className="gap-1.5 bg-primary text-primary-foreground hover:opacity-90" + className={cn("h-8 gap-1.5", R_CTRL)} > {saving ? : } 保存 @@ -424,24 +424,24 @@ export default function BusinessChainEditorPage() {
{/* 标题 + 描述 */} -
-
- 标题 +
+
+ 标题 setTitle(e.target.value)} placeholder="如:应急方案三段链" - className="bg-card" + className={cn("h-8 bg-background", R_CTRL)} maxLength={120} />
- 描述 + 描述 setDescription(e.target.value)} placeholder="链条用途说明(可选)" - className="bg-card" + className={cn("h-8 bg-background", R_CTRL)} maxLength={500} />
@@ -456,15 +456,15 @@ export default function BusinessChainEditorPage() { >
{/* 左栏:候选智能体 */} -
-
-
+
+
+
候选智能体 - + {pool.length} - + 拖到右侧 / 点击加入
@@ -475,7 +475,7 @@ export default function BusinessChainEditorPage() { onTagIdsChange={setFilterTagIds} />
-
+
{poolLoading ? (
@@ -511,7 +511,7 @@ export default function BusinessChainEditorPage() { {activeDrag ? ( -
+
{activeDrag.name}
@@ -541,17 +541,18 @@ function PoolAgentCard({
-
{agent.name || agent.agent_id}
+
{agent.name || agent.agent_id}
{agent.description && (
{agent.description} @@ -559,7 +560,7 @@ function PoolAgentCard({ )}
{inChain ? ( - + 已加入 ) : ( @@ -567,7 +568,10 @@ function PoolAgentCard({ type="button" onClick={onAdd} onPointerDown={(e) => e.stopPropagation()} - className="shrink-0 self-center rounded-lg p-1.5 text-muted-foreground opacity-0 transition-all hover:bg-primary hover:text-primary-foreground group-hover:opacity-100" + className={cn( + "shrink-0 self-center p-1.5 text-muted-foreground opacity-0 transition-all hover:bg-primary hover:text-primary-foreground group-hover:opacity-100", + R_CTRL, + )} title="加入链条" > @@ -592,16 +596,17 @@ function SeatsColumn({ }) { const { setNodeRef, isOver } = useDroppable({ id: SEATS_DROPPABLE_ID }); return ( -
-
+
+
链条席位 - + 按顺序发言 {seats.length === 0 ? ( -
+
从左侧把智能体拖到这里,组成有序业务链条
@@ -630,7 +635,6 @@ function SeatsColumn({ key={seat.agent_id} seat={seat} index={index} - isLast={index === seats.length - 1} onRemove={() => onRemove(seat.agent_id)} /> ))} @@ -646,12 +650,10 @@ function SeatsColumn({ function SeatStrip({ seat, index, - isLast, onRemove, }: { seat: ChainSeat; index: number; - isLast: boolean; onRemove: () => void; }) { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = @@ -662,14 +664,11 @@ function SeatStrip({ ref={setNodeRef} style={{ transform: CSS.Transform.toString(transform), transition }} className={cn( - "relative flex items-center gap-3 rounded-xl border border-border bg-card p-3 shadow-sm transition-shadow", - isDragging && "z-10 opacity-70 shadow-lg ring-1 ring-primary/30", + "flex items-center gap-3 border border-border bg-card p-2.5", + R_CTRL, + isDragging && "z-10 opacity-70 shadow-[0_4px_12px_rgba(15,23,42,0.12)]", )} > - {/* 顺序连接线(最后一个不画) */} - {!isLast && ( - - )}