二进制流进行预览pdf、excel、docx

发布于:2025-09-04 ⋅ 阅读:(13) ⋅ 点赞:(0)

安装

  • @vue-office/docx:Word文档预览组件
  • @vue-office/excel:Excel文档预览组件
  • @vue-office/pdf:PDF文档预览组件
  • #docx文档预览组件
    npm install @vue-office/docx vue-demi
    
    #excel文档预览组件
    npm install @vue-office/excel vue-demi
    
    #pdf文档预览组件
    npm install @vue-office/pdf vue-demi

    引入

  • import VueOfficePdf from '@vue-office/pdf/lib/v3'
    import VueOfficeDocx from '@vue-office/docx/lib/v3'
    import VueOfficeExcel from '@vue-office/excel/lib/v3'
    import '@vue-office/docx/lib/v3/index.css'
    import '@vue-office/excel/lib/v3/index.css'

    使用

        <el-dialog
          v-model="previewDialogVisible"
          :title="previewFileName"
          width="80%"
          height="80%"
          top="5vh"
          :before-close="handlePreviewClose"
          destroy-on-close
        >
          <div class="preview-container">
            <div class="preview-info">
              <!-- 加载状态 -->
              <div v-if="loadingPreview" class="loading-container">
                <el-skeleton animated>
                  <template #template>
                    <el-skeleton-item variant="p" style="height: 300px" />
                  </template>
                </el-skeleton>
                <p style="text-align: center; margin-top: 20px;">文件加载中...</p>
              </div>
              <!-- 2. 错误状态(加载失败时显示) -->
              <div v-else-if="previewError" class="error-container">
                <el-icon color="#f56c6c" size="48" style="margin-bottom: 16px;">
                  <CircleCloseFilled />
                </el-icon>
                <p style="color: #f56c6c;">{{ previewError }}</p>
              </div>
            </div>
    
            <!-- PDF文件预览 -->
            <VueOfficePdf
              v-if="currentPreviewData?.fileType === 'pdf'"
              :src="previewFileUrl"
              class="pdf-preview"
              @rendered="renderedHandler"
              @error="errorHandler"
              style="height: 70vh;"
            />
            <!-- docx文件预览 -->
            <vue-office-docx
              v-else-if="['docx', 'doc'].includes(currentPreviewData?.fileType)"
              :src="previewFileUrl"
              style="height: 70vh;"
              @rendered="renderedHandler"
              @error="errorHandler"
            />
            <!-- excel文件预览 -->
            <vue-office-excel
              v-else-if="['xlsx', 'xls'].includes(currentPreviewData?.fileType)"
              :src="previewFileUrl"
              :options="options"
              style="height: 70vh;"
              @rendered="renderedHandler"
              @error="errorHandler"
            />
            <!-- 4. 不支持的文件格式(无匹配时显示) -->
            <div v-else class="unsupported-container">
              <el-icon color="#909399" size="48" style="margin-bottom: 16px;">
                <InfoFilled />
              </el-icon>
              <p style="color: #909399;">
                不支持当前文件格式({{ currentPreviewData?.fileType || '未知' }}),请点击"下载文件"查看
              </p>
            </div>
          </div>
    
          <template #footer>
            <div class="dialog-footer">
              <el-button @click="handlePreviewClose">关闭</el-button>
    <!--          <el-button type="primary" @click="handleDownloadCurrentFile">下载文件</el-button>-->
            </div>
          </template>
        </el-dialog>

    通过二进制流转换成本地Blob文件 url 进行预览


// 获取文件扩展名
const getFileExtension = (fileName) => {
  if (!fileName) return ''
  return fileName.slice(((fileName.lastIndexOf('.') - 1) >>> 0) + 2).toLowerCase()
}

// 名称点击事件
const handlePrintArchiveName = async (rowData) => {
  console.log(rowData.fileType)
  // 检查是否有预览权限
  if (!permission.value.download) {
    ElMessage.warning('您没有预览权限')
    return
  }

  // 检查是否有所需的文件信息
  if (!rowData.importFullFilePath && !rowData.filePath) {
    ElMessage.warning('文件信息不完整,无法预览')
    return
  }

  try {
    // 重置状态
    loadingPreview.value = true
    previewError.value = ''
    currentPreviewData.value = rowData
    previewFileName.value = rowData.archiveName || '文件预览'

    let quryDomainUrl = changeUrl('xzspknowledgearchive', 'common/xzspknowledgearchive/downloadfile')
    const response = await commonPosts(quryDomainUrl, {
      businessFlg: 'download',
      moduleName: 'xzspknowledgearchive',
      uniqueId: rowData.uniqueId,
      fileName: rowData.filePath
    }, {
      repeatSubmit: false, // 按需传递防重复提交参数
      responseType: 'blob' // 关键:指定响应类型为 blob
    })
    // 5. 生成本地预览URL(核心步骤)
    const fileExt = getFileExtension(rowData.filePath)
    const mimeType = getMimeTypeByExt(fileExt)
    // 用正确的 MIME 类型重新包装 Blob(解决类型缺失问题)
    const fileBlob = new Blob([response], { type: mimeType })
    previewFileUrl.value = URL.createObjectURL(fileBlob)
    // 显示预览弹窗
    previewDialogVisible.value = true
    // 延迟关闭加载状态,给文件一些加载时间
    setTimeout(() => {
      loadingPreview.value = false
    }, 1000)
  } catch (error) {
    console.error('预览失败:', error)
    loadingPreview.value = false
    previewError.value = '文件预览失败,请重试'
    ElMessage.error('文件预览失败,请重试')
  }
}

// 新增:根据文件后缀获取 MIME 类型的工具函数
const getMimeTypeByExt = (ext) => {
  const mimeMap = {
    // Office 文档
    xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    xls: 'application/vnd.ms-excel',
    docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    doc: 'application/msword',
    // 其他常见类型
    pdf: 'application/pdf',
    txt: 'text/plain',
    zip: 'application/zip'
  }
  return mimeMap[ext.toLowerCase()] || 'application/octet-stream' // 默认二进制类型
}

// PDF渲染完成回调
const renderedHandler = () => {
  console.log('PDF渲染完成')
  loadingPreview.value = false
}

// PDF渲染错误回调
const errorHandler = (error) => {
  console.error('PDF渲染失败:', error)
  loadingPreview.value = false
  previewError.value = 'PDF文件渲染失败,请尝试下载查看'
}

// 关闭预览弹窗
const handlePreviewClose = () => {
  previewDialogVisible.value = false
  previewFileUrl.value = ''
  previewFileName.value = ''
  currentPreviewData.value = null
  loadingPreview.value = false
  previewError.value = ''
}