封装 x-spreadsheet 带样式导入导出

2023-11-06

接上两篇

vue 下使用 exceljs + x-spreadsheet 带样式导入Excel

vue 下使用 exceljs + x-spreadsheet 带样式导出Excel

下面封装好一个组件

<template>
  <div ref="sheetContainer" v-bind:id="sheetContainerId" class="grid"></div>
</template>

<script>
import Spreadsheet from "x-data-spreadsheet";
import zhCN from "x-data-spreadsheet/src/locale/zh-cn";
import _ from "lodash";
import * as Excel from "exceljs/dist/exceljs";
import * as tinycolor from "tinycolor2";
import { Guid } from "js-guid";
export default {
  name: "xspreadsheet",
  props: {
    ColumnCount: {
      type: Number,
      default: () => 50,
    },
    ColumnWidth: {
      type: Number,
      default: () => 100,
    },
    RowCount: {
      type: Number,
      default: () => 9999,
    },
    SheetName: {
      type: String,
      default: () => [],
    },
    Headers: {
      type: Array,
      default: () => [],
    },
    Records: {
      type: Array,
      default: () => [],
    },
    /*表头样式*/
    HeaderStyle: {
      type: Object,
      default: () => {
        return {
          //bgcolor: "#f4f5f8",
          textwrap: true,
          color: "#900b09",
          align: "center",
          valign: "middle",
          border: {
            top: ["thin", "#1E1E1E"],
            bottom: ["thin", "#1E1E1E"],
            right: ["thin", "#1E1E1E"],
            left: ["thin", "#1E1E1E"],
          },
          font: {
            bold: true,
          },
        };
      },
    },
    /*表体样式*/
    RecordStyle: {
      type: Object,
      default: () => {
        return {
          //bgcolor: "#f4f5f8",
          textwrap: true,
          color: "#900b09",
          align: "left",
          valign: "middle",
          border: {
            top: ["thin", "#1E1E1E"],
            bottom: ["thin", "#1E1E1E"],
            right: ["thin", "#1E1E1E"],
            left: ["thin", "#1E1E1E"],
          },
          font: {
            bold: false,
          },
        };
      },
    },
    File: {
      type: null,
      default: () => null,
    },
    ExportJsonProperties: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      xs: null,
      sheetContainerId: Guid.newGuid().toString(),
      DataSource: []
    };
  },
  mounted() {
    this.$nextTick(() => {
      this.init();
    });
  },
  watch: {
    File: {
      handler(newV, oldV) {
        this.$nextTick(() => {
          this.loadExcelFile(newV);
        });
      },
    },
    Headers: {
      deep: true,
      handler(newV) {
        let result = [];
        if (Array.isArray(newV) && newV.length > 0) {
          let headerRow = { cells: [] };
          for (let i = 0; i < newV.length; i++) {
            headerRow.cells.push({
              text: newV[i],
              editable: false,
              style: 0,
            });
          }
          result.push(headerRow);
        }
        if (Array.isArray(this.Records) && this.Records.length > 0) {
          for (let i = 0; i < this.Records.length; i++) {
            let recordRow = { cells: [] };
            if (JSON.stringify(this.Records[i]) != "{}") {
              for(let k=0; k < this.ExportJsonProperties.length; k++) {
                recordRow.cells.push({
                  text: this.Records[i][this.ExportJsonProperties[k]] + "",
                  editable: true,
                  style: 1,
                });
              }
            } else {
              for (let i = 0; i < this.ColumnCount; i++) {
                recordRow.cells.push({
                  text: "",
                  editable: true,
                  style: 1,
                });
              }
            }
            result.push(recordRow);
          }
        }
        this.DataSource = result;
      },
    },
    Records: {
      deep: true,
      handler(newV) {
        let result = [];
        if (Array.isArray(this.Headers) && this.Headers.length > 0) {
          let headerRow = { cells: [] };
          for (let i = 0; i < this.Headers.length; i++) {
            headerRow.cells.push({
              text: this.Headers[i],
              editable: false,
              style: 0,
            });
          }
          result.push(headerRow);
        }
        if (Array.isArray(newV) && newV.length > 0) {
          for (let i = 0; i < newV.length; i++) {
            let recordRow = { cells: [] };
            if (JSON.stringify(newV[i]) != "{}") {
              for(let k=0; k < this.ExportJsonProperties.length; k++) {
                recordRow.cells.push({
                  text: newV[i][this.ExportJsonProperties[k]] + "",
                  editable: true,
                  style: 1,
                });
              }
            } else {
              for (let i = 0; i < this.ColumnCount; i++) {
                recordRow.cells.push({
                  text: "",
                  editable: true,
                  style: 1,
                });
              }
            }
            result.push(recordRow);
          }
        }
        this.DataSource = result;
      }
    },
    DataSource : {
      deep : true,
      handler(newW) {
        if (this.xs) {
          console.log(newW)
          this.xs.loadData([
            {
              name: this.SheetName,
              styles: [this.HeaderStyle, this.RecordStyle],
              rows: newW,
            },
          ]);
        }
      }
    }
  },
  methods: {
    // 初始化表格
    init() {
      if (
        this.$refs.sheetContainer &&
        this.$refs.sheetContainer.offsetHeight &&
        this.$refs.sheetContainer.offsetWidth
      ) {
        //设置中文
        Spreadsheet.locale("zh-cn", zhCN);
        this.xs = new Spreadsheet(
          document.getElementById(this.sheetContainerId),
          {
            mode: "edit",
            showToolbar: true,
            showGrid: true,
            showContextmenu: true,
            showBottomBar: true,
            view: {
              height: () =>
                this.$refs.sheetContainer &&
                this.$refs.sheetContainer.offsetHeight &&
                _.isNumber(this.$refs.sheetContainer.offsetHeight)
                  ? this.$refs.sheetContainer.offsetHeight
                  : 0,
              width: () =>
                this.$refs.sheetContainer &&
                this.$refs.sheetContainer.offsetWidth &&
                _.isNumber(this.$refs.sheetContainer.offsetWidth)
                  ? this.$refs.sheetContainer.offsetWidth
                  : 0,
            },
            formats: [],
            fonts: [],
            formula: [],
            row: {
              len: this.RowCount,
              height: 25,
            },
            col: {
              len: this.ColumnCount,
              width: this.ColumnWidth,
              indexWidth: 60,
              minWidth: 60,
            },
          }
        );
        this.loadData();
      }
    },
    loadData() {
      if (this.xs) {
        this.xs.loadData([
          {
            name: this.SheetName,
            styles: [this.HeaderStyle, this.RecordStyle],
            rows: this.DataSource,
          },
        ]);
      }
    },
    // 导入excel
    loadExcelFile(file) {
      if (file) {
        const wb = new Excel.Workbook();
        const reader = new FileReader();
        reader.readAsArrayBuffer(file);
        reader.onload = () => {
          const buffer = reader.result;
          // 微软的 Excel ColorIndex 一个索引数字对应一个颜色
          const indexedColors = [
            "000000",
            "FFFFFF",
            "FF0000",
            "00FF00",
            "0000FF",
            "FFFF00",
            "FF00FF",
            "00FFFF",
            "000000",
            "FFFFFF",
            "FF0000",
            "00FF00",
            "0000FF",
            "FFFF00",
            "FF00FF",
            "00FFFF",
            "800000",
            "008000",
            "000080",
            "808000",
            "800080",
            "008080",
            "C0C0C0",
            "808080",
            "9999FF",
            "993366",
            "FFFFCC",
            "CCFFFF",
            "660066",
            "FF8080",
            "0066CC",
            "CCCCFF",
            "000080",
            "FF00FF",
            "FFFF00",
            "00FFFF",
            "800080",
            "800000",
            "008080",
            "0000FF",
            "00CCFF",
            "CCFFFF",
            "CCFFCC",
            "FFFF99",
            "99CCFF",
            "FF99CC",
            "CC99FF",
            "FFCC99",
            "3366FF",
            "33CCCC",
            "99CC00",
            "FFCC00",
            "FF9900",
            "FF6600",
            "666699",
            "969696",
            "003366",
            "339966",
            "003300",
            "333300",
            "993300",
            "993366",
            "333399",
            "333333",
          ];
          wb.xlsx.load(buffer).then((workbook) => {
            let workbookData = [];
            workbook.eachSheet((sheet, sheetIndex) => {
              // 构造x-data-spreadsheet 的 sheet 数据源结构
              let sheetData = {
                name: sheet.name,
                styles: [],
                rows: {},
                merges: [],
              };
              // 收集合并单元格信息
              let mergeAddressData = [];
              for (let mergeRange in sheet._merges) {
                sheetData.merges.push(sheet._merges[mergeRange].shortRange);
                let mergeAddress = {};
                // 合并单元格起始地址
                mergeAddress.startAddress = sheet._merges[mergeRange].tl;
                // 合并单元格终止地址
                mergeAddress.endAddress = sheet._merges[mergeRange].br;
                // Y轴方向跨度
                mergeAddress.YRange =
                  sheet._merges[mergeRange].model.bottom -
                  sheet._merges[mergeRange].model.top;
                // X轴方向跨度
                mergeAddress.XRange =
                  sheet._merges[mergeRange].model.right -
                  sheet._merges[mergeRange].model.left;
                mergeAddressData.push(mergeAddress);
              }
              sheetData.cols = {};
              for (let i = 0; i < sheet.columns.length; i++) {
                sheetData.cols[i.toString()] = {};
                if (sheet.columns[i].width) {
                  // 不知道为什么从 exceljs 读取的宽度显示到 x-data-spreadsheet 特别小, 这里乘以8
                  sheetData.cols[i.toString()].width =
                    sheet.columns[i].width * 8;
                } else {
                  // 默认列宽
                  sheetData.cols[i.toString()].width = 100;
                }
              }

              // 遍历行
              sheet.eachRow((row, rowIndex) => {
                sheetData.rows[(rowIndex - 1).toString()] = { cells: {} };
                //includeEmpty = false 不包含空白单元格
                row.eachCell(
                  { includeEmpty: true },
                  function (cell, colNumber) {
                    let cellText = "";
                    if (cell.value && cell.value.result) {
                      // Excel 单元格有公式
                      cellText = cell.value.result;
                    } else if (cell.value && cell.value.richText) {
                      // Excel 单元格是多行文本
                      for (let text in cell.value.richText) {
                        // 多行文本做累加
                        cellText += cell.value.richText[text].text;
                      }
                    } else {
                      // Excel 单元格无公式
                      cellText = cell.value;
                    }

                    //解析单元格,包含样式
                    //*********************单元格存在背景色******************************
                    // 单元格存在背景色
                    let backGroundColor = null;
                    if (
                      cell.style.fill &&
                      cell.style.fill.fgColor &&
                      cell.style.fill.fgColor.argb
                    ) {
                      // 8位字符颜色先转rgb再转16进制颜色
                      backGroundColor = ((val) => {
                        val = val.trim().toLowerCase(); //去掉前后空格
                        let color = {};
                        try {
                          let argb =
                            /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(
                              val
                            );
                          color.r = parseInt(argb[2], 16);
                          color.g = parseInt(argb[3], 16);
                          color.b = parseInt(argb[4], 16);
                          color.a = parseInt(argb[1], 16) / 255;
                          return tinycolor(
                            `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`
                          ).toHexString();
                        } catch (e) {
                          console.log(e);
                        }
                      })(cell.style.fill.fgColor.argb);
                    }

                    if (backGroundColor) {
                      cell.style.bgcolor = backGroundColor;
                    }
                    //*************************************************************************** */

                    //*********************字体存在背景色******************************
                    // 字体颜色
                    let fontColor = null;
                    if (
                      cell.style.font &&
                      cell.style.font.color &&
                      cell.style.font.color.argb
                    ) {
                      // 8位字符颜色先转rgb再转16进制颜色
                      fontColor = ((val) => {
                        val = val.trim().toLowerCase(); //去掉前后空格
                        let color = {};
                        try {
                          let argb =
                            /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(
                              val
                            );
                          color.r = parseInt(argb[2], 16);
                          color.g = parseInt(argb[3], 16);
                          color.b = parseInt(argb[4], 16);
                          color.a = parseInt(argb[1], 16) / 255;
                          return tinycolor(
                            `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`
                          ).toHexString();
                        } catch (e) {
                          console.log(e);
                        }
                      })(cell.style.font.color.argb);
                    }
                    if (fontColor) {
                      //console.log(fontColor)
                      cell.style.color = fontColor;
                    }
                    //************************************************************************ */

                    // exceljs 对齐的格式转成 x-date-spreedsheet 能识别的对齐格式
                    if (
                      cell.style.alignment &&
                      cell.style.alignment.horizontal
                    ) {
                      cell.style.align = cell.style.alignment.horizontal;
                      cell.style.valign = cell.style.alignment.vertical;
                    }

                    //处理合并单元格
                    let mergeAddress = _.find(mergeAddressData, function (o) {
                      return o.startAddress == cell._address;
                    });
                    if (mergeAddress) {
                      // 遍历的单元格属于合并单元格
                      if (cell.master.address != mergeAddress.startAddress) {
                        // 不是合并单元格中的第一个单元格不需要计入数据源
                        return;
                      }
                      // 说明是合并单元格区域的起始单元格
                      sheetData.rows[(rowIndex - 1).toString()].cells[
                        (colNumber - 1).toString()
                      ] = {
                        text: cellText,
                        style: 0,
                        merge: [mergeAddress.YRange, mergeAddress.XRange],
                      };

                      //解析单元格,包含样式
                      let xsCellStyle = _.cloneDeep(cell.style);
                      xsCellStyle.border = {};
                      // 边框线
                      if (
                        cell.style.border &&
                        JSON.stringify(cell.style.border) != "{}"
                      ) {
                        let coloneStyle = cell.style.border;
                        xsCellStyle.border = {};
                        if (coloneStyle.bottom) {
                          xsCellStyle.border.bottom = [];
                          xsCellStyle.border.bottom[0] =
                            coloneStyle.bottom.style;
                          if (_.isString(coloneStyle.bottom.color)) {
                            xsCellStyle.border.bottom[1] =
                              coloneStyle.bottom.color;
                          } else {
                            xsCellStyle.border.bottom[1] = "#000000";
                          }
                        }
                        if (coloneStyle.right) {
                          xsCellStyle.border.right = [];
                          xsCellStyle.border.right[0] = coloneStyle.right.style;
                          if (_.isString(coloneStyle.right.color)) {
                            xsCellStyle.border.right[1] =
                              coloneStyle.right.color;
                          } else {
                            xsCellStyle.border.right[1] = "#000000";
                          }
                        }
                        if (coloneStyle.left) {
                          xsCellStyle.border.left = [];
                          xsCellStyle.border.left[0] = coloneStyle.left.style;
                          if (_.isString(coloneStyle.left.color)) {
                            xsCellStyle.border.left[1] = coloneStyle.left.color;
                          } else {
                            xsCellStyle.border.left[1] = "#000000";
                          }
                        }
                        if (coloneStyle.top) {
                          xsCellStyle.border.top = [];
                          xsCellStyle.border.top[0] = coloneStyle.top.style;
                          if (_.isString(coloneStyle.top.color)) {
                            xsCellStyle.border.top[1] = coloneStyle.top.color;
                          } else {
                            xsCellStyle.border.top[1] = "#000000";
                          }
                        }
                      }

                      sheetData.styles.push(xsCellStyle);
                      //对应的style存放序号
                      sheetData.rows[(rowIndex - 1).toString()].cells[
                        (colNumber - 1).toString()
                      ].style = sheetData.styles.length - 1;
                    } else {
                      // 非合并单元格
                      sheetData.rows[(rowIndex - 1).toString()].cells[
                        (colNumber - 1).toString()
                      ] = { text: cellText, style: 0 };
                      //解析单元格,包含样式
                      let xsCellStyle = _.cloneDeep(cell.style);
                      xsCellStyle.border = {};

                      // 边框线
                      if (
                        cell.style.border &&
                        JSON.stringify(cell.style.border) != "{}"
                      ) {
                        let coloneStyle = cell.style.border;
                        xsCellStyle.border = {};
                        if (coloneStyle.bottom) {
                          xsCellStyle.border.bottom = [];
                          xsCellStyle.border.bottom[0] =
                            coloneStyle.bottom.style;
                          if (_.isString(coloneStyle.bottom.color)) {
                            xsCellStyle.border.bottom[1] =
                              coloneStyle.bottom.color;
                          } else {
                            xsCellStyle.border.bottom[1] = "#000000";
                          }
                        }
                        if (coloneStyle.right) {
                          xsCellStyle.border.right = [];
                          xsCellStyle.border.right[0] = coloneStyle.right.style;
                          if (_.isString(coloneStyle.right.color)) {
                            xsCellStyle.border.right[1] =
                              coloneStyle.right.color;
                          } else {
                            xsCellStyle.border.right[1] = "#000000";
                          }
                        }
                        if (coloneStyle.left) {
                          xsCellStyle.border.left = [];
                          xsCellStyle.border.left[0] = coloneStyle.left.style;
                          if (_.isString(coloneStyle.left.color)) {
                            xsCellStyle.border.left[1] = coloneStyle.left.color;
                          } else {
                            xsCellStyle.border.left[1] = "#000000";
                          }
                        }
                        if (coloneStyle.top) {
                          xsCellStyle.border.top = [];
                          xsCellStyle.border.top[0] = coloneStyle.top.style;
                          if (_.isString(coloneStyle.top.color)) {
                            xsCellStyle.border.top[1] = coloneStyle.top.color;
                          } else {
                            xsCellStyle.border.top[1] = "#000000";
                          }
                        }
                      }

                      sheetData.styles.push(xsCellStyle);
                      //对应的style存放序号
                      sheetData.rows[(rowIndex - 1).toString()].cells[
                        (colNumber - 1).toString()
                      ].style = sheetData.styles.length - 1;
                    }
                  }
                );
              });
              workbookData.push(sheetData);
            });
            this.xs.loadData(workbookData);
          });
        };
      }
    },
    // 导出excel
    exportExcel(fileName) {
      const exceljsWorkbook = new Excel.Workbook();
      exceljsWorkbook.modified = new Date();
      this.xs.getData().forEach(function (xws) {
        let rowobj = xws.rows;
        // 构造exceljs文档结构
        const exceljsSheet = exceljsWorkbook.addWorksheet(xws.name);
        // 读取列宽
        let sheetColumns = [];
        let colIndex = 0;
        for (let col in xws.cols) {
          if (xws.cols[col].width) {
            sheetColumns.push({
              header: colIndex + "",
              key: colIndex + "",
              width: xws.cols[col].width / 8,
            });
          }
          colIndex++;
        }
        exceljsSheet.columns = sheetColumns;
        for (let ri = 0; ri < rowobj.len; ++ri) {
          let row = rowobj[ri];
          if (!row) continue;
          // 构造exceljs的行(如果尚不存在,则将返回一个新的空对象)
          const exceljsRow = exceljsSheet.getRow(ri + 1);
          Object.keys(row.cells).forEach(function (k) {
            let idx = +k;
            if (isNaN(idx)) return;
            const exceljsCell = exceljsRow.getCell(Number(k) + 1);
            exceljsCell.value = row.cells[k].text;
            
            if (
              xws.styles[row.cells[k].style]
            ) {
              // 垂直对齐方式
              if (xws.styles[row.cells[k].style].valign) {
                if (
                  exceljsCell.alignment == undefined ||
                  exceljsCell.alignment == null
                ) {
                  exceljsCell.alignment = {};
                }
                exceljsCell.alignment.vertical = 
                  xws.styles[row.cells[k].style].valign;
              }
              // 水平对齐方式
              if (xws.styles[row.cells[k].style].align) {
                if (
                  exceljsCell.alignment == undefined ||
                  exceljsCell.alignment == null
                ) {
                  exceljsCell.alignment = {};
                }
                exceljsCell.alignment.horizontal =
                  xws.styles[row.cells[k].style].align;
                }
              // exceljsSheet.getCell(exceljsCell._address).alignment = { vertical: xws.styles[row.cells[k].style].valign, horizontal: xws.styles[row.cells[k].style].align }
            }

            // 边框
            if (
              JSON.stringify(xws.styles[row.cells[k].style]) != "{}" &&
              JSON.stringify(xws.styles[row.cells[k].style].border) != "{}"
            ) {
              //exceljsCell.border = xws.styles[row.cells[k].style].border;
              exceljsCell.border = {};
              // bottom
              if (
                xws.styles[row.cells[k].style].border.bottom &&
                Array.isArray(xws.styles[row.cells[k].style].border.bottom) &&
                xws.styles[row.cells[k].style].border.bottom.length == 2
              ) {
                exceljsCell.border.bottom = {};
                exceljsCell.border.bottom.style =
                  xws.styles[row.cells[k].style].border.bottom[0];
                exceljsCell.border.bottom.color = {};
                //exceljsCell.border.bottom.color.indexed = 64
                exceljsCell.border.bottom.color =
                  xws.styles[row.cells[k].style].border.bottom[1];
              }
              // left
              if (
                xws.styles[row.cells[k].style].border.left &&
                Array.isArray(xws.styles[row.cells[k].style].border.left) &&
                xws.styles[row.cells[k].style].border.left.length == 2
              ) {
                exceljsCell.border.left = {};
                exceljsCell.border.left.style =
                  xws.styles[row.cells[k].style].border.left[0];
                exceljsCell.border.left.color = {};
                //exceljsCell.border.left.color.indexed = 64
                exceljsCell.border.left.color =
                  xws.styles[row.cells[k].style].border.left[1];
              }
              // right
              if (
                xws.styles[row.cells[k].style].border.right &&
                Array.isArray(xws.styles[row.cells[k].style].border.right) &&
                xws.styles[row.cells[k].style].border.right.length == 2
              ) {
                exceljsCell.border.right = {};
                exceljsCell.border.right.style =
                  xws.styles[row.cells[k].style].border.right[0];
                exceljsCell.border.right.color = {};
                //exceljsCell.border.right.color.indexed = 64
                exceljsCell.border.right.color =
                  xws.styles[row.cells[k].style].border.right[1];
              }
              // top
              if (
                xws.styles[row.cells[k].style].border.top &&
                Array.isArray(xws.styles[row.cells[k].style].border.top) &&
                xws.styles[row.cells[k].style].border.top.length == 2
              ) {
                exceljsCell.border.top = {};
                exceljsCell.border.top.style =
                  xws.styles[row.cells[k].style].border.top[0];
                exceljsCell.border.top.color = {};
                //exceljsCell.border.right.color.indexed = 64
                exceljsCell.border.top.color =
                  xws.styles[row.cells[k].style].border.top[1];
              }
            }

            // 背景色
            if (xws.styles[row.cells[k].style].bgcolor) {
              let rgb = tinycolor(
                xws.styles[row.cells[k].style].bgcolor
              ).toRgb();
              let rHex = parseInt(rgb.r).toString(16).padStart(2, "0");
              let gHex = parseInt(rgb.g).toString(16).padStart(2, "0");
              let bHex = parseInt(rgb.b).toString(16).padStart(2, "0");
              let aHex = parseInt(rgb.a).toString(16).padStart(2, "0");
              let _bgColor = aHex + rHex + gHex + bHex;
              // 设置exceljs背景色
              exceljsCell.fill = {
                type: "pattern",
                pattern: "solid",
                fgColor: { argb: _bgColor },
              };
            }
            // 字体
            exceljsCell.font = xws.styles[row.cells[k].style].font;
            // 字体颜色
            if (xws.styles[row.cells[k].style].color) {
              let rgb = tinycolor(xws.styles[row.cells[k].style].color).toRgb();
              let rHex = parseInt(rgb.r).toString(16).padStart(2, "0");
              let gHex = parseInt(rgb.g).toString(16).padStart(2, "0");
              let bHex = parseInt(rgb.b).toString(16).padStart(2, "0");
              let aHex = parseInt(rgb.a).toString(16).padStart(2, "0");
              let _fontColor = aHex + rHex + gHex + bHex;
              exceljsCell.font.color = { argb: _fontColor };
            }
            // 合并单元格
            if (row.cells[k].merge) {
              // 开始行
              let startRow = ri + 1;
              // 结束行,加上Y轴跨度
              let endRow = startRow + row.cells[k].merge[0];
              // 开始列
              let startColumn = Number(k) + 1;
              // 结束列,加上X轴跨度
              let endColumn = startColumn + row.cells[k].merge[1];
              // 按开始行,开始列,结束行,结束列合并
              exceljsSheet.mergeCells(startRow, startColumn, endRow, endColumn);
            }
          });
        }
      });
      // writeBuffer 把写好的excel 转换成 ArrayBuffer 类型
      exceljsWorkbook.xlsx.writeBuffer().then((data) => {
        const link = document.createElement("a");
        // Blob 实现下载excel
        const blob = new Blob([data], {
          type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8",
        });
        link.href = window.URL.createObjectURL(blob);
        link.download = `${fileName}.xlsx`;
        link.click();
      });
    },
    // 导出json
    exportJson() {
      let sheetsData = this.xs.getData();
      let rows = Object.entries(sheetsData[0].rows);
      let jsonData = [];
      if (Array.isArray(this.ExportJsonProperties) && this.ExportJsonProperties.length > 0) {
        // 遍历数据,跳过第一行表头
        for (let i = 1; i < rows.length; i++) {
          if (rows[i] && rows[i][1] && rows[i][1].cells) {
            let row = Object.entries(rows[i][1].cells);
            // 构造行对象
            let JsonRow = {};
            for (let k = 0; k < row.length; k++) {
              let cells = row[k];
              JsonRow[this.ExportJsonProperties[k]] = cells[1].text;
            }
            jsonData.push(JsonRow);
          }
        }
      }
      return jsonData;
    },
  },
  destroyed() {},
};
</script>
<style>
</style>

