修改评查详情

This commit is contained in:
2025-04-16 18:47:22 +08:00
parent 9a9ce5fa55
commit 947d61f5bc
18 changed files with 381 additions and 305 deletions
+49 -18
View File
@@ -44,6 +44,7 @@ interface FileDetailsProps {
export function FileDetails({ fileInfo, contractInfo, reviewInfo }: FileDetailsProps) {
// 情况状态对应的标签
const controlContractShow = false
const renderResultBadge = (result: string) => {
switch (result) {
case 'success':
@@ -75,11 +76,39 @@ export function FileDetails({ fileInfo, contractInfo, reviewInfo }: FileDetailsP
// 渲染信息区块
const renderInfoSection = (title: string, icon: string, color: string, children: ReactNode) => {
// 根据color参数返回对应的具体类名
const getBgClass = (colorName: string) => {
switch(colorName) {
case 'blue': return 'bg-blue-50';
case 'green': return 'bg-green-50';
case 'purple': return 'bg-purple-50';
default: return 'bg-gray-50';
}
};
const getTextClass = (colorName: string) => {
switch(colorName) {
case 'blue': return 'text-blue-500';
case 'green': return 'text-green-500';
case 'purple': return 'text-purple-500';
default: return 'text-gray-500';
}
};
const getTextTitleClass = (colorName: string) => {
switch(colorName) {
case 'blue': return 'text-blue-700';
case 'green': return 'text-green-700';
case 'purple': return 'text-purple-700';
default: return 'text-gray-700';
}
};
return (
<div className="info-section">
<div className={`info-header bg-${color}-50`}>
<i className={`${icon} text-${color}-500 text-lg mr-2`}></i>
<h3 className={`font-medium text-${color}-700`}>{title}</h3>
<div className={`info-header ${getBgClass(color)}`}>
<i className={`${icon} ${getTextClass(color)} text-lg mr-2`}></i>
<h3 className={`font-medium ${getTextTitleClass(color)}`}>{title}</h3>
</div>
<div className="info-content">
{children}
@@ -114,20 +143,22 @@ export function FileDetails({ fileInfo, contractInfo, reviewInfo }: FileDetailsP
))}
{/* 合同信息 */}
{renderInfoSection('合同信息', 'ri-file-paper-2-line', 'green', (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{renderInfoRow('合同类型', contractInfo.contractType)}
{renderInfoRow('签约日期', contractInfo.signDate)}
{renderInfoRow('合同当事人', (
<div>
<div>{contractInfo.parties.partyA}</div>
<div>{contractInfo.parties.partyB}</div>
</div>
))}
{renderInfoRow('合同金额', contractInfo.amount)}
{renderInfoRow('履行期限', contractInfo.period)}
</div>
))}
{ controlContractShow && (
renderInfoSection('合同信息', 'ri-file-paper-2-line', 'green', (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{renderInfoRow('合同类型', contractInfo.contractType)}
{renderInfoRow('签约日期', contractInfo.signDate)}
{renderInfoRow('合同当事人', (
<div>
<div>{contractInfo.parties.partyA}</div>
<div>{contractInfo.parties.partyB}</div>
</div>
))}
{renderInfoRow('合同金额', contractInfo.amount)}
{renderInfoRow('履行期限', contractInfo.period)}
</div>
))
)}
{/* 评查信息 */}
{renderInfoSection('评查信息', 'ri-search-eye-line', 'purple', (
@@ -139,7 +170,7 @@ export function FileDetails({ fileInfo, contractInfo, reviewInfo }: FileDetailsP
{renderInfoRow('评查结果', (
<div className="flex items-center">
{renderResultBadge(reviewInfo.result)}
<span className="text-sm ml-2">{reviewInfo.issueCount}</span>
{reviewInfo.issueCount > 0 && <span className="text-sm ml-2">{reviewInfo.issueCount}</span>}
</div>
))}
</div>
+9 -9
View File
@@ -26,17 +26,17 @@ export function FileInfo({ fileInfo, onConfirmResults }: FileInfoProps) {
<div className="mb-4 file-info-header">
<div className="flex justify-between items-center">
<div>
<h2 className="text-xl font-medium">
<h2 className="text-xl font-medium max-w-xl">
{fileInfo.fileName}
<span className="text-sm text-secondary font-normal ml-2">
{fileInfo.contractNumber}
</span>
{fileInfo.fileSize && (
<span className="text-xs text-gray-500 ml-2">
{fileInfo.fileSize} | {fileInfo.fileFormat} | {fileInfo.pageCount}
</span>
)}
</h2>
<span className="text-xs text-gray-500">
{fileInfo.contractNumber}
</span>
{fileInfo.fileSize && (
<span className="text-xs text-gray-500 ml-2">
{fileInfo.fileSize} | {fileInfo.fileFormat} | {fileInfo.pageCount}
</span>
)}
{fileInfo.uploadTime && (
<div className="text-xs text-gray-500 mt-1">
{fileInfo.uploadTime} | {fileInfo.uploadUser}
+2 -12
View File
@@ -4,18 +4,8 @@
*/
import { useState, useEffect, useRef } from 'react';
// 定义评查点类型
interface ReviewPoint {
id: string;
title: string;
status: string;
content: string;
suggestion: string;
position?: {
section: string;
index: number;
};
}
// 导入统一的ReviewPoint类型
import { type ReviewPoint } from './';
// 定义文档内容类型
interface FileContent {
+124 -63
View File
@@ -39,6 +39,12 @@ export interface ReviewPoint {
index: number;
};
result?: boolean;
legalBasis?: {
name?: string;
content?: string;
articles?: Array<string | { name?: string; content?: string; [key: string]: unknown }>;
[key: string]: unknown;
};
}
// 统计数据类型
@@ -70,6 +76,7 @@ export function ReviewPointsList({
const [userInputText, setUserInputText] = useState(''); // 用户输入的审核意见文本
const [searchText, setSearchText] = useState(''); // 搜索文本
const [statusFilter, setStatusFilter] = useState<string | null>(null); // 状态过滤
// eslint-disable-next-line no-unused-vars
const [suggestionTexts, setSuggestionTexts] = useState<Record<string, string>>({}); // 存储每个评查点的建议文本
// 初始化建议文本
@@ -83,6 +90,7 @@ export function ReviewPointsList({
}, [reviewPoints]);
// 处理建议文本变更
// eslint-disable-next-line no-unused-vars
const handleSuggestionChange = (reviewPointId: string, text: string) => {
setSuggestionTexts(prev => ({
...prev,
@@ -135,7 +143,7 @@ export function ReviewPointsList({
alert(`将为评查点 ${reviewPointId} 执行一键替换操作`);
// 更新评查点状态为成功
onStatusChange(reviewPointId, 'success');
// onStatusChange(reviewPointId, 'success');
};
/**
@@ -206,9 +214,17 @@ export function ReviewPointsList({
<div className="flex justify-between items-center">
{/* 总计数量 */}
<div className="flex items-center">
<div className="w-7 h-7 bg-gray-100 rounded-md flex items-center justify-center">
<button
className={`w-7 h-7 bg-gray-100 rounded-md flex items-center justify-center cursor-pointer ${statusFilter === null && searchText === '' ? 'ring-2 ring-gray-400' : ''}`}
onClick={() => {
setStatusFilter(null);
setSearchText('');
}}
aria-label="显示所有评查点"
type="button"
>
<span className="text-sm font-semibold text-gray-600">{totalToShow}</span>
</div>
</button>
<span className="text-xs text-gray-500 ml-1"></span>
</div>
<div className="h-8 border-r border-gray-200"></div>
@@ -422,75 +438,120 @@ export function ReviewPointsList({
return (
<div className="mt-2">
{/* 内容显示区域 */}
<div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3">
{/* 移除顶部的"当前值"标题,在每个内容项中显示 */}
{typeof reviewPoint.content === 'object' && reviewPoint.content !== null ? (
// 当 content 是对象时的渲染方式
<div>
{Object.entries(reviewPoint.content).map(([key, value], index) => (
<div key={index} className="mb-2 pb-2 border-b border-gray-100 last:border-b-0 last:mb-0 last:pb-0">
{/* 使用flex布局使key和状态标签左右对齐 */}
{reviewPoint.content !== null && (
(typeof reviewPoint.content === 'string' && reviewPoint.content !== '') ||
(typeof reviewPoint.content === 'object' && Object.keys(reviewPoint.content).length > 0)
) && (
<>
{/* 内容显示区域 */}
<div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3">
{/* 移除顶部的"当前值"标题,在每个内容项中显示 */}
{typeof reviewPoint.content === 'object' && reviewPoint.content !== null ? (
// 当 content 是对象时的渲染方式
<div>
{Object.entries(reviewPoint.content).map(([key, value], index) => (
<div key={index} className="mb-2 pb-2 border-b border-gray-100 last:border-b-0 last:mb-0 last:pb-0">
{/* 使用flex布局使key和状态标签左右对齐 */}
<div className="flex justify-between items-center mb-1">
<span className="text-xs">{key}</span>
{/* <span className={`text-xs ${isErrorStatus ? 'text-error' : 'text-warning'}`}>
{isErrorStatus ? '不符合规范' : '需优化'}
</span> */}
<span className={`text-xs ${isErrorStatus ? 'text-error' : 'text-warning'}`}>
{value ? '' : '缺失'}
</span>
</div>
<p className="text-xs text-left">{value || ' '}</p>
</div>
))}
</div>
) : (
// 当 content 是字符串时的渲染方式
<>
{/* 为字符串内容也添加标题和状态 */}
<div className="flex justify-between items-center mb-1">
<span className="text-xs">{key}</span>
<span className="text-xs"></span>
<span className={`text-xs ${isErrorStatus ? 'text-error' : 'text-warning'}`}>
{isErrorStatus ? '不符合规范' : '需优化'}
</span>
</div>
<p className="text-xs text-left">{value || '(内容为空)'}</p>
</div>
))}
<p className="text-xs text-left">{reviewPoint.content || '(内容为空)'}</p>
</>
)}
</div>
) : (
// 当 content 是字符串时的渲染方式
<>
{/* 为字符串内容也添加标题和状态 */}
<div className="flex justify-between items-center mb-1">
<span className="text-xs"></span>
<span className={`text-xs ${isErrorStatus ? 'text-error' : 'text-warning'}`}>
{isErrorStatus ? '不符合规范' : '需优化'}
</span>
{/* 法律依据内容 */}
{reviewPoint.legalBasis && (typeof reviewPoint.legalBasis === 'object') && (
(reviewPoint.legalBasis.name || reviewPoint.legalBasis.content ||
(reviewPoint.legalBasis.articles && Array.isArray(reviewPoint.legalBasis.articles) && reviewPoint.legalBasis.articles.length > 0)) && (
<div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3">
<div className="flex justify-between items-center mb-1">
<span className="text-xs font-medium"></span>
</div>
{reviewPoint.legalBasis.name && (
<p className="text-xs text-left mb-1">{reviewPoint.legalBasis.name}</p>
)}
{reviewPoint.legalBasis.content && (
<p className="text-xs text-left mb-1"><span className="font-medium"></span>{reviewPoint.legalBasis.content}</p>
)}
{reviewPoint.legalBasis.articles && Array.isArray(reviewPoint.legalBasis.articles) && reviewPoint.legalBasis.articles.length > 0 && (
<div>
<p className="text-xs text-left font-medium mb-1"></p>
<ul className="list-disc pl-4">
{reviewPoint.legalBasis.articles.map((item, index) => (
<li key={index} className="text-xs text-left">
{typeof item === 'string' ? item :
typeof item === 'object' && item !== null ?
(item.name ? `${item.name}: ${item.content || ''}` :
item.content || JSON.stringify(item)) :
String(item)}
</li>
))}
</ul>
</div>
)}
</div>
)
)}
{/* 建议修改区域 */}
<div className="mb-2">
<div className="flex justify-between items-center mb-2">
<span className="text-gray-700"></span>
{/* <span className="text-green-500">符合规范</span> */}
</div>
<p className="text-xs text-left">{reviewPoint.content || '(内容为空)'}</p>
</>
)}
</div>
<textarea
value={suggestionTexts[reviewPoint.id] || ''}
onChange={(e) => handleSuggestionChange(reviewPoint.id, e.target.value)}
className="w-full p-2 border rounded bg-white min-h-[100px] focus:outline-none focus:border-[#00684a] focus:shadow-[0_0_0_2px_rgba(0,104,74,0.2)]"
/>
</div>
{/* 建议修改区域 */}
<div className="mb-2">
<div className="flex justify-between items-center mb-2">
<span className="text-gray-700"></span>
<span className="text-green-500"></span>
</div>
<textarea
value={suggestionTexts[reviewPoint.id] || ''}
onChange={(e) => handleSuggestionChange(reviewPoint.id, e.target.value)}
className="w-full p-2 border rounded bg-gray-50 min-h-[100px] focus:outline-none focus:border-[#00684a] focus:shadow-[0_0_0_2px_rgba(0,104,74,0.2)]"
/>
</div>
{/* 操作按钮区域 */}
<div className="flex space-x-2 mt-2">
{/* 一键替换按钮 - 只有非人工审核的点或未通过的人工审核点才显示 */}
{(!reviewPoint.needsHumanReview || (!reviewPoint.result && reviewPoint.status !== 'success')) && (
<button
className="replace-action flex-1 justify-center"
onClick={() => handleReplace(reviewPoint.id)}
>
<i className="ri-replace-line"></i>
</button>
)}
{/* 人工审核按钮 */}
{reviewPoint.needsHumanReview && !reviewPoint.result && reviewPoint.status !== 'success' && (
<button
className="replace-action flex-1 justify-center human-review-request"
onClick={() => handleEditReviewPoint(reviewPoint.id)}
>
<i className="ri-edit-line"></i>
</button>
)}
</div>
</>
)}
{/* 操作按钮区域 */}
<div className="flex space-x-2 mt-2">
{/* 一键替换按钮 - 只有非人工审核的点或未通过的人工审核点才显示 */}
{(!reviewPoint.needsHumanReview || (!reviewPoint.result && reviewPoint.status !== 'success')) && (
<button
className="replace-action flex-1 justify-center"
onClick={() => handleReplace(reviewPoint.id)}
>
<i className="ri-replace-line"></i>
</button>
)}
{/* 人工审核按钮 */}
{reviewPoint.needsHumanReview && !reviewPoint.result && reviewPoint.status !== 'success' && (
<button
className="replace-action flex-1 justify-center human-review-request"
onClick={() => handleEditReviewPoint(reviewPoint.id)}
>
<i className="ri-edit-line"></i>
</button>
)}
</div>
</div>
);
}