记录table生成稍微复杂excel

92 阅读1分钟

代码

/**
 * column 表头
 * {
 *    label: string 名称,
 *    prop: string 对应data字段,
 *    placeholder: boolean 是否把单元格变为占位符,用于头部行合并,
 *    headerslot: boolean 是否在表头上方插入数据,
 *    boottomslot: boolean 是否在表底部插入数据,
 *    style: string 单元格样式,
 *    colspan: number 列合并,
 *    rowspan: number 行合并,
 *    noExport: boolean 是否导出此列,
 *    exceltype: string 列类型
 * } array
 *
 * data 表格数据 array
 * title 表名称 string
 * retract 缩进字段 string array
 * tableHead 拼接表头
 * tableBody 拼接表格
 */
import { dateFormat } from '@/views/accounting/utils/date'

export const excel = {
  column: [],
  data: [],
  title: '',
  tableBody: '',
  tableHead: '',
  retract: '',
  option: function(option = { title: '', column: null, data: null }) {
    const msg = {
      column: 'the column is null',
      data: 'the data is null'
    }
    for (const key in msg) {
      if (!option[key]) {
        console.error(msg[key])
        return false
      }
    }
    this.title = option.title
    this.column = this.filterColumn(option.column)
    this.data = option.data
    this.retract = option.retract
    this.download()
  },
  /**
   * 下载
   */
  download: async function() {
    const table = await this.generateTable()
    const worksheet = `${this.title}_${dateFormat(new Date(), 'yyyyMMddhhmmss')}`
    const uri = 'data:application/vnd.ms-excel;base64,'
    const template = `
          <html xmlns:o="urn:schemas-microsoft-com:office:office"
          xmlns:x="urn:schemas-microsoft-com:office:excel"
          xmlns="http://www.w3.org/TR/REC-html40">
          <head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet>
          <x:Name>${worksheet}</x:Name>
          <x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>
          </x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]-->
          </head><body>${table}</body></html>`
    const a = document.createElement('a')
    a.href = uri + window.btoa(unescape(encodeURIComponent(template)))
    a.download = `${worksheet}.xls`
    a.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }))
    this.done()
  },
  /**
   * 生成table
   */
  generateTable: async function() {
    // 判断是否需要复杂表头 (tips:目前只支持表头两层嵌套)
    if (this.column.some(item => item.children && item.children.length && !item.headerslot && !item.bottomslot)) {
      await this.generateColspanHead()
      this.column = await this.flagColumn()
    }
    await this.generateHead()
    await this.generateBody(this.data)
    return `<table border="1" cellpadding="0" cellspacing="0" style="vnd.ms-excel.numberformat:@;border-collapse:collapse; text-align: center;">
              ${this.generateSlot('headerslot')}
              ${this.tableHead}
              ${this.tableBody}
              ${this.generateSlot('bottomslot')}
            </table>`
  },
  /**
   * 生成table header
   */
  generateHead: function() {
    this.tableHead += '<tr>'
    for (let i = 0; i < this.column.length; i++) {
      const item = this.column[i]
      if (item.headerslot || item.bottomslot) {
        continue
      }
      if (item.placeholder) {
        continue
      }
      this.tableHead += `<td style="font-weight: bold">${item.label}</td>`
    }
    this.tableHead += '</tr>'
  },
  /**
   * 生成复杂 head
   */
  generateColspanHead: function() {
    this.tableHead += '<tr>'
    for (let i = 0; i < this.column.length; i++) {
      const item = this.column[i]
      if (item.headerslot || item.bottomslot) {
        continue
      }
      if (item.children && item.children.length) {
        this.tableHead += `<td style="font-weight: bold" colspan="${item.children?.length}">${item.label}</td>`
      } else {
        this.tableHead += `<td style="font-weight: bold" rowspan="2">${item.label}</td>`
      }
    }
    this.tableHead += '</tr>'
  },
  /**
   * 生成table 主体
   */
  generateBody: function(data, level = 0) {
    if (!data.length) {
      return false
    }
    data.forEach(item => {
      this.tableBody += `<tr>`
      for (let i = 0; i < this.column.length; i++) {
        const col = this.column[i]
        if (!col.headerslot && !col.bottomslot) {
          if (isNaN(Number(item[col.prop])) || col.exceltype === 'string') {
            this.tableBody += `<td style="padding-left: ${this.retract && (this.retract.includes(col.prop) || this.retract === col.prop) ? level * 30 : 0}px">${item[col.prop] || ''}</td>`
          } else {
            this.tableBody += `<td style="vnd.ms-excel.numberformat:#,##0.00;">${Number(item[col.prop]) || ''}</td>`
          }
        }
      }
      this.tableBody += `</tr>`
      if (item.children && item.children.length) {
        this.generateBody(item.children, level + 1)
      }
    })
  },
  /**
   * slottype:[headerslot, bottomslot]
   * 生成底部或者头的插入行
   */
  generateSlot: function(slottype) {
    let slot = ''
    for (let i = 0; i < this.column.length; i++) {
      const item = this.column[i]
      if (item[slottype]) {
        if (item.children && item.children.length) {
          slot += `<tr>`
          item.children.forEach(d => {
            slot += `<td rowspan="${d.rowspan}" colspan="${d.colspan}" style="${d.style}">${d.label}</td>`
          })
          slot += `</tr>`
          continue
        }
        slot += `
          <tr>
            <td rowspan="${item.rowspan}" colspan="${item.colspan}" style="${item.style}">${item.label}</td>
          </tr>
        `
      }
    }
    return slot
  },
  /**
   * 过滤不需导入的列
   */
  filterColumn(column) {
    const newColumn = []
    for (let i = 0; i < column.length; i++) {
      const item = column[i]
      if (!item.noExport) {
        newColumn.push(item)
      }
    }
    return newColumn
  },
  /**
   * 扁平化head column
   */
  flagColumn: function() {
    const newColumn = []
    for (let i = 0; i < this.column.length; i++) {
      const item = this.column[i]
      if (item.children && item.children.length && !item.headerslot && !item.bottomslot) {
        newColumn.push(...item.children)
        continue
      }
      item.placeholder = true
      newColumn.push(item)
    }
    return newColumn
  },
  /**
   * 下载完成
   */
  done() {
    this.tableBody = ''
    this.tableHead = ''
    this.column = []
    this.data = []
    this.title = ''
    this.retract = ''
  }
}

示例

excel.option({
  title: '资产负债表',
  column: [
    {
      label: '资产负债表',
      headerslot: true,
      style: 'text-align: center; height: 40px; font-size: 20px',
      colspan: this.column.length
    },
    {
      label: dateFormat(new Date(), 'yyyy-MM-dd'),
      headerslot: true,
      style: 'text-align: center',
      colspan: this.column.length
    },
    {
      headerslot: true,
      children: [
        {
          label: `编制单位:${this.bizInfo.warehouse.deptName}`,
          style: 'text-align: left; border-right: 0',
          colspan: this.column.length / 2
        },
        {
          label: `单位:元`,
          style: 'text-align: right; border-left: 0',
          colspan: this.column.length / 2
        }
      ]
    },
    ...this.column
  ],
  data: this.data
})

导出效果

image.png

完全根据自己的业务编写,如果您要借鉴,请自行修改!