调用组件的页面

<template>
  <div class="container">
    <div class="toolbar">
      <input
        type="file"
        class="choose"
        @change="loadExcelFile"
        accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
      />
      <el-button @click="exportJson">导出JSON</el-button>
      <el-button @click="exportExcel">导出xlsx</el-button>
    </div>
    <Sheet ref="Sheet" SheetName="调配订单变量" :ExportJsonProperties="JsonProperties" :File="file" :Headers="headers" :ColumnCount="headers.length" :ColumnWidth="300" :RowCount="records.length + 1" :Records="records"></Sheet>
  </div>
</template>

<script>
import Sheet from "@/components/SpreadSheet.vue";
export default {
  components: { Sheet },
  data() {
    return {
      file : null,
      headers : ['序号','订单号','产品名称','订单状态','计划生产数量','实际生产数量','单位','计划开始时间','计划结束时间','调配罐',
      'TK101出油比率','TK101出油数量',
      'TK102出油比率','TK102出油数量',
      'TK103出油比率','TK103出油数量',
      'TK104出油比率','TK104出油数量',
      'TK108出油比率','TK108出油数量',
      'TK109出油比率','TK109出油数量',
      '添加剂1','添加剂2'],
      records: [{},{},{},{},{},{},{},{},{},{},],
      // 定义导出的Json结构
      JsonProperties: [
        'Index',
        'OrderNo',
        'ProductName',
        'ProductStatus',
        'PlanQuantity',
        'RealQuantity',
        'Unit',
        'StartDate',
        'EndDate',
        'MixTank',
        'TK101Rate',
        'TK101Quantity',
        'TK102Rate',
        'TK102Quantity',
        'TK103Rate',
        'TK103Quantity',
        'TK104Rate',
        'TK104Quantity',
        'TK108Rate',
        'TK108Quantity',
        'TK109Rate',
        'TK109Quantity',
        'Additive1',
        'Additive2'
      ]
    }
  },
  mounted() {},
  methods: {
    loadExcelFile(e) {
      this.file = e.target.files[0]
    },
    exportJson() {
      let json = this.$refs.Sheet.exportJson()
      console.log(json)
    },
    exportExcel() {
      this.$refs.Sheet.exportExcel('调配订单变量')
    },
  },
};
</script>
<style lang="less" scoped>
.container {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  .toolbar {
    width: 100%;
    height: 50px;
  }
  .grid {
    width: 100%;
    height: calc(100% - 80px);
  }
  /deep/ .x-spreadsheet-toolbar {
    padding: 0px;
    width: calc(100% - 2px) !important;
  }
}
.choose::-webkit-file-upload-button {
  color: white;
  display: inline-block;
  background: #409EFF;
  border: none;
  padding: 12px 20px;
  width: 100px;
  height: 40px;
  border-radius: 3px;
  white-space: nowrap;
  cursor: pointer;
  font-size: 10pt;
}
</style>

