[xlsx-chart] Export the Excel table and bring the chart in the Vue project

Export the Excel table in the Vue project and bring the chart

1. Demand scenario:

We will encounter some requirements during the development process, which is to export the data in the table to an excel file and download it to the local, but basically the downloaded excel file currently has only simple table data and no related charts (such as pie charts, histograms, etc.). When we bring these charts in our excel file, it can help users see the relationship between the data more concisely;

2. Use tools:

The xslx-chart plug-in, since xslx-chart is a plug-in in a node environment, we can use it in two ways. The following will introduce how to use xslx-cahrt (emphasis on method 2)

3. Start using:

Method 1 : Use the xslx-chart tool based on the node environment;
Note: Since xslx-chart runs in the nodejs environment, we need to run a node service locally in the project to use xslx-cahrt. Ideas: 1. While running the project, we run a nodejs service (xlsx-
chart
-serve) locally, and process the table operations in the nodejs service. After processing, the file is sent to the front end, and the front end downloads the file;

As shown in the figure: we execute directly when we run the project
insert image description here

npm run dev

It will execute serve and xlsx at the same time; the function of xlsx is to run the service of exporting the form;
note:
executing multiple commands at the same time requires the tool concurrently
, using && to connect the two commands is serial, which will interrupt the execution of the command 2. After our service is running, when the front end needs to export the table, the data will be sent to the nodejs service, which will be processed by the xlsx service. After processing, the file will be sent to the front end
.

Method 2 (recommended) : carry out secondary development based on xlsx-chart, and use it directly in the front-end.
The idea:
we can see the source code of the xlsx-chart tool. It is not difficult to find that the chart it generates is to read the template file locally and then replace the data in the template table with the latest data to generate a new table and new chart. It uses the fs tool to read files in the node environment. Then we can use axios on the front-end to request local files. As long as the content and type of the read file are consistent, the key point: the response data type in the axios request needs to be configured; Set responseType to
' arraybuffer '

insert image description here
Then the latest generated file can be downloaded using file-saver.
insert image description here
The modified source code file is as follows:

Step 1: Prepare source code:
base.js

