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

参考文档

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

 

一、概述

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

 

 

二、操作步骤

1. 安装

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

 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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文件预览的页面,进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import React, { useEffect, useState, useRef } from 'react';<br>
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){ <br>       // setFilePath(filePath); //本地测试文件url<br>       // 或
            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);     
        })
    };<br>
    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 @   一只两支三指  阅读(5044)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示