• js xlsx自定义样式导出


    使用的技术:
    xlsx 用于解析和编写各种电子表格。比如excel、csv、html文件
    xlsx-style 为xlsx库添加样式,比如字体颜色,大小,行宽等。但是只支持xlsx、xlsm、xlsb格式
    FileSaver.js 负责下载保存文件。对各种兼容性比较好,对跨域文件也有处理
    lodash js常用工具库


    需要注意的地方
    一、引入xlsx-style报错

    1. This relative module was not found:
    2. ./cptable in ./node_modules/xlsx-style@0.8.13@xlsx-style/dist/cpexcel.js

    亲测两种解决方案:

    1. 在\node_modules\xlsx-style\dist\cpexcel.js 807行 的
    2. var cpt = require(’./cpt’ + ‘able’);把这一行改成 var cpt = cptable;

    把xlsx的cpexcel.js文件复制到xlsx-style的dist文件夹,覆盖cpexcel.js。


    二、颜色添加不上

    xlsx-style 只 支持16进制的ARGB颜色,比如:{ rgb: "FFFFAA00" }。注意这里是没有#号的。
    添加背景色是使用fill的fgColor,而不是bgColor。这里需要驼峰编写。
    实现步骤
    1、需求描述

    将列表数据导出成Excel。所有的导出表格都固定表头样式,但是内容不固定,特殊列需要高亮(添加背景色),其他的数据列按需求设置宽度,可以添加换行等。(比如身份证号固定字符长度,地址列固定宽度、自动换行

    2、实现导出核心代码

    使用xlsx的json_to_sheet工具方法,把数组的数据格式转换成xlsx需要worksheet。然后再添加xlsx-style支持的样式代码,使用xlsx-style编写成数据流,最后再使用FileSaver.js把数据流保存成文件

    创建工作簿

    let wb = XLSX.utils.book_new()

    创建显示表头

    1. const ws = XLSX.utils.json_to_sheet([
    2.   colNames
    3. ], { header: keys, skipHeader: true })

    追加数据到excel中,从第二行开始

    1.   XLSX.utils.sheet_add_json(ws, data, { header: keys, skipHeader: true,
    2. origin: 'A2' })

    添加表头样式,设置背景色、字体大小、下边框线

    1. for (const key in ws) {
    2.   // 第一行
    3.   if(key.replace(/[^0-9]/ig, '') === '1') {
    4.     ws[key].s = {
    5.       fill: {
    6.         fgColor: { rgb: 'FFA3F4B1' } // 添加背景色
    7.       },
    8.       font: {
    9.         name: '宋体', // 字体
    10.         sz: 12, // 字体大小
    11.         bold: true // 加粗
    12.       },
    13.       border: {
    14.           // 下划线
    15.         bottom: {
    16.           style: 'thin',
    17.           color: 'FF000000'
    18.         }
    19.       }
    20.     }
    21.   }
    22. }


    生成xlsx导出类。这里必须使用xlsx-style才能生成指定样式

    1. wbOut = XLSXStyle.write(wb, { bookType: bookType, bookSST: false,
    2. type: 'binary' })

    生成并下载文件

    saveAs(new Blob([s2ab(wbOut)], { type: '' }), filename)

    完整的代码

    1. import XLSX from 'xlsx'
    2. import XLSXStyle from 'xlsx-style'
    3. import { saveAs } from 'file-saver'
    4. import path from 'path'
    5. import _ from 'lodash'
    6. const FILE_NAME = '云途教育表格.xlsx'
    7. const COL_PARAMS = ['hidden', 'wpx', 'width', 'wch', 'MDW']
    8. const STYLE_PARAMS = ['fill', 'font', 'alignment', 'border']
    9. /**
    10.  * 数字转换成excel表头。 (递归处理)
    11.  * @param {Number} num 需要转换的数字
    12.  */
    13. // eslint-disable-next-line no-unused-vars
    14. function numToString(num) {
    15.   let strArray = []
    16.   let numToStringAction = function(o) {
    17.     let temp = o - 1
    18.     let a = parseInt(temp / 26)
    19.     let b = temp % 26
    20.     strArray.push(String.fromCharCode(64 + parseInt(b + 1)))
    21.     if (a > 0) {
    22.       numToStringAction(a)
    23.     }
    24.   }
    25.   numToStringAction(num)
    26.   return strArray.reverse().join('')
    27. }
    28. /**
    29.  * 表头字母转换成数字。(进制转换)
    30.  * @param {string} str 需要装换的字母
    31.  */
    32. function stringToNum(str) {
    33.   let temp = str.toLowerCase().split('')
    34.   let len = temp.length
    35.   let getCharNumber = function(charx) {
    36.     return charx.charCodeAt() - 96
    37.   }
    38.   let numout = 0
    39.   let charnum = 0
    40.   for (let i = 0; i < len; i++) {
    41.     charnum = getCharNumber(temp[i])
    42.     numout += charnum * Math.pow(26, len - i - 1)
    43.   }
    44.   return numout
    45. }
    46. /**
    47.  * worksheet转成ArrayBuffer
    48.  * @param {worksheet} s xlsx库中的worksheet
    49.  */
    50. function s2ab(s) {
    51.   if (typeof ArrayBuffer !== 'undefined') {
    52.     const buf = new ArrayBuffer(s.length)
    53.     const view = new Uint8Array(buf)
    54.     for (let i = 0; i !== s.length; ++i) {
    55.       view[i] = s.charCodeAt(i) & 0xFF
    56.     }
    57.     return buf
    58.   } else {
    59.     const buf = new Array(s.length)
    60.     for (let i = 0; i !== s.length; ++i) {
    61.       buf[i] = s.charCodeAt(i) & 0xFF
    62.     }
    63.     return buf
    64.   }
    65. }
    66. /**


     * 导出成Excel

    1.  * @param {Array} data 数据
    2.  * @param {Object} columns 每列参数说明。可直接写对应的中文名称,也可以写这一列的样式

    。比如列宽,背景色

    1.  * @param {String} filename 文件名。可根据文件名后缀动态判断文件格式。
    2. 支持xlsx, xlsm, xlsb(这三个支持自定义样式), html, csv等
    3.  * @param {Object} styleConf 其他xlsx-style的参数
    1.  *
    2.  * columns数据格式:{
    3.  *         name: '姓名',
    4.  *         sex: '性别',
    5.  *         birthday: {
    6.  *           name: '出生日期',
    7.  *           wch: 14
    8.  *         },
    9.  *         address: {
    10.  *           name: '地址',
    11.  *           wpx: 160,
    12.  *           alignment: { wrapText: true }
    13.  *         }
    14.  *       }
    15.  *
    16.  * styleConf数据格式: {
    17.  *          'E4': {
    18.  *             font: {
    19.  *               bold: true,
    20.  *               color: { rgb 'FFFF0000' }
    21.  *             }
    22.  *          },
    23.  *          '!merges': [ // 合并第一行
    24.  *             {
    25.  *               s: { c: 0, r: 0 },
    26.  *               e: { c: 7, r: 0 }
    27.  *             }
    28.  *          ]
    29.  *        }
    30.  */
    1. export function exportExcel(data, columns, filename = FILE_NAME, styleConf) {
    2.   let keys = _.keys(columns)
    3.   let colNames = _.mapValues(columns, o => {
    4.     if (_.isPlainObject(o)) {
    5.       return o.name
    6.     } else {
    7.       return o
    8.     }
    9.   })

      // 创建工作簿

      let wb = XLSX.utils.book_new()

      // 显示表头

    1.   const ws = XLSX.utils.json_to_sheet([
    2.     colNames
    3.   ], { header: keys, skipHeader: true })


      // 过滤数据,只显示表头包含的数据

    1.   for (let i = 0; i < data.length; i++) {
    2.     data[i] = _.pick(data[i], keys)
    3.   }


      // 追加数据到excel中,从第二行开始

    1.   XLSX.utils.sheet_add_json(ws, data, { header: keys, skipHeader: true, origin: 'A2' })
    2.   wb.SheetNames.push('sheet1')
    3.   wb.Sheets['sheet1'] = ws

      // 根据不同的扩展名,导出不同格式的文件

    1.   let bookType = null
    2.   let ext = path.extname(filename)
    3.   if (ext == null) {
    4.     filename += '.xlsx'
    5.     bookType = 'xlsx'
    6.   } else {
    7.     bookType = ext.substr(1).toLowerCase()
    8.   }
    9.   let wbOut


      // 如果是支持样式的格式

    1.   if (['xlsx', 'xlsm', 'xlsb'].includes(bookType)) {
    2.     // ws['!merges'] = [{// 合并第一行数据[B1,C1,D1,E1]
    3.     //   s: {// s为开始
    4.     //     c: 0, // 开始列
    5.     //     r: 0// 开始取值范围
    6.     //   },
    7.     //   e: {// e结束
    8.     //     c: 7, // 结束列
    9.     //     r: 0// 结束范围
    10.     //   }
    11.     // }]
    12.     for (const key in ws) {
    13.       // 第一行,表头
    14.       if (key.replace(/[^0-9]/ig, '') === '1') {
    15.         ws[key].s = {
    16.           fill: {
    17.             fgColor: { rgb: 'FFA3F4B1' }
    18.           },
    19.           font: {
    20.             name: '宋体',
    21.             sz: 12,
    22.             bold: true
    23.           },
    24.           border: {
    25.             bottom: {
    26.               style: 'thin',
    27.               color: 'FF000000'
    28.             }
    29.           }
    30.         }
    31.       } else {
    32.         let str = key.replace(/[^A-Za-z]+$/ig, '')
    33.         let colIndex = stringToNum(str) - 1
    34.         if (keys[colIndex] && _.isPlainObject(columns[keys[colIndex]])) {
    35.           const a = _.pick(columns[keys[colIndex]], STYLE_PARAMS)
    36.           ws[key].s = _.assign(ws[key].s, a)
    37.         }
    38.       }
    39.     }
    40.     // 设置列宽
    41.     const colsP = []
    42.     _.mapValues(columns, o => {
    43.       colsP.push(_.pick(o, COL_PARAMS))
    44.     })
    45.     ws['!cols'] = colsP
    46.     // 合并其他样式参数
    47.     if (styleConf) {
    48.       for (const key in styleConf) {
    49.         if (ws.hasOwnProperty(key)) {
    50.           ws[key].s = styleConf[key]
    51.         }
    52.       }
    53.     }
    54.     wbOut = XLSXStyle.write(wb, { bookType: bookType, bookSST: false, type: 'binary' })
    55.   } else {
    56.     wbOut = XLSX.write(wb, { bookType: bookType, bookSST: false, type: 'binary' })
    57.   }
    58.   saveAs(new Blob([s2ab(wbOut)], { type: '' }), filename)
    59. }


    调用实例

    1. import { exportExcel } from '../../utils/xlsxUtils.js'
    2. async exportStudent() {
    3.       const params = {
    4.         pageNum: this.pageNum,
    5.         pageSize: this.pageSize
    6.       }
    7.       params[this.queryForm.searchType] = this.queryForm.searchInput
    8.       this.exportLoading = true
    9.       try {
    10.         const res = await this.$http.get('/v1/ExportOrImport/exportStudentInfo', params)
    11.         res.data.forEach(item => {
    12.           item.courseStr = item.courses.map(o => o.name).join(',')
    13.           item.classStr = item.classes.map(o => o.name).join(',')
    14.         })
    15.         await exportExcel(res.data, {
    16.           name: '姓名',
    17.           sex: '性别',
    18.           idCard: {
    19.             name: '身份证号',
    20.             wch: 18
    21.           },
    22.           birthday: {
    23.             name: '出生日期',
    24.             wch: 14
    25.           },
    26.           primaryContactPhone: {
    27.             name: '联系人号码',
    28.             wch: 11
    29.           },
    30.           primaryContactName: '联系人姓名',
    31.           relation: '联系人关系',
    32.           nickName: '昵称',
    33.           school: '就读学校',
    34.           grade: '年级',
    35.           address: {
    36.             name: '地址',
    37.             wpx: 160,
    38.             alignment: { wrapText: true }
    39.           },
    40.           remark: {
    41.             name: '备注',
    42.             wpx: 240,
    43.             alignment: { wrapText: true }
    44.           },
    45.           courseStr: {
    46.             name: '报读课程',
    47.             wpx: 120,
    48.             font: { color: { rgb: 'ffff0000' } },
    49.             alignment: { wrapText: true }
    50.           },
    51.           classStr: {
    52.             name: '班级',
    53.             wpx: 120,
    54.             font: { color: { rgb: 'ffff0000' } },
    55.             alignment: { wrapText: true }
    56.           }
    57.         }, '学员信息表.xlsx', {
    58.           'E4': {
    59.             fill: { fgColor: { rgb: 'FFFF0000' } }
    60.           }
    61.         })
    62.       } catch (error) {
    63.       }
    64.       this.exportLoading = false
    65.     }



    最后导出的样式:

     

  • 相关阅读:
    php jquery ajax 无法传递POST值的问题
    数字IC设计笔试常见大题整理(简答+手撕)
    【王道】操作系统OS第五章I/O管理(五)
    2022年湖北省科技计划项目“包干制”管理申报条件以及流程
    【NestJS系列】核心概念:Providers提供者
    Nginx 配置 SSL(HTTPS)详解
    golang适合做什么
    本地搭建Stackedit Markdown编辑器结合内网穿透实现远程访问
    带你深入浅出Vue
    【牛客网刷题系列 之 Verilog快速入门】~ 使用函数实现数据大小端转换
  • 原文地址:https://blog.csdn.net/weixin_51225684/article/details/127654860