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
This commit is contained in:
2025-11-16 15:43:39 +08:00
parent 11325f88a7
commit 0fe6821a89
52 changed files with 7324 additions and 109 deletions

View File

@@ -0,0 +1,192 @@
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;