Vue 图片和PDF预览组件

Vue 图片和PDF预览组件

可能只有我自己能看懂吧
就是一个vue的图片预览和PDF预览

<template>
  <el-dialog :visible.sync="fileObj.isShowDialog" width="70%" top="7vh" v-dialogDrag :close-on-click-modal="false"
    append-to-body custom-class="dialogImgBox" @close="close" :fullscreen="dialogFull" v-loading="loading">
    <template slot="title">
      <div style="display: flex;align-items:center;">
        <span class="mr10">文件预览</span>
        <i class="el-icon-full-screen" style="z-index: 999;color:red;cursor: pointer;font-size: 24px;"
          @click="dialogFull? dialogFull=false: dialogFull=true"></i>
      </div>
    </template>
    <div class="flex1 flex flexcolumn autoX autoY">
      <div :class="['txtcenter', 'flex', 'mb15', $style.btnSize]">
        <i class="el-icon-zoom-out cursor" @click="zoomOut" title="缩小" v-if="isImg"></i>
        <i class="el-icon-zoom-in cursor ml20 mr20" @click="zoomIn" title="放大" v-if="isImg"></i>
        <i class="el-icon-refresh-right cursor mrAuto" @click="rotateImg" title="图片右转" v-if="isImg"></i>
        <i :class="[showFileList ? 'el-icon-s-unfold' : 'el-icon-s-fold', 'cursor']"
          v-if="fileObj.fileList && fileObj.fileList.length > 1" @click="showFileList = !showFileList" title="图片列表"></i>
      </div>
      <div class="flex1 autoX autoY" v-if="isImg">
        <img ref="dialogimg" :width="imgWidth" :height="imgHeight" v-if="fileurl" :src="fileurl"
          :style="{ transform: 'rotate(' + imgrotate + 'deg)'}">
      </div>
      <!-- 加载全部页面的PDF是循环生成,不能指定ref,不能调用print打印方法 -->
      <div class="flex1 autoX autoY" v-if="isPdf">
        <pdf v-for="i in numPages" :key="i" :page="i" :src="fileurl" style="width: 100%; height: auto;"
          @num-pages="pageCount = $event">
        </pdf>
      </div>
    </div>

    <!-- 右边的切换的文件名 -->
    <div :class="[$style.dialogImgList, showFileList ? '' : $style.dialogImgListClose, 'autoY']"
      v-if="fileObj.fileList && fileObj.fileList.length > 0">
      <!-- <p @click="showFile({url: item.url, type: item.type, name: item.name})" :class="['cursor', 'mb15', name === item.name ? $style.active : '']" v-for="(item, index) of fileObj.fileList" :key="index"> {{ item.name }}</p> -->
      <!-- <el-table
      :data="fileObj.fileList"
      style="width: 100%"
      border
      size="mini"
      >
      <el-table-column
        prop="zlmc"
        label="资料名称"
        show-overflow-tooltip
        >
      </el-table-column>
      <el-table-column
        prop="name"
        label="文件名称"
        show-overflow-tooltip
        >
        <template slot-scope="scope">
          <p
          @click="showFile({url: scope.row.url, type: scope.row.type, name: scope.row.name})"
          :class="['cursor', name === scope.row.name ? $style.active : '']"
          >
          {{ scope.row.name }}
          </p>
        </template>
      </el-table-column>
      </el-table> -->

      <div class="el-tree-dialog">
        <el-tree v-if="fileObj.isShowDialog" :data="dataList" :default-expand-all="true" :highlight-current="true"
          :props="defaultProps" node-key="id" :current-node-key="currentId" @node-click="handleNodeClick">
        </el-tree>
      </div>

    </div>
  </el-dialog>
</template>

