Vue2+xlsx+xlsx-style导出带有线框和表头的表格(详细注释)

参考:使用Vue+xlsx+xlsx-style实现导出自定义样式的Excel文件

xlsx-style.js报错Can‘t resolve ‘./cptable‘ in ‘xxx\node_modules_xlsx_xslx-style报错-CSDN博客

"xlsx": "^0.18.5",

 "xlsx-style": "^0.8.13"

"vue": "^2.5.10",

在libs里创建excel.js

import * as XLSX from 'xlsx'
import * as XLSX_STYLE from 'xlsx-style' // Vue2 + Webpack


//thList 表头,keyList 对应的字段名
/**
 * 导出数据
 */
export function handelExportExcelClick(title, datas, columns) {

    // 一、准备数据
    const { thList, keyList } = getThList(columns);//thList:表头,keyList:对应字段名,我的columns对应的字段是label和prop,如果你是其他的,请自行修改getThList函数
    const data = getList(datas, keyList)//getList函数:1、给数据添加合计行,如果不需要合计,可以在getList方法中注释掉;2、给数据中的undefined或者null字段赋值为空字符串,3、这一步骤是必须的;如果你在调用handelExportExcelClick方法时已经将数据处理好了,可以忽略getList方法,直接const data = datas;就行;

    const list = getExportDataList(data, thList, keyList)
    // yLength用于设置列宽
    const yLength = thList.length;

    // 二、新建一个工作簿
    const workBook = XLSX.utils.book_new()

    // 三、使用二维数组生成一个工作表
    const workSheet = sheet_from_array_of_arrays(list, yLength, title)

    // 四、将 "A1" 到 "M1" 的单元格合并为 "A1"
    workSheet['!merges'] = [
        {
            s: { // s ("start"): c = 0 r = 0 -> "A1"
                r: 0,
                c: 0
            },
            e: { // e ("end"):   c = 0 r = 9 -> "J1"
                r: 0,
                c: yLength - 1
            }
        }
    ]

    // 五、设置每列的宽度(单位:px)
    let wsCols = [] 
    // 我是通过表头label的字段的值来判断来做的列宽,你也可以用其他方式来设置宽度
    columns.forEach(item => {
        if(item.label=='发货单位'||item.label=='收货单位'){
            wsCols.push({ wch: 25 })
        }
        else if(item.label=='一次过磅时间'||item.label=='二次过磅时间'){
            wsCols.push({ wch: 20 })
        }
        else if(item.label=='手机号码'){
            wsCols.push({ wch: 12 })
        }
        else{
            wsCols.push({ wch: 10 })
        }
    })
    workSheet['!cols'] = wsCols

    // 六、设置每行的高度(单位:px)
    let wsRows = []
    for (let i in list) {
        if (i == 0) {
            wsRows.push({ hpx: 100 }) // 首行标题高度为 100px
        } else {
            // wsRows.push({ hpx: 30 }) // 其他行高度为 30px,可以注释,表格会默认高度
        }
    }
    workSheet['!rows'] = wsRows

    // 七、设置单元格样式 对表头的样式进行修改,如果不需要可以注释
    // for (let key in workSheet) {
    //     if (key == '!ref' || key == '!merges' || key == '!cols' || key == '!rows') {
    //         continue
    //     } else {
    //         // 匹配表格第一行(注意 A1 单元已合并为一个单元),设置其样式
    //         if (key == 'A1') {
    //             workSheet[key] = {
    //                 t: 's', // 设置单元格类型(type: b Boolean, e Error, n Number, d Date, s Text, z Stub)
    //                 v: title, // 这里设置了表头!
    //                 // l: { Target: "https://sheetjs.com", Tooltip: "Find us @ SheetJS.com!" }, // 单元格超链接 cell hyperlink / tooltip (More Info)
    //                 s: { // 设置单元格样式
    //                     // fill: { // 设置背景色
    //                     //     fgColor: { rgb: 'ffffff' },
    //                     // },
    //                     font: { // 设置字体
    //                         name: '等线', // 字体名称
    //                         sz: 30, // 字体大小
    //                         bold: true, // 字体是否加粗
    //                         color: { rgb: '001529' }, // 文字颜色
    //                     },
    //                     alignment: { // 设置居中
    //                         horizontal: 'center', // 水平(向左、向右、居中)
    //                         vertical: 'center', // 上下(向上、向下、居中)
    //                         wrapText: false, // 设置单元格自动换行,目前仅对非合并单元格生效
    //                         indent: 0 // 设置单元格缩进
    //                     },
    //                     border: {
    //                         top: {
    //                             style: 'thin',
    //                         },
    //                         bottom: {
    //                             style: 'thin',
    //                         },
    //                         left: {
    //                             style: 'thin',
    //                         },
    //                         right: {
    //                             style: 'thin',
    //                         },
    //                     }
    //                 },
    //             }
    //         }
    //         else {
    //             workSheet[key].s = {
    //                 border: {
    //                     top: {
    //                         style: 'thin',
    //                     },
    //                     bottom: {
    //                         style: 'thin',
    //                     },
    //                     left: {
    //                         style: 'thin',
    //                     },
    //                     right: {
    //                         style: 'thin',
    //                     },
    //                 },
    //                 fill: { // 设置背景色
    //                     fgColor: { rgb: 'ffffff' },
    //                 },
    //                 font: { // 设置字体
    //                     name: '微软雅黑', // 字体名称
    //                     sz: 10, // 字体大小
    //                 },
    //                 alignment: {
    //                     horizontal: 'center', // 水平(向左、向右、居中)
    //                     vertical: 'center', // 上下(向上、向下、居中)
    //                     wrapText: false, // 设置单元格自动换行,目前仅对非合并单元格生效
    //                     indent: 0 // 设置单元格缩进
    //                 }
    //             }
    //         }
    //     }
    // }

    // 八、在工作簿中添加工作表
    XLSX.utils.book_append_sheet(workBook, workSheet, 'sheet')

    // 九、使用 xlsx-style 写入文件方式,使得自定义样式生效
    const tmpDown = new Blob([
        s2ab(
            XLSX_STYLE.write(workBook, {
                bookType: 'xlsx',
                bookSST: true,
                type: 'binary',
                cellStyles: true,
            })
        ),
    ])

    // 十、导出 Excel 文件
    downloadExcelFile(tmpDown, title + '.xlsx')//这里使用了title为文件的名字,可以以在调用handelExportExcelClick方法时传入一个参数来代替
}

