304 lines
13 KiB
TypeScript
Executable File
304 lines
13 KiB
TypeScript
Executable File
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 handleFileSelect = async (file: File) => {
|
||
const fileKey = `file-${Date.now()}`; // 生成唯一的文件标识
|
||
|
||
handleFileUpload(
|
||
file,
|
||
async (result) => {
|
||
setUploadedFileId(result.fileId);
|
||
setUploadedFileName(result.fileName);
|
||
|
||
|
||
// 添加到已上传文件列表
|
||
setUploadedFiles(prev => [...prev, {id: result.fileId, name: file.name}]);
|
||
|
||
// 在前端保存文件名映射(用于当前会话)
|
||
setFileNameMap(prev => ({
|
||
...prev,
|
||
[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 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' }}>
|
||
<h2>文件分享中心</h2>
|
||
|
||
<Tabs defaultActiveKey="upload">
|
||
<TabPane tab="上传分享" key="upload">
|
||
{/* 文件上传区域 */}
|
||
<div style={{ marginBottom: '40px' }}>
|
||
<h3>第一步:上传文件</h3>
|
||
<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>
|
||
|
||
{/* 已上传文件列表 */}
|
||
{uploadedFiles.length > 0 && (
|
||
<div style={{
|
||
border: '1px solid #f0f0f0',
|
||
borderRadius: '4px',
|
||
overflow: 'hidden'
|
||
}}>
|
||
{uploadedFiles.map((file, index) => (
|
||
<div key={file.id} style={{
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
padding: '10px 15px',
|
||
borderBottom: index < uploadedFiles.length - 1 ? '1px solid #f0f0f0' : 'none',
|
||
backgroundColor: index % 2 === 0 ? '#fafafa' : 'white'
|
||
}}>
|
||
<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' }} />}
|
||
/>
|
||
</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>
|
||
);
|
||
} |