草庐IT

vue xlsx插件导入

cecoal 2023-04-08 原文

首先安装xlsx插件

yarn add xlsx@0.15.3

安装时候就有一个坑。版本问题第一次安装没有指定版本直接yarn add xlsx,安装的最新版本0.18多,会报XLSX没有read方法(读取Excel表格对象的方法)

具体代码

<el-upload class="upload-btn"
            ref="upload"
            action
            accept=".xls, .xlsx"
            :show-file-list="false"
            :on-change="readExcel"
            :auto-upload="false">
   <el-button slot="trigger"
              icon="el-icon-upload"
              size="small"
              type="primary">导入</el-button>
 </el-upload>
import XLSX from 'xlsx';

//导入 表单上传
	readExcel (file) {
	  // console.log('file', file);
	  const types = file.name.slice(file.name.lastIndexOf('.'))
	  const fileType = ['.xlsx', '.xls'].some(item => item === types)
	  // console.log(file.name.lastIndexOf('.'), types, fileType);
	  //  校验格式
	  if (!fileType) {
	    this.$message('格式错误!请重新上传')
	    return
	  }
	  // 返回workbook
	  this.file2Xce(file).then(tabJson => {
	    // console.log('tabJson', tabJson);
	    this.handleImportItem(tabJson)  //处理导入的数据使导入的数据在页面中展示。这个方法根据自己的需求,属于定制化
	  })
	},

  // 读表单,返回workbook
    file2Xce (file) {
      return new Promise(resolve => {
        const reader = new FileReader()
        reader.onload = e => {
          const data = e.target.result
          // 二进制流方式读取得到整份Excel表格对象
          this.excelData = XLSX.read(data, {
            type: 'binary'
          })
          // console.log('exvelData', this.excelData);
          // 只取第一个工作表
          const wsname = this.excelData.SheetNames[0]// 取第一张表
          const ws = XLSX.utils.sheet_to_json(this.excelData.Sheets[wsname])// 生成json表格内容
          resolve(ws)
        }
        // 以二进制方式打开文件
        reader.readAsBinaryString(file.raw); //file.raw取上传文件的File
      })
    },
 

需求
导入的Excel表格模板是这样格式的

可以发现第7行才是真正的表头。Excel中测量n列(是动态的),对应的项目表格中的样本数列。

需要根据Excel表中项目和项目中项目编号对应上,确定是哪一行的数据,然后把测量和样本对应上。

