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

news2024/12/23 14:01:25

  • 需求背景
  • 解决思路
  • 解决效果
  • 将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.coloradmin.cn/o/1460743.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Aster实现一台电脑当两台使——副屏搭配键鼠

前言&#xff1a;笔者每年回家&#xff0c;都面临着想要和小伙伴一起玩游戏&#xff0c;但小伙伴没有电脑/只有低配电脑的问题。与此同时&#xff0c;笔者自身的电脑是高配置的电脑&#xff0c;因此笔者想到&#xff0c;能否在自己的电脑上运行游戏&#xff0c;在小伙伴的电脑上…

Code-Audit(代码审计)习题记录

介绍&#xff1a; 自己懒得搭建靶场了&#xff0c;靶场地址是 GitHub - CHYbeta/Code-Audit-Challenges: Code-Audit-Challenges为了方便在公网练习&#xff0c;可以随地访问&#xff0c;本文所有的题目均来源于网站HSCSEC-Code Audit 1、习题一 题目内容如下&#xff1a; 1…

2024pytest自动化测试框架学习(三)

在自动化测试中我们经常会针对某些测试方法编写前置测试数据&#xff0c;当测试方法执行完毕后再清理这些测试数据。之前我们接触的unittest框架中&#xff0c;使用setUp、tearDown方法来解决前置数据、后置数据销毁的问题。pytest为我们提供了更加强大灵活的fixtrue来完成该实…

《Solidity 简易速速上手小册》第8章:高级 Solidity 概念(2024 最新版)

文章目录 8.1 高级数据类型和结构8.1.1 基础知识解析更深入的理解实际操作技巧 8.1.2 重点案例&#xff1a;构建一个去中心化身份系统案例 Demo&#xff1a;创建去中心化身份系统案例代码DecentralizedIdentityContract.sol 测试和验证拓展案例 8.1.3 拓展案例 1&#xff1a;管…

MedicalGPT 训练医疗大模型,实现了包括增量预训练、有监督微调、RLHF(奖励建模、强化学习训练)和DPO(直接偏好优化)

MedicalGPT 训练医疗大模型&#xff0c;实现了包括增量预训练、有监督微调、RLHF(奖励建模、强化学习训练)和DPO(直接偏好优化)。 MedicalGPT: Training Your Own Medical GPT Model with ChatGPT Training Pipeline. 训练医疗大模型&#xff0c;实现了包括增量预训练、有监督微…

我为什么不喜欢关电脑?

程序员为什么不喜欢关电脑&#xff1f; 你是否注意到&#xff0c;程序员们似乎从不关电脑&#xff1f;别以为他们是电脑上瘾&#xff0c;实则是有他们自己的原因&#xff01;让我们一起揭秘背后的原因&#xff0c;看看程序员们真正的“英雄”本色&#xff01; 一、上大学时。 …

AP5216 降压恒流 全亮 半亮9W 车灯手电筒 性价比方案

AP5216 是一款 PWM工作模式, 高效率、外 围简单、内置功率管&#xff0c;适用于5V&#xff5e;100V输入的高 精度降压 LED 恒流驱动芯片。输出功率可达 9W&#xff0c;电流 1.0A。AP5216 可实现全亮/半亮功能切换&#xff0c;通过 MODE 切换&#xff1a;全亮/半亮 模式。AP5216…

《小程序商城定制:为你打造独特的线上商业帝国》

在当今数字化的时代&#xff0c;拥有一个定制的小程序商城已经成为了许多企业和个人创业者的首要任务。小程序商城作为一种轻量级的移动应用&#xff0c;具有无需下载、使用方便、传播迅速等优势&#xff0c;为用户提供了更加便捷的购物体验。而通过定制小程序商城&#xff0c;…

linux部署jenkins,支持jdk1.8

无废话&#xff0c;纯干活安装指令 本文前提条件需安装jdk8&#xff0c;安装参考&#xff1a;Linux配置jdk环境 下载资源 # 创建安装目录 mkdir -p /data/jenkins && cd /data/jenkins# 下载jenkins的war包&#xff0c;v2.346.x支持jdk1.8&#xff0c;高于这个版本的…

书生·浦语大模型实战营第四节课作业

