vue3 用xlsx 解决 excel 低版本office无法打开问题

  • 需求背景
  • 解决思路
  • 解决效果
  • 将json导出为excel
  • 将table导为excel
  • 导出样式

需求背景

原使用 vue3-json-excel ,导致在笔记本office环境下,出现兼容性问题

 <vue3-json-excel class="export-btn" :fetch="excelGetList" :fields="jsonFields" header="告警配置列表" name="告警配置列表.xls" type="xls">
   <span>批量导出</span>
 </vue3-json-excel>

生成的.xls文件,提示文件格式和扩展名不匹配,文件可能已损坏或不安全在这里插入图片描述
生成的.xlsx文件,提示文件格式或文件扩展名无效
在这里插入图片描述

解决思路

既然 vue3-json-excel 达不到效果,就改用xlsx

解决效果

在这里插入图片描述

将json导出为excel

/**
 * 将json数据导出为excel
 * @param {object} options
 * @param {[]} data 数据源 // {'供热系统': "大唐热电供热系统", '供热面积(万m²)': 34.099, '分布式水泵数量': 0, '当前供热系统': "大唐热电供热系统"}
 * @param {string} fileName 文件名称
 * @param [] keySort  字段排序 // ['供热系统', '供热面积(万m²)', '分布式水泵数量','当前供热系统']
 * @param {string} sheetName sheet名称
 * @param {boolean} titleNum 表头数
 */
import XLSXStyle from "xlsx-style-vite" //"^0.0.2"
import FileSaver from "file-saver";// "^2.0.5"
import * as XLSX from "xlsx";//"^0.17.0",

export const exportToExcel = (data, fileName, keySort, sheetName = "sheet1", titleNum = 1) => {
  // const sheet = XLSX.utils.json_to_sheet(data)
  const temp = data.map(item => {
    const arr = []
    keySort.forEach(k => arr.push(item[k]))
    return arr
  })
  temp.unshift(keySort)
  const sheet = XLSX.utils.aoa_to_sheet(temp)
  const wb = {
    SheetNames: [sheetName],
    Sheets: {
      [sheetName]: sheet
    }
  }
  const range = XLSX.utils.decode_range(wb.Sheets[sheetName]['!ref']);
  //单元格边框样式
  const borderStyle = {
    top: {
      style: "thin",
      color: {rgb: "000000"}
    },
    bottom: {
      style: "thin",
      color: {rgb: "000000"}
    },
    left: {
      style: "thin",
      color: {rgb: "000000"}
    },
    right: {
      style: "thin",
      color: {rgb: "000000"}
    }
  };
  const cWidth = [];
  for (let C = range.s.c; C < range.e.c + 1; ++C) {   //SHEET列
    let len = 100; //默认列宽
    const len_max = 400; //最大列宽
    for (let R = range.s.r; R <= range.e.r; ++R) {  //SHEET行
      const cell = {c: C, r: R};                    //二维 列行确定一个单元格
      const cell_ref = XLSX.utils.encode_cell(cell);  //单元格 A1、A2
      if (wb.Sheets[sheetName][cell_ref]) {
        if (R < titleNum) {
          wb.Sheets[sheetName][cell_ref].s = {  //设置第一行单元格的样式 style
            font: {
              sz: 14, // 标题字号
              color: {rgb: '060B0E'},// 颜色
              bold: true//加粗
            },
            alignment: {
              horizontal: 'center',
              vertical: 'center',
            },
            fill: {
              fgColor: {rgb: 'E4E4E4'}, // 背景
            },
            border: borderStyle,//用上面定义好的边框样式
          };
        } else {
          wb.Sheets[sheetName][cell_ref].s = {
            alignment: {
              horizontal: 'center',
              vertical: 'center',
            },
            border: borderStyle,//用上面定义好的边框样式
          };
        }
        //动态自适应:计算列宽
        const va = JSON.parse(JSON.stringify(wb.Sheets[sheetName][cell_ref].v || ''));
        const card1 = JSON.parse(JSON.stringify(va.toString())).match(/[\u4e00-\u9fa5]/g); //匹配中文
        let card11 = "";
        if (card1) card11 = card1.join("")
        const card2 = JSON.parse(JSON.stringify(va.toString())).replace(/([^\u0000-\u00FF])/g, "");  //剔除中文
        let st = 0;
        if (card11) st += card11.length * 20  //中文字节码长度
        if (card2) st += card2.length * 10  //非中文字节码长度
        if (st > len) len = st;
      }
    }
    cWidth.push({'wpx': len > len_max ? len_max : len});     //列宽
  }
  wb.Sheets[sheetName]['!cols'] = cWidth;
  const wopts = {bookType: 'xlsx', bookSST: false, type: 'binary'};
  const wbout = XLSXStyle.write(wb, wopts); //一定要用XLSXStyle不要用XLSX,XLSX是没有格式的!
  FileSaver(new Blob([s2ab(wbout)], {type: ""}), fileName + '.xlsx');

  function s2ab(s) {
    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;
  }
}