其中一种写法,写的有点麻烦,有时间用传入的tabs这种格式处理这种方法在写一遍

     // 处理导入的数据
    handleImportItem (tabs) {
      // console.log(tabs);
      // console.log('excelData', this.excelData);
      // 获取所有sheet页的name,不包括隐藏
      const sheetNames = (this.excelData.Workbook.Sheets.filter(l => l.Hidden !== 1) || []).map(l => l.name)
      // console.log(sheetNames);
      // 根据所有sheet页的name,解析sheet页的数据,变成A1(A列1行),B2这样单元格对象的数组 取第一个工作表
      const sheetData = sheetNames.map(l => {
        return {
          ...this.excelData.Sheets[l],
          sheetName: l
        }
      })[0]
      // console.log('sheetData', sheetData);
      // 取第7行之后有用数据 以A1,B2单元格的key值组成数组
      let cell = (Object.keys(sheetData).filter(l => /^[A-Z].*/.test(l)) || []).filter(c => c.replace(/[^0-9]/ig, "") >= 7);
      // console.log('cell', cell);
      // 导入的表格头
      let excelHeader = []
      cell.forEach(c => {
        let str = c.replace(/[^a-zA-Z]/g, "") //列 A,B,C...
        let num = c.replace(/[^0-9]/ig, "") //行 1,2,3...
        // console.log(str, num);
        // 动态表格头绑定code 第7行是真正的表格头
        if (num == 7) {
          // console.log(c, sheetData[c], sheetData['A7']);
          if (c == 'A7' && sheetData['A7'].v == '项目') {
            excelHeader.push({
              name: sheetData[c].v,
              code: 'itemCode',
              col: str,
            })
          } else {
            if (sheetData['A7'].v !== '项目') {
              this.$message('格式错误!请重新选择');
              return;
            } else {
              this.quantitativeData.forEach(q => {
                if (q.itemList) {
                  // console.log(q.sampleTableHeaders);
                  q.sampleTableHeaders.forEach(l => {
                    if (l.name.replace(/[^0-9]/ig, "") == sheetData[c].v.replace(/[^0-9]/ig, "")) {
                      excelHeader.push({
                        name: sheetData[c].v,
                        code: l.code,
                        col: str
                      })
                    }
                  })
                }
              })
            }
          }
        }

        // 赋值 导入的表格头里code与页面中表格头的code对应上就赋值
        // 通过项目编号判断是否是同一行
        excelHeader.forEach(l => {
          this.quantitativeData.forEach(q => {
            if (q.itemList) {
              // console.log('q', q);
              q.itemList.forEach(i => {
                // 判断项目编号
                if (sheetData[c].v == i.itemCode) {
                  // 项目编号对应上的行表头
                  let row = cell.filter(v => v.replace(/[^0-9]/ig, "") == num)
                  row.forEach(r => {
                    // 判断列号和导入的表格头的col 把样本对应上,样本列表要小于样本数
                    if (r.replace(/[^a-zA-Z]/g, "") == l.col && parseInt(l.code.replace(/[^0-9]/ig, "")) <= i.sampleCnt) {
                      // i[l.code] = sheetData[r].v
                      this.$set(i, l.code, sheetData[r].v)
                    }
                  })
                  this.checkSampleCnt(i, q)
                }
              })
            }
          })
        })
      })
      // console.log('excelHeader', excelHeader);
    },

来了来了
使用传入的tabs完成处理数据,赋值。(果然简单好多,代码量也蹭蹭减少,运行速度也快)
现在的需求也和上面的不太一样了,现在需求,页面加了来料批次列。需要项目和设备项目号匹配且批次一致,如果页面批次没有直接赋值就行。

因为是优化,所以想尽量减少循环,所以加了参数dataItem(页面中点击导入的大项)之前一直是循环整个页面,取到大项,再循环大项下的小项,直接就两层循环了,再加上处理数据赋值写的也麻烦,总共循环写了5层,里面还有filter。相当于最多循环6。。。实际操作随便几十条数据就卡死,别骂了别骂了,我知道写的烂到家了

upload组件on-change事件如果想传自定义参数,请这样写

<el-upload
                class="upload-btn"
                ref="upload"
                action
                accept=".xls, .xlsx"
                :show-file-list="false"
                :on-change="
                  (file, fileList) => {
                    readExcel(file, dataItem);
                  }
                "
                :auto-upload="false"
              >
                <el-button slot="trigger" icon="el-icon-upload" size="small" type="primary">导入</el-button>
              </el-upload>

