修改评查详情
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -105,4 +105,4 @@ export function Table<T extends Record<string, any>>({
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user