diff --git a/frontend-web/src/core/page-layout/sidebar-menu.ts b/frontend-web/src/core/page-layout/sidebar-menu.ts index b959d0b2..1d3f3bd3 100644 --- a/frontend-web/src/core/page-layout/sidebar-menu.ts +++ b/frontend-web/src/core/page-layout/sidebar-menu.ts @@ -21,6 +21,7 @@ import { Grid3X3, HelpCircle, History, + Layers, Layout, Library, List, @@ -209,10 +210,16 @@ export const sidebarMenuItems: MenuItem[] = [ { id: "mgmt-agents", label: "智能体管理", icon: Bot, path: "/page/strategy/agents" }, { id: "mgmt-business-chain-mapping", - label: "业务链条管理", + label: "配置管理", icon: Network, path: "/page/strategy/business-chain-mapping", }, + { + id: "mgmt-business-chains", + label: "业务链条配置", + icon: Layers, + path: "/page/strategy/business-chains", + }, { id: "mgmt-skills", label: "技能管理", icon: Sparkles, path: "/page/strategy/skills" }, { id: "mgmt-memory", label: "记忆管理", icon: Brain, path: "/page/strategy/memory" }, { diff --git a/frontend-web/src/pages/BusinessChainMappingPage.tsx b/frontend-web/src/pages/BusinessChainMappingPage.tsx index a3d05703..ae2db62d 100644 --- a/frontend-web/src/pages/BusinessChainMappingPage.tsx +++ b/frontend-web/src/pages/BusinessChainMappingPage.tsx @@ -1,5 +1,5 @@ /** - * 业务链条管理页 —— 把固定业务码(3Q/6BF/7BF/8BF)绑定到「智能体」+「业务链」。 + * 配置管理页 —— 把固定业务码(3Q/6BF/7BF/8BF)绑定到「智能体」+「业务链」。 * * 表格三列:业务(只读)/ 智能体 / 业务链。编辑模式下后两列为可搜索下拉: * - 智能体来自 `/api/agents` @@ -129,7 +129,7 @@ export default function BusinessChainMappingPage() {

- 业务链条管理 + 配置管理

为每个业务绑定智能体与业务链。多智能体会商从对应业务自动取用此处配置的业务链。业务链仅可选择「公共」链条。 diff --git a/frontend-web/src/pages/StrategyRoutes.tsx b/frontend-web/src/pages/StrategyRoutes.tsx index 5f0129ed..dd8180b4 100644 --- a/frontend-web/src/pages/StrategyRoutes.tsx +++ b/frontend-web/src/pages/StrategyRoutes.tsx @@ -17,6 +17,8 @@ import { ActionAnalyzes } from "@/strategy-components/components/task-components import KnowledgeBaseResourceManagement from "@/strategy-components/components/resource-management/KnowledgeBaseResourceManagement"; import NetworkActorResourceManagement from "@/strategy-components/components/resource-management/NetworkActorResourceManagement"; import RoundtablePlanningPage from "@/roundtable-planning/pages/RoundtablePlanningPage"; +import BusinessChainListPage from "@/roundtable-planning/pages/BusinessChainListPage"; +import BusinessChainEditorPage from "@/roundtable-planning/pages/BusinessChainEditorPage"; import "@/roundtable-planning/styles/roundtable-planning.css"; import AgentChatPage from "./AgentChatPage"; import AgentsPage from "./AgentsPage"; @@ -174,6 +176,8 @@ export default function StrategyRoutes() { } /> } /> } /> + } /> + } /> } /> + + + + } + /> {/* Phase 7 临时预览入口:派活链路流程图弹窗(假数据自测,接通后台后可删) */} void; } -export function BusinessChainListDialog({ - open, - onOpenChange, +export interface BusinessChainListBodyProps { + /** + * 是否激活(控制何时拉取列表):弹窗里传 `open`(打开才拉), + * 独立页面里恒为 `true`。 + */ + active: boolean; + /** 进入配置页。带 chainId = 打开并切到该链条;不带 = 空白新建。 */ + onConfigure: (chainId?: string) => void; + /** 列表为空时的「去创建」引导文案差异(弹窗说「点下方配置」,页面说「点右上配置」)。 */ + emptyHint?: string; +} + +/** + * 业务链条列表主体(筛选 tab + 滚动卡片列表)。 + * + * 从 `BusinessChainListDialog` 抽出,供弹窗与独立配置页(`BusinessChainListPage`) + * 复用,保证两处的加载 / 发布 / 卡片渲染逻辑完全一致。外层负责标题栏与「配置」入口、 + * 以及撑出 `flex flex-col` 高度(本组件用 `flex-1` 占满剩余空间)。 + */ +export function BusinessChainListBody({ + active, onConfigure, -}: BusinessChainListDialogProps) { + emptyHint, +}: BusinessChainListBodyProps) { const [chains, setChains] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -65,9 +84,9 @@ export function BusinessChainListDialog({ } }, []); - // 每次打开 / 切换筛选都重新拉,保证配置页改动后回来看到的是最新列表。 + // 每次激活 / 切换筛选都重新拉,保证配置页改动后回来看到的是最新列表。 useEffect(() => { - if (!open) return; + if (!active) return; let cancelled = false; void (async () => { setLoading(true); @@ -84,7 +103,7 @@ export function BusinessChainListDialog({ return () => { cancelled = true; }; - }, [open, scope]); + }, [active, scope]); const handleTogglePublic = useCallback( async (chain: BusinessChain) => { @@ -101,6 +120,73 @@ export function BusinessChainListDialog({ [refresh, scope], ); + return ( + <> + {/* 筛选:我的 / 公共 */} +

+ {(["mine", "public"] as const).map((s) => ( + + ))} +
+ +
+ {loading ? ( +
+ + 正在加载业务链条… +
+ ) : error ? ( +
+ 加载业务链条失败:{error} +
+ ) : chains.length === 0 ? ( +
+ +
+ {scope === "public" ? "还没有公共链条" : "还没有业务链条"} +
+

+ {scope === "public" + ? "公共链条是其他账号(或你自己)发布出来、所有人可见的链条。去「我的」里发布一条试试。" + : emptyHint ?? + "业务链条是可复用的有序智能体编排。点击下方「配置」去创建第一条链条。"} +

+
+ ) : ( +
+ {chains.map((chain) => ( + onConfigure(chain.id)} + onTogglePublic={() => handleTogglePublic(chain)} + /> + ))} +
+ )} +
+ + ); +} + +export function BusinessChainListDialog({ + open, + onOpenChange, + onConfigure, +}: BusinessChainListDialogProps) { return ( @@ -114,61 +200,7 @@ export function BusinessChainListDialog({ - {/* 筛选:我的 / 公共 */} -
- {(["mine", "public"] as const).map((s) => ( - - ))} -
- -
- {loading ? ( -
- - 正在加载业务链条… -
- ) : error ? ( -
- 加载业务链条失败:{error} -
- ) : chains.length === 0 ? ( -
- -
- {scope === "public" ? "还没有公共链条" : "还没有业务链条"} -
-

- {scope === "public" - ? "公共链条是其他账号(或你自己)发布出来、所有人可见的链条。去「我的」里发布一条试试。" - : "业务链条是可复用的有序智能体编排。点击下方「配置」去创建第一条链条。"} -

-
- ) : ( -
- {chains.map((chain) => ( - onConfigure(chain.id)} - onTogglePublic={() => handleTogglePublic(chain)} - /> - ))} -
- )} -
+
+ ); +} + +// ── 链条席位编排流(体现「同层并行 · 层间串行」)───────────────────────────── + +function ChainSeatsFlow({ chain }: { chain: BusinessChain }) { + // 名称回退表:用落库快照(seats)解析 stage 里的 agent_id。 + const seatById = new Map(chain.seats.map((s) => [s.agent_id, s] as const)); + + // 分层来源:带 stages(分层编排)就按 stages 渲染(过滤空阶段);否则每个席位各成 + // 一段、顺序串行(线性链,与旧版展示一致)。 + const stages = + chain.stages && chain.stages.length > 0 + ? chain.stages.filter((s) => s.length > 0) + : chain.seats.map((s) => [s.agent_id]); + + // 跨阶段累加的全局序号 —— 决定头像配色,与编辑器 / 展平 seats 顺序对齐。 + const orderById = new Map(); + let counter = 0; + for (const stageIds of stages) { + for (const id of stageIds) { + orderById.set(id, counter); + counter += 1; + } + } + + const seatTag = (agentId: string) => { + const seat = seatById.get(agentId); + return ( + + {seat?.name || agentId} + + ); + }; + + return ( +
+ {stages.map((stageIds, stageIdx) => ( + + {stageIdx > 0 && ( + + )} + {stageIds.length > 1 ? ( + // 并行阶段:席位竖向并列在虚线框里,左上角标「并行 N」。 +
+ + 并行 {stageIds.length} + + {stageIds.map((id) => seatTag(id))} +
+ ) : ( + seatTag(stageIds[0]) + )} +
+ ))}
); } diff --git a/frontend-web/src/roundtable-planning/pages/BusinessChainListPage.tsx b/frontend-web/src/roundtable-planning/pages/BusinessChainListPage.tsx new file mode 100644 index 00000000..0929ce46 --- /dev/null +++ b/frontend-web/src/roundtable-planning/pages/BusinessChainListPage.tsx @@ -0,0 +1,64 @@ +/** + * 业务链条配置页(独立页面版)。 + * + * 把原本挂在圆桌页顶栏「业务链条」按钮上的 `BusinessChainListDialog` 弹窗内容, + * 单独画成一个全屏页面:综合管理 → 业务链条配置 入口进来即是本页。 + * + * - 列表主体复用 `BusinessChainListBody`(与弹窗同一份加载 / 发布 / 卡片逻辑)。 + * - 顶部「配置」按钮 = 进入编辑器空白新建;卡片「设置」= 进入编辑器并切到该链条。 + * + * 路由 base 感知:用 `useRouteNavigate` 跳转,strategy / workspace 两套路由树下 + * 都能落到当前 base 的编辑器(`roundtable/chains`)。 + */ +import { LayersIcon, SettingsIcon } from "lucide-react"; + +import { Button } from "@/components/ui/button"; +import { useRouteNavigate } from "@/core/route-base"; + +import { BusinessChainListBody } from "@/roundtable-planning/components/BusinessChainListDialog"; + +export default function BusinessChainListPage() { + const navigate = useRouteNavigate(); + + // 进入编辑器:带 chainId = 打开并切到该链条;不带 = 空白新建。 + // 路径写 workspace base,`useRouteNavigate` 会按当前路由 base 改写。 + const goConfigure = (chainId?: string) => { + navigate( + chainId + ? `/page/workspace/roundtable/chains?chainId=${encodeURIComponent(chainId)}` + : "/page/workspace/roundtable/chains", + ); + }; + + return ( +
+ {/* 顶栏:标题 + 描述 + 配置入口 */} +
+
+
+
+ + 业务链条配置 +
+

+ 可复用的有序智能体编排。点击卡片「设置」可进入编辑器修改该链条;「发布」后所有账号都能在「公共」里查看 / 选用。 +

+
+ +
+
+ + {/* 列表主体(与弹窗共用 BusinessChainListBody) */} +
+ +
+
+ ); +} diff --git a/frontend-web/src/strategy-components/components/task-components/SentimentAnalyzes.tsx b/frontend-web/src/strategy-components/components/task-components/SentimentAnalyzes.tsx index 4a624605..2d9156e5 100644 --- a/frontend-web/src/strategy-components/components/task-components/SentimentAnalyzes.tsx +++ b/frontend-web/src/strategy-components/components/task-components/SentimentAnalyzes.tsx @@ -170,7 +170,7 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa setShowAnalysisModal(false); }; - // 单智能体分析:用「业务链条管理」里为本业务配置的智能体,跳转到其问答页(新会话), + // 单智能体分析:用「配置管理」里为本业务配置的智能体,跳转到其问答页(新会话), // 把任务名称/简介作为开场消息暂存,由 AgentChatPage 读取 sessionStorage.pendingAgentMessage // 自动发出并开始问答。未配置智能体则提示去配置。 const handleSingleAgentAnalysis = async () => { @@ -184,7 +184,7 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa /* ignore → 下面按未配置处理 */ } if (!agentId) { - toast.error('该业务尚未在「业务链条管理」中配置单智能体'); + toast.error('该业务尚未在「配置管理」中配置单智能体'); return; } const text = [ diff --git a/frontend-web/src/strategy-components/components/task-components/TaskAnalyzes.tsx b/frontend-web/src/strategy-components/components/task-components/TaskAnalyzes.tsx index 7a8eea09..5ceb856a 100644 --- a/frontend-web/src/strategy-components/components/task-components/TaskAnalyzes.tsx +++ b/frontend-web/src/strategy-components/components/task-components/TaskAnalyzes.tsx @@ -170,7 +170,7 @@ const TaskAnalyzes: React.FC = ({ title, category, subCategor setShowAnalysisModal(false); }; - // 单智能体分析:用「业务链条管理」里为本业务配置的智能体,跳转到其问答页(新会话), + // 单智能体分析:用「配置管理」里为本业务配置的智能体,跳转到其问答页(新会话), // 把任务名称/简介作为开场消息暂存,由 AgentChatPage 读取 sessionStorage.pendingAgentMessage // 自动发出并开始问答。未配置智能体则提示去配置。 const handleSingleAgentAnalysis = async () => { @@ -184,7 +184,7 @@ const TaskAnalyzes: React.FC = ({ title, category, subCategor /* ignore → 下面按未配置处理 */ } if (!agentId) { - toast.error('该业务尚未在「业务链条管理」中配置单智能体'); + toast.error('该业务尚未在「配置管理」中配置单智能体'); return; } const text = [