处理数据,赋值简便写法

    handleImportItem(tabs, dataItem) {
      let excelHeader = [];
      tabs.forEach((tabData, index) => {
        if (index == 4 && Object.values(tabData)[0] !== '项目') {
          this.$message('格式错误!请重新选择');
          return;
        } else {
          if (Object.values(tabData)[0] === '项目') {
            for (let i in tabData) {
              let num = tabData[i].replace(/[^0-9]/gi, '');
              if (num) {
                if (num < 10) {
                  tabData[i] = 'data0' + tabData[i].replace(/[^0-9]/gi, '');
                } else {
                  tabData[i] = 'data' + tabData[i].replace(/[^0-9]/gi, '');
                }
              } else {
                if (tabData[i] === '名义值') tabData[i] = 'specsVal';
                if (tabData[i] === '上公差') tabData[i] = 'upperTol';
                if (tabData[i] === '下公差') tabData[i] = 'lowerTol';
                if (tabData[i] === '状态') tabData[i] = 'checkRsType';
                if (tabData[i] === '测量值') tabData[i] = 'data01';
              }
              excelHeader.push({
                name: tabData[i],
                code: i,
              });
            }
            // console.log('excelHeader', excelHeader);
          }
          if (index > 4) {
            dataItem.itemList.forEach(item => {
              // 设备项目号和项目对应
              if (
                excelHeader[0].name === '项目' &&
                item.eqpItemNo === tabData[excelHeader[0].code] &&
                (item.lotNo === tabs[2].__EMPTY || !item.lotNo)
              ) {
                // 来料批次
                if (!item.lotNo) this.$set(item, 'lotNo', tabs[2].__EMPTY);
                excelHeader.forEach(header => {
                  let headerNum = header.name.replace(/[^0-9]/gi, '');
                  // 测量数据小于样本数;
                  if (parseInt(headerNum) <= +item.sampleCnt) {
                    // 处理导入数据为指数
                    typeof tabData[header.code] === 'string' && tabData[header.code].indexOf('^') >= 0
                      ? (tabData[header.code] = Math.pow(
                          +tabData[header.code].split('^')[0],
                          +tabData[header.code].split('^')[1]
                        ))
                      : tabData[header.code];
                    this.$set(item, header.name, tabData[header.code]);
                  }
                  // 名义值,上下公差
                  if (!headerNum) {
                    if (tabData[header.code] === 'OK') tabData[header.code] = 'PASS';
                    if (tabData[header.code] === 'NG') tabData[header.code] = 'FAIL';
                    this.$set(item, header.name, tabData[header.code]);
                  }
                });
              }
              this.checkSampleCnt(item, dataItem);
            });
          }
        }
      });
    },

最近又遇到了导入的Excel 中数据有日期时间的
直接导入不太行会得到一串数字。
官方文档上有这两个参数是关于时间格式转换的


但是吧实际使用的时候dateNF这个参数不起作用,不管是XLSX.read上这个参数还是XLSX.utils.sheet_to_json这个上边dateNF参数都没起作用,不晓得为啥

导入日期时间解决

// 读表单,返回workbook
file2Xce (file) {
  return new Promise(resolve => {
    let reader = new FileReader()
    reader.onload = e => {
      let data = e.target.result
      this.excelData = XLSX.read(data, {
        type: 'binary',
        cellDates: true, // 格式转成中国标准时间
        dateNF: 'yyyy-MM-dd' // 没起作用
      })
      let wsname = this.excelData.SheetNames[0]
      console.log(this.excelData.Sheets[wsname]);
      let ws = XLSX.utils.sheet_to_json(this.excelData.Sheets[wsname],{row: true}) // 转日期格式时,最好加上row: true参数
      resolve(ws)
    }
    reader.readAsBinaryString(file.raw);
  })
},
// 赋值
handleGetFile(data) {
  data.forEach( row => {
    if(row['预计完成时间']) {
      this.filesInfo.caseActionTos.push({
        Date: moment(row['预计完成时间']).format('YYYY/MM/DD'), // 赋值时候转成想要的格式
      })
    }
  })
},

更详细的参数什么的看下边的
xlsx官方文档https://www.npmjs.com/package/xlsx

