From caf871ecadd9a81e0e562a77df437bce5c90fe55 Mon Sep 17 00:00:00 2001 From: huangzhuang_3rd Date: Thu, 13 Aug 2026 00:47:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20V1=20-=20DAG=E7=BC=96=E6=8E=92/?= =?UTF-8?q?=E5=91=8A=E8=AD=A6=E7=B3=BB=E7=BB=9F/Agent=E5=BE=AA=E7=8E=AF/?= =?UTF-8?q?=E7=9F=A5=E8=AF=86=E5=BA=93RAG/Webhook=20+=20=E5=AE=A1=E6=A0=B8?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=AF=8C=E4=B8=8A=E4=B8=8B=E6=96=87=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/Reviews.tsx | 286 ++++++++++++++++++++++++--------- 1 file changed, 214 insertions(+), 72 deletions(-) diff --git a/frontend/src/pages/Reviews.tsx b/frontend/src/pages/Reviews.tsx index 16fa37a..221272f 100644 --- a/frontend/src/pages/Reviews.tsx +++ b/frontend/src/pages/Reviews.tsx @@ -1,13 +1,14 @@ import { useState, useEffect } from 'react'; import { Card, Table, Button, Modal, Tag, Space, message, Input, - Typography, Empty, Badge, Tooltip, + Typography, Empty, Badge, Tooltip, Descriptions, } from 'antd'; import { CheckOutlined, CloseOutlined, EyeOutlined, FileSearchOutlined, } from '@ant-design/icons'; import { reviewApi } from '@/api'; -import type { Review, ReviewDecisionPayload } from '@/types'; +import type { Review, ReviewDecisionPayload, TaskPriority } from '@/types'; +import { PriorityTag } from '@/components/constants'; const { TextArea } = Input; const { Text, Paragraph } = Typography; @@ -26,6 +27,26 @@ const reviewTypeConfig: Record = { quality_check: '质量检查', }; +/** 格式化成本(分 -> 元) */ +const formatCost = (cents?: number) => { + if (cents === undefined || cents === null) return '¥0.00'; + return `¥${(cents / 100).toFixed(2)}`; +}; + +/** 尝试从任务输入数据中解析出 prompt 字段,否则返回原始内容 */ +const parseTaskInput = (raw?: string): string => { + if (!raw) return '-'; + try { + const parsed = JSON.parse(raw); + if (parsed && typeof parsed === 'object' && 'prompt' in parsed) { + return String(parsed.prompt); + } + return JSON.stringify(parsed, null, 2); + } catch { + return raw; + } +}; + export default function Reviews() { const [reviews, setReviews] = useState([]); const [loading, setLoading] = useState(false); @@ -85,71 +106,128 @@ export default function Reviews() { width: 60, }, { - title: '任务', + title: '任务信息', key: 'task', - render: (_: any, record: Review) => ( - - 任务 #{record.task_id} - - 项目 #{record.project_id} + width: 240, + render: (_: unknown, record: Review) => ( + + + {record.task_title || `任务 #${record.task_id}`} + {record.task_description && ( + + + {record.task_description} + + + )} ), }, + { + title: '所属项目', + key: 'project', + width: 140, + render: (_: unknown, record: Review) => ( + + {record.project_name || `项目 #${record.project_id}`} + + ), + }, + { + title: '执行Worker', + key: 'worker', + width: 200, + render: (_: unknown, record: Review) => { + if (!record.worker_name && !record.worker_model_name) { + return -; + } + return ( + + + {record.worker_name || '-'}{' / '}{record.worker_model_name || ''} + + + ); + }, + }, { title: '审核类型', dataIndex: 'review_type', key: 'review_type', + width: 100, render: (type: string) => ( {reviewTypeConfig[type] || type} ), }, + { + title: '优先级', + dataIndex: 'task_priority', + key: 'task_priority', + width: 80, + render: (priority: string) => + priority ? : -, + }, { title: '状态', dataIndex: 'status', key: 'status', + width: 100, render: (status: string) => { const cfg = reviewStatusConfig[status] || { label: status, color: 'default' }; - return ; + return ; }, }, + { + title: '成本', + key: 'cost', + width: 140, + render: (_: unknown, record: Review) => ( + + + {record.token_usage != null ? `${record.token_usage} tokens` : '-'} + + + {formatCost(record.cost_cents)} + + + ), + }, { title: '提交时间', dataIndex: 'submitted_at', key: 'submitted_at', + width: 160, render: (time: string) => ( {time ? new Date(time).toLocaleString('zh-CN') : '-'} ), }, - { - title: '审核人', - key: 'reviewer', - render: (_: any, record: Review) => ( - record.reviewer_id ? ( - - #{record.reviewer_id} - {record.reviewer_role ? ` (${record.reviewer_role})` : ''} - - ) : ( - - - ) - ), - }, { title: '操作', key: 'action', width: 220, - render: (_: any, record: Review) => ( + fixed: 'right' as const, + render: (_: unknown, record: Review) => ( - - {record.status === 'pending' && ( <> } - width={640} + width={800} > {detailModal && ( -
- - {reviewTypeConfig[detailModal.review_type] || detailModal.review_type} - - + + {/* 1. 任务信息 */} + + + + {detailModal.task_title || `任务 #${detailModal.task_id}`} + + {detailModal.task_description && ( + + {detailModal.task_description} + + )} + + {detailModal.task_type || '-'} + + + + {parseTaskInput(detailModal.task_input_data)} + + + + -
- 任务 ID: - {detailModal.task_id} - | 项目 ID: - {detailModal.project_id} -
+ {/* 2. 所属项目 */} + + + + {detailModal.project_name || `项目 #${detailModal.project_id}`} + + {detailModal.project_description && ( + + {detailModal.project_description} + + )} + + -
- 提交时间: - - {detailModal.submitted_at - ? new Date(detailModal.submitted_at).toLocaleString('zh-CN') - : '-'} - -
+ {/* 3. 执行Worker */} + + + + {detailModal.worker_name || '-'} + + + + {detailModal.worker_provider || '-'}{' / '}{detailModal.worker_model_name || '-'} + + + {detailModal.worker_system_prompt && ( + + + {detailModal.worker_system_prompt} + + + )} + + - {detailModal.reviewed_at && ( -
- 审核时间: - {new Date(detailModal.reviewed_at).toLocaleString('zh-CN')} -
- )} - - AI 产出内容: + {/* 4. AI产出内容 */} - {detailModal.review_content} + {detailModal.review_content || '-'} + {/* 5. 执行消耗 */} + + + + {detailModal.token_usage != null ? detailModal.token_usage : '-'} + + + {detailModal.duration_ms != null ? `${detailModal.duration_ms} ms` : '-'} + + + {formatCost(detailModal.cost_cents)} + + + + + {/* 6. 审核意见 */} {detailModal.reviewer_comment && ( -
- 审核意见: - - {detailModal.reviewer_comment} - -
+ + {detailModal.reviewer_comment} + )} -
+
)} @@ -309,3 +448,6 @@ export default function Reviews() { ); } + +/** antd Badge `status` prop accepted values. */ +type BadgeStatus = 'success' | 'processing' | 'default' | 'error' | 'warning';