将table导为excel

/**
 * 将table导出为excel
 * @param {object} options
 * @param {[]} table 表格元素
 * @param {string} fileName 文件名称
 * @param {string} sheetName sheet名称
 * @param {boolean} titleNum 表头数
 */
export const exportTableToExcel = (dom, fileName, sheetName = "sheet1", titleNum = 1) => {
  const wb = XLSX.utils.table_to_book(dom, {sheet: sheetName, raw: true});
  const range = XLSX.utils.decode_range(wb.Sheets[sheetName]['!ref']);
  //单元格边框样式
  const borderStyle = {
    top: {
      style: "thin",
      color: {rgb: "000000"}
    },
    bottom: {
      style: "thin",
      color: {rgb: "000000"}
    },
    left: {
      style: "thin",
      color: {rgb: "000000"}
    },
    right: {
      style: "thin",
      color: {rgb: "000000"}
    }
  };
  const cWidth = [];
  for (let C = range.s.c; C < range.e.c + 1; ++C) {   //SHEET列
    let len = 100; //默认列宽
    const len_max = 400; //最大列宽
    for (let R = range.s.r; R <= range.e.r; ++R) {  //SHEET行
      const cell = {c: C, r: R};                    //二维 列行确定一个单元格
      const cell_ref = XLSX.utils.encode_cell(cell);  //单元格 A1、A2
      if (wb.Sheets[sheetName][cell_ref]) {
        // if (R == 0){
        if (R < titleNum) {
          wb.Sheets[sheetName][cell_ref].s = {  //设置第一行单元格的样式 style
            font: {
              sz: 14,
              color: {rgb: '060B0E'},
              bold: true
            },
            alignment: {
              horizontal: 'center',
              vertical: 'center',
            },
            fill: {
              fgColor: {rgb: 'E4E4E4'},
            },
            border: borderStyle,//用上面定义好的边框样式
          };
        } else {
          wb.Sheets[sheetName][cell_ref].s = {
            alignment: {
              horizontal: 'center',
              vertical: 'center',
            },
            border: borderStyle,//用上面定义好的边框样式
          };
        }
        //动态自适应:计算列宽
        const va = JSON.parse(JSON.stringify(wb.Sheets[sheetName][cell_ref].v || ''));
        const card1 = JSON.parse(JSON.stringify(va.toString())).match(/[\u4e00-\u9fa5]/g); //匹配中文
        let card11 = "";
        if (card1) {
          card11 = card1.join("")
        }
        const card2 = JSON.parse(JSON.stringify(va.toString())).replace(/([^\u0000-\u00FF])/g, "");  //剔除中文
        let st = 0;
        if (card11) {
          st += card11.length * 20  //中文字节码长度
        }
        if (card2) {
          st += card2.length * 10  //非中文字节码长度
        }
        if (st > len) {
          len = st;
        }
      }
    }
    if (len > len_max) {//最大宽度
      len = len_max;
    }
    cWidth.push({'wpx': len});     //列宽
  }
  wb.Sheets[sheetName]['!cols'] = cWidth.slice(1, -1);
  // 删除列-----重点-----0就是第一列。。
  // wb.Sheets[sheetName]['!cols'][0] = {hidden: true}
  deleteCol(wb.Sheets[sheetName], 0)
  deleteCol(wb.Sheets[sheetName], cWidth.length - 1)
  // 合并列
  // wb.Sheets[sheetName]["!merges"] = [
  //   {  //合并A1A2单元格
  //     s: {//s为开始
  //       c: 0,//开始列
  //       r: 0//开始取值范围
  //     },
  //     e: {//e结束
  //       c: 1,//结束列
  //       r: 0//结束范围
  //     }
  //   }
  // ]
  const wopts = {bookType: 'xlsx', bookSST: false, type: 'binary'};
  const wbout = XLSXStyle.write(wb, wopts); //一定要用XLSXStyle不要用XLSX,XLSX是没有格式的!
  FileSaver(new Blob([s2ab(wbout)], {type: ""}), fileName + '.xlsx');

  function encodeCell(r, c) {
    return XLSX.utils.encode_cell({r, c});
  }

  // 删除行
  function deleteRow(ws, index) {
    const range = XLSX.utils.decode_range(ws['!ref']);
    for (let row = index; row < range.e.r; row++) {
      for (let col = range.s.c; col <= range.e.c; col++) {
        ws[encodeCell(row, col)] = ws[encodeCell(row + 1, col)];
      }
    }
    range.e.r--;
    ws['!ref'] = XLSX.utils.encode_range(range.s, range.e);
  }

  // 删除列
  function deleteCol(ws, index) {
    const range = XLSX.utils.decode_range(ws['!ref']);
    for (let col = index; col < range.e.c; col++) {
      for (let row = range.s.r; row <= range.e.r; row++) {
        ws[encodeCell(row, col)] = ws[encodeCell(row, col + 1)];
      }
    }
    range.e.c--;
    ws['!ref'] = XLSX.utils.encode_range(range.s, range.e);
  }

  function s2ab(s) {
    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;
  }
}

