Skip to content

Commit 2d70071

Browse files
committed
feat: pdf view
1 parent 15b9ebd commit 2d70071

1 file changed

Lines changed: 143 additions & 40 deletions

File tree

src/pages/reactPdf/index.jsx

Lines changed: 143 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,173 @@
1-
import React, { useState } from 'react'
1+
import React, { useState, useCallback } from 'react'
22
import { Document, Page, pdfjs } from 'react-pdf'
3-
import { ChevronLeft, ChevronRight, ZoomIn, ZoomOut } from 'lucide-react'
3+
import { ChevronLeft, ChevronRight, ZoomIn, ZoomOut, Loader2 } from 'lucide-react'
44
import FixTabPanel from '@stateless/FixTabPanel'
55
import 'react-pdf/dist/esm/Page/AnnotationLayer.css'
66
import 'react-pdf/dist/esm/Page/TextLayer.css'
77

8-
// Set up the worker for react-pdf
8+
// 设置PDF.js worker
99
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.mjs`
1010

11+
// 错误边界组件,用于捕获子组件错误
12+
class ErrorBoundary extends React.Component {
13+
constructor(props) {
14+
super(props)
15+
this.state = { hasError: false, error: null }
16+
}
17+
18+
static getDerivedStateFromError(error) {
19+
return { hasError: true, error }
20+
}
21+
22+
componentDidCatch(error, errorInfo) {
23+
console.error('PDF渲染错误:', error, errorInfo)
24+
}
25+
26+
render() {
27+
if (this.state.hasError) {
28+
return this.props.fallback || <div>加载PDF时发生错误,请重试。</div>
29+
}
30+
return this.props.children
31+
}
32+
}
33+
1134
const ReactPdf = () => {
1235
const [numPages, setNumPages] = useState(null)
1336
const [pageNumber, setPageNumber] = useState(1)
1437
const [scale, setScale] = useState(1.0)
38+
const [isLoading, setIsLoading] = useState(true)
39+
const [error, setError] = useState(null)
1540

16-
function onDocumentLoadSuccess({ numPages }) {
41+
// 文档加载成功处理
42+
const onDocumentLoadSuccess = useCallback(({ numPages }) => {
1743
setNumPages(numPages)
18-
}
44+
setIsLoading(false)
45+
}, [])
46+
47+
// 文档加载错误处理
48+
const onDocumentLoadError = useCallback((err) => {
49+
console.error('文档加载错误:', err)
50+
setError('无法加载PDF文档,请检查URL是否正确。')
51+
setIsLoading(false)
52+
}, [])
53+
54+
// 页面渲染错误处理
55+
const onPageLoadError = useCallback((err) => {
56+
console.error('页面加载错误:', err)
57+
setError('加载页面时发生错误。')
58+
}, [])
1959

60+
// 页面渲染成功处理
61+
const onPageLoadSuccess = useCallback(() => {
62+
setError(null)
63+
}, [])
64+
65+
// 翻页处理
2066
const changePage = (offset) => {
21-
setPageNumber((prevPageNumber) => Math.min(Math.max(prevPageNumber + offset, 1), numPages || 1))
67+
if (!numPages) return
68+
setPageNumber((prev) => {
69+
const newPage = prev + offset
70+
return Math.min(Math.max(newPage, 1), numPages)
71+
})
2272
}
2373

74+
// 缩放处理
2475
const changeScale = (delta) => {
25-
setScale((prevScale) => Math.min(Math.max(prevScale + delta, 0.5), 2.0))
76+
setScale((prev) => {
77+
const newScale = prev + delta
78+
return Math.min(Math.max(newScale, 0.5), 2.0)
79+
})
2680
}
2781

28-
const httpsUrl = 'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf'
82+
const pdfUrl = 'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf'
83+
2984
return (
3085
<FixTabPanel>
31-
<div className="min-h-screen bg-gray-100">
86+
<div className="flex min-h-screen flex-col bg-gray-100">
3287
<header className="bg-blue-600 p-4 text-white">
3388
<h1 className="text-2xl font-bold">React PDF Viewer</h1>
3489
</header>
35-
<main className="container mx-auto flex grow flex-col items-center p-4">
36-
<div className="mb-4 w-full max-w-3xl rounded-lg bg-white p-4 shadow-md">
37-
<Document file={httpsUrl} onLoadSuccess={onDocumentLoadSuccess} className="flex justify-center">
38-
<Page pageNumber={pageNumber} scale={scale} />
39-
</Document>
40-
</div>
41-
<div className="flex items-center justify-center space-x-4">
42-
<button
43-
onClick={() => changePage(-1)}
44-
disabled={pageNumber <= 1}
45-
className="rounded-full bg-blue-500 p-2 text-white disabled:bg-gray-300"
46-
>
47-
<ChevronLeft className="h-6 w-6" />
48-
</button>
49-
<span className="mx-1 text-xl">
50-
Page {pageNumber} of {numPages}
51-
</span>
52-
<button
53-
onClick={() => changePage(1)}
54-
disabled={pageNumber >= (numPages || 1)}
55-
className="rounded-full bg-blue-500 p-2 text-white disabled:bg-gray-300"
56-
>
57-
<ChevronRight className="h-6 w-6" />
58-
</button>
59-
<button onClick={() => changeScale(-0.1)} className="rounded-full bg-blue-500 p-2 text-white">
60-
<ZoomOut className="h-6 w-6" />
61-
</button>
62-
<button onClick={() => changeScale(0.1)} className="rounded-full bg-blue-500 p-2 text-white">
63-
<ZoomIn className="h-6 w-6" />
64-
</button>
90+
91+
<main className="container mx-auto flex-grow p-4">
92+
<div className="mx-auto mb-4 w-full max-w-3xl rounded-lg bg-white p-4 shadow-md">
93+
{isLoading ? (
94+
<div className="flex h-96 items-center justify-center">
95+
<Loader2 className="h-10 w-10 animate-spin text-blue-500" />
96+
<span className="ml-2 text-gray-600">加载PDF文档中...</span>
97+
</div>
98+
) : error ? (
99+
<div className="flex h-96 items-center justify-center text-red-500">{error}</div>
100+
) : (
101+
<ErrorBoundary fallback={<div>PDF渲染失败,请重试。</div>}>
102+
<Document
103+
file={pdfUrl}
104+
onLoadSuccess={onDocumentLoadSuccess}
105+
onLoadError={onDocumentLoadError}
106+
className="flex justify-center"
107+
>
108+
<Page
109+
key={`page-${pageNumber}-scale-${scale}`}
110+
pageNumber={pageNumber}
111+
scale={scale}
112+
onLoadError={onPageLoadError}
113+
onLoadSuccess={onPageLoadSuccess}
114+
renderAnnotationLayer={true}
115+
renderTextLayer={true}
116+
/>
117+
</Document>
118+
</ErrorBoundary>
119+
)}
65120
</div>
121+
122+
{/* 控制按钮区域 */}
123+
{numPages && !error && (
124+
<div className="mt-4 flex items-center justify-center space-x-4">
125+
<button
126+
onClick={() => changePage(-1)}
127+
disabled={pageNumber <= 1}
128+
className="rounded-full bg-blue-500 p-2 text-white transition-colors hover:bg-blue-600 disabled:cursor-not-allowed disabled:bg-gray-300"
129+
aria-label="上一页"
130+
>
131+
<ChevronLeft className="h-6 w-6" />
132+
</button>
133+
134+
<span className="mx-1 text-xl">
135+
{pageNumber} 页,共 {numPages}
136+
</span>
137+
138+
<button
139+
onClick={() => changePage(1)}
140+
disabled={pageNumber >= numPages}
141+
className="rounded-full bg-blue-500 p-2 text-white transition-colors hover:bg-blue-600 disabled:cursor-not-allowed disabled:bg-gray-300"
142+
aria-label="下一页"
143+
>
144+
<ChevronRight className="h-6 w-6" />
145+
</button>
146+
147+
<div className="mx-4 h-6 border-r border-gray-300"></div>
148+
149+
<button
150+
onClick={() => changeScale(-0.1)}
151+
disabled={scale <= 0.5}
152+
className="rounded-full bg-blue-500 p-2 text-white transition-colors hover:bg-blue-600 disabled:cursor-not-allowed disabled:bg-gray-300"
153+
aria-label="缩小"
154+
>
155+
<ZoomOut className="h-6 w-6" />
156+
</button>
157+
158+
<span className="text-gray-600">{Math.round(scale * 100)}%</span>
159+
160+
<button
161+
onClick={() => changeScale(0.1)}
162+
disabled={scale >= 2.0}
163+
className="rounded-full bg-blue-500 p-2 text-white transition-colors hover:bg-blue-600 disabled:cursor-not-allowed disabled:bg-gray-300"
164+
aria-label="放大"
165+
>
166+
<ZoomIn className="h-6 w-6" />
167+
</button>
168+
</div>
169+
)}
66170
</main>
67-
{/* <embed type="application/pdf" src={`${httpsUrl}`} key={`${httpsUrl}`} width="100%" height="600px" />; */}
68171
</div>
69172
</FixTabPanel>
70173
)

0 commit comments

Comments
 (0)