Bootstrap

【AntDesignVue | Table】纯前端表格 导入导出 Table表单复杂表头规则匹配模型

项目场景:

1,ant-design-vue 使用表单导入展示数据
2,项目需求显示为如下图所示,故涉及复杂表头的处理和显示
3,本篇文章主要以复杂表头的处理讲解为主,简单表头导入导出均引入xlsx之类的插件即可使用

循环对应产品属性的参与者信息并且标记填充展示
展示


功能分析:

1,导出的效果,如果用原生自带的程序,是不会将两级复杂表头导出的,会默认展示两层表头的第二层,比如这样;

由于没有一级表头的区分,故二级的信息是只会展示出一类,但是那样导出会出现信息错乱和覆盖,导致数据缺失,达不到理性效果。
展示图正确展示效果
展示
2,导入的效果,如果是由此类复杂表头导入解析的数据则需要对表单的表头进行分析和逻辑处理操作,达到获取想要的数据集。

项目效果展示,根据所需逻辑处理展示数据,包括覆盖数据,替换数据,替换表头参数,替换数据容器等…
展示


功能实现:

npm 引入
//用于对xlsx数据操作
npm install xlsx
//用于table自定义导出
npm install file-saver
npm install better-xlsx

前端代码

<input type="file" @change="importFile(this)" id="imFile" style="display: none" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"/>

<a-button type="primary" @click="GetExportExcel">导出文件</a-button>
<a-button type="primary" @click="PushEx()">导入文件</a-button>

JS调用

复杂表头方案可参考官方示例:https://antdv.com/components/table-cn/#components-table-demo-grouping-table-head

//columns 表格cloums的展示头信息
var columns = []
data(){
	return {
		columns,
		imFile:''
	}
},
mounted () {
 this.imFile = document.getElementById('imFile');//导入文件实例
}
// 导出表单
GetExportExcel(){
  	ExportExcel(columns,this.data,'标价记录表单');
},
PushEx: function () { // 点击导入按钮
 	this.imFile.click()
},
//复杂表头解析方案
async importFile() { // 导入excel
    this.fullscreenLoading = true
    let obj = this.imFile
    if (!obj.files) {
      this.fullscreenLoading = false
      return
    }
    var f = obj.files[0]
    let datainfo = await readerExcel(f,columns);
    // return console.log('读取到的数据',datainfo);
    let hasemenu = [];
    for(let i in datainfo[0]){
        if(i>0) hasemenu.push(datainfo[0][i])
    }
    let changmenu = this.data;
    //请开发者自行更改以下处理数据代码
    for(var y=0;y<changmenu.length;y++){
            for(var x=0;x<this.gys.length;x++){
        for(var z=0;z<hasemenu.length;z++){
            let n = x>0?'1':'';
                if(changmenu[y]['Sid_'+x] == hasemenu[z]['公司编码'+n] && changmenu[y]['name']==hasemenu[z]['产品名称']){
                    changmenu[y]['SellPrice_'+x] = hasemenu[z]['报价'+n];
                }
            }
        }
    }
    // console.log(changmenu)
    this.data = [];
    this.data = changmenu;
    console.log(this.data,'最后渲染结果集')
    return
  },

导出功能JS文件:
可以将此JS文件作为一个公共方法放置静态文件;

程序使用(博主项目在config文件目录下)

import ExportExcel from "@/config/excelpush";//导出获取表单
import ReaderExcel from "@/config/getin";//导入读取数据
var XLSX = require('xlsx');//引入的xlsx方法处理形式
import { File } from 'better-xlsx';
import { saveAs } from 'file-saver';

