在前端开发中,处理 Excel 文件是特别常见的任务,尤其是在一些后台管理业务中会经常涉及到数据的导入和导出等。本文将介绍如何使用 xlsx 和 file-saver 这两个库来实现前端的 Excel 数据导入与导出。xlsx 允许我们读取和创建 Excel 文件,而 file-saver 使得将文件保存到本地变得简单。

1.使用xlsx和file-saver完成导出excel功能

// 导入第三方包
import xlsx from 'xlsx'
import fs from 'file-saver'

xlsx(json, fields, filename = '.xlsx') {
    // 遍历json数据,并根据fields映射修改字段名
    json.forEach(item => {
        for (const i in item) {
            if (fields.hasOwnProperty(i)) {
                item[fields[i]] = item[i]; // 将旧字段名的值赋给新字段名
            }
            delete item[i]; // 删除旧字段名
        }
    });

    const sheetName = filename; // 设置Excel文件的名称
    const wb = xlsx.utils.book_new(); // 创建一个新的工作簿对象
    const ws = xlsx.utils.json_to_sheet(json, { header: Object.values(fields) }); // 将处理后的JSON数据转换为工作表
    
    wb.SheetNames.push(sheetName); // 将工作表名称添加到工作簿的SheetNames数组
    wb.Sheets[sheetName] = ws; // 将工作表添加到工作簿

    // 设置默认单元格样式
    const defaultCellStyle = { 
        font: { name: 'Verdana', sz: 13, color: 'FF00FF88' }, 
        fill: { fgColor: { rgb: 'FFFFAA00' }} 
    };
    
    // 写入选项,包括样式配置
    const wopts = { 
        bookType: 'xlsx', 
        bookSST: false, 
        type: 'binary', 
        cellStyles: true, 
        defaultCellStyle: defaultCellStyle, 
        showGridLines: false 
    };
    
    // 生成Excel文件的二进制数据
    const wbout = xlsx.write(wb, wopts);
    
    // 将二进制数据转换为Blob对象
    const blob = new Blob([this.s2ab(wbout)], { type: 'application/octet-stream' });
    
    // 使用file-saver库保存文件
    fs.saveAs(blob, filename + '.xlsx');
},

s2ab(s) {
    let buf;
    if (typeof ArrayBuffer !== 'undefined') {
        // 如果支持ArrayBuffer,将字符串转换为ArrayBuffer
        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 {
        // 如果不支持ArrayBuffer,使用数组
        buf = new Array(s.length);
        for (let i = 0; i !== s.length; ++i) buf[i] = s.charCodeAt(i) & 0xFF;
        return buf;
    }
}

2.在点击事件中触发导出事件

<button @click="ImportData">导出</button>

switchExportData (data) {
    const exportData = JSON.parse(JSON.stringify(data))
    // 这里可以对数据进行一次加工,转换为用户需要的最终形式
    return exportData
},
ExportData (data = []) {
    const columns={name:'姓名',age:'年龄',sex:'性别'}   // json 数据 的 key => value 对应关系
    const exportData = this.switchExportData(data)  // 处理数据
    this.xlsx(exportData, columns, 'excel表名' )
},

3.导入功能

/* 读取文件 将文件转换为二进制 */
readFile (file) {
    return new Promise(resolve => {
        const reader = new FileReader()
        reader.readAsBinaryString(file)
        reader.onload = ev => {
            resolve(ev.target.result)
        }
    })
},
addHoursToDate(dateStr, hoursToAdd) {
    // 解析日期字符串为 Date 对象
    const date = new Date(dateStr);   
    // 添加小时
    date.setHours(date.getHours() + hoursToAdd);
    // 格式化为 'YYYY-MM-DD' 格式
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始,+1
    const day = String(date.getDate()).padStart(2, '0');  
    return `${year}-${month}-${day}`;
},
switchObj (data, originalObj) {
    data.forEach(item => {
        const obj = {}
        for (const key in originalObj) {
            // 对日期格式的数据做兼容处理
            if (item[originalObj[key]] instanceof Date) {
                obj[key] = this.addHoursToDate(item[originalObj[key]]),8) || ''  // 加八小时是为了校准时差
            } else {
                obj[key] = String(item[originalObj[key]] || '') // 将其他比如数字类型的数据统一转换成字符串类型
            }
        }
        result.push(obj)
    })
    return result
},
handleUploadChange (file) {
    const dataBinary = await this.readFile(file.target.files[0])  // file.target.files[0]即为文件对象
    file.target.value = null // 注意上传后要将input的值设为空
    const workBook = xlsx.read(dataBinary, { type: 'binary', cellDates: true })
    const workSheet = workBook.Sheets[workBook.SheetNames[0]]
    const data = xlsx.utils.sheet_to_json(workSheet) // 拿到的数据原始值,key值为中文
    const columns={name:'姓名',age:'年龄',sex:'性别'}   // json 数据 的 key => value 对应关系
    let importData = this.switchObj(data, columns)  // 将key和value转换为接口所需要的形式

4.button和input触发上传事件

<button @click="ImportData">导入</button>
<input ref="file" type="file" name="file" accept=".xlsx,.xls" @change="handleUploadChange">

Importdata () {
    this.$refs.file.click()
}