效果如图,目前“导出Json”还没有做 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

封装 x-spreadsheet 带样式导入导出 的相关文章

  • 如何在 JavaScript 中正确初始化 ErrorEvent?

    我需要开火ErrorEvent以编程方式 但无法弄清楚如何初始化事件的属性 这些属性是只读的 并且initEvent 仅初始化事件类型 是否冒泡以及是否可取消 I tried var myErrorEvent new ErrorEvent
  • 用于 Javascript、HTML 和 CSS 网站的 Visual Studio 项目,具有调试功能

    我正在网络浏览器中开发一个基于纯前端的应用程序 我使用的是 Javascript jQuery Knockout HTML CSS 引导程序 所以没有后端 没有必要 在这方面 我在 Visual Studio 中找不到合适的项目模板 截至
  • 在 jsconfig.json 中找不到“import-resolver-typescript/lib”错误

    Problem Error File Users nish7 Documents Code WebDev HOS frontend node modules eslint import resolver typescript lib not
  • 如何理解javascript React中的这段代码

    我在网上找到了这个函数在js中的实现 这个函数递归地过滤一个对象数组 每个对象可能有属性 children 它是对象数组 并且对象也可能有孩子等等 该函数工作正常 但我有点不明白 这是我的功能 getFilteredArray array
  • 通过span标签动态包装js字符串,这可能会在React中危险地渲染

    我有一个字符串 我想使用 aria label 标签将 span 标签单独添加到字母表中 最终结果将在 React 中危险地呈现 请指教 这就是我所拥有的 const str D C B B const addAriaLabels str
  • 更改 c3.js 散点图中气泡的大小

    我有一个散点图 我想改变点的大小 使它们看起来像气泡 有人可以告诉我如何改变气泡的大小吗 这是我的代码 var chart c3 generate data xs IBM ibm x Microsoft microsoft x column
  • JavaScript 设置滚动高度

    在 JavaScript 中 将一个元素的滚动高度设置为另一个元素的滚动高度的正确方法是什么 直接赋值没有效果 谢谢 格雷格 直接是不可能的 scrollHeight 是一个只读属性 包含元素内容的总高度 以像素为单位 如果有元素 A 并且
  • 按空格键后执行JS代码

    这是我的 JavaScript 代码 var changeIdValue function id value document getElementById id style height value document getElement
  • 使用 ES6 从子级获取父类名?

    我想获取父类名称 Parent 但我只能使用此代码检索子类名称 Child use strict class Parent class Child extends Parent var instance new Child console
  • Vue.js:折叠/展开父级中的所有元素

    我需要为我的 Vue 组件 一些可折叠面板 添加 展开 折叠全部 功能 如果用户单击折叠按钮 然后单击某个面板并将其展开 然后单击折叠按钮不会做任何事因为观看的参数不会改变 那么如何正确实现此功能 按钮必须始终折叠和展开组件 我准备了简单的
  • 使用 jQuery 检测用户何时滚动到 div 底部

    我有一个 div 框 称为 Flux 里面有可变数量的内容 此 divbox 的溢出设置为自动 现在 我想做的是 当使用滚动到此 DIV 框的底部时 将更多内容加载到页面中 我知道如何执行此操作 加载内容 但我不知道如何检测用户何时滚动到
  • 从 jsonp fetch Promise 获取 json

    我刚刚开始使用react native 并且我正在以文档中的经典示例作为基础 fetch https facebook github io react native movies json then response gt response
  • 包含菜单按钮的 Ag-grid 单元格

    我在我的项目中使用社区版本的 ag grid 我正在尝试在每一行的一个单元格中添加菜单按钮 单击菜单按钮时 应该会弹出菜单 其中包含编辑 删除 重命名选项 并且当单击菜单上的任何项目时 我需要触发具有行值的事件 我正在尝试创建一个将显示按钮
  • 检测 html 元素内的用户选择

    如何检测用户选择 用鼠标突出显示 是否在某个元素内 某个元素的子元素 Example div sdfsdf div some span content span div sdfsd div 伪代码 if window getSelectio
  • 在添加 ApiController 属性之前,ASP.NET Core 3.1 无法处理 Axios 请求

    我有以下问题 每当我向 Api 端点发送内容时 ASP NET Core 3 1 就无法处理该请求 但是 当我添加ApiController属性它工作得很好 我的代码是正确的 但只有当我添加此属性时才有效 怎么会这样呢 作为参考 这是我的代
  • jQuery find() 只返回第一个匹配的结果?

    我在 jQuery 中使用 find 方法 但无法获得与选择器条件匹配的所有结果 这是我的 HTML div class something div
  • Openlayers 3 中心地图

    我在唱歌开放层 3 http openlayers org en v3 0 0 apidoc 显示地图 我想使用经纬度坐标将地图居中 我正在使用快速入门代码 http openlayers org en v3 1 1 doc quickst
  • 流星与承诺

    我一直在尝试养成使用 Promise 的习惯 但在尝试在 Meteor 上下文中的服务器端代码中使用它们时遇到了问题 这就是问题 if Meteor isServer Meteor startup function code to run
  • AngularJS:如何通过 websocket 发送文件?

    我是 websocket 的新手 我被分配了一个现有的工作聊天模块 目前该模块仅向其他用户发送消息 我被要求集成用户可以互相发送 附件 的功能 供参考 我发现了这个链接 https stackoverflow com questions 1
  • 父元素的 mousedown 事件中的 offsetX 和 offsetY 错误

    我在 mousedown 上获取 offsetX 时遇到问题 下面是我的代码 div Click the text The mouseDown function is triggered when the mouse button is p