<script>
import '@/utils/drag.js'
// import ElDragDialog from '@/directive/el-drag-dialog'
import pdf from 'vue-pdf'
import CMapReaderFactory from "vue-pdf/src/CMapReaderFactory.js";
import { getArrayBufferFile, getBlobFile } from '@/api/permission/getFileStream'
export default {
  name: 'FilePrwview',
  // directives: {
  //   ElDragDialog
  // },
  props: {
    fileObj: {
      type: Object,
      default: () => {
        return {
          fileList: [], // 文件列表, {url: '', name: '', type: ''}
          isShowDialog: false, // 是否显示弹窗
          url: '', // 是否有默认url传入
          type: '',
          name: '',
        }
      }
    }
  },
  watch: {
    fileObj: {
      handler: function (val) {
        if (val && val.isShowDialog) {
          this.dataList = this.sameValueConcat(val.fileList)
          this.dataList.forEach((item, index) => {
            item.name = item.zlmc
            item.id = item.id
          })
          this.currentId = val.id;
          if (val.url) {
            this.checkFileType(val)
          } else {
            this.showFile(val.fileList[0])
          }
        }
      },
      deep: true,
      immediate: true
    }
  },
  components: {
    pdf
  },
  data() {
    return {
      fileurl: '',
      isImg: false,  // 是否是图片
      isPdf: false,  // 是否是文件
      numPages: 0,
      pageCount: 0,
      showFileList: true,
      imgWidth: 'auto',
      imgHeight: 'auto',
      imgrotate: 0,
      name: '',
      loading: false,
      defaultProps: {
        children: 'children',
        label: 'name'
      },
      dataList: [],
      currentId: '',
      dialogFull: false,
    }
  },
  methods: {
    handleNodeClick(data) {
      this.currentId = data.id;
      this.showFile({ url: data.url, type: data.type, name: data.name, id: data.id })
    },
    sameValueConcat(data) {
      let dataInfo = {}
      data.forEach((item, index) => {
        let { zlbh } = item
        if (!dataInfo[zlbh]) {
          dataInfo[zlbh] = {
            zlbh,
            zlmc: item.zlmc,
            children: [],
          }
        }
        dataInfo[zlbh].children.push(item)
      })
      let list = Object.values(dataInfo)
      return list
    },
    // 查看图片
    checkFileType(val) {
      switch (val.type) {
        case 'img':
          this.showImg(val)
          this.isPdf = false
          this.isImg = true
          break
        case 'pdf':
          this.showReadFile(val)
          this.isImg = false
          this.isPdf = true
          break
      }
    },
    async loadPdfHandler() {
      // src为你服务器上存放pdf的路径
      // this.pdfUrl = pdf.createLoadingTask(this.filepdfurl)
      this.pdfUrl = pdf.createLoadingTask({
        url: this.fileurl,
        CMapReaderFactory
      })
      this.pdfUrl.promise.then(pdf => {
        this.numPages = pdf.numPages // 这里拿到当前pdf总页数
        this.loading = false
      }).catch(err => {
        console.log(err)
        this.loading = false
      })
    },
    showReadFile(val) {
      if (val.url) {
        console.log(val.url);
        this.loading = true
        getBlobFile(val.url).then(response => {  // 将后台的图片二进制流转化为base64
          this.fileurl = window.URL.createObjectURL(new Blob([response.data]))
          this.loadPdfHandler()
        }).catch(e => {
          this.$message.error('获取PDF数据失败,请联系管理员')
          this.loading = false
        })
      } else {
        this.$message.error('文件链接失效')
      }
      this.name = val.name
    },
    zoomOut() {
      const w = this.$refs.dialogimg.width
      this.imgWidth = w * 0.9
    },
    zoomIn() {
      const w = this.$refs.dialogimg.width
      this.imgWidth = w * 0.9
    },
    rotateImg() {
      this.imgrotate = this.imgrotate + 90
      if (this.imgrotate === 360) {
        this.imgrotate = 0
      }
    },
    close() {
      this.imgWidth = 'auto'
      this.imgHeight = 'auto'
      this.imgrotate = 0
      this.fileurl = ''
      this.$emit('closeDialog')
    },
    showImg(val) {
      if (val.url) {
        console.log(this.fileList, val.url)
        this.loading = true
        // Uint8Array 数组类型表示一个8位无符号整型数组,创建时内容被初始化为0。创建完后,可以以对象的方式或使用数组下标索引的方式引用数组中的元素。
        // String.fromCharCode 将 Unicode 编码转为一个字符   String.fromCharCode(65);  // 输出 A
        getArrayBufferFile(val.url).then(response => {  // 将后台的图片二进制流转化为base64
          this.fileurl = 'data:image/png;base64,' + btoa(
            new Uint8Array(response.data).reduce((data, byte) => data + String.fromCharCode(byte), '')
          )
        }).catch(e => {
          this.$message.error('获取图片数据失败:' + e)
        }).finally(() => {
          this.loading = false
        })
      } else {
        this.$message.error('文件链接失效')
      }
      this.name = val.name
    },
    showFile(val) {
      this.checkFileType(val)
    }
  }
}
</script>

<style lang="scss" module>
:global(.dialogImgBox) {
  display: flex;

  :global(.el-dialog__body) {
    flex: 1;
    display: flex;
    overflow: hidden;
  }

  .dialogImgList {
    width: 350px;
    margin-left: 10px;
    padding-left: 10px;
    border-left: 1px solid #eee;
    transition-property: width, border-left;
    transition-duration: .3s;

    .active {
      color: #409EFF;
    }

    .dialogImgListItem {
      padding-left: 10px;

      :hover {
        color: #409EFF;
      }

      :global(.active) {
        color: #409EFF;
      }
    }
  }

  .dialogImgListClose {
    width: 0;
    border-left: none;
  }
}

