基于xlsx导出excel文件,自定义合并某一列或多列值相同的单元格

0 阅读4分钟

调用方式

const titleList = [
  // {label:"序号",prop:"seq"},
  {label:"",prop:"fItenName"},
  {label:"",prop:"itemName"},
  {label:"现款",prop:"xk"},
  {label:"承兑",prop:"cd"},
  {label:"铁信",prop:"tx"},
  {label:"商票",prop:"sp"},
  {label:"应付账款融资",prop:"yszkrz"},
  {label:"金融保理",prop:"jrbl"},
  {label:"流贷",prop:"ld"},
  {label:"银承、信用证",prop:"xyz"},
  {label:"合计",prop:"sum"},
]
const paymentReport = ref([
{
itemName:"55",
fitenName:"55",
xk :"55",
cd:"55",
tx:"55",
sp:"55",
yszkrz:"55",
jrbl:"55",
xyz:"55",
ld:"55",
sum:"55",
}
])
const exportEvent = () => {
//表格数据
    let datalist = [...paymentReport.value,{fItenName:"账户",itemName:"账户1"},{fItenName:"账户",itemName:"账户2"},{fItenName:"账户余额:"+endBalance.value}]
 
    exportExcel(titleList,datalist,[],{bookType:"xlsx"},"收支报表",[{label:"A",cIndex:0}])
  
  //titleList  表头列名数组
  
 //[{label:"A",cIndex:0}]
 //label指excel表中A列,cIndex为第一列,索引为0
};

导出文件方法:

import * as XLSX from 'xlsx'
import * as XLSX_STYLE from 'xlsx-js-style'


/**
 * 导出数据
 */
export function exportExcel(headers, datasource, options, type, fileName="未命名" ,cell=[],footerList=[])  {
    // 一、准备数据
    const list = getExportDataList(headers, datasource, options)

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

    // 三、使用二维数组生成一个工作表
    const workSheet = sheet_from_array_of_arrays(list)
    console.log(workSheet)
    // 五、设置每列的宽度(单位:px)
    const wsCols = headers.map(item=>{
        return {wch:item.width?item.width:16}
    })
    workSheet['!cols'] = wsCols

    // 六、设置每行的高度(单位:px)
    let wsRows = []
    for (let i in list) {
        if (i == 1) {
            wsRows.push({ hpx: 50 }) // 首行高度为 100px
        } else {
            wsRows.push({ hpx: 30 }) // 其他行高度为 30px
        }
    }
    workSheet['!rows'] = wsRows
    var cellData =[]

    // 七、设置单元格样式
    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: fileName, // 设置单元格内容(raw value (number, string, Date object, boolean))
                    // l: { Target: "https://sheetjs.com", Tooltip: "Find us @ SheetJS.com!" }, // 单元格超链接 cell hyperlink / tooltip (More Info)
                    s: { // 设置单元格样式
                        fill: { // 设置背景色
                            fgColor: { rgb: 'ffffff' },
                        },
                        font: { // 设置字体
                            name: '等线', // 字体名称
                            sz: 24, // 字体大小
                            bold: true, // 字体是否加粗
                            // color: { rgb: '5e7ce0' }, // 文字颜色
                        },
                        alignment: { // 设置居中
                            horizontal: 'center', // 水平(向左、向右、居中)
                            vertical: 'center', // 上下(向上、向下、居中)
                            wrapText: false, // 设置单元格自动换行,目前仅对非合并单元格生效
                            indent: 0 // 设置单元格缩进
                        },
                    },
                }
            }
            // 匹配表格第二行(A2 B2 C2 D2...),设置其样式
            else if (key.match(/\d+/g).join('') == '2') {
                workSheet[key].s = {
                    border: {
                        top: {
                            style: 'thin',
                        },
                        bottom: {
                            style: 'thin',
                        },
                        left: {
                            style: 'thin',
                        },
                        right: {
                            style: 'thin',
                        },
                    },
                    fill: { // 设置背景色
                        fgColor: { rgb: 'eeeeee' },
                    },
                    font: { // 设置字体
                        name: '微软雅黑', // 字体名称
                        sz: 10, // 字体大小
                    },
                    alignment: {
                        horizontal: 'center', // 水平(向左、向右、居中)
                        vertical: 'center', // 上下(向上、向下、居中)
                        wrapText: false, // 设置单元格自动换行,目前仅对非合并单元格生效
                        indent: 0 // 设置单元格缩进
                    }
                }
            }
            // 其它单元格,设置其样式
            else {
                cell.filter(item=>{
                    if(key != `${item.label}1` && key != `${item.label}2` && key.indexOf(`${item.label}`) > -1){
                        cellData.push({value:workSheet[key].v,key:item.label,cIndex:item.cIndex})
                    }
                })
                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: true, // 设置单元格自动换行,目前仅对非合并单元格生效
                        indent: 0 // 设置单元格缩进
                    }
                }
            }
        }
    }
    //设置需要合并的单元格
    // 四、将 "A1" 到 "M1" 的单元格合并为 "A1"
// s:开始行  e:截至行   c:列  r:行
    let mergeArr = [{
        s: { c: 0, r: 0 },
        e: { c: headers.length-1, r: 0 },
    }
    ]
    //循环设置指定列需要合并的行数据
    cell.filter(item=>{
        let data = []
        cellData.forEach(item1=>{
            if(item1.key===item.label){
                //判断那一列单元格合并
                data.push(item1.value)
            }
        })
        console.log(data)
        mergeArr = mergeArr.concat(mergeCell(data,item.cIndex))
    })
    // if (footerList.length){
    //     mergeArr.push({
    //         s: { c: 0, r: list.length-1 },
    //         e: { c: headers.length-1, r: list.length-1 },
    //     })
    // }

    console.log(mergeArr)
    workSheet['!merges'] = mergeArr
    // 八、在工作簿中添加工作表
    XLSX.utils.book_append_sheet(workBook, workSheet, fileName)

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

    // 十、导出 Excel 文件
    const date = new Date()
    const formattedDate =
        `` +
        `${date.getFullYear()}` + // 年
        `${(date.getMonth() + 1).toString().padStart(2, '0')}` + // 月
        `${date.getDate().toString().padStart(2, '0')}` + // 日
        `${date.getHours().toString().padStart(2, '0')}` + // 时
        `${date.getMinutes().toString().padStart(2, '0')}` + // 分
        `${date.getSeconds().toString().padStart(2, '0')}` + // 秒
        ``.trim()
    downloadExcelFile(tmpDown, `${fileName}-${formattedDate}.${type.bookType == 'biff2' ? 'xlsx' : type.bookType}`)

}
/**
 * 合并单元格
 */
function mergeCell(data,c) {
    let arr = []
    let data1 = [...new Set(data)]
    let  num = 2
    data1.forEach((item,index) =>{
        let numr = num
        let numc = numr+data.filter(item1=>item1===item).length-1
        num =  numr+data.filter(item1=>item1===item).length
        console.log(numr,numc)

        let obj = {
            s: { c: c, r: numr },
            e: { c: c, r: numc },
        }
        arr.push(obj)
    })
    return arr
}

/**
 * 准备数据
 */
function  getExportDataList(headers, datasource, options) {
    const thList = headers.map(item=>item.label)
    const keyList = headers.map(item=>item.prop)
    const targetList = datasource
    const tdList = formatJson(keyList, targetList) // 过滤字段以及转换数据格式,即:表格数据
    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, opts) {
    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';
            else if (cell.v instanceof Date) {
                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)
    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)
}

以上方法导出合并结果:

image.png