最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 使用xlsx-style导出Excel文件并设置样式

    正文概述 掘金(chenyuhu)   2020-12-04   1392

    前言

    ​ 最近工作中接到一个需求,是将页面中的表格数据给整合成Excel文件给用户下载,这个问题最开始的想法是后端直接生成一个文件流就好了,但是他要求要有文件的格式和部分文本的样式。但是后端觉得麻烦,就丢给可怜又无助的前端来做。 ​ 本来先想着就直接使用sheetJS提供的js-xlsx来实现就好了,可是发现无法对表格进行样式渲染。(其实是可以实现的,不过要使用js-xlsx的Pro版本,这个版本是要钱的,作为一个贫穷公司的贫穷前端码农来说这个是可以的。没有啥是手写不能解决的。如果真的有,那就和产品硬钢换需求)而我就找到一个xlsx-style的开源库。 这个库其实是js-xlsx的一个分支,它可以对导出的Excel文件进行编辑样式,但是目前为止,还是不能编辑单元格的高度。不过这也不影响我们使用,毕竟还是可以编辑单元格的宽度,字体的样式,填充颜色,边框等等样式,还是可以使用的哦~

    安装

    因为我们开发的环境是electron+vue的,本身就自带node,所以就不用担心node相关的库我们使用不了。

    yarn

    yarn add xlsx-style
    

    In the browser:

    <script lang="javascript" src="dist/xlsx.core.min.js"></script>
    

    With bower:

    bower install js-xlsx-style#beta
    
    导出Excel文件说明
    单元格对象(cell-Object)
    cellObject = {c:C,r:R}
    

    其中c代表列号,r代表行号,单元格B5,用对象表示就是{c:1,r:5}。如果要是想表示单元格的范围,则可以使用{s:S,e:E},其中s表示第一个单元格即start,e表示最后一个单元格即end,这里面的s和e都是cellObject对象;;例如要表示A1:B5则可以用对象就是{s:{c:0,r:0},e:{c:1,r:4}}

    工作表格对象(sheet-object)
    sheetObject = {
      A1: {
        v: '单元格',
        t: 's',
        s: {
          font: {},
          fill: {},
          numFmt: {},
          alignment: {},
          border: {}
        }
      }
    }
    

    V: 表示单元格的值;

    t:表示单元格值的类型,b:表示Boolean布尔值,n表示number数组,e表示error错误信息,s表示string字符串,d:表示date日期

    s: 表示单元格的样式,后面会重点讲这个

    当然单元格的属性肯定不会只有这些,但是对于一般的简单导出,这些其实就已经足够了,剩下的属性其实我们用的也不多

    使用xlsx-style导出Excel文件并设置样式

    ok,看完这些基础信息配置后我们看看如何将一个数组给变成Excel文件的,只需要三步:打开冰箱,把大象放进冰箱,关上冰箱

    //  表格中展示的原始数据
    const tableArray = [
      {
        name: '张三',
        age: 14,
        gender: '男'
      },
      {
        name: '李四',
        age: 23,
        gender: '女'
      }
    ]
    
    const title = ['姓名','年龄','性别']
    
    /**
    	* 转换数据的函数
    	* tableArray 需要转换的数据
    	* title 表格展示的title
    	* excludeKey 不需要导出的数据
    	* backgroundRed 背景颜色为红色
    */
    const ProcessingData = ({tableArray,title,excludeKey,backgroundRed}) => {
      if(!tableArray?.length) {
         return null
         }
      const excelTable = [title]
      // 获取转换的的数组
      const sheetData = tableArray.map((item, index) => {
        // lodash 方法自己按需引用
        const copyItem = cloneDeep(item)
        if (excludeKey) {
          delete copyItem[excludeKey]
        }
        const colArray = Object.values(copyItem)
        colArray.unshift(index + 1)
        return colArray
      })
      sheetData.unshift(...excelTable)
      const sheet = {}
      sheetData.forEach((item, index) => {
        item.forEach((sheetItem, key) => {
          const itemIndex = `${EnLetter[key]}${index + 1}`
          const s = { alignment: { vertical: 'center', horizontal: 'center', wrapText: true } }
          if (backgroundRed && backgroundRed.length && index === 1) {
            if (backgroundRed.some(item => item === key)) {
              // 注意设置颜色的时候要是ARGB格式
              s.fill = { fgColor: { rgb: 'FFFF0023' } }
              s.font = { color: { rgb: 'FFFFFFFF' } }
              s.alignment.vertical = 'left'
            }
          }
          sheet[itemIndex] = { v: sheetItem, t: 's', s }
        })
      })
      const sheetKeys = Object.keys(sheet)
      const ref = `${sheetKeys[0]}:${sheetKeys.pop()}`
      sheet['!ref'] = ref
      return { sheet, sheetData }
    }
    
    const base64ToBlob = s => {
      const bstr = atob(s)
      let n = bstr.length
      const u8Arr = new Uint8Array(n)
      while (n--) {
        u8Arr[n] = bstr.charCodeAt(n)
      }
      return new Blob([u8Arr], { type: 'xlsx' })
    }
    
    /**
     * 转换为文件
     * @param {Object} params
     * @param {Array} params.tableArray 表格中展示的数据
     * @param {Boolean} params.autoSerial 是否为自动序号
     * @param {String} params.fileName 文件名
     * @param {Array} params.title 文件表格展示的title
     * @param {Boolean} params.autoWidth 是否为自适应宽度
     * @param {Array} params.description 文件的描述
     * @param {Array} params.backgroundRed 背景标红的序号一般指title
     */
    const JSON2ExcelFile = ({
      tableArray,
      title,
      autoWidth = true,
      errorToRed = true,
      description,
      backgroundRed,
      excludeKey
    }) => {
      const { sheet, sheetData: excelArray } = ProcessingData({
        tableArray,
        title,
        description,
        excludeKey,
        errorToRed,
        backgroundRed
      })
      // 合并单元格描述的
      sheet['!merges'] = [
        {
          s: {
            c: 0,
            r: 0
          },
          e: {
            c: 7,
            r: 0
          }
        }
      ]
      // 设置自动高度
      if (autoWidth) {
        /* 设置worksheet每列的最大宽度 */
        excelArray.shift()
        const colWidth = excelArray.map(row =>
          row.map(val => {
            /* 先判断是否为null/undefined */
            if (val == null) {
              return {
                wch: 20
              }
            }
            if (val.toString().charCodeAt(0) > 255) {
              /* 再判断是否为中文 */
              return {
                wch: val.toString().length * 2 > 20 ? val.toString().length * 2 : 10
              }
            }
            return {
              wch: val.toString().length > 20 ? val.toString().length * 2 : 20
            }
          })
        )
        // /* 以第一行为初始值 */
        const result = colWidth[0]
        for (let i = 1; i < colWidth.length; i += 1) {
          for (let j = 0; j < colWidth[i].length; j += 1) {
            if (result[j].wch < colWidth[i][j].wch) {
              result[j].wch = colWidth[i][j].wch
            }
          }
        }
        sheet['!cols'] = result
      }
      const workbook = { Sheets: { 员工信息表: sheet }, SheetNames: ['员工信息表'] }
      const bouts = XLSXStyle.write(workbook, { type: 'base64', bookType: 'xlsx', cellStyles: true })
      const file = base64ToBlob(bouts)
      return file
    }
    
    /**
     * 将错误信息导出为Excel表格,用于下载使用
     * @param {Object} params
     * @param {Array} params.tableArray 表格中展示的数据
     * @param {String} params.fileName 文件名
     * @param {Array} params.title 文件表格展示的title
     * @param {Boolean} params.autoWidth 是否为自适应宽度
     * @param {Array} params.description 文件的描述
     * @param {Array} params.backgroundRed 背景标红的序号
     */
    const ExcelFile = ({
      tableArray,
      filename,
      title,
      autoWidth = true,
      errorToRed = true,
      description,
      backgroundRed,
      excludeKey
    }) => {
     const file = JSON2ExcelFile({
        tableArray,
        autoWidth,
        title,
        errorToRed,
        description,
        backgroundRed,
        excludeKey
      })
      const homeDir = homedir()
      const options = {
        title: '保存Excel',
        defaultPath: `${homeDir}\\downloads\\${filename}.xlsx`,
        filters: [{ name: 'excel', extensions: ['xlsx'] }]
      }
      remote.dialog.showSaveDialog(options).then(({ filePath: filename }) => {
        if (!filename) return
        const reader = new FileReader()
        reader.readAsArrayBuffer(file)
        reader.onload = () => {
          const buffer = Buffer.from(reader.result)
          fs.writeFile(filename, buffer, {}, err => {
            if (err) {
              Message({
                showClose: true,
                message: '下载失败',
                type: 'error'
              })
              throw err
            }
            const vm = new Vue()
            const h = vm.$createElement
            const fileName = h('p', basename(filename))
            const notifier = Notification.success({
              title: '下载完成',
              duration: 0,
              dangerouslyUseHTMLString: true,
              message: h('div', [
                fileName,
                h('span', {
                  style: {
                    display: 'inline-block',
                    color: '#409eff',
                    cursor: 'pointer',
                    margin: '10px 0 0'
                  },
                  domProps: {
                    innerHTML: '在文件夹中显示'
                  },
                  on: {
                    click: () => {
                      fs.access(filename, err => {
                        if (err) {
                          vm.$message.error({
                            message: '该文件不存在',
                            showClose: true
                          })
                        } else {
                          remote.shell.showItemInFolder(filename)
                        }
                        notifier.close()
                      })
                    }
                  }
                })
              ])
            })
          })
        }
      })
    }
    

    前端实现导出Excel文件,基本的实现逻辑是在现将展示的table的数据转换成Excel能够识别的json数据,将这些数据转换成文件需要的blob数据。将这个blob数据写成文件;

    @startuml
    start
    :获取需要导出的数据;
    :将数据转换为Excel能识别的json数据;
    :将json转化为blob数据;
    :将blob文件写成文件;
    :导出文件;
    stop
    @enduml
    

    下载网 » 使用xlsx-style导出Excel文件并设置样式

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元