乐趣区

关于前端:Vuex与前端表格施展组合拳实现大屏展示应用的交互增强

Vuex 是一个专为 Vue.js 利用程序开发的状态管理模式。它采纳集中式存储管理利用的所有组件的状态,并以相应的规定保障状态以一种可预测的形式发生变化。

下图是一个产品开发中十分常见的大屏展现界面示例。
通过 Vue 提供的 Vuex,上方三个仪表板以及下方的表格组件共享同一个数据源,曾经实现了数据扭转后同步响应更新。

“很棒的大屏展现性能,能反对 Excel 数据的导入导出吗,表格数据能够实时编辑更新吗?”

如果你曾经开发软件很长时间,可能不止一次地从最终客户或者产品经理那里听到过这个灵魂拷问。对于非技术人群来说,感觉要求 Excel 导入 / 导出 / 展现是一个十分失常且容易实现的需要。

但实际上,这个问题经常让前端开发人员感到恐惧。解决 Excel 文件须要大量工作。
这个问题通过前端表格能够变得简略,将电子表格嵌入 Web 应用程序。同时和其余的组件进行交互。这篇博客将钻研如何应用现有的这个大屏展现 Vue 利用作为根底,应用前端电子表格对其进行加强。

本文假设你曾经理解 HTML、CSS 和 JavaScript。以及 Vue 的根底利用。如果你有应用过 Vuex,当然会更容易了解,如果还没有,也不必放心。VueX 在这个我的项目中的利用很简略。

对于 VueX,能够在 Vue 官网理解更多信息

本文将分为上面的几个局部

  • Vuex 的原始利用
  • 给利用增加实时编辑性能
  • 增加 Excel 数据导入性能
  • 增加导出为 Excel 性能

蕴含 Vuex 的原始利用
如上图看到的,将要应用的 Vue 应用程序是一个简略的大屏展现界面,带有几个汇总信息仪表板和一个数据表。

能够通过上面的附件获取这个 Vue 利用我的项目代码,而后运行“npm install”以及“npm run serve”即可启动利用。
附件下载地址:
https://gcdn.grapecity.com.cn…

原始的 Vue 利用代码构造如下:

  • Vuex 和 Vue 应用程序都定义在 main.js 中。
  • 有几个单文件 Vue 组件,位于该 components 文件夹中。

Vuex store 代码如下,初始状态只有一个设置为 recentSales 的值,示意近期销售记录:

const store = new Vuex.Store({  
state: {recentSales}  
});

通过 recentSales 这一个数据,如何生成三个统计表和一个表格?关上 Dashboard.vue 组件。在其中,能够看到基于 Vuex 存储中的数据生成了几个计算属性:

<template>
  <div style="background-color: #ddd">
    <NavBar title="销售仪表板"/>
    <div class="container">
      <div class="row">
        <TotalSales :total="totalSales"/>
        <SalesByCountry :salesData="countrySales"/>
        <SalesByPerson :salesData="personSales"/>
        <SalesTableBySpreadjs :tableData="salesTableData"/>
        <SalesTable :tableData="salesTableData"/>
        
      </div>
    </div>
  </div>
</template>

<script>
import NavBar from "./NavBar";
import TotalSales from "./TotalSales";
import SalesByCountry from "./SalesByCountry";
import SalesByPerson from "./SalesByPerson";
import SalesTable from "./SalesTable";
import SalesTableBySpreadjs from "./SalesTableBySpreadjs";
import {groupBySum} from "../util/util";

export default {components: { NavBar, SalesByCountry, SalesByPerson, SalesTable, TotalSales ,SalesTableBySpreadjs},
  computed: {totalSales() {
      const total = this.$store.state.recentSales.reduce((acc, sale) => (acc += sale.value),
        0
      );
      return parseInt(total);
    },
    countrySales() {
      const items = this.$store.state.recentSales;
      const groups = groupBySum(items, "country", "value");
      return groups;
    },
    personSales() {
      const items = this.$store.state.recentSales;
      const groups = groupBySum(items, "soldBy", "value");
      return groups;
    },
    salesTableData() {return this.$store.state.recentSales;}
  }
};
</script>

因而 recentSales 这个单个数据集目前能为这个大屏展现的几个仪表板和表格提供统一数据。因为数据位于 Vuex store 中,那么如果数据更新,所有仪表板面板都会自动更新。
当咱们用能够编辑的电子表格替换现有的表格来进行编辑时,这种个性将派上用场。

将前端电子表格增加到您的 Vue 应用程序

咱们要用前端电子表格替换这个 html 表格,在 component 文件夹新建一个 vue 文件,命名为 SalesTableBySpreadjs.vue,而后在其中增加一个 template:

