feat: sync rule management and review ui fixes
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user