LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

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

admin
2024年6月12日 12:15 本文热度 721

导入的样式包括字体,字号,列宽,合并单元格,【部分能识别】的背景色,文字颜色

【说明】导入的Excel 字体颜色和背景色只能识别【标准色】,别的如"主题颜色",exceljs 解析出来不是颜色值

 原Excel样式如下

 导入到 x-data-spreadsheet 如下图

 完整代码如下

<template>

  <div>

    <div>

      <input

        type="file"

        @change="loadExcelFile"

        accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"

      />

      <button @click="exportJson">导出JSON</button>

      <button @click="exportExcel">导出xlsx</button>

    </div>

    <!--web spreadsheet组件-->

    <div ref="sheetContainer" id="x-spreadsheet-demo"></div>

  </div>

</template>

 

<script>

// 在spreadsheet.js中初始化json数据 https://blog.csdn.net/CBGCampus/article/details/125366246

// 带样式导出 https://blog.csdn.net/weixin_42302145/article/details/121476579

// 引入依赖包

import Spreadsheet from "x-data-spreadsheet";

import zhCN from 'x-data-spreadsheet/src/locale/zh-cn'

import _ from "lodash";

import * as XLSX from "xlsx";

import * as Excel from 'exceljs/dist/exceljs'

import * as tinycolor from "tinycolor2";

export default {

  name: "xspreadsheet-demo",

  data() {

    return {

      xs: null,

      jsondata: {

        type: "",

        label: "",

      },

    };

  },

  mounted() {

    this.init();

  },

  methods: {

    init() {

      //设置中文

      Spreadsheet.locale("zh-cn", zhCN);

      this.xs = new Spreadsheet("#x-spreadsheet-demo", {

        mode: "edit",

        showToolbar: true,

        showGrid: true,

        showContextmenu: true,

        showBottomBar: true,

        view: {

          height: () => this.$refs.sheetContainer.offsetHeight,

          width: () => this.$refs.sheetContainer.offsetWidth,

        },

        formats: [],

        fonts: [],

        formula: [],

        row: {

          len: 100,

          height: 25,

        },

        col: {

          len: 26,

          width: 100,

          indexWidth: 60,

          minWidth: 60,

        },

        style: {

          bgcolor: "#ffffff",

          align: "left",

          valign: "middle",

          textwrap: false,

          textDecoration: "normal",

          strikethrough: false,

          color: "#0a0a0a",

          font: {

            name: "Helvetica",

            size: 10,

            bold: false,

            italic: false,

          }

        },

      })

        .loadData([])

        .change((cdata) => {

          // console.log(cdata);

          console.log(">>>", this.xs.getData());

        });

 

      this.xs

        .on("cell-selected", (cell, ri, ci) => {

          console.log("cell:", cell, ", ri:", ri, ", ci:", ci);

        })

        .on("cell-edited", (text, ri, ci) => {

          console.log("text:", text, ", ri: ", ri, ", ci:", ci);

        });

 

      setTimeout(() => {

        // xs.loadData([{ rows }]);

        // xs.cellText(14, 3, 'cell-text').reRender();

        // console.log('cell(8, 8):', this.xs.cell(8, 8));

        // console.log('cellStyle(8, 8):', this.xs.cellStyle(8, 8));

      }, 5000);

    },

    transRgba(rgba) {

          let result = '';

          let reg = /\(.*\)/;  //字符串匹配括号内的子串

          let arr = []

          result = reg.exec(rgba)[0];  // 截取'(255,255,255,0.6)'

          result = result.substr(1, result.length - 2); //截取十进制的'255,255,255,0.6'

          arr = result.split(','); //字符串切割 ['255','255','255','0.6']

          for (let i = 0; i < arr.length; i++) {

              arr[i] = parseFloat(arr[i]); //字符串类型转为浮点数类型

              if (i == arr.length - 1) {  //对于最后一个透明度数据,需要先将0-1的数值*255

                  arr[i] = arr[i] * 255;

              }

  

              arr[i] = trans10to16(arr[i]);

          }

          return arr.join('');

    },

    // 向x-spreadsheet 带样式导入Excel

      loadExcelFile(e) {

      const wb = new Excel.Workbook();

      const reader = new FileReader()

      reader.readAsArrayBuffer(e.target.files[0])

      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 = []

          console.log(workbook)

          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] }

                  sheetData.styles.push(cell.style)

                  //对应的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 }

                  //解析单元格,包含样式

                  sheetData.styles.push(cell.style)

                  //对应的style存放序号

                  sheetData.rows[(rowIndex - 1).toString()].cells[(colNumber - 1).toString()].style = sheetData.styles.length - 1

                }

              });

            })

            workbookData.push(sheetData)

          })

          this.xs.loadData(workbookData);

        })

      }

    },

    // 导出excel

    exportExcel() {

      let new_wb = XLSX.utils.book_new();

      this.xs.getData().forEach(function (xws) {

        let aoa = [[]];

        let rowobj = xws.rows;

        for (let ri = 0; ri < rowobj.len; ++ri) {

          let row = rowobj[ri];

          if (!row) continue;

          aoa[ri] = [];

          Object.keys(row.cells).forEach(function (k) {

            let idx = +k;

            if (isNaN(idx)) return;

            aoa[ri][idx] = row.cells[k].text;

          });

        }

        let ws = XLSX.utils.aoa_to_sheet(aoa);

        XLSX.utils.book_append_sheet(new_wb, ws, xws.name);

      });

      XLSX.writeFile(new_wb, "调配单变量配置.xlsx");

    },

    // 导出为 JSON

    exportJson() {

      let sheetsData = this.xs.getData();

      let rows = Object.entries(sheetsData[0].rows);

      let objectProperties = [

        "Index",

        "OrderIndex",

        "OrderNo",

        "ProductName",

        "OrderStatus",

      ];

      let jsonData = [];

      // 遍历数据,跳过第一行表头

      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 = {

            Index: null,

            OrderIndex: null,

            OrderNo: null,

            ProductName: null,

            OrderStatus: null,

          };

          for (let k = 0; k < row.length; k++) {

            let cells = row[k];

            JsonRow[objectProperties[k]] = cells[1].text;

          }

          jsonData.push(JsonRow);

        }

      }

      console.log(jsonData);

    },

    stox(wb) {

      var out = [];

      wb.SheetNames.forEach(function (name) {

        var o = { name: name, rows: {} };

        var ws = wb.Sheets[name];

        var aoa = XLSX.utils.sheet_to_json(ws, { raw: false, header: 1 });

        aoa.forEach(function (r, i) {

          var cells = {};

          r.forEach(function (c, j) {

            cells[j] = { text: c };

          });

          o.rows[i] = { cells: cells };

        });

        out.push(o);

      });

      return out;

    },

    fixData(data) {

      var o = "",

        l = 0,

        w = 10240;

      for (; l < data.byteLength / w; ++l)

        o += String.fromCharCode.apply(

          null,

          new Uint8Array(data.slice(l * w, l * w + w))

        );

      o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)));

      return o;

    },

  },

};

</script>

<style 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;

  }

}

</style>


该文章在 2024/6/12 12:21:34 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2024 ClickSun All Rights Reserved