feat(roundtable): 深链策划流程小图 + 流程图弹窗横向/缩略图/深蓝适配
- Step1 消息列表头部:深链 taskId 模式取到目的树后,左侧标题换成「策划流程」 彩色层级小图(任务→目的→…→叙事,每级配色对齐流程图节点 + 类型小图标), 点击打开任务目的流程图弹窗;仅第一步、有数据时出现。 - TaskPurposeDialog 流程图:横向(LR)布局 + 右下角缩略图(MiniMap)。 - FlowGraphPanel 深蓝主题适配(随 ThemeContext):容器/头部/画布点阵/连线/图例/ 小地图/节点详情弹窗 + 节点配色(FLOW_NODE_TYPE_META_DARK),浅色红主题不受影响。 - 圆桌侧栏深色兜底:neutral 家族 + 米色 hex(#FAF6F5/#ECE7E4/#E8D0CC) 映射到深蓝 token(关系图背板、第N步 tag 等不再泛白)。 附带提交工作区其余暂存改动(AI 写作等),不含 config.yaml。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
85cca36ddb
commit
03b6fb724c
40 files changed
+2167
-119
No files matched your search
@@ -15,6 +15,12 @@ import {
|
||||
getCachedTaskDeeplinkConfig,
|
||||
} from "@/core/auth/task-deeplink-config";
|
||||
import { requestCrudService } from "@/service/request-axios-service";
|
||||
import type {
|
||||
FlowGraphData,
|
||||
FlowGraphEdge,
|
||||
FlowGraphNode,
|
||||
FlowNodeType,
|
||||
} from "@/roundtable-planning/lib/flow-graph";
|
||||
|
||||
/** 归一化后的目的树节点(用于展示,逐层:目的→行为体→预期行为→驱动因素→叙事)。 */
|
||||
export interface PurposeTreeNode {
|
||||
@@ -112,6 +118,83 @@ export function flattenPurposeTree(nodes: PurposeTreeNode[]): PurposeFlatCell[][
|
||||
return rows;
|
||||
}
|
||||
|
||||
/* --------------------- 目的树 → 流程图(flow-graph) --------------------- */
|
||||
|
||||
/**
|
||||
* 目的树各层级标签 → 流程图节点类型(仅决定配色,见 FLOW_NODE_TYPE_META)。
|
||||
* 未知层级回退 task。层级名本身仍原样写进节点 group,画布上当小标签显示。
|
||||
*/
|
||||
const PURPOSE_LABEL_TO_FLOW_TYPE: Record<string, FlowNodeType> = {
|
||||
目的: "stage",
|
||||
行为体: "role",
|
||||
预期行为: "task",
|
||||
驱动因素: "decision",
|
||||
叙事: "deliverable",
|
||||
};
|
||||
|
||||
/** 目的树的一个层级(深度):层级中文名 + 对应流程图节点类型(决定配色)。 */
|
||||
export interface PurposeLevel {
|
||||
/** 层级中文名(目的 / 行为体 / 预期行为 / 驱动因素 / 叙事…)。 */
|
||||
label: string;
|
||||
/** 流程图节点类型(用于取 FLOW_NODE_TYPE_META 配色,与画布节点一致)。 */
|
||||
type: FlowNodeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按**深度**抽出目的树的有序层级(每层取该深度第一个非空层级名作代表)。
|
||||
*
|
||||
* 目的树每一深度的层级是一致的(深度 0 = 目的、1 = 行为体、… 直到叶子叙事),
|
||||
* 故逐层 BFS、每层取一个代表标签即可得到「任务策划共几级、分别是什么」,
|
||||
* 供消息列表上方的「流程示例图」按层级画彩色小图标。
|
||||
*/
|
||||
export function derivePurposeLevels(tree: PurposeTreeNode[]): PurposeLevel[] {
|
||||
const levels: PurposeLevel[] = [];
|
||||
let frontier = tree;
|
||||
let depth = 0;
|
||||
while (frontier.length > 0 && depth < 12) {
|
||||
const label = frontier.find((n) => n.label?.trim())?.label?.trim() || `层级 ${depth + 1}`;
|
||||
levels.push({ label, type: PURPOSE_LABEL_TO_FLOW_TYPE[label] ?? "task" });
|
||||
frontier = frontier.flatMap((n) => n.children ?? []);
|
||||
depth += 1;
|
||||
}
|
||||
return levels;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把归一化后的目的树转换成 Step 3 流程图组件(FlowGraphPanel)认识的 FlowGraphData。
|
||||
*
|
||||
* - 逐层深度优先遍历,给每个节点分配稳定 id(`p1/p2/…`,按遍历顺序);
|
||||
* - 父→子建一条边(from=父 id);多个根目的各自成一棵子树(画布支持多根);
|
||||
* - 节点主文本(`text`)作 label,层级名(`label`)作 group(画布顶部小标签),
|
||||
* 推荐理由并进 detail(点节点弹窗里看)。
|
||||
* 容错沿用 PurposeTreeNode(normalizeNode 已兜底字段/层级变动),这里不再二次判断。
|
||||
*/
|
||||
export function purposeTreeToFlowGraph(
|
||||
tree: PurposeTreeNode[],
|
||||
taskId?: string,
|
||||
): FlowGraphData {
|
||||
const nodes: FlowGraphNode[] = [];
|
||||
const edges: FlowGraphEdge[] = [];
|
||||
let counter = 0;
|
||||
const walk = (node: PurposeTreeNode, parentId: string | null) => {
|
||||
const id = `p${++counter}`;
|
||||
const type = PURPOSE_LABEL_TO_FLOW_TYPE[node.label] ?? "task";
|
||||
const text = node.text?.trim() || node.label || "(空)";
|
||||
const detail = node.reason?.trim()
|
||||
? `${text}\n理由:${node.reason.trim()}`
|
||||
: node.text?.trim() || undefined;
|
||||
nodes.push({ id, label: text, type, group: node.label || undefined, detail });
|
||||
if (parentId) edges.push({ from: parentId, to: id });
|
||||
node.children?.forEach((child) => walk(child, id));
|
||||
};
|
||||
tree.forEach((root) => walk(root, null));
|
||||
return {
|
||||
title: taskId ? `任务目的(任务 ${taskId})` : "任务目的",
|
||||
nodes,
|
||||
edges,
|
||||
};
|
||||
}
|
||||
|
||||
/** 从接口/假数据的整包 body 里取出 data 数组并归一化成树。 */
|
||||
function bodyToTree(body: unknown): PurposeTreeNode[] {
|
||||
const data =
|
||||
|
||||
Reference in new issue
Block a user