# 实时执行视图参考(WorkflowEditor 运行结果 Tab 形态) 平台有两种运行展示形态,数据层完全相同(`useWorkflowRunner` + Visit 卡片),模板差异如下: | 形态 | 参考文件 | 特征 | |---|---|---| | 回放态(历史记录页) | `views/workflow/RunHistory.vue`(完整自包含) | 卡片默认折叠、无实时状态面板、无 AskFor 交互 | | **实时态(执行中)** | 本文档(提取自平台 `WorkflowEditor.vue` 运行结果 Tab) | running 强制展开、`.live` 高亮、AskFor 匹配、重连徽章 | 业务方做「发起运行 + 实时观看」的页面时,以 RunHistory.vue 的卡片结构为基础,叠加本文档的差异部分。 ## 1. script 侧:从 useWorkflowRunner 解构 ```js const { // 状态 running, showResult, workflowResult, runMeta, // runMeta: { runId, workingDirFiles } visits, // Visit 卡片数组(visitUuid 为主键) expandedContext, expandedThinking, expandedLogs, // 三组折叠状态(reactive,按 visitUuid 索引) pendingAsk, // 全局唯一 AskFor { nodeId, visitUuid, clarifyId, question, choices } reconnecting, // SSE 重连中标志 // 方法 startRun, submitClarify, toggleContext, toggleThinking, toggleLogs } = useWorkflowRunner({ getNodes, nodeTypeInfo, workflowIdRef }) ``` `getNodes` 说明(v1.2 更新): - 平台内使用时是画布节点列表的 ref(VueFlow `getNodes`),用于运行状态高亮与 nodeType/label 推断。 - **外部集成(无画布)可传 `ref([])`**:v1.2 起 `node_status RUNNING` 事件自身携带 `nodeType` / `nodeLabel`,`ensureVisit` 优先使用事件值建卡片,Visit 类型/名称显示不受影响(此前无画布时运行期间会显示「未知」,需等 `visit_recorded` 到达才修正)。 - `workflowIdRef` 同理:外部 API 场景按需传入运行所属工作流标识(仅用于拼接内部接口,外部端点路径不依赖它)。 另需本地派生与工具(平台同名实现语义): - `sortedVisits`:computed,按 `visit.sortOrder` 升序排列(sortOrder 缺失的排尾部) - `highlightedVisitUuid`:可选,画布/列表联动高亮用;无画布集成可省略 - `statusText(s)`、`visitTypeInfo(nodeType)`(nodeTypeInfo lookup + `UNKNOWN_NODE_TYPE_INFO` 兜底) - `formatTimestamp(ts)`、`formatContextValue(v)`、`logTypeLabel` / `isToolLogType`(`utils/logLabels`) - `trimLeadingBlankLines`(`utils/hermesThinking`)、`renderMarkdown`(`utils/markdown`) - `unwrapNodeOutput` / `nodeOutputStatus` / `nodeOutputMessage` / `parseVisitVariables`(`composables/useWorkflowRunner` 与 `utils/visitChain` 导出) ## 2. 模板主体(平台逐字提取) ```vue
暂无运行结果
#{{ idx + 1 }} {{ v.nodeLabel }} {{ v.typeLabel }}
第 {{ v.iterSeq }} 次 {{ statusText(v.status) }}
自由跳转自 {{ v.fromNodeId }}
思考过程 实时输出中… ({{ v.streamingThinking.length }} 字) ▶
{{
          trimLeadingBlankLines(v.streamingThinking) }}
执行日志 ({{ v.logs.length }}) ▶
{{ formatTimestamp(log.timestamp) }} {{ logTypeLabel(log.type) }}
失败 {{ nodeOutputMessage(v.output) }}
{{ key }} {{ typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value) }}
{{ v.errorMsg }}
▶ 查看完整上下文({{ Object.keys(v.contextSnapshot || parseVisitVariables(v.variablesJson)).length }} 个变量)
{{ key }} {{ formatContextValue(value) }}
运行ID: {{ runMeta.runId }}
工作空间文件列表({{ runMeta.workingDirFiles.length }})
  • {{ f }}
``` ## 3. 实时态专属样式(RunHistory.vue 副本中不存在,需补充) 以下提取自平台 `WorkflowEditor.vue` 样式段。`RunHistory.vue` 覆盖回放态;实时视图需在其基础上补充: ```css /* 流式思考过程区块(RunHistory 已有基础版;.live 为实时态专属) */ .nrc-thinking.live { border-color: rgba(168, 85, 247, 0.5); box-shadow: 0 0 0 1px rgba(168, 85, 247, 0.15); } .nrc-thinking-live { color: #ec4899; font-size: 10px; animation: nrc-pulse 1.2s ease-in-out infinite; } .nrc-thinking-static { color: #888; font-size: 10px; } @keyframes nrc-pulse { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } } ``` `.nrc-logs.live` 同理(加强边框,与 `.nrc-thinking.live` 视觉一致)。 ## 4. 与回放态(RunHistory.vue)的关键行为差异 | 维度 | 回放态 RunHistory | 实时态(本文档) | |---|---|---| | 卡片默认状态 | 全部折叠(`collapsed` 由回放构建置 true) | running 展开;**首次进入终态时自动收起**(`useWorkflowRunner` 在 FINISHED_STATES 分支 delete 折叠 key;重连/重复事件不重复折叠) | | 思考/日志展开条件 | `expandedXxx[visitUuid] === true` | `v.status === 'running' \|\| expandedXxx[visitUuid] === true`(running 强制展开) | | 折叠头右侧提示 | `(N 字)` 静态字数 | running 时「实时输出中…」脉冲动画,否则 `(N 字)` | | 实时状态面板 | 不渲染(`currentStatus` 为空) | ``;节点完成后 runner 清空自动隐藏 | | AskFor | 无交互 | `AgentThinkingPanel` 挂载条件含 `pendingAsk && pendingAsk.visitUuid === v.visitUuid`,并传 `:reconnecting` 与 `@submit-clarify` | | 区块 live 高亮 | 无 | `.nrc-thinking` / `.nrc-logs` 加 `live` 类(running 时) | | 重连提示 | 无 | `reconnecting` 传给 `AgentThinkingPanel` → `RunReconnectBadge` | | 运行元数据行 | 详情页 footer(`outputMeta` 从 run 记录解析) | `nrc-clear-row`:`runMeta.runId` + `workingDirFiles`(SSE `workflow_complete` 推送)+ 下载/清除按钮 | | 工作空间下载 | 内部直链 `window.location.href` | 外部 API 需 `X-API-Key` 头 → fetch + blob 或后端反代直链 | ## 5. 发起运行(平台运行对话框形态,可选参考) 平台在运行前弹出对话框收集输入变量与上传文件(`inputFields` 来自工作流 `userInput` 节点字段定义,外部 API 可从 `GET /api/v1/workflows` 的 `inputs` 获取): - 必填项 `*` 标记 + 文本输入(数值型也用 text) - 「变量导出 / 变量导入」按钮(JSON 文件) - 「上传文件到工作目录」:`选择文件`(`multiple`)与 `选择目录`(`webkitdirectory`,浏览器原生提供 `webkitRelativePath` 保留目录结构,传入 `startRun` 的 `relativePath`) - 确认后调 `startRun(inputs)` → `useWorkflowRunner` 内部走 `api/workflow.js` 的 `runWorkflow`(外部适配版:创建 → 启动 → 订阅 SSE)