var _ = require("underscore");
var Backbone = require("backbone");
var JSZip = require("jszip");
var xml2js = require("xml2js");
var VError = require("verror");
var axios = require('axios');
// var fs = require ("fs");
var async = require("async");
var Chart = Backbone.Model.extend({
    
    
  /*
    Read XML file from xlsx as object
  */
  read: function (opts, cb) {
    
    
    var me = this;
    var t = me.zip.file(opts.file).asText();
    var parser = new xml2js.Parser({
    
     explicitArray: false });
    parser.parseString(t, function (err, o) {
    
    
      if (err) {
    
    
        return new VError(err, "getXML");
      }
      cb(err, o);
    });
  },
  /*
    Build XML from object and write to zip
  */
  write: function (opts) {
    
    
    var me = this;
    var builder = new xml2js.Builder();
    var xml = builder.buildObject(opts.object);
    me.zip.file(opts.file, new Buffer(xml), {
    
     base64: true });
  },
  /*
    Get column name
  */
  getColName: function (n) {
    
    
    var abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    n--;
    if (n < 26) {
    
    
      return abc[n];
    } else {
    
    
      return abc[(n / 26 - 1) | 0] + abc[n % 26];
    }
  },
  /*
    Get shared string index
  */
  getStr: function (s) {
    
    
    var me = this;
    if (!me.str.hasOwnProperty(s)) {
    
    
      throw new VError("getStr: Unknown string: " + s);
    }
    return me.str[s];
  },
  /*
    Write table
  */
  writeTable: function (cb) {
    
    
    var me = this;
    me.read({
    
     file: "xl/worksheets/sheet2.xml" }, function (err, o) {
    
    
      if (err) {
    
    
        return cb(new VError(err, "writeTable"));
      }
      o.worksheet.dimension.$.ref = "A1:" + me.getColName(me.titles.length + 1) + (me.fields.length + 1);
      var rows = [{
    
    
        $: {
    
    
          r: 1,
          spans: "1:" + (me.titles.length + 1)
        },
        c: _.map(me.titles, function (t, x) {
    
    
          return {
    
    
            $: {
    
    
              r: me.getColName(x + 2) + 1,
              t: "s"
            },
            v: me.getStr(t)
          }
        })
      }];
      _.each(me.fields, function (f, y) {
    
    
        var r = {
    
    
          $: {
    
    
            r: y + 2,
            spans: "1:" + (me.titles.length + 1)
          }
        };
        var c = [{
    
    
          $: {
    
    
            r: "A" + (y + 2),
            t: "s"
          },
          v: me.getStr(f)
        }];
        _.each(me.titles, function (t, x) {
    
    
          c.push({
    
    
            $: {
    
    
              r: me.getColName(x + 2) + (y + 2)
            },
            v: me.data[t][f]
          });
        });
        r.c = c;
        rows.push(r);
      });
      // table的數據
      o.worksheet.sheetData.row = rows;
      console.log('o',o,rows)
      me.write({
    
     file: "xl/worksheets/sheet2.xml", object: o });
      cb();
    });
  },
  /*
    Write strings
  */
  writeStrings: function (cb) {
    
    
    var me = this;
    me.read({
    
     file: "xl/sharedStrings.xml" }, function (err, o) {
    
    
      if (err) {
    
    
        return cb(new VError(err, "writeStrings"));
      }
      o.sst.$.count = me.titles.length + me.fields.length;
      o.sst.$.uniqueCount = o.sst.$.count;
      var si = [];
      _.each(me.titles, function (t) {
    
    
        si.push({
    
     t: t });
      });
      _.each(me.fields, function (t) {
    
    
        si.push({
    
     t: t });
      });
      me.str = {
    
    };
      _.each(si, function (o, i) {
    
    
        me.str[o.t] = i;
      });
      o.sst.si = si;
      me.write({
    
     file: "xl/sharedStrings.xml", object: o });
      cb();
    });
  },
  /*
    Remove unused charts
  */
  removeUnusedCharts: function (o) {
    
    
    var me = this;
    if (me.tplName != "charts") {
    
    
      return;
    };
    var axId = [];
    function addId (o) {
    
    
      _.each(o["c:axId"], function (o) {
    
    
        axId.push(o.$.val);
      });
    };
    _.each(["line", "radar", "area", "scatter", "pie"], function (chart) {
    
    
      if (!me.charts[chart]) {
    
    
        delete o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:" + chart + "Chart"];
      } else {
    
    
        addId(o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:" + chart + "Chart"]);
      };
    });
    if (!me.charts["column"] && !me.charts["bar"]) {
    
    
      delete o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"];
    } else
      if (me.charts["column"] && !me.charts["bar"]) {
    
    
        o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"] = o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][0];
        addId(o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"]);
      } else
        if (!me.charts["column"] && me.charts["bar"]) {
    
    
          o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"] = o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][1];
          addId(o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"]);
        } else {
    
    
          addId(o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][0]);
          addId(o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][1]);
        };

    var catAx = [];
    _.each(o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:catAx"], function (o) {
    
    
      if (axId.indexOf(o["c:axId"].$.val) > -1) {
    
    
        catAx.push(o);
      };
    });
    o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:catAx"] = catAx;

    var valAx = [];
    _.each(o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:valAx"], function (o) {
    
    
      if (axId.indexOf(o["c:axId"].$.val) > -1) {
    
    
        valAx.push(o);
      };
    });
    o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:valAx"] = valAx;
  },
  /*
    Write chart
  */
  writeChart: function (cb) {
    
    
    var me = this;
    var chart;
    me.read({
    
     file: "xl/charts/chart1.xml" }, function (err, o) {
    
    
      if (err) {
    
    
        return cb(new VError(err, "writeChart"));
      }
      var ser = {
    
    };
      _.each(me.titles, function (t, i) {
    
    
        var chart = me.data[t].chart || me.chart;
        var r = {
    
    
          "c:idx": {
    
    
            $: {
    
    
              val: i
            }
          },
          "c:order": {
    
    
            $: {
    
    
              val: i
            }
          },
          "c:tx": {
    
    
            "c:strRef": {
    
    
              "c:f": "Table!$" + me.getColName(i + 2) + "$1",
              "c:strCache": {
    
    
                "c:ptCount": {
    
    
                  $: {
    
    
                    val: 1
                  }
                },
                "c:pt": {
    
    
                  $: {
    
    
                    idx: 0
                  },
                  "c:v": t
                }
              }
            }
          },
          "c:cat": {
    
    
            "c:strRef": {
    
    
              "c:f": "Table!$A$2:$A$" + (me.fields.length + 1),
              "c:strCache": {
    
    
                "c:ptCount": {
    
    
                  $: {
    
    
                    val: me.fields.length
                  }
                },
                "c:pt": _.map(me.fields, function (f, j) {
    
    
                  return {
    
    
                    $: {
    
    
                      idx: j
                    },
                    "c:v": f
                  };
                })
              }
            }
          },
          "c:val": {
    
    
            "c:numRef": {
    
    
              "c:f": "Table!$" + me.getColName(i + 2) + "$2:$" + me.getColName(i + 2) + "$" + (me.fields.length + 1),
              "c:numCache": {
    
    
                "c:formatCode": "General",
                "c:ptCount": {
    
    
                  $: {
    
    
                    val: me.fields.length
                  }
                },
                "c:pt": _.map(me.fields, function (f, j) {
    
    
                  return {
    
    
                    $: {
    
    
                      idx: j
                    },
                    "c:v": me.data[t][f]
                  };
                })
              }
            }
          }
        };
        if (chart == "scatter") {
    
    
          r["c:xVal"] = r["c:cat"];
          delete r["c:cat"];
          r["c:yVal"] = r["c:val"];
          delete r["c:val"];
          r["c:spPr"] = {
    
    
            "a:ln": {
    
    
              $: {
    
    
                w: 28575
              },
              "a:noFill": ""
            }
          };
        };
        ser[chart] = ser[chart] || [];
        ser[chart].push(r);
      });
      /*
            var tag = chart == "column" ? "bar" : chart;
            o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][0]["c:ser"] = ser;
      */
      _.each(ser, function (ser, chart) {
    
    
        if (chart == "column") {
    
    
          if (me.tplName == "charts") {
    
    
            o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][0]["c:ser"] = ser;
          } else {
    
    
            o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"]["c:ser"] = ser;
          };
        } else
          if (chart == "bar") {
    
    
            if (me.tplName == "charts") {
    
    
              o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][1]["c:ser"] = ser;
            } else {
    
    
              o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"]["c:ser"] = ser;
            };
          } else {
    
    
            o["c:chartSpace"]["c:chart"]["c:plotArea"]["c:" + chart + "Chart"]["c:ser"] = ser;
          };
      });
      me.removeUnusedCharts(o);
      /*
            if (me.showVal) {
              o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:" + tag + "Chart"]["c:dLbls"]["c:showVal"] = {
                $: {
                  val: "1"
                }
              };
            };
      */
      if (me.chartTitle) {
    
    
        me.writeTitle(o, me.chartTitle);
      };
      /*
            o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"] = o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][0];
            o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:catAx"] = o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:catAx"][0];
            o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:valAx"] = o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:valAx"][0];
            delete o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:lineChart"];
            delete o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:areaChart"];
      */
      // me.write({ file: "xl/worksheets/sheet2.xml/", object: o });
      me.write({
    
     file: "xl/charts/chart1.xml", object: o });
      cb();
    });
  },
  /*
    Chart title
  */
  writeTitle: function (chart, title) {
    
    
    var me = this;
    chart["c:chartSpace"]["c:chart"]["c:title"] = {
    
    
      "c:tx": {
    
    
        "c:rich": {
    
    
          "a:bodyPr": {
    
    },
          "a:lstStyle": {
    
    },
          "a:p": {
    
    
            "a:pPr": {
    
    
              "a:defRPr": {
    
    }
            },
            "a:r": {
    
    
              "a:rPr": {
    
    
                $: {
    
    
                  lang: "ru-RU"
                }
              },
              "a:t": title
            }
          }
        }
      },
      "c:layout": {
    
    },
      "c:overlay": {
    
    
        $: {
    
    
          val: "0"
        }
      }
    };
    chart["c:chartSpace"]["c:chart"]["c:autoTitleDeleted"] = {
    
    
      $: {
    
    
        val: "0"
      }
    };
  },
  /*
    Set template name
  */
  setTemplateName: function () {
    
    
    var me = this;
    var charts = {
    
    };
    _.each(me.data, function (o) {
    
    
      charts[o.chart || me.chart] = true;
    });
    me.charts = charts;
    if (charts["radar"]) {
    
    
      me.tplName = "radar";
      return;
    };
    if (charts["scatter"]) {
    
    
      me.tplName = "scatter";
      return;
    };
    if (charts["pie"]) {
    
    
      me.tplName = "pie";
      return;
    };
    if (_.keys(charts).length == 1) {
    
    
      me.tplName = _.keys(charts)[0];
      return;
    };
    me.tplName = "charts";
  },
  /*
    Generate XLSX with chart
    chart: column, bar, line, radar, area, scatter, pie
    titles: []
    fields: []
    data: {title: {field: value, ...}, ...}
  */
  generate: function (opts, cb) {
    
    
    var me = this;
    opts.type = opts.type || "nodebuffer";
    _.extend(me, opts);
    async.series([
      function (cb) {
    
    
        me.zip = new JSZip();
        me.setTemplateName();
        let path = me.templatePath ? me.templatePath : (__dirname + "template/" + me.tplName + ".xlsx");
        console.log("path:", path);
        axios({
    
    
          url: path, //your url
          method: 'GET',
          responseType: 'arraybuffer', // important
        }).then((response) => {
    
    
          console.log('response',response)
          me.zip.load(response.data);
          cb();
        });
		//也可以使用fetch代替axios            
        //window.fetch(path).then((response) => {
    
    
        //  response.arrayBuffer().then(function (buffer) {
    
    
        //    // do something with buffer
        //    console.log('response.arraybuffer()', buffer)
        //    me.zip.load(buffer);
        //    cb();
        //  })
        //});
        // fs.readFile(path, function (err, data) {
    
    
        //  if (err) {
    
    
        //    console.error(`Template ${path} not read: ${err}`);
        //    return cb (err);
        //  };
        //  me.zip.load (data);
        //  cb ();
        // });
        // fs.readFile(path, function (err, data) {
    
    
        //  if (err) {
    
    
        //    console.error(`Template ${path} not read: ${err}`);
        //    return cb (err);
        //  };
        //  me.zip.load (data);
        //  cb ();
        // });
      },
      function (cb) {
    
    
        me.writeStrings(cb);
      },
      function (cb) {
    
    
        _.each(me.titles, function (t) {
    
    
          me.data[t] = me.data[t] || {
    
    };
          _.each(me.fields, function (f) {
    
    
            me.data[t][f] = me.data[t][f] || 0;
          });
        });
        me.writeTable(cb);
      },
      function (cb) {
    
    
        me.writeChart(cb);
      }
    ], function (err) {
    
    
      if (err) {
    
    
        return cb(new VError(err, "build"));
      }
      var result = me.zip.generate({
    
     type: me.type });
      cb(null, result);
    });
  }
});
module.exports = Chart;

