Vue3-导出导入excel

功能

主要实现excel导入导出功能,同时具有合并单元格,美化单元格等功能,引用依赖包exceljs、file-saver,好像也没有什么要介绍的,可以看看官网文档然后直接使用了

导入依赖包

javascript 复制代码
pnpm install exceljs file-saver @types/file-saver

封装Excel帮助类(excelHelper.js)

javascript 复制代码
import ExcelJS from 'exceljs'
import FileSaver from 'file-saver'
import { cloneDeep } from 'lodash'

/**
 * 读取excel文件
 * @param {object} file 文件
 * @param {String} sheetName 获取工作表名称
 * @returns 
 */
export async function readExcelFile(file, sheetName) {
    const arrayBuffer = file.arrayBuffer()
    const result = []
    const workbook = new ExcelJS.Workbook()
    await workbook.xlsx.load(arrayBuffer)
    const worksheet = workbook.getWorksheet(sheetName || 1)
    worksheet.eachRow((row, rowNumber) => {
        console.log(rowNumber)
        const rowData = []
        result.push(rowData)
        row.eachCell((cell, colNumber) => {
            rowData.push(cell.value)
            console.log(colNumber)
        })
    })
    return result
}
/**
 * 
 * 数据导出excel
 * @param {String} excelOpt.fileName   导出文件名
 * @param {String} excelOpt.configs.sheetName 工作表名称,默认从sheet1开始
 * @param {Array[Array]} excelOpt.configs.headers  表格头部 eg:[['标题1','标题2'],['标题3','标题4']]
 * @param {Array[Array]} excelOpt.configs.data   表格数据
 * @param {Array[Object]} excelOpt.configs.merges 合并信息 eg:[row: 0, col: 0, rowspan: 3, colspan: 1]
 * @param {Array[object]} excelOpt.configs.views 工作表视图配置
 * @param {Array[Number]} config.columnsWidth 每个字段列对应的宽度
 * @param {Object} excelOpt.configs.protect 工作表保护【此配置会保护全表,一般推荐只针对单元格进行保护配置】
 * @param {Array} excelOpt.configs.fields 辅助导出顺序
 * 
 */
export function exportExcel(excelOpt) {
    if (!excelOpt) return
    const opt = cloneDeep(excelOpt)
    console.dir(excelOpt.sheetInfos[0])
    const dataOptions = {
        fileName: opt.fileName || `导出excel文件【${Date.now()}】.xlsx`,
        worksheets: []
    }
    if (!Array.isArray(opt.sheetInfos)) {
        opt.sheetInfos = [opt.sheetInfos]
    }
    opt.sheetInfos.forEach(item => {
        //对象根据fields进行组装数组数据
        let data = item.data.map(obj => {
            return item.fields.map(key => {
                return obj[key]
            })
        })
        let excelData = [].concat(item.headers).concat(data)
        let merges = item.merges.map(m => {
            return [m.row + 1, m.col + 1, m.row + m.rowspan, m.col + m.colspan]
        })
        let excelAttrs = item.attrs.map(attr => {
            attr.rowStart += 1;
            attr.rowEnd += 1;
            attr.colStart += 1;
            attr.colEnd += 1;
            return attr
        })
        dataOptions.worksheets.push({
            data: excelData,
            merges: merges,
            attrs: excelAttrs,
            views: item.views,
            columnsWidth: item.columnsWidth,
            protect: item.protect,
            sheetName: item.sheetName
        })
    })
    createExcel(dataOptions)
}
// 创建Excel文件方法
async function createExcel(options) {
    if (!options.worksheets.length) return;
    // 创建工作簿
    const workbook = new ExcelJS.Workbook();
    for (let i = 0; i < options.worksheets.length; i++) {
        const sheetOption = options.worksheets[i];
        // 创建工作表
        const sheet = workbook.addWorksheet(sheetOption.sheetName || 'sheet' + (i + 1));
        // 添加数据行
        sheet.addRows(sheetOption.data);
        // 配置视图
        sheet.views = sheetOption.views;
        // 单元格合并处理【开始行,开始列,结束行,结束列】
        if (sheetOption.merges) {
            sheetOption.merges.forEach((item) => {
                sheet.mergeCells(item);
            });
        }
        // 工作表保护
        if (sheetOption.protect) {
            // const res = await sheet.protect(sheetOption.protect.password, sheetOption.protect.options);
            await sheet.protect(sheetOption.protect.password, sheetOption.protect.options);
        }
        // 单元格样式处理
        if (sheetOption.attrs.length) {
            sheetOption.attrs.forEach((item) => {
                const attr = item.attr || {};
                // 获取开始行-结束行; 开始列-结束列
                const rowStart = item.rowStart;
                const rowEnd = item.rowEnd;
                const colStart = item.colStart;
                const colEnd = item.colEnd;
                if (rowStart) { // 设置行
                    for (let r = rowStart; r <= rowEnd; r++) {
                        // 获取当前行
                        const row = sheet.getRow(r);
                        if (colStart) { // 列设置
                            for (let c = colStart; c <= colEnd; c++) {
                                // 获取当前单元格
                                const cell = row.getCell(c);
                                Object.keys(attr).forEach((key) => {
                                    // 给当前单元格设置定义的样式
                                    cell[key] = attr[key];
                                });
                            }
                        } else {
                            // 未设置列,整行设置【大纲级别】
                            Object.keys(attr).forEach((key) => {
                                row[key] = attr[key];
                            });
                        }
                    }
                } else if (colStart) { // 未设置行,只设置了列
                    for (let c = colStart; c <= colEnd; c++) {
                        // 获取当前列,整列设置【大纲级别】
                        const column = sheet.getColumn(c);
                        Object.keys(attr).forEach((key) => {
                            column[key] = attr[key];
                        });
                    }
                } else {
                    // 没有设置具体的行列,则为整表设置
                    Object.keys(attr).forEach((key) => {
                        sheet[key] = attr[key];
                    });
                }
            })
        }
        // 列宽设置
        if (sheetOption.columnsWidth) {
            for (let i = 0; i < sheet.columns.length; i++) {
                sheet.columns[i].width = sheetOption.columnsWidth[i]
            }
        }
    }

    // 生成excel文件
    workbook.xlsx.writeBuffer().then(buffer => {
        // application/octet-stream 二进制数据
        FileSaver.saveAs(new Blob([buffer], { type: 'application/octet-stream' }), options.fileName)
    })
}

