first commit

This commit is contained in:
2026-03-11 16:28:04 +08:00
commit c0f9ddabbf
101 changed files with 11601 additions and 0 deletions

View File

@@ -0,0 +1,329 @@
import React, { useState } from 'react';
import { useParams } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import {
Card,
Button,
Space,
Typography,
Row,
Col,
Statistic,
Table,
Tag,
Divider,
Descriptions,
Select,
Checkbox,
message,
Steps,
Result,
} from 'antd';
import {
FileTextOutlined,
FileExcelOutlined,
FilePdfOutlined,
FileWordOutlined,
DownloadOutlined,
PrinterOutlined,
HistoryOutlined,
CheckCircleOutlined,
} from '@ant-design/icons';
import type { ColumnsType } from 'antd/es/table';
import { fetchCase, fetchAssessments, fetchReports, generateReport, getReportDownloadUrl } from '../../services/api';
const Reports: React.FC = () => {
const { id = '1' } = useParams();
const qc = useQueryClient();
const [generated, setGenerated] = useState(false);
const { data: currentCase } = useQuery({ queryKey: ['case', id], queryFn: () => fetchCase(id) });
const { data: assessData } = useQuery({ queryKey: ['assessments', id], queryFn: () => fetchAssessments(id) });
const { data: reportsData } = useQuery({ queryKey: ['reports', id], queryFn: () => fetchReports(id) });
const allAssessments = assessData?.items ?? [];
const reportsList = reportsData?.items ?? [];
const confirmedAssessments = allAssessments.filter(
(a) => a.reviewStatus === 'confirmed' && a.assessedAmount > 0,
);
const totalConfirmed = confirmedAssessments.reduce(
(s, a) => s + a.assessedAmount,
0,
);
const genMutation = useMutation({
mutationFn: (reportType: string) => generateReport(id, { report_type: reportType }),
onSuccess: () => {
setGenerated(true);
qc.invalidateQueries({ queryKey: ['reports', id] });
message.success('报告生成成功');
},
});
if (!currentCase) return null;
const historyColumns: ColumnsType<(typeof mockReports)[0]> = [
{
title: '类型',
dataIndex: 'type',
width: 100,
render: (t: string) => {
const map: Record<string, { icon: React.ReactNode; label: string; color: string }> = {
pdf: { icon: <FilePdfOutlined />, label: 'PDF', color: 'red' },
excel: { icon: <FileExcelOutlined />, label: 'Excel', color: 'green' },
word: { icon: <FileWordOutlined />, label: 'Word', color: 'blue' },
};
const cfg = map[t] || map.pdf;
return <Tag icon={cfg.icon} color={cfg.color}>{cfg.label}</Tag>;
},
},
{
title: '版本',
dataIndex: 'version',
width: 80,
render: (v: number) => `v${v}`,
},
{
title: '生成时间',
dataIndex: 'createdAt',
width: 180,
},
{
title: '操作',
width: 120,
render: () => (
<Space>
<Button type="link" size="small" icon={<DownloadOutlined />}>
</Button>
</Space>
),
},
];
return (
<div>
<Row gutter={16} style={{ marginBottom: 24 }}>
<Col span={8}>
<Card variant="borderless">
<Statistic
title="已确认被骗金额"
value={totalConfirmed}
precision={2}
prefix="¥"
valueStyle={{ color: '#cf1322', fontSize: 24 }}
/>
</Card>
</Col>
<Col span={8}>
<Card variant="borderless">
<Statistic
title="已确认交易笔数"
value={confirmedAssessments.length}
suffix="笔"
prefix={<CheckCircleOutlined />}
valueStyle={{ color: '#52c41a' }}
/>
</Card>
</Col>
<Col span={8}>
<Card variant="borderless">
<Statistic
title="历史报告"
value={reportsList.length}
suffix="份"
prefix={<HistoryOutlined />}
/>
</Card>
</Col>
</Row>
<Row gutter={24}>
<Col span={14}>
<Card
title={
<Space>
<FileTextOutlined />
<span></span>
</Space>
}
style={{ marginBottom: 24 }}
>
<Descriptions column={2} size="small" style={{ marginBottom: 24 }}>
<Descriptions.Item label="案件编号">
{currentCase.caseNo}
</Descriptions.Item>
<Descriptions.Item label="案件名称">
{currentCase.title}
</Descriptions.Item>
<Descriptions.Item label="受害人">
{currentCase.victimName}
</Descriptions.Item>
<Descriptions.Item label="承办人">
{currentCase.handler}
</Descriptions.Item>
<Descriptions.Item label="已确认金额">
<Typography.Text strong style={{ color: '#cf1322' }}>
¥{totalConfirmed.toLocaleString('zh-CN', { minimumFractionDigits: 2 })}
</Typography.Text>
</Descriptions.Item>
<Descriptions.Item label="确认笔数">
{confirmedAssessments.length}
</Descriptions.Item>
</Descriptions>
<Divider />
<Typography.Text strong></Typography.Text>
<div style={{ margin: '12px 0 24px' }}>
<Space size={16}>
<Card
hoverable
style={{ width: 140, textAlign: 'center' }}
styles={{ body: { padding: 16 } }}
>
<FileExcelOutlined style={{ fontSize: 32, color: '#52c41a' }} />
<div style={{ marginTop: 8 }}>
<Typography.Text>Excel </Typography.Text>
</div>
<div>
<Checkbox defaultChecked></Checkbox>
</div>
</Card>
<Card
hoverable
style={{ width: 140, textAlign: 'center' }}
styles={{ body: { padding: 16 } }}
>
<FilePdfOutlined style={{ fontSize: 32, color: '#cf1322' }} />
<div style={{ marginTop: 8 }}>
<Typography.Text>PDF </Typography.Text>
</div>
<div>
<Checkbox defaultChecked></Checkbox>
</div>
</Card>
<Card
hoverable
style={{ width: 140, textAlign: 'center' }}
styles={{ body: { padding: 16 } }}
>
<FileWordOutlined style={{ fontSize: 32, color: '#1677ff' }} />
<div style={{ marginTop: 8 }}>
<Typography.Text>Word </Typography.Text>
</div>
<div>
<Checkbox></Checkbox>
</div>
</Card>
</Space>
</div>
<Typography.Text strong></Typography.Text>
<div style={{ margin: '12px 0 24px' }}>
<Space direction="vertical">
<Checkbox defaultChecked></Checkbox>
<Checkbox defaultChecked></Checkbox>
<Checkbox defaultChecked></Checkbox>
<Checkbox defaultChecked></Checkbox>
<Checkbox defaultChecked></Checkbox>
<Checkbox></Checkbox>
<Checkbox></Checkbox>
</Space>
</div>
{!generated ? (
<Button
type="primary"
size="large"
icon={<FileTextOutlined />}
loading={genMutation.isPending}
onClick={() => genMutation.mutate('excel')}
block
>
{genMutation.isPending ? '正在生成报告...' : '生成报告'}
</Button>
) : (
<Result
status="success"
title="报告已生成"
subTitle="您可以下载或打印以下报告文件"
extra={[
<Button
key="excel"
icon={<DownloadOutlined />}
onClick={() => message.info('演示模式:下载 Excel')}
>
Excel
</Button>,
<Button
key="pdf"
type="primary"
icon={<DownloadOutlined />}
onClick={() => message.info('演示模式:下载 PDF')}
>
PDF
</Button>,
<Button
key="print"
icon={<PrinterOutlined />}
onClick={() => message.info('演示模式:打印')}
>
</Button>,
]}
/>
)}
</Card>
</Col>
<Col span={10}>
<Card
title={
<Space>
<HistoryOutlined />
<span></span>
</Space>
}
style={{ marginBottom: 24 }}
>
<Table
rowKey="id"
columns={historyColumns}
dataSource={reportsList}
pagination={false}
size="small"
/>
</Card>
<Card title="报告预览" style={{ minHeight: 300 }}>
<div
style={{
background: '#fafafa',
border: '1px dashed #d9d9d9',
borderRadius: 8,
padding: 24,
textAlign: 'center',
minHeight: 240,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}
>
<FileTextOutlined style={{ fontSize: 48, color: '#bfbfbf' }} />
<Typography.Text type="secondary" style={{ marginTop: 12 }}>
{generated
? '点击左侧"下载"查看完整报告'
: '生成报告后可在此预览'}
</Typography.Text>
</div>
</Card>
</Col>
</Row>
</div>
);
};
export default Reports;