Note: Since we sent the axios request to modify the responseType: 'arraybuffer', we need to ensure that this configuration is successful. If the mock is used in the project and referenced, we must pay attention, because the mock will set the responseType of the global axios to ' '; As a result,
we cannot get the template file content of excel when we send the request, so we need to ensure that the mock is not used in the project. (Template files can be installed with xlsx-chart first, then taken out in node_modules, and then uninstalled with xlsx-chart); we only need basic templates (columnAvg.xlsx, columnGroup.xlsx, columnGroupAvg.xlsx are not used )


insert image description here


Step 3: Use the xxx.vue file in vue

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png" />
    <br />
    <button @click="getChartExcel" style="margin: 0 auto">
      导出带图标的excel表格
    </button>
  </div>
</template>

<script>
// xlsx-chart修改后的源码文件
import XLSXChart from "./utils/base";
import FileSaver from "file-saver";

export default {
      
      
  name: "App",
  methods: {
      
      
    getChartExcel () {
      
      
      let xlsxChart = new XLSXChart();
      let opts = {
      
      
        chart: "bar",
        titles: ["Price"],
        fields: ["Apple", "Blackberry", "Strawberry", "Cowberry", "jasonchen"],
        data: {
      
      
          Price: {
      
      
            Apple: 10,
            Blackberry: 5,
            Strawberry: 15,
            Cowberry: 20,
            jasonchen: 2000
          },
        },
        chartTitle: "Area chart",
      };
      xlsxChart.generate(opts, function (err, data) {
      
      
        if (err) {
      
      
          console.error(err);
        } else {
      
      
          let blob = new Blob([data]);
          FileSaver.saveAs(blob, "chart.xlsx");
        }
      });
    }
  },
};

</script>

<style>
#app {
      
      
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Step 4: The Final Effect
insert image description here

Step 5: Advanced use:
idea: After using it, it is not difficult to understand that the realization idea of ​​xlsx-chart is to read the local excel template file when exporting excel, and then use the data we want to display to replace the data in the template table, and finally achieve the effect of chart generation; but can we add a few more charts on the basis of the existing template? The answer is yes and very simple 1. We open one of the template files column.xlsx; ), pay attention to select all
columns
insert image description here

2. Generate a chart
insert image description here

3. After repeating the above operations to generate multiple charts, cut these generated charts into the Chart sheet and save them
insert image description here

4. Export the form again (using the saved template)
insert image description here
Article project source code address :
https://github.com/Jason-chen-coder/Xlsx-EasyExportExcelWithChars
is written at the end:

1. The above is based on xlsx-chart to develop the function of exporting table data with charts in excel, and simple expansion. If you have a better idea, you can directly modify the source code of base.js and modify the chart style of the excel template, and even add more functions related to excel in the template; 2. The project link in the document: https://github.com/Jason-chen-coder/exportExcelForChart , the big guys can modify his full version of the source code to develop
more
functions

Guess you like

Origin blog.csdn.net/weixin_39085822/article/details/111505072