feat(data-annotation): 扩展文件预览功能支持多媒体格式

- 添加对图片、视频、音频文件的预览支持
- 新增文件类型检测逻辑,支持多种媒体格式扩展名
- 实现不同文件类型的预览界面适配
- 更新预览弹窗以支持文本、图像、音视频内容展示
- 调整预览窗口尺寸以优化不同媒体类型显示效果
- 完善预览组件卸载时的状态清理机制
This commit is contained in:
2026-01-19 19:38:57 +08:00
parent 7a73322858
commit 21cc505f97

View File

@@ -48,6 +48,8 @@ export default function CreateAnnotationTask({
const [fileContent, setFileContent] = useState("");
const [fileContentLoading, setFileContentLoading] = useState(false);
const [previewFileName, setPreviewFileName] = useState("");
const [previewFileType, setPreviewFileType] = useState<"text" | "image" | "video" | "audio">("text");
const [previewMediaUrl, setPreviewMediaUrl] = useState("");
useEffect(() => {
if (!open) return;
@@ -121,30 +123,57 @@ export default function CreateAnnotationTask({
// 预览文件内容
const handlePreviewFileContent = async (file: any) => {
// 支持预览的文本文件类型
const textExtensions = ['.json', '.jsonl', '.txt', '.csv', '.tsv', '.xml', '.md', '.yaml', '.yml'];
const fileName = file.fileName?.toLowerCase() || '';
const isTextFile = textExtensions.some(ext => fileName.endsWith(ext));
if (!isTextFile) {
message.warning("仅支持预览文本类文件(JSON、JSONL、TXT、CSV 等)");
// 文件类型扩展名映射
const textExtensions = ['.json', '.jsonl', '.txt', '.csv', '.tsv', '.xml', '.md', '.yaml', '.yml'];
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg'];
const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi'];
const audioExtensions = ['.mp3', '.wav', '.ogg', '.aac', '.flac', '.m4a'];
const isTextFile = textExtensions.some(ext => fileName.endsWith(ext));
const isImageFile = imageExtensions.some(ext => fileName.endsWith(ext));
const isVideoFile = videoExtensions.some(ext => fileName.endsWith(ext));
const isAudioFile = audioExtensions.some(ext => fileName.endsWith(ext));
if (!isTextFile && !isImageFile && !isVideoFile && !isAudioFile) {
message.warning("不支持预览该文件类型");
return;
}
setFileContentLoading(true);
setPreviewFileName(file.fileName);
const fileUrl = `/api/data-management/datasets/${selectedDatasetId}/files/${file.id}/download`;
try {
const response = await fetch(`/api/data-management/datasets/${selectedDatasetId}/files/${file.id}/download`);
if (!response.ok) {
throw new Error('下载失败');
}
const text = await response.text();
// 限制预览内容长度,避免大文件导致页面卡顿
const maxLength = 50000;
if (text.length > maxLength) {
setFileContent(text.substring(0, maxLength) + '\n\n... (内容过长,仅显示前 50000 字符)');
} else {
setFileContent(text);
if (isTextFile) {
// 文本文件:获取内容
const response = await fetch(fileUrl);
if (!response.ok) {
throw new Error('下载失败');
}
const text = await response.text();
// 限制预览内容长度
const maxLength = 50000;
if (text.length > maxLength) {
setFileContent(text.substring(0, maxLength) + '\n\n... (内容过长,仅显示前 50000 字符)');
} else {
setFileContent(text);
}
setPreviewFileType("text");
} else if (isImageFile) {
// 图片文件:直接使用 URL
setPreviewMediaUrl(fileUrl);
setPreviewFileType("image");
} else if (isVideoFile) {
// 视频文件:使用 URL
setPreviewMediaUrl(fileUrl);
setPreviewFileType("video");
} else if (isAudioFile) {
// 音频文件:使用 URL
setPreviewMediaUrl(fileUrl);
setPreviewFileType("audio");
}
setFileContentVisible(true);
} catch (error) {
@@ -574,7 +603,7 @@ export default function CreateAnnotationTask({
</Button>
]}
>
<div className="mb-2 text-xs text-gray-500"></div>
<div className="mb-2 text-xs text-gray-500"></div>
<Table
dataSource={datasetPreviewData}
columns={[
@@ -618,29 +647,66 @@ export default function CreateAnnotationTask({
{/* 文件内容预览弹窗 */}
<Modal
open={fileContentVisible}
onCancel={() => setFileContentVisible(false)}
onCancel={() => {
setFileContentVisible(false);
setPreviewMediaUrl("");
setFileContent("");
}}
title={`文件预览:${previewFileName}`}
width={800}
width={previewFileType === "text" ? 800 : 700}
footer={[
<Button key="close" onClick={() => setFileContentVisible(false)}>
<Button key="close" onClick={() => {
setFileContentVisible(false);
setPreviewMediaUrl("");
setFileContent("");
}}>
</Button>
]}
>
<pre
style={{
maxHeight: '500px',
overflow: 'auto',
backgroundColor: '#f5f5f5',
padding: '12px',
borderRadius: '4px',
fontSize: '12px',
whiteSpace: 'pre-wrap',
wordBreak: 'break-all',
}}
>
{fileContent}
</pre>
{previewFileType === "text" && (
<pre
style={{
maxHeight: '500px',
overflow: 'auto',
backgroundColor: '#f5f5f5',
padding: '12px',
borderRadius: '4px',
fontSize: '12px',
whiteSpace: 'pre-wrap',
wordBreak: 'break-all',
}}
>
{fileContent}
</pre>
)}
{previewFileType === "image" && (
<div style={{ textAlign: 'center' }}>
<img
src={previewMediaUrl}
alt={previewFileName}
style={{ maxWidth: '100%', maxHeight: '500px', objectFit: 'contain' }}
/>
</div>
)}
{previewFileType === "video" && (
<div style={{ textAlign: 'center' }}>
<video
src={previewMediaUrl}
controls
style={{ maxWidth: '100%', maxHeight: '500px' }}
>
</video>
</div>
)}
{previewFileType === "audio" && (
<div style={{ textAlign: 'center', padding: '40px 0' }}>
<audio src={previewMediaUrl} controls style={{ width: '100%' }}>
</audio>
</div>
)}
</Modal>
</>
);