拿走即用的Excel导出方法封装缺陷修正!

前言

# 用xlsx-style玩转Excel导出------拿走即用的Excel导出方法封装 一文中,我们封装了可以实现多种自定义需求的Excel导出方法。然而,在最近的需求开发中,发现此方法在自定义单元格颜色方面有缺陷,故重新修改记录一下。

缺陷

原方法,只能整体定义表头区域样式、内容区域样式,颗粒度未到达每一个单元格。

也就是说,通过原方法定义后,表头区域所有单元格的样式是一样的,内容区域所有单元格的样式也是一样的。

这就不能满足不同单元格展示不同样式的需求。

改进

加入setCellStyle回调函数。

通过setCellStyle函数获取单元格信息,并根据需要处理单元格样式逻辑。

js 复制代码
/** 
  * 配置单元格样式
  * @param { 单元格字段名 } key
  * @param { 单元格内容信息 } info
  * @param { 表头/内容区域区分 } type 
*/ 
  function setCellStyle(key, info, type) { 
    if(条件判断) {
      return {
      }
    }
    return {
        border: {top: {style: 'thin'},bottom: {style: 'thin'},left: {style: 'thin'},right: {style: 'thin'},},
        font: { name: '宋体', sz: 11, italic: false, underline: false },
        alignment: { vertical: 'center', horizontal: 'left',},
        fill: { fgColor: { rgb: 'FFFFFF' } },
    }
  
  }

1.表头

js 复制代码
 // 处理sheet表头
  const _header = data.header.map((item, i) =>
      Object.assign({}, {
        key: item.dataIndex,
        title: item.title,
        // 定位单元格
        position: getCharCol(i) + 1,
        // 设置表头样式
        s: setCellStyle ? setCellStyle(key, item, 'header') : defaultCellStyle.headerStyle,
      })
    ).reduce((prev, next) =>
      Object.assign({}, prev, {
        [next.position]: { v: next.title, key: next.key, s: next.s },
      }), {}
    )
 

2.内容

js 复制代码
 data.dataSource.forEach((item, i) => {
    data.header.forEach((obj, index) => {
        const key = getCharCol(index) + (i + 2)
        const key_t = obj.dataIndex
        _data[key] = {
          v: item[key_t],
          s: setCellStyle? setCellStyle(key_t, item,'content') : defaultCellStyle.dataStyle,
        }
      })
    })

完整代码

js 复制代码
import XLSX from 'xlsx-style'

// 默认工作簿配置

const defaultWorkBook = {
  bookType: 'xlsx',
  bookSST: false,
  type: 'binary',

}

// 默认样式配置

const borderAll = { 
  top: {
    style: 'thin',
  },
  bottom: {
    style: 'thin',
  },
  left: {
    style: 'thin',
  },
  right: {
    style: 'thin',
  },
}

const defaultCellStyle = {
  // 表头区域样式配置
  headerStyle: { 
    border: borderAll,
    font: { name: '宋体', sz: 11, italic: false, underline: false, bold: true },
    alignment: { vertical: 'center', horizontal: 'center' },
    fill: { fgColor: { rgb: 'FFFFFF' } },
  },
  // 内容区域样式配置
  dataStyle: { 
    border: borderAll,
    font: { name: '宋体', sz: 11, italic: false, underline: false },
    alignment: { vertical: 'center', horizontal: 'left', wrapText: true },
    fill: { fgColor: { rgb: 'FFFFFF' } },
  },
}

