update: mock mode
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import {
|
||||
App,
|
||||
Card,
|
||||
Steps,
|
||||
Row,
|
||||
@@ -15,8 +16,6 @@ import {
|
||||
Descriptions,
|
||||
Progress,
|
||||
Alert,
|
||||
Divider,
|
||||
message,
|
||||
} from 'antd';
|
||||
import {
|
||||
CloudUploadOutlined,
|
||||
@@ -27,20 +26,81 @@ import {
|
||||
FileTextOutlined,
|
||||
InboxOutlined,
|
||||
RightOutlined,
|
||||
PlayCircleOutlined,
|
||||
ThunderboltOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { fetchCase, fetchImages, fetchTransactions, fetchAssessments, uploadImages } from '../../services/api';
|
||||
import { fetchCase, fetchImages, fetchTransactions, fetchAssessments, uploadImages, startCaseOcr, triggerAnalysis } from '../../services/api';
|
||||
import type { EvidenceImage } from '../../types';
|
||||
|
||||
const { Dragger } = Upload;
|
||||
|
||||
const Workspace: React.FC = () => {
|
||||
const { id = '1' } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const [currentStep, setCurrentStep] = useState(3);
|
||||
const queryClient = useQueryClient();
|
||||
const { message } = App.useApp();
|
||||
const [uploadingCount, setUploadingCount] = useState(0);
|
||||
|
||||
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 ocrMutation = useMutation({
|
||||
mutationFn: () => startCaseOcr(id, false),
|
||||
onMutate: () => {
|
||||
message.open({
|
||||
key: 'workspace-ocr',
|
||||
type: 'loading',
|
||||
content: '正在提交 OCR 任务...',
|
||||
duration: 0,
|
||||
});
|
||||
},
|
||||
onSuccess: (res) => {
|
||||
message.open({
|
||||
key: 'workspace-ocr',
|
||||
type: 'success',
|
||||
content: res.message,
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: ['images', id] });
|
||||
},
|
||||
onError: () => {
|
||||
message.open({
|
||||
key: 'workspace-ocr',
|
||||
type: 'error',
|
||||
content: 'OCR任务提交失败',
|
||||
});
|
||||
},
|
||||
});
|
||||
const analysisMutation = useMutation({
|
||||
mutationFn: () => triggerAnalysis(id),
|
||||
onMutate: () => {
|
||||
message.open({
|
||||
key: 'workspace-analysis',
|
||||
type: 'loading',
|
||||
content: '正在提交案件分析任务...',
|
||||
duration: 0,
|
||||
});
|
||||
},
|
||||
onSuccess: (res) => {
|
||||
message.open({
|
||||
key: 'workspace-analysis',
|
||||
type: 'success',
|
||||
content: res.message || '分析任务已提交',
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: ['assessments', id] });
|
||||
queryClient.invalidateQueries({ queryKey: ['suggestions', id] });
|
||||
queryClient.invalidateQueries({ queryKey: ['transactions', id] });
|
||||
queryClient.invalidateQueries({ queryKey: ['flows', id] });
|
||||
queryClient.invalidateQueries({ queryKey: ['case', id] });
|
||||
},
|
||||
onError: () => {
|
||||
message.open({
|
||||
key: 'workspace-analysis',
|
||||
type: 'error',
|
||||
content: '案件分析提交失败',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const images = imagesData ?? [];
|
||||
const txList = txData?.items ?? [];
|
||||
@@ -49,6 +109,16 @@ const Workspace: React.FC = () => {
|
||||
const highConfirm = assessments.filter((a) => a.confidenceLevel === 'high').length;
|
||||
const pendingReview = assessments.filter((a) => a.reviewStatus === 'pending').length;
|
||||
|
||||
const currentStep = useMemo(() => {
|
||||
if (images.length === 0) return 0;
|
||||
const doneCount = images.filter((i: EvidenceImage) => i.ocrStatus === 'done').length;
|
||||
if (doneCount < images.length) return 1;
|
||||
if (txList.length === 0) return 2;
|
||||
if (assessments.length === 0) return 3;
|
||||
if (pendingReview > 0) return 4;
|
||||
return 5;
|
||||
}, [images, txList.length, assessments.length, pendingReview]);
|
||||
|
||||
if (!currentCase) return null;
|
||||
|
||||
const steps = [
|
||||
@@ -60,7 +130,7 @@ const Workspace: React.FC = () => {
|
||||
{
|
||||
title: 'OCR识别',
|
||||
icon: <ScanOutlined />,
|
||||
description: `${images.filter((i: any) => i.ocrStatus === 'done').length}/${images.length} 已完成`,
|
||||
description: `${images.filter((i: EvidenceImage) => i.ocrStatus === 'done').length}/${images.length} 已完成`,
|
||||
},
|
||||
{
|
||||
title: '交易归并',
|
||||
@@ -70,7 +140,7 @@ const Workspace: React.FC = () => {
|
||||
{
|
||||
title: '资金分析',
|
||||
icon: <ApartmentOutlined />,
|
||||
description: '已生成路径图',
|
||||
description: assessments.length > 0 ? `已完成,${assessments.length} 笔认定` : '待分析',
|
||||
},
|
||||
{
|
||||
title: '认定复核',
|
||||
@@ -117,7 +187,6 @@ const Workspace: React.FC = () => {
|
||||
<Steps
|
||||
current={currentStep}
|
||||
items={steps}
|
||||
onChange={(v) => setCurrentStep(v)}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
@@ -136,10 +205,41 @@ const Workspace: React.FC = () => {
|
||||
multiple
|
||||
accept="image/*"
|
||||
showUploadList={false}
|
||||
beforeUpload={(file, fileList) => {
|
||||
uploadImages(id, fileList as unknown as File[])
|
||||
.then(() => message.success('截图上传成功'))
|
||||
.catch(() => message.error('上传失败'));
|
||||
beforeUpload={(file) => {
|
||||
setUploadingCount((c) => c + 1);
|
||||
message.open({
|
||||
key: 'img-upload',
|
||||
type: 'loading',
|
||||
content: `正在上传截图(队列中 ${uploadingCount + 1} 张)...`,
|
||||
duration: 0,
|
||||
});
|
||||
uploadImages(id, [file as File])
|
||||
.then(() => {
|
||||
message.success('截图上传成功');
|
||||
})
|
||||
.then(() => {
|
||||
queryClient.invalidateQueries({ queryKey: ['images', id] });
|
||||
queryClient.invalidateQueries({ queryKey: ['case', id] });
|
||||
})
|
||||
.catch(() => {
|
||||
message.error('上传失败');
|
||||
})
|
||||
.finally(() => {
|
||||
setUploadingCount((c) => {
|
||||
const next = Math.max(0, c - 1);
|
||||
if (next === 0) {
|
||||
message.destroy('img-upload');
|
||||
} else {
|
||||
message.open({
|
||||
key: 'img-upload',
|
||||
type: 'loading',
|
||||
content: `正在上传截图(队列中 ${next} 张)...`,
|
||||
duration: 0,
|
||||
});
|
||||
}
|
||||
return next;
|
||||
});
|
||||
});
|
||||
return false;
|
||||
}}
|
||||
style={{ padding: '20px 0' }}
|
||||
@@ -154,6 +254,11 @@ const Workspace: React.FC = () => {
|
||||
支持微信、支付宝、银行APP、数字钱包等多种来源截图
|
||||
</p>
|
||||
</Dragger>
|
||||
{uploadingCount > 0 && (
|
||||
<Typography.Text type="secondary">
|
||||
当前有 {uploadingCount} 张截图正在上传,请稍候...
|
||||
</Typography.Text>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card title="处理进度">
|
||||
@@ -163,7 +268,7 @@ const Workspace: React.FC = () => {
|
||||
<Progress
|
||||
type="circle"
|
||||
percent={Math.round(
|
||||
images.length ? (images.filter((i: any) => i.ocrStatus === 'done').length /
|
||||
images.length ? (images.filter((i: EvidenceImage) => i.ocrStatus === 'done').length /
|
||||
images.length) *
|
||||
100 : 0,
|
||||
)}
|
||||
@@ -243,29 +348,28 @@ const Workspace: React.FC = () => {
|
||||
<Space direction="vertical" style={{ width: '100%' }}>
|
||||
<Button
|
||||
block
|
||||
onClick={() => navigate('/cases/1/screenshots')}
|
||||
icon={<RightOutlined />}
|
||||
type="primary"
|
||||
ghost
|
||||
loading={ocrMutation.isPending}
|
||||
onClick={() => ocrMutation.mutate()}
|
||||
icon={<PlayCircleOutlined />}
|
||||
>
|
||||
查看截图与 OCR 结果
|
||||
</Button>
|
||||
<Button
|
||||
block
|
||||
onClick={() => navigate('/cases/1/transactions')}
|
||||
icon={<RightOutlined />}
|
||||
>
|
||||
查看交易归并
|
||||
</Button>
|
||||
<Button
|
||||
block
|
||||
onClick={() => navigate('/cases/1/analysis')}
|
||||
icon={<RightOutlined />}
|
||||
>
|
||||
查看资金分析
|
||||
开始 OCR 识别
|
||||
</Button>
|
||||
<Button
|
||||
block
|
||||
type="primary"
|
||||
onClick={() => navigate('/cases/1/review')}
|
||||
ghost
|
||||
loading={analysisMutation.isPending}
|
||||
onClick={() => analysisMutation.mutate()}
|
||||
icon={<ThunderboltOutlined />}
|
||||
>
|
||||
{analysisMutation.isPending ? '分析中...' : '执行案件分析'}
|
||||
</Button>
|
||||
<Button
|
||||
block
|
||||
type="primary"
|
||||
onClick={() => navigate(`/cases/${id}/review`)}
|
||||
icon={<RightOutlined />}
|
||||
>
|
||||
进入认定复核
|
||||
@@ -284,7 +388,7 @@ const Workspace: React.FC = () => {
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
onClick={() => navigate('/cases/1/review')}
|
||||
onClick={() => navigate(`/cases/${id}/review`)}
|
||||
>
|
||||
立即复核
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user