随机推荐

  • 数据包在网络中的传输过程详解

    我们当今使用电子设备都离不开网络 通过网络我们可以聊天 玩游戏 看电影都操作 网络的本质就是交换数据 本文我们就来看下数据是如何在网络中传输的 计算机网络模型 现在有两种计算机网络模型 分别为OSI七层模型和TCP IP四层模型 OSI将计
  • MySQL中的触发器

    MySQL中的触发器 在数据库应用中 我们经常需要对数据进行某些操作 并在操作完成后进行相应的处理 这时候 可以使用触发器来实现这些功能 MySQL提供了强大的触发器功能 本文将带您深入了解MySQL中的触发器 什么是触发器 触发器是一种特
  • HierarchicalDataTemplate

    针对具有分层数据结构的控件设计的 比如说TreeView 相当于可以每一个层级上做DataTemplate XmlDataProvider 数据源 写在Resources下
  • Redis——缓存击穿、穿透、雪崩

    Redis的缓存击穿 穿透 雪崩 这几个概念是设计大流量接口时所需要考虑的问题 也是面试常问的Redis相关的基础知识 本篇捋一下这几个概念 做一个小结 大家都知道 计算机的瓶颈之一就是IO 为了解决内存与磁盘速度不匹配的问题 产生了缓存
  • 操作系统作业 - 内存管理 - 请求分页分配方式模拟

    内存管理 请求分页分配方式 设计方案报告 文末有源码 文章目录 内存管理 请求分页分配方式 设计方案报告 1 项目需求 1 1 基本任务 1 2 功能描述 1 3 项目目的 2 开发环境 3 项目结构 4 操作说明 5 系统分析 5 1 置
  • 洛谷题单 算法1-7 搜索

    USACO08FEB Meteor Shower S 题目描述 Bessie hears that an extraordinary meteor shower is coming reports say that these meteor
  • 【千律】C++基础:宽窄字节字符串的相互转换与控制台输出

    方案1 include
  • 怎么调节手机的刷新率_二分钟科普:手机上的“高刷新率”

    上回粗略带过屏幕刷新率 这篇将会以更简单的叙述 介绍手机屏幕刷新率和插帧 本期关键词 屏幕刷新率 FPS 插帧 正文 不纠结这是谁带节奏 进步是必然的 屏幕刷新率 通常单位为Hz 是一个硬件固定数值 例如一部手机的屏幕刷新率为120Hz 那
  • Conditional DETR spatial attention & content attention可视化(二)

    就是将attention图通过加权叠加 叠加到原图上 要通过cv2 applyColorMap 将attention的单通道图转为三通道图 将attention中一些小的值置0 不然叠加之后会干扰原图 产生色差 至于蓝色 是通过cv2 ap
  • tcp retransmission 出现的原因_TCP 协议快被淘汰了,UDP 协议才是新世代的未来?

    公众号关注 运维之美 设为 星标 每天带你玩转 Linux TCP 协议可以说是今天互联网的基石 作为可靠的传输协议 在今天几乎所有的数据都会通过 TCP 协议传输 然而 TCP 在设计之初没有考虑到现今复杂的网络环境 当你在地铁上或者火车
  • 多线程:什么是同步与异步?二者的区别

    今天看到一道面试题 同步与异步有什么区别 同步 异步 这个在我们学习多线程的时候 会接触到这个概念 后面所学的一系列多线程知识运用也是以这两个点开展的 由于学习的时候囫囵吞枣 导致我对这两个概念没法准确说出定义及其区别 现在记录一下 如果光
  • 修改主机名(/etc/hostname和/etc/hosts区别)

    ubuntu永久修改主机名 1 查看主机名 在Ubuntu系统中 快速查看主机名有多种方法 其一 打开一个GNOME终端窗口 在命令提示符中可以看到主机名 主机名通常位于 符号后 其二 在终端窗口中输入命令 hostname或uname n
  • Visual Stdio 2017 Community 中文版哪里下载方便

    嫌官网不好用的话 推荐先下一个腾讯电脑管家 腾讯电脑管家自带了软件下载中心 可以去那里获取Visual Stdio 2017 Community 具体步骤如下 1 安装腾讯电脑管家 2 打开腾讯电脑管家 点击软件管理 如图 3 搜索Visu
  • 寻找环——指针法

    一 在一条链中找环 bool judge int a 存在返回ture 否则返回false int slow 0 fast 0 do slow a slow fast a a fast while slow fast a fast 1 if
  • 嵌入式数据库-SQLite3的基本指令及用C语言操作数据库

    SQLite简介 轻量化 易用的嵌入式数据库 用于设备端的数据管理 可以理解成单点的数据库 传统服务器型数据 库用于管理多端设备 更加复杂 SQLite是一个无服务器的数据库 是自包含的 这也称为嵌入式数据库 这意味着数据库引擎作为应用程序
  • DEMO:修改外向交货单BAPI_OUTB_DELIVERY_CHANGE_SAP刘梦_新浪博客

    简单demo 交货单数量 1 然后维护上序列号 有的公司没有启用序列号就不需要了 REPORT zdemo vl02n TABLES likp lips PARAMETERS p vbeln TYPE vbeln vl PARAMETER
  • 若依源码DataScopeAspect数据权限

    目录 源代码 使用场景 代码解析 源代码 从这里可以看出 表sys role dept 的用途 之前一起不清楚 role dept关联表的含义 自定义数据权限时 DATA SCOPE CUSTOM 通过角色 可以管理哪几个部门 来实现的 A
  • qt中的setStyleSheet的基本用法(按钮的几种状态)

    参考博客 https blog csdn net qq 42250189 article details 105199339 https blog csdn net weixin 38169769 article details 99894
  • 在打印ASCII值是26的字符的时候,输出的结果不认识

    现象 解决办法 32其实是一种转义字符 后面的32是8进制的 8进制的32代表的就是2 8 0 3 8 1 结果为十进制的26 在ASCII表里边0 31个字符 开头的32个字符 以及第127个字符 最后一个字符 都是不可见的 无法显示 但
  • 封装 x-spreadsheet 带样式导入导出

    接上两篇 vue 下使用 exceljs x spreadsheet 带样式导入Excel vue 下使用 exceljs x spreadsheet 带样式导出Excel 下面封装好一个组件