function exportExcel(exportData, fileName = '未命名',  setCellStyle,  workBookConfig = defaultWorkBook) {

  if (!(exportData && exportData.length)) {
    return
  }
  
  // 定义工作簿对象
  const wb = { SheetNames: [], Sheets: {} }

  exportData.forEach((data, index) => {
  
    // 处理sheet表头
    const _header = data.header.map((item, i) =>
      Object.assign({}, {
        key: item.dataIndex,
        title: item.title,
        // 定位单元格
        position: getCharCol(i) + 1, 
        // 设置表头样式
        s: setCellStyle ? setCellStyle(key, item, 'header') : defaultCellStyle.headerStyle,
      })
    ).reduce((prev, next) =>
      Object.assign({}, prev, {
        [next.position]: { v: next.title, key: next.key, s: next.s },
      }), {}
    )

    // 处理sheet内容
    const _data = {}
    data.dataSource.forEach((item, i) => {
      data.header.forEach((obj, index) => {
        const key = getCharCol(index) + (i + 2)
        const key_t = obj.dataIndex
        _data[key] = {
          v: item[key_t],
           s: setCellStyle? setCellStyle(key_t, item, 'content') : defaultCellStyle.dataStyle,
        }
      })
    })

    const output = Object.assign({}, _header, _data)
    const outputPos = Object.keys(output)


    // 设置单元格宽度
    const colWidth = data.header.map(item => { return { wpx: item.width || 80 } })

    const merges = data.workSheetConfig && data.workSheetConfig.merges

    const freeze = data.workSheetConfig && data.workSheetConfig.freeze

    // 处理sheet名
    
    wb.SheetNames[index] = data.sheetName ? data.sheetName : 'Sheet' + (index + 1)
    
    // 处理sheet数据
    
    wb.Sheets[wb.SheetNames[index]] = Object.assign({},
      output, // 导出的内容
      {
        '!ref': `${outputPos[0]}:${outputPos[outputPos.length - 1]}`,
        '!cols': [...colWidth],
        '!merges': merges ? [...merges] : undefined,
        '!freeze': freeze ? [...freeze] : undefined,
      }
    )
  })
  
  // 转成二进制对象
  const tmpDown = new Blob(
    [s2ab(XLSX.write(wb, workBookConfig))],
    { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }
  )
  
  // 下载表格
  downExcel(tmpDown, `${fileName + '.'}${workBookConfig.bookType === 'biff2' ? 'xls' : workBookConfig.bookType}`)
}

/**
 * 生成ASCll值 从A开始
 * @param {*} n
 */
function getCharCol(n) {
  if (n > 25) {
    let s = ''
    let m = 0
    while (n > 0) {
      m = n % 26 + 1
      s = String.fromCharCode(m + 64) + s
      n = (n - m) / 26
    }
    return s
  }
  return String.fromCharCode(65 + n)
}

// 字符串转字符流---转化为二进制的数据流
function s2ab(s) {
  if (typeof ArrayBuffer !== 'undefined') {
    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
  } else {
    const buf = new Array(s.length)
    for (let i = 0; i !== s.length; ++i) { buf[i] = s.charCodeAt(i) & 0xff }
    return buf
  }
}

function downExcel(obj, fileName) {
  const a_node = document.createElement('a')
  a_node.download = fileName

  // 兼容ie
  if ('msSaveOrOpenBlob' in navigator) {
    window.navigator.msSaveOrOpenBlob(obj, fileName)
  } else {
    // 新的对象URL指向执行的File对象或者是Blob对象.
    a_node.href = URL.createObjectURL(obj)
  }
  a_node.click()
  setTimeout(() => {
    URL.revokeObjectURL(obj)
  }, 100)
}

export {
  exportExcel,
}
相关推荐
青年夏日科技工作者20 分钟前
虚幻浏览器插件 UE与JS通信
前端·javascript·html
雷神乐乐1 小时前
创建前端项目的方法
前端·javascript·vue.js
prince_zxill1 小时前
JavaScript面向对象编程:Prototype与Class的对比详解
前端·javascript·ecmascript·原型模式
D.eL2 小时前
Vue 2 项目中 Mock.js 的完整集成与使用教程
前端·javascript·vue.js
brzhang2 小时前
墙裂推荐一个在 Apple Silicon 上创建和管理虚拟机的轻量级开源工具:lume
前端·后端
Along丶WG3 小时前
解决国内服务器 npm install 卡住的问题
前端·npm·node.js
prince_zxill3 小时前
Node.js 和 npm 安装教程
前端·javascript·vue.js·npm·node.js
弄不死的强仔4 小时前
可被electron等调用的Qt截图-录屏工具【源码开放】
前端·javascript·qt·electron·贴图·qt5
霸王蟹4 小时前
el-table组件样式如何二次修改?
前端·javascript·vue.js·笔记·学习·前端框架
star010-5 小时前
一文学会HTML编程之视频+图文详解详析
前端·网络·网络安全·html·html5