纯前端实现excel表格导入导出

2023-11-15

前言

github: https://github.com/stardew516...

以往做excel表格下载功能的时候,都是后端生成好表格后,存储在某个地方,然后给前端一个链接,前端使用a标签加download下载,或者使用node。其实纯前端也是可以做表格下载的,有一个很好用的javascript插件叫js-xlsx。

js-xlsx

github:https://github.com/SheetJS/js...
使用js-xlsx时,前端可以将后端返回的json数据拼接成自己需要导出的格式,下载到电脑中,完全不依赖后端。导入只需像平时一样选择文件,然后解析excel表格数据,转换成json格式。

目前js-xlsx对各浏览器的支持情况如下图所示:
js-xlsx兼容性

用法

以vue使用为例

  1. vue-cli脚手架搭好框架
  2. 安装包xlsxnpm install xlsx --save
  3. 代码实现(全)

    <template>
      <div class="index" v-loading.fullscreen.lock="fullscreenLoading" element-loading-text="拼命加载中...">
        <input type="file" @change="importFile(this)" id="imFile" style="display: none"
               accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"/>
        <a id="downlink"></a>
        <el-button class="button" @click="uploadFile()">导入</el-button>
        <el-button class="button" @click="downloadFile(excelData)">导出</el-button>
        <!--错误信息提示-->
        <el-dialog title="提示" v-model="errorDialog" size="tiny">
          <span>{{errorMsg}}</span>
            <span slot="footer" class="dialog-footer">
              <el-button type="primary" @click="errorDialog=false">确认</el-button>
            </span>
        </el-dialog>
        <!--展示导入信息-->
        <el-table :data="excelData" tooltip-effect="dark">
          <el-table-column label="名称" prop="name" show-overflow-tooltip></el-table-column>
          <el-table-column label="分量" prop="size" show-overflow-tooltip></el-table-column>
          <el-table-column label="口味" prop="taste" show-overflow-tooltip></el-table-column>
          <el-table-column label="单价(元)" prop="price" show-overflow-tooltip></el-table-column>
          <el-table-column label="剩余(份)" prop="remain" show-overflow-tooltip></el-table-column>
        </el-table>
      </div>
    </template>
    
    <script>
      // 引入xlsx
      var XLSX = require('xlsx')
      export default {
        name: 'Index',
        data () {
          return {
            fullscreenLoading: false, // 加载中
            imFile: '', // 导入文件el
            outFile: '',  // 导出文件el
            errorDialog: false, // 错误信息弹窗
            errorMsg: '', // 错误信息内容
            excelData: [  // 测试数据
              {
                name: '红烧鱼', size: '大', taste: '微辣', price: '40', remain: '100'
              },
              {
                name: '麻辣小龙虾', size: '大', taste: '麻辣', price: '138', remain: '200'
              },
              {
                name: '清蒸小龙虾', size: '大', taste: '清淡', price: '138', remain: '200'
              },
              {
                name: '香辣小龙虾', size: '大', taste: '特辣', price: '138', remain: '200'
              },
              {
                name: '十三香小龙虾', size: '大', taste: '中辣', price: '138', remain: '108'
              },
              {
                name: '蒜蓉小龙虾', size: '大', taste: '中辣', price: '138', remain: '100'
              },
              {
                name: '凉拌牛肉', size: '中', taste: '中辣', price: '48', remain: '60'
              },
              {
                name: '虾仁寿司', size: '大', taste: '清淡', price: '29', remain: '无限'
              },
              {
                name: '海苔寿司', size: '大', taste: '微辣', price: '26', remain: '无限'
              },
              {
                name: '金针菇寿司', size: '大', taste: '清淡', price: '23', remain: '无限'
              },
              {
                name: '泡菜寿司', size: '大', taste: '微辣', price: '24', remain: '无限'
              },
              {
                name: '鳗鱼寿司', size: '大', taste: '清淡', price: '28', remain: '无限'
              },
              {
                name: '肉松寿司', size: '大', taste: '清淡', price: '22', remain: '无限'
              },
              {
                name: '三文鱼寿司', size: '大', taste: '清淡', price: '30', remain: '无限'
              },
              {
                name: '蛋黄寿司', size: '大', taste: '清淡', price: '20', remain: '无限'
              }
            ]
          }
        },
        mounted () {
          this.imFile = document.getElementById('imFile')
          this.outFile = document.getElementById('downlink')
        },
        methods: {
          uploadFile: function () { // 点击导入按钮
            this.imFile.click()
          },
          downloadFile: function (rs) { // 点击导出按钮
            let data = [{}]
            for (let k in rs[0]) {
              data[0][k] = k
            }
            data = data.concat(rs)
            this.downloadExl(data, '菜单')
          },
          importFile: function () { // 导入excel
            this.fullscreenLoading = true
            let obj = this.imFile
            if (!obj.files) {
              this.fullscreenLoading = false
              return
            }
            var f = obj.files[0]
            var reader = new FileReader()
            let $t = this
            reader.onload = function (e) {
              var data = e.target.result
              if ($t.rABS) {
                $t.wb = XLSX.read(btoa(this.fixdata(data)), {  // 手动转化
                  type: 'base64'
                })
              } else {
                $t.wb = XLSX.read(data, {
                  type: 'binary'
                })
              }
              let json = XLSX.utils.sheet_to_json($t.wb.Sheets[$t.wb.SheetNames[0]])
              console.log(typeof json)
              $t.dealFile($t.analyzeData(json)) // analyzeData: 解析导入数据
            }
            if (this.rABS) {
              reader.readAsArrayBuffer(f)
            } else {
              reader.readAsBinaryString(f)
            }
          },
          downloadExl: function (json, downName, type) {  // 导出到excel
            let keyMap = [] // 获取键
            for (let k in json[0]) {
              keyMap.push(k)
            }
            console.info('keyMap', keyMap, json)
            let tmpdata = [] // 用来保存转换好的json
            json.map((v, i) => keyMap.map((k, j) => Object.assign({}, {
              v: v[k],
              position: (j > 25 ? this.getCharCol(j) : String.fromCharCode(65 + j)) + (i + 1)
            }))).reduce((prev, next) => prev.concat(next)).forEach(function (v) {
              tmpdata[v.position] = {
                v: v.v
              }
            })
            let outputPos = Object.keys(tmpdata)  // 设置区域,比如表格从A1到D10
            let tmpWB = {
              SheetNames: ['mySheet'], // 保存的表标题
              Sheets: {
                'mySheet': Object.assign({},
                  tmpdata, // 内容
                  {
                    '!ref': outputPos[0] + ':' + outputPos[outputPos.length - 1] // 设置填充区域
                  })
              }
            }
            let tmpDown = new Blob([this.s2ab(XLSX.write(tmpWB,
              {bookType: (type === undefined ? 'xlsx' : type), bookSST: false, type: 'binary'} // 这里的数据是用来定义导出的格式类型
            ))], {
              type: ''
            })  // 创建二进制对象写入转换好的字节流
            var href = URL.createObjectURL(tmpDown)  // 创建对象超链接
            this.outFile.download = downName + '.xlsx'  // 下载名称
            this.outFile.href = href  // 绑定a标签
            this.outFile.click()  // 模拟点击实现下载
            setTimeout(function () {  // 延时释放
              URL.revokeObjectURL(tmpDown) // 用URL.revokeObjectURL()来释放这个object URL
            }, 100)
          },
          analyzeData: function (data) {  // 此处可以解析导入数据
            return data
          },
          dealFile: function (data) {   // 处理导入的数据
            console.log(data)
            this.imFile.value = ''
            this.fullscreenLoading = false
            if (data.length <= 0) {
              this.errorDialog = true
              this.errorMsg = '请导入正确信息'
            } else {
              this.excelData = data
            }
          },
          s2ab: function (s) { // 字符串转字符流
            var buf = new ArrayBuffer(s.length)
            var view = new Uint8Array(buf)
            for (var i = 0; i !== s.length; ++i) {
              view[i] = s.charCodeAt(i) & 0xFF
            }
            return buf
          },
          getCharCol: function (n) { // 将指定的自然数转换为26进制表示。映射关系:[0-25] -> [A-Z]。
            let s = ''
            let m = 0
            while (n > 0) {
              m = n % 26 + 1
              s = String.fromCharCode(m + 64) + s
              n = (n - m) / 26
            }
            return s
          },
          fixdata: function (data) {  // 文件流转BinaryString
            var o = ''
            var l = 0
            var w = 10240
            for (; l < data.byteLength / w; ++l) {
              o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w)))
            }
            o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)))
            return o
          }
        }
      }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style>
      .el-table th>.cell {
        text-align: center;
      }
      .button {
        margin-bottom: 20px;
      }
    </style>
    
  4. 启动项目npm run dev

