前端导入预览,导出表格(不依赖后端)

165 阅读1分钟

在实际开发场景中,有些表格只单单依靠前端就可以实现表格导入导出的功能,以下是案例详情:

须注意事项!!!!

  • 前端实现导出表格只针对没有分页的表格,只能导出页面展示的表格(由于仅靠前端完成功能,没有调用接口,因此数据库的自然无法导出)
  • 此次案例的导出功能只针对表格样式较为简单的表格(合并表格的还没做好)
  • 只针对xlsx、xls格式的文件
  • 使用vue2+element

功能动图预览

导入导出表格gif.gif

导入导出功能

页面部分

  • 使用了elelment的上传组件(模板部分)
   <el-upload
        class="upload-demo"
        action=""
        ref="upload"
        :on-change="handleChange"
        :file-list="fileListUpload"
        :limit="1"
        accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
        :auto-upload="false"
      >
        <el-button size="small" type="primary">导入表格预览</el-button>
        <div slot="tip" class="el-upload__tip">
          只 能 上 传 xlsx / xls 文 件
        </div>
      </el-upload>
      <el-button size="small" type="primary" @click="doExport"
          >导出</el-button
        >
      <div id="tableHtml"></div>

  • js部分使用了
  • xlsx插件,

npm i xlsx -D 或者 yarn add xlsx -D

  • xlsxFile方法,js方法
<script>
import download from '@/utils/xlsxFile' 
export default {
  name: "LabProjectIndex",
  data() {
    return {
      outdata:null,
      fileListUpload: [],
      outdataExport:[],
      fileTemp:{}
    };
  },

  mounted() {},

  methods: {
    doExport(e) {
    
      download(this.outdataExport,'测试数据.xlsx')//导出的文件名
    
    },
    handleChange(file, fileList) {
      this.fileTemp = file.raw;
      if (this.fileTemp) {
        if (
          this.fileTemp.type ==
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
          this.fileTemp.type == "application/vnd.ms-excel"
        ) {
          this.importfxx(this.fileTemp);
          this.$refs.upload.clearFiles();
        } else {
          this.$message({
            type: "warning",
            message: "附件格式错误,请删除后重新上传!",
          });
        }
      } else {
        this.$message({
          type: "warning",
          message: "请上传附件!",
        });
      }
    },

    importfxx(obj) {
      let _this = this;
      // 通过DOM取文件数据
      this.file = obj;
      let rABS = false; //是否将文件读取为二进制字符串
      let f = this.file;
      let reader = new FileReader();
      //if (!FileReader.prototype.readAsBinaryString) {
      FileReader.prototype.readAsBinaryString = function (f) {
        let binary = "";
        let rABS = false; //是否将文件读取为二进制字符串
        let wb; //读取完成的数据
        let reader = new FileReader();
        reader.onload = function (e) {
          let bytes = new Uint8Array(reader.result);
          let length = bytes.byteLength;
          for (let i = 0; i < length; i++) {
            binary += String.fromCharCode(bytes[i]);
          }
          console.log(binary, "binarybinary");
          let XLSX = require("xlsx"); //使用了xlsx插件,需要yarn add xlsx 或者 npm i xlsx
          if (rABS) {
            wb = XLSX.read(btoa(fixdata(binary)), {
              //手动转化
              type: "base64",
            });
          } else {
            wb = XLSX.read(binary, {
              type: "binary",
            });
          }

          // this.outdata = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]); //转换为数据(数组对象)
          // 将读取到的数据转换成html
          this.outdata = XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]); //转换为html
          console.log( this.outdataExport)
          document.querySelector("#tableHtml").innerHTML = this.outdata;

          _this.outdataExport = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]); //转换数据
          // this.tableToHtml(XLSX.utils.sheet_to_html(wb.Sheets[wb.SheetNames[0]]))
          // console.log(this.outdata, "outdataoutdata");
        };
        reader.readAsArrayBuffer(f);
      };

      if (rABS) {
        reader.readAsArrayBuffer(f);
      } else {
        reader.readAsBinaryString(f);
      }
    },
  },
};
</script>

xlsxFile.js


import * as xlsx from 'xlsx'


export default function download(json,fileName){
   const type = 'xlsx'//定义导出文件的格式
   var tmpDown;//导出的内容
   var tmpdata = json[0];
   json.unshift({});
   var keyMap = []; //获取keys
   for (var k in tmpdata) {
       keyMap.push(k);
       json[0][k] = k;
   }
   var tmpdata = [];//用来保存转换好的json 
   
   json.map((v, i) => keyMap.map((k, j) => Object.assign({}, {
       v: v[k],
       position: (j > 25 ? getCharCol(j) : String.fromCharCode(65 + j)) + (i + 1)
   }))).reduce((prev, next) => prev.concat(next)).forEach((v, i) => tmpdata[v.position] = {
       v: v.v
   });
   var outputPos = Object.keys(tmpdata); //设置区域,比如表格从A1到D10
   var tmpWB = {
       SheetNames: ['mySheet'], //保存的表标题
       Sheets: {
           'mySheet': Object.assign({},
               tmpdata, //内容
               {
                   '!ref': outputPos[0] + ':' + outputPos[outputPos.length - 1] //设置填充区域
               })
       }
   };
   tmpDown = new Blob([s2ab(xlsx.write(tmpWB, 
       {bookType: (type == undefined ? 'xlsx':type),bookSST: false, type: 'binary'}//这里的数据是用来定义导出的格式类型
       ))], {
       type: ""
   }); //创建二进制对象写入转换好的字节流
   saveAs(tmpDown,fileName);
}

function saveAs(obj, fileName){//导出功能实现
   var tmpa = document.createElement("a");
   tmpa.download = fileName || "下载";
   tmpa.href = URL.createObjectURL(obj); //绑定a标签
   tmpa.click(); //模拟点击实现下载
   setTimeout(function () { //延时释放
       URL.revokeObjectURL(obj); //用URL.revokeObjectURL()来释放这个object URL
   }, 100);
}

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

function getCharCol(n){
   let temCol = '',
   s = '',
   m = 0
   while (n > 0) {
       m = n % 26 + 1
       s = String.fromCharCode(m + 64) + s
       n = (n - m) / 26
   }
   return s
}

导入部分功能实现,将xlsx的方法读取导入文件的数据,然后将其转换为html,然后将其赋值给dom的innerHTML:

document.querySelector("#tableHtml").innerHTML = this.outdata;

导出部分功能实现,在导入表格时,将导入的表格读取转换为数据,赋值给全局变量,然后再点击导出时调用xlsxFile方法

document.querySelector("#tableHtml").innerHTML = this.outdata;

开发中遇到的问题

1.element的upload上传组件在上传文件后需要将文件清除,否则无法重新上传 this.$refs.upload.clearFiles();

2.在使用XLSX.utils.sheet_to_htmlXLSX.utils.sheet_to_json方法将转换后的数据或者dom赋值给全局变量时,由于是在render.onload方法中进行的,因此就引发了this指向的问题,所以需要在外不定义this,以便在render.onload方法中赋值let _this = this;

3.在引入xlsx插件的时候,需要import * as xlsx from 'xlsx' 重命名