From 8b6c957a30f0aa8b981b6977300886b502c9c6e3 Mon Sep 17 00:00:00 2001 From: WangDL Date: Sun, 24 May 2026 18:11:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20M4-07=20=E2=80=94=20Hermes=20agent=20ta?= =?UTF-8?q?sks=20+=20artifacts=20tabs=20with=20approval=20buttons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace iframe-only HermesSettings with 3-tab page - Agent tasks table with approve/reject buttons - Agent artifacts table with type labels - Keep Hermes panel iframe tab Co-Authored-By: Claude Opus 4.7 --- src/pages/HermesSettings.tsx | 86 +++++++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 6 deletions(-) diff --git a/src/pages/HermesSettings.tsx b/src/pages/HermesSettings.tsx index 20d9989..ec8a923 100644 --- a/src/pages/HermesSettings.tsx +++ b/src/pages/HermesSettings.tsx @@ -1,11 +1,85 @@ +import { Tabs, Table, Tag, Button, Space, Typography } from 'antd' +import { CheckOutlined, CloseOutlined, RobotOutlined } from '@ant-design/icons' +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' +import { api } from '@/services/http-client' +import { message } from 'antd' + +const { Title } = Typography + +const statusColors: Record = { pending: 'blue', approved: 'green', rejected: 'red', completed: 'green' } +const artifactTypeLabels: Record = { issue_draft: 'Issue 草稿', milestone_summary: '里程碑总结', release_notes: '发布说明', decision_record: '决策记录' } + export default function HermesSettings() { + const qc = useQueryClient() + + const { data: tasks, isLoading: tLoading } = useQuery({ + queryKey: ['hermes', 'tasks'], + queryFn: () => api.get('/admin-api/hermes/tasks').then(d => d ?? []), + }) + + const { data: artifacts, isLoading: aLoading } = useQuery({ + queryKey: ['hermes', 'artifacts'], + queryFn: () => api.get('/admin-api/hermes/artifacts').then(d => d ?? []), + }) + + const approveTask = useMutation({ + mutationFn: (id: string) => api.post(`/admin-api/hermes/tasks/${id}/approve`), + onSuccess: () => { message.success('已通过'); qc.invalidateQueries({ queryKey: ['hermes', 'tasks'] }) }, + }) + + const rejectTask = useMutation({ + mutationFn: (id: string) => api.post(`/admin-api/hermes/tasks/${id}/reject`), + onSuccess: () => { message.success('已驳回'); qc.invalidateQueries({ queryKey: ['hermes', 'tasks'] }) }, + }) + + const taskColumns = [ + { title: 'ID', dataIndex: 'id', width: 80, ellipsis: true }, + { title: '类型', dataIndex: 'type', width: 90 }, + { title: '输入', dataIndex: 'input', width: 180, ellipsis: true, render: (i: string) => i ? i.slice(0, 100) : '-' }, + { title: '输出', dataIndex: 'output', width: 200, ellipsis: true, render: (o: string) => o ? o.slice(0, 120) : '-' }, + { title: '状态', dataIndex: 'status', width: 70, render: (s: string) => {s} }, + { title: '创建时间', dataIndex: 'createdAt', width: 110, render: (d: string) => new Date(d).toLocaleString() }, + { + title: '操作', width: 120, + render: (_: any, r: any) => r.status === 'pending' ? ( + + + + + ) : null, + }, + ] + + const artifactColumns = [ + { title: 'ID', dataIndex: 'id', width: 80, ellipsis: true }, + { title: '类型', dataIndex: 'type', width: 100, render: (t: string) => artifactTypeLabels[t] || t }, + { title: '标题', dataIndex: 'title', width: 200, ellipsis: true }, + { title: '内容', dataIndex: 'content', width: 250, ellipsis: true, render: (c: string) => c ? c.slice(0, 150) : '-' }, + { title: '状态', dataIndex: 'status', width: 70, render: (s: string) => {s} }, + { title: '创建时间', dataIndex: 'createdAt', width: 110, render: (d: string) => new Date(d).toLocaleString() }, + ] + return ( -
-