效果图

clipboard.png

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

纯前端实现excel表格导入导出 的相关文章

  • Android 和 Facebook SDK:从 /me/picture 图形调用中解码图片

    编辑 Anwser 在这篇文章的末尾 我正在尝试通过内置的 Facebook SDK 功能获取 Facebook 用户的个人资料图片Request 我正在使用一个 me picture调用获取个人资料图片并将其转换为Bitmap 调用工作正
  • 如何读取本地 JSON 文件进行测试

    我正在尝试编写用于 json 验证的单元测试 因为该应用程序严重依赖于来自 REST API 的 json 我有一个包含简单 json 的本地文件 goodFeaturedJson txt 内容 test TEST 测试用例 void te
  • Facebook Graph API 使用 json 和 C# 检索好友

    我正在使用 C 和 Graph API 进行工作 并且能够获取 Facebook 用户个人资料信息 例如 ID 姓名和电子邮件 然后反序列化 JSON 以便能够将值分配给标签 然而 我的问题是 当我去获取好友列表或任何与此相关的列表时 如何
  • HTML 解析 - 从 div 内的表格获取数据?

    我对 HTML 解析 抓取的整个想法还比较陌生 我希望我能来这里获得我需要的帮助 基本上我想要做的 我认为 是指定我希望从中获取数据的页面的 url 在这种情况下 http www epgpweb com guild us Caelestr
  • Web API 复杂参数属性均为 null

    我有一个 Web API 服务调用可以更新用户的首选项 不幸的是 当我从 jQuery ajax 调用中调用此 POST 方法时 请求参数对象的属性始终为 null 或默认值 而不是传入的值 如果我使用 REST 客户端调用相同的方法 我使
  • Android AsyncTask 第二次调用时未执行

    我有一个扩展 AsyncTask 的类 public class SendJSONArray2Server extends AsyncTask
  • serde_json::from_str 错误,其中字符串来自文件

    extern crate serde json use serde json Value use std fs File use std io prelude fn main let filepath map test anhui txt
  • 在 Vue js 中获取 JSON 属性时出错

    我在使用 Vue js 时遇到了一个奇怪的行为 我进行 ajax 调用 将结果 一些 JSON 存储到名为 modello 的 Vue 数据属性中 lineaGialla selected false descrizione Questa
  • Flot 0.8.2 折线图 - 颜色错误

    我正在使用 Flot 折线图并设置它们的颜色 我发现了一个奇怪的错误 在前 3 种颜色之后 绘图对所有其他线条使用最后一种颜色 这不是正确的行为 更有趣的是图例显示了正确的颜色 这是一个已知的错误 var dataSet label d1
  • 如何使用Gson将JSONArray转换为List?

    在我的 Android 项目中 我试图将收到的 JSONArray 转换为列表 在 的帮助下这个答案 https stackoverflow com questions 8371274 how to parse json array in
  • 将 JSON 字符串传递给 Django 模板

    我一直在用头撞墙 试图找出为什么我无法将从 Django 模型生成的 JSON 字符串传递到模板的 javascript 静态文件中 事实证明 问题不在模型级别 使用serializers serialize 在脚本本身中放入相同的字符串将
  • 如何在 laravel 中查询 json 列?

    我用的是 Laravel 5 6 我有一块田地 字段的数据类型为json 字段 desc 字段 的值如下所示 code 1 club CHE country ENGLAND code 2 club BAY country GERMANY c
  • 使用架构定义验证 JSON 对象

    只要我们可以根据预定义的模式 即 XSD 或 DTD 验证传入的 XML 文件 我们就可以对传入的 JSON 对象执行验证 有可用的 JSON 架构定义吗 有一个工作草案JSON 模式 http json schema org 您还可以看一
  • 使用 ActiveModel::Serializers 包含两个父 json 数组

    我正在尝试发送如下所示的前端应用程序 json facilities id 5 name happy days ranch location address 1424 Pastoral Lane zipcode 25245 instruct
  • 如何在 Scala 中使用 Circe 解码 JSON 列表/数组

    我有代码片段 cursor downField params downField playlist downField items as List Clip 其中 Clip 是字符串和数字的简单 case 类 传入的 Json 应包含一个
  • 解析JSON数据并将其放入gridview中

    我正在开发一个应用程序 我必须在其中解析JSON数据并且必须将它们放入自定义中gridview 它应该是这样的 到目前为止 我已经在 asynctask 中解析了 JSON 数据并获取了这些值 这是我的代码 private class ge
  • Windows Phone 的 JSON 反序列化

    我正在尝试反序列化以下 JSON 但我真的不知道如何使用 JSON net 来完成这项工作 我正在使用 C 和 JSON Net 库 我的 JSON 如下 found 3 bounds 43 54919 172 62148 43 54487
  • 来自 Pandas DataFrame 的用户定义的 Json 格式

    我有一个 pandas dataFrame 打印 pandas DataFrame 后 结果如下所示 country branch no of employee total salary count DOB count email x a
  • JSONP 回调失败,需要 javascript/jquery 帮助

    我是 json 的菜鸟 了解一点 jquery 并尝试让一个小脚本工作 我想检索某个纬度 经度的时间 并根据我在网上阅读的内容编写了这个脚本 getJSON http ws geonames org timezoneJSON lat 47
  • HttpResponseMessage 的内容为 JSON

    我有一个 ASP NET MVC WEB API 由于多种原因 由于没有授权而重定向 我不能只使用一个简单的对象并在我的控制器方法中返回它 因此我需要 HttpResponseMessage 类来允许我重定向 目前我正在这样做 var re

