新增合同模板上传功能,支持选择PDF和Word格式文件,并实现上传逻辑及状态管理。

This commit is contained in:
2025-09-11 17:25:58 +08:00
parent c611d6621d
commit a5ca3a8261
3 changed files with 618 additions and 11 deletions
+79
View File
@@ -136,6 +136,85 @@ export async function uploadFileToBinary(file: File): Promise<ArrayBuffer> {
* @param jwtToken JWT token
* @returns 上传结果
*/
/**
* 上传合同模板(用于与合同文档结构对比)
* @param file 模板文件
* @param documentId 源合同文档ID
* @param comparisonId 已有对比记录ID(可选)
* @param jwtToken JWT token
* @returns 上传结果
*/
export async function uploadContractTemplate(
file: File,
documentId: number,
comparisonId?: number,
jwtToken?: string
): Promise<{data: FileUploadResponse; error?: never} | {data?: never; error: string; status?: number}> {
try {
console.log('【合同模板上传】开始上传模板:', { fileName: file.name, documentId, comparisonId });
// 创建FormData对象
const formData = new FormData();
// 添加文件
formData.append('file', file);
// 添加上传信息
const uploadInfo = {
document_id: documentId,
...(comparisonId && { comparison_id: comparisonId })
};
formData.append('upload_info', JSON.stringify(uploadInfo));
// 构建请求URL
const uploadUrl = `${UPLOAD_URL}/upload_contract_template`;
console.log('【合同模板上传】准备发送请求到服务器:', uploadUrl);
// 设置请求头
const headers: HeadersInit = {
'Accept': 'application/json'
};
if (jwtToken) {
headers['Authorization'] = `Bearer ${jwtToken}`;
}
// 发送请求
const response = await fetch(uploadUrl, {
method: 'POST',
headers,
body: formData
});
console.log('【合同模板上传】服务器响应状态:', response.status);
if (!response.ok) {
const errorText = await response.text();
console.error('【合同模板上传】服务器返回错误:', errorText);
return {
error: `服务器错误: ${response.status} ${response.statusText}`,
status: response.status
};
}
const result = await response.json();
console.log('【合同模板上传】服务器返回结果:', result);
if (result.success) {
return { data: result.result };
} else {
return { error: result.error || '合同模板上传失败' };
}
} catch (error) {
console.error('【合同模板上传】上传过程中发生错误:', error);
return {
error: error instanceof Error ? error.message : '合同模板上传过程中发生未知错误'
};
}
}
/**
* 合同文档追加附件并合并
* @param documentId 合同文档ID
+182 -11
View File
@@ -17,6 +17,7 @@ import {
uploadFileToBinary,
uploadDocumentToServer,
appendContractAttachments,
uploadContractTemplate,
type Document,
type DocumentType,
type FileUploadResponse,
@@ -313,6 +314,11 @@ export default function FilesUpload() {
const [attachmentMergeMode, setAttachmentMergeMode] = useState<'overwrite' | 'new'>('overwrite');
const [attachmentRemark, setAttachmentRemark] = useState<string>("");
const [attachmentUploading, setAttachmentUploading] = useState<boolean>(false);
// 合同模板上传状态
const [showTemplateUpload, setShowTemplateUpload] = useState<boolean>(false);
const [templateFile, setTemplateFile] = useState<File | null>(null);
const [templateUploading, setTemplateUploading] = useState<boolean>(false);
const [uploadProgress, setUploadProgress] = useState(0);
const [uploadSpeed, setUploadSpeed] = useState("0KB/s");
@@ -864,6 +870,75 @@ export default function FilesUpload() {
setAttachmentUploading(false);
}
};
// 处理合同模板文件选择
const handleTemplateFileSelected = (files: FileList) => {
try {
console.log('【合同模板上传】开始处理模板文件选择, 文件数量:', files.length);
if (files.length > 0) {
const file = files[0];
// 验证文件类型,支持PDF和Word
const fileName = file.name.toLowerCase();
const isValidType =
file.type === 'application/pdf' || fileName.endsWith('.pdf') ||
file.type === 'application/msword' || fileName.endsWith('.doc') ||
file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' || fileName.endsWith('.docx');
if (isValidType) {
setTemplateFile(file);
console.log('【合同模板上传】有效文件:', file.name);
} else {
messageService.error('只支持PDF、Word格式的文件', {
title: '文件类型错误',
confirmText: '确定',
cancelText: '',
});
}
}
} catch (error) {
console.error('【合同模板上传】处理文件选择时发生错误:', error);
}
};
// 处理合同模板上传
const handleTemplateUpload = async () => {
if (!selectedDocumentId || !templateFile) {
toastService.error('请选择文档和模板文件');
return;
}
try {
setTemplateUploading(true);
const result = await uploadContractTemplate(
templateFile,
selectedDocumentId,
undefined, // comparisonId
loaderData.userInfo?.token
);
if (result.error) {
throw new Error(result.error);
}
toastService.success('合同模板上传成功!');
// 重置状态
setTemplateFile(null);
setShowTemplateUpload(false);
setSelectedDocumentId(null);
// 刷新文档列表
await filterDocuments(reviewType);
} catch (error) {
console.error('【合同模板上传】上传失败:', error);
toastService.error(error instanceof Error ? error.message : '合同模板上传失败');
} finally {
setTemplateUploading(false);
}
};
// 检查并准备上传
const checkAndPrepareUpload = (mainFiles: File[], attachmentFiles: File[]) => {
@@ -1785,17 +1860,30 @@ export default function FilesUpload() {
</Button>
{record.type_id === 1 && record.status === DocumentStatus.PROCESSED && (
<Button
type="primary"
size="small"
icon="ri-attachment-line"
onClick={() => {
setSelectedDocumentId(record.id);
setShowAttachmentUpload(true);
}}
>
</Button>
<>
<Button
type="primary"
size="small"
icon="ri-attachment-line"
onClick={() => {
setSelectedDocumentId(record.id);
setShowAttachmentUpload(true);
}}
>
</Button>
<Button
type="default"
size="small"
icon="ri-file-copy-line"
onClick={() => {
setSelectedDocumentId(record.id);
setShowTemplateUpload(true);
}}
>
</Button>
</>
)}
</div>
)
@@ -2310,6 +2398,89 @@ export default function FilesUpload() {
</div>
</div>
)}
{/* 合同模板上传模态框 */}
{showTemplateUpload && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg p-6 w-full max-w-lg mx-4">
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-semibold"></h3>
<button
onClick={() => {
setShowTemplateUpload(false);
setSelectedDocumentId(null);
setTemplateFile(null);
}}
className="text-gray-400 hover:text-gray-600"
>
<i className="ri-close-line text-xl"></i>
</button>
</div>
<div className="space-y-4">
{/* 文档信息 */}
<div className="bg-gray-50 p-3 rounded">
<p className="text-sm text-gray-600">
ID: <span className="font-medium">{selectedDocumentId}</span>
</p>
<p className="text-xs text-gray-500 mt-1">
PDFWord格式
</p>
</div>
{/* 文件上传区域 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
<span className="text-red-500">*</span>
</label>
<UploadArea
onFilesSelected={handleTemplateFileSelected}
multiple={false}
accept=".pdf,.doc,.docx"
tipText="支持PDF、Word格式"
mainText="选择模板文件"
buttonText="选择文件"
icon="ri-file-copy-line"
/>
{templateFile && (
<div className="mt-2">
<p className="text-sm text-green-600 mb-2">
<i className="ri-checkbox-circle-line"></i>
</p>
<div className="text-xs text-gray-600 bg-gray-50 p-2 rounded">
<i className="ri-file-line mr-1"></i>
{templateFile.name} ({formatFileSize(templateFile.size)})
</div>
</div>
)}
</div>
{/* 操作按钮 */}
<div className="flex justify-end gap-3 pt-4 border-t">
<Button
type="default"
onClick={() => {
setShowTemplateUpload(false);
setSelectedDocumentId(null);
setTemplateFile(null);
}}
disabled={templateUploading}
>
</Button>
<Button
type="primary"
onClick={handleTemplateUpload}
disabled={!templateFile || templateUploading}
icon={templateUploading ? "ri-loader-4-line" : "ri-upload-cloud-line"}
>
{templateUploading ? '上传中...' : '开始上传'}
</Button>
</div>
</div>
</div>
</div>
)}
</div>
);
}