previewFile.vue 9.28 KB
<template>
  <div style="z-index: 1000; height: 75vh; overflow: auto;">
    <img v-if="imgUrl" :src="imgUrl" alt="" style="width: 100%; height: 100%"/>
    <!-- pdf和txt使用iframe -->
    <pdf-view v-if="pdfUrl" v-model="pdfUrl" :fileName="fileName"></pdf-view>
    <!--<iframe v-if="pdfUrl" :src="pdfUrl" frameborder="0" style="width: 100%; height: 100%; min-height: 100vh"></iframe>-->
    <video v-if="videoUrl" :src="videoUrl" controls style="width: 100%; height: 100%; max-height: 800px"></video>
    <div v-if="docFile" ref="word" style="height:100%">
      <iframe v-if="fileUrl" :src="fileUrl" width='100%' height='100%' frameborder='1'></iframe>
    </div>
    <div v-if="docxFile">
      <div ref="file"></div>
    </div>
    <div v-if="xlsFile" class="excel-view-container">
      <!-- Excel使用tab选项卡来模拟表格里的sheet业 -->
      <a-tabs type="card" v-if="sheetNames && sheetNames.length" @change="tabsClick">
        <a-tab-pane v-for="(item, index) in sheetNames" :key="index" :tab="item">
          <div id="excelView" v-html="excelView"></div>
        </a-tab-pane>
      </a-tabs>
    </div>
  </div>
</template>

<script>
import axios from 'axios'
import pdfView from '@/views/components/common/pdfView'
import { getFileExtension } from '@/views/utils/common'

//定义blob对应的type
const fileTypeMap = {
  xls: 'application/vnd.ms-excel',
  xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  doc: 'application/msword',
  docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  pdf: 'application/pdf',
  ppt: 'application/pdf',
  pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  png: 'image/png',
  gif: 'image/gif',
  jpeg: 'image/jpeg',
  jpg: 'image/jpeg',
  txt: 'text/plain',
};

