新增提示Toast组件
This commit is contained in:
@@ -9,13 +9,48 @@ interface FileInfoProps {
|
||||
uploadTime?: string;
|
||||
uploadUser?: string;
|
||||
auditStatus?: number;
|
||||
path?: string;
|
||||
};
|
||||
onConfirmResults: () => void;
|
||||
}
|
||||
|
||||
export function FileInfo({ fileInfo, onConfirmResults }: FileInfoProps) {
|
||||
const handleDownloadFile = () => {
|
||||
alert('下载原文件功能');
|
||||
const handleDownloadFile = async () => {
|
||||
try {
|
||||
const urlBefore = 'http://172.18.0.100:9000/docauditai/';
|
||||
const downloadUrl = `${urlBefore}${fileInfo.path}`;
|
||||
|
||||
// 使用fetch获取文件内容
|
||||
const response = await fetch(downloadUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`下载失败: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
// 将响应转换为Blob
|
||||
const blob = await response.blob();
|
||||
|
||||
// 创建Blob URL
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
|
||||
// 创建一个隐藏的a标签并点击它
|
||||
const a = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
a.href = blobUrl;
|
||||
// 从路径中获取文件名
|
||||
const fileName = fileInfo.path?.split('/').pop() || 'document';
|
||||
a.download = decodeURIComponent(fileName);
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
|
||||
// 清理
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
}, 100);
|
||||
} catch (error) {
|
||||
console.error('下载文件失败:', error);
|
||||
alert(`下载文件失败: ${error instanceof Error ? error.message : '未知错误'}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleExportReport = () => {
|
||||
|
||||
@@ -12,6 +12,27 @@ pdfjs.GlobalWorkerOptions.workerSrc = '/pdf.worker.js';
|
||||
// 导入统一的ReviewPoint类型
|
||||
import { type ReviewPoint } from './';
|
||||
|
||||
/**
|
||||
* 自定义样式
|
||||
* 这些样式解决了PDF页面在放大时互相重叠的问题
|
||||
*/
|
||||
const styles = {
|
||||
pdfContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column' as const,
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
position: 'relative' as const,
|
||||
},
|
||||
pageContainer: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column' as const,
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
position: 'relative' as const,
|
||||
}
|
||||
};
|
||||
|
||||
// 定义文档内容类型
|
||||
interface FileContent {
|
||||
title: string;
|
||||
@@ -76,24 +97,28 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
||||
};
|
||||
|
||||
// 当选中的评查点变化时,滚动到对应位置
|
||||
useEffect(() => {
|
||||
if (activeReviewPointId && contentRef.current) {
|
||||
const highlightElement = contentRef.current.querySelector(`[data-review-id="${activeReviewPointId}"]`);
|
||||
if (highlightElement) {
|
||||
highlightElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
// useEffect(() => {
|
||||
// if (activeReviewPointId && contentRef.current) {
|
||||
// const highlightElement = contentRef.current.querySelector(`[data-review-id="${activeReviewPointId}"]`);
|
||||
// if (highlightElement) {
|
||||
// highlightElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
// // highlightElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
|
||||
// 添加临时突出显示效果
|
||||
highlightElement.classList.add('highlight-focus');
|
||||
setTimeout(() => {
|
||||
highlightElement.classList.remove('highlight-focus');
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
}, [activeReviewPointId]);
|
||||
// // 添加临时突出显示效果
|
||||
// highlightElement.classList.add('highlight-focus');
|
||||
// setTimeout(() => {
|
||||
// highlightElement.classList.remove('highlight-focus');
|
||||
// }, 1500);
|
||||
// }
|
||||
// }
|
||||
// }, [activeReviewPointId]);
|
||||
|
||||
// 处理页面跳转
|
||||
const prevTargetPageRef = useRef<number | undefined>(undefined);
|
||||
useEffect(() => {
|
||||
if (targetPage && numPages && targetPage <= numPages) {
|
||||
// 如果有目标页码,并且与上次不同或activeReviewPointId变化了,则执行跳转
|
||||
if (targetPage && numPages && targetPage <= numPages && (targetPage !== prevTargetPageRef.current || activeReviewPointId)) {
|
||||
prevTargetPageRef.current = targetPage;
|
||||
let newTargetPage = targetPage;
|
||||
try {
|
||||
// 安全地访问ocrResult
|
||||
@@ -107,11 +132,11 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
||||
|
||||
const pageElement = document.getElementById(`page-${newTargetPage}`);
|
||||
if (pageElement) {
|
||||
console.log(`跳转到第${newTargetPage}页`);
|
||||
pageElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
console.log(`跳转到第${newTargetPage}页,对应评查点ID: ${activeReviewPointId}`);
|
||||
pageElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
}
|
||||
}, [targetPage, numPages, fileContent]);
|
||||
}, [targetPage, numPages, fileContent, activeReviewPointId]);
|
||||
|
||||
// 获取评查点对应的样式类
|
||||
const getHighlightClass = (status: string) => {
|
||||
@@ -133,6 +158,15 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
||||
console.log("PDF加载成功,页数:", numPages);
|
||||
}
|
||||
|
||||
// 计算页面在缩放后的实际间距
|
||||
const calculatePageMargin = (zoomFactor: number) => {
|
||||
// 基础间距为30px,随着缩放倍数线性增加
|
||||
const baseMargin = 30;
|
||||
// 页面缩放后,需要额外添加的间距 = (缩放倍数 - 1) * 页面高度
|
||||
const additionalMargin = Math.max(0, (zoomFactor - 1) * 800); // 800是估计的页面高度
|
||||
return baseMargin + additionalMargin;
|
||||
};
|
||||
|
||||
/**
|
||||
* 渲染PDF文档的所有页面
|
||||
*
|
||||
@@ -157,25 +191,35 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
||||
const pageReviewPoints = reviewPoints.filter(point =>
|
||||
point.position && point.position.section === `page-${i}`
|
||||
);
|
||||
// console.log("pageReviewPoints-------",pageReviewPoints);
|
||||
|
||||
// 计算当前缩放级别下的页面容器样式
|
||||
const zoomFactor = zoomLevel / 100;
|
||||
const pageContainerStyle = {
|
||||
...styles.pageContainer,
|
||||
marginBottom: `${calculatePageMargin(zoomFactor)}px`, // 动态计算页面间距
|
||||
};
|
||||
|
||||
// 为每一页创建组件
|
||||
pages.push(
|
||||
<div key={i} id={`page-${i}`} className="mb-6">
|
||||
<div key={i} id={`page-${i}`} style={pageContainerStyle}>
|
||||
{/* 页码标识,显示在页面上方 */}
|
||||
<div className="text-center text-gray-500 text-sm mb-2">第 {i} 页</div>
|
||||
|
||||
{/* 页面容器,应用缩放变换,设置相对定位用于放置评查点高亮 */}
|
||||
<div style={{
|
||||
transform: `scale(${zoomLevel / 100})`, // 根据zoomLevel应用缩放
|
||||
transformOrigin: 'top center', // 缩放原点设置为顶部中心
|
||||
position: 'relative' // 相对定位,作为评查点高亮的定位参考
|
||||
}}>
|
||||
<div
|
||||
className="page-wrapper flex justify-center"
|
||||
style={{
|
||||
transform: `scale(${zoomFactor})`, // 根据zoomLevel应用缩放
|
||||
transformOrigin: 'top center', // 缩放原点设置为顶部中心
|
||||
position: 'relative', // 相对定位,作为评查点高亮的定位参考
|
||||
maxWidth: '100%', // 限制最大宽度
|
||||
}}
|
||||
>
|
||||
{/* 渲染PDF页面组件 */}
|
||||
<Page
|
||||
pageNumber={i} // 当前页码
|
||||
renderTextLayer={true} // 启用文本层,使文本可选择
|
||||
renderAnnotationLayer={true} // 启用注释层,显示PDF内置注释
|
||||
pageNumber={i} // 当前页码
|
||||
renderTextLayer={true} // 启用文本层,使文本可选择
|
||||
renderAnnotationLayer={true} // 启用注释层,显示PDF内置注释
|
||||
className="border border-gray-300 shadow-md" // 添加边框和阴影样式
|
||||
/>
|
||||
|
||||
@@ -218,20 +262,22 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
||||
// 渲染文档内容
|
||||
const renderDocumentContent = () => {
|
||||
return (
|
||||
<Document
|
||||
file={'http://172.18.0.100:9000/docauditai/'+fileContent.path}
|
||||
onLoadSuccess={onDocumentLoadSuccess}
|
||||
onLoadError={(error) => {
|
||||
console.error("PDF加载错误:", error);
|
||||
setLoadError("PDF文档加载失败:" + (error.message || "未知错误"));
|
||||
}}
|
||||
className="flex flex-col items-center"
|
||||
error={<div className="text-red-500">PDF文档加载失败,请检查链接或网络连接。</div>}
|
||||
noData={<div>无数据</div>}
|
||||
loading={<div className="text-center py-10">PDF加载中...</div>}
|
||||
>
|
||||
{renderAllPages()}
|
||||
</Document>
|
||||
<div style={styles.pdfContainer}>
|
||||
<Document
|
||||
file={'http://172.18.0.100:9000/docauditai/'+fileContent.path}
|
||||
onLoadSuccess={onDocumentLoadSuccess}
|
||||
onLoadError={(error) => {
|
||||
console.error("PDF加载错误:", error);
|
||||
setLoadError("PDF文档加载失败:" + (error.message || "未知错误"));
|
||||
}}
|
||||
className="flex flex-col items-center w-full"
|
||||
error={<div className="text-red-500">PDF文档加载失败,请检查链接或网络连接。</div>}
|
||||
noData={<div>无数据</div>}
|
||||
loading={<div className="text-center py-10">PDF加载中...</div>}
|
||||
>
|
||||
{renderAllPages()}
|
||||
</Document>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -255,15 +301,20 @@ export function FilePreview({ fileContent, reviewPoints, activeReviewPointId, ta
|
||||
>
|
||||
<i className="ri-zoom-out-line"></i>
|
||||
</button>
|
||||
<button
|
||||
{/* <button
|
||||
className="ant-btn ant-btn-sm ant-btn-default py-0 px-1 text-xs h-5 leading-5"
|
||||
onClick={toggleHighlights}
|
||||
>
|
||||
<i className="ri-mark-pen-line"></i> {highlightsVisible ? '隐藏问题' : '显示问题'}
|
||||
</button>
|
||||
</button> */}
|
||||
<span className="ml-2 text-xs text-gray-500">{"比例:"+zoomLevel+"%"}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="file-preview-content" ref={contentRef}>
|
||||
<div
|
||||
className="file-preview-content relative overflow-y-auto"
|
||||
ref={contentRef}
|
||||
style={{ maxHeight: 'calc(100vh - 150px)' }}
|
||||
>
|
||||
{loadError ? (
|
||||
<div className="text-red-500 p-4">
|
||||
<p>{loadError}</p>
|
||||
|
||||
@@ -25,6 +25,7 @@ import { useState, useEffect } from 'react';
|
||||
*/
|
||||
export interface ReviewPoint {
|
||||
id: string;
|
||||
pointName: string;
|
||||
title: string;
|
||||
groupName: string;
|
||||
status: string;
|
||||
@@ -34,7 +35,7 @@ export interface ReviewPoint {
|
||||
humanReviewNote?: string;
|
||||
humanReviewBy?: string;
|
||||
humanReviewTime?: string;
|
||||
contentPage?: number[];
|
||||
contentPage?: Record<string, number[]>;
|
||||
position?: {
|
||||
section: string;
|
||||
index: number;
|
||||
@@ -133,7 +134,7 @@ export function ReviewPointsList({
|
||||
// 清除编辑状态
|
||||
setEditingReviewPoint(null);
|
||||
|
||||
alert(`${action === 'approve' ? '通过' : '不通过'}了评查点 ${reviewPointResultId},审核内容: ${message}`);
|
||||
// alert(`${action === 'approve' ? '通过' : '不通过'}了评查点 ${reviewPointResultId},审核内容: ${message}`);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -143,6 +144,7 @@ export function ReviewPointsList({
|
||||
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()) ||
|
||||
(typeof point.content === 'string' && point.content.toLowerCase().includes(searchText.toLowerCase())) ||
|
||||
@@ -297,7 +299,7 @@ export function ReviewPointsList({
|
||||
<i className="ri-search-line absolute left-2 top-0.5 text-gray-400"></i>
|
||||
{searchText && (
|
||||
<button
|
||||
className="absolute right-2 top-1.5 text-gray-400 hover:text-gray-600"
|
||||
className="absolute right-2 top-0.5 text-gray-400 hover:text-gray-600"
|
||||
onClick={() => setSearchText('')}
|
||||
>
|
||||
<i className="ri-close-line"></i>
|
||||
@@ -430,8 +432,8 @@ export function ReviewPointsList({
|
||||
// 如果当前评查点不处于编辑状态,只显示简单信息
|
||||
if (editingReviewPoint !== reviewPoint.id) {
|
||||
// 根据result和status决定渲染哪种样式
|
||||
if (reviewPoint.result === true || (reviewPoint.result === undefined && reviewPoint.status === 'success')) {
|
||||
// 已通过的评查点只显示基本信息和人工审核注释
|
||||
if (reviewPoint.result === true ){
|
||||
// 已通过的评查点只显示基本信息和人工审核注释 delete
|
||||
if (reviewPoint.needsHumanReview && reviewPoint.humanReviewNote) {
|
||||
return (
|
||||
<div className="mt-2">
|
||||
@@ -449,6 +451,7 @@ export function ReviewPointsList({
|
||||
|
||||
// 处理 result=true 且 postAction=manual 的情况
|
||||
if (reviewPoint.postAction === 'manual') {
|
||||
// 处理重新审核意见的提交
|
||||
const handleReReview = (reviewPointId: string, status: string) => {
|
||||
const note = manualReviewNotes[reviewPointId] || '';
|
||||
if (!note.trim()) {
|
||||
@@ -461,6 +464,7 @@ export function ReviewPointsList({
|
||||
// 可以添加提交成功后的状态更新等操作
|
||||
};
|
||||
|
||||
// 处理重新审核意见的输入
|
||||
const handleNoteChange = (reviewPointId: string, text: string) => {
|
||||
setManualReviewNotes(prev => ({
|
||||
...prev,
|
||||
@@ -503,7 +507,70 @@ export function ReviewPointsList({
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
// 处理 result=true 且 postAction!=manual 的情况
|
||||
return (
|
||||
<>
|
||||
{checkContentPage(reviewPoint).pageIndex === 0 && (
|
||||
<p className="text-xs text-red-500 select-text text-left">该评查点无法找到索引内容,无法自动定位到对应页面。</p>
|
||||
)}
|
||||
<div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3 select-text">
|
||||
{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 cursor-pointer hover:bg-gray-100 transition-colors duration-200 rounded p-1"
|
||||
onClick={(e) => {
|
||||
// 阻止事件冒泡,防止触发父元素的点击事件
|
||||
e.stopPropagation();
|
||||
|
||||
console.log(`非通过:单独点击${key}----`,reviewPoint);
|
||||
// 检查评查点是否有 contentPage 以及当前 key 对应的页码数组
|
||||
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
|
||||
// 获取当前 key 对应的第一个页码并跳转
|
||||
console.log(`非通过:单独点击${key}----页码---`,reviewPoint.contentPage[key][0]);
|
||||
|
||||
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
|
||||
} else {
|
||||
// 如果没有对应页码,弹出提示
|
||||
// alert(`无法找到"${key}"对应的内容页面`);
|
||||
}
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
|
||||
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
|
||||
} else {
|
||||
// alert(`无法找到"${key}"对应的内容页面`);
|
||||
}
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={`查看${key}内容详情`}
|
||||
>
|
||||
{/* 使用flex布局使key和状态标签左右对齐 */}
|
||||
<div className="flex justify-between items-center mb-1">
|
||||
<span className="text-xs">{key}</span>
|
||||
<span className={`text-xs ${value ? 'text-error' : 'text-warning'}`}>
|
||||
{value ? '' : '缺失'}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-left select-text">{value || (value === '' ? <span className="invisible">占位符</span> : '')}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
// 当 content 是字符串时的渲染方式
|
||||
<>
|
||||
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// 非通过状态,显示内容和修改建议
|
||||
@@ -512,35 +579,108 @@ export function ReviewPointsList({
|
||||
|
||||
return (
|
||||
<div className="mt-2">
|
||||
|
||||
{/* 没有索引内容提示 */}
|
||||
{reviewPoint.contentPage &&
|
||||
checkContentPage(reviewPoint).pageIndex === 0 && (
|
||||
// <div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3 select-text">
|
||||
<p className="text-xs text-red-500 select-text text-left">该评查点无法找到索引内容,无法自动定位到对应页面。</p>
|
||||
// </div>
|
||||
)}
|
||||
|
||||
{/* 建议内容显示区域 */}
|
||||
{reviewPoint.suggestion && (
|
||||
<div className="p-2 bg-blue-50 rounded border border-blue-200 text-xs mb-3 select-text">
|
||||
<div className="flex items-start">
|
||||
<i className="ri-information-line text-blue-500 mr-2 mt-0.5"></i>
|
||||
<p className="text-xs text-gray-600 select-text text-left">{reviewPoint.suggestion}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{/* 法律依据内容 */}
|
||||
{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 select-text">
|
||||
<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 select-text">{reviewPoint.legalBasis.name}</p>
|
||||
)}
|
||||
{reviewPoint.legalBasis.content && (
|
||||
<p className="text-xs text-left mb-1 select-text"><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 select-text">
|
||||
{reviewPoint.legalBasis.articles.map((item, index) => (
|
||||
<li key={index} className="text-xs text-left select-text">
|
||||
{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>
|
||||
)
|
||||
)}
|
||||
|
||||
|
||||
{reviewPoint.content !== null && (
|
||||
(typeof reviewPoint.content === 'string' && reviewPoint.content !== '') ||
|
||||
(typeof reviewPoint.content === 'object' && Object.keys(reviewPoint.content).length > 0)
|
||||
) && (
|
||||
<>
|
||||
{/* 建议内容显示区域 */}
|
||||
{reviewPoint.suggestion && (
|
||||
<div className="p-2 bg-blue-50 rounded border border-blue-200 text-xs mb-3 select-text">
|
||||
<div className="flex items-start">
|
||||
<i className="ri-information-line text-blue-500 mr-2 mt-0.5"></i>
|
||||
<p className="text-xs text-gray-600 select-text">{reviewPoint.suggestion}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 内容显示区域 */}
|
||||
<div className="p-2 bg-white rounded border border-gray-200 text-xs mb-3 select-text">
|
||||
{/* 移除顶部的"当前值"标题,在每个内容项中显示 */}
|
||||
{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">
|
||||
<div
|
||||
key={index}
|
||||
className="mb-2 pb-2 border-b border-gray-100 last:border-b-0 last:mb-0 cursor-pointer hover:bg-gray-100 transition-colors duration-200 rounded p-1"
|
||||
onClick={(e) => {
|
||||
// 阻止事件冒泡,防止触发父元素的点击事件
|
||||
e.stopPropagation();
|
||||
|
||||
console.log(`非通过:单独点击${key}----`,reviewPoint);
|
||||
// 检查评查点是否有 contentPage 以及当前 key 对应的页码数组
|
||||
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
|
||||
// 获取当前 key 对应的第一个页码并跳转
|
||||
console.log(`非通过:单独点击${key}----页码---`,reviewPoint.contentPage[key][0]);
|
||||
|
||||
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
|
||||
} else {
|
||||
// 如果没有对应页码,弹出提示
|
||||
// alert(`无法找到"${key}"对应的内容页面`);
|
||||
}
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
|
||||
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
|
||||
} else {
|
||||
// alert(`无法找到"${key}"对应的内容页面`);
|
||||
}
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={`查看${key}内容详情`}
|
||||
>
|
||||
{/* 使用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>
|
||||
@@ -549,6 +689,8 @@ export function ReviewPointsList({
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
) : (
|
||||
// 当 content 是字符串时的渲染方式
|
||||
<>
|
||||
@@ -563,96 +705,65 @@ export function ReviewPointsList({
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 法律依据内容 */}
|
||||
{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 select-text">
|
||||
<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 select-text">{reviewPoint.legalBasis.name}</p>
|
||||
)}
|
||||
{reviewPoint.legalBasis.content && (
|
||||
<p className="text-xs text-left mb-1 select-text"><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 select-text">
|
||||
{reviewPoint.legalBasis.articles.map((item, index) => (
|
||||
<li key={index} className="text-xs text-left select-text">
|
||||
{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>
|
||||
)
|
||||
)}
|
||||
|
||||
{/* 建议修改区域 */}
|
||||
{/* {(reviewPoint.postAction !== 'none') && ( */}
|
||||
<div className="mb-2">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-gray-700 text-[0.8rem]">{reviewPoint.postAction === 'manual' ? "审核意见:" : "建议修改为:"}</span>
|
||||
{/* <span className="text-green-500">符合规范</span> */}
|
||||
</div>
|
||||
<textarea
|
||||
value={manualReviewNotes[reviewPoint.id] || ''}
|
||||
placeholder={reviewPoint.postAction === 'manual' ? "请输入审核意见(可选)..." : "请输入建议修改内容..."}
|
||||
onChange={(e) => handleManualReviewNotesChange(reviewPoint.id, e.target.value)}
|
||||
className="text-xs 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="flex space-x-2 mt-2">
|
||||
{/* 一键替换按钮 - 只有非人工审核的点或未通过的人工审核点才显示 */}
|
||||
{(reviewPoint.postAction !== 'manual') && (
|
||||
<button
|
||||
className="replace-action flex-1 justify-center"
|
||||
onClick={() => handleReplace(reviewPoint.id)}
|
||||
>
|
||||
<i className="ri-replace-line"></i> 一键替换
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* 人工审核按钮 */}
|
||||
{reviewPoint.postAction === 'manual' && (
|
||||
<div className="w-full flex justify-end gap-2">
|
||||
<button
|
||||
className="bg-[#1890ff] hover:bg-blue-600 text-white py-1 px-2 rounded-md text-sm"
|
||||
onClick={() => {
|
||||
const note = manualReviewNotes[reviewPoint.id] || '';
|
||||
handleReviewAction(reviewPoint.id, 'approve', note);
|
||||
}}
|
||||
>
|
||||
<i className="ri-check-line mr-1"></i> 通过
|
||||
</button>
|
||||
<button
|
||||
className="bg-[#f5222d] hover:bg-red-600 text-white py-1 px-2 rounded-md text-sm"
|
||||
onClick={() => {
|
||||
const note = manualReviewNotes[reviewPoint.id] || '';
|
||||
handleReviewAction(reviewPoint.id, 'reject', note);
|
||||
}}
|
||||
>
|
||||
<i className="ri-close-line mr-1"></i> 不通过
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
{/* 建议修改区域 */}
|
||||
{/* {((reviewPoint.postAction === 'manual') || (reviewPoint.content !== null && Object.keys(reviewPoint.content).length > 0)) && ( */}
|
||||
{(reviewPoint.postAction === 'manual') && (
|
||||
<div className="mb-2">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<span className="text-gray-700 text-[0.8rem]">{reviewPoint.postAction === 'manual' ? "审核意见:" : "建议修改为:"}</span>
|
||||
{/* <span className="text-green-500">符合规范</span> */}
|
||||
</div>
|
||||
<textarea
|
||||
value={manualReviewNotes[reviewPoint.id] || ''}
|
||||
placeholder={reviewPoint.postAction === 'manual' ? "请输入审核意见(可选)..." : "请输入建议修改内容..."}
|
||||
onChange={(e) => handleManualReviewNotesChange(reviewPoint.id, e.target.value)}
|
||||
className="text-xs 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>
|
||||
)}
|
||||
|
||||
{/* 操作按钮区域 */}
|
||||
{/* {((reviewPoint.postAction === 'manual') || (reviewPoint.content !== null && Object.keys(reviewPoint.content).length > 0)) && ( */}
|
||||
{(reviewPoint.postAction === 'manual') && (
|
||||
<div className="flex space-x-2 mt-2">
|
||||
{/* 一键替换按钮 - 只有非人工审核的点或未通过的人工审核点才显示 */}
|
||||
{(reviewPoint.postAction !== 'manual') && (
|
||||
<button
|
||||
className="replace-action flex-1 justify-center"
|
||||
onClick={() => handleReplace(reviewPoint.id)}
|
||||
>
|
||||
<i className="ri-replace-line"></i> 一键替换
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* 人工审核按钮 */}
|
||||
{reviewPoint.postAction === 'manual' && (
|
||||
<div className="w-full flex justify-end gap-2">
|
||||
<button
|
||||
className="bg-[#1890ff] hover:bg-blue-600 text-white py-1 px-2 rounded-md text-sm"
|
||||
onClick={() => {
|
||||
const note = manualReviewNotes[reviewPoint.id] || '';
|
||||
handleReviewAction(reviewPoint.id, 'approve', note);
|
||||
}}
|
||||
>
|
||||
<i className="ri-check-line mr-1"></i> 通过
|
||||
</button>
|
||||
<button
|
||||
className="bg-[#f5222d] hover:bg-red-600 text-white py-1 px-2 rounded-md text-sm"
|
||||
onClick={() => {
|
||||
const note = manualReviewNotes[reviewPoint.id] || '';
|
||||
handleReviewAction(reviewPoint.id, 'reject', note);
|
||||
}}
|
||||
>
|
||||
<i className="ri-close-line mr-1"></i> 不通过
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -670,12 +781,45 @@ export function ReviewPointsList({
|
||||
// 当 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">
|
||||
<div
|
||||
key={index}
|
||||
className="mb-2 pb-2 border-b border-gray-100 last:border-b-0 last:mb-0 last:pb-0 cursor-pointer hover:bg-gray-50 transition-colors duration-200 rounded p-1"
|
||||
onClick={(e) => {
|
||||
// 阻止事件冒泡,防止触发父元素的点击事件
|
||||
e.stopPropagation();
|
||||
|
||||
console.log(`非通过:单独点击${key}----`,reviewPoint);
|
||||
// 检查评查点是否有 contentPage 以及当前 key 对应的页码数组
|
||||
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
|
||||
// 获取当前 key 对应的第一个页码并跳转
|
||||
console.log(`非通过:单独点击${key}----页码---`,reviewPoint.contentPage[key][0]);
|
||||
|
||||
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
|
||||
} else {
|
||||
// 如果没有对应页码,弹出提示
|
||||
alert(`无法找到"${key}"对应的内容页面`);
|
||||
}
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
if (reviewPoint.contentPage && reviewPoint.contentPage[key] && reviewPoint.contentPage[key].length > 0) {
|
||||
onReviewPointSelect(reviewPoint.id, reviewPoint.contentPage[key][0]);
|
||||
} else {
|
||||
alert(`无法找到"${key}"对应的内容页面`);
|
||||
}
|
||||
}
|
||||
}}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label={`查看${key}内容详情`}
|
||||
>
|
||||
{/* 使用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 ? '不符合规范' : '需优化'}
|
||||
{/* {isErrorStatus ? '不符合规范' : '需优化'} */}
|
||||
{value ? '' : '缺失'}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-left select-text">{value || (value === '' ? <span className="invisible">占位符</span> : '')}</p>
|
||||
@@ -770,14 +914,54 @@ export function ReviewPointsList({
|
||||
// 找到被点击的评查点
|
||||
const reviewPoint = reviewPoints.find(point => point.id === id);
|
||||
|
||||
// 如果评查点存在并且有contentPage数组,传递第一个页码
|
||||
if (reviewPoint && reviewPoint.contentPage && reviewPoint.contentPage.length > 0) {
|
||||
onReviewPointSelect(id, reviewPoint.contentPage[0]);
|
||||
// 如果评查点存在
|
||||
if (reviewPoint) {
|
||||
// 使用checkContentPage方法获取页码和key
|
||||
const { pageIndex, key } = checkContentPage(reviewPoint);
|
||||
|
||||
// 如果有有效页码,传递ID和页码
|
||||
if (pageIndex > 0) {
|
||||
console.log(`跳转到页面 ${pageIndex},对应内容 ${key || '未知'}`);
|
||||
onReviewPointSelect(id, pageIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// 没有有效页码,只传递ID
|
||||
onReviewPointSelect(id);
|
||||
} else {
|
||||
// 没有找到评查点,只传递ID
|
||||
onReviewPointSelect(id);
|
||||
}
|
||||
};
|
||||
|
||||
// 检查评查点的contentPage
|
||||
const checkContentPage = (reviewPoint: ReviewPoint): { pageIndex: number, key?: string, id: string } => {
|
||||
// 返回对象初始化
|
||||
const result = { pageIndex: 0, id: reviewPoint.id };
|
||||
|
||||
// 如果contentPage不存在或是空对象,返回默认值
|
||||
if (!reviewPoint.contentPage || Object.keys(reviewPoint.contentPage).length === 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// 遍历contentPage中的每个key
|
||||
for (const key of Object.keys(reviewPoint.contentPage)) {
|
||||
const pageArr = reviewPoint.contentPage[key];
|
||||
// 如果数组存在且长度大于0
|
||||
if (pageArr && pageArr.length > 0) {
|
||||
// 返回第一个找到的有效页码,以及对应的key
|
||||
return {
|
||||
pageIndex: pageArr[0],
|
||||
key,
|
||||
id: reviewPoint.id
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 如果遍历完所有key都没找到有效页码,返回默认值
|
||||
return result;
|
||||
};
|
||||
|
||||
// 组件主渲染函数
|
||||
return (
|
||||
<div className="review-points-panel select-text">
|
||||
@@ -805,6 +989,8 @@ export function ReviewPointsList({
|
||||
style={{ userSelect: 'text' }}
|
||||
>
|
||||
{/* 评查点标题和状态 */}
|
||||
{/* 评查点名称 pointName*/}
|
||||
<div className="review-point-title flex-1 text-left text-blue-500">{'评查点名称:'+reviewPoint.pointName}</div>
|
||||
<div className="review-point-header flex justify-between items-start">
|
||||
<div className="review-point-title flex-1 text-left">{reviewPoint.title}</div>
|
||||
{/* 评查点所属分组 */}
|
||||
|
||||
Reference in New Issue
Block a user