<template>
  <el-button type="primary" @click="exportToExcel">
    <img src="@/assets/imgs/OA/open.png" class="mr-8px" alt="" />
    导出
  </el-button>
</template>
<script setup lang="ts">
/**
 * @description 导出为excel
 */
import * as XLSX from 'xlsx'
import { saveAs } from 'file-saver'
interface IProp {
  data: any[][] // 数据 [[表头，表头，表头],[数据，数据，数据]]
  fileName?: string // 文件名
  mergeRanges?: {
    s: { r: number; c: number } // 合并的起始单元格
    e: { r: number; c: number } // 合并的结束单元格
  }[] // 合并单元格列表
  colsWidth?: { wch: number }[] // 列宽
  title?: string // 标题
}
// 定义组件props
const props = defineProps<IProp>()
const titleStyle = {
  font: {
    bold: true,
    size: 20,
    color: { rgb: '#000000' }, // 设置颜色
    name: 'Arial' // 设置字体为Arial
  },
  alignment: {
    horizontal: 'center', // 水平居中
    vertical: 'middle' // 垂直居中
  },
  '!important': true // 添加 !important 规则
}
// 导出Excel函数
const exportToExcel = () => {
  // 从props获取数据
  const data = props.data

  // 如果有标题，添加标题
  if (props.title) {
    // 创建一个包含样式信息的单元格对象（样式未生效，不知道是什么原因）
    const titleCell = { v: props.title, s: titleStyle }
    // 将单元格对象添加到数据中
    data.unshift([titleCell])
  }

  // 创建一个工作簿
  const wb = XLSX.utils.book_new()

  // 创建一个工作表
  const ws = XLSX.utils.aoa_to_sheet(data)

  // 如果有标题, 则在第一行插入标题
  if (props.title) {
    // 添加标题并合并单元格
    const mergeRange = { s: { r: 0, c: 0 }, e: { r: 0, c: data[1].length - 1 } } // 合并单元格范围
    ws['!merges'] = [mergeRange] // 合并单元格
  }

  // 多个合并范围 合并单元格
  if (props.mergeRanges) {
    ws['!merges'] = props.mergeRanges
  }

  // 设置列宽，定义多个列宽
  if (props.colsWidth) {
    ws['!cols'] = props.colsWidth ?? []
  }

  // 将工作表添加到工作簿中
  XLSX.utils.book_append_sheet(wb, ws, 'Sheet1')

  // 生成Excel文件
  const wbout = XLSX.write(wb, { type: 'binary', bookType: 'xlsx' })

  // 下载Excel文件
  saveAs(
    new Blob([s2ab(wbout)], { type: 'application/octet-stream' }),
    props.fileName || 'exported_data.xlsx'
  )
}

// 将二进制字符串转换为字节数组
const s2ab = (s: string) => {
  const buf = new ArrayBuffer(s.length)
  const view = new Uint8Array(buf)
  for (let i = 0; i < s.length; i++) view[i] = s.charCodeAt(i) & 0xff
  return buf
}
</script>

<style scoped></style>