<template>
  <TablePanel title="近期销售额">
    <gc-spread-sheets
      :hostClass="hostClass"
      @workbookInitialized="workbookInit"
      style="height: 300px"
    >
      <gc-worksheet
        :dataSource="tableData"
        :autoGenerateColumns="autoGenerateColumns"
      >
        <gc-column
          :width="50"
          :dataField="'id'"
          :headerText="'ID'"
          :visible="visible"
          :resizable="resizable"
        >
        </gc-column>
        <gc-column
          :width="300"
          :dataField="'client'"
          :headerText="'Client'"
          :visible="visible"
          :resizable="resizable"
        >
        </gc-column>
        <gc-column
          :width="350"
          :headerText="'Description'"
          :dataField="'description'"
          :visible="visible"
          :resizable="resizable"
        >
        </gc-column>
        <gc-column
          :width="100"
          :dataField="'value'"
          :headerText="'Value'"
          :visible="visible"
          :formatter="priceFormatter"
          :resizable="resizable"
        >
        </gc-column>
        <gc-column
          :width="100"
          :dataField="'itemCount'"
          :headerText="'Quantity'"
          :visible="visible"
          :resizable="resizable"
        >
        </gc-column>
        <gc-column
          :width="100"
          :dataField="'soldBy'"
          :headerText="'Sold By'"
          :visible="visible"
          :resizable="resizable"
        ></gc-column>
        <gc-column
          :width="100"
          :dataField="'country'"
          :headerText="'Country'"
          :visible="visible"
          :resizable="resizable"
        ></gc-column>
      </gc-worksheet>
    </gc-spread-sheets>
  </TablePanel>
</template>

其中,gc-spread-sheets 元素创立了一个电子表格并定义了如何显示数据列。gc-column 中的 dataField 属性通知该列应该显示底层数据集的哪个属性。

接下来是 js 局部:

import "@grapecity/spread-sheets/styles/gc.spread.sheets.excel2016colorful.css";

_// SpreadJS imports_  
import "@grapecity/spread-sheets-vue";  
import Excel from "@grapecity/spread-excelio";

import TablePanel from "./TablePanel";  
export default {components: { TablePanel},  
  props: ["tableData"],  
  data(){  
      return {  
        hostClass:'spreadsheet',  
        autoGenerateColumns:true,  
        width:200,  
        visible:true,  
        resizable:true,  
        priceFormatter:"$ #.00"  
      }  
    },  
  methods: {workbookInit: function(_spread_) {this._spread = spread;}  
    }  
};

只需很少的代码即可实现。其中的几个数据属性和办法,是绑定到纯前端电子表格组件的配置选项,workbookInit 办法是 SpreadJS 在初始化工作表时调用的回调。

回到 Dashboard.vue 文件,退出刚刚创立的 SalesTableBySpreadjs 组件。
而后从新运行,即可显示电子表格数据:

<template>
  <div style="background-color: #ddd">
    <NavBar title="销售仪表板"/>
    <div class="container">
      <div class="row">
        <TotalSales :total="totalSales"/>
        <SalesByCountry :salesData="countrySales"/>
        <SalesByPerson :salesData="personSales"/>
        <SalesTableBySpreadjs :tableData="salesTableData"/>
<SalesTable :tableData="salesTableData"/>

        
      </div>
    </div>
  </div>
</template>

<script>
import NavBar from "./NavBar";
import TotalSales from "./TotalSales";
import SalesByCountry from "./SalesByCountry";
import SalesByPerson from "./SalesByPerson";
import SalesTable from "./SalesTable";
import SalesTableBySpreadjs from "./SalesTableBySpreadjs";
import {groupBySum} from "../util/util";

export default {components: { NavBar, SalesByCountry, SalesByPerson, SalesTable, TotalSales ,SalesTableBySpreadjs},
  computed: {totalSales() {
      const total = this.$store.state.recentSales.reduce((acc, sale) => (acc += sale.value),
        0
      );
      return parseInt(total);
    },
    countrySales() {
      const items = this.$store.state.recentSales;
      const groups = groupBySum(items, "country", "value");
      return groups;
    },
    personSales() {
      const items = this.$store.state.recentSales;
      const groups = groupBySum(items, "soldBy", "value");
      return groups;
    },
    salesTableData() {return this.$store.state.recentSales;}
  }
};
</script>

当初咱们曾经用一个残缺的电子表格替换了原来的 html table,接下来能够对电子表格中的金额列中显示的金额进行编辑。比方将第 6 行的金额从 35,000 美元更改为 3500 美元,能够看到下面三个仪表板显示的内容同时也进行了更新。

