138 lines
3.7 KiB
React
138 lines
3.7 KiB
React
|
|
import { useState, useMemo } from 'react'
|
|||
|
|
import { Document, Page, pdfjs } from 'react-pdf'
|
|||
|
|
import { Button, Space, InputNumber, message, Spin } from 'antd'
|
|||
|
|
import {
|
|||
|
|
LeftOutlined,
|
|||
|
|
RightOutlined,
|
|||
|
|
ZoomInOutlined,
|
|||
|
|
ZoomOutOutlined,
|
|||
|
|
} from '@ant-design/icons'
|
|||
|
|
import 'react-pdf/dist/Page/AnnotationLayer.css'
|
|||
|
|
import 'react-pdf/dist/Page/TextLayer.css'
|
|||
|
|
import './PDFViewer.css'
|
|||
|
|
|
|||
|
|
// 配置 PDF.js worker - 使用本地文件
|
|||
|
|
pdfjs.GlobalWorkerOptions.workerSrc = '/pdf-worker/pdf.worker.min.mjs'
|
|||
|
|
|
|||
|
|
function PDFViewer({ url, filename }) {
|
|||
|
|
const [numPages, setNumPages] = useState(null)
|
|||
|
|
const [pageNumber, setPageNumber] = useState(1)
|
|||
|
|
const [scale, setScale] = useState(1.0)
|
|||
|
|
|
|||
|
|
// 使用 useMemo 避免不必要的重新加载
|
|||
|
|
const fileConfig = useMemo(() => ({ url }), [url])
|
|||
|
|
|
|||
|
|
const onDocumentLoadSuccess = ({ numPages }) => {
|
|||
|
|
setNumPages(numPages)
|
|||
|
|
setPageNumber(1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const onDocumentLoadError = (error) => {
|
|||
|
|
message.error('PDF文件加载失败')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const goToPrevPage = () => {
|
|||
|
|
setPageNumber((prev) => Math.max(prev - 1, 1))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const goToNextPage = () => {
|
|||
|
|
setPageNumber((prev) => Math.min(prev + 1, numPages))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const zoomIn = () => {
|
|||
|
|
setScale((prev) => Math.min(prev + 0.2, 3.0))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const zoomOut = () => {
|
|||
|
|
setScale((prev) => Math.max(prev - 0.2, 0.5))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handlePageChange = (value) => {
|
|||
|
|
if (value >= 1 && value <= numPages) {
|
|||
|
|
setPageNumber(value)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="pdf-viewer-container">
|
|||
|
|
{/* 工具栏 */}
|
|||
|
|
<div className="pdf-toolbar">
|
|||
|
|
<Space>
|
|||
|
|
<Button
|
|||
|
|
icon={<LeftOutlined />}
|
|||
|
|
onClick={goToPrevPage}
|
|||
|
|
disabled={pageNumber <= 1}
|
|||
|
|
size="small"
|
|||
|
|
>
|
|||
|
|
上一页
|
|||
|
|
</Button>
|
|||
|
|
<Space.Compact>
|
|||
|
|
<InputNumber
|
|||
|
|
min={1}
|
|||
|
|
max={numPages || 1}
|
|||
|
|
value={pageNumber}
|
|||
|
|
onChange={handlePageChange}
|
|||
|
|
size="small"
|
|||
|
|
style={{ width: 60 }}
|
|||
|
|
/>
|
|||
|
|
<Button size="small" disabled>
|
|||
|
|
/ {numPages || 0}
|
|||
|
|
</Button>
|
|||
|
|
</Space.Compact>
|
|||
|
|
<Button
|
|||
|
|
icon={<RightOutlined />}
|
|||
|
|
onClick={goToNextPage}
|
|||
|
|
disabled={pageNumber >= numPages}
|
|||
|
|
size="small"
|
|||
|
|
>
|
|||
|
|
下一页
|
|||
|
|
</Button>
|
|||
|
|
</Space>
|
|||
|
|
|
|||
|
|
<Space>
|
|||
|
|
<Button icon={<ZoomOutOutlined />} onClick={zoomOut} size="small">
|
|||
|
|
缩小
|
|||
|
|
</Button>
|
|||
|
|
<span style={{ minWidth: 50, textAlign: 'center' }}>
|
|||
|
|
{Math.round(scale * 100)}%
|
|||
|
|
</span>
|
|||
|
|
<Button icon={<ZoomInOutlined />} onClick={zoomIn} size="small">
|
|||
|
|
放大
|
|||
|
|
</Button>
|
|||
|
|
</Space>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* PDF内容区 */}
|
|||
|
|
<div className="pdf-content">
|
|||
|
|
<Document
|
|||
|
|
file={fileConfig}
|
|||
|
|
onLoadSuccess={onDocumentLoadSuccess}
|
|||
|
|
onLoadError={onDocumentLoadError}
|
|||
|
|
loading={
|
|||
|
|
<div className="pdf-loading">
|
|||
|
|
<Spin size="large" />
|
|||
|
|
<div style={{ marginTop: 16 }}>正在加载PDF...</div>
|
|||
|
|
</div>
|
|||
|
|
}
|
|||
|
|
error={<div className="pdf-error">PDF加载失败,请稍后重试</div>}
|
|||
|
|
>
|
|||
|
|
<Page
|
|||
|
|
pageNumber={pageNumber}
|
|||
|
|
scale={scale}
|
|||
|
|
renderTextLayer={true}
|
|||
|
|
renderAnnotationLayer={true}
|
|||
|
|
loading={
|
|||
|
|
<div className="pdf-loading">
|
|||
|
|
<Spin size="large" />
|
|||
|
|
<div style={{ marginTop: 16 }}>正在渲染页面...</div>
|
|||
|
|
</div>
|
|||
|
|
}
|
|||
|
|
/>
|
|||
|
|
</Document>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default PDFViewer
|