feat(dc): Complete Tool B frontend development with UI optimization

- Implement Tool B 5-step workflow (upload, schema, processing, verify, result)
- Add back navigation button to Portal
- Optimize Step 2 field list styling to match prototype
- Fix step 3 label: 'dual-blind' to 'dual-model'
- Create API service layer with 7 endpoints
- Integrate Tool B route into DC module
- Add comprehensive TypeScript types

Components (~1100 lines):
- index.tsx: Main Tool B entry with state management
- Step1Upload.tsx: File upload and health check
- Step2Schema.tsx: Smart template configuration
- Step3Processing.tsx: Dual-model extraction progress
- Step4Verify.tsx: Conflict verification workbench
- Step5Result.tsx: Result display
- StepIndicator.tsx: Step progress component
- api/toolB.ts: API service layer

Status: Frontend complete, ready for API integration
This commit is contained in:
2025-12-03 09:36:35 +08:00
parent 33db2687b9
commit 5f1e7af92c
47 changed files with 2757 additions and 10 deletions

View File

@@ -0,0 +1,167 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import Step1Upload from './Step1Upload';
import Step2Schema from './Step2Schema';
import Step3Processing from './Step3Processing';
import Step4Verify from './Step4Verify';
import Step5Result from './Step5Result';
import StepIndicator from './components/StepIndicator';
import { Bot, Split, ArrowLeft } from 'lucide-react';
export type Step = 'upload' | 'schema' | 'processing' | 'verify' | 'result';
export interface ExtractionField {
id: string;
name: string;
desc: string;
width?: string;
}
export interface ToolBState {
// Step 1
fileName: string;
fileKey: string;
selectedColumn: string;
healthCheckResult: {
status: 'unknown' | 'good' | 'bad';
emptyRate?: number;
avgLength?: number;
totalRows?: number;
estimatedTokens?: number;
message?: string;
};
// Step 2
diseaseType: string;
reportType: string;
fields: ExtractionField[];
// Step 3
taskId?: string;
progress: number;
// Step 4
rows: any[];
// Step 5
resultFileUrl?: string;
}
const ToolBModule: React.FC = () => {
const navigate = useNavigate();
const [currentStep, setCurrentStep] = useState<Step>('upload');
const [state, setState] = useState<ToolBState>({
fileName: '',
fileKey: '',
selectedColumn: '',
healthCheckResult: { status: 'unknown' },
diseaseType: 'lung_cancer',
reportType: 'pathology',
fields: [],
progress: 0,
rows: [],
});
const updateState = (updates: Partial<ToolBState>) => {
setState(prev => ({ ...prev, ...updates }));
};
return (
<div className="min-h-screen bg-slate-50 font-sans text-slate-800 p-6">
<div className="max-w-7xl mx-auto bg-white rounded-2xl shadow-sm border border-slate-200 min-h-[800px] flex flex-col overflow-hidden">
{/* Header */}
<div className="px-8 py-5 border-b border-slate-100 flex justify-between items-center bg-gradient-to-r from-purple-50 via-white to-white">
<div className="flex items-center gap-4">
<button
onClick={() => navigate('/data-cleaning')}
className="flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-white border border-slate-200 hover:border-slate-300 text-slate-600 hover:text-slate-900 transition-all shadow-sm hover:shadow"
title="返回数据清洗工作台"
>
<ArrowLeft className="w-4 h-4" />
<span className="text-sm font-medium"></span>
</button>
<div className="h-8 w-px bg-slate-200"></div>
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center text-purple-600">
<Bot className="w-6 h-6" />
</div>
<div>
<h1 className="text-xl font-bold text-slate-900"></h1>
<div className="flex items-center gap-2 text-xs text-slate-500">
<span className="flex items-center gap-1">
<Split className="w-3 h-3" />
</span>
<span></span>
<span>DeepSeek-V3 & Qwen-Max</span>
</div>
</div>
</div>
</div>
{/* 状态指示器 */}
{currentStep === 'verify' && (
<div className="flex gap-3">
<div className="flex items-center gap-1.5 px-3 py-1 bg-blue-50 text-blue-700 text-xs rounded-full border border-blue-100 font-medium">
<div className="w-2 h-2 rounded-full bg-blue-500"></div> DeepSeek
</div>
<div className="flex items-center gap-1.5 px-3 py-1 bg-orange-50 text-orange-700 text-xs rounded-full border border-orange-100 font-medium">
<div className="w-2 h-2 rounded-full bg-orange-500"></div> Qwen
</div>
</div>
)}
</div>
{/* Step Indicator */}
<div className="pt-6 pb-2">
<StepIndicator currentStep={currentStep} />
</div>
{/* Main Content */}
<div className="flex-1 px-8 pb-8 relative overflow-hidden flex flex-col">
{currentStep === 'upload' && (
<Step1Upload
state={state}
updateState={updateState}
onNext={() => setCurrentStep('schema')}
/>
)}
{currentStep === 'schema' && (
<Step2Schema
state={state}
updateState={updateState}
onNext={() => setCurrentStep('processing')}
onPrev={() => setCurrentStep('upload')}
/>
)}
{currentStep === 'processing' && (
<Step3Processing
state={state}
updateState={updateState}
onComplete={() => setCurrentStep('verify')}
/>
)}
{currentStep === 'verify' && (
<Step4Verify
state={state}
updateState={updateState}
onComplete={() => setCurrentStep('result')}
/>
)}
{currentStep === 'result' && (
<Step5Result
state={state}
/>
)}
</div>
</div>
</div>
);
};
export default ToolBModule;