feat: sync rule management and review ui fixes

This commit is contained in:
wren
2026-05-07 17:27:42 +08:00
parent 87e82d1caa
commit c00e5feff0
13 changed files with 565 additions and 161 deletions
+68 -17
View File
@@ -82,6 +82,8 @@ interface DocumentListScope {
documentTypeIds: number[];
}
const LIST_SCOPE_STORAGE_KEY = 'documents.listScope';
// 审核状态筛选选项
const auditStatusOptions = [
// { value: "", label: "全部" },
@@ -257,6 +259,10 @@ export default function DocumentsIndex() {
sessionStorage.setItem(SEARCH_PARAMS_STORAGE_KEY, params.toString());
}
};
const persistListScope = useCallback((scope: DocumentListScope) => {
if (typeof window === 'undefined') return;
localStorage.setItem(LIST_SCOPE_STORAGE_KEY, JSON.stringify(scope));
}, []);
// 首次进入且 URL 无任何查询参数时,尝试从 sessionStorage 恢复
useEffect(() => {
@@ -311,7 +317,38 @@ export default function DocumentsIndex() {
}
}
return { selectedModuleId, documentTypeIds };
if (selectedModuleId || documentTypeIds.length > 0) {
const scope = { selectedModuleId, documentTypeIds };
persistListScope(scope);
return scope;
}
const storedScope = localStorage.getItem(LIST_SCOPE_STORAGE_KEY);
if (storedScope) {
try {
const parsedScope = JSON.parse(storedScope) as Partial<DocumentListScope>;
return {
selectedModuleId: Number.isFinite(Number(parsedScope.selectedModuleId)) && Number(parsedScope.selectedModuleId) > 0
? Number(parsedScope.selectedModuleId)
: null,
documentTypeIds: Array.isArray(parsedScope.documentTypeIds)
? parsedScope.documentTypeIds.map((item) => Number(item)).filter((item) => Number.isFinite(item) && item > 0)
: [],
};
} catch (error) {
console.error('解析 localStorage 文档列表作用域失败:', error);
}
}
return { selectedModuleId: null, documentTypeIds: [] };
}, [persistListScope]);
const isDeletableFileStatus = useCallback((status?: string | null) => {
return status === 'Processed' || status === 'Failed';
}, []);
const hasHistoryResultStats = useCallback((doc: DocumentVersionUI) => {
return [doc.pass_count, doc.warning_count, doc.error_count, doc.manual_count].some((value) => value !== null && value !== undefined);
}, []);
// 客户端数据请求
@@ -412,6 +449,7 @@ export default function DocumentsIndex() {
try {
const nextScope = readListScopeFromSession();
setListScope(nextScope);
persistListScope(nextScope);
setScopeReady(true);
} catch (error) {
console.error('❌ [useEffect] 初始化文档列表作用域失败:', error);
@@ -421,7 +459,7 @@ export default function DocumentsIndex() {
setScopeReady(true);
loadingBarService.hide();
}
}, [readListScopeFromSession]);
}, [persistListScope, readListScopeFromSession]);
// 监听 URL 参数变化,重新获取数据
useEffect(() => {
@@ -628,6 +666,11 @@ export default function DocumentsIndex() {
// 下载文档
const handleDownload = (path: string) => {
if (!path) {
toastService.warning('当前版本暂无可下载原文件');
return;
}
// 使用 PDF 代理路由获取文件,自动添加 JWT 认证
const downloadUrl = `/api/pdf-proxy?path=${encodeURIComponent(path)}`;
@@ -650,7 +693,7 @@ export default function DocumentsIndex() {
// 删除文档
const handleDelete = (id: string, name: string, fileStatus: string) => {
// 禁止删除处理中的文件
if (fileStatus !== "Processed" && fileStatus !== "Failed") {
if (!isDeletableFileStatus(fileStatus)) {
toastService.warning("文件正在处理中,无法删除");
return;
}
@@ -684,7 +727,7 @@ export default function DocumentsIndex() {
// 检查是否有正在处理中的文件
const hasProcessingFiles = documents.some((doc: DocumentUI) =>
selectedRowKeys.includes(doc.id.toString()) && doc.fileStatus !== 'Processed'
selectedRowKeys.includes(doc.id.toString()) && !isDeletableFileStatus(doc.fileStatus)
);
if (hasProcessingFiles) {
@@ -1149,6 +1192,10 @@ export default function DocumentsIndex() {
// 渲染历史版本行的辅助函数
const renderHistoryRow = (historyDoc: DocumentVersionUI, parentDoc: DocumentUI) => {
const canDownloadHistory = Boolean(historyDoc.path);
const canShowHistoryStats = hasHistoryResultStats(historyDoc);
const canAppendHistoryAssets = parentDoc.type === '1' && historyDoc.fileStatus === 'Processed' && canDownloadHistory;
return (
<tr key={`history-${historyDoc.id}`} className="history-row">
<td className="align-middle px-4 py-3" style={{ width: '50px' }}>
@@ -1194,16 +1241,20 @@ export default function DocumentsIndex() {
})()}
</td>
<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}
/>
{canShowHistoryStats ? (
<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}
/>
) : (
<span className="text-xs text-gray-400">-</span>
)}
</td>
<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%' }}>
@@ -1230,7 +1281,7 @@ export default function DocumentsIndex() {
</Link>
)}
{/* 下载按钮 - 需要 document:document:view 权限 */}
{canView && (
{canView && canDownloadHistory && (
<button
type="button"
className="text-xs px-2 py-1 h-7 mr-1 text-gray-500 hover:underline hover:text-gray-700"
@@ -1241,7 +1292,7 @@ export default function DocumentsIndex() {
</button>
)}
{/* 追加附件和上传模板按钮 - 需要 document:document:view 权限 */}
{canView && parentDoc.type === '1' && historyDoc.fileStatus === 'Processed' && (
{canView && canAppendHistoryAssets && (
<>
<button
type="button"
@@ -1272,7 +1323,7 @@ export default function DocumentsIndex() {
</>
)}
{/* 删除按钮 - 需要 document:document:view 权限 */}
{canView && (
{canView && isDeletableFileStatus(historyDoc.fileStatus) && (
<button
type="button"
className="text-xs px-2 py-1 h-7 text-error hover:underline hover:text-red-700"