feat(intelligent-eval): align detail/report drawer layouts with other modules

详情抽屉改两列布局(左静态上下文/右动态流程),会话进度加 Progress;
报告抽屉顶部加统计概览行与维度得分柱状图,移除总结卡内重复的分数展示。
This commit is contained in:
sinohqb 2026-08-06 01:03:28 +08:00
parent 22cc437e2d
commit 01f451c155
2 changed files with 163 additions and 109 deletions

View File

@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from 'react'
import {
Alert, Button, Card, Descriptions, Empty, Input, Modal, Popconfirm, Space, Spin, Tag, message,
Alert, Button, Card, Col, Descriptions, Empty, Input, Modal, Popconfirm, Progress, Row, Space, Spin, Tag, message,
} from 'antd'
import { FileTextOutlined, StopOutlined } from '@ant-design/icons'
import { intelligentEvalsApi, type IntelligentEval, type IntelligentEvalSession } from '../../api'
@ -125,8 +125,10 @@ export default function EvalDetail({ ev, targetName, onOpenReport, onChanged }:
</Space>
</div>
<Row gutter={16}>
<Col span={9}>
<Card size="small" title="基本信息" style={sectionCard}>
<Descriptions column={3} size="small">
<Descriptions column={1} size="small">
<Descriptions.Item label="评测对象">{targetName}</Descriptions.Item>
<Descriptions.Item label="时间窗口">{ev.time_window_hours} </Descriptions.Item>
<Descriptions.Item label="创建时间">{formatDateTime(ev.created_at)}</Descriptions.Item>
@ -158,7 +160,9 @@ export default function EvalDetail({ ev, targetName, onOpenReport, onChanged }:
</Descriptions.Item>
</Descriptions>
</Card>
</Col>
<Col span={15}>
{ev.status === 'planning' && (
ev.plan_feedback ? (
<Alert
@ -195,7 +199,18 @@ export default function EvalDetail({ ev, targetName, onOpenReport, onChanged }:
)}
{showSessions && (
<Card size="small" title={`会话进度(${ev.completed_sessions}/${ev.session_count}`} style={sectionCard}>
<Card
size="small"
title={`会话进度(${ev.completed_sessions}/${ev.session_count}`}
style={sectionCard}
extra={(
<Progress
percent={ev.session_count ? Math.round((ev.completed_sessions / ev.session_count) * 100) : 0}
size="small"
style={{ width: 140 }}
/>
)}
>
{sessions === null && <Spin size="small" />}
{sessions !== null && sessions.length === 0 && (
<div style={{ fontSize: 13, color: colors.textSecondary }}>
@ -230,6 +245,8 @@ export default function EvalDetail({ ev, targetName, onOpenReport, onChanged }:
)}
</Card>
)}
</Col>
</Row>
<Modal
title="打回计划"

View File

@ -1,8 +1,9 @@
import { useEffect, useState } from 'react'
import {
Button, Card, Collapse, Empty, Space, Spin, Statistic, Tag, message,
Button, Card, Col, Collapse, Empty, Row, Space, Spin, Statistic, Tag, message,
} from 'antd'
import { ArrowLeftOutlined, DownloadOutlined } from '@ant-design/icons'
import { Bar } from '@ant-design/charts'
import ChatBubble from '../ChatBubble'
import {
intelligentEvalsApi,
@ -143,6 +144,23 @@ export default function EvalReport({ ev, onBack }: EvalReportProps) {
const findings = [...(report?.findings ?? [])]
.sort((a, b) => severityOf(a.severity).order - severityOf(b.severity).order)
const scores = Object.entries(report?.scores ?? {})
const overall = scores.length > 0
? (scores.reduce((sum, [, v]) => sum + Number(v), 0) / scores.length).toFixed(1)
: null
const sevCounts = findings.reduce((acc, f) => {
acc[f.severity] = (acc[f.severity] ?? 0) + 1
return acc
}, {} as Record<string, number>)
const scoreData = scores.map(([dimension, score]) => ({ dimension, score: Number(score) }))
const scoreConfig = {
data: scoreData,
xField: 'dimension',
yField: 'score',
coordinate: { transform: [{ type: 'transpose' }] },
height: Math.max(160, scoreData.length * 44),
color: colors.primary,
}
return (
<div style={{ height: '100%', overflowY: 'auto', padding: '0 16px 16px' }}>
@ -167,17 +185,36 @@ export default function EvalReport({ ev, onBack }: EvalReportProps) {
{report && (
<>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col span={6}>
<Card size="small"><Statistic title="综合评分" value={overall ?? '—'} /></Card>
</Col>
<Col span={6}>
<Card size="small">
<Statistic title="问题发现" value={findings.length} />
<div style={{ fontSize: 12, color: colors.textMuted }}>
{sevCounts.high ?? 0} · {sevCounts.medium ?? 0} · {sevCounts.low ?? 0}
</div>
</Card>
</Col>
<Col span={6}>
<Card size="small"><Statistic title="亮点" value={report.highlights.length} /></Card>
</Col>
<Col span={6}>
<Card size="small"><Statistic title="会话数" value={ev.session_count} /></Card>
</Col>
</Row>
{scoreData.length > 0 && (
<Card size="small" title="维度得分" style={sectionCard}>
<Bar {...scoreConfig} />
</Card>
)}
<Card size="small" title="总结" style={sectionCard}>
<div style={{ whiteSpace: 'pre-wrap', fontSize: 13, lineHeight: 1.8 }}>
{report.summary}
</div>
{scores.length > 0 && (
<div style={{ display: 'flex', gap: 32, marginTop: 12, flexWrap: 'wrap' }}>
{scores.map(([dimension, score]) => (
<Statistic key={dimension} title={dimension} value={score} />
))}
</div>
)}
</Card>
<Card size="small" title={`问题发现(${findings.length}`} style={sectionCard}>