reactHooks+antd+react-pdf封装pdf预览组件

1、下载插件:

npm i react-pdf

2、PdfPreview/index.tsx

复制代码
/*
  进入该组件时,通过路由传递path进来,形如:
    history.push({ pathname: '/pdfPreview', query: { path } })
*/
import React, { useState } from 'react'
import { Spin, Tooltip, Input } from 'antd'
import {
  LeftOutlined,
  RightOutlined,
  PlusCircleOutlined,
  MinusCircleOutlined,
  FullscreenOutlined,
  FullscreenExitOutlined,
  ArrowLeftOutlined
} from '@ant-design/icons'
import styles from './index.less'

import { Document, Page, pdfjs } from 'react-pdf'
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`

import { history } from 'umi'

const PdfPreview: React.FC<{}> = (Props: any) => {
  const [pageNumber, setPageNumber] = useState(1)
  const [pageNumberInput, setPageNumberInput] = useState(1)
  const [pageNumberFocus, setPageNumberFocus] = useState(false)
  const [numPages, setNumPages] = useState(1)
  const [pageWidth, setPageWidth] = useState(600)
  const [fullscreen, setFullscreen] = useState(false)
  const { location } = history
  const { path }: any = location.query

  const onDocumentLoadSuccess = ({ numPages }: any) => {
    setNumPages(numPages)
  }
  const lastPage = () => {
    if (pageNumber == 1) return
    const page = pageNumber - 1
    setPageNumber(page)
    setPageNumberInput(page)
  }
  const nextPage = () => {
    if (pageNumber === numPages) return
    const page = pageNumber + 1
    setPageNumber(page)
    setPageNumberInput(page)
  }
  const onPageNumberFocus = (e: any) => {
    setPageNumberFocus(true)
  }
  const onPageNumberBlur = (e: any) => {
    setPageNumberFocus(false)
    setPageNumberInput(pageNumber)
  }
  const onPageNumberChange = (e: any) => {
    let value = e.target.value
    value = value <= 0 ? 1 : value
    value = value >= numPages ? numPages : value
    setPageNumberInput(value)
  }
  const toPage = (e: any) => {
    setPageNumber(Number(e.target.value))
  }
  const pageZoomOut = () => {
    if (pageWidth <= 600) return
    const width = pageWidth * 0.8
    setPageWidth(width)
  }
  const pageZoomIn = () => {
    const width = pageWidth * 1.2
    setPageWidth(width)
  }
  const pageFullscreen = () => {
    if (fullscreen) {
      setFullscreen(false)
      setPageWidth(600)
    } else {
      setFullscreen(true)
      setPageWidth(window.screen.width - 40)
    }
  }

  return (
    <div className={styles.view}>
      <ArrowLeftOutlined className={styles.back} onClick={() => history.goBack()} />
      <div className={styles.pageContainer}>
        <Document file={path} onLoadSuccess={onDocumentLoadSuccess} loading={<Spin size="large" />}>
          <Page pageNumber={pageNumber} width={pageWidth} loading={<Spin size="large" />} />
        </Document>
      </div>
      <div className={styles.pageTool}>
        <Tooltip title={pageNumber == 1 ? '已是第一页' : '上一页'}>
          <LeftOutlined className={styles.outlined} onClick={lastPage} />
        </Tooltip>
        <Input
          value={pageNumberFocus ? pageNumberInput : pageNumber}
          onFocus={onPageNumberFocus}
          onBlur={onPageNumberBlur}
          onChange={onPageNumberChange}
          onPressEnter={toPage}
          type="number"
        />
        / {numPages}
        <Tooltip title={pageNumber == numPages ? '已是最后一页' : '下一页'}>
          <RightOutlined className={styles.outlined} onClick={nextPage} />
        </Tooltip>
        <Tooltip title="放大">
          <PlusCircleOutlined className={styles.outlined} onClick={pageZoomIn} />
        </Tooltip>
        <Tooltip title="缩小">
          <MinusCircleOutlined className={styles.outlined} onClick={pageZoomOut} />
        </Tooltip>
        <Tooltip title={fullscreen ? '恢复默认' : '适合窗口'}>
          {fullscreen ? (
            <FullscreenExitOutlined className={styles.outlined} onClick={pageFullscreen} />
          ) : (
            <FullscreenOutlined className={styles.outlined} onClick={pageFullscreen} />
          )}
        </Tooltip>
      </div>
    </div>
  )
}

export default PdfPreview
复制代码

PdfPreview/index.less

复制代码
.view {
  display: flex;
  justify-content: center;
  height: calc(100vh - 206px);
  padding: 20px 0;
  overflow: auto;
  background: #444;
  .back {
    position: absolute;
    top: 0;
    left: 0;
    color: #fff;
    font-size: 20px;
    transition: color .3s;
    width: 40px;
    line-height: 30px;
    background-color: red;
  }
  .back:hover {
    // color: #1890ff;
  }
  .pageContainer {
    width: max-content;
    max-width: 100%;
    box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 4px 0px;
  }
  .pageTool {
    position: absolute;
    bottom: 20px;
    padding: 8px 15px;
    color: white;
    background: rgb(66, 66, 66);
    border-radius: 15px;
    .outlined {
      margin: 0 10px;
      user-select: none;
    }
    input {
      display: inline-block;
      width: 50px;
      height: 24px;
      margin-right: 10px;
      text-align: center;
    }
    input::-webkit-outer-spin-button,
    input::-webkit-inner-spin-button {
      -webkit-appearance: none;
    }
    input[type='number'] {
      -moz-appearance: textfield;
    }
  }
}
View Code
复制代码

3、效果:

posted @   吴小明-  阅读(1154)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· 地球OL攻略 —— 某应届生求职总结
点击右上角即可分享
微信分享提示