fenghuo/apps/web/components/FileDownload.tsx

158 lines
4.7 KiB
TypeScript
Raw Normal View History

2025-05-28 20:00:36 +08:00
import React, { useState } from 'react';
import { useTusUpload } from '../hooks/useTusUpload';
interface FileDownloadProps {
fileId?: string;
fileName?: string;
className?: string;
}
export function FileDownload({ fileId, fileName, className }: FileDownloadProps) {
const { getFileUrlByFileId, getFileInfo } = useTusUpload();
const [inputFileId, setInputFileId] = useState(fileId || '');
const [fileInfo, setFileInfo] = useState<any>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// 获取文件信息
const handleGetFileInfo = async () => {
if (!inputFileId.trim()) {
setError('请输入文件ID');
return;
}
setLoading(true);
setError(null);
try {
const info = await getFileInfo(inputFileId);
if (info) {
setFileInfo(info);
} else {
setError('文件不存在或未准备好');
}
} catch (err) {
setError('获取文件信息失败');
} finally {
setLoading(false);
}
};
// 直接下载文件
const handleDirectDownload = () => {
const downloadUrl = getFileUrlByFileId(inputFileId);
window.open(downloadUrl, '_blank');
};
// 复制下载链接
const handleCopyLink = async () => {
const downloadUrl = getFileUrlByFileId(inputFileId);
try {
await navigator.clipboard.writeText(downloadUrl);
alert('下载链接已复制到剪贴板!');
} catch (error) {
console.error('复制失败:', error);
}
};
// 在新窗口预览文件
const handlePreview = () => {
const downloadUrl = getFileUrlByFileId(inputFileId);
window.open(downloadUrl, '_blank');
};
return (
<div className={`p-6 bg-white rounded-lg shadow-md ${className || ''}`}>
<h3 className="text-lg font-semibold mb-4"></h3>
{/* 文件ID输入 */}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-2">ID</label>
<div className="flex gap-2">
<input
type="text"
value={inputFileId}
onChange={(e) => setInputFileId(e.target.value)}
placeholder="输入文件ID"
className="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<button
onClick={handleGetFileInfo}
disabled={loading}
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50"
>
{loading ? '查询中...' : '查询'}
</button>
</div>
</div>
{/* 错误信息 */}
{error && <div className="mb-4 p-3 bg-red-100 border border-red-300 text-red-700 rounded-md">{error}</div>}
{/* 文件信息 */}
{fileInfo && (
<div className="mb-6 p-4 bg-gray-50 rounded-md">
<h4 className="font-medium mb-2"></h4>
<div className="space-y-1 text-sm">
<p>
<span className="font-medium">:</span> {fileInfo.title || '未知'}
</p>
<p>
<span className="font-medium">:</span> {fileInfo.type || '未知'}
</p>
<p>
<span className="font-medium">:</span> {fileInfo.status || '未知'}
</p>
{fileInfo.meta?.size && (
<p>
<span className="font-medium">:</span> {formatFileSize(fileInfo.meta.size)}
</p>
)}
<p>
<span className="font-medium">:</span> {new Date(fileInfo.createdAt).toLocaleString()}
</p>
</div>
</div>
)}
{/* 操作按钮 */}
{inputFileId && (
<div className="flex gap-2 flex-wrap">
<button
onClick={handleDirectDownload}
className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700"
>
</button>
<button onClick={handlePreview} className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">
/
</button>
<button onClick={handleCopyLink} className="px-4 py-2 bg-gray-600 text-white rounded-md hover:bg-gray-700">
</button>
</div>
)}
{/* 使用说明 */}
<div className="mt-6 p-4 bg-blue-50 rounded-md">
<h4 className="text-sm font-medium text-blue-900 mb-2">使</h4>
<ul className="text-xs text-blue-700 space-y-1">
<li> ID后点击"查询"</li>
<li> "直接下载"</li>
<li> "预览/查看"PDF等可预览的文件</li>
<li> "复制链接"</li>
</ul>
</div>
</div>
);
}
// 格式化文件大小
function formatFileSize(bytes: number): string {
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];
}