From 741b0b9d0c5598424297cf40954e610c0033d9a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com>
Date: Tue, 16 Jun 2026 22:41:05 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20TaskDetail=E4=BB=BB?=
=?UTF-8?q?=E5=8A=A1=E4=BA=A7=E5=87=BA=E4=B8=8EAI=E8=87=AA=E5=AE=A1?=
=?UTF-8?q?=E7=BB=93=E8=AE=BA=E5=B1=95=E7=A4=BA(output=5Fjson=E8=A7=A3?=
=?UTF-8?q?=E6=9E=90+review=E7=BA=A2=E7=BB=BF=E6=A0=87)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/api/types.ts | 7 +++
src/i18n/en/taskDetail.ts | 7 +++
src/i18n/zh-CN/taskDetail.ts | 7 +++
src/views/TaskDetail.vue | 97 ++++++++++++++++++++++++++++++++++++
4 files changed, 118 insertions(+)
diff --git a/src/api/types.ts b/src/api/types.ts
index c5e1b8d..b95ef26 100644
--- a/src/api/types.ts
+++ b/src/api/types.ts
@@ -93,6 +93,13 @@ export interface TaskRecord {
base_branch: string | null
/** review 退回累计轮数(in_review→in_progress / testing→in_review 各 +1) */
review_rounds: number
+ /**
+ * 任务产出 JSON 字符串(决策 a:task 中心,产出跟 task 走)。
+ * schema: { text: string, model?: string, usage?: {...}, review?: {
+ * verdict: 'pass'|'fail'|'unknown', summary: string, suggestions: string[], ... } }
+ * 旧任务无产出时为 undefined。前端 parsedOutput try/catch 兜底解析。
+ */
+ output_json?: string
created_at: string
updated_at: string
}
diff --git a/src/i18n/en/taskDetail.ts b/src/i18n/en/taskDetail.ts
index ae2fe7d..76b3d2b 100644
--- a/src/i18n/en/taskDetail.ts
+++ b/src/i18n/en/taskDetail.ts
@@ -18,6 +18,13 @@ export default {
assignee: 'Assignee',
baseBranch: 'Base Branch',
workflowDef: 'Workflow Definition',
+ // F-AiNodeSelfReview: task output + AI self-review verdict
+ output: 'Output',
+ review: {
+ pass: 'Review Passed',
+ fail: 'Review Failed',
+ unknown: 'Review Inconclusive',
+ },
reviewRounds: 'Review Round {n}',
createdAt: 'Created At',
updatedAt: 'Updated At',
diff --git a/src/i18n/zh-CN/taskDetail.ts b/src/i18n/zh-CN/taskDetail.ts
index 34994db..2885900 100644
--- a/src/i18n/zh-CN/taskDetail.ts
+++ b/src/i18n/zh-CN/taskDetail.ts
@@ -18,6 +18,13 @@ export default {
assignee: '负责人',
baseBranch: '基础分支',
workflowDef: '工作流定义',
+ // F-AiNodeSelfReview: 任务产出 + AI 自审结论展示
+ output: '产出',
+ review: {
+ pass: '自审通过',
+ fail: '自审未通过',
+ unknown: '自审结论未知',
+ },
reviewRounds: '第 {n} 轮 Review',
createdAt: '创建时间',
updatedAt: '更新时间',
diff --git a/src/views/TaskDetail.vue b/src/views/TaskDetail.vue
index 3099de6..9b15bff 100644
--- a/src/views/TaskDetail.vue
+++ b/src/views/TaskDetail.vue
@@ -125,6 +125,23 @@
{{ $t('taskDetail.workflowDef') }}
{{ task.workflow_def_id ?? '—' }}
+
+
+
+
{{ $t('taskDetail.output') }}
+
+
+
+
{{ reviewVerdictLabel }}
+
{{ parsedOutput.review.summary }}
+
+
+
+
+
+
{{ $t('taskDetail.createdAt') }}
{{ formatDate(task.created_at) }}
@@ -191,6 +208,57 @@ const { rendered: renderedDesc, ensureLoaded } = useRendered(
() => task.value?.description ?? '',
)
+// ============================================================
+// F-AiNodeSelfReview: output_json 解析 + AI 产出 Markdown 渲染
+// ------------------------------------------------------------
+// output_json schema(与 df-nodes ai_node.rs 写产出现对齐):
+// { text, model?, usage?, review?: { verdict: 'pass'|'fail'|'unknown', summary?, suggestions? } }
+// parsedOutput try/catch: JSON.parse 边界必要兜底(LLM 自审输出可能不合规/老数据格式漂移),
+// 解析失败返回 null → v-if 守卫整块不渲染(不裸显报错)。
+interface TaskOutputReview {
+ verdict?: string
+ summary?: string
+ suggestions?: unknown
+}
+interface TaskOutput {
+ text?: string
+ model?: string
+ usage?: unknown
+ review?: TaskOutputReview
+}
+const parsedOutput = computed(() => {
+ const raw = task.value?.output_json
+ if (!raw) return null
+ try {
+ return JSON.parse(raw) as TaskOutput
+ } catch {
+ return null
+ }
+})
+// AI 产出正文渲染(复用模块单例渲染器,与 renderedDesc 共享 marked/DOMPurify/缓存)
+const { rendered: renderedOutput } = useRendered(
+ () => parsedOutput.value?.text ?? '',
+)
+// review.verdict 红绿标:pass→绿(success),fail→红(danger),未知/缺失→灰(dim)
+const reviewVerdictClass = computed(() => {
+ const v = parsedOutput.value?.review?.verdict
+ if (v === 'pass') return 'review-pass'
+ if (v === 'fail') return 'review-fail'
+ return 'review-unknown'
+})
+// review.verdict 标签(pass/fail/unknown 走 i18n,异常值兜底 unknown)
+const reviewVerdictLabel = computed(() => {
+ const v = parsedOutput.value?.review?.verdict
+ if (v === 'pass') return t('taskDetail.review.pass')
+ if (v === 'fail') return t('taskDetail.review.fail')
+ return t('taskDetail.review.unknown')
+})
+// review.suggestions 列表(数组守卫,非数组时返回空避免 v-for 报错)
+const reviewSuggestions = computed(() => {
+ const s = parsedOutput.value?.review?.suggestions
+ return Array.isArray(s) ? s.map(String) : []
+})
+
const projectName = computed(() => {
const p = projects.value.find(p => p.id === task.value?.project_id)
return p?.name ?? task.value?.project_id ?? '—'
@@ -564,6 +632,35 @@ onBeforeUnmount(() => {
/* B-24 漏 white-space 覆盖致 v-html 后 HTML 标签间 \n 被 pre-wrap 渲染为空行间距——移除 pre-wrap,描述由 .ai-md 接管(line-height 1.5 对齐 AiChat li) */
.info-item .value.description { line-height: 1.5; }
+/* ===== F-AiNodeSelfReview: output_json 区块(产出 + 自审结论卡) ===== */
+.output-block .output-body {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+/* 自审结论卡:verdict 红绿标(pass 绿 / fail 红 / unknown 灰),复用全局 --df-success/danger token */
+.review-card {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ padding: 8px 12px;
+ border-radius: var(--df-radius-sm);
+ border: 0.5px solid var(--df-border);
+ font-size: 13px;
+}
+.review-pass { background: var(--df-success-bg); border-color: rgba(61,219,160,0.3); }
+.review-fail { background: var(--df-danger-bg); border-color: rgba(240,101,101,0.3); }
+.review-unknown { background: var(--df-bg-card); }
+.review-verdict { font-weight: 600; }
+.review-pass .review-verdict { color: var(--df-success); }
+.review-fail .review-verdict { color: var(--df-danger); }
+.review-unknown .review-verdict { color: var(--df-text-dim); }
+.review-summary { color: var(--df-text-secondary); line-height: 1.5; }
+.review-suggestions { margin: 4px 0 0; padding-left: 20px; color: var(--df-text-secondary); }
+.review-suggestions li { line-height: 1.6; }
+/* AI 产出正文(继承全局 .ai-md 块级样式,line-height 对齐描述) */
+.output-text { line-height: 1.5; }
+
/* ===== 任务描述 Markdown 渲染(B-24,基础样式收敛至全局 ai-md.css) ===== */
.project-link {