2025-03-21 10:55:44 +08:00
|
|
|
|
import { useTusUpload } from "@web/src/hooks/useTusUpload";
|
|
|
|
|
import { ShareCodeGenerator } from "../sharecode/sharecodegenerator";
|
|
|
|
|
import { ShareCodeValidator } from "../sharecode/sharecodevalidator";
|
|
|
|
|
import { useState } from "react";
|
|
|
|
|
import { message, Progress, Button, Tabs } from "antd";
|
|
|
|
|
import { UploadOutlined } from "@ant-design/icons";
|
|
|
|
|
|
|
|
|
|
const { TabPane } = Tabs;
|
|
|
|
|
|
2025-03-20 23:09:41 +08:00
|
|
|
|
export default function DeptSettingPage() {
|
2025-03-21 10:55:44 +08:00
|
|
|
|
const [uploadedFileId, setUploadedFileId] = useState<string>('');
|
2025-03-21 17:25:39 +08:00
|
|
|
|
const [fileNameMap, setFileNameMap] = useState<Record<string, string>>({});
|
2025-03-21 10:55:44 +08:00
|
|
|
|
|
|
|
|
|
// 使用您的 useTusUpload hook
|
|
|
|
|
const { uploadProgress, isUploading, uploadError, handleFileUpload } = useTusUpload({
|
|
|
|
|
onSuccess: (fileId: string) => {
|
|
|
|
|
setUploadedFileId(fileId);
|
|
|
|
|
message.success('文件上传成功');
|
|
|
|
|
},
|
|
|
|
|
onError: (error: Error) => {
|
|
|
|
|
message.error('上传失败:' + error.message);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 处理文件上传
|
|
|
|
|
const handleFileSelect = async (file: File) => {
|
|
|
|
|
const fileKey = `file-${Date.now()}`; // 生成唯一的文件标识
|
2025-03-21 17:25:39 +08:00
|
|
|
|
|
2025-03-21 10:55:44 +08:00
|
|
|
|
handleFileUpload(
|
|
|
|
|
file,
|
2025-03-21 17:25:39 +08:00
|
|
|
|
async (result) => {
|
2025-03-21 10:55:44 +08:00
|
|
|
|
setUploadedFileId(result.fileId);
|
2025-03-21 17:25:39 +08:00
|
|
|
|
|
|
|
|
|
// 在前端保存文件名映射(用于当前会话)
|
|
|
|
|
setFileNameMap(prev => ({
|
|
|
|
|
...prev,
|
|
|
|
|
[result.fileId]: file.name
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// 上传成功后保存原始文件名到数据库
|
|
|
|
|
try {
|
|
|
|
|
console.log('正在保存文件名到数据库:', file.name, '对应文件ID:', result.fileId);
|
|
|
|
|
|
|
|
|
|
const response = await fetch('/api/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('文件名保存失败,下载时可能无法显示原始文件名');
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-21 10:55:44 +08:00
|
|
|
|
message.success('文件上传成功');
|
|
|
|
|
},
|
|
|
|
|
(error) => {
|
|
|
|
|
message.error('上传失败:' + error.message);
|
|
|
|
|
},
|
|
|
|
|
fileKey
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 处理分享码生成成功
|
|
|
|
|
const handleShareSuccess = (code: string) => {
|
|
|
|
|
message.success('分享码生成成功:' + code);
|
|
|
|
|
// 可以在这里添加其他逻辑,比如保存到历史记录
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 处理分享码验证成功
|
|
|
|
|
const handleValidSuccess = async (fileId: string) => {
|
|
|
|
|
try {
|
|
|
|
|
// 构建下载URL
|
|
|
|
|
const response = await fetch(`/api/upload/download/${fileId}`);
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw new Error('文件下载失败');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取文件名
|
|
|
|
|
const contentDisposition = response.headers.get('content-disposition');
|
2025-03-21 17:25:39 +08:00
|
|
|
|
console.log('Content-Disposition 头:', contentDisposition);
|
|
|
|
|
|
|
|
|
|
let filename = fileNameMap[fileId] || 'downloaded-file'; // 优先使用本地缓存的文件名
|
|
|
|
|
|
2025-03-21 10:55:44 +08:00
|
|
|
|
if (contentDisposition) {
|
2025-03-21 17:25:39 +08:00
|
|
|
|
// 改进文件名提取逻辑
|
|
|
|
|
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
|
|
|
|
|
const matches = filenameRegex.exec(contentDisposition);
|
|
|
|
|
if (matches && matches[1]) {
|
|
|
|
|
// 移除引号并解码 URL 编码的文件名
|
2025-03-21 10:55:44 +08:00
|
|
|
|
filename = matches[1].replace(/['"]/g, '');
|
2025-03-21 17:25:39 +08:00
|
|
|
|
try {
|
|
|
|
|
// 尝试解码 URL 编码的文件名
|
|
|
|
|
filename = decodeURIComponent(filename);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.warn('文件名解码失败,使用原始文件名');
|
|
|
|
|
}
|
2025-03-21 10:55:44 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-21 17:25:39 +08:00
|
|
|
|
console.log('提取的文件名:', filename);
|
|
|
|
|
|
2025-03-21 10:55:44 +08:00
|
|
|
|
// 创建下载链接
|
|
|
|
|
const blob = await response.blob();
|
|
|
|
|
const url = window.URL.createObjectURL(blob);
|
|
|
|
|
const link = document.createElement('a');
|
|
|
|
|
link.href = url;
|
2025-03-21 17:25:39 +08:00
|
|
|
|
|
|
|
|
|
// 使用获取到的文件名或本地缓存的文件名
|
2025-03-21 10:55:44 +08:00
|
|
|
|
link.download = filename;
|
2025-03-21 17:25:39 +08:00
|
|
|
|
|
|
|
|
|
// 触发下载
|
2025-03-21 10:55:44 +08:00
|
|
|
|
document.body.appendChild(link);
|
|
|
|
|
link.click();
|
|
|
|
|
document.body.removeChild(link);
|
|
|
|
|
window.URL.revokeObjectURL(url);
|
|
|
|
|
|
|
|
|
|
message.success('文件下载开始');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('下载失败:', error);
|
|
|
|
|
message.error('文件下载失败');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-03-20 23:09:41 +08:00
|
|
|
|
return (
|
2025-03-21 10:55:44 +08:00
|
|
|
|
<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 style={{
|
|
|
|
|
padding: '20px',
|
|
|
|
|
border: '2px dashed #d9d9d9',
|
|
|
|
|
borderRadius: '8px',
|
|
|
|
|
textAlign: 'center'
|
|
|
|
|
}}>
|
|
|
|
|
<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: '12px 24px',
|
|
|
|
|
backgroundColor: '#1890ff',
|
|
|
|
|
color: 'white',
|
|
|
|
|
borderRadius: '6px',
|
|
|
|
|
cursor: 'pointer',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<UploadOutlined /> 选择文件
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
{isUploading && (
|
|
|
|
|
<div style={{ marginTop: '20px' }}>
|
|
|
|
|
<Progress
|
|
|
|
|
percent={Object.values(uploadProgress)[0] || 0}
|
|
|
|
|
status="active"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{uploadError && (
|
|
|
|
|
<div style={{ color: '#ff4d4f', marginTop: '10px' }}>
|
|
|
|
|
{uploadError}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</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>
|
2025-03-20 23:09:41 +08:00
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|