Files
AIclinicalresearch/frontend/src/components/review/EditorialReview.tsx
HaHafeng 0fe6821a89 feat(frontend): add batch processing and review features
- Add batch processing API and mode
- Add deep read mode for full-text analysis
- Add document selector and switcher components
- Add review page with editorial and methodology assessment
- Add capacity indicator and usage info modal
- Add custom hooks for batch tasks and chat modes
- Update layouts and routing
- Add TypeScript types for chat features
2025-11-16 15:43:39 +08:00

193 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Card, Collapse, Space, Typography, Tag, Alert, Empty, List, Divider } from 'antd';
import {
CheckCircleOutlined,
WarningOutlined,
CloseCircleOutlined,
FileTextOutlined,
} from '@ant-design/icons';
import type { EditorialReview as EditorialReviewType, EditorialItem } from '../../api/reviewApi';
import ScoreCard from './ScoreCard';
const { Title, Text, Paragraph } = Typography;
const { Panel } = Collapse;
interface EditorialReviewProps {
data: EditorialReviewType;
}
/**
* 稿约规范性评估详情组件
* 展示11个评估标准的详细结果
*/
const EditorialReview = ({ data }: EditorialReviewProps) => {
if (!data) {
return <Empty description="暂无评估数据" />;
}
// 获取状态图标和颜色
const getStatusDisplay = (status: 'pass' | 'warning' | 'fail') => {
const config = {
pass: {
icon: <CheckCircleOutlined />,
color: 'success' as const,
text: '通过',
},
warning: {
icon: <WarningOutlined />,
color: 'warning' as const,
text: '警告',
},
fail: {
icon: <CloseCircleOutlined />,
color: 'error' as const,
text: '不通过',
},
};
return config[status];
};
// 统计数据
const stats = {
total: data.items.length,
pass: data.items.filter((item) => item.status === 'pass').length,
warning: data.items.filter((item) => item.status === 'warning').length,
fail: data.items.filter((item) => item.status === 'fail').length,
};
return (
<Card title={
<Space>
<FileTextOutlined />
<span>稿</span>
</Space>
}>
<Space direction="vertical" size="large" style={{ width: '100%' }}>
{/* 总体评分 */}
<ScoreCard
title="总体评分"
score={data.overall_score}
description="稿约规范性综合评分"
size="large"
/>
{/* 总结 */}
<Alert
message="评估总结"
description={data.summary}
type="info"
showIcon
/>
{/* 统计信息 */}
<Card size="small">
<Space size="large">
<div>
<Text type="secondary"></Text>
<Text strong style={{ fontSize: 16, marginLeft: 8 }}>{stats.total}</Text>
</div>
<Divider type="vertical" />
<div>
<Text type="success"></Text>
<Text strong style={{ fontSize: 16, marginLeft: 8, color: '#52c41a' }}>{stats.pass}</Text>
</div>
<Divider type="vertical" />
<div>
<Text type="warning"></Text>
<Text strong style={{ fontSize: 16, marginLeft: 8, color: '#faad14' }}>{stats.warning}</Text>
</div>
<Divider type="vertical" />
<div>
<Text type="danger"></Text>
<Text strong style={{ fontSize: 16, marginLeft: 8, color: '#f5222d' }}>{stats.fail}</Text>
</div>
</Space>
</Card>
{/* 详细评估结果 */}
<Card title="详细评估11项标准" size="small">
<Collapse defaultActiveKey={data.items.map((_, index) => index.toString())}>
{data.items.map((item: EditorialItem, index: number) => {
const statusDisplay = getStatusDisplay(item.status);
return (
<Panel
header={
<Space>
<Tag icon={statusDisplay.icon} color={statusDisplay.color}>
{statusDisplay.text}
</Tag>
<Text strong>{item.criterion}</Text>
<Text type="secondary">{item.score}</Text>
</Space>
}
key={index}
>
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
{/* 评分 */}
<div>
<Text strong></Text>
<Text style={{ fontSize: 18, marginLeft: 8, color: '#1890ff' }}>
{item.score} / 100
</Text>
</div>
{/* 问题列表 */}
{item.issues && item.issues.length > 0 && (
<div>
<Text strong style={{ color: '#f5222d' }}></Text>
<List
size="small"
bordered
dataSource={item.issues}
renderItem={(issue: string) => (
<List.Item>
<CloseCircleOutlined style={{ color: '#f5222d', marginRight: 8 }} />
{issue}
</List.Item>
)}
style={{ marginTop: 8 }}
/>
</div>
)}
{/* 改进建议 */}
{item.suggestions && item.suggestions.length > 0 && (
<div>
<Text strong style={{ color: '#52c41a' }}></Text>
<List
size="small"
bordered
dataSource={item.suggestions}
renderItem={(suggestion: string) => (
<List.Item>
<CheckCircleOutlined style={{ color: '#52c41a', marginRight: 8 }} />
{suggestion}
</List.Item>
)}
style={{ marginTop: 8 }}
/>
</div>
)}
{/* 无问题提示 */}
{(!item.issues || item.issues.length === 0) && item.status === 'pass' && (
<Alert
message="该项检查通过,无需修改"
type="success"
showIcon
/>
)}
</Space>
</Panel>
);
})}
</Collapse>
</Card>
</Space>
</Card>
);
};
export default EditorialReview;