collect-system/apps/web/src/app/admin/deptsettingpage/page.tsx

357 lines
15 KiB
TypeScript
Raw Normal View History

2025-04-02 16:14:30 +08:00
import { useTusUpload } from "@web/src/hooks/useTusUpload";
import { ShareCodeGenerator } from "../sharecode/sharecodegenerator";
import { ShareCodeValidator } from "../sharecode/sharecodevalidator";
import { useState, useRef, useCallback } from "react";
import { message, Progress, Button, Tabs, DatePicker } from "antd";
import { UploadOutlined, DeleteOutlined, InboxOutlined } from "@ant-design/icons";
import { env } from '../../../env'
const { TabPane } = Tabs;
export default function DeptSettingPage() {
const [uploadedFileId, setUploadedFileId] = useState<string>('');
const [uploadedFileName, setUploadedFileName] = useState<string>('');
const [fileNameMap, setFileNameMap] = useState<Record<string, string>>({});
const [uploadedFiles, setUploadedFiles] = useState<{ id: string, name: string }[]>([]);
const [isDragging, setIsDragging] = useState(false);
const [expireTime, setExpireTime] = useState<Date | null>(null);
const dropRef = useRef<HTMLDivElement>(null);
// 使用您的 useTusUpload hook
const { uploadProgress, isUploading, uploadError, handleFileUpload } = useTusUpload({
onSuccess: (result) => {
setUploadedFileId(result.fileId);
setUploadedFileName(result.fileName);
message.success('文件上传成功');
},
onError: (error: Error) => {
message.error('上传失败:' + error.message);
}
});
// 清除已上传文件
const handleClearFile = () => {
setUploadedFileId('');
setUploadedFileName('');
setUploadedFiles([]);
setFileNameMap({});
};
// 处理文件上传
const handleFileSelect = async (file: File) => {
// 限制:如果已有上传文件,则提示用户
if (uploadedFiles.length > 0) {
message.warning('只能上传一个文件,请先删除已上传的文件');
return;
}
const fileKey = `file-${Date.now()}`; // 生成唯一的文件标识
handleFileUpload(
file,
async (result) => {
setUploadedFileId(result.fileId);
setUploadedFileName(result.fileName);
// 添加到已上传文件列表
setUploadedFiles([{ id: result.fileId, name: file.name }]);
// 在前端保存文件名映射(用于当前会话)
setFileNameMap({
[result.fileId]: file.name
});
// 上传成功后保存原始文件名到数据库
try {
console.log('正在保存文件名到数据库:', result.fileName, '对应文件ID:', result.fileId);
const response = await fetch(`http://${env.SERVER_IP}:${env.SERVER_PORT}/upload/filename`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileId: result.fileId,
fileName: file.name
}),
});
const responseText = await response.text();
console.log('保存文件名响应:', response.status, responseText);
if (!response.ok) {
console.error('保存文件名失败:', responseText);
message.warning('文件名保存失败,下载时可能无法显示原始文件名');
} else {
console.log('文件名保存成功:', file.name);
}
} catch (error) {
console.error('保存文件名请求失败:', error);
message.warning('文件名保存失败,下载时可能无法显示原始文件名');
}
message.success('文件上传成功');
},
(error) => {
message.error('上传失败:' + error.message);
},
fileKey
);
};
// 处理多个文件上传 - 已移除
// const handleFilesUpload = (file: File) => {
// handleFileSelect(file);
// };
// 处理文件删除
const handleDeleteFile = async (fileId: string) => {
try {
// 可以添加删除文件的API调用
// const response = await fetch(`http://${env.SERVER_IP}:${env.SERVER_PORT}/upload/delete/${fileId}`, {
// method: 'DELETE'
// });
// if (!response.ok) {
// throw new Error('删除文件失败');
// }
// 无论服务器删除是否成功,前端都需要更新状态
setUploadedFiles([]);
setUploadedFileId('');
setUploadedFileName('');
setFileNameMap({});
message.success('文件已删除');
} catch (error) {
console.error('删除文件错误:', error);
message.error('删除文件失败');
}
};
// 拖拽相关处理函数
const handleDragEnter = useCallback((e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(true);
}, []);
const handleDragLeave = useCallback((e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
}, []);
const handleDragOver = useCallback((e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(true);
}, []);
const handleDrop = useCallback((e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
handleFileSelect(e.dataTransfer.files[0]);
}, []);
// 处理分享码生成成功
const handleShareSuccess = (code: string) => {
message.success('分享码生成成功:' + code);
// 可以在这里添加其他逻辑,比如保存到历史记录
};
// 处理分享码验证成功
const handleValidSuccess = async (fileId: string, fileName: string) => {
try {
// 构建下载URL包含文件名参数
const downloadUrl = `/upload/download/${fileId}?fileName=${encodeURIComponent(fileName)}`;
const response = await fetch(downloadUrl);
if (!response.ok) {
throw new Error('文件下载失败');
}
// 创建下载链接
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
// 直接使用传入的 fileName
link.download = fileName;
// 触发下载
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
message.success('文件下载开始');
} catch (error) {
console.error('下载失败:', error);
message.error('文件下载失败');
}
};
return (
<div style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
<span className="text-2xl py-4"></span>
<Tabs defaultActiveKey="upload">
<TabPane tab="上传分享" key="upload">
{/* 文件上传区域 */}
<div style={{ marginBottom: '40px' }}>
<h3></h3>
{/* 如果没有已上传文件,显示上传区域 */}
{uploadedFiles.length === 0 ? (
<div
ref={dropRef}
onDragEnter={handleDragEnter}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
style={{
padding: '20px',
border: `2px dashed ${isDragging ? '#1890ff' : '#d9d9d9'}`,
borderRadius: '8px',
textAlign: 'center',
backgroundColor: isDragging ? 'rgba(24, 144, 255, 0.05)' : 'transparent',
transition: 'all 0.3s',
marginBottom: '20px'
}}
>
<InboxOutlined style={{ fontSize: '48px', color: isDragging ? '#1890ff' : '#d9d9d9' }} />
<p></p>
<p style={{ fontSize: '12px', color: '#888' }}></p>
<input
type="file"
id="file-input"
style={{ display: 'none' }}
onChange={(e) => {
const file = e.target.files?.[0];
if (file) {
handleFileSelect(file);
}
}}
disabled={isUploading}
/>
<label
htmlFor="file-input"
style={{
display: 'inline-block',
padding: '8px 16px',
backgroundColor: '#1890ff',
color: 'white',
borderRadius: '4px',
cursor: 'pointer',
marginTop: '10px'
}}
>
<UploadOutlined />
</label>
</div>
) : (
<div style={{ marginBottom: '20px' }}>
<div style={{
padding: '10px',
backgroundColor: '#f6ffed',
border: '1px solid #b7eb8f',
borderRadius: '4px',
marginBottom: '10px'
}}>
<p style={{ color: '#52c41a', margin: 0 }}>
</p>
</div>
</div>
)}
{/* 已上传文件列表 */}
{uploadedFiles.length > 0 && (
<div style={{
border: '1px solid #f0f0f0',
borderRadius: '4px',
overflow: 'hidden'
}}>
{uploadedFiles.map((file) => (
<div key={file.id} style={{
display: 'flex',
alignItems: 'center',
padding: '10px 15px',
backgroundColor: '#fafafa'
}}>
<div style={{
display: 'flex',
alignItems: 'center',
flex: 1
}}>
<div style={{
width: '20px',
height: '20px',
borderRadius: '50%',
backgroundColor: '#52c41a',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginRight: '10px'
}}>
<span style={{ color: 'white', fontSize: '12px' }}></span>
</div>
<span>{file.name}</span>
</div>
<Button
type="text"
icon={<DeleteOutlined style={{ color: '#ff4d4f' }} />}
onClick={() => handleDeleteFile(file.id)}
title="删除此文件"
/>
</div>
))}
</div>
)}
{isUploading && (
<div style={{ marginTop: '20px' }}>
<Progress
percent={Math.round(Object.values(uploadProgress)[0] || 0)}
status="active"
/>
</div>
)}
{uploadError && (
<div style={{ color: '#ff4d4f', marginTop: '10px' }}>
{uploadError}
</div>
)}
</div>
{/* 生成分享码区域 */}
{uploadedFileId && (
<div style={{ marginBottom: '40px' }}>
<h3></h3>
<ShareCodeGenerator
fileId={uploadedFileId}
onSuccess={handleShareSuccess}
/>
</div>
)}
</TabPane>
{/* 使用分享码区域 */}
<TabPane tab="下载文件" key="download">
<div>
<h3>使</h3>
<ShareCodeValidator
onValidSuccess={handleValidSuccess}
/>
</div>
</TabPane>
</Tabs>
</div>
);
}