export default {
  name: 'previewFile',
  props: {
    value: {
      type: String,
      default() {
        return null
      },
    },
    fileName: {
      type: String,
      default() {
        return null
      }
    }
  },
  components: {
    pdfView
  },
  data() {
    return {
      fileType: '',     //文件类型
      data: '',         //文件路径,blob 类型
      imgUrl: '',       //图片路径
      pdfUrl: '',       //pdf路径
      videoUrl: '',     //视频路径
      excelView: '',    //表格转换后的html数据
      docFile: false,   //是否是老版word文件(doc)
      docxFile: false,  //是否是新版word文件(docx)
      xlsFile: false,   //是否是Excel文件
      execlArraybufferData: null, //Excelblob转换为arraybuff数据
      sheetNames: null, //从数据中获取到的sheet页数组
      imgType: [ 'bmp','jpg','jpeg','png','tif','gif','pcx','tga','exif','fpx','svg','psd','cdr','pcd','dxf','ufo','eps','ai','raw','WMF','webp','avif','apng' ],
      videoType: [ 'wmv','asf','asx','rm','rmvb','mp4','3gp','mov','m4v','avi','dat','mkv','flv','vob' ],
      wordType: [ 'text','pdf','doc','docx','xls','xlsx','ppt','pptx','rar','zip','7z','apz','ar','bz','car','dar','cpgz','f','ha','hbc','hbc2','hbe','hpk','hyp' ],
      fileUrl: '',
      previewAnnex: false,
      previewUrl: '',
      previewType: '',
      fileData: null
    }
  },
  mounted() {
    this.coverUrl()
  },
  methods: {
    // 预览文件
    filePreviewPDF() {
      this.fileType = this.previewType
      if (this.imgType.includes(this.fileType)) {
        // 图片类型的
        this.imgUrl = window.URL.createObjectURL(this.data)
      } else if (this.videoType.includes(this.fileType)) {
        // 视频类型的
        this.videoUrl = window.URL.createObjectURL(this.data)
      } else if (this.fileType == 'pdf' || this.fileType == 'txt') {
        // pdf和文本类型的,用iframe打开
        this.pdfUrl = this.previewUrl//window.URL.createObjectURL(this.data)
      } else if (this.fileType == 'doc') {
        this.docFile = true
        this.fileUrl = 'https://view.officeapps.live.com/op/view.aspx?src=' + location.origin + this.value
      } else if (this.fileType == 'docx') {
        // word类型的用docx-preview插件
        this.docxFile = true
        axios.request({
          method: 'GET', 
          url: this.previewUrl,
          responseType: 'arraybuffer',
        }).then((res) => {
          if (res) {
            let docx = require('docx-preview')
            docx.renderAsync(res.data, this.$refs.file)
          } else {  
            this.$message.error('数据接口请求失败!')
          }
        })
      } else if (this.fileType == 'xls' || this.fileType == 'xlsx') {
        // 表格类型的用xlsx插件
        this.xlsFile = true
        axios.request({
          method: 'GET', 
          url: this.previewUrl,
          responseType: 'blob',
          headers: {
            "Content-Type": "application/vnd.ms-excel;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
          },
        }).then((res) => {
          if (res) {
            let reader = new FileReader()
            reader.readAsArrayBuffer(res.data) // blob类型转换为ArrayBuffer类型
            this.tabChange(0, reader)
          } else {  
            this.$message.error('数据接口请求失败!')
          }
        })
      } else {
        //this.handleClose();
        this.$message.error('不支持此文件预览')
      }
    },
    tabChange(index, reader) {
      this.excelView = ''
      let XLSX = require('xlsx')
      let _this = this

      // 如果第一次进来
      if (!this.sheetNames) {
        // 文件转换加载完成后
        reader.onload = function () {
          let arraybufferData = this.result
          this.execlArraybufferData = arraybufferData
          let data = new Uint8Array(arraybufferData) // es2017的方法
          let workbook = XLSX.read(data, { type: 'array' }) // 得到表格的array数据
          _this.workbooks = workbook // 赋值到此组件最外面,一会要用
          let sheetNames = workbook.SheetNames // 得到execl工作表名称集合,结果类似这样['sheet1','sheet2']
          _this.sheetNames = sheetNames // 赋值到此组件最外面,一会要用
          let worksheet = workbook.Sheets[sheetNames[index]] // 获取第几个工作表0就是'sheet1',以此类推
          _this['excelView'] = XLSX.utils.sheet_to_html(worksheet) // 把表格的array数据转换成html数据
          _this.$nextTick(function () {
            // DOM加载完毕后执行,解决HTMLConnection有内容但是length为0问题。
            _this.setStyle4ExcelHtml()
          })
        }
      } else {
        // 已经有数据了的时候直接获取对应sheet里的内容
        let worksheet = this.workbooks.Sheets[this.sheetNames[index]]
        this['excelView'] = XLSX.utils.sheet_to_html(worksheet)
        this.$nextTick(function () {
          // DOM加载完毕后执行,解决HTMLConnection有内容但是length为0问题。
          this.setStyle4ExcelHtml()
        })
      }
    },
    // 设置Excel转成HTML后的样式
    setStyle4ExcelHtml() {
      const excelViewDOM = document.getElementById('excelView')
      if (excelViewDOM) {
        const excelViewTDNodes = excelViewDOM.getElementsByTagName('td') // 获取的是HTMLConnection
        if (excelViewTDNodes) {
          const excelViewTDArr = Array.prototype.slice.call(excelViewTDNodes)
          for (const i in excelViewTDArr) {
            const id = excelViewTDArr[i].id // 默认生成的id格式为sjs-A1、sjs-A2......
            if (id) {
              const idNum = id.replace(/[^0-9]/gi, '') // 提取id中的数字,即行号
              if (idNum && (idNum === '1' || idNum === 1)) {
                // 第一行标题行
                excelViewTDArr[i].classList.add('class4Title')
              }
              if (idNum && (idNum === '1' || idNum === 1)) {
                // 第二行表头行
                excelViewTDArr[i].classList.add('class4TableTh')
              }
            }
          }
        }
      }
    },
    tabsClick (key) {
      this.tabChange(key)
    },
    //文件预览判断
    coverUrl () {
      var type = getFileExtension(this.fileName)
      this.previewType = type
      this.previewUrl = this.value
      this.previewAnnex = true
      this.filePreviewPDF()
      return Promise.resolve()
    },
  },
  watch: {
    previewType: {
      handler(value) {
        this.fileType = value
      },
      immediate: true, // 重点
      deep: true, // 重点
    },
    previewUrl: {
      handler(value) {
        this.data = value
      },
      immediate: true, // 重点
      deep: true, // 重点
    },
  },
};
</script>

<style lang="less" scoped="">
  ::v-deep table {
    width: 100% !important;
    border-collapse: collapse !important;
    border-spacing: 0 !important;
    text-align: center !important;
    border: 0px !important;
    overflow-x: auto !important;
  }
 
  ::v-deep table tr td {
    border: 2px solid gray !important; 
    width: 300px !important;
    height: 33px !important;
  }
  /**整体样式 */
  ::v-deep .excel-view-container {
    background-color: #ffffff;
  }
  /**标题样式 */
  ::v-deep .class4Title {
    font-size: 22px !important;
    font-weight: bold !important;
    padding: 10px !important;
  }
  /**表格表头样式 */
  ::v-deep .class4TableTh {
    /* font-size: 14px !important; */
    font-weight: bold !important;
    padding: 2px !important;
    background-color: #ccc !important;
  }
</style>