react-pdf 实现pdf文件预览功能

参考文档

  https://www.npmjs.com/package/react-pdf
  https://github.com/wojtekmaj/react-pdf#readme

 

一、概述

react项目中,很多时候(尤其是需展示报告的页面)会遇到需要预览pdf文件的需求。而据调研,使用react-pdf插件可以很好地实现这个功能。

 

 

二、操作步骤

1. 安装

yarn add react-pdf  或 npm install --save react-pdf

 

2. 新建一个 PdfViewer.jsx 的公共组件,作为pdf预览的封装文件

import React, { useEffect, useState, useRef } from 'react';
import { Spin, Tooltip, Input } from 'antd';
import { LeftOutlined, RightOutlined, ZoomInOutlined, ZoomOutOutlined } from '@ant-design/icons';

import { Document, Page, pdfjs } from "react-pdf";
import 'react-pdf/dist/esm/Page/AnnotationLayer.css';
import 'react-pdf/dist/esm/Page/TextLayer.css';
pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js`;

const PageCom = (props) => {
  const [filePath, setFilePath] = useState(null);

  const [pageCurrent, setPageCurrent] = useState(1);
  const pageCurrentRef = useRef(pageCurrent); 

  const [pageTotal, setPageTotal] = useState(1);
  const pageTotalRef = useRef(pageTotal); 

  const [pageWidth, setPageWidth] = useState(960);
  const pageWidthRef = useRef(pageWidth);     

  useEffect(()=>{      
    setPageCurrent(1);
    pageCurrentRef.current= 1;

    setFilePath(props.filePath);    
  },[props.filePath])  

  const prevPage = () => {
    if (pageCurrentRef.current == 1) { return; }
    setPageCurrent(pageCurrentRef.current - 1);
    pageCurrentRef.current= pageCurrentRef.current - 1;
  };

  const nextPage = () => {
    if (pageCurrentRef.current == pageTotalRef.current) {
      return;
    }
    
    setPageCurrent(pageCurrentRef.current + 1);
    pageCurrentRef.current= pageCurrentRef.current + 1;
  };

  const pageNumChange = e => {
    let value = Number(e.target.value);
    let value2 = 1;

    if(value<=0){
        value2 = 1;
    } else if(value >= pageTotalRef.current){
        value2 = pageTotalRef.current;
    } else {
        value2 = value;
    }
  
    setPageCurrent(value);
    pageCurrentRef.current= value;
  };

  const toPage = e => {
    console.log('toPage====',e)
    let value = Number(e.target.value);
    let value2 = value;

    if(value<=0){
        value2 = 1;
    } else if(value >= pageTotalRef.current){
        value2 = pageTotalRef.current;
    } else {
        value2 = value;
    }
    setPageCurrent(value2);
    pageCurrentRef.current= value2;
  };

  const pageZoomOut = () => {
    if (pageWidthRef.current <= 960) {
      return
    }
    const pageWidth = pageWidthRef.current * 0.8;    
    setPageWidth(pageWidth);    
    pageWidthRef.current = pageWidth;
  };

  const pageZoomIn = () => {
    const pageWidth = pageWidthRef.current * 1.2
    setPageWidth(pageWidth);    
    pageWidthRef.current = pageWidth;
  };

  const onDocumentLoadSuccess = (args) => {
    setPageTotal(args.numPages);
    pageTotalRef.current = args.numPages;
  };

  return (
    <div className="pdfViewer-wrapper">
      {filePath?(<>
        <div className="pageContainer">
          <Document file={filePath} onLoadSuccess={ onDocumentLoadSuccess }  loading={<Spin size="large" />} >
            <Page pageNumber={pageCurrent} width={pageWidth} loading={<Spin size="large" />}  />
          </Document>
        </div>

        <div className="pageTool">
          <Tooltip title={pageCurrent == 1 ? "已是第一页" : "上一页"}>
            <LeftOutlined onClick={prevPage} />
          </Tooltip>
          <Input value={pageCurrent} onChange={ pageNumChange } onPressEnter={ toPage } type="number" /> / {pageTotal}
          <Tooltip title={pageCurrent == pageTotal ? "已是最后一页" : "下一页"}>
            <RightOutlined onClick={ nextPage } />
          </Tooltip>
          <Tooltip title="放大">
            <ZoomInOutlined onClick={ pageZoomIn } />
          </Tooltip>
          <Tooltip title="缩小">
            <ZoomOutOutlined onClick={ pageZoomOut } />
          </Tooltip>       
        </div>
      </>):(<div className="empty-wrapper">未生成报告文件!</div>)}
    </div>
  );  
};

export default PageCom;

 

3. 在需要pdf文件预览的页面,进行处理

import React, { useEffect, useState, useRef } from 'react';
import pdfPath from '@/lib/demo.pdf'; import PdfViewer from './PdfViewer.jsx'; import '@/styles/report.less'; import axios from 'axios' const PageCom = (props) => { const [filePath, setFilePath] = useState(null); useEffect(()=>{ if(token){
       // setFilePath(filePath); //本地测试文件url
       // 或 getData(); //远程获取文件url } },[token]) /* 获取报告文件流数据 */ const getData = ()=>{ axios.get('/api/getPdf', { responseType: 'blob' }).then(res => { const blob = new Blob([res.data], { type: res.headers["content-type"] }); let URL = window.URL || window.webkitURL; let href = URL.createObjectURL(blob); setFilePath(href); }) };
return <div className="page-fjhScreenReport"> {/* 查询条件 */} {/* ... */} {/* 文档内容 */} <div className="con-wrapper"> <PdfViewer filePath={filePath}/> </div> </div>; }; export default PageCom;

  

4. 对pdf预览样式进行处理 

.pdfViewer-wrapper {
        background:#f5f5f5;
        display: flex;
        justify-content: center;
        height: 100vh;
        padding: 10px 0;
        overflow: auto;
        box-sizing: border-box;

        .empty-wrapper{
            padding: 100px 0 50px;
            text-align: center;
            font-size: 20px;
            letter-spacing: 2px;
            color: #999;
        }

        .pageContainer {            
            z-index: 1;
            box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 4px 0px;
            width:max-content;
            max-width:100%;
            &:after{
                content:'';
                position: relative;
                display: block;
                height:110px;
                clear:both;
                overflow: hidden;
            }
        }

        .pageTool{
            z-index: 99;
            font-size: 18px;
            position: absolute;
            bottom: 10px;
            background: rgb(66, 66, 66);
            color: white;
            padding: 8px 15px;
            border-radius: 15px;
            display: flex;
            justify-content: center;
            align-items: center;
            .anticon{margin: 0 10px;}
            i{
            padding: 5px;
            margin:0 5px;
            &:hover{
                background: #333;
            }
            }
            input{
            display: inline-block;
            width: 50px;
            text-align: center;
            margin-right: 10px;
            height: 24px;
            }
            input::-webkit-outer-spin-button,
            input::-webkit-inner-spin-button {
            -webkit-appearance: none;
            }
            input[type="number"]{
            -moz-appearance: textfield;
            }
        }
}

 

注释:pdf 文件因为字体等原因,可能导致不完全加载,详见参考文档(不过用通用字体生成的pdf一般是没有问题的)!

 

 

 

posted @ 2024-03-08 16:56  一只两支三指  阅读(3242)  评论(0编辑  收藏  举报