1、在react项目中安装react-pdf依赖包
建议安装8.0.2版本的react-pdf,如果安装更高版本的可能出现一些浏览器的兼容性问题;
npm install react-pdf@8.0.2 -S
1、PC端的使用
1.1、封装一个组件:PdfViewModal.tsx
import React, { useState } from 'react' import { Modal, Spin, Alert } from 'antd' import { Document, Page, pdfjs } from 'react-pdf' import 'react-pdf/dist/esm/Page/AnnotationLayer.css' import 'react-pdf/dist/esm/Page/TextLayer.css'; // 配置 PDF.js 的 worker 文件 pdfjs.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.js', import.meta.url).toString() interface PDFPreviewModalProps { fileName: string | null fileUrl: string | null // 传入的 PDF 文件地址 onCancel: () => void // 关闭弹框的回调 } const PDFPreviewModal: React.FC= ({ fileName, fileUrl, onCancel }) => { const [numPages, setNumPages] = useState null>(null) const [pdfWidth, setPdfWidth] = useState (600) // 默认宽度为 600px const [loading, setLoading] = useState<boolean>(true) // 控制加载状态 const [error, setError] = useState<boolean>(false) // 控制加载错误状态 // 当 PDF 加载成功时,设置页面数量 const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => { setNumPages(numPages) setLoading(false) // 加载成功后,隐藏 loading } // 加载失败时,设置错误状态 const onDocumentLoadError = () => { setLoading(false) setError(true) // 出错时显示错误提示 } // 获取 PDF 页面加载后的宽度 const onPageLoadSuccess = ({ width }: { width: number }) => { setPdfWidth(width) } return ( <Modal title={`【${fileName}】预览`} open onCancel={onCancel} footer={null} width={pdfWidth + 100} style={{ top: 20 }} > {error ? ( ) : ( <> {loading && ( )} {fileUrl && ( <> <Document //file={new URL('/public/temp/DXF文件要求.pdf',import.meta.url).toString()} file={fileUrl} onLoadSuccess={onDocumentLoadSuccess} onLoadError={onDocumentLoadError} > {Array.from(new Array(numPages), (el, index) => (> )} > )} ) } export default PDFPreviewModal))}
1.2、业务代码中引入该组件
import React, { useState, useEffect, useCallback } from 'react' import { Form } from 'antd' import { List } from 'antd' import PDFPreviewModal from '@/components/PdfViewModal.tsx' const PdfTest = (props: any) => { const [previewFile, setPreviewFile] = useState()
const onTestPdf = () => {
setPreviewFile({
fileName: 'abc.pdf',
fileUrl: 'http://****/abc.pdf'
})
}return () } export default PdfTest
测试预览PDF
{!!previewFile?.publicFileUrl && ( <PDFPreviewModal fileName={previewFile?.fileName} fileUrl={previewFile?.publicFileUrl} onCancel={() => setPreviewFile('')} /> )}
2、H5移动端的使用
移动端加入放大、缩小、上一页、下一页的功能;
2.1、封装一个组件:PDFViwer.tsx
import React, { useState } from 'react'; import { Button, Modal, Space, Toast, Divider } from 'antd-mobile' import { UpOutline, DownOutline, AddCircleOutline, MinusCircleOutline } from 'antd-mobile-icons' import { Document, Page, pdfjs } from 'react-pdf'; import 'react-pdf/dist/esm/Page/AnnotationLayer.css'; // 样式导入 import 'react-pdf/dist/esm/Page/TextLayer.css' // 配置 PDF.js 的 worker 文件 pdfjs.GlobalWorkerOptions.workerSrc = new URL('pdfjs-dist/build/pdf.worker.min.js', import.meta.url).toString() interface PDFPreviewModalProps { fileUrl: string | null; // 传入的 PDF 文件地址 } const styleBtnDv = { display: 'flex', justifyContent: 'center', height: '1rem', alignItems: 'center', gap: '0.4rem', margin: '0.3rem 1rem', padding: '0 0.6rem', background: '#444', borderRadius: '0.5rem' } const styleBtn = { flex: 1, display: 'flex', justifyContent: 'center', height: '0.6rem', alignItems: 'center', } // PDF预览功能 const PDFViwer: React.FC= ({ fileUrl }) => { const [pageNumber, setPageNumber] = useState(1); const [numPages, setNumPages] = useState(1); const [scale, setScale] = useState(0.65); // 当 PDF 加载成功时,设置页面数量 const onDocumentLoadSuccess = ({ numPages }: { numPages: number }) => { setNumPages(numPages); }; //上一页 function lastPage() { if (pageNumber == 1) { Toast.show({ content: '已是第一页' }) return; } const page = pageNumber - 1; setPageNumber(page); } //下一页 function nextPage() { if (pageNumber == numPages) { Toast.show("已是最后一页"); return; } const page = pageNumber + 1; setPageNumber(page); } //缩小 function pageZoomOut() { if (scale <= 0.3) { Toast.show("已缩放至最小"); return; } const newScale = scale - 0.1; setScale(newScale); } //放大 function pageZoomIn() { if (scale >= 5) { Toast.show("已放大至最大"); return; } const newScale = scale + 0.1; setScale(newScale); } return ( {/* 预览 PDF 文件 */} {fileUrl ? (); }; export default PDFViwer;<Document // 写死的pdf文件地址,用于本地测试使用,打包提交前需要注释掉 // file={new URL("/public/temp/AI销售助手-宽带&套餐&战新.pdf", import.meta.url).toString()} // 真实传入的pdf地址 file={fileUrl} onLoadSuccess={onDocumentLoadSuccess} >) : (没有选择文件
)}{pageNumber}/{numPages}
2.2、业务代码中引入该组件
import React, { useMemo, useRef, useState } from 'react' import { ErrorBlock, Swiper, SwiperRef, Popup, } from 'antd-mobile' import PDFViwer from '@/components/PDFViwer'; const ellipsis1 = { "white-space": "nowrap", "overflow": "hidden", "text-overflow": "ellipsis", } const IntroduceDocList = (props: any) => { const { loading, introduceDocList } = props // const introduceDocList = [ // {publicFileUrl: '/public/temp/DXF文件要求.pdf', fileName:'DXF文件要求.pdf'}, // {publicFileUrl: '/public/temp/AI销售助手-宽带&套餐&战新.pdf', fileName:'AI销售助手-宽带&套餐&战新.pdf'}, // ]
const [introduceDocList, setIntroduceDocList] = useState({
{publicFileUrl: 'http://****/abc.pdf', fileName:'abc.pdf'},
{publicFileUrl: 'http://****/def.pdf', fileName:'def.pdf'},
});
const [pdf, setPdf] = useState({ id: 1 }); const [showPdfViwer, setShowPdfViwer] = useState(false) const onOpenPdfViewer = (item) => { console.log(item); setPdf(item); setShowPdfViwer(true); } return ({ introduceDocList?.map(item => () } export default IntroduceDocListonOpenPdfViewer(item)}>{item.fileName})) } <Popup position='right' visible={showPdfViwer} showCloseButton bodyStyle={{ width: '100%' }} destroyOnClose={true} onClose={() => { setShowPdfViwer(false) setPdf({ id: 1 }) }} >{pdf?.fileName}
效果图:
注意:挡在本地开发时,如果预览的pdf文件地址是线上地址,则会报跨域的问题,需要服务端解决跨域问题。