.btnSize {
  font-size: 30px;
}
</style>
<style lang="scss" scoped>
// .el-tree-dialog::v-deep .el-tree-node__content {
//   font-size: 18px!important;
// }
.el-tree-dialog::v-deep .el-tree-node__children {
  color: #409EFF !important;
}

.el-tree-dialog::v-deep .el-tree-node .is-current>.el-tree-node__content {
  background: rgba(0, 0, 0, 0.2);
  border-right: 3px solid #1677ff;
  color: red;

  .el-tree-node__expand-icon {
    color: red;
  }

  .is-leaf {
    color: rgba(0, 0, 0, 0);
  }
}

.active {
  color: red;
}
</style>

getFileStream.js

import axios from 'axios'
import { getToken, getSid } from '@/utils/auth'

const sid = getSid() || ''
const uiticket = getToken()

const service = axios.create({
  withCredentials: true, // send cookies when cross-domain requests
  // timeout: 5000 // request timeout
})

/**
 * 判断文件类型
 * @param {*} type
 * @returns
 */
export function getFileType (type) {
  let filetype = ''
  switch (type) {
    case 'jpg':
    case 'JPG':
    case 'jpeg':
    case 'JPEG':
    case 'gif':
    case 'GIF':
    case 'bmp':
    case 'BMP':
    case 'png':
    case 'PNG':
      filetype = 'img'
      break
    case 'pdf':
    case 'PDF':
      filetype = 'pdf'
      break
  }
  return filetype
}

/**
 * 判断文件类型
 * @param {*} type
 * @returns
 */
 export function getFileType2 (type) {
  let filetype = ''
  switch (type) {
    case 'jpg':
    case 'JPG':
    case 'jpeg':
    case 'JPEG':
    case 'gif':
    case 'GIF':
    case 'bmp':
    case 'BMP':
    case 'png':
    case 'PNG':
      filetype = 'img'
      break
    case 'pdf':
      filetype = 'pdf'
      break
    case 'PDF':
      filetype = 'pdf'
      break
    case 'doc':
      filetype = 'doc'
      break
    case 'DOC':
      filetype = 'doc'
      break
    case 'docx':
      filetype = 'docx'
      break
    case 'DOCX':
      filetype = 'docx'
      break
    case 'xlsx':
      filetype = 'xlsx'
      break
    case 'XLSX':
      filetype = 'xlsx'
      break
    case 'xls':
      filetype = 'xls'
      break
    case 'XLS':
      filetype = 'xls'
      break
  }
  return filetype
}


function pubDownloadFn (blob, name) {
  console.log(name,'999')
  let downloadElement = document.createElement('a')//创建<a>标签
  let href = window.URL.createObjectURL(blob) // 创建下载的链接
  downloadElement.href = href
  downloadElement.download = name // 下载后文件名
  document.body.appendChild(downloadElement)
  downloadElement.click() // 点击下载
  document.body.removeChild(downloadElement) // 下载完成移除元素
  window.URL.revokeObjectURL(href) // 释放掉blob对象
}

/**
 * 获取ArrayBuffer格式的二进制数据
 * @param {*} url
 * @returns
 */
export function getArrayBufferFile (url) {
  return service({
    method: 'get',
    url: window.location.protocol + '//' + window.location.host + url ,
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'uiticket': getToken() , // 必须添加的请求头
      'sid': getSid() || ''  // 必须添加的请求头
    },
    responseType: 'arraybuffer', // 关键 设置 响应类型为二进制流
  })
}


/**
 * 获取ArrayBuffer格式的二进制数据
 * @param {*} url
 * @returns
 */
 export function getArrayBufferFile2 (url) {
  return service({
    method: 'get',
    // url: window.location.protocol + '//' + window.location.host + url ,
    url: url ,
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'uiticket': getToken() , // 必须添加的请求头
      'sid': getSid() || ''  // 必须添加的请求头
    },
    responseType: 'arraybuffer', // 关键 设置 响应类型为二进制流
  })
}

/**
 * 获取Blob格式的二进制数据
 * @param {*} url
 * @returns
 */
export function getBlobFile (url) {
  return service({
    method: 'get',
    url: window.location.protocol + '//' + window.location.host + url ,
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'uiticket': getToken() , // 必须添加的请求头
      'sid': getSid() || ''  // 必须添加的请求头
    },
    responseType: 'blob', // 关键 设置 响应类型为二进制流
  })
}

/**
 * 获取Blob格式的二进制数据
 * @param {*} url
 * @returns
 */
 export function getBlobFile2 (url) {
  return service({
    method: 'get',
    // url: window.location.protocol + '//' + window.location.host + url ,
    url: url ,
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'uiticket': getToken() , // 必须添加的请求头
      'sid': getSid() || ''  // 必须添加的请求头
    },
    responseType: 'blob', // 关键 设置 响应类型为二进制流
  })
}


