Files
leaudit-platform-frontend/app/components/ui/TemplateUploadModal.tsx
T
LiangShiyong 1658bb1c6f feat: 1. 重构交叉评查任务的文档列表的显示,对接接口查询当前任务的文档相关信息。
2.文档上传通过接口去查询是否存在同名的文件,做上传前拦截提示。
3.交叉评查的评查结果也同步添加企查查的企业信息查询模块。
4. 封装上传附件和上传模板的模态框的组件,在交叉评查的文档列表中引入显示。
5. 交叉评查的评查结果中关于合同类型的文档同步加入结构比对的功能。
2025-12-13 07:18:37 +08:00

229 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from "react";
import { messageService } from "./MessageModal";
// 格式化文件大小
const formatFileSize = (bytes: number) => {
if (bytes === 0) return "0 Bytes";
const k = 1024;
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
};
export interface TemplateUploadModalProps {
/** 是否显示 */
isOpen: boolean;
/** 关闭回调 */
onClose: () => void;
/** 目标文档ID */
documentId: number | null;
/** 目标文档名称 */
documentName: string | null;
/** 目标文档版本号(可选) */
documentVersion?: number | null;
/** 上传回调 */
onUpload: (file: File) => Promise<void>;
/** 是否正在上传 */
uploading?: boolean;
/** 标题(可选,默认"上传合同模板" */
title?: string;
/** 支持的文件格式描述(可选) */
supportedFormatsDesc?: string;
}
export function TemplateUploadModal({
isOpen,
onClose,
documentId,
documentName,
documentVersion,
onUpload,
uploading = false,
title = "上传合同模板",
supportedFormatsDesc = "支持.pdf、.docx格式,用于与合同文档进行结构对比"
}: TemplateUploadModalProps) {
// 模板文件
const [templateFile, setTemplateFile] = useState<File | null>(null);
// 拖拽状态
const [isDragging, setIsDragging] = useState<boolean>(false);
// 重置状态
const resetState = () => {
setTemplateFile(null);
setIsDragging(false);
};
// 关闭处理
const handleClose = () => {
resetState();
onClose();
};
// 处理文件选择
const handleFileSelected = (files: FileList) => {
try {
if (files.length > 0) {
const file = files[0];
// 验证文件类型
const fileName = file.name.toLowerCase();
const isValidType =
file.type === 'application/pdf' || fileName.endsWith('.pdf') ||
file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' || fileName.endsWith('.docx');
if (isValidType) {
setTemplateFile(file);
} else {
messageService.error('只支持.pdf、.docx格式的文件', {
title: '文件类型错误',
confirmText: '确定',
cancelText: '',
});
}
}
} catch (error) {
console.error('处理文件选择时发生错误:', error);
}
};
// 处理上传
const handleUpload = async () => {
if (!documentId || !templateFile) {
return;
}
await onUpload(templateFile);
resetState();
};
// 拖拽事件处理
const handleDragEnter = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(true);
};
const handleDragOver = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
};
const handleDragLeave = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
};
const handleDrop = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
const files = e.dataTransfer.files;
if (files && files.length > 0) {
handleFileSelected(files);
}
};
if (!isOpen) return null;
return (
<div
className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-[9999]"
onClick={handleClose}
>
<div
className="bg-white rounded-lg p-6 w-full max-w-xl mx-4"
onClick={(e) => e.stopPropagation()}
>
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-semibold">{title}</h3>
<button
onClick={handleClose}
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">
: <span className="font-medium">{documentName}</span>
{documentVersion && (
<span className="ml-2 text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">
v{documentVersion}
</span>
)}
</p>
<p className="text-xs text-gray-500 mt-1">
{supportedFormatsDesc}
</p>
</div>
{/* 文件上传区域 */}
<div>
<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 rounded-lg p-6 text-center transition-colors ${
isDragging
? 'border-primary bg-primary-light'
: 'border-gray-300 hover:border-gray-400'
}`}
onDragEnter={handleDragEnter}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
>
<input
type="file"
accept=".pdf,.docx"
onChange={(e) => e.target.files && handleFileSelected(e.target.files)}
className="hidden"
id="template-file-input-modal"
/>
<label htmlFor="template-file-input-modal" className="cursor-pointer">
<i className={`ri-file-copy-line text-3xl mb-2 block ${isDragging ? 'text-primary' : 'text-gray-400'}`}></i>
<p className={`text-sm ${isDragging ? 'text-primary font-medium' : 'text-gray-600'}`}>
{isDragging ? '松开鼠标上传文件' : '点击选择文件或拖拽文件到此处'}
</p>
<p className="text-xs text-gray-500 mt-1">.pdf.docx格式</p>
</label>
</div>
{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
className="px-4 py-2 text-sm border border-gray-300 rounded-md hover:bg-gray-50"
onClick={handleClose}
disabled={uploading}
>
</button>
<button
className="px-4 py-2 text-sm bg-primary text-white rounded-md hover:bg-primary-dark disabled:opacity-50"
onClick={handleUpload}
disabled={!templateFile || uploading}
>
{uploading ? '上传中...' : '开始上传'}
</button>
</div>
</div>
</div>
</div>
);
}