使用(导出,导入)

javascript 复制代码
//导出
const exportExcelFn = () => {
  const data = [{
    index: 1,
    title1: 'title1-1',
    title2: 'title1-2',
    title3: 'title1-3',
    title4: 'title1-4',
    title5: 'title1-5',
    title6: 'title1-6',
    title7: 'title1-7',
    title8: 'title1-8',
    title9: 'title1-9',
    title10: 'title1-10',
  },
  {
    index: 2,
    title1: 'title2-1',
    title2: 'title2-2',
    title3: 'title2-3',
    title4: 'title2-4',
    title5: 'title2-5',
    title6: 'title2-6',
    title7: 'title2-7',
    title8: 'title2-8',
    title9: 'title2-9',
    title10: 'title2-10',
  }]
  const opt = {
    fileName: '积分明细.xlsx',
    sheetInfos: [
      {
        fields: ['index', 'title1', 'title2', 'title3', 'title4', 'title5', 'title6', 'title7', 'title8', 'title9', 'title10'],
        headers: [['序号', '标题1', '标题2', '标题3', '标题4', '标题5', '标题6', '标题7', '标题8', '标题9', '标题10']],
        data: data,
        merges: [
          // { row: 1, col: 1, rowspan: 11, colspan: 1 },
        ],
        attrs: [],
        columnsWidth: [20, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],
        sheetName: '导出数据测试'
      }
    ]
  }
  //设置单元格样式
  opt.sheetInfos.map(item => {
    item.attrs.push({
      rowStart: 0,
      rowEnd: item.headers.length + item.data.length,
      colStart: 0,
      colEnd: item.headers[0].length - 1,
      attr: {
        alignment: { vertical: "middle", horizontal: "center" },
        // border: {
        //   top: { style: "thin" },
        //   left: { style: "thin" },
        //   bottom: { style: "thin" },
        //   right: { style: "thin" }
        // }
      }
    })
    // 设置表头填充颜色,字体加粗
    item.attrs.push({
      rowStart: 0,
      rowEnd: item.headers?.length - 1,
      colStart: 0,
      colEnd: item.headers[0].length - 1,
      attr: {
        fill: {
          type: "pattern",
          pattern: "solid",
          fgColor: { argb: "99CCFF" }
        },
        font: {
          bold: true
        }
      }
    })
  })
  exportExcel(opt)
}
javascript 复制代码
<script setup>
import { readExcelFile } from '@/utils/excelHelper'

const fileUploadFn = async (file) => {
  const excelData = await readExcelFile(file)
  console.log('Excel Data:', excelData)
  return false;
}
</script>

<template>
  <div>
    <el-upload :before-upload="fileUploadFn" accept=".xlsx, .xls">
      <el-button type="primary" class="upload">上传</el-button>
    </el-upload>
  </div>
</template>
相关推荐
E-iceblue2 小时前
Excel 列转行/行列转换全指南:从 4 种常见解法到 Python 批量自动化
python·excel·python库·spire.xls
Data-Miner7 小时前
AI处理数据的脚本复用与逻辑复用有什么区别?哪种方式更能沉淀工作成果
数据分析·excel
福兮说10 小时前
在浏览器里写一个“够用就好“的 Excel 公式求值器
前端·javascript·excel·编译原理
慧都小妮子1 天前
标签打印如何减少手工抄数?用Bartender 直连 Excel 批量打印
excel·bartender·条码打印·标签打印软件·数据驱动打印·工业打印机
SunnyDays10111 天前
Java 设置 Excel 数据验证:整数、日期、文本长度、下拉列表和时间
java·excel·数据验证·下拉列表
Metaphor6921 天前
使用 Python 设置 Excel 行列自适应 【代码示例】
python·excel
用户0332126663672 天前
使用 Python 设置 Excel 行列自适应 【代码示例】
python·excel
企业数字化笔记2 天前
固定资产Excel批量导入怎么防重复?资产编码、重复检查和错误回执
java·后端·excel
CORNERSTONE3652 天前
低代码开发有哪些应用场景?
低代码·excel·erp·mes·企业数字化
小葱炖豆腐3 天前
python绘制excel折线图
python·excel·numpy·pandas·matplotlib