/**
 * 下载图片
 * @param {*} url
 * @param {*} name
 * @returns Promesion
 */
export function downloadImage (url, name) {
  return getArrayBufferFile(url).then(response => {  // 将后台的图片二进制流转化为base64
    const arr = name.indexOf('.') !== -1 ? name.split('.') : url.split('.')
    let typename = arr[arr.length - 1].toLowerCase()
    if (typename && (typename.indexOf('jpe') !== -1 || typename.indexOf('jpg') !== -1  || typename.indexOf('JPG') !== -1  || typename.indexOf('JPE') !== -1)) {
      typename = 'jpeg'
    }
    const blob = new Blob([response.data], { type: 'image/' + typename })//这里type指定下载文件的类型
    if (window.navigator.msSaveOrOpenBlob) {
      navigator.msSaveBlob(blob, name)
    } else {
      pubDownloadFn(blob, name)
    }
  })
}


/**
 * 下载图片
 * @param {*} url
 * @param {*} name
 * @returns Promesion
 */
 export function downloadImage2 (url, name) {
  return getArrayBufferFile2(url).then(response => {  // 将后台的图片二进制流转化为base64
    const arr = name.indexOf('.') !== -1 ? name.split('.') : url.split('.')
    let typename = arr[arr.length - 1].toLowerCase()
    if (typename && (typename.indexOf('jpe') !== -1 || typename.indexOf('jpg') !== -1  || typename.indexOf('JPG') !== -1  || typename.indexOf('JPE') !== -1)) {
      typename = 'jpeg'
    }
    const blob = new Blob([response.data], { type: 'image/' + typename })//这里type指定下载文件的类型
    if (window.navigator.msSaveOrOpenBlob) {
      navigator.msSaveBlob(blob, name)
    } else {
      pubDownloadFn(blob, name)
    }
  })
}


/**
 * 下载PDF
 * @param {*} url
 * @param {*} name
 */
export function downloadPdf (url, name) {
  return getBlobFile(url).then(response => {  // 将后台的图片二进制流转化为base64
    const blob = new Blob([response.data], { type: 'application/*' })//这里type指定下载文件的类型
    if (window.navigator.msSaveOrOpenBlob) {
      navigator.msSaveBlob(blob, name)
    } else {
      pubDownloadFn(blob, name)
    }
  })
}


/**
 * 下载PDF
 * @param {*} url
 * @param {*} name
 */
 export function downloadPdf2 (url, name) {
  return getBlobFile2(url).then(response => {  // 将后台的图片二进制流转化为base64
    const blob = new Blob([response.data], { type: 'application/*' })//这里type指定下载文件的类型
    if (window.navigator.msSaveOrOpenBlob) {
      navigator.msSaveBlob(blob, name)
    } else {
      pubDownloadFn(blob, name)
    }
  })
}

相关推荐

  1. Vue 图片PDF组件

    2024-05-26 06:08:14       13 阅读
  2. vue2自定义封装图片组件

    2024-05-26 06:08:14       30 阅读
  3. pdf组件react-pdfpdfjs-dist

    2024-05-26 06:08:14       14 阅读
  4. vue实现图片

    2024-05-26 06:08:14       6 阅读
  5. vue-pdf pdf (数据流)

    2024-05-26 06:08:14       19 阅读
  6. Vue 移动端制作PDF

    2024-05-26 06:08:14       39 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-26 06:08:14       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-26 06:08:14       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-26 06:08:14       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-26 06:08:14       18 阅读

热门阅读

  1. 互联网十万个为什么之什么是分布式计算?

    2024-05-26 06:08:14       11 阅读
  2. 汇编:数据类型

    2024-05-26 06:08:14       11 阅读
  3. Python 爬虫编写入门

    2024-05-26 06:08:14       11 阅读
  4. Scala的简单学习二

    2024-05-26 06:08:14       9 阅读
  5. vue中实现动态点击事件名

    2024-05-26 06:08:14       8 阅读
  6. Docker打包nginx镜像丢失挂载的配置文件

    2024-05-26 06:08:14       15 阅读
  7. Leetcode704_二分查找

    2024-05-26 06:08:14       9 阅读
  8. StringMVC

    StringMVC

    2024-05-26 06:08:14      9 阅读
  9. 【MySQL精通之路】SQL优化(1)-查询优化

    2024-05-26 06:08:14       10 阅读
  10. 前端后端是什么

    2024-05-26 06:08:14       12 阅读
  11. SpringBoot配置优先级

    2024-05-26 06:08:14       9 阅读