Files
AIclinicalresearch/frontend/src/pages/rvw/components/EditorialReport.tsx
HaHafeng 2481b786d8 deploy: Complete 0126-27 deployment - database upgrade, services update, code recovery
Major Changes:
- Database: Install pg_bigm/pgvector plugins, create test database
- Python service: v1.0 -> v1.1, add pymupdf4llm/openpyxl/pypandoc
- Node.js backend: v1.3 -> v1.7, fix pino-pretty and ES Module imports
- Frontend: v1.2 -> v1.3, skip TypeScript check for deployment
- Code recovery: Restore empty files from local backup

Technical Fixes:
- Fix pino-pretty error in production (conditional loading)
- Fix ES Module import paths (add .js extensions)
- Fix OSSAdapter TypeScript errors
- Update Prisma Schema (63 models, 16 schemas)
- Update environment variables (DATABASE_URL, EXTRACTION_SERVICE_URL, OSS)
- Remove deprecated variables (REDIS_URL, DIFY_API_URL, DIFY_API_KEY)

Documentation:
- Create 0126 deployment folder with 8 documents
- Update database development standards v2.0
- Update SAE deployment status records

Deployment Status:
- PostgreSQL: ai_clinical_research_test with plugins
- Python: v1.1 @ 172.17.173.84:8000
- Backend: v1.7 @ 172.17.173.89:3001
- Frontend: v1.3 @ 172.17.173.90:80

Tested: All services running successfully on SAE
2026-01-27 08:13:27 +08:00

138 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 规范性评估报告组件
*/
import { AlertTriangle, CheckCircle, XCircle } from 'lucide-react';
import type { EditorialReviewResult } from '../types';
import ScoreRing from './ScoreRing';
interface EditorialReportProps {
data: EditorialReviewResult;
}
export default function EditorialReport({ data }: EditorialReportProps) {
const getStatusIcon = (status: 'pass' | 'warning' | 'fail') => {
switch (status) {
case 'pass':
return <CheckCircle className="w-5 h-5 text-green-500" />;
case 'warning':
return <AlertTriangle className="w-5 h-5 text-amber-500" />;
case 'fail':
return <XCircle className="w-5 h-5 text-red-500" />;
}
};
const getStatusTag = (status: 'pass' | 'warning' | 'fail') => {
switch (status) {
case 'pass':
return <span className="tag tag-green"></span>;
case 'warning':
return <span className="tag tag-amber"></span>;
case 'fail':
return <span className="tag tag-red"></span>;
}
};
const getItemBgClass = (status: 'pass' | 'warning' | 'fail') => {
switch (status) {
case 'pass':
return '';
case 'warning':
return 'bg-amber-50/50';
case 'fail':
return 'bg-red-50/50';
}
};
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">
{data.overall_score >= 80 ? '基本符合稿约规范' :
data.overall_score >= 60 ? '部分符合稿约规范' : '不符合稿约规范'}
</h3>
<p className="text-slate-600 text-sm mt-1">{data.summary}</p>
</div>
</div>
{/* 检测详情 */}
<div 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 font-bold text-sm text-slate-700">
{data.items.length}
</div>
<div className="divide-y divide-gray-100">
{data.items.map((item, index) => (
<div key={index} className={`p-5 ${getItemBgClass(item.status)}`}>
<div className="flex gap-4">
<div className="mt-1">{getStatusIcon(item.status)}</div>
<div className="flex-1">
<div className="flex justify-between items-start">
<h4 className="font-bold text-sm text-slate-800">{item.criterion}</h4>
<div className="flex items-center gap-2">
<span className="text-xs text-slate-500">{item.score}</span>
{getStatusTag(item.status)}
</div>
</div>
{item.issues && item.issues.length > 0 && (
<div className="mt-3 space-y-1">
{item.issues.map((issue, i) => (
<p key={i} className="text-sm text-slate-600"> {issue}</p>
))}
</div>
)}
{item.suggestions && item.suggestions.length > 0 && (
<div className="mt-3 bg-white border border-gray-200 p-3 rounded-lg">
<p className="text-xs font-bold text-slate-500 mb-1"></p>
{item.suggestions.map((suggestion, i) => (
<p key={i} className="text-xs text-slate-600"> {suggestion}</p>
))}
</div>
)}
</div>
</div>
</div>
))}
</div>
</div>
</div>
);
}