首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

react-pdf预览在线PDF的使用

编程知识
2024年09月14日 15:39

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

效果图:

 

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

 

From:https://www.cnblogs.com/libo0125ok/p/18414292
本文地址: http://shuzixingkong.net/article/2024
0评论
提交 加载更多评论
其他文章 小李移动开发成长记 —— 大话小程序
小李移动开发成长记 —— 大话小程序 做传统网站前端开发的同学初次接触小程序,会有许多困惑:为什么没有div,view 是什么、怎么没有 ajax,wx.request 为什么是回调方式、预览怎么要用小程序开发者工具、APPID有什么用、安装npm包怎么还要构建、tabBar 是什么、语法怎么和vu
小李移动开发成长记 —— 大话小程序
全能还是专精?关于技术通才与技术专家的思考
在日新月异的 IT 行业中,每隔数年乃至数月,便会涌现出革新性的技术或前沿框架,引领行业潮流。 比如前端开发,我刚开始工作时,大部分都是静态页面+JavaScript,页面上只有一些简单的交互。 后来出现了Ajax技术和JQuery库,现在想起当年第一次使用JQuery时,真的觉得这就是前端库的终点
全能还是专精?关于技术通才与技术专家的思考
OpenSSL证书通过Subject Alternative Name扩展字段扩展证书支持的域名
1、概述 1.1 什么是Subject Alternative Name(证书主体别名) SAN(Subject Alternative Name) 是 SSL 标准 x509 中定义的一个扩展。它允许一个证书支持多个不同的域名。通过使用SAN字段,可以在一个证书中指定多个DNS名称(域名)、IP地
OpenSSL证书通过Subject Alternative Name扩展字段扩展证书支持的域名 OpenSSL证书通过Subject Alternative Name扩展字段扩展证书支持的域名 OpenSSL证书通过Subject Alternative Name扩展字段扩展证书支持的域名
docker安装运行kafka单机版
这里我们安装一下kafka的单机版,由于kafka是基于zk进行管理的,如果我们没有安装过zk的话,需要进行安装好zk再安装kafka,当然如果已经安装过了, 那就没必要安装了。我们可以执行docker images命令查看我们的zk镜像是否已经存在了。执行的主要的流程如下所示: 1. docker
网络服务性能优化:Wrktcp与Perf工具详解
wrktcp安装 码云地址:https://gitee.com/icesky1stm/wrktcp 直接下载,cd wrktcp-master &amp;&amp; make,会生成wrktcp,就ok了,很简单 wrktcp使用 压测首先需要一个服务,写了一个epoll+边沿触发的服务,业务是判断
网络服务性能优化:Wrktcp与Perf工具详解
Codes 开源研发项目管理平台——创新的敏捷测试解决方案
Codes 是国内首款重新定义 SaaS 模式的开源项目管理平台,支持云端认证、本地部署、全部功能开放,并且对 30 人以下团队免费。它通过整合迭代、看板、度量和自动化等功能,简化测试协同工作,使敏捷测试更易于实施。并提供低成本的敏捷测试解决方案,如同步在线离线测试用例、流程化管理缺陷、低代码接口自
Codes 开源研发项目管理平台——创新的敏捷测试解决方案 Codes 开源研发项目管理平台——创新的敏捷测试解决方案 Codes 开源研发项目管理平台——创新的敏捷测试解决方案
擅长处理临时数据的结构——栈
目录实践1 —— 从字符串中移除星号 栈和数组存储数据的方式一样,它们都只是元素的列表。不同之处在于栈的以下3个限制: 数据只能从栈末插入; 数据只能从栈末删除; 只能读取栈的最后一个元素。 栈和队列、链表...一样,都是抽象的数据结构, 何为抽象数据结构? 它指一种数据组织的形式,它不关注具体的实
擅长处理临时数据的结构——栈 擅长处理临时数据的结构——栈 擅长处理临时数据的结构——栈
记一次 公司.NET项目部署在Linux环境压测时 内存暴涨分析
一:背景 讲故事 公司部署在某碟上的项目在9月份压测50并发时,发现某个容器线程、内存非正常的上涨,导致功能出现了异常无法使用。根据所学,自己分析了下线程和内存问题,分析时可以使用lldb或者windbg,但是个人比较倾向于界面化的windbg,所以最终使用windbg开干。 二:WinDbg 分析