/** * 评查点列表组件 * * 功能概述: * - 展示评查结果统计信息(总计、通过、警告、错误数量) * - 提供评查点过滤功能(按状态和搜索文本) * - 显示评查点详细信息(标题、状态、内容、建议修改等) * - 支持评查点操作(一键替换、人工审核等) * * 组件结构: * - 统计区域: 显示评查点数量统计 * - 搜索区域: 提供文本搜索功能 * - 评查点列表: 展示所有评查点 * - 评查点卡片: 展示单个评查点详情 * - 评查点头部: 显示标题和状态 * - 评查点内容: 显示当前内容和问题 * - 建议修改区域: 显示建议的修改内容 * - 操作按钮: 提供一键替换和人工审核功能 */ import { useState, useEffect } from 'react'; import { toastService } from '../ui/Toast'; // import { toastService } from '../ui/Toast'; /** * 评查点类型定义 * 用于展示单个评查结果 */ export interface ReviewPoint { id: string; documentId?: string; pointId?: string; editAuditStatusId?: string | number; editAuditStatus: number; pointName: string; title: string; groupName: string; status: string; content: Record; suggestion: string; needsHumanReview?: boolean; humanReviewNote?: string; humanReviewBy?: string; humanReviewTime?: string; contentPage?: Record; position?: { section: string; index: number; }; result?: boolean; legalBasis?: { name?: string; content?: string; articles?: Array; [key: string]: unknown; }; postAction?: string; actionContent?: string; } // 统计数据类型 interface Statistics { total: number; success: number; warning: number; error: number; score: number; } interface ReviewPointsListProps { reviewPoints: ReviewPoint[]; statistics: Statistics; activeReviewPointResultId: string | null; onReviewPointSelect: (id: string, page?: number) => void; onStatusChange: (id: string, editAuditStatusId: string | number, status: string, message: string) => void; } export function ReviewPointsList({ reviewPoints, statistics, activeReviewPointResultId, onReviewPointSelect, onStatusChange }: ReviewPointsListProps) { // 状态管理 const [editingReviewPoint, setEditingReviewPoint] = useState(null); // 当前正在编辑的评查点ID const [searchText, setSearchText] = useState(''); // 搜索文本 const [statusFilter, setStatusFilter] = useState(null); // 状态过滤 // const [suggestionTexts, setSuggestionTexts] = useState>({}); // 存储每个评查点的建议文本 // 添加重新审核意见的状态/ 用户输入的修改内容 / 用户提前写好的修改内容 const [manualReviewNotes, setManualReviewNotes] = useState>({}); // 初始化建议文本 useEffect(() => { // 将所有评查点的建议文本存储到状态中 const suggestions: Record = {}; reviewPoints.forEach(point => { suggestions[point.id] = point.suggestion || ''; }); // setSuggestionTexts(suggestions); // 使用函数式更新,不再需要外部 manualReviewNotes 变量 setManualReviewNotes(prev => { const notes = { ...prev }; reviewPoints.forEach(point => { notes[point.id] = point.actionContent || ''; }); return notes; }); }, [reviewPoints]); // 处理建议文本变更 // const handleSuggestionChange = (reviewPointId: string, text: string) => { // setSuggestionTexts(prev => ({ // ...prev, // [reviewPointId]: text // })); // }; /** * 处理评查点审核操作 * @param reviewPointResultId 评查点结果ID * @param editAuditStatusId 审核状态记录ID * @param action 操作类型: 'approve' 通过 / 'reject' 不通过 / 'review' 重新审核 * @param message 用户输入的审核内容 */ const handleReviewAction = (reviewPointResultId: string, editAuditStatusId: string | number | undefined, action: 'approve' | 'reject' | 'review', message: string) => { // 更新评查点状态 // console.log('handleReviewAction-------', reviewPointResultId, editAuditStatusId, action, message); if(message.trim() === ''){ toastService.error('请输入审核意见'); return; } if (action === 'review') { // 重新审核时,不更新结果状态,只更新审核意见和审核状态 // console.log('重新审核-------', reviewPointResultId, editAuditStatusId || '', 'review', message); onStatusChange(reviewPointResultId, editAuditStatusId || '', 'review', message); // 找到当前评查点并更新其editAuditStatus为0,使其立即显示通过/不通过按钮 const updatedReviewPoint = reviewPoints.find(point => point.id === reviewPointResultId); if (updatedReviewPoint) { updatedReviewPoint.editAuditStatus = 0; } } else { // 通过/不通过时,更新结果状态和审核意见 // console.log('通过/不通过-------', reviewPointResultId, editAuditStatusId || '', action === 'approve' ? 'true' : 'false', message); onStatusChange(reviewPointResultId, editAuditStatusId || '', action === 'approve' ? 'true' : 'false', message); } // 将参数输出到控制台 console.log('评查点审核操作', { id: reviewPointResultId, editAuditStatusId: editAuditStatusId, action: action, content: message, status: action === 'approve' ? 'true' : (action === 'reject' ? 'false' : 'review') }); // 清除编辑状态 setEditingReviewPoint(null); }; /** * 过滤评查点 * 根据搜索文本和状态过滤条件筛选评查点 */ const filteredReviewPoints = reviewPoints.filter(point => { // 匹配搜索文本 const matchesSearch = searchText === '' || point.pointName.toLowerCase().includes(searchText.toLowerCase()) || point.title.toLowerCase().includes(searchText.toLowerCase()) || point.groupName.toLowerCase().includes(searchText.toLowerCase()) || JSON.stringify(point.content).toLowerCase().includes(searchText.toLowerCase()) // 处理状态过滤 let matchesStatus = false; if (statusFilter === null) { // 未选择过滤条件时显示所有 matchesStatus = true; } else if (statusFilter === 'success') { // 过滤"通过"状态 matchesStatus = point.result === true; } else if (statusFilter === 'warning') { // 过滤"警告"状态 matchesStatus = point.result === false && point.status === 'warning'; } else if (statusFilter === 'error') { // 过滤"错误"状态 matchesStatus = point.result === false && point.status === 'error'; } return matchesSearch && matchesStatus; }); /** * 处理一键替换操作 * @param reviewPointId 评查点ID */ const handleReplace = (reviewPointId: string) => { // 在实际应用中,这里应该调用API进行内容替换 // 模拟替换操作 alert(`将为评查点 ${reviewPointId} 执行一键替换操作`); // 更新评查点状态为成功 // onStatusChange(reviewPointId, 'success'); }; /** * 渲染评查统计信息 * 显示总计、通过、警告、错误数量 */ const renderStatistics = () => { // 确保传入的statistics存在,否则使用计算值 const statsToUse = statistics || { total: reviewPoints.length, success: 0, warning: 0, error: 0, score: 0 }; // 计算各个状态的评查点数量 const successCount = reviewPoints.filter( point => point.result === true || (point.result === undefined && point.status === 'success') ).length; const warningCount = reviewPoints.filter( point => point.result === false && (point.status === 'warning' || point.status === 'info') ).length; const errorCount = reviewPoints.filter( point => point.result === false && point.status === 'error' ).length; // 如果没有计算值,则使用传入的统计值 const totalToShow = statsToUse.total === 0 ? reviewPoints.length : statsToUse.total; const successToShow = successCount || statsToUse.success; const warningToShow = warningCount || statsToUse.warning; const errorToShow = errorCount || statsToUse.error; return (
{/* 总计数量 */}
{/* 通过数量 */}
{/* 警告数量 */}
{/* 错误数量 */}
); }; /** * 渲染搜索框 * 用于按文本搜索评查点 */ const renderSearchBar = () => { return (
setSearchText(e.target.value)} /> {searchText && ( )}
); }; /** * 渲染评查点状态标签 * @param status 状态文本 * @param result 评查结果 * @returns 状态标签组件 */ const renderStatusBadge = (status: string, result?: boolean) => { // 优先根据result判断是否通过 if (result === true) { return ( 通过 ); } // 当result为false时,根据status决定显示警告还是错误 if (result === false) { if (status === 'warning') { return ( 警告 ); } else if (status === 'error') { return ( 不通过 ); } } // 兼容旧版逻辑,当没有result时,仍按status判断 switch (status) { case 'success': return ( 通过 ); case 'warning': return ( 警告 ); case 'error': return ( 不通过 ); case 'processing': return ( 处理中 ); default: return ( 警告 ); } }; /** * 渲染人工审核标记 * @param reviewPoint 评查点 * @returns 人工审核标记组件 */ const renderHumanReviewBadge = (reviewPoint: ReviewPoint) => { if (reviewPoint.postAction === 'manual') { return ( 需人工 ); } return null; }; /** * 渲染人工审核注释 * @param reviewPoint 评查点 * @returns 人工审核注释组件 */ const renderHumanReviewNote = (reviewPoint: ReviewPoint) => { // 目前needsHumanReview和humanReviewNote都为空,所以不显示 if (reviewPoint.needsHumanReview && reviewPoint.humanReviewNote) { return (
{reviewPoint.humanReviewNote} {reviewPoint.humanReviewBy && reviewPoint.humanReviewTime && (
审核人:{reviewPoint.humanReviewBy} | 时间:{reviewPoint.humanReviewTime}
)}
); } return null; }; /** * 渲染评查点主要内容 * @param reviewPoint 评查点 * @returns 评查点主要内容组件 */ const renderContent = (reviewPoint: ReviewPoint) => { return ( <> {/* 修改评查结果的结构之后,显示新的结构 */} {Object.entries(reviewPoint.content).map(([key, value], index) => (
{ e.stopPropagation(); console.log(`单独点击${key}----`, reviewPoint); const valuePage = parseInt(value.page as string); const contentPage = parseInt(reviewPoint.contentPage?.[key] as string); // 检查value中的page属性是否存在,优先取value中的page if (valuePage > 0) { console.log(`存在page且不为空:单独点击${key}---------->evaluated_results内的页码:`, valuePage); onReviewPointSelect(reviewPoint.id, valuePage); } else if(contentPage && contentPage > 0) { console.log(`存在page且为空:单独点击${key}---------->ocr_result内的页码:`, contentPage); onReviewPointSelect(reviewPoint.id, contentPage); }else { toastService.error(`无法找到"${key}"对应的索引内容`); console.log(`单独点击${key}--------没有对应页码`); } }} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); const valuePage = parseInt(value.page as string); const contentPage = parseInt(reviewPoint.contentPage?.[key] as string); // 检查value中的page属性是否存在,优先取value中的page if (valuePage > 0) { onReviewPointSelect(reviewPoint.id, valuePage); } else if(contentPage && contentPage > 0) { onReviewPointSelect(reviewPoint.id, contentPage); } else { toastService.error(`无法找到"${key}"对应的索引内容`); console.log(`单独点击${key}--------没有对应页码`); } } }} role="button" tabIndex={0} aria-label={`查看${key}内容详情`} >
{key} {parseInt(value.page as string)>0 || parseInt(reviewPoint.contentPage?.[key] as string)>0 ? '' : } {value.value?.toString().trim() ? '' : '缺失'}

{(value.value?.toString().trim() === '') ? 占位符 : value.value?.toString() || ''}

))} ); }; /** * 渲染评查点内容与建议 * @param reviewPoint 评查点 * @returns 评查点内容与建议组件 */ const renderReviewPointContent = (reviewPoint: ReviewPoint) => { const handleManualReviewNotesChange = (reviewPointId: string, text: string) => { setManualReviewNotes(prev => ({ ...prev, [reviewPointId]: text })); }; // 如果当前评查点不处于编辑状态,只显示简单信息 if (editingReviewPoint !== reviewPoint.id) { // 根据result和status决定渲染哪种样式 if (reviewPoint.result === true) { // 已通过的评查点只显示基本信息和人工审核注释 delete TODO // if (reviewPoint.needsHumanReview && reviewPoint.humanReviewNote) { // return ( //
//
//

已处理

// {reviewPoint.suggestion && ( //
//

{reviewPoint.suggestion}

//
// )} //
//
// ); // } // 处理 result=true 且 postAction=manual 的情况 if (reviewPoint.postAction === 'manual') { const note = manualReviewNotes[reviewPoint.id] || ''; // 处理重新审核意见的输入 const handleNoteChange = (reviewPointId: string, text: string) => { setManualReviewNotes(prev => ({ ...prev, [reviewPointId]: text })); }; return ( <> {checkContentPage(reviewPoint).pageIndex === 0 && (

该评查点无法找到索引内容,无法自动定位到对应页面。

)}
{reviewPoint.suggestion && (

{reviewPoint.suggestion}

)} {/* 评查点内容显示区域 */} {reviewPoint.content && Object.entries(reviewPoint.content).length > 0 && (
{/* 修改评查结果的结构之后,显示新的结构 */} {renderContent(reviewPoint)}
)} {/* 额外的文本输入框区域 */}
{reviewPoint.editAuditStatus === 0 ? (
) : ( )}
); } // 处理 result=true 且 postAction!=manual 的情况 return ( <> {checkContentPage(reviewPoint).pageIndex === 0 && (

