Vue集成SpreadJs(Excel在线编辑操作)

场景:最近公司开了个新项目,需要在线做财务损益报表,经调研需要支持如下功能:

  • Excel报表的导入导出
  • 报表的数据存档
  • 在线编辑公式
  • sheet 和单元格的权限控制(与系统权限挂钩)
  • 多人同时编辑
    经过技术选型,目前定位了通过SpreadJs 实现。并与SpreadJs工程师进行了多轮电话沟通(再次感谢Spread工程师的细心回答)。
    注:在今年8月份,spreadJs最新版已经提供了vue组件的支持,可自行选择官方demo,本文中实现的是采用非vue组件集成的方式。

本文初步实现1、2、3的需求,并在文章最后附注4、5的实现思路希望能帮到你们

实现后效果如下

图片.png

实现步骤如下:

1.环境介绍

1.后台:spring boot
2.前台:vue、vue-element、webpack、iview
3.插件:SpreadJs V12

2.SpreadJs插件下载
下载地址:https://www.grapecity.com.cn/download/?pid=57
操作步骤如下图:填写完成后会收到官方发送的邮件

图片.png

下载后目录结构如下:
图片.png

3.插件初始化

  • 首先将下载后的文件中的css和js提取出来放到前端项目的static目录下面,如下图:


    图片.png
  • 其次在项目根路径index.html中全局引入Spread插件的JS和CSS,如下图红色标记的内容


    图片.png

4.代码实现
思路如下:
4.1定义div 承载spreadjs的 canvas 视图
<div ref="excelView" id="excelView" :style="spreadStyle"></div>
4.2在default对象中初始化spread和excelIo对象,并在mounted生命周期函数中初始化,代码结构见图片1:

图片1.png

4.3 定义文件上传组件,可采用ivew的上传组件或定义原生的 <input type="file"/>,本文采用的是原生的html文件上传
注:不要采用vue-element的上传组件,因为获取到的文件对象类型不适配spreadjs 格式要求,请自行console打印查看效果
4.4 构建FormData对象,将文件以及文件名称附加进去,并通过axios上传到后台指定方法中,代码见图二:
图二.png

4.5 编写后台代码接收前台传递的文件,并存档到文件服务器且在关系型数据库中添加标记,记录文件在服务器的路径

整体前端代码如下:

<template xmlns="http://www.w3.org/1999/html" xmlns:v-on="http://www.w3.org/1999/xhtml">
  <div class="app-container pull-auto">
    <basic-container>
      <div class="filter-container crud-menu">
        <div class="crud-menu_left">
          <input type="file" class="form-control" id="upload_file" name="upload_file" accept=".xlsx"
                 style="display: none;" @change='changeExcel'>
          <Input placeholder="请选择文件..." v-model="fileName" id="upload_file_tmp" style="width: 250px"
                 onclick="upload_file.click();" readonly/>
          <Button type="primary" icon="md-arrow-round-up" @click="selectExcel">浏览</Button>
          <Button type="primary" icon="md-arrow-round-down" @click="saveExcel">导出</Button>
          <Button type="primary" icon="ios-egg" @click="saveTemplate">保存模板</Button>
        </div>
      </div>

      <div ref="excelView" id="excelView" :style="spreadStyle"></div>

    </basic-container>
  </div>

</template>
<style scoped>

</style>

<script>
  import axios from '@/router/axios'
  export default {
    data () {
      return {
        fileName: "",
        excelIo: {},
        spread: {},
        spreadStyle: {
          width: '100%',
          height: '430px'
        }
      };
    },
    mounted () {
      this.spread = new GC.Spread.Sheets.Workbook(document.getElementById("excelView"), {sheetCount: 1});
      this.excelIo = new GC.Spread.Excel.IO();
    },
    methods: {
      //上传EXCEL
      upExcel(){
        var excelFile = document.getElementById("upload_file").files[0];
        this.excelIo.open(excelFile, (json) => {
          var workbookObj = json;
          this.spread.fromJSON(workbookObj);
        }, function (e) {
          alert('文件读取失败,仅支持xlsx类型');
        });

      },
      //选择文件
      selectExcel(){
        upload_file.click();
      },
      changeExcel(){
        var excelFile = document.getElementById("upload_file").files[0];
        this.fileName = document.getElementById("upload_file").files[0].name;
        this.upExcel();
      },
      //保存EXCEL
      saveExcel(){
        //生成时间戳
        var fileName = new Date().getTime() + '.xlsx';
        var json = this.spread.toJSON();
        this.excelIo.save(json, (blob) => {
          saveAs(blob, fileName);
        }, function (e) {
          console.log(e);
        });
      },
      //保存模板
      saveTemplate(){
        //生成时间戳
        var fileName = new Date().getTime() + '.json';
        var json = this.spread.toJSON();
        var jsontext = JSON.stringify(json);
        //前台保存
        saveAs(new Blob([jsontext], {type: "text/plain;charset=utf-8"}), fileName);
        //保存后台
        let formData = new FormData();
        var thefile = document.getElementById("upload_file").files[0];
        formData.append("file", thefile);
        formData.append("fileName", thefile.name);
        axios.post('/admin/excelup/doImport', formData).then(function (response) {
          alert("模板保存成功");
        }).catch(function (error) {
          alert("上传失败");
          console.log(error);
        });

      }
    }
  }
</script>

最后为以下两个需求提供下解决思路:

  • sheet 和单元格的权限控制(与系统权限挂钩)
    spreadjs支持sheet的隐藏与展示功能,通过前台获取后台传递的权限表示,在spreadjs 对象初始化的时候进行 api调用控制。
  • 多人同时编辑
    首先,spreadjs是不提供此类功能,需要自己定制化开发,实现思路是
    多人同时编辑时,根据spreadjs 提供的事件或脏数据功能与后台进行接口交互,然后通过websocket通信将数据同步到其他端。

推荐阅读更多精彩内容

  • 因为要结局swift3.0中引用snapKit的问题,看到一篇介绍Xcode8,swift3变化的文章,觉得很详细...
    uniapp阅读 2,979评论 1 8
  • 无辜的“开车”人:那天车上放着鹏泊的啷个里个啷这首歌.....不自觉的车速就上了七十,结果警车就跟上来了,我心想,...
    茱枽阅读 99评论 4 7
  • 如今网络云集的时代里,店铺淘客在人们视野中越来越常见,但最近两年由于各种规则的出台限制了店铺淘客的发展,但...
    小凯铭阅读 221评论 0 0
  • 飞翔,飞翔, 我愿和你一起飞翔。 飞过那辛酸苦辣, 飞到那梦想的天堂。 飞过那春夏秋冬, 飞到那幸福的故乡。 飞过...
    郜春雷阅读 44评论 0 0
  • 运营学院收获以及感受 一,针对社群的大伙 从进群到现在,这几天,我的感受:大家都很厉害,行动力十足,大家互相帮助,...
    赖宇航阅读 64评论 0 0