diff --git a/frontend-web/.env.production b/frontend-web/.env.production
index c655d856..d7dac2cd 100644
--- a/frontend-web/.env.production
+++ b/frontend-web/.env.production
@@ -5,6 +5,8 @@
VITE_BACKEND_BASE_URL=https://ch1.b.uat.4.cn/deerflow
VITE_LANGGRAPH_BASE_URL=https://ch1.b.uat.4.cn/deerflow/api
+# CMZS后端接口
+VITE_API_BASE_URL=https://ch1.b.uat.4.cn/zncm
# 轻应用「新建笔记」系列接口
VITE_NOTE_API_BASE_URL=https://your-intranet-host/note-api
# 佳来模型对话接口
diff --git a/frontend-web/src/core/page-layout/sidebar-menu.ts b/frontend-web/src/core/page-layout/sidebar-menu.ts
index 674e7e52..d2133107 100644
--- a/frontend-web/src/core/page-layout/sidebar-menu.ts
+++ b/frontend-web/src/core/page-layout/sidebar-menu.ts
@@ -22,6 +22,7 @@ import {
Grid3X3,
HelpCircle,
History,
+ Layers,
Layout,
Library,
List,
@@ -61,8 +62,13 @@ export const sidebarMenuItems: MenuItem[] = [
path: "/page/strategy/qa/multi-agent-roundtable",
},
// { id: "expert-qa", label: "专家问答", icon: Brain, path: "/page/strategy/qa/expert-qa" },
- { id: "qa-zzjc", label: "zzjc支持", icon: Bot },
- { id: "qa-zzxd", label: "zzXD应用", icon: Layout },
+ {
+ id: "qa-zzjc",
+ label: "zzjc支持",
+ icon: Bot,
+ path: "/page/strategy/agents/6bf/chats/new",
+ },
+ { id: "qa-zzxd", label: "zzXD应用", icon: Layout, path: "/page/strategy/agents/10d69686718349688e58a8790cb61024/chats/new", },
{ id: "qa-research", label: "课题研究", icon: FileText, path: "/page/canvas/ai-writing" },
],
},
@@ -209,10 +215,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 (
+ );
+}
+
+// ── 链条席位编排流(体现「同层并行 · 层间串行」)─────────────────────────────
+
+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/components/BusinessChainPicker.tsx b/frontend-web/src/roundtable-planning/components/BusinessChainPicker.tsx
index 05dda9b6..bb431011 100644
--- a/frontend-web/src/roundtable-planning/components/BusinessChainPicker.tsx
+++ b/frontend-web/src/roundtable-planning/components/BusinessChainPicker.tsx
@@ -289,6 +289,43 @@ function ChainCard({
const deletedCount = chain.seats.length - survivingCount;
const usable = survivingCount >= CHAIN_MIN_SEATS;
+ // ── 席位编排(体现「同层并行 · 层间串行」)─────────────────────────────
+ // 名称回退表:实时 name(nameById)→ 落库快照(seat.name)→ 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 seatOrder = 0;
+ for (const stageIds of stages) {
+ for (const id of stageIds) {
+ orderById.set(id, seatOrder);
+ seatOrder += 1;
+ }
+ }
+ // 单个席位 tag:存活按配色,已删除置灰删除线(保留旧行为)。
+ const seatTag = (agentId: string) => {
+ const alive = isExisting(agentId);
+ const label = nameById.get(agentId) ?? seatById.get(agentId)?.name ?? agentId;
+ return (
+
+ {label}
+
+ );
+ };
+
return (
)}
- {/* 智能体名 tag(按顺序,← 箭头连接) */}
+ {/* 席位编排:按 stage 分组——并行阶段竖向并列在虚线框里,阶段之间用 → 串联。
+ 不用 :dev 期 tagger 会往 Fragment 注入 data-lov-id 触发 React 警告,
+ 这里用带 key 的扁平数组(箭头 + 阶段),注入落到真实元素上即可。 */}
- {chain.seats.flatMap((seat, i) => {
- const alive = isExisting(seat.agent_id);
- const label = nameById.get(seat.agent_id) ?? seat.name ?? seat.agent_id;
- // 不用
:dev 期 tagger 会往 Fragment 注入 data-lov-id 触发 React 警告,
- // 这里改成带 key 的扁平数组(箭头 + tag),注入落到真实元素上即可。
+ {stages.flatMap((stageIds, stageIdx) => {
const nodes: ReactNode[] = [];
- if (i > 0) {
+ if (stageIdx > 0) {
nodes.push(
,
);
}
- nodes.push(
-
- {label}
- ,
- );
+ if (stageIds.length > 1) {
+ // 并行阶段:席位竖向并列在虚线框里,左上角标「并行 N」。
+ nodes.push(
+
+
+ 并行 {stageIds.length}
+
+ {stageIds.map((id) => seatTag(id))}
+
,
+ );
+ } else {
+ nodes.push(seatTag(stageIds[0]));
+ }
return nodes;
})}
diff --git a/frontend-web/src/roundtable-planning/hooks/useStep1Intent.ts b/frontend-web/src/roundtable-planning/hooks/useStep1Intent.ts
index 89ec494e..251214f1 100644
--- a/frontend-web/src/roundtable-planning/hooks/useStep1Intent.ts
+++ b/frontend-web/src/roundtable-planning/hooks/useStep1Intent.ts
@@ -123,6 +123,12 @@ export interface UseStep1IntentReturn {
clarifications: ClarificationPayload[],
overrideByKey?: Record,
) => void;
+ /**
+ * 用户在「当前意图摘要」卡里手动编辑后保存:直接覆盖 `intentReady`。下游(Step 2 seed
+ * `buildInitialIntent`、左右侧栏摘要卡、step2Locked 判定)都读 `intentReady`,改这里即全局生效。
+ * 持久化由调用方(页面)在更新后触发草稿保存。
+ */
+ updateIntentReady: (next: IntentSummary) => void;
/** Abort 当前 intent 流。气泡尾部追加「(已暂停)」并 toast。 */
stopIntentStream: () => void;
/** 从草稿加载:批量 setState + 标记 init 已完成,跳过自动 runStep1Init。 */
@@ -568,6 +574,12 @@ export function useStep1Intent(options: UseStep1IntentOptions): UseStep1IntentRe
setInteractiveMessages((prev) => [...prev, msg]);
}, []);
+ const updateIntentReady = useCallback((next: IntentSummary) => {
+ // 用户手动改完意图 → 覆盖为权威值,并清掉「待澄清」态(已确认意图,无需再答问)。
+ setIntentReady(next);
+ setPendingIntentClarificationMsgId(null);
+ }, []);
+
return {
intentThreadId,
intentReady,
@@ -582,6 +594,7 @@ export function useStep1Intent(options: UseStep1IntentOptions): UseStep1IntentRe
runStep1Init,
sendIntentMessage,
submitIntentClarifications,
+ updateIntentReady,
stopIntentStream,
hydrateFromDraft,
appendLocalMessage,
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/roundtable-planning/pages/RoundtablePlanningPage.tsx b/frontend-web/src/roundtable-planning/pages/RoundtablePlanningPage.tsx
index e4d75d7f..4e311ef2 100644
--- a/frontend-web/src/roundtable-planning/pages/RoundtablePlanningPage.tsx
+++ b/frontend-web/src/roundtable-planning/pages/RoundtablePlanningPage.tsx
@@ -41,12 +41,14 @@ import {
ListOrdered,
Loader2,
Pencil,
+ Plus,
Save,
Settings,
Sparkles,
Trash2,
Upload,
Users,
+ X,
} from "lucide-react";
import { useTheme } from "@/contexts/ThemeContext";
@@ -89,6 +91,7 @@ import {
type SeatSelectionResult,
} from "@/roundtable-planning/lib/business-chain";
import type { OrchestrationPlan } from "@/roundtable-planning/lib/dag-types";
+import type { IntentSummary } from "@/roundtable-planning/api/intent";
import { getJobByDraft, startJob } from "@/roundtable-planning/api/roundtable-jobs";
import { getChain } from "@/roundtable-planning/api/chains";
import { filterRecommendCandidates } from "@/roundtable-planning/api/recommend";
@@ -499,6 +502,26 @@ export default function RoundtablePlanningPage() {
loadedFromDraftRefHolder.current = drafts.loadedFromDraftRef.current;
});
+ // ─── 用户手动编辑「当前意图摘要」并保存 ───────────────────────
+ // 先 updateIntentReady 覆盖意图,再 bump 计数触发一次草稿保存。**必须用计数 + effect**
+ // 而非直接调 handleManualSave:enqueueSave 闭包捕获的 getStep1Snapshot 读的是本次渲染的
+ // step1.intentReady,同步调用会在 setState flush 前存进旧意图(踩坑);放到 effect 里,等
+ // 重渲染后再保存,getStep1Snapshot 才读到新值(与 step2/step3 的计数触发器同构)。
+ const [intentEditTick, setIntentEditTick] = useState(0);
+ const handleSaveIntent = useCallback(
+ (next: IntentSummary) => {
+ step1.updateIntentReady(next);
+ setIntentEditTick((t) => t + 1);
+ },
+ [step1],
+ );
+ useEffect(() => {
+ if (intentEditTick === 0) return;
+ drafts.handleManualSave();
+ // 仅在 tick 递增时保存;handleManualSave 取最新闭包(effect 跑在重渲染之后)。
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [intentEditTick]);
+
// ─── 从侧边栏「多智能体会商」记录进入 ───────────────────────
// 侧边栏点击某条草稿时:不在本页 → 把 id 存进 sessionStorage 再跳转,挂载时消费;
// 已在本页 → 直接派 `roundtable:open-draft` 事件。两条路径都走 handleLoadDraft。
@@ -1106,6 +1129,7 @@ export default function RoundtablePlanningPage() {
intentReady={step1.intentReady}
timelineEntries={step1.timelineEntries}
triggerToast={triggerToast}
+ onSaveIntent={handleSaveIntent}
/>
@@ -1763,6 +1787,148 @@ function ModelSelectorCard({
);
}
+/**
+ * 「当前意图摘要」编辑态:目标(单行/多行文本)、约束、关键假设(均为可增删的列表)。
+ * 受控本地草稿,保存时过滤空行、要求目标非空,再上抛 {@link IntentSummary}。
+ */
+function IntentSummaryEditor({
+ initial,
+ title,
+ onSave,
+ onCancel,
+}: {
+ initial: IntentSummary;
+ title: string;
+ onSave: (next: IntentSummary) => void;
+ onCancel: () => void;
+}) {
+ const [objective, setObjective] = useState(initial.objective ?? "");
+ const [constraints, setConstraints] = useState(
+ initial.constraints?.length ? [...initial.constraints] : [""],
+ );
+ const [assumptions, setAssumptions] = useState(
+ initial.assumptions?.length ? [...initial.assumptions] : [],
+ );
+
+ const objectiveInvalid = objective.trim().length === 0;
+
+ const updateRow = (
+ setter: React.Dispatch>,
+ idx: number,
+ value: string,
+ ) => setter((prev) => prev.map((v, i) => (i === idx ? value : v)));
+ const removeRow = (setter: React.Dispatch>, idx: number) =>
+ setter((prev) => prev.filter((_, i) => i !== idx));
+ const addRow = (setter: React.Dispatch>) =>
+ setter((prev) => [...prev, ""]);
+
+ const handleSave = () => {
+ if (objectiveInvalid) return;
+ onSave({
+ objective: objective.trim(),
+ constraints: constraints.map((c) => c.trim()).filter(Boolean),
+ assumptions: assumptions.map((a) => a.trim()).filter(Boolean),
+ });
+ };
+
+ const rowList = (
+ label: string,
+ rows: string[],
+ setter: React.Dispatch>,
+ placeholder: string,
+ ) => (
+
+
{label}
+
+ {rows.map((row, i) => (
+
+ updateRow(setter, i, e.target.value)}
+ placeholder={placeholder}
+ maxLength={200}
+ className="min-w-0 flex-1 rounded-lg border border-slate-200 bg-white px-2.5 py-1.5 text-[11.5px] text-slate-700 focus:border-primary focus:outline-none focus:ring-1 focus:ring-[var(--brand-200)]/50"
+ />
+
+
+ ))}
+
+
+
+ );
+
+ return (
+
+
+
+
+
+ 编辑{title}
+
+
+
+
+
+
+
+
+
+
+ {rowList("约束", constraints, setConstraints, "如:预算 / 时间 / 合规 / 范围")}
+ {rowList("假设", assumptions, setAssumptions, "智能体为你做的假设(可选)")}
+
+
+ );
+}
+
interface IntentSummaryCardProps {
intentReady: ReturnType["intentReady"];
title?: string;
@@ -1773,6 +1939,11 @@ interface IntentSummaryCardProps {
expanded: boolean;
onToggle: () => void;
};
+ /**
+ * 提供则显示「编辑」按钮,允许用户手动修改识别出的意图(目标 / 约束 / 假设)并保存。
+ * 保存后回调上抛新意图,由页面覆盖 `intentReady` 并持久化草稿。不传 = 只读。
+ */
+ onSave?: (next: IntentSummary) => void;
}
function IntentSummaryCard({
@@ -1780,9 +1951,25 @@ function IntentSummaryCard({
title = "当前意图摘要",
collapsible = false,
sidebarToggle,
+ onSave,
}: IntentSummaryCardProps) {
const [expanded, setExpanded] = useState(!collapsible);
const showFullDetail = !collapsible || expanded;
+ const [editing, setEditing] = useState(false);
+
+ if (editing && intentReady) {
+ return (
+ setEditing(false)}
+ onSave={(next) => {
+ onSave?.(next);
+ setEditing(false);
+ }}
+ />
+ );
+ }
return (
@@ -1791,21 +1978,33 @@ function IntentSummaryCard({
{title}
- {sidebarToggle && (
-
- )}
+
+ {onSave && intentReady && (
+
+ )}
+ {sidebarToggle && (
+
+ )}
+
@@ -2052,6 +2251,8 @@ interface RightSidebarProps {
intentReady: ReturnType
["intentReady"];
timelineEntries: ReturnType["timelineEntries"];
triggerToast: (msg: string) => void;
+ /** 用户编辑并保存意图的回调(透传给 IntentSummaryCard)。 */
+ onSaveIntent: (next: IntentSummary) => void;
}
function RightSidebar({
@@ -2063,6 +2264,7 @@ function RightSidebar({
intentReady,
timelineEntries,
triggerToast,
+ onSaveIntent,
}: RightSidebarProps) {
// Step 2 侧栏内容已迁至左侧;Step 3 沿用原逻辑不展示右侧栏。
if (currentStep !== 1) return null;
@@ -2078,7 +2280,7 @@ function RightSidebar({
modelsLoading={modelsLoading}
/>
-
+
{/* 澄清记录 Timeline */}
diff --git a/frontend-web/src/strategy-components/components/task-components/KanbanColumn.tsx b/frontend-web/src/strategy-components/components/task-components/KanbanColumn.tsx
index 237c30d3..d59ede02 100644
--- a/frontend-web/src/strategy-components/components/task-components/KanbanColumn.tsx
+++ b/frontend-web/src/strategy-components/components/task-components/KanbanColumn.tsx
@@ -37,10 +37,10 @@ export const KanbanColumn: React.FC
= ({
const columnClass = isDark
? 'border border-[rgba(67,89,124,0.46)] bg-[linear-gradient(180deg,rgba(18,36,67,0.96),rgba(12,28,54,0.96))] shadow-[0_18px_40px_rgba(3,10,22,0.18)]'
- : 'border border-[#E8E8E8] bg-[linear-gradient(180deg,#FFFFFF_0%,#FFF9F9_100%)] shadow-[0_14px_28px_rgba(245,34,45,0.06)]';
+ : 'border border-[#E8E8E8] bg-[linear-gradient(180deg,#FFFFFF_0%,#FFF9F9_100%)] shadow-[0_2px_8px_rgba(34,34,34,0.08)]';
return (
-
+
@@ -53,7 +53,7 @@ export const KanbanColumn: React.FC = ({
{title}
@@ -76,14 +76,14 @@ export const KanbanColumn: React.FC = ({
/>
-
+
按时间倒序展示,支持分页切换。
-
+
{tasks.length === 0 ? (
= ({ 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 = [
@@ -226,7 +226,7 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa
const toolbarClass = isDark
? 'border border-[rgba(67,89,124,0.46)] bg-[rgba(10,23,42,0.94)] text-white shadow-[0_16px_36px_rgba(3,10,22,0.2)]'
- : 'border border-[#E8E8E8] bg-white text-[#222222] shadow-[0_14px_30px_rgba(245,34,45,0.06)]';
+ : 'border border-[#E8E8E8] bg-white text-[#222222] shadow-[0_2px_8px_rgba(34,34,34,0.08)]';
const metricClass = isDark
? 'border border-[rgba(67,89,124,0.46)] bg-[rgba(12,29,54,0.94)]'
@@ -237,31 +237,31 @@ const SentimentAnalyzes: React.FC = ({ title, category, subCa
: 'border-[#E8E8E8] bg-white text-[#222222] placeholder:text-[#A6A6A6]';
const metrics = [
- { label: '全部任务', value: unanalyzedTotal + runningTotal + completedTotal },
- { label: '未分析', value: unanalyzedTotal },
- { label: '分析中', value: runningTotal },
- { label: '已完成', value: completedTotal },
+ { label: '全部任务', value: unanalyzedTotal + runningTotal + completedTotal, icon: Layers, fg: '#222222', chipBg: '#F5F5F5', chipFg: '#7F7F7F' },
+ { label: '未分析', value: unanalyzedTotal, icon: Inbox, fg: '#F5222D', chipBg: '#FEEDED', chipFg: '#F5222D' },
+ { label: '分析中', value: runningTotal, icon: Loader, fg: '#1677FF', chipBg: '#EAF4FF', chipFg: '#1677FF' },
+ { label: '已完成', value: completedTotal, icon: CircleCheck, fg: '#52C41A', chipBg: '#EEFCF7', chipFg: '#189D72' },
];
return (
-
+
-
-
-
+
+
+
{category}
-
-
+
+
{subCategory}
-
{title}
-
+
{title}
+
面向规划任务的情绪与倾向分析看板,支持快速检索、会商启动与结果回看。
@@ -269,7 +269,7 @@ const SentimentAnalyzes: React.FC
= ({ title, category, subCa
-