update: mock mode
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import {
|
||||
@@ -13,10 +13,8 @@ import {
|
||||
Tag,
|
||||
Divider,
|
||||
Descriptions,
|
||||
Select,
|
||||
Checkbox,
|
||||
message,
|
||||
Steps,
|
||||
Result,
|
||||
} from 'antd';
|
||||
import {
|
||||
@@ -32,10 +30,64 @@ import {
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { fetchCase, fetchAssessments, fetchReports, generateReport, getReportDownloadUrl } from '../../services/api';
|
||||
|
||||
type ContentKeys =
|
||||
| 'include_summary'
|
||||
| 'include_transactions'
|
||||
| 'include_flow_chart'
|
||||
| 'include_timeline'
|
||||
| 'include_reasons'
|
||||
| 'include_inquiry'
|
||||
| 'include_screenshots';
|
||||
|
||||
const contentOptions: Array<{ key: ContentKeys; label: string; defaultOn: boolean }> = [
|
||||
{ key: 'include_summary', label: '被骗金额汇总表', defaultOn: true },
|
||||
{ key: 'include_transactions', label: '交易明细清单(含证据索引)', defaultOn: true },
|
||||
{ key: 'include_flow_chart', label: '资金流转路径图', defaultOn: true },
|
||||
{ key: 'include_timeline', label: '交易时间轴', defaultOn: true },
|
||||
{ key: 'include_reasons', label: '认定理由与排除说明', defaultOn: true },
|
||||
{ key: 'include_inquiry', label: '笔录辅助问询建议', defaultOn: false },
|
||||
{ key: 'include_screenshots', label: '原始截图附件', defaultOn: false },
|
||||
];
|
||||
|
||||
const STORAGE_PREFIX = 'report-content-';
|
||||
|
||||
const loadContentSelection = (caseId: string): Record<ContentKeys, boolean> => {
|
||||
try {
|
||||
const raw = localStorage.getItem(`${STORAGE_PREFIX}${caseId}`);
|
||||
if (raw) return JSON.parse(raw);
|
||||
} catch { /* ignore */ }
|
||||
const defaults: Record<string, boolean> = {};
|
||||
contentOptions.forEach((o) => { defaults[o.key] = o.defaultOn; });
|
||||
return defaults as Record<ContentKeys, boolean>;
|
||||
};
|
||||
|
||||
const saveContentSelection = (caseId: string, sel: Record<ContentKeys, boolean>) => {
|
||||
try {
|
||||
localStorage.setItem(`${STORAGE_PREFIX}${caseId}`, JSON.stringify(sel));
|
||||
} catch { /* ignore */ }
|
||||
};
|
||||
|
||||
const Reports: React.FC = () => {
|
||||
const { id = '1' } = useParams();
|
||||
const qc = useQueryClient();
|
||||
const [generated, setGenerated] = useState(false);
|
||||
const [selectedFormats, setSelectedFormats] = useState<Array<'excel' | 'pdf' | 'word'>>([
|
||||
'excel',
|
||||
'pdf',
|
||||
]);
|
||||
const [contentSel, setContentSel] = useState<Record<ContentKeys, boolean>>(() => loadContentSelection(id));
|
||||
|
||||
useEffect(() => {
|
||||
setContentSel(loadContentSelection(id));
|
||||
}, [id]);
|
||||
|
||||
const toggleContent = useCallback((key: ContentKeys, checked: boolean) => {
|
||||
setContentSel((prev) => {
|
||||
const next = { ...prev, [key]: checked };
|
||||
saveContentSelection(id, next);
|
||||
return next;
|
||||
});
|
||||
}, [id]);
|
||||
|
||||
const { data: currentCase } = useQuery({ queryKey: ['case', id], queryFn: () => fetchCase(id) });
|
||||
const { data: assessData } = useQuery({ queryKey: ['assessments', id], queryFn: () => fetchAssessments(id) });
|
||||
@@ -53,20 +105,37 @@ const Reports: React.FC = () => {
|
||||
);
|
||||
|
||||
const genMutation = useMutation({
|
||||
mutationFn: (reportType: string) => generateReport(id, { report_type: reportType }),
|
||||
onSuccess: () => {
|
||||
mutationFn: async (reportTypes: Array<'excel' | 'pdf' | 'word'>) => {
|
||||
const result = await Promise.all(
|
||||
reportTypes.map((reportType) =>
|
||||
generateReport(id, { report_type: reportType, ...contentSel }),
|
||||
),
|
||||
);
|
||||
return result;
|
||||
},
|
||||
onSuccess: (_res, vars) => {
|
||||
setGenerated(true);
|
||||
qc.invalidateQueries({ queryKey: ['reports', id] });
|
||||
message.success('报告生成成功');
|
||||
message.success(`报告生成成功:${vars.map((v) => v.toUpperCase()).join(' / ')}`);
|
||||
},
|
||||
onError: () => {
|
||||
message.error('报告生成失败');
|
||||
},
|
||||
});
|
||||
|
||||
if (!currentCase) return null;
|
||||
|
||||
const historyColumns: ColumnsType<(typeof mockReports)[0]> = [
|
||||
const latestReportByType = reportsList.reduce<Record<string, (typeof reportsList)[0]>>((acc, report) => {
|
||||
if (!acc[report.reportType]) {
|
||||
acc[report.reportType] = report;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const historyColumns: ColumnsType<(typeof reportsList)[0]> = [
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
dataIndex: 'reportType',
|
||||
width: 100,
|
||||
render: (t: string) => {
|
||||
const map: Record<string, { icon: React.ReactNode; label: string; color: string }> = {
|
||||
@@ -92,9 +161,15 @@ const Reports: React.FC = () => {
|
||||
{
|
||||
title: '操作',
|
||||
width: 120,
|
||||
render: () => (
|
||||
render: (_, report) => (
|
||||
<Space>
|
||||
<Button type="link" size="small" icon={<DownloadOutlined />}>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
icon={<DownloadOutlined />}
|
||||
href={getReportDownloadUrl(report.id)}
|
||||
target="_blank"
|
||||
>
|
||||
下载
|
||||
</Button>
|
||||
</Space>
|
||||
@@ -188,7 +263,18 @@ const Reports: React.FC = () => {
|
||||
<Typography.Text>Excel 汇总表</Typography.Text>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox defaultChecked>选择</Checkbox>
|
||||
<Checkbox
|
||||
checked={selectedFormats.includes('excel')}
|
||||
onChange={(e) => {
|
||||
setSelectedFormats((prev) =>
|
||||
e.target.checked
|
||||
? Array.from(new Set([...prev, 'excel']))
|
||||
: prev.filter((x) => x !== 'excel'),
|
||||
);
|
||||
}}
|
||||
>
|
||||
选择
|
||||
</Checkbox>
|
||||
</div>
|
||||
</Card>
|
||||
<Card
|
||||
@@ -201,7 +287,18 @@ const Reports: React.FC = () => {
|
||||
<Typography.Text>PDF 报告</Typography.Text>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox defaultChecked>选择</Checkbox>
|
||||
<Checkbox
|
||||
checked={selectedFormats.includes('pdf')}
|
||||
onChange={(e) => {
|
||||
setSelectedFormats((prev) =>
|
||||
e.target.checked
|
||||
? Array.from(new Set([...prev, 'pdf']))
|
||||
: prev.filter((x) => x !== 'pdf'),
|
||||
);
|
||||
}}
|
||||
>
|
||||
选择
|
||||
</Checkbox>
|
||||
</div>
|
||||
</Card>
|
||||
<Card
|
||||
@@ -214,7 +311,18 @@ const Reports: React.FC = () => {
|
||||
<Typography.Text>Word 文书</Typography.Text>
|
||||
</div>
|
||||
<div>
|
||||
<Checkbox>选择</Checkbox>
|
||||
<Checkbox
|
||||
checked={selectedFormats.includes('word')}
|
||||
onChange={(e) => {
|
||||
setSelectedFormats((prev) =>
|
||||
e.target.checked
|
||||
? Array.from(new Set([...prev, 'word']))
|
||||
: prev.filter((x) => x !== 'word'),
|
||||
);
|
||||
}}
|
||||
>
|
||||
选择
|
||||
</Checkbox>
|
||||
</div>
|
||||
</Card>
|
||||
</Space>
|
||||
@@ -223,13 +331,15 @@ const Reports: React.FC = () => {
|
||||
<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>
|
||||
{contentOptions.map((opt) => (
|
||||
<Checkbox
|
||||
key={opt.key}
|
||||
checked={contentSel[opt.key]}
|
||||
onChange={(e) => toggleContent(opt.key, e.target.checked)}
|
||||
>
|
||||
{opt.label}
|
||||
</Checkbox>
|
||||
))}
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
@@ -239,7 +349,13 @@ const Reports: React.FC = () => {
|
||||
size="large"
|
||||
icon={<FileTextOutlined />}
|
||||
loading={genMutation.isPending}
|
||||
onClick={() => genMutation.mutate('excel')}
|
||||
onClick={() => {
|
||||
if (selectedFormats.length === 0) {
|
||||
message.warning('请至少选择一种导出格式');
|
||||
return;
|
||||
}
|
||||
genMutation.mutate(selectedFormats);
|
||||
}}
|
||||
block
|
||||
>
|
||||
{genMutation.isPending ? '正在生成报告...' : '生成报告'}
|
||||
@@ -253,7 +369,9 @@ const Reports: React.FC = () => {
|
||||
<Button
|
||||
key="excel"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => message.info('演示模式:下载 Excel')}
|
||||
href={latestReportByType.excel ? getReportDownloadUrl(latestReportByType.excel.id) : undefined}
|
||||
target="_blank"
|
||||
disabled={!latestReportByType.excel}
|
||||
>
|
||||
下载 Excel
|
||||
</Button>,
|
||||
@@ -261,10 +379,21 @@ const Reports: React.FC = () => {
|
||||
key="pdf"
|
||||
type="primary"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={() => message.info('演示模式:下载 PDF')}
|
||||
href={latestReportByType.pdf ? getReportDownloadUrl(latestReportByType.pdf.id) : undefined}
|
||||
target="_blank"
|
||||
disabled={!latestReportByType.pdf}
|
||||
>
|
||||
下载 PDF
|
||||
</Button>,
|
||||
<Button
|
||||
key="word"
|
||||
icon={<DownloadOutlined />}
|
||||
href={latestReportByType.word ? getReportDownloadUrl(latestReportByType.word.id) : undefined}
|
||||
target="_blank"
|
||||
disabled={!latestReportByType.word}
|
||||
>
|
||||
下载 Word
|
||||
</Button>,
|
||||
<Button
|
||||
key="print"
|
||||
icon={<PrinterOutlined />}
|
||||
|
||||
Reference in New Issue
Block a user