js导出excel封装【原生、配置式】

0、有引用库,table2excel,参考demo文件

【也可只做配置渲染】参考封装原生html的table处理方法【参数类似eltable】-CSDN博客

1、调用示例

  const config = {
    name: '导出文件',
    data: [
      { name: '测试1', time: '2023年', status: '001', age: '11' },
      { name: '测试2', time: '2023年', status: '001', age: '11' },
      { name: '测试3', time: '2023年', status: '001', age: '12' },
      { name: '测试4', time: '2023年', status: '001', age: '13' },
      { name: '测试5', time: '2023年', status: '001', age: '14' },
    ],
    columnsData: [
      {
        label: '学生信息',
        children: [
          {
            label: "姓名",
            prop: 'name',
          },
          {
            label: "时间",
            prop: 'time',
          },
          {
            label: "年龄",
            prop: 'age',
          },
        ]
      },
    ]
  }
  exportExcelUitl.export(config)

2、核心代码

const exportExcelUitl = {}
!function(){
    // vNode对象转为html字符串
    const vnodeToString = function (vnode) {
        // 如果是文本节点,直接返回文本内容
        if (['string', 'boolean', 'undefined', 'null', 'number'].includes(typeof vnode) || !vnode) {
            return vnode;
        }
        // 转换节点的属性为字符串形式
        const attrs = Object.keys(vnode.attrs || {})
            .map((key) => `${key}="${vnode.attrs[key]}"`)
            .join(' ');
        // 转换子节点为字符串形式
        const children = (vnode.children || [])
            .map(vnodeToString)
            .join('');
        // 返回包含标签名、属性和子节点的字符串形式
        return `<${vnode.tag} ${attrs}>${children}</${vnode.tag}>`;
    }
    this.vnodeToString = vnodeToString


    class DataToExcelHtml {
        // 标题
        title = ''
        // 原始数据
        originalData = []
        // 表格列配置数据
        columnsData = []
        // 渲染数据vNode
        renderData = []
        // 表头vNode数据
        headerVNode = []
        // 表体vNode数据
        bodyVNode = []
        // table标签的dom的id
        tableDomId = 'excel-line-data'
    
        // 列宽
        colWidth = 120
    
    
        constructor(config) {
            this.tableDomId = config.tableDomId
            this.dom = config.dom
            this.colWidth = config.colWidth || 120
            this.originalData = config.data
            this.columnsData = config.columnsData
            this._setColIndex()
            this.headerVNode = this._setHeaderVNode()
            this.bodyVNode = this._setBodyVNode()
            this.setInnerHtml()
        }
    
    
        // 取最后一层
        flattenObjectArrayLast(arr, key = "children") {
            let flattened = [];
            arr.forEach(v => {
                if (v.children && v.children.length > 0) {
                    flattened = flattened.concat(this.flattenObjectArrayLast(v[key]))
                } else {
                    flattened.push(v);
                }
            })
            return flattened;
        }
    
        // 表头   设置每个字段所在的列 行下标值
        _setColIndex(data = this.columnsData, index = 0, row_index = 0) {
            data.forEach(v => {
                v.__colspan = this.flattenObjectArrayLast(v.children || []).length || 1
                v.__rowspan = v.rowspan || 1
                v.__col_index = v.__colspan > 1 ? null : index
                v.__row_index = row_index
                index++
                if (v.children?.length) {
                    index = this._setColIndex(v.children, index, row_index + 1)
                }
            })
            return index
        }
    
        // 设置样式
        _setStyle(col) {
            // 表样式通用
            const commonTrStyle = "height: 30px;"
            const commonBorder = 'border-width:1px;border-style:solid;border-color:#000000;'
            const commonAttrsLabel = {
                style: `text-align:${col.align || 'center'};font-size: 12px;` + commonBorder + commonTrStyle + col.styleStr,
            }
            return commonAttrsLabel
        }
    
        // 设置表头
        _setHeaderVNode(data = this.columnsData) {
            const that = this
            // 递归获取表头合并行深度
            let deep = (function getDeep(list) {
                let deep = 1
                list.forEach(col => {
                    let curDeep = 1
                    if (col.children && col.children.length) {
                        curDeep += getDeep(col.children)
                    }
                    deep = curDeep > deep ? curDeep : deep
                })
                return deep
            })(data)
    
    
    
            // 递归获取表头VNode
            this.headerVNode = (function recData(list, curDeep = 1, tr = []) {
                list.forEach(col => {
                    tr[col.__row_index] = tr[col.__row_index] || { tag: 'tr', children: [] }
                    const obj = {
                        tag: 'td',
                        children: [col.label],
                        attrs: {
                            style: that._setStyle(col).style,
                            rowspan: col.__rowspan || 1,
                            colspan: col.__colspan || 1,
                            width: col.width || that.colWidth,
                        }
                    }
                    if (col.children) {
                        recData(col.children, curDeep++, tr)
                    } else if (!col.children?.length) {
                    }
                    tr[col.__row_index].children.push(obj)
                })
                return tr
            })(data)
            return this.headerVNode
        }
    
        _setBodyVNode(data = this.originalData, columnsData = this.columnsData) {
            const flatColumns = this.flattenObjectArrayLast(columnsData).filter(v => v.__col_index !== null)
            function getChild(row, col, index) {
                if (col.type === 'index') {
                    return index + 1
                }
                return row[col.prop] || ''
            }
            this.bodyVNode = data.map((row, index) => {
                const tr = { tag: 'tr', children: [] }
                // 列下标
                flatColumns.forEach((col, idx) => {
                    tr.children.push({
                        tag: 'td',
                        children: [getChild(row, col, index)],
                        attrs: {
                            style: this._setStyle(col).style,
                        }
                    })
                })
                return tr
            })
            return this.bodyVNode
        }
    
        // 设为innerHtml
        setInnerHtml(vNode = this.headerVNode) {
            const i = vnodeToString({
                tag: 'table',
                attrs: {
                    style: "border-collapse: collapse;border:1px",
                    border: 1,
                    cellspacing: '0',
                    id: this.tableDomId
                },
                children: [{
                    tag: 'tbody',
                    attrs: {
                        style: ""
                    },
                    children: [].concat(vNode, this.bodyVNode)
                }]
            })
            this.dom.innerHTML = i
        }
    }

    this.DataToExcelHtml = DataToExcelHtml


    this.export = function (config) {
        const dom = document.createElement('div');
        dom.style.position = 'fixed'
        dom.style.opacity = 0
        document.body.appendChild(dom);
        config.dom = dom
        config.tableDomId = 'table__onlyId_' + new Date().getTime()
        new DataToExcelHtml(config)
        new Table2Excel('#' + config.tableDomId).export(config.name || ('导出表格' + new Date().getTime()))
        setTimeout(() => {
            dom.parentNode.removeChild(dom);
        }, 1000)
    }


}.call(exportExcelUitl)

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值