diff --git a/web/src/components/DocumentPreview/index.tsx b/web/src/components/DocumentPreview/index.tsx index 8ab67be1..02345d13 100644 --- a/web/src/components/DocumentPreview/index.tsx +++ b/web/src/components/DocumentPreview/index.tsx @@ -4,10 +4,10 @@ * @Author: yujiangping * @Date: 2026-03-16 19:01:12 * @LastEditors: yujiangping - * @LastEditTime: 2026-03-16 19:17:47 + * @LastEditTime: 2026-03-17 16:19:45 */ import { useState, useEffect, useRef, useCallback, type FC } from 'react'; -import { Spin, Alert, Button, Table, InputNumber } from 'antd'; +import { Spin, Alert, Button, Table, InputNumber, Image } from 'antd'; import { ReloadOutlined, DownloadOutlined, @@ -21,12 +21,10 @@ import { cookieUtils } from '@/utils/request'; import mammoth from 'mammoth'; import * as XLSX from 'xlsx'; import * as pdfjsLib from 'pdfjs-dist'; +import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.mjs?url'; // 设置 pdf.js worker -pdfjsLib.GlobalWorkerOptions.workerSrc = new URL( - 'pdfjs-dist/build/pdf.worker.mjs', - import.meta.url, -).toString(); +pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker; interface DocumentPreviewProps { fileUrl: string; @@ -65,9 +63,12 @@ const DocumentPreview: FC = ({ const [pptCurrentPage, setPptCurrentPage] = useState(1); const [pptTotalPages, setPptTotalPages] = useState(0); + // 图片状态 + const [imageBlobUrl, setImageBlobUrl] = useState(''); + // 支持预览的文件类型 const previewableTypes = [ - '.pdf', '.txt', '.md', + '.pdf', '.txt', '.md', '.csv', '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', @@ -90,7 +91,7 @@ const DocumentPreview: FC = ({ }; const isPdfFile = () => getFileExtension() === '.pdf'; const isWordFile = () => ['.doc', '.docx'].includes(getFileExtension()); - const isExcelFile = () => ['.xls', '.xlsx'].includes(getFileExtension()); + const isExcelFile = () => ['.xls', '.xlsx', '.csv'].includes(getFileExtension()); const isPptFile = () => ['.ppt', '.pptx'].includes(getFileExtension()); const isPreviewable = () => previewableTypes.includes(getFileExtension()); @@ -227,6 +228,28 @@ const DocumentPreview: FC = ({ } }, [fileUrl]); + // ========== 图片加载逻辑 ========== + const loadImageFile = async () => { + setLoading(true); + setError(false); + setErrorMessage(''); + try { + const arrayBuffer = await fetchFileBuffer(fileUrl); + const ext = getFileExtension().replace('.', ''); + const mimeMap: Record = { + jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png', + gif: 'image/gif', bmp: 'image/bmp', webp: 'image/webp', svg: 'image/svg+xml', + }; + const blob = new Blob([arrayBuffer], { type: mimeMap[ext] || 'image/png' }); + const url = URL.createObjectURL(blob); + setImageBlobUrl(url); + setLoading(false); + } catch (err: any) { + console.error('加载图片文件失败:', err); + handleError(err.message || '图片加载失败'); + } + }; + // ========== 文本/Word/Excel 加载逻辑 ========== const loadTextFile = async () => { setLoading(true); @@ -274,12 +297,42 @@ const DocumentPreview: FC = ({ } }; + const isCsvFile = () => getFileExtension() === '.csv'; + const loadExcelFile = async () => { setLoading(true); setError(false); setErrorMessage(''); try { const arrayBuffer = await fetchFileBuffer(fileUrl); + + // CSV 文件需要处理编码问题(可能是 GBK/GB2312) + if (isCsvFile()) { + let csvText: string; + // 先尝试 UTF-8 解码 + const utf8Text = new TextDecoder('utf-8').decode(arrayBuffer); + // 检测是否有乱码特征(常见的 GBK 被错误解析为 UTF-8 的替换字符) + if (utf8Text.includes('\uFFFD') || /[\x80-\xff]/.test(utf8Text.slice(0, 200))) { + // 尝试 GBK 解码 + try { + csvText = new TextDecoder('gbk').decode(arrayBuffer); + } catch { + csvText = utf8Text; + } + } else { + csvText = utf8Text; + } + const workbook = XLSX.read(csvText, { type: 'string' }); + const sheets = workbook.SheetNames.map(sheetName => { + const worksheet = workbook.Sheets[sheetName]; + const data = XLSX.utils.sheet_to_json(worksheet, { header: 1 }) as any[][]; + return { sheetName, data }; + }); + setExcelData(sheets); + setLoading(false); + return; + } + const workbook = XLSX.read(arrayBuffer, { type: 'array' }); const sheets = workbook.SheetNames.map(sheetName => { const worksheet = workbook.Sheets[sheetName]; @@ -311,7 +364,7 @@ const DocumentPreview: FC = ({ else if (isExcelFile()) loadExcelFile(); else if (isPdfFile()) loadPdfFile(); else if (isPptFile()) loadPptFile(); - else if (isImageFile()) setLoading(false); + else if (isImageFile()) loadImageFile(); }, [fileUrl]); // PDF 翻页/缩放后重新渲染 @@ -412,11 +465,11 @@ const DocumentPreview: FC = ({ {/* 图片预览 */} {isImageFile() && !error && !loading && (
- {fileName handleError('图片加载失败')} + style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain' }} + onError={() => handleError('图片渲染失败')} />
)}