# 实时执行视图参考(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
暂无运行结果
自由跳转自 {{ v.fromNodeId }}
{{
trimLeadingBlankLines(v.streamingThinking) }}
{{ formatTimestamp(log.timestamp) }}
{{ logTypeLabel(log.type) }}
{{ log.message }}
{{ log.detail }}
{{ log.message }}
{{ log.detail }}
失败
{{ 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 }})
```
## 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)