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,178 @@
/**
* Tool B API 服务层
* 病历结构化机器人 API调用
*/
const API_BASE = '/api/v1/dc/tool-b';
export interface HealthCheckRequest {
fileKey: string;
columnName: string;
}
export interface HealthCheckResponse {
status: 'good' | 'bad';
emptyRate: number;
avgLength: number;
totalRows: number;
estimatedTokens: number;
message: string;
}
export interface Template {
diseaseType: string;
reportType: string;
displayName: string;
fields: Array<{
name: string;
desc: string;
width?: string;
}>;
}
export interface CreateTaskRequest {
projectName: string;
fileKey: string;
textColumn: string;
diseaseType: string;
reportType: string;
targetFields: Array<{
name: string;
desc: string;
}>;
}
export interface CreateTaskResponse {
taskId: string;
}
export interface TaskProgress {
taskId: string;
status: 'pending' | 'processing' | 'completed' | 'failed';
progress: number;
totalRows: number;
processedRows: number;
conflictCount?: number;
estimatedTime?: string;
logs: string[];
}
export interface ExtractionItem {
id: string;
rowIndex: number;
originalText: string;
status: 'clean' | 'conflict';
extractedData: Record<string, {
modelA: string;
modelB: string;
chosen: string | null;
}>;
}
/**
* 健康检查API
*/
export async function healthCheck(request: HealthCheckRequest): Promise<HealthCheckResponse> {
const response = await fetch(`${API_BASE}/health-check`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
if (!response.ok) {
throw new Error(`Health check failed: ${response.statusText}`);
}
return await response.json();
}
/**
* 获取模板列表
*/
export async function getTemplates(): Promise<{ templates: Template[] }> {
const response = await fetch(`${API_BASE}/templates`);
if (!response.ok) {
throw new Error(`Get templates failed: ${response.statusText}`);
}
return await response.json();
}
/**
* 创建提取任务
*/
export async function createTask(request: CreateTaskRequest): Promise<CreateTaskResponse> {
const response = await fetch(`${API_BASE}/tasks`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
if (!response.ok) {
throw new Error(`Create task failed: ${response.statusText}`);
}
return await response.json();
}
/**
* 查询任务进度
*/
export async function getTaskProgress(taskId: string): Promise<TaskProgress> {
const response = await fetch(`${API_BASE}/tasks/${taskId}/progress`);
if (!response.ok) {
throw new Error(`Get task progress failed: ${response.statusText}`);
}
return await response.json();
}
/**
* 获取验证网格数据
*/
export async function getTaskItems(taskId: string): Promise<{ items: ExtractionItem[] }> {
const response = await fetch(`${API_BASE}/tasks/${taskId}/items`);
if (!response.ok) {
throw new Error(`Get task items failed: ${response.statusText}`);
}
return await response.json();
}
/**
* 裁决冲突
*/
export async function resolveConflict(
itemId: string,
fieldName: string,
value: string
): Promise<{ success: boolean }> {
const response = await fetch(`${API_BASE}/items/${itemId}/resolve`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ fieldName, chosenValue: value }),
});
if (!response.ok) {
throw new Error(`Resolve conflict failed: ${response.statusText}`);
}
return await response.json();
}
/**
* 导出结果
*/
export async function exportResults(taskId: string): Promise<Blob> {
const response = await fetch(`${API_BASE}/tasks/${taskId}/export`);
if (!response.ok) {
throw new Error(`Export results failed: ${response.statusText}`);
}
return await response.blob();
}