react-pdf预览在线PDF的使用

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<PDFPreviewModalProps> = ({ fileName, fileUrl, onCancel }) => {   const [numPages, setNumPages] = useState<number | null>(null)   const [pdfWidth, setPdfWidth] = useState<number>(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 ? (         <Alert message="加载 PDF 文件失败" type="error" showIcon />       ) : (         <>           {loading && (             <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '80vh' }}>               <Spin size="large" />             </div>           )}           {fileUrl && (             <>             <div style={{ height: '88vh', overflowY: 'auto', padding: '24px' }}>               <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) => (                   <Page key={`page_${index + 1}`} pageNumber={index + 1} onLoadSuccess={onPageLoadSuccess} />                 ))}               </Document>             </div>             </>           )}         </>       )}     </Modal>   ) }  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<any>()

const onTestPdf = () => {
  setPreviewFile({
    fileName: 'abc.pdf',
    fileUrl: 'http://****/abc.pdf'
  })
}
return ( <div className="mrUp mrLink">
   <div onClick={onTestPdf}>测试预览PDF</div>
{!!previewFile?.publicFileUrl && ( <PDFPreviewModal fileName={previewFile?.fileName} fileUrl={previewFile?.publicFileUrl} onCancel={() => setPreviewFile('')} /> )} </div> ) } export default PdfTest

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<PDFPreviewModalProps> = ({ 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 (     <div>       {/* 预览 PDF 文件 */}       {fileUrl ? (         <div style={{ height: 'calc(100vh - 4.5rem)', overflowY: 'auto', padding: '24px' }}>           <Document             // 写死的pdf文件地址,用于本地测试使用,打包提交前需要注释掉             // file={new URL("/public/temp/AI销售助手-宽带&套餐&战新.pdf", import.meta.url).toString()}             // 真实传入的pdf地址             file={fileUrl}             onLoadSuccess={onDocumentLoadSuccess}           >             <Page pageNumber={pageNumber} scale={scale} />           </Document>         </div>       ) : (         <p>没有选择文件</p>       )}       <div style={styleBtnDv}>         <div style={styleBtn} onClick={lastPage}><UpOutline color='#fff' fontSize={'0.6rem'} /></div>         <div style={{ color: '#fff', fontSize: '0.35rem', ...styleBtn }}>{pageNumber}/{numPages}</div>         <div style={styleBtn} onClick={nextPage}><DownOutline color='#fff' fontSize={'0.6rem'} /></div>         <div style={styleBtn} onClick={pageZoomIn}><AddCircleOutline color='#fff' fontSize={'0.6rem'} /></div>         <div style={styleBtn} onClick={pageZoomOut}><MinusCircleOutline color='#fff' fontSize={'0.6rem'} /></div>       </div>     </div>   ); };  export default PDFViwer;

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 ( <div> { introduceDocList?.map(item => ( <div data-url={item?.publicFileUrl} style={{ marginBottom: '0.3rem', fontSize: '0.4rem' }}> <span style={{color:'#0B75FF'}} onClick={() => onOpenPdfViewer(item)}>{item.fileName}</span> </div> )) } <Popup position='right' visible={showPdfViwer} showCloseButton bodyStyle={{ width: '100%' }} destroyOnClose={true} onClose={() => { setShowPdfViwer(false) setPdf({ id: 1 }) }} > <div style={{ padding: '0.3rem 1rem', fontSize: '0.35rem', fontWeight: 600, textAlign:'center', ...ellipsis1 }}>{pdf?.fileName}</div> <div style={{ height: '100%' }} data-url={pdf?.publicFileUrl}> <PDFViwer fileUrl={pdf?.publicFileUrl} /> </div> </Popup> </div> ) } export default IntroduceDocList

效果图:

react-pdf预览在线PDF的使用

 

注意:挡在本地开发时,如果预览的pdf文件地址是线上地址,则会报跨域的问题,需要服务端解决跨域问题。

 

发表评论

评论已关闭。

相关文章