function ExportExcel(column, dataSource, fileName = 'example') {
  // 新建工作谱
  const file = new File();
  dataSource = JSON.parse(JSON.stringify(dataSource));
  // 新建表
  let sheet = file.addSheet('sheet-test');
  // 获取表头行数
  let depth = getDepth(column);
  // 获取表头的列数
  let columnNum = getColumns(column);
  // 新建表头行数
  let rowArr = [];
  for (let k = 0; k < depth; k++) {
    rowArr.push(sheet.addRow());
  }
  // 根据列数填充单元格
  rowArr.map(ele => {
    for (let j = 0; j < columnNum; j++) {
      let cell = ele.addCell();
      cell.value = j;
    }
  });
  // 初始化表头
  init(column, 0, 0);
  // 按顺序展平column
  let columnLineArr = [];
  columnLine(column);
  // 根据column,将dataSource里面的数据排序,并且转化为二维数组
  let dataSourceArr = [];
  for(var x=0;x<dataSource.length;x++){
    for(let info in dataSource[x]){
      console.log(info)
      //此处可对导出的table表格数据做数据操作处理
      /*
      if(info.indexOf('IsActive_')>=0){
        console.log(dataSource[x][info])
        dataSource[x][info]=dataSource[x][info]?'中标':'';
        console.log(dataSource[x]);
      }
      */
    }
}
  dataSource.map(ele => {
    let dataTemp = [];
    columnLineArr.map(item => {
      dataTemp.push({
        [item.dataIndex]: ele[item.dataIndex],
        value: ele[item.dataIndex],
      });
    });
    dataSourceArr.push(dataTemp);
  });
  // debugger;
  // 绘画表格数据
  dataSourceArr.forEach((item, index) => {
    //根据数据,创建对应个数的行
    let row = sheet.addRow();
    row.setHeightCM(0.8);
    //创建对应个数的单元格
    item.map(ele => {
      let cell = row.addCell();
      if (ele.hasOwnProperty('num')) {
        cell.value = index + 1;
      } else {
        cell.value = ele.value;
      }
      cell.style.align.v = 'center';
      cell.style.align.h = 'center';
    });
  });
  //设置每列的宽度
  for (var i = 0; i < 4; i++) {
    sheet.col(i).width = 20;
  }
  file.saveAs('blob').then(function(content) {
    saveAs(content, fileName + '.xlsx');
  });

  // 按顺序展平column
  function columnLine(column) {
    column.map(ele => {
      if (ele.children === undefined || ele.children.length === 0) {
        columnLineArr.push(ele);
      } else {
        columnLine(ele.children);
      }
    });
  }
  // 初始化表头
  function init(column, rowIndex, columnIndex) {
    column.map((item, index) => {
      let hCell = sheet.cell(rowIndex, columnIndex);
      // 如果没有子元素, 撑满列
      if (item.title === '操作') {
        hCell.value = '';
        return;
      } else if (item.children === undefined || item.children.length === 0) {
        // 第一行加一个单元格
        hCell.value = item.title;
        hCell.vMerge = depth - rowIndex - 1;
        hCell.style.align.h = 'center';
        hCell.style.align.v = 'center';
        columnIndex++;
        // rowIndex++
      } else {
        let childrenNum = 0;
        function getColumns(arr) {
          arr.map(ele => {
            if (ele.children) {
              getColumns(ele.children);
            } else {
              childrenNum++;
            }
          });
        }
        getColumns(item.children);
        hCell.hMerge = childrenNum - 1;
        hCell.value = item.title;
        hCell.style.align.h = 'center';
        hCell.style.align.v = 'center';
        let rowCopy = rowIndex;
        rowCopy++;
        init(item.children, rowCopy, columnIndex);
        // 下次单元格起点
        columnIndex = columnIndex + childrenNum;
      }
    });
  }
  // 获取表头rows
  function getDepth(arr) {
    const eleDepths = [];
    arr.forEach(ele => {
      let depth = 0;
      if (Array.isArray(ele.children)) {
        depth = getDepth(ele.children);
      }
      eleDepths.push(depth);
    });
    return 1 + max(eleDepths);
  }

  function max(arr) {
    return arr.reduce((accu, curr) => {
      if (curr > accu) return curr;
      return accu;
    });
  }
  // 计算表头列数
  function getColumns(arr) {
    let columnNum = 0;
    arr.map(ele => {
      if (ele.children) {
        getColumns(ele.children);
      } else {
        columnNum++;
      }
    });
    return columnNum;
  }
}

export default ExportExcel;

导入功能JS文件

此功能需要开发者自行获取表单的信息进行匹配
代码如下:

export function readerExcel(f, headerCodes) {
    //自定义函数
    var ret= function(arr){
        var ress=[arr[0]];
        for(var j=1;j<arr.length;j++){
            var repeat= false;
            let x=1;
            for(var i=0;i<ress.length;i++){
                if(arr[j]==ress[i]){
                    repeat=true;
                    ress.push(arr[j]+x);
                    x++
                    break;
                }
            }
            if(!repeat){
                ress.push(arr[j]);
            }
        }
        return ress;
    }
    return new Promise(resolve => {
      const res = [] // 组装表格数据对象
      let binary = ''
      let wb // 读取完成的数据
      let outData
      const reader = new FileReader()
      reader.onload = function(e) {
        const bytes = new Uint8Array(reader.result)
        const length = bytes.byteLength
        for (let i = 0; i < length; i++) {
          binary += String.fromCharCode(bytes[i])
        }
        const XLSX = require('xlsx')
        wb = XLSX.read(binary, {
          type: 'binary'
        })
        const sheetArr = wb.Sheets // excel的sheet
        //此处操作是为了将表格的空位置替换为""
		wb.SheetNames.map((sheetName, sheetIndex) => {
		const sheet2JSONOpts = {
		/** Default value for null/undefined values */
		defval: ''//给defval赋值为空的字符串
		}
		/*
			{header:1,defval:''} 
			此处操作为将表格的表头位置,空位置替换为""
		*/
        outData = XLSX.utils.sheet_to_json(sheetArr[sheetName],{header:1,defval:''})
        //outData 为解析的初始表格数据
        //此处为博主项目所需数据模型,开发者可自行更改 -start
        outData[1][0] = outData[0][0]
        //上一行代码是为了处理项目展示逻辑的处理方案,替换第二级目录表头的空位
        let newArr_cs = []
        newArr_cs = ret(outData[1]);
        const arr = []
        for(var x=1;x<outData.length;x++){
            let obj = {}
            for(var y=0;y<newArr_cs.length;y++){  
                obj[newArr_cs[y]] = outData[x][y];
            }
            arr.push(obj)
        }
        //此处为博主项目所需数据模型,开发者可自行更改 -end
          res.push(arr)
        })
        resolve(res)
      }
      reader.readAsArrayBuffer(f)
    })
  }

  export default readerExcel;

内容总结:

1,导出数据模型的展示;
2,导入数据的逻辑处理;

;