起因是 SpreadJS 被编辑后同步更新了它的数据源 =>VUEX store 中的 recentSales。

到这里咱们曾经有了一个能够随着数据变动而实时更新的增强型仪表板。下一步咱们能够通过导出导入 Excel 数据的性能来做进一步加强。

导出为 Excel 文件

将 Excel 导出性能增加到工作表很容易。首先,在仪表板中增加一个导出按钮。把它放在表格面板的底部,在 gc-spread-sheets 完结标记之后:

 </gc-spread-sheets>  
    <div class="row my-3">
      <div class="col-sm-4">
        <button class="btn btn-primary mr-3" @click="exportSheet">
          导出文件
        </button>
      </div>
    </div>
  </TablePanel>  
</template>

接下来增加点击时触发的 exportSheet 办法,从名为 file-saver 的 NPM 包中导入一个函数:

import {saveAs} from 'file-saver';

而后将 exportSheet 增加到组件的办法对象中:

exportSheet: function () {
      const spread = this._spread;
      const fileName = "SalesData.xlsx";
      //const sheet = spread.getSheet(0);
      const excelIO = new IO();
      const json = JSON.stringify(
        spread.toJSON({
          includeBindingSource: true,
          columnHeadersAsFrozenRows: true,
        })
      );
      excelIO.save(
        json,
        function (blob) {saveAs(blob, fileName);
        },
        function (e) {console.log(e);
        }
      );
    },

运行测试点击按钮,即可间接获取到导出的 excel 文件。
须要留神的是,咱们设置了两个序列化选项:includeBindingSource 和 columnHeadersAsFrozenRows。以确保绑定到工作表的数据被正确导出,且工作表蕴含列题目,。

Excel 数据导入

在 template 中,增加以下代码增加一个 file 类型的 input 用于导入文件:

<div class="col-sm-8">
        <button class="btn btn-primary float-end mx-2"> 导入文件 </button>
        <input
          type="file"
          class="fileSelect float-end mt-1"
          @change="fileChange($event)"
        />
</div>

而后将 fileChange 办法增加到组件的 method 对象中:

fileChange: function (e) {if (this._spread) {
        const fileDom = e.target || e.srcElement;
        const excelIO = new IO();
        //const spread = this._spread;
        const store = this.$store;

        excelIO.open(fileDom.files[0], (data) => {const newSalesData = extractSheetData(data);

          store.commit("updateRecentSales", newSalesData);
        });
      }
    },

抉择文件后,应用 SpreadJS 中的 ExcelIO 导入它。获取其中的 json 数据。传入自定义的函数 extractSheetData,从中提取须要的数据,而后将其提交回 Vuex store, 来更新 recentSales 数据。

extractSheetData 函数能够在 src/util.util.js 文件中找到。extractSheetData 函数假设导入工作表中的数据与原始数据集具备雷同的列。如果有人上传的电子表格不合乎此要求,将无奈解析。这个应该是大多数客户能够承受的限度。数据不符时,也能够尝试给客户一个提示信息。

另外,还须要在 main.js 中为 Vuex store 增加 updateRecentSales 来更新数据,
批改后的 store 如下:

const store = new Vuex.Store({
  state: {recentSales},
  mutations: {updateRecentSales (state,param) {
      
      let sales=state.recentSales;
      let arr=sales.map(function(o){return o.id});
      param.forEach((newsale)=>{if(arr.indexOf(newsale.id)>0){console.log("update");
            state.recentSales[arr.indexOf(newsale.id)]=newsale;
          }
          else{console.log("add");
            state.recentSales.push(newsale);
          }
      });
      console.log(state.recentSales);
    }
  },
  actions: {updateRecentSales ({commit},param) {commit('updateRecentSales',param)
    }
  }
});

能够看到,Vuex store 调用 commit 后,会触发 updateRecentSales 办法对 recentSales 进行更新,id 雷同时进行更新,有新的 id 时进行新增。
最初,SpreadJS 工作表和所有仪表板面板都会同步更新以反映新数据。

最终的我的项目下载地址:
https://gcdn.grapecity.com.cn…

Vue、Vuex 和 SpreadJS 的配合应用让这个利用的加强开发变的十分不便。借助 Vue 的模板和数据绑定、Vuex 的治理共享状态,响应式数据存储和纯前端的交互式电子表格,能够在很短内创立简单的企业 JavaScript 应用程序。

大家如果感兴趣能够拜访更多在线实例:
https://demo.grapecity.com.cn…

退出移动版