/**
 * 准备数据
 */
function getExportDataList(data, thList, keyList) {
    const tdList = formatJson(keyList, data) // 过滤字段以及转换数据格式,即:表格数据
    tdList.unshift(thList) // 将 thList 数组添加到 tdList 数组开头,即:表格头部
    tdList.unshift(['']) // 将空字符串数组添加到 tdList 数组开头,即:表格首行
    const list = tdList
    return list
}

/**
 * 过滤字段以及转换数据格式
 */
function formatJson(filterVal, jsonData) {
    return jsonData.map(v => filterVal.map(item => {
        if (item === 'name') {
            if (v['name'] != null && v['name'] != '') {
                return v[item].split(';').join('\n')
            } else {
                return '-'
            }
        }
        else {
            return v[item]
        }
    }))
}

/**
 * 使用二维数组生成一个工作表
 */
function sheet_from_array_of_arrays(data, yLength, title) {
    var ws = {};
    var range = {
        s: {
            c: 10000000,
            r: 10000000
        },
        e: {
            c: 0,
            r: 0
        }
    }

    for (var R = 0; R != data.length; ++R) {

        for (var C = 0; C != data[R].length; ++C) {
            if (range.s.r > R) range.s.r = R;
            if (range.s.c > C) range.s.c = C;
            if (range.e.r < R) range.e.r = R;
            if (range.e.c < C) range.e.c = C;
            var cell = {
                v: data[R][C]
            };
            if (cell.v == null) continue;
            var cell_ref = XLSX.utils.encode_cell({
                c: C,
                r: R
            })

            if (typeof cell.v === 'number') cell.t = 'n';
            else if (typeof cell.v === 'boolean') cell.t = 'b';
            // 这里是时间格式的处理,建议在页面中调用handelExportExcelClick方法之前,就将时间转换了
            // else if (cell.v instanceof Date) {
            //     //console.log(cell.v)
            //     cell.t = 'n';
            //     cell.z = XLSX.SSF._table[14];
            //     cell.v = date_num(cell.v);
            //     // cell.z = 'YYYY-MM-DD'
            //     cell.z = 'YYYY-MM-DD HH:mm:ss'
            // } 
            else cell.t = 's'

            ws[cell_ref] = cell
        }
    }

    if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range)
    // 新增这部分:处理合并单元格 A1 到 M1 的样式

    for (let c = range.s.c; c <= range.e.c; c++) {
        for (let r = range.s.r; r <= range.e.r; r++) {
            let cell_ref = XLSX.utils.encode_cell({ c: c, r: r });
            if (r === 0 && c >= 0 && c <= yLength - 1) { // 对应合并的第一行
                ws[cell_ref] = {
                    t: 's',
                    v: title,
                    s: {
                        font: {
                            name: '等线',
                            sz: 30,
                            bold: true,
                            color: { rgb: '001529' },
                        },
                        alignment: {
                            horizontal: 'center',
                            vertical: 'center',
                            wrapText: false,
                            indent: 0,
                        },
                        border: {
                            top: { style: 'thin' },
                            bottom: { style: 'thin' },
                            left: { style: 'thin' },
                            right: { style: 'thin' },
                        },
                    },
                };
            }
            else { // 其他单元格样式
                ws[cell_ref].s = {
                    //... 其他单元格样式 ...
                    border: {
                        top: {
                            style: 'thin',
                        },
                        bottom: {
                            style: 'thin',
                        },
                        left: {
                            style: 'thin',
                        },
                        right: {
                            style: 'thin',
                        },
                    },
                    fill: { // 设置背景色
                        fgColor: { rgb: 'ffffff' },
                    },
                    font: { // 设置字体
                        name: '微软雅黑', // 字体名称
                        sz: 10, // 字体大小
                    },
                    alignment: {
                        horizontal: 'center', // 水平(向左、向右、居中)
                        vertical: 'center', // 上下(向上、向下、居中)
                        wrapText: false, // 设置单元格自动换行,目前仅对非合并单元格生效
                        indent: 0 // 设置单元格缩进
                    }
                };
            }
        }
    }

    return ws
}

