Summary: - Migrate backend to modules/rvw with v2 API routes (/api/v2/rvw) - Add new database fields: selectedAgents, editorialScore, methodologyStatus, picoExtract, isArchived - Create frontend module in frontend-v2/src/modules/rvw - Implement Dashboard with task list, filtering, batch operations - Implement ReportDetail with dual tabs (editorial/methodology) - Implement AgentModal for intelligent agent selection - Register RVW module in moduleRegistry.ts - Add navigation entry in TopNavigation - Update documentation for RVW module status (v3.0) - Update system status document (v2.9) Features: - User can select agents: editorial, methodology, or both - Support batch task execution - Task status filtering - Replace console.log with logger service - Maintain v1 API backward compatibility Tested: Frontend and backend verified locally Status: 85% complete (Phase 1-3 done)
114 lines
4.8 KiB
TypeScript
114 lines
4.8 KiB
TypeScript
/**
|
||
* 方法学评估报告组件
|
||
*/
|
||
import { XCircle, AlertTriangle, CheckCircle } from 'lucide-react';
|
||
import type { MethodologyReviewResult } from '../types';
|
||
import ScoreRing from './ScoreRing';
|
||
|
||
interface MethodologyReportProps {
|
||
data: MethodologyReviewResult;
|
||
}
|
||
|
||
export default function MethodologyReport({ data }: MethodologyReportProps) {
|
||
const getSeverityStyle = (severity: 'major' | 'minor') => {
|
||
return severity === 'major'
|
||
? { border: 'border-red-200', bg: 'bg-red-50', icon: <XCircle className="w-4 h-4 text-red-500" />, label: '严重' }
|
||
: { border: 'border-amber-200', bg: 'bg-amber-50', icon: <AlertTriangle className="w-4 h-4 text-amber-500" />, label: '轻微' };
|
||
};
|
||
|
||
const getOverallStatus = () => {
|
||
if (data.overall_score >= 80) return { label: '通过', color: 'text-green-700', bg: 'bg-green-50' };
|
||
if (data.overall_score >= 60) return { label: '存疑', color: 'text-amber-700', bg: 'bg-amber-50' };
|
||
return { label: '不通过', color: 'text-red-700', bg: 'bg-red-50' };
|
||
};
|
||
|
||
const status = getOverallStatus();
|
||
|
||
return (
|
||
<div className="space-y-6 fade-in">
|
||
{/* 总分卡片 */}
|
||
<div className={`bg-white p-6 rounded-2xl shadow-sm border flex items-center gap-8 ${
|
||
data.overall_score >= 80 ? 'border-green-200' :
|
||
data.overall_score >= 60 ? 'border-amber-200' : 'border-red-200'
|
||
}`}>
|
||
<ScoreRing score={data.overall_score} size="medium" />
|
||
<div className="flex-1">
|
||
<h3 className="font-bold text-lg text-slate-800 flex items-center gap-2">
|
||
方法学评估
|
||
<span className={`text-sm px-2 py-0.5 rounded ${status.bg} ${status.color}`}>
|
||
{status.label}
|
||
</span>
|
||
</h3>
|
||
<p className="text-slate-600 text-sm mt-1">{data.summary}</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 分项详情 */}
|
||
{data.parts.map((part, partIndex) => (
|
||
<div key={partIndex} className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
||
<div className="p-4 bg-slate-50 border-b border-gray-200 flex justify-between items-center">
|
||
<span className="font-bold text-sm text-slate-700">{part.part}</span>
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-xs text-slate-500">{part.score}分</span>
|
||
{part.issues.length === 0 ? (
|
||
<span className="tag tag-green flex items-center gap-1">
|
||
<CheckCircle className="w-3 h-3" />
|
||
无问题
|
||
</span>
|
||
) : (
|
||
<span className="tag tag-amber">
|
||
{part.issues.length}个问题
|
||
</span>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{part.issues.length > 0 ? (
|
||
<div className="divide-y divide-gray-100">
|
||
{part.issues.map((issue, issueIndex) => {
|
||
const severity = getSeverityStyle(issue.severity);
|
||
return (
|
||
<div key={issueIndex} className={`p-5 ${severity.bg}`}>
|
||
<div className="flex gap-3">
|
||
<div className="mt-0.5">{severity.icon}</div>
|
||
<div className="flex-1">
|
||
<div className="flex items-center gap-2 mb-1">
|
||
<span className="font-bold text-sm text-slate-800">{issue.type}</span>
|
||
<span className={`text-xs px-1.5 py-0.5 rounded ${
|
||
issue.severity === 'major' ? 'bg-red-100 text-red-700' : 'bg-amber-100 text-amber-700'
|
||
}`}>
|
||
{severity.label}
|
||
</span>
|
||
</div>
|
||
<p className="text-sm text-slate-600">{issue.description}</p>
|
||
{issue.location && (
|
||
<p className="text-xs text-slate-400 mt-1">
|
||
位置:{issue.location}
|
||
</p>
|
||
)}
|
||
{issue.suggestion && (
|
||
<div className="mt-3 bg-white border border-gray-200 p-3 rounded-lg">
|
||
<p className="text-xs text-slate-600">
|
||
<strong>建议:</strong>{issue.suggestion}
|
||
</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
) : (
|
||
<div className="p-6 text-center text-slate-500 text-sm">
|
||
<CheckCircle className="w-8 h-8 text-green-400 mx-auto mb-2" />
|
||
该部分未发现问题
|
||
</div>
|
||
)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
|