first commit
This commit is contained in:
300
frontend/src/pages/workspace/Workspace.tsx
Normal file
300
frontend/src/pages/workspace/Workspace.tsx
Normal file
@@ -0,0 +1,300 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import {
|
||||
Card,
|
||||
Steps,
|
||||
Row,
|
||||
Col,
|
||||
Statistic,
|
||||
Typography,
|
||||
Upload,
|
||||
Button,
|
||||
Space,
|
||||
Tag,
|
||||
Descriptions,
|
||||
Progress,
|
||||
Alert,
|
||||
Divider,
|
||||
message,
|
||||
} from 'antd';
|
||||
import {
|
||||
CloudUploadOutlined,
|
||||
ScanOutlined,
|
||||
MergeCellsOutlined,
|
||||
ApartmentOutlined,
|
||||
AuditOutlined,
|
||||
FileTextOutlined,
|
||||
InboxOutlined,
|
||||
RightOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { fetchCase, fetchImages, fetchTransactions, fetchAssessments, uploadImages } from '../../services/api';
|
||||
|
||||
const { Dragger } = Upload;
|
||||
|
||||
const Workspace: React.FC = () => {
|
||||
const { id = '1' } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const [currentStep, setCurrentStep] = useState(3);
|
||||
|
||||
const { data: currentCase } = useQuery({ queryKey: ['case', id], queryFn: () => fetchCase(id) });
|
||||
const { data: imagesData } = useQuery({ queryKey: ['images', id], queryFn: () => fetchImages(id) });
|
||||
const { data: txData } = useQuery({ queryKey: ['transactions', id], queryFn: () => fetchTransactions(id) });
|
||||
const { data: assessData } = useQuery({ queryKey: ['assessments', id], queryFn: () => fetchAssessments(id) });
|
||||
|
||||
const images = imagesData ?? [];
|
||||
const txList = txData?.items ?? [];
|
||||
const assessments = assessData?.items ?? [];
|
||||
|
||||
const highConfirm = assessments.filter((a) => a.confidenceLevel === 'high').length;
|
||||
const pendingReview = assessments.filter((a) => a.reviewStatus === 'pending').length;
|
||||
|
||||
if (!currentCase) return null;
|
||||
|
||||
const steps = [
|
||||
{
|
||||
title: '上传截图',
|
||||
icon: <CloudUploadOutlined />,
|
||||
description: `${images.length} 张已上传`,
|
||||
},
|
||||
{
|
||||
title: 'OCR识别',
|
||||
icon: <ScanOutlined />,
|
||||
description: `${images.filter((i: any) => i.ocrStatus === 'done').length}/${images.length} 已完成`,
|
||||
},
|
||||
{
|
||||
title: '交易归并',
|
||||
icon: <MergeCellsOutlined />,
|
||||
description: `${txList.length} 笔交易`,
|
||||
},
|
||||
{
|
||||
title: '资金分析',
|
||||
icon: <ApartmentOutlined />,
|
||||
description: '已生成路径图',
|
||||
},
|
||||
{
|
||||
title: '认定复核',
|
||||
icon: <AuditOutlined />,
|
||||
description: `${pendingReview} 笔待复核`,
|
||||
},
|
||||
{
|
||||
title: '报告导出',
|
||||
icon: <FileTextOutlined />,
|
||||
description: '待生成',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card style={{ marginBottom: 24 }}>
|
||||
<Row justify="space-between" align="middle">
|
||||
<Col>
|
||||
<Space direction="vertical" size={4}>
|
||||
<Space>
|
||||
<Typography.Title level={4} style={{ margin: 0 }}>
|
||||
{currentCase.title}
|
||||
</Typography.Title>
|
||||
<Tag color="orange">待复核</Tag>
|
||||
</Space>
|
||||
<Typography.Text type="secondary">
|
||||
{currentCase.caseNo} · 承办人:{currentCase.handler} · 受害人:{currentCase.victimName}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
</Col>
|
||||
<Col>
|
||||
<Statistic
|
||||
title="当前识别被骗金额"
|
||||
value={currentCase.totalAmount}
|
||||
precision={2}
|
||||
prefix="¥"
|
||||
valueStyle={{ color: '#cf1322', fontSize: 28 }}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
<Card style={{ marginBottom: 24 }}>
|
||||
<Steps
|
||||
current={currentStep}
|
||||
items={steps}
|
||||
onChange={(v) => setCurrentStep(v)}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Row gutter={24}>
|
||||
<Col span={16}>
|
||||
<Card
|
||||
title="快速上传截图"
|
||||
style={{ marginBottom: 24 }}
|
||||
extra={
|
||||
<Typography.Text type="secondary">
|
||||
支持 JPG/PNG,可批量拖拽
|
||||
</Typography.Text>
|
||||
}
|
||||
>
|
||||
<Dragger
|
||||
multiple
|
||||
accept="image/*"
|
||||
showUploadList={false}
|
||||
beforeUpload={(file, fileList) => {
|
||||
uploadImages(id, fileList as unknown as File[])
|
||||
.then(() => message.success('截图上传成功'))
|
||||
.catch(() => message.error('上传失败'));
|
||||
return false;
|
||||
}}
|
||||
style={{ padding: '20px 0' }}
|
||||
>
|
||||
<p className="ant-upload-drag-icon">
|
||||
<InboxOutlined style={{ fontSize: 48, color: '#1677ff' }} />
|
||||
</p>
|
||||
<p className="ant-upload-text">
|
||||
点击或拖拽手机账单截图到此区域
|
||||
</p>
|
||||
<p className="ant-upload-hint">
|
||||
支持微信、支付宝、银行APP、数字钱包等多种来源截图
|
||||
</p>
|
||||
</Dragger>
|
||||
</Card>
|
||||
|
||||
<Card title="处理进度">
|
||||
<Row gutter={[24, 16]}>
|
||||
<Col span={8}>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<Progress
|
||||
type="circle"
|
||||
percent={Math.round(
|
||||
images.length ? (images.filter((i: any) => i.ocrStatus === 'done').length /
|
||||
images.length) *
|
||||
100 : 0,
|
||||
)}
|
||||
size={80}
|
||||
/>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<Typography.Text>OCR 识别率</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<Progress
|
||||
type="circle"
|
||||
percent={Math.round(
|
||||
txList.length ? (txList.filter((t) => !t.isDuplicate).length /
|
||||
txList.length) *
|
||||
100 : 0,
|
||||
)}
|
||||
size={80}
|
||||
strokeColor="#52c41a"
|
||||
/>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<Typography.Text>去重有效率</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<Progress
|
||||
type="circle"
|
||||
percent={Math.round(
|
||||
assessments.length ? (highConfirm / assessments.length) * 100 : 0,
|
||||
)}
|
||||
size={80}
|
||||
strokeColor="#fa8c16"
|
||||
/>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<Typography.Text>高置信占比</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col span={8}>
|
||||
<Card title="案件概况" style={{ marginBottom: 24 }}>
|
||||
<Descriptions column={1} size="small">
|
||||
<Descriptions.Item label="截图总数">
|
||||
{images.length} 张
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="涉及APP">
|
||||
<Space>
|
||||
<Tag color="green">微信</Tag>
|
||||
<Tag color="blue">支付宝</Tag>
|
||||
<Tag color="purple">银行</Tag>
|
||||
<Tag>数字钱包</Tag>
|
||||
</Space>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="提取交易数">
|
||||
{txList.length} 笔
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="去重后交易">
|
||||
{txList.filter((t) => !t.isDuplicate).length} 笔
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="涉诈对手方">2 个</Descriptions.Item>
|
||||
<Descriptions.Item label="待复核">
|
||||
<Typography.Text type="warning">
|
||||
{pendingReview} 笔
|
||||
</Typography.Text>
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Card>
|
||||
|
||||
<Card title="快捷操作">
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<Button
|
||||
block
|
||||
onClick={() => navigate('/cases/1/screenshots')}
|
||||
icon={<RightOutlined />}
|
||||
>
|
||||
查看截图与 OCR 结果
|
||||
</Button>
|
||||
<Button
|
||||
block
|
||||
onClick={() => navigate('/cases/1/transactions')}
|
||||
icon={<RightOutlined />}
|
||||
>
|
||||
查看交易归并
|
||||
</Button>
|
||||
<Button
|
||||
block
|
||||
onClick={() => navigate('/cases/1/analysis')}
|
||||
icon={<RightOutlined />}
|
||||
>
|
||||
查看资金分析
|
||||
</Button>
|
||||
<Button
|
||||
block
|
||||
type="primary"
|
||||
onClick={() => navigate('/cases/1/review')}
|
||||
icon={<RightOutlined />}
|
||||
>
|
||||
进入认定复核
|
||||
</Button>
|
||||
</Space>
|
||||
</Card>
|
||||
|
||||
{pendingReview > 0 && (
|
||||
<Alert
|
||||
message={`有 ${pendingReview} 笔交易待人工确认`}
|
||||
description="系统已完成自动分析,请进入认定复核页面审阅并确认结果。"
|
||||
type="warning"
|
||||
showIcon
|
||||
style={{ marginTop: 24 }}
|
||||
action={
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
onClick={() => navigate('/cases/1/review')}
|
||||
>
|
||||
立即复核
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Workspace;
|
||||
Reference in New Issue
Block a user