vue + exceljs + file-saver + html2canvas导出图片到excel中的实现方法

        给大家分享一个前端导出图片到excel中的实现方法。

一、需求

        把每个单元格内图片和文字整体绘制成一张图片,再将每张绘制完的图片按照页面的排版导出到excel中,其目的是在导出的excel中便于直接复制图片使用。

图片1图片2图片3图片4图片5
图片1图片2图片3图片4图片5
图片1图片2图片3图片4图片5

二、导出结果

三、实现过程

        使用html2canvas将每个单元格里的文字和图片作为一个整体图片绘制出来,使用canvas.toDataURL获取图片的base64数据,使用exceljs中的addImage方法添加图片到excel里,最后使用file-save中的saveAs方法导出。

        在实现的过程中,如果遇到图片跨域,需要添加配置config = { useCORS: true }到html2canvas(element, config)中,否则导出的图片不显示。

        如果导出的图片很多,可以使用消息队列分批并行导出。

1、安装依赖

npm install exceljs
npm install html2canvas
npm install file-saver

2、页面代码

  <div>
    <a-button @click="exportToExcel">导出为Excel</a-button>
    <div class="groupBox">
      <div class="groupItem" v-for="(group, colIndex) in listData" :key="colIndex">
        <div
          class="cellBox"
          ref="imageRef"
          v-for="(element, elementIndex) in group"
          :key="elementIndex"
        >
          <div>图片{{elementIndex+1}}</div>
          <img
            v-if="element?.src"
            :width="150"
            :height="150"
            :src="element?.src"
          />
        </div>
      </div>
    </div>
  </div>

3、ts代码

//导出Excel
const imageRef = ref<any>(null)
const config = { useCORS: true } //HTML2canvas绘图时图片允许跨域

// 消息队列的操作接口
interface MessageQueueOperation {
  execute(): Promise<void>
}

// 创建一个队列操作的工厂函数
function createOperation(
  rowIndex: number,
  queueList: any[],
  workbook: any,
  worksheet: any,
): MessageQueueOperation {
  return {
    execute: async () => {
      console.log(`Operation ${rowIndex} is starting.`)
      for (let i = 0; i < queueList.length; i++) {
        let canvas = await html2canvas(queueList[i], config)
        let context = canvas.getContext('2d')
        let imageBase64 = canvas.toDataURL('image/png')
        context?.clearRect(0, 0, canvas.width, canvas.height)
        if (imageBase64.startsWith('data:image/')) {
          const imageId = workbook.addImage({
            base64: imageBase64,
            extension: 'png',
          })

          worksheet.addImage(imageId, {
            tl: { col: i, row: rowIndex },
            ext: { width: 200, height: 200 },
          })
        }
        worksheet.columns[i].width = 27
        const row = worksheet.getRow(rowIndex + 1)
        row.height = 310 // 设置行高
      }
      // await new Promise(resolve => setTimeout(resolve, rowIndex * 100))
      console.log(`Operation ${rowIndex} is completed.`)
    },
  }
}

// 并行执行操作
async function executeOperationsConcurrently(operations: MessageQueueOperation[]) {
  const promises = operations.map(op => op.execute())
  await Promise.all(promises)
}

const batchArray = (array, batchSize, workbook, worksheet) => {
  let curRowIndex = 0
  let result: any = []
  for (let i = 0; i < array.length; i += batchSize) {
    let opt = createOperation(curRowIndex, array.slice(i, i + batchSize), workbook, worksheet)
    result.push(opt)
    curRowIndex++
  }
  return result
}

//导出
function exportToExcel() {
  const workbook = new Workbook()
  const worksheet = workbook.addWorksheet('Sheet1')
  if (imageRef.value) {
    let optList = batchArray(imageRef.value, 10, workbook, worksheet)
    executeOperationsConcurrently(optList).then(async () => {
      console.log('All operations completed.')
      const buffer = await workbook.xlsx.writeBuffer()
      const blob = new Blob([buffer], {
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      })
      saveAs(blob, '测试.xlsx')
    })
  }
}

4、css代码

.groupBox {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-content: flex-start;

  .groupItem {
    display: flex;
    flex-direction: column;
    margin: 7px;

    .cellBox {
      max-width: 60rem;
      display: flex;
      align-items: center;
      text-align: center;
      flex-direction: column;
      background-color: #f6f8fa;
      padding: 8px 16px;
      margin-top: 0.5rem;
      border: solid 1px #ccc9c9;
      cursor: move;

      img {
        cursor: pointer;
      }
    }
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值