/**
 * 日期转换
 */
function date_num(v, date1904) {
    if (date1904) {
        v += 1462;
    }
    var epoch = Date.parse(v);
    return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}

/**
 * 文件流转换
 */
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
    }
}

/**
 * 使用 a 标签下载文件
 */
function downloadExcelFile(obj, fileName) {
    const a_node = document.createElement('a')
    a_node.download = fileName
    if ('msSaveOrOpenBlob' in navigator) {
        window.navigator.msSaveOrOpenBlob(obj, fileName)
    } else {
        a_node.href = URL.createObjectURL(obj)
    }
    a_node.click()
    setTimeout(() => {
        URL.revokeObjectURL(obj)
    }, 2000)
}
function getThList(columns) {
    let thList = []
    let keyList = []
    columns.forEach(item => {
        thList.push(item.label)
        keyList.push(item.prop)
    })
    return { thList, keyList }
}
function getList(data, keyList) {

    // 初始化sumObj,所有key对应值设为空字符串
    let sumObj = {};
    keyList.forEach(key => {
        sumObj[key] = '';
    });
    // 设置第一个item的key值为'合计'
    sumObj[keyList[0]] = '合计'
    // 遍历data并累加数字类型的item[key]
    data.forEach(item => {
        keyList.forEach(key => {
            if (typeof item[key] === 'number') {
                sumObj[key] = Number(sumObj[key]) + item[key];
            }
        });
    });
    // 将数据中的null和undefined替换为空字符串,如果表格数据中没有显示null和undefined,可以注释掉,如果有,这一步是必须的!
    data.forEach(item => {
        for (let key in item) {
            if (item[key] === null || item[key] === undefined) {
                item[key] = '';
            }
        }
    });
    data.push(sumObj);
    return data;
}
在页面中使用:
<template>
    <div>
        <el-button title="导出表格" size="small" @click="exportTable('这里是导出表格的表头和文件名称')">
            <i class="el-icon-tickets"></i>
        </el-button>
    </div>
</template>
<script>
import { handelExportExcelClick } from '@/libs/excel'
import dayjs from 'dayjs'
export default {
    data() {
        return {
            tableData:[],//表格数据
            fields: [//表格字段,我在表格中做了筛选功能,使用show字段来控制显示隐藏
                { label: '姓名', prop: 'name', show: true},
                { label: '年龄', prop: 'age', show: true},
                { label: '性别', prop: 'sex', show: true},
                { label: '地址', prop: 'address', show: true},
                { label: '电话', prop: 'phone', show: true},
                { label: '邮箱', prop: 'email', show: true},
                { label: '时间', prop: 'date', show: true},
                { label: '操作', width: 200, show: true},
            ],
        }
    },
    methods: {
        // 导出数据
        exportTable(title){
            if (this.tableData.length) {
                var columns = this.fields;
                // var columns = []
                // this.fields.forEach((item) => {
                //     if(item.show && item.label !== '操作'){
                //         columns.push({
                //             label: item.label,
                //             prop: item.prop,
                //         })
                //     }
                // })
                var data = JSON.parse(JSON.stringify(this.tableData))
                // data.forEach(item => {
                //     item.weOneWeighDate=dayjs(item.weOneWeighDate).format('YYYY-MM-DD HH:mm:ss')
                //     item.weTwoWeighDate=dayjs(item.weTwoWeighDate).format('YYYY-MM-DD HH:mm:ss')
                // })
                // 总之,在调用handelExportExcelClick之前,将数据需要过滤的字段过滤了好了再调用
                handelExportExcelClick(title, data, columns)
            } else {
                this.$message.info('表格数据不能为空!')
            }
        },
    }
}
</script>
效果参考:

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值