react-pdf 实现pdf文件预览功能
参考文档
https://www.npmjs.com/package/react-pdfhttps://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一般是没有问题的)!
浙公网安备 33010602011771号