该评查点无法找到索引内容,无法自动定位到对应页面。

)}
{/* 修改评查结果的结构之后,显示新的结构 */} {renderContent(reviewPoint)}
); } return (
{/* 没有索引内容提示 */} {checkContentPage(reviewPoint).pageIndex === 0 && (

该评查点无法找到索引内容,无法自动定位到对应页面。

)} {/* 建议内容显示区域 */} {reviewPoint.suggestion && (

{reviewPoint.suggestion}

)} {/* 法律依据内容 */} {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)) && (
法律依据
{reviewPoint.legalBasis.name && (

{reviewPoint.legalBasis.name}

)} {reviewPoint.legalBasis.content && (

条款内容:{reviewPoint.legalBasis.content}

)} {reviewPoint.legalBasis.articles && Array.isArray(reviewPoint.legalBasis.articles) && reviewPoint.legalBasis.articles.length > 0 && (

相关条款:

    {reviewPoint.legalBasis.articles.map((item, index) => (
  • {typeof item === 'string' ? item : typeof item === 'object' && item !== null ? (item.name ? `${item.name}: ${item.content || ''}` : item.content || JSON.stringify(item)) : String(item)}
  • ))}
)}
) )} {reviewPoint.content !== null && Object.keys(reviewPoint.content).length > 0 && ( <> {/* 内容显示区域 */}
{/* 修改评查结果的结构之后,显示新的结构 */} {renderContent(reviewPoint)}
)} {/* 建议修改区域 */} {/* {((reviewPoint.postAction === 'manual') || (reviewPoint.content !== null && Object.keys(reviewPoint.content).length > 0)) && ( */} {(reviewPoint.postAction === 'manual') && (
{reviewPoint.postAction === 'manual' ? "审核意见:" : "建议修改为:"} {/* 符合规范 */}