feat: 1. 完善文档列表的显示效果,数据对接后端接口返回。
2. 对评查点分组和文档类型的编辑删除新增操作进行限制。
This commit is contained in:
+184
-180
@@ -11,8 +11,9 @@ import { FilterPanel, FilterSelect, SearchFilter, DateRangeFilter } from "~/comp
|
||||
import { TableRowSkeleton, LoadingIndicator, NumberSkeleton } from '~/components/ui/SkeletonScreen';
|
||||
import documentsIndexStyles from "~/styles/pages/documents_index.css?url";
|
||||
import documentVersionStyles from "~/styles/components/document-version.css?url";
|
||||
import { getDocuments, getDocumentsWithVersionInfo, getDocumentHistory, deleteDocument, type DocumentUI, type DocumentVersionUI } from "~/api/files/documents";
|
||||
import { IssuesDiff } from "~/components/ui/IssuesDiff";
|
||||
import { deleteDocument, getDocumentsListFromAPI, type DocumentUI, type DocumentVersionUI } from "~/api/files/documents";
|
||||
// import { IssuesDiff } from "~/components/ui/IssuesDiff";
|
||||
import { ResultStats } from "~/components/ui/ResultStats";
|
||||
import { getDocumentTypes } from "~/api/document-types/document-types";
|
||||
import { updateDocumentAuditStatus } from "~/api/evaluation_points/rules-files";
|
||||
import { appendContractAttachments, uploadContractTemplate } from "~/api/files/files-upload";
|
||||
@@ -184,8 +185,8 @@ export default function DocumentsIndex() {
|
||||
const fetcher = useFetcher<ActionResponse>();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// 存储从 sessionStorage 获取的 reviewType
|
||||
const [reviewType, setReviewType] = useState<string | null>(null);
|
||||
// 存储从 sessionStorage 获取的 documentTypeIds
|
||||
const [documentTypeIds, setDocumentTypeIds] = useState<number[] | null>(null);
|
||||
|
||||
// 添加页面加载状态管理
|
||||
const [isLoadingData, setIsLoadingData] = useState(true);
|
||||
@@ -260,96 +261,100 @@ export default function DocumentsIndex() {
|
||||
const currentPage = parseInt(searchParams.get("page") || "1", 10);
|
||||
const pageSize = parseInt(searchParams.get("pageSize") || "10", 10);
|
||||
|
||||
|
||||
// 客户端数据请求
|
||||
const fetchData = useCallback(async (storedReviewType: string) => {
|
||||
const fetchData = useCallback(async (typeIds: number[]) => {
|
||||
setIsLoadingData(true);
|
||||
loadingBarService.show();
|
||||
|
||||
try {
|
||||
// 从 localStorage 获取用户ID(与 token 管理保持一致)
|
||||
const userId = getUserId();
|
||||
if (!userId) {
|
||||
// 从 loaderData 获取 JWT Token
|
||||
const jwtToken = loaderData.frontendJWT;
|
||||
if (!jwtToken) {
|
||||
toastService.error('用户身份验证失败,无法获取文档列表');
|
||||
setIsLoadingData(false);
|
||||
loadingBarService.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建搜索参数(token 由 axios 拦截器自动从 localStorage 获取)
|
||||
const searchParams = {
|
||||
console.log('🔑 [fetchData] 文档类型IDs:', typeIds);
|
||||
|
||||
// 调用新的 API 函数
|
||||
const result = await getDocumentsListFromAPI({
|
||||
page: currentPage,
|
||||
pageSize: pageSize,
|
||||
name: search || undefined,
|
||||
documentNumber: documentNumber || undefined,
|
||||
documentType: documentType || undefined,
|
||||
documentTypeIds: documentType ? [parseInt(documentType, 10)] : typeIds, // 如果有单独选择的类型,优先使用
|
||||
auditStatus: auditStatus || undefined,
|
||||
fileStatus: fileStatus || undefined,
|
||||
dateFrom: dateFrom || undefined,
|
||||
dateTo: dateTo || undefined,
|
||||
reviewType: storedReviewType || undefined,
|
||||
userId: userId, // 添加用户ID筛选
|
||||
page: currentPage,
|
||||
pageSize
|
||||
};
|
||||
token: jwtToken
|
||||
});
|
||||
|
||||
// 获取文档列表(带版本信息)
|
||||
const documentsResponse = await getDocumentsWithVersionInfo(searchParams);
|
||||
if (documentsResponse.error) {
|
||||
throw new Error(documentsResponse.error);
|
||||
if (result.error) {
|
||||
toastService.error('获取文档列表失败: ' + result.error);
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔑 从 sessionStorage 读取文档类型 IDs
|
||||
const typeIdsStr = sessionStorage.getItem('documentTypeIds');
|
||||
const documentTypeIds = typeIdsStr ? JSON.parse(typeIdsStr) : undefined;
|
||||
if (!result.data) {
|
||||
toastService.error('获取文档列表失败: 返回数据为空');
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取经过过滤的文档类型列表(token 由 axios 拦截器自动获取)
|
||||
// 更新状态
|
||||
setDocuments(result.data.documents);
|
||||
setTotal(result.data.total);
|
||||
|
||||
// 获取经过过滤的文档类型列表
|
||||
const filteredTypesResponse = await getDocumentTypes({
|
||||
pageSize: 500,
|
||||
documentTypeIds: documentTypeIds // 使用动态的文档类型 IDs
|
||||
});
|
||||
documentTypeIds: typeIds
|
||||
}, jwtToken);
|
||||
const filteredDocumentTypes = filteredTypesResponse.data?.types || [];
|
||||
const filteredOptions = filteredDocumentTypes.map(type => ({
|
||||
value: type.id,
|
||||
label: type.name
|
||||
}));
|
||||
|
||||
// console.log('文档列表',documentsResponse)
|
||||
|
||||
// 更新状态
|
||||
setDocuments(documentsResponse.data?.documents || []);
|
||||
setTotal(documentsResponse.data?.total || 0);
|
||||
setFilteredDocumentTypeOptions(filteredOptions);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('获取文档列表失败:', error);
|
||||
console.error('❌ [fetchData] 获取文档列表失败:', error);
|
||||
toastService.error('获取文档列表失败: ' + (error instanceof Error ? error.message : '未知错误'));
|
||||
} finally {
|
||||
setIsLoadingData(false);
|
||||
loadingBarService.hide();
|
||||
}
|
||||
}, [search, documentNumber, documentType, auditStatus, fileStatus, dateFrom, dateTo, currentPage, pageSize, getUserId]);
|
||||
}, [search, documentNumber, documentType, auditStatus, fileStatus, dateFrom, dateTo, currentPage, pageSize, loaderData.frontendJWT]);
|
||||
|
||||
// 在组件挂载时从 sessionStorage 获取 reviewType 并加载数据
|
||||
// 在组件挂载时从 sessionStorage 获取 documentTypeIds 并加载数据
|
||||
useEffect(() => {
|
||||
try {
|
||||
if (typeof window !== 'undefined') {
|
||||
const storedReviewType = sessionStorage.getItem('reviewType');
|
||||
setReviewType(storedReviewType);
|
||||
|
||||
// 如果有 reviewType,则加载数据
|
||||
if (storedReviewType) {
|
||||
fetchData(storedReviewType);
|
||||
const typeIdsStr = sessionStorage.getItem('documentTypeIds');
|
||||
if (typeIdsStr) {
|
||||
const typeIds = JSON.parse(typeIdsStr) as number[];
|
||||
console.log('📋 [useEffect] 从 sessionStorage 获取文档类型IDs:', typeIds);
|
||||
setDocumentTypeIds(typeIds);
|
||||
|
||||
// 加载数据
|
||||
fetchData(typeIds);
|
||||
} else {
|
||||
console.warn('⚠️ [useEffect] sessionStorage 中没有 documentTypeIds');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取 sessionStorage 中的 reviewType 失败:', error);
|
||||
console.error('❌ [useEffect] 获取 sessionStorage 中的 documentTypeIds 失败:', error);
|
||||
}
|
||||
}, [fetchData]);
|
||||
|
||||
// 监听 URL 参数变化,重新获取数据
|
||||
useEffect(() => {
|
||||
if (reviewType) {
|
||||
fetchData(reviewType);
|
||||
if (documentTypeIds) {
|
||||
fetchData(documentTypeIds);
|
||||
}
|
||||
}, [searchParams, fetchData, reviewType]);
|
||||
}, [searchParams, fetchData, documentTypeIds]);
|
||||
|
||||
// 使用并更新缓存数据
|
||||
useEffect(() => {
|
||||
@@ -380,15 +385,15 @@ export default function DocumentsIndex() {
|
||||
if (fetcher.data.result) {
|
||||
toastService.success(fetcher.data.message);
|
||||
// 删除成功后重新加载数据
|
||||
if (reviewType) {
|
||||
fetchData(reviewType);
|
||||
if (documentTypeIds) {
|
||||
fetchData(documentTypeIds);
|
||||
}
|
||||
} else if (fetcher.data.message) {
|
||||
toastService.error(fetcher.data.message);
|
||||
// 删除失败只显示错误信息,不刷新数据
|
||||
}
|
||||
}
|
||||
}, [fetcher.data, fetcher.state, fetchData, reviewType, isDeleting]);
|
||||
}, [fetcher.data, fetcher.state, fetchData, documentTypeIds, isDeleting]);
|
||||
|
||||
// 分页处理函数
|
||||
const handlePageChange = (page: number) => {
|
||||
@@ -649,20 +654,29 @@ export default function DocumentsIndex() {
|
||||
|
||||
// 导出列表
|
||||
const handleExport = async () => {
|
||||
// 如果没有文档,显示提示信息
|
||||
if (documents.length === 0) {
|
||||
// alert('当前页面没有文档可供导出');
|
||||
toastService.error('当前页面没有文档可供导出');
|
||||
// 检查是否选中了文档
|
||||
if (selectedRowKeys.length === 0) {
|
||||
toastService.error('请至少选择一个文档');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 过滤出选中的文档(仅主文档,不包括历史版本)
|
||||
const selectedDocuments = documents.filter((doc: DocumentUI) =>
|
||||
selectedRowKeys.includes(doc.id.toString())
|
||||
);
|
||||
|
||||
if (selectedDocuments.length === 0) {
|
||||
toastService.error('没有可导出的文档');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建一个ZIP文件
|
||||
const JSZip = await import('jszip').then(module => module.default);
|
||||
const zip = new JSZip();
|
||||
|
||||
// 准备所有下载任务
|
||||
const downloadTasks = documents.map(async (doc: DocumentUI) => {
|
||||
|
||||
// 准备所有下载任务(仅选中的文档)
|
||||
const downloadTasks = selectedDocuments.map(async (doc: DocumentUI) => {
|
||||
try {
|
||||
if (!doc.path) {
|
||||
console.warn(`文档 ${doc.name} 没有有效的路径`);
|
||||
@@ -840,16 +854,16 @@ export default function DocumentsIndex() {
|
||||
}
|
||||
|
||||
toastService.success('附件追加成功!');
|
||||
|
||||
|
||||
// 重置状态
|
||||
setAttachmentFiles([]);
|
||||
setAttachmentRemark("");
|
||||
setShowAttachmentUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
|
||||
|
||||
// 刷新文档列表
|
||||
if (reviewType) {
|
||||
fetchData(reviewType);
|
||||
if (documentTypeIds && documentTypeIds.length > 0) {
|
||||
fetchData(documentTypeIds);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@@ -911,15 +925,15 @@ export default function DocumentsIndex() {
|
||||
}
|
||||
|
||||
toastService.success('合同模板上传成功!');
|
||||
|
||||
|
||||
// 重置状态
|
||||
setTemplateFile(null);
|
||||
setShowTemplateUpload(false);
|
||||
setSelectedDocumentId(null);
|
||||
|
||||
|
||||
// 刷新文档列表
|
||||
if (reviewType) {
|
||||
fetchData(reviewType);
|
||||
if (documentTypeIds && documentTypeIds.length > 0) {
|
||||
fetchData(documentTypeIds);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@@ -933,79 +947,29 @@ export default function DocumentsIndex() {
|
||||
// 展开/折叠历史版本
|
||||
const handleToggleExpand = async (doc: DocumentUI) => {
|
||||
const newExpanded = new Set(expandedRows);
|
||||
const newLoading = new Set(loadingHistory);
|
||||
|
||||
if (expandedRows.has(doc.id)) {
|
||||
// 折叠:移除展开状态
|
||||
newExpanded.delete(doc.id);
|
||||
setExpandedRows(newExpanded);
|
||||
|
||||
// 清空历史版本数据
|
||||
// 更新展开状态
|
||||
setDocuments(prevDocs =>
|
||||
prevDocs.map(d =>
|
||||
d.id === doc.id ? { ...d, historyVersions: undefined, isExpanded: false } : d
|
||||
d.id === doc.id ? { ...d, isExpanded: false } : d
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// 展开:加载历史版本
|
||||
// 展开:显示历史版本
|
||||
newExpanded.add(doc.id);
|
||||
setExpandedRows(newExpanded);
|
||||
|
||||
// 如果还没有加载历史版本,则加载
|
||||
if (!doc.historyVersions && doc.historyCount && doc.historyCount > 0) {
|
||||
newLoading.add(doc.id);
|
||||
setLoadingHistory(newLoading);
|
||||
|
||||
try {
|
||||
// 从 localStorage 获取用户ID(与 token 管理保持一致)
|
||||
const userId = getUserId();
|
||||
if (!userId) {
|
||||
toastService.error('用户身份验证失败');
|
||||
newExpanded.delete(doc.id);
|
||||
setExpandedRows(newExpanded);
|
||||
newLoading.delete(doc.id);
|
||||
setLoadingHistory(newLoading);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await getDocumentHistory(
|
||||
doc.name,
|
||||
userId,
|
||||
doc.id
|
||||
);
|
||||
|
||||
if (result.data) {
|
||||
// 更新文档的历史版本数据
|
||||
setDocuments(prevDocs =>
|
||||
prevDocs.map(d =>
|
||||
d.id === doc.id
|
||||
? { ...d, historyVersions: result.data, isExpanded: true }
|
||||
: d
|
||||
)
|
||||
);
|
||||
} else if (result.error) {
|
||||
toastService.error(`加载历史版本失败: ${result.error}`);
|
||||
// 加载失败时取消展开
|
||||
newExpanded.delete(doc.id);
|
||||
setExpandedRows(newExpanded);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载历史版本失败:', error);
|
||||
toastService.error('加载历史版本失败');
|
||||
newExpanded.delete(doc.id);
|
||||
setExpandedRows(newExpanded);
|
||||
} finally {
|
||||
newLoading.delete(doc.id);
|
||||
setLoadingHistory(newLoading);
|
||||
}
|
||||
} else {
|
||||
// 已经加载过,只更新展开状态
|
||||
setDocuments(prevDocs =>
|
||||
prevDocs.map(d =>
|
||||
d.id === doc.id ? { ...d, isExpanded: true } : d
|
||||
)
|
||||
);
|
||||
}
|
||||
// 更新展开状态(历史版本数据已经在主数据中了)
|
||||
setDocuments(prevDocs =>
|
||||
prevDocs.map(d =>
|
||||
d.id === doc.id ? { ...d, isExpanded: true } : d
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1013,26 +977,35 @@ export default function DocumentsIndex() {
|
||||
const renderHistoryRow = (historyDoc: DocumentVersionUI, parentDoc: DocumentUI) => {
|
||||
return (
|
||||
<tr key={`history-${historyDoc.id}`} className="history-row">
|
||||
<td className="align-middle">
|
||||
<td className="align-middle px-4 py-3" style={{ width: '50px' }}>
|
||||
<input type="checkbox" disabled style={{ visibility: 'hidden' }} />
|
||||
</td>
|
||||
<td className="align-middle">
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<td className="align-middle px-4 py-3" style={{ width: '25%' }}>
|
||||
<div className="flex items-center gap-3">
|
||||
<i className="ri-history-line text-gray-400 text-lg"></i>
|
||||
<span className="history-version-label">
|
||||
<span className="history-version-label text-sm">
|
||||
v{historyDoc.versionNumber} 版本
|
||||
</span>
|
||||
<span className="history-version-label text-sm">
|
||||
{historyDoc.documentNumber}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-xs text-gray-600 px-4">{historyDoc.documentNumber}</td>
|
||||
<td className="text-xs text-gray-600 px-4">{formatFileSize(historyDoc.size)}</td>
|
||||
<td className="px-4">
|
||||
<div className={`inline-flex items-center px-2 py-1 rounded-full text-xs bg-green-100 text-green-800`}>
|
||||
<i className="ri-check-line mr-1"></i>
|
||||
<span>{fileProcessingStatusOptions.find(s => s.value === historyDoc.fileStatus)?.label || '已完成'}</span>
|
||||
</div>
|
||||
<td className="text-xs text-gray-600 px-4 py-3" style={{ width: '8%' }}>{formatFileSize(historyDoc.size)}</td>
|
||||
<td className="px-4 py-3" style={{ width: '8%' }}>
|
||||
{(() => {
|
||||
const fileStatus = historyDoc.fileStatus || "-";
|
||||
const status = fileProcessingStatusOptions.find(s => s.value === fileStatus) || fileProcessingStatusOptions[0];
|
||||
const isSpinning = fileStatus !== "Processed" && fileStatus !== "Failed";
|
||||
return (
|
||||
<div className={`inline-flex items-center px-2 py-1 rounded-full text-xs bg-${status.color}-100 text-${status.color}-800`}>
|
||||
<i className={`${status.icon} ${isSpinning ? "animate-spin" : ""} mr-1`}></i>
|
||||
<span>{status.label}</span>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
</td>
|
||||
<td className="px-4">
|
||||
<td className="px-4 py-3" style={{ width: '8%' }}>
|
||||
<div className={`inline-flex items-center px-2 py-1 rounded-full text-xs ${
|
||||
historyDoc.auditStatus === 1 ? 'bg-green-100 text-green-800' :
|
||||
historyDoc.auditStatus === -1 ? 'bg-red-100 text-red-800' :
|
||||
@@ -1042,17 +1015,21 @@ export default function DocumentsIndex() {
|
||||
<span>{auditStatusMapping[historyDoc.auditStatus]?.label || '待审核'}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4">
|
||||
<IssuesDiff
|
||||
currentIssues={historyDoc.issues}
|
||||
previousIssues={undefined}
|
||||
issuesDiff={historyDoc.issuesDiff}
|
||||
issuesDiffType={historyDoc.issuesDiffType}
|
||||
<td className="px-4 py-3" style={{ width: '15%' }}>
|
||||
<ResultStats
|
||||
passCount={historyDoc.pass_count}
|
||||
warningCount={historyDoc.warning_count}
|
||||
errorCount={historyDoc.error_count}
|
||||
manualCount={historyDoc.manual_count}
|
||||
previousPassCount={historyDoc.previous_pass_count}
|
||||
previousWarningCount={historyDoc.previous_warning_count}
|
||||
previousErrorCount={historyDoc.previous_error_count}
|
||||
previousManualCount={historyDoc.previous_manual_count}
|
||||
/>
|
||||
</td>
|
||||
<td className="text-xs text-gray-600 px-4">{historyDoc.uploadTime}</td>
|
||||
<td className="">
|
||||
<div className="operations-cell flex flex-wrap gap-1 px-4">
|
||||
<td className="text-xs text-gray-600 px-4 py-3" style={{ width: '10%' }}>{historyDoc.uploadTime}</td>
|
||||
<td className="px-4 py-3" style={{ width: '25%' }}>
|
||||
<div className="operations-cell flex flex-wrap gap-1">
|
||||
<Link
|
||||
to={`/reviews?id=${historyDoc.id}&previousRoute=documents`}
|
||||
className="text-xs px-2 py-1 h-7 mr-1 hover:underline"
|
||||
@@ -1144,6 +1121,7 @@ export default function DocumentsIndex() {
|
||||
<span className="doc-name-text font-medium text-gray-900" title={record.name}>
|
||||
{record.name}
|
||||
</span>
|
||||
<span className="document-number">{record.documentNumber}</span>
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<FileTypeTag
|
||||
type={record.type}
|
||||
@@ -1157,7 +1135,7 @@ export default function DocumentsIndex() {
|
||||
{record.isTest && (
|
||||
<span className="text-xs bg-gray-100 text-gray-500 px-1 rounded">测试</span>
|
||||
)}
|
||||
{/* 版本徽章 - 始终显示 */}
|
||||
{/* 版本徽章*/}
|
||||
{record.historyCount !== undefined && record.historyCount > 0 ?
|
||||
<span className="version-badge">
|
||||
<i className="ri-history-line"></i>
|
||||
@@ -1169,24 +1147,16 @@ export default function DocumentsIndex() {
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "文档编号",
|
||||
key: "documentNumber",
|
||||
width:'10%',
|
||||
render: (_: unknown, record: DocumentUI) => (
|
||||
<span className="document-number">{record.documentNumber}</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "文件大小",
|
||||
key: "size",
|
||||
width: "10%",
|
||||
width: "8%",
|
||||
render: (_: unknown, record: DocumentUI) => formatFileSize(record.size)
|
||||
},
|
||||
{
|
||||
title: "文件状态",
|
||||
key: "fileStatus",
|
||||
width:'10%',
|
||||
width:'8%',
|
||||
render: (_: unknown, record: DocumentUI) => {
|
||||
// 处理fileStatus为null或undefined的情况
|
||||
// console.log('fileStatus',record.fileStatus)
|
||||
@@ -1205,7 +1175,7 @@ export default function DocumentsIndex() {
|
||||
{
|
||||
title: "审核状态",
|
||||
key: "auditStatus",
|
||||
width:"10%",
|
||||
width:"8%",
|
||||
render: (_: unknown, record: DocumentUI) => {
|
||||
// 处理auditStatus为null或undefined的情况,默认为0(待审核)
|
||||
const auditStatus = record.auditStatus != null ? record.auditStatus : 0;
|
||||
@@ -1220,34 +1190,29 @@ export default function DocumentsIndex() {
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "问题数量",
|
||||
title: "结果统计",
|
||||
key: "issues",
|
||||
width:"10%",
|
||||
width:"18%",
|
||||
render: (_: unknown, record: DocumentUI) => (
|
||||
<IssuesDiff
|
||||
currentIssues={record.issues}
|
||||
previousIssues={record.previousIssues}
|
||||
issuesDiff={
|
||||
record.issues != null && record.previousIssues != null
|
||||
? Math.abs(record.issues - record.previousIssues)
|
||||
: undefined
|
||||
}
|
||||
issuesDiffType={
|
||||
record.issues != null && record.previousIssues != null
|
||||
? record.issues > record.previousIssues
|
||||
? 'increase'
|
||||
: record.issues < record.previousIssues
|
||||
? 'decrease'
|
||||
: 'same'
|
||||
: undefined
|
||||
}
|
||||
<ResultStats
|
||||
passCount={record.pass_count}
|
||||
warningCount={record.warning_count}
|
||||
errorCount={record.error_count}
|
||||
manualCount={record.manual_count}
|
||||
previousPassCount={record.previous_pass_count}
|
||||
previousWarningCount={record.previous_warning_count}
|
||||
previousErrorCount={record.previous_error_count}
|
||||
previousManualCount={record.previous_manual_count}
|
||||
warningMessages={record.warning_messages}
|
||||
errorMessages={record.error_messages}
|
||||
manualMessages={record.manual_messages}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: "上传时间",
|
||||
key: "uploadTime",
|
||||
width:"10%",
|
||||
width:"8%",
|
||||
render: (_: unknown, record: DocumentUI) => record.uploadTime
|
||||
},
|
||||
{
|
||||
@@ -1375,8 +1340,20 @@ export default function DocumentsIndex() {
|
||||
<FilterPanel
|
||||
actions={
|
||||
<>
|
||||
<Button
|
||||
type="default"
|
||||
<Button
|
||||
type="primary"
|
||||
icon="ri-search-line"
|
||||
onClick={() => {
|
||||
if (documentTypeIds) {
|
||||
fetchData(documentTypeIds);
|
||||
}
|
||||
}}
|
||||
className="mr-2"
|
||||
>
|
||||
搜索
|
||||
</Button>
|
||||
<Button
|
||||
type="default"
|
||||
icon="ri-refresh-line"
|
||||
onClick={handleReset}
|
||||
className="mr-2"
|
||||
@@ -1494,7 +1471,34 @@ export default function DocumentsIndex() {
|
||||
{documents.map((doc) => (
|
||||
<>
|
||||
{/* 主文档行 */}
|
||||
<tr key={doc.id} className="border-b hover:bg-gray-50 transition-colors">
|
||||
<tr
|
||||
key={doc.id}
|
||||
className={`border-b hover:bg-gray-50 transition-colors ${
|
||||
doc.historyCount && doc.historyCount > 0 ? 'cursor-pointer' : ''
|
||||
}`}
|
||||
onClick={(e) => {
|
||||
// 只有有历史版本的行才可以点击
|
||||
if (!doc.historyCount || doc.historyCount === 0) return;
|
||||
|
||||
// 检查点击的是否是可交互元素(链接、按钮、输入框等)
|
||||
const target = e.target as HTMLElement;
|
||||
const isInteractiveElement =
|
||||
target.tagName === 'A' ||
|
||||
target.tagName === 'BUTTON' ||
|
||||
target.tagName === 'INPUT' ||
|
||||
target.closest('a') ||
|
||||
target.closest('button') ||
|
||||
target.closest('input') ||
|
||||
target.closest('.result-stats-wrapper') ||
|
||||
target.closest('.result-stat-item');
|
||||
|
||||
// 如果点击的是可交互元素,不触发展开/收起
|
||||
if (isInteractiveElement) return;
|
||||
|
||||
// 触发展开/收起
|
||||
handleToggleExpand(doc);
|
||||
}}
|
||||
>
|
||||
{columns.map((col, index) => (
|
||||
<td key={col.key || index} className="px-4 py-3 text-sm">
|
||||
{col.render ? col.render(null, doc, index) : (doc as any)[col.key]}
|
||||
|
||||
Reference in New Issue
Block a user