fix: 1. 修改dockerFile

2. 修复一些合同起草的刷新报错问题
This commit is contained in:
2025-12-09 14:46:07 +08:00
parent 59c127806c
commit de923f6521
10 changed files with 251 additions and 86 deletions
+71 -25
View File
@@ -4,7 +4,8 @@
*/
import type { ActionFunctionArgs, LoaderFunctionArgs, MetaFunction } from '@remix-run/node';
import { useFetcher, useLoaderData, useNavigate } from '@remix-run/react';
import { redirect } from '@remix-run/node';
import { isRouteErrorResponse, useFetcher, useLoaderData, useNavigate, useParams, useRouteError } from '@remix-run/react';
import { useEffect, useRef, useState } from 'react';
import { downloadFile } from '~/api/axios-client';
import type { ContractTemplate } from '~/api/contract-template/templates';
@@ -56,12 +57,16 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
const templateId = url.searchParams.get('templateId');
const title = url.searchParams.get('title');
if (!filePath) {
throw new Response('文件路径参数缺失', { status: 400 });
}
// 如果参数缺失(可能是刷新导致),重定向到模板列表
if (!filePath || !templateId) {
console.log('[Loader] URL参数缺失,可能是刷新导致,重定向到模板列表');
console.log('[Loader] filePath:', filePath, 'templateId:', templateId);
if (!templateId) {
throw new Response('模板ID参数缺失', { status: 400 });
// 如果有 templateId,重定向到模板详情页;否则重定向到模板列表
if (templateId) {
return redirect(`/contract-template/detail/${templateId}`);
}
return redirect('/contract-template');
}
console.log('[Loader] 起草合同:', { filePath, templateId, title });
@@ -94,7 +99,12 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
// console.log('[Loader] 生成的 schema:', JSON.stringify(placeholderSchema, null, 2));
} catch (error) {
console.error('[Loader] 提取占位符失败:', error);
placeholderSchema = null;
console.error('[Loader] 错误类型:', error instanceof Error ? 'Error' : typeof error);
console.error('[Loader] 错误消息:', error instanceof Error ? error.message : String(error));
// 无论什么错误,都重定向回模板详情页(因为文件可能已被刷新删除)
console.log('[Loader] 发生错误,重定向到模板详情页');
return redirect(`/contract-template/detail/${templateId}`);
}
// 创建模板对象
@@ -254,20 +264,6 @@ export default function ContractDraftPage() {
}
};
const handleBeforeUnload = () => {
deleteFileSync();
};
// 监听页面卸载事件
window.addEventListener('beforeunload', handleBeforeUnload);
// 清理事件监听器
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
// 组件卸载时(路由切换),执行删除
deleteFileSync();
};
}, [draft.file_path]);
// 单个替换占位符
@@ -276,12 +272,14 @@ export default function ContractDraftPage() {
console.log(`[Draft] 单个替换: ${placeholder} -> ${value}`);
// 设置 AI 建议替换参数,触发 FilePreview 中的静默替换
// 添加唯一的时间戳,确保每次点击都会触发新的替换操作(即使值相同)
setAiSuggestionReplace({
searchText: placeholder,
replaceText: value,
pageNumber: 1, // 从第一页开始搜索
silentReplace: true // 静默替换,不显示搜索替换面板
});
silentReplace: true, // 静默替换,不显示搜索替换面板
timestamp: Date.now() // 添加时间戳,确保对象始终是新的
} as any);
// 短暂延迟后清除参数,以便下次可以重新触发
setTimeout(() => {
@@ -368,8 +366,12 @@ export default function ContractDraftPage() {
console.log('[Complete] 步骤3:下载文件');
await handleExportDocument();
// 步骤4删除 MinIO 文件
console.log('[Complete] 步骤4:删除 MinIO 文件');
// 步骤4清除会话标记
const sessionKey = `contract-draft-${draft.id}-loaded`;
sessionStorage.removeItem(sessionKey);
// 步骤5:删除 MinIO 文件
console.log('[Complete] 步骤5:删除 MinIO 文件');
const formData = new FormData();
formData.append('_action', 'deleteFile');
formData.append('filePath', draft.file_path);
@@ -393,6 +395,10 @@ export default function ContractDraftPage() {
confirmText: '确定返回',
cancelText: '取消',
onConfirm: () => {
// 清除会话标记
const sessionKey = `contract-draft-${draft.id}-loaded`;
sessionStorage.removeItem(sessionKey);
// 删除 MinIO 文件
const formData = new FormData();
formData.append('_action', 'deleteFile');
@@ -427,6 +433,13 @@ export default function ContractDraftPage() {
</div>
</div>
<div className="flex items-center gap-3">
{/* 刷新提醒 */}
<div className="flex items-center gap-2 px-3 py-2 text-sm text-yellow-700 bg-yellow-50 border border-yellow-200 rounded-lg">
<i className="ri-alert-line text-base"></i>
<span></span>
</div>
{/* 草稿状态标签 */}
<span className="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg bg-gradient-to-r from-blue-50 to-blue-100 text-blue-700 border border-blue-200 shadow-sm">
<i className="ri-draft-line text-base"></i>
<span>{draft.status === 'draft' ? '草稿' : draft.status === 'completed' ? '已完成' : '已归档'}</span>
@@ -485,3 +498,36 @@ export default function ContractDraftPage() {
</div>
);
}
/**
* 错误边界组件 - 处理页面加载错误时自动重定向
*/
export function ErrorBoundary() {
const error = useRouteError();
const params = useParams();
const navigate = useNavigate();
useEffect(() => {
console.error('[ErrorBoundary] 捕获到错误:', error);
// 自动重定向到模板详情页
const templateId = new URLSearchParams(window.location.search).get('templateId');
if (templateId) {
console.log('[ErrorBoundary] 自动重定向到模板详情页:', templateId);
// 使用 replace 避免返回到错误页面
navigate(`/contract-template/detail/${templateId}`, { replace: true });
} else {
console.log('[ErrorBoundary] 无法获取 templateId,重定向到模板列表');
navigate('/contract-template', { replace: true });
}
}, [error, navigate]);
// 显示一个简单的加载提示(用户几乎看不到,因为会立即重定向)
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="text-center">
<div className="text-gray-600">...</div>
</div>
</div>
);
}
+1 -1
View File
@@ -163,7 +163,7 @@ export default function ContractTemplateDetail() {
}, []);
const handleBack = () => {
navigate(-1);
navigate('/contract-template/list');
};
// 使用统一的下载方法(与 rules-files.tsx 相同)
+94 -8
View File
@@ -231,6 +231,10 @@ export default function DocumentsIndex() {
const [attachmentRemark, setAttachmentRemark] = useState<string>("");
const [attachmentUploading, setAttachmentUploading] = useState<boolean>(false);
const [templateUploading, setTemplateUploading] = useState<boolean>(false);
// 拖拽状态
const [isDraggingAttachment, setIsDraggingAttachment] = useState<boolean>(false);
const [isDraggingTemplate, setIsDraggingTemplate] = useState<boolean>(false);
// 查询参数记忆 key 与保存/恢复方法
const SEARCH_PARAMS_STORAGE_KEY = 'documents.searchParams';
@@ -970,7 +974,7 @@ export default function DocumentsIndex() {
try {
setTemplateUploading(true);
const result = await uploadContractTemplate(
templateFile,
selectedDocumentId,
@@ -994,7 +998,7 @@ export default function DocumentsIndex() {
if (documentTypeIds && documentTypeIds.length > 0) {
fetchData(documentTypeIds);
}
} catch (error) {
console.error('【合同模板上传】上传失败:', error);
toastService.error(error instanceof Error ? error.message : '合同模板上传失败');
@@ -1003,6 +1007,64 @@ export default function DocumentsIndex() {
}
};
// 处理附件拖拽事件
const handleAttachmentDragEnter = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDraggingAttachment(true);
};
const handleAttachmentDragOver = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
};
const handleAttachmentDragLeave = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDraggingAttachment(false);
};
const handleAttachmentDrop = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDraggingAttachment(false);
const files = e.dataTransfer.files;
if (files && files.length > 0) {
handleAttachmentFilesSelected(files);
}
};
// 处理模板拖拽事件
const handleTemplateDragEnter = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDraggingTemplate(true);
};
const handleTemplateDragOver = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
};
const handleTemplateDragLeave = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDraggingTemplate(false);
};
const handleTemplateDrop = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDraggingTemplate(false);
const files = e.dataTransfer.files;
if (files && files.length > 0) {
handleTemplateFileSelected(files);
}
};
// 展开/折叠历史版本
const handleToggleExpand = async (doc: DocumentUI) => {
const newExpanded = new Set(expandedRows);
@@ -1725,7 +1787,17 @@ export default function DocumentsIndex() {
<label className="block text-sm font-medium text-gray-700 mb-2">
<span className="text-red-500">*</span>
</label>
<div className="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center hover:border-gray-400 transition-colors">
<div
className={`border-2 border-dashed rounded-lg p-6 text-center transition-colors ${
isDraggingAttachment
? 'border-primary bg-primary-light'
: 'border-gray-300 hover:border-gray-400'
}`}
onDragEnter={handleAttachmentDragEnter}
onDragOver={handleAttachmentDragOver}
onDragLeave={handleAttachmentDragLeave}
onDrop={handleAttachmentDrop}
>
<input
type="file"
multiple
@@ -1735,8 +1807,10 @@ export default function DocumentsIndex() {
id="attachment-file-input"
/>
<label htmlFor="attachment-file-input" className="cursor-pointer">
<i className="ri-attachment-line text-3xl text-gray-400 mb-2 block"></i>
<p className="text-sm text-gray-600"></p>
<i className={`ri-attachment-line text-3xl mb-2 block ${isDraggingAttachment ? 'text-primary' : 'text-gray-400'}`}></i>
<p className={`text-sm ${isDraggingAttachment ? 'text-primary font-medium' : 'text-gray-600'}`}>
{isDraggingAttachment ? '松开鼠标上传文件' : '点击选择文件或拖拽文件到此处'}
</p>
<p className="text-xs text-gray-500 mt-1">.pdf.docx.zip.rar格式</p>
</label>
</div>
@@ -1885,7 +1959,17 @@ export default function DocumentsIndex() {
<label className="block text-sm font-medium text-gray-700 mb-2">
<span className="text-red-500">*</span>
</label>
<div className="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center hover:border-gray-400 transition-colors">
<div
className={`border-2 border-dashed rounded-lg p-6 text-center transition-colors ${
isDraggingTemplate
? 'border-primary bg-primary-light'
: 'border-gray-300 hover:border-gray-400'
}`}
onDragEnter={handleTemplateDragEnter}
onDragOver={handleTemplateDragOver}
onDragLeave={handleTemplateDragLeave}
onDrop={handleTemplateDrop}
>
<input
type="file"
accept=".pdf,.docx"
@@ -1894,8 +1978,10 @@ export default function DocumentsIndex() {
id="template-file-input"
/>
<label htmlFor="template-file-input" className="cursor-pointer">
<i className="ri-file-copy-line text-3xl text-gray-400 mb-2 block"></i>
<p className="text-sm text-gray-600"></p>
<i className={`ri-file-copy-line text-3xl mb-2 block ${isDraggingTemplate ? 'text-primary' : 'text-gray-400'}`}></i>
<p className={`text-sm ${isDraggingTemplate ? 'text-primary font-medium' : 'text-gray-600'}`}>
{isDraggingTemplate ? '松开鼠标上传文件' : '点击选择文件或拖拽文件到此处'}
</p>
<p className="text-xs text-gray-500 mt-1">.pdf.docx格式</p>
</label>
</div>