导出样式

应粉丝要求,附上导出样式

import {h} from "vue";
import {ElMessageBox} from "element-plus";

ElMessageBox({
  title: '导出Excel表格',
  draggable: true,
  showCancelButton: true,
  showConfirmButton: false,
  message: h('div', null, [ // 这里用到了h函数
    h(ElButton, { text: true, type: 'primary', innerHTML: '导出选中数据', onClick: assignExport }),
    h(ElButton, { text: true, type: 'success', innerHTML: '导出所有数据', onClick: allExport })
  ]),
  cancelButtonText: '取消',
})
// 导出
const assignExport = () =>{
  console.log(111)
}
const allExport = () =>{
  console.log(222)
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/399348.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

国际贸易报关需要向海关提交哪些资料 | 全球数字贸易发展联盟 | 箱讯科技

1、进出口货物报关单。一般进口货物应填写一式二份;需要由海关核销的货物&#xff0c;如加工贸易货物和保税货物等&#xff0c;应填写专用报关单一式三份;货物出口后需国内退税的&#xff0c;应另填一份退税专用报关单。 2、货物发票。要求份数比报关单少一份&#xff0c;对货…

OpenProject 安装迁移

文章目录 1、概要2、备份3、新服务器安装OpenProject4、新服务器安装Postgresql5、旧服务器数据转移到新服务器5.1、转移attachments附件5.2、转移conf配置文件5.3、转移存储库5.4、导入Postgres数据库 6、openproject配置 1、概要 注意&#xff1a;本文仅适用于 DEB/RPM 包安…

高品质定制线缆厂家推荐:精工电联-打造智能智造的线缆解决方案(线缆定制流程)

高品质定制线缆厂家&#xff1a;精工电联-打造智能智造的线缆解决方案&#xff08;定制线缆定制流程&#xff09; 精工电联 在百年未有之大变局的当下&#xff0c;科技发展日新月异的今天&#xff0c;智能制造正在改变着各行各业的生产及人们的生活方式。作为线缆制造领域的领先…

0220作业

C语言实现LED1闪烁 led.h #ifndef __LED_H__ #define __LED_H__//RCC寄存器封装 #define RCC_MP_AHB4_ENSETR (*(volatile unsigned int*)0x50000A28) //寄存器封装//GPIO寄存器封装 typedef struct{volatile unsigned int MODER; //00volatile unsigned int OTYPER; //04vol…

c++:蓝桥杯中的基础算法1(枚举,双指针)

枚举 基础概念&#xff1a; 枚举&#xff08;Enum&#xff09;是一种用户定义的数据类型&#xff0c;用于定义一个有限集合的命名常量。在C中&#xff0c;枚举类型可以通过关键字enum来定义。 下面是一个简单的枚举类型的定义示例&#xff1a; #include <iostream>enum…

Spring Bean 的生命周期了解么?

Spring Bean 的生命周期基本流程 一个Spring的Bean从出生到销毁的全过程就是他的整个生命周期, 整个生命周期可以大致分为3个大的阶段 : 创建 使用 销毁 还可以分为5个小步骤 : 实例化(Bean的创建) , 初始化赋值, 注册Destruction回调 , Bean的正常使用 以及 Bean的销毁 …

leetcode 105. 从前序与中序遍历序列构造二叉树【构造二叉树】

原题链接&#xff1a;105. 从前序与中序遍历序列构造二叉树 题目描述&#xff1a; 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 输入输出描述&…

ETL快速拉取物流信息

我国作为世界第一的物流大国&#xff0c;但是在目前的物流信息系统还存在着几大的痛点。主要包括以下几个方面&#xff1a; 数据孤岛&#xff1a;有些物流企业各个部门之间的数据标准不一致&#xff0c;难以实现数据共享和协同&#xff0c;容易导致信息孤岛。 操作繁琐&#x…

小程序--loading和toast

一、loading wx.showLoading({})显示loading提示框。wx.hideLoading({})隐藏loading提示框。 title&#xff1a;文字提示内容 mask&#xff1a;是否显示透明蒙层&#xff0c;防止触摸穿透。 更多属性参考showLoading官方文档。 wx.showLoading({title: 加载中...,mask: true }…

HTML学习笔记——08:表单<form>

HTML <form> 元素表示文档中的一个区域&#xff0c;此区域包含交互控件&#xff0c;用于向 Web 服务器提交信息。 例如&#xff1a;登录页面。 作用&#xff1a;搜集不同类型的用户输入&#xff0c;并向服务器传送数据。 注意&#xff1a;表单本身并不可见&#xff01;…

Git基本操作(2)

Git基本操作&#xff08;2&#xff09; 上交文件之后&#xff0c;git文件的变化git cat-file HEAD指针里面有啥文件被修改git statusgit diff 文件名 版本回退&#xff08;git reset&#xff09;撤销回退git reflog 撤销的三种情况还没有addgit checkout -- [file] 已经add还没…

全志 T527 适配 I2S

一、背景概念 在 T5 系列芯片中&#xff0c;内置了一个 AudioHub 模块&#xff0c;使用的 是I2S 接口&#xff0c;都跟 AudioHub 关联在一起&#xff0c;此时外挂的声卡若想正常工作&#xff0c;还需要配置 AudioHub 的路由信息。&#xff08;AudioHub 是全志 T527 特有的模块&…

【Java程序员面试专栏 数据结构】三 高频面试算法题:栈和队列

一轮的算法训练完成后,对相关的题目有了一个初步理解了,接下来进行专题训练,以下这些题目就是汇总的高频题目,因为栈和队列这两哥们结构特性比较向对应,所以放到一篇Blog中集中练习 题目题干直接给出对应博客链接,这里只给出简单思路、代码实现、复杂度分析 题目关键字…

高考杂志高考杂志社高考编辑部2023年第32期目录

高考论坛 高中数学课堂教学中创设有效情境的策略探究 黄进生; 3-5 核心素养为导向的高中物理教学探究 王丽萍; 6-8 高中化学“教、学、评”一体化教学模式的有效应用 陈燕; 9-11《高考》投稿&#xff1a;cn7kantougao163.com 新高考背景下高中英语阅读理解教学…

xshell安装社区(免费)版本

xshell安装社区(免费)版本 官方网址&#xff0c;点击直接跳转&#xff1a;https://www.xshell.com/zh/free-for-home-school/ 1.打开链接之后&#xff0c;先选择 家庭/学校免费 &#xff0c;然后输入名字和邮箱&#xff0c;勾选需要下载的资源&#xff1a; 2.然后邮箱就会收到…

Dockerfile文件中只指定挂载点会发生什么?

当你在VOLUME指令中只指定容器内的路径&#xff08;挂载点&#xff09;而不指定宿主机的目录时&#xff0c;Docker会为该挂载点自动生成一个匿名卷。这个匿名卷存储在宿主机的某个位置&#xff0c;但这个具体位置是由Docker自动管理的&#xff0c;用户通常不需要关心这个存储位…

JavaScript 设计模式之组合模式

组合模式 在我们日常中肯呢个会将一个表单用这种模式来创建 const Car function () { } Car.prototype.getName function () { throw new Error("需要重写该方法") } Car.prototype.getPrice function () {throw new Error("需要重写该方法") } const…

K8S临时小结

k8s是什么&#xff1f;能解决什么问题&#xff1f; k8s是容器管理平台&#xff0c;一套复杂的开源系统 如何更好的维护pod&#xff0c;k8s第二大要素&#xff08;pod控制器&#xff09; k8s的很多对容器&#xff08;pod&#xff09;管理的高级特性&#xff0c;都是基于控制器…

HarmonyOS—@Observed装饰器和@ObjectLink嵌套类对象属性变化

Observed装饰器和ObjectLink装饰器&#xff1a;嵌套类对象属性变化 概述 ObjectLink和Observed类装饰器用于在涉及嵌套对象或数组的场景中进行双向数据同步&#xff1a; 被Observed装饰的类&#xff0c;可以被观察到属性的变化&#xff1b;子组件中ObjectLink装饰器装饰的状…

HCIA-HarmonyOS设备开发认证V2.0-IOT硬件子系统-GPIO

目录 一、GPIO 概述二、GPIO模块相关API三、实例四、GPIO HDF驱动开发4.1、LED驱动程序(待续...)4.2、LED驱动配置(待续...) 坚持就有收获 轻量系统设备通常需要进行外设控制&#xff0c;例如温湿度数据的采集、灯开关的控制&#xff0c;因此在完成内核开发后&#xff0c;需要进…
最新文章