基础作业 fintune过程 这里要注意下。 合并完参数的模型再进行网页部署时&#xff0c;需要用到InternLM源码&#xff0c;教程里面忽略了需要commit版本。通过以下命令转到所需版本&#xff0c;然后就可以看到web_demo.py。 cd InternLM git checkout 3028f07cb79e5b1d7342f4…

ping 8.8.8.8和ping www.baidu.com都OK,但是打不开网页

ping 8.8.8.8和ping www.baidu.com都OK&#xff0c;但是打不开网页 打开设置 -> 网络 找到IPV4, DNS栏输入 8.8.8.8 , apply 设置里界面变成这样 然后网页就能加载了

EPS-HiL转向硬件在环测试方案

1、项目背景 为提升研发能力&#xff0c;深入研究电动助力转向系统(EPS)功能&#xff0c;拟搭建EPS硬件在环测试设备。本设备可进行多种EPS相关测试实验。 2、需求分析 本方案中被测EPS如下图&#xff0c;为转向杆式电动助力转向器&#xff08;C-EPS&#xff09;&#xff0c…

Apache Flink连载(三十一):Flink基于Kubernetes部署(1)-Kubernetes介绍

🏡 个人主页:IT贫道-CSDN博客 🚩 私聊博主:私聊博主加WX好友,获取更多资料哦~ 🔔 博主个人B栈地址:豹哥教你学编程的个人空间-豹哥教你学编程个人主页-哔哩哔哩视频 目录

【多线程】Java线程的几种状态详解

线程的状态 1 观察线程的所有状态2 线程状态和状态转移的意义3 观察线程的状态和转移 1 观察线程的所有状态 线程的状态是⼀个枚举类型 Thread.State public class ThreadState {public static void main(String[] args) {for (Thread.State state : Thread.State.values()) …

利用故事推动企业变革:如何提升数据分析技能

单一的数据和表格尽管有算法的支撑&#xff0c;但在其表达方式上总会让人感到头疼。当我们需要深入了解企业的盈利能力&#xff0c;或是尝试评估业务的增长机会时&#xff0c;以往都会将精力全部放在分析数字、阅读信息、回顾历史和沟通交流之上&#xff0c;却忽略随之而生成的…

【中创:抢抓机遇,乘势而上】AI引爆算力需求,算力市场或将持续供不应求

2030年人类将迎来YB数据时代&#xff0c;全球算力总规模达到56ZFlops&#xff0c;其中智能算力达到52.5ZFlops&#xff0c;在算力需求中占绝对主导地位。 AI大模型迭代速度越来越快&#xff0c;各大厂商对智能算力投入大幅增加&#xff0c;支持存储和训练的高端AI服务器的需求…

基于Java+SpringBoot的旅游路线规划系统(源码+论文)

文章目录 目录 文章目录 前言 一、功能设计 二、功能实现 1.1 前端首页模块的实现 1.2 景点新闻 1.3 景点在线预订 1.4 酒店在线预订 1.5 管理员景点管理 1.6 管理员旅游线路管理 1.7 酒店信息管理 三、库表设计 前言 随着我国的经济的不断发展&#xff0c;现在的一些热门的景…

雷卯推荐多种8KW以上TVS二极管

一、 应用 TVS 二极管应用广泛&#xff0c;如消费电子产品、汽车工业、弹药、电信、航空航天工业和智能控制系统。 二、雷卯推荐系列产品列表&#xff1a; 以上仅是部分产品推荐&#xff0c;更多产品请联系leiditech 三、 封装尺寸数据 四、雷卯国产化替代表 部分展示 上海…

深究 DevOps 与平台工程的区别

今天&#xff0c;我们将讨论平台工程和 DevOps 的关系。尽管这两个概念有一些共同点&#xff0c;但它们仍然是截然不同的&#xff0c;我们将具体了解它们之间的区别。本文旨在解释当代软件工程中的这两个基本概念。通过实际案例&#xff0c;我们将分别说明这两个方法如何塑造了…

svn tortoiseSVN没有显示更新后的log

同事更新了svn提交 自己打开文件发现文件没有更新&#xff0c;而且log显示也没用更新 需要勾选log界面下的这个 显示同事提交合并的修改