有关vue xlsx插件导入的更多相关文章

  1. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  2. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  3. ruby - 检查是否通过 require 执行或导入了 Ruby 程序 - 2

    如何检查Ruby文件是否是通过“require”或“load”导入的,而不是简单地从命令行执行的?例如:foo.rb的内容:puts"Hello"bar.rb的内容require'foo'输出:$./foo.rbHello$./bar.rbHello基本上,我想调用bar.rb以不执行puts调用。 最佳答案 将foo.rb改为:if__FILE__==$0puts"Hello"end检查__FILE__-当前ruby​​文件的名称-与$0-正在运行的脚本的名称。 关于ruby-检查是否

  4. ruby-on-rails - 您希望看到哪些 Rails 插件? - 2

    您认为可以作为插件很好地存在于您的Rails应用程序中必须实现的哪些行为?您过去曾搜索过哪些插件功能但找不到?哪些现有的Rails插件可以改进或扩展,如何改进或扩展? 最佳答案 我希望在管理界面中看到一个引擎插件,它提供了应用程序中所有模型的仪表板摘要,以及可配置的事件图表。 关于ruby-on-rails-您希望看到哪些Rails插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questio

  5. ruby - vagrant 从 github 安装插件 - 2

    我们正在使用Vagrant进行部署,我们最终希望将此集群部署在Rackspace上。vagrant-rackspace插件是一个自然的选择,但它有一些错误,这些错误未包含在最新的0.1.1版本中(notablythatvagrantprovisiondoesn'twork)。我已经在我的personalfork中解决了这个问题通过合并其他人的工作来对存储库进行改造。是否可以从github安装vagrant插件?显而易见的事情没有奏效:[unix]$vagrantplugininstallvagrant-rackspace--plugin-sourcehttps://github.com

  6. Ruby:如何从另一个文件导入变量? - 2

    我正在尝试创建一个与compass一起使用的本地配置文件,这样我们就可以处理开发人员机器上的不同导入路径。到目前为止,我已经尝试将文件导入到异常block中,以防它不存在,然后进一步使用该变量:local_config.rbVENV_FOLDER='venv'config.rbVENV_FOLDER='.'beginrequire'local_config.rb'rescueLoadErrorendputsVENV_FOLDER通常我是一名Python开发人员,所以我希望导入将VENV_FOLDER的值更改为venv,但它仍然是。之后。有没有一种方法可以导入local_config.r

  7. ruby - Ruby 导入的方法总是私有(private)的吗? - 2

    最好用一个例子来解释:文件1.rb:deffooputs123end文件2.rb:classArequire'file1'endA.new.foo将给出错误“':调用了私有(private)方法'foo'”。我可以通过执行A.new.send("foo")来解决这个问题,但是有没有办法公开导入的方法?编辑:澄清一下,我没有混淆include和require。另外,我不能使用正常包含的原因(正如许多人正确指出的那样)是因为这是元编程设置的一部分。我需要允许用户在运行时添加功能;例如,他可以说“run-this-app--includefile1.rb”,应用程序的行为将根据他在file1

  8. ruby-on-rails - Rails 导入 CSV 错误 : invalid byte sequence in UTF-8 - 2

    尝试在我的Rails应用程序中导入CSV文件时,出现错误UTF-8中的无效字节序列。一切正常,直到我添加了一个gsub方法来将其中一个CSV列与我的数据库中的一个字段进行比较。当我导入CSV文件时,我想检查每一行的地址是否包含在特定客户端的不同地址数组中。我有一个带有alt_addresses属性的客户端模型,其中包含客户端地址的几种不同可能格式。然后我有一个引用模型(如果您熟悉本地SEO,您就会知道这个术语)。引用模型没有地址字段,但它有一个nap_correct?字段(NAP代表“姓名”、“地址”、“电话号码”)。如果CSV行的名称、地址和电话号码与我在该客户的数据库中拥有的相同,

  9. IDEA使用LeetCode插件 - 2

    前言我们习惯用idea编写、调试代码,在LeetCode上刷题时,如果能够在IDEA编写代码,并且做好代码管理,是一件事半功倍的事情。对于后续复习题目,做笔记也会非常便利。本文目的在于介绍LeetCodeEditor的使用,以及配置工具类,最终目录结构如下:note:放置笔记src:放置代码leetcode.editor.cn:插件LeetCodeEditor自动生成utils:自定义的工具包,可用于自动化输入测试用例,定义题目需要的类(结构体)out:运行测试时自动生成LeetCodeEditorGitHub:https://github.com/shuzijun/leetcode-edit

  10. regex - Ruby 是否有类似于 Perl 6 语法的插件? - 2

    多年来,Perl一直是我首选的编程语言工具之一。Perl6语法看起来像是一个很棒的语言特性。我想知道是否有人开始为Ruby做这样的事情。 最佳答案 如果您想在Ruby中使用实际的Perl6语法,最好的选择是Cardinal,Parrot上的ruby​​编译器。它目前尚未完成并且非常缓慢,但我非常希望它最终成为一个可行的ruby​​实现。它目前大部分处于非事件状态,等待Parrot中的一些基础架构更改以支持改进的解析速度和其他功能。 关于regex-Ruby是否有类似于Perl6语法的插件

随机推荐