import React, { useEffect, useRef, useState, useCallback } from 'react'; import { createPortal } from 'react-dom'; import { Spin, Tooltip, Input } from 'antd'; import { LeftOutlined, RightOutlined, PlusCircleOutlined, MinusCircleOutlined, FullscreenExitOutlined, FullscreenOutlined, CloseCircleOutlined, ExclamationCircleOutlined, RotateLeftOutlined, RotateRightOutlined, UnorderedListOutlined, } from '@ant-design/icons'; import './index.less'; import { Document, Page, pdfjs } from 'react-pdf'; import pdfjsWorker from 'react-pdf/dist/esm/pdf.worker.entry'; pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker; const PDFView = ({ file, parentDom, onClose, }: { file?: string | null; parentDom?: HTMLDivElement | null; onClose?: () => void; }) => { const defaultWidth = 600; const pageDiv = useRef(null); const [numPages, setNumPages] = useState(0); const [pageNumber, setPageNumber] = useState(1); const [pageWidth, setPageWidth] = useState(defaultWidth); const [fullscreen, setFullscreen] = useState(false); const [rotation, setRotation] = useState(0); const [showThumbnails, setShowThumbnails] = useState(false); const [visiblePages, setVisiblePages] = useState([1]); // 控制可见页面 const parent = parentDom || document.body; // 加载 PDF 元信息,不渲染全部页面 const onDocumentLoadSuccess = useCallback(({ numPages }: { numPages: number }) => { setNumPages(numPages); }, []); const lastPage = () => pageNumber > 1 && setPageNumber(pageNumber - 1); const nextPage = () => pageNumber < numPages && setPageNumber(pageNumber + 1); const onPageNumberChange = (e: { target: { value: string } }) => { let value = Math.max(1, Math.min(numPages, Number(e.target.value) || 1)); setPageNumber(value); setVisiblePages([value]); // 只加载当前页 }; const pageZoomIn = () => setPageWidth(pageWidth * 1.2); const pageZoomOut = () => pageWidth > defaultWidth && setPageWidth(pageWidth * 0.8); const pageFullscreen = () => { setPageWidth(fullscreen ? defaultWidth : parent.offsetWidth - 50); setFullscreen(!fullscreen); }; const rotateLeft = () => setRotation((prev) => (prev - 90) % 360); const rotateRight = () => setRotation((prev) => (prev + 90) % 360); const toggleThumbnails = () => setShowThumbnails(!showThumbnails); // 动态更新可见页面 useEffect(() => { if (!showThumbnails) { setVisiblePages([pageNumber]); } else { // 缩略图模式下限制加载数量,避免卡顿 const start = Math.max(1, pageNumber - 2); const end = Math.min(numPages, pageNumber + 2); setVisiblePages(Array.from({ length: end - start + 1 }, (_, i) => start + i)); } }, [pageNumber, showThumbnails, numPages]); useEffect(() => setPageNumber(1), [file]); useEffect(() => { if( pageDiv.current){ (pageDiv.current.scrollTop = 0) } }, [pageNumber]); const renderContent=()=>(
} loading={
} > {showThumbnails ? (
{Array.from({ length: numPages }, (_, i) => i + 1).map((page) => (
{ setPageNumber(page); setShowThumbnails(false); }} > {visiblePages.includes(page) ? ( } renderTextLayer={false} // 禁用文本层,提升性能 renderAnnotationLayer={false} // 禁用注释层 /> ) : (
第 {page} 页
)} 第 {page} 页
))}
) : ( } renderTextLayer={false} // 禁用文本层 renderAnnotationLayer={false} // 禁用注释层 error={() => setPageNumber(1)} /> )}
{' '} / {numPages} {fullscreen ? : } {onClose && ( )}
) if(parentDom){ return renderContent() } return createPortal( renderContent(), parent,) }; export default PDFView;