• 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 = ({ fileName, fileUrl, onCancel }) => {
      const [numPages, setNumPages] = useStatenull>(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 (

       
    测试预览PDF

    {!!previewFile?.publicFileUrl && ( <PDFPreviewModal fileName={previewFile?.fileName} fileUrl={previewFile?.publicFileUrl} onCancel={() => setPreviewFile('')} /> )}
    ) } 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 = ({ 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 ? (
    <Document // 写死的pdf文件地址,用于本地测试使用,打包提交前需要注释掉 // file={new URL("/public/temp/AI销售助手-宽带&套餐&战新.pdf", import.meta.url).toString()} // 真实传入的pdf地址 file={fileUrl} onLoadSuccess={onDocumentLoadSuccess} >
    ) : (

    没有选择文件

    )}
    {pageNumber}/{numPages}
    ); }; 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 (
    { introduceDocList?.map(item => (
    onOpenPdfViewer(item)}>{item.fileName}
    )) } <Popup position='right' visible={showPdfViwer} showCloseButton bodyStyle={{ width: '100%' }} destroyOnClose={true} onClose={() => { setShowPdfViwer(false) setPdf({ id: 1 }) }} >
    {pdf?.fileName}
    ) } export default IntroduceDocList
    复制代码

    效果图:

     

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

     

  • 相关阅读:
    vue实现关键字查询列表数据
    R语言ggplot2可视化:使用ggpubr包的ggmaplot函数可视化MA图(MA-plot)、设置label.rectangle参数为图中标签添加矩形框
    3、Kafka Broker
    25.4 MySQL 函数
    版本动态 | SolidUI 0.2.0 版本发布
    ELK安装、部署、调试(五)filebeat的安装与配置
    JVM - 直接内存
    【Netty】Netty 编解码器(十四)
    Netty面试经典问题
    设计模式——抽象工厂模式
  • 原文地址:https://www.cnblogs.com/libo0125ok/p/18414292