feat(rvw): Complete Phase 4-5 - Bug fixes and Word export

Summary:
- Fix methodology score display issue in task list (show score instead of 'warn')
- Add methodology_score field to database schema
- Fix report display when only methodology agent is selected
- Implement Word document export using docx library
- Update documentation to v3.0/v3.1

Backend changes:
- Add methodologyScore to Prisma schema and TaskSummary type
- Update reviewWorker to save methodologyScore
- Update getTaskList to return methodologyScore

Frontend changes:
- Install docx and file-saver libraries
- Implement handleExportReport with Word generation
- Fix activeTab auto-selection based on available data
- Add proper imports for docx components

Documentation:
- Update RVW module status to 90% (Phase 1-5 complete)
- Update system status document to v3.0

Tested: All review workflows verified, Word export functional
This commit is contained in:
2026-01-10 22:52:15 +08:00
parent 179afa2c6b
commit 440f75255e
237 changed files with 3942 additions and 657 deletions

View File

@@ -1,7 +1,7 @@
/**
* 任务表格组件
*/
import { FileText, FileType2, Loader2, Play, Eye } from 'lucide-react';
import { FileText, FileType2, Loader2, Play, Eye, RefreshCw, Trash2 } from 'lucide-react';
import type { ReviewTask } from '../types';
import { formatFileSize, formatTime } from '../api';
@@ -11,6 +11,7 @@ interface TaskTableProps {
onSelectChange: (ids: string[]) => void;
onViewReport: (task: ReviewTask) => void;
onRunTask: (task: ReviewTask) => void;
onDeleteTask: (task: ReviewTask) => void;
}
export default function TaskTable({
@@ -18,7 +19,8 @@ export default function TaskTable({
selectedIds,
onSelectChange,
onViewReport,
onRunTask
onRunTask,
onDeleteTask
}: TaskTableProps) {
const allSelected = tasks.length > 0 && selectedIds.length === tasks.length;
@@ -127,36 +129,89 @@ export default function TaskTable({
// 渲染操作按钮
const renderActions = (task: ReviewTask) => {
if (task.status === 'completed') {
// 待审稿:[开始审稿] [删除]
if (task.status === 'pending') {
return (
<div className="flex items-center justify-end gap-2">
<button
onClick={() => onRunTask(task)}
className="bg-indigo-600 hover:bg-indigo-700 text-white px-3 py-1.5 rounded-md transition-colors text-xs font-medium flex items-center gap-1"
>
<Play className="w-3 h-3" />
稿
</button>
<button
onClick={() => onDeleteTask(task)}
className="text-slate-400 hover:text-red-500 hover:bg-red-50 p-1.5 rounded-md transition-colors"
title="删除"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
);
}
// 处理中:[查看进度]
if (['extracting', 'reviewing', 'reviewing_editorial', 'reviewing_methodology'].includes(task.status)) {
return (
<button
onClick={() => onViewReport(task)}
className="text-indigo-600 font-bold hover:bg-indigo-50 px-3 py-1.5 rounded-md transition-colors text-xs flex items-center gap-1"
className="text-indigo-600 hover:bg-indigo-50 px-3 py-1.5 rounded-md transition-colors text-xs font-medium flex items-center gap-1"
>
<Eye className="w-3 h-3" />
</button>
);
}
if (task.status === 'pending') {
return (
<button
onClick={() => onRunTask(task)}
className="border border-indigo-200 text-indigo-600 hover:bg-indigo-50 px-3 py-1.5 rounded-md transition-colors text-xs font-medium flex items-center gap-1"
>
<Play className="w-3 h-3" />
</button>
);
}
if (task.status === 'extracting' || task.status === 'reviewing') {
return (
<span className="text-xs text-slate-400 flex items-center gap-1">
<Loader2 className="w-3 h-3 animate-spin" />
</span>
</button>
);
}
// 已完成:[查看报告] [重新审稿] [删除]
if (task.status === 'completed') {
return (
<div className="flex items-center justify-end gap-2">
<button
onClick={() => onViewReport(task)}
className="text-indigo-600 font-bold hover:bg-indigo-50 px-3 py-1.5 rounded-md transition-colors text-xs flex items-center gap-1"
>
<Eye className="w-3 h-3" />
</button>
<button
onClick={() => onRunTask(task)}
className="text-slate-500 hover:text-indigo-600 hover:bg-indigo-50 p-1.5 rounded-md transition-colors"
title="重新审稿"
>
<RefreshCw className="w-4 h-4" />
</button>
<button
onClick={() => onDeleteTask(task)}
className="text-slate-400 hover:text-red-500 hover:bg-red-50 p-1.5 rounded-md transition-colors"
title="删除"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
);
}
// 失败:[重新审稿] [删除]
if (task.status === 'failed') {
return (
<div className="flex items-center justify-end gap-2">
<button
onClick={() => onRunTask(task)}
className="border border-indigo-200 text-indigo-600 hover:bg-indigo-50 px-3 py-1.5 rounded-md transition-colors text-xs font-medium flex items-center gap-1"
>
<RefreshCw className="w-3 h-3" />
稿
</button>
<button
onClick={() => onDeleteTask(task)}
className="text-slate-400 hover:text-red-500 hover:bg-red-50 p-1.5 rounded-md transition-colors"
title="删除"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
);
}
@@ -250,3 +305,5 @@ export default function TaskTable({
);
}