随机推荐

  • 【vue3引入高德地图】

    vue3引入高德地图 文章目录 vue3引入高德地图 前言 一 准备工作 1 开发文档 2 添加应用 二 使用步骤 1 npm 安装 2 地图容器创建 3 组件引入 4 js api 安全密钥 5 初始化地图 6 图层 6 1 添加 设置
  • 在 Windows 10 编译 Qt 5.15

    译好的下载链接 Qt5 15 8 Windows x86 VS2017 Qt5 15 8 Windows x86 64 VS2017 Qt5 15 8 Windows x86 VS2019 Qt5 15 8 Windows x86 64 V
  • 【Unity】通过代码控制编译器的暂停

    暂停编译器 EditorApplication isPaused true 结束编译器 EditorApplication isPlaying false
  • sklearn决策树之random_state & splitter

    在上一篇博文 决策树的sklearn实现 中 我们建立了一棵完整的决策树 但是如果在建立模型时不设置random state的数值 score会在某个值附近波动 引起画出来的每一棵树都 一样 它为什么会 稳定呢 如果使用其他数据集 它还会不
  • 互斥机制之自旋锁(spinlock)

    一 基础 自旋锁 如果测试结果表明锁仍被占用 程序将在一个小的循环内重复这个 测试并设置 操作 即进行所谓的 自旋 1 定义自旋锁 spinlock t spin 2 初始化自旋锁 spin lock init lock 该宏用于动态初始化
  • 【论文笔记】Masked Autoencoders Are Scalable Vision Learners

    论文 论文标题 Masked Autoencoders Are Scalable Vision Learners 发表于 CVPR2021 论文链接 https arxiv org pdf 2111 06377 pdf 论文代码 https
  • WebGL学习系列-片元着色器简介

    前言 到目前为止 我们绘制过点 三角形 矩形等 但使用的都是单色系 之前曾经说过着色器的概念 着色器分为顶点着色器和片元着色器 我们一直在使用顶点着色器 而对片元着色器基本没有提及过 本小节将展开对片元着色器的简单介绍 彩色的点 之前提到过
  • Sybase IQ常用函数大全--杂项函数

    Sybase IQ常用函数大全 杂项函数 查询索引 COALESCE 函数 返回列表中的第一个非 NULL 表达式 IFNULL 函数 返回第一个非空值表达式或 NULL ISNULL 函数 返回参数列表中的第一个非 NULL 表达式的值
  • 笔记~【软件测试基础知识】——黑盒测试和白盒测试

    这里写目录标题 一 黑盒测试 二 白盒测试 一 黑盒测试 黑盒测试概述 黑盒测试也称功能测试或数据驱动测试 它已知产品所应具有的功能 通过测试来检测每个功能是否能够正常使用 主要针对软件界面和软件功能 在测试时 把程序看作一个不能打开的黑盒
  • cv::Mat的翻转和转置

    cv Mat的本质是矩阵 openCV对Mat类型的处理 实际上也是矩阵操作 这里给个小例子 介绍转置操作和翻转操作 这段代码受了 http www tuicool com articles emIr2u 的启发 原图 Mat m1 imr
  • 【数据预处理】Pandas缺失的数据处理

    目录 缺少数据基础 何时 为何 数据丢失 被视为 缺失 的值 日期时间 插入缺失数据 缺少数据的计算 Sum Prod of Empties Nans GroupBy中的NA值 清理 填写缺失数据 填充缺失值 fillna 用PandasO
  • flutter -- 自定义音乐播放器/视频播放器

    写在前头 flutter 自定义实现音乐播放的文章挺多的 但是在开发中还是碰见了超级无语的情况 没想到需求竟然要音频的1倍到2倍的播放倍速 我一度质疑这个功能的实际用途 但是既然提出来了 开发就得撅屁股实现 这里采用了第三方的视频播放器来实
  • 如何使用BurpSuite(后续)

    前面那篇文章是我前几天写的 我发现我把简单的问题弄得复杂了 今天我给大家再写一篇关于BurpSuite的使用 1 下载安装免费版或者收费版 这里就不演示了 2 运行软件 一直NEXT就可以 3 打开工具 此时拦截状态显示OFF 4 在打开的
  • Python中类成员变量与实例成员变量相互影响的原因超详细解释

    今天在看python学习手册时看到了两句话 一 第26章中 类对象提供默认行为 二 第26章中 实例对象是具体的元素 书中给的例子是这样的 但上网查了一下好像第二句话不是非常准确 如下面的文章 原文 https www jb51 net a
  • 优化算法学习(LM算法)

    文章目录 推荐书籍 理论理解 程序实现 ceres安装 代码 推荐书籍 建议学习 METHODS FOR NON LINEAR LEAST SQUARES PROBLEMS http www2 imm dtu dk pubdb views
  • Eclipse导入Maven项目,实在算得上是历经千辛万苦

    私下接触了一个项目 架构师那边用的是idea 并且是一个Maven项目 架构师说他那边idea可以自动将Maven项目转换为Web项目 但我已经习惯用Eclipse了 所以还需要自己动手试一试 这一试 一上午的时间算是过去了 尤其是中间遇到
  • 商品关联度分析(关联三度,附Python实战) 我的钱就是这么没的,不只有皮尔森系数的相关分析

    引言 上一年组织烧烤活动买食材时 我在超市的货架29买了一个烧烤架 然后到货架27买了瓶1 5L的可乐 最后在货架25找到了我需要的塑料小碗 今年再去那家超市的时候 特地再去烧烤架所在的货架查看了一下 看看有没有什么值得记录的灵感 果不其然
  • Mybatis处理枚举

    Springboot 集成 Mybatis 处理枚举 mybatis自带了两个处理枚举的 类 EnumTypeHandler EnumOrdinalTypeHandler 一个使用枚举的name 一个使用枚举的下标 做项目时 会节省数据库资
  • 业务逻辑漏洞总结

    业务逻辑漏洞总结
  • 纯前端实现excel表格导入导出

    前言 github https github com stardew516 以往做excel表格下载功能的时候 都是后端生成好表格后 存储在某个地方 然后给前端一个链接 前端使用a标签加download下载 或者使用node 其实纯前端也是