vue纯前端实现表格不规则的合并行和合并列,设置某一行的字体颜色;表格导出的时候设置列宽和删除指定列,设置多个shett页。

648 阅读2分钟

再也不要跟产品说:‘这个功能实现不了了’,记录一下,有需要的可以看看提供各思路。
实现功能:
(1)合并行和合并列
(2)设置指定行的样式
(3)导出删除指定列
(4)导出设置多个sheet
(5)导出指定列宽

像标题所说,可以大致分位两个主要功能点。

第一:用的是饿了么ui里面的表格,el-table的不规则的合并行和列(红框和绿框),并可以设置指定行的样式(蓝色字体)。不多bb,看下图:

image.png

说一下实现思路:
1、颜色是用 :row-class-name="" 这个属性
2、表头行合并是用 :header-cell-style=""
3、列的合并是用: :span-method=""

上一下代码吧:

  <el-table
    class="table"
  	:data="dataSource"
  	:border="true"
        size='mini'
  	:cell-style="{ 'text-align': 'center', 'padding':'0px' }"
  	style="width: 100%"
        :row-class-name="tableRowClassName"
  	:header-cell-style="rowClass"
  	:span-method="arraySpanMethod"
  >
  	<el-table-column prop="categoryName" label="类别" width="100" :key='tabPosition+"1"'></el-table-column>
  	<el-table-column prop="childCategoryName" label="类别2" width="100" show-overflow-tooltip :key='tabPosition+"2"'></el-table-column>
  	<el-table-column prop="contentName" label="工作内容" show-overflow-tooltip :key='tabPosition+"3"'></el-table-column>
  	<el-table-column prop="resultName" label="成果输出" width="280" show-overflow-tooltip :key='tabPosition+"4"'></el-table-column>
  	<el-table-column prop="isComplete" label="是否完成" width="100" :key='tabPosition+"5"'>
      <template slot-scope="scope">
      	<span :style="scope.row.isComplete=='是'?'color: #16aa20;':'' ">{{scope.row.isComplete}}</span>
      </template>
    </el-table-column>
  	<el-table-column prop="flowStatus" label="流程状态" width="100" :key='tabPosition+"6"'></el-table-column>
  	<el-table-column label="附件材料" width="100" :key='tabPosition+"7"'>
  		<template slot-scope="scope">
        <el-button type="text" size="small" @click="toDetail(scope.row)">{{isShowSee?scope.row.material:'查看'}}</el-button>
  		</template>
  	</el-table-column>
  </el-table>

</template>

<script>
  // import { queryRecord } from '@/api/index'
export default {
  name: "print",
  props: {
    dataSource:{
      type:Array
    },
    tabPosition:{
      type:String
    },
    // 清单查看进来的都要显示'查看'俩字
    isShowSee:{
      type:Boolean,
      default:true
    }
  },
  data() {
    return {

    };
  },
  watch:{
    tabPosition(val){
      console.log(val)
    }
  },
  created() {
  },
  methods: {
    toDetail(row){
        this.$emit('toDetail',row)
    },
    //合并行
    arraySpanMethod({ row, column, rowIndex, columnIndex }) {
        // 合并第一列的行  产品名字(categoryId)相同合并
        if (columnIndex === 0) {
            if(rowIndex ===0 || row.categoryId != this.dataSource[rowIndex-1].categoryId){
                let rowspan = 0;
                this.dataSource.forEach(element => {
                    if(element.categoryId === row.categoryId){
                        rowspan ++;
                    }
                });
                return [rowspan, 1];
            }else {
                return [0, 0];
            }
        }else if (columnIndex === 1) {
          // 合并第二列的行  产品名字(childCategoryId)相同合并
            if(rowIndex ===0 || row.childCategoryId != this.dataSource[rowIndex-1].childCategoryId){
                let rowspan = 0;
                // 从当前index往后找一样的,不是从头找
               for(let i=rowIndex;i<this.dataSource.length;i++){
                if(this.dataSource[i].childCategoryId === row.childCategoryId){
                  rowspan ++;
                }
               }
                if(rowIndex !=0){
                    return [rowspan, 1];
                }else{
                    return [rowspan, 1];
                }

             }else {
                return [0, 0];
                }
         }else if (columnIndex === 2) {
         // 合并第三列的行  产品名字(contentId)相同合并
            if(rowIndex ===0 || row.contentId != this.dataSource[rowIndex-1].contentId){
         let rowspan = 0;
         // 从当前index往后找一样的,不是从头找
          for(let i=rowIndex;i<this.dataSource.length;i++){
                if(this.dataSource[i].contentId === row.contentId){
                    rowspan ++;
                }
          }
            if(rowIndex !=0){
                return [rowspan, 1];
            }else{
                return [rowspan, 1];
            }
                }else {
                  // 找出下一个和前一个相同的合
                    return [0, 0];
                }
            }
    },
    //合并列 前两列的合并
    rowClass({ column,rowIndex,columnIndex}){
     if(rowIndex === 0 && columnIndex === 0){
        this.$nextTick(() =>{
        document.getElementsByClassName(column.id)[0].setAttribute('colSpan',2);
        })  //  一定要写在加载完毕后,nextTick 更新的最晚,才能获取到dom节点。
        }
        if (rowIndex === 0 && columnIndex === 1){
            return {display:'none'}
        }
            return { 'text-align': 'center' }
    },
    // 颜色
    tableRowClassName({row, rowIndex}) {
        if(row.hasOwnProperty('isProgress')){
          if (row.isProgress==true||row.isProgress=='true') {
            return 'success-row';
          } else {
            return '';
          }
          return '';
        }
      }

  }
};
</script>

<style lang="scss" scoped>
.table  ::v-deep tr.success-row{
  color: #2e77ff;
}

</style>

上面是一个组件,在页面中引用:

<div class="main" v-show="tabPosition==navTab[0].id">
    <customTable :dataSource="dataSource1" @toDetail="toDetail" ref="exportTableRef" id="file1"/>
</div>
<div class="main" v-show="tabPosition==navTab[1].id">
    <customTable :dataSource="dataSource2" @toDetail="toDetail" ref="exportTableRef" id="file2"/>
</div>
<div class="main" v-show="tabPosition==navTab[2].id">
    <customTable :dataSource="dataSource3" @toDetail="toDetail" ref="exportTableRef" id="file3"/>
</div>

第二点:表格导出设置列宽和删除列,设置多个sheet页,这里是兼容到IE9吧,先看下效果

image.png

这里用到了两个插件:

安装插件

npm install -S file-saver xlsx

npm install -D script-loader

下面是导出的js文件

import FileSaver from "file-saver";
import * as XLSX from 'xlsx'
// import XLSX from 'xlsx'

export function exportPlanTable(tableDom1,tableDom2,tableDom3) {

  let tableHeader1 = tableDom1.querySelector('.el-table__header-wrapper');
  let tableBody1 = tableDom1.querySelector('.el-table__body');
  tableHeader1.childNodes[0].appendChild(tableBody1.childNodes[1]);
  let headerDom1 = tableHeader1.childNodes[0].querySelectorAll('th');
  // 移除头部
  for (let key in headerDom1) {
    if (headerDom1[key].innerText === '附件材料') {
      if (isIE() || isIE11()) {
        headerDom1[key].removeNode(true);
      } else {
        headerDom1[key].remove();
      }
    }
    if (headerDom1[key].innerText === '类别2') {
      if (isIE() || isIE11()) {
        headerDom1[key].removeNode(true);
      } else {
        headerDom1[key].remove();
      }
    }
  }
  // 移除附件材料的内容
  let tableList1 = tableHeader1.childNodes[0].childNodes[2].querySelectorAll('td');
  for (let key = 0; key < tableList1.length; key++) {
    if (tableList1[key].innerText === '查看') {
      if (isIE() || isIE11()) {
        tableList1[key].removeNode(true);
      } else {
        tableList1[key].remove();
      }
    }
  }
  XLSX.utils.table_to_book(tableHeader1)

  // let tableDom2 = document.querySelector("#file2").cloneNode(true);
  let tableHeader2 = tableDom2.querySelector('.el-table__header-wrapper');
  let tableBody2 = tableDom2.querySelector('.el-table__body');
  tableHeader2.childNodes[0].appendChild(tableBody2.childNodes[1]);
  let headerDom2 = tableHeader2.childNodes[0].querySelectorAll('th');
  // 移除头部
  for (let key in headerDom2) {
    if (headerDom2[key].innerText === '附件材料') {
      if (isIE() || isIE11()) {
        headerDom2[key].removeNode(true);
      } else {
        headerDom2[key].remove();
      }
    }
    if (headerDom2[key].innerText === '类别2') {
      if (isIE() || isIE11()) {
        headerDom2[key].removeNode(true);
      } else {
        headerDom2[key].remove();
      }
    }
  }
  // 移除附件材料的内容
  let tableList2 = tableHeader2.childNodes[0].childNodes[2].querySelectorAll('td');
  for (let key = 0; key < tableList2.length; key++) {
    if (tableList2[key].innerText === '查看') {
      if (isIE() || isIE11()) {
        tableList2[key].removeNode(true);
      } else {
        tableList2[key].remove();
      }
    }
  }
  XLSX.utils.table_to_book(tableHeader2)

  // let tableDom3 = document.querySelector("#file3").cloneNode(true);
  let tableHeader3 = tableDom3.querySelector('.el-table__header-wrapper');
  let tableBody3 = tableDom3.querySelector('.el-table__body');
  tableHeader3.childNodes[0].appendChild(tableBody3.childNodes[1]);
  let headerDom3 = tableHeader3.childNodes[0].querySelectorAll('th');
  // 移除头部
  for (let key in headerDom3) {
    if (headerDom3[key].innerText === '附件材料') {
      if (isIE() || isIE11()) {
        headerDom3[key].removeNode(true);
      } else {
        headerDom3[key].remove();
      }
    }
    if (headerDom3[key].innerText === '类别2') {
      if (isIE() || isIE11()) {
        headerDom3[key].removeNode(true);
      } else {
        headerDom3[key].remove();
      }
    }
  }
  // 移除附件材料的内容
  let tableList3 = tableHeader3.childNodes[0].childNodes[2].querySelectorAll('td');
  for (let key = 0; key < tableList3.length; key++) {
    if (tableList3[key].innerText === '查看') {
      if (isIE() || isIE11()) {
        tableList3[key].removeNode(true);
      } else {
        tableList3[key].remove();
      }
    }
  }
  XLSX.utils.table_to_book(tableHeader3)

  var xlsxParam = {
      raw: true
  }; //转换成excel时,使用原始的格式,这样导出的时候数字过长不会变成科学计数法
  let workbook = XLSX.utils.book_new(); //创建一个工作薄
  // 这里可以添加多个sheet页
  let ws1 = XLSX.utils.table_to_sheet(tableDom1, xlsxParam);
  XLSX.utils.book_append_sheet(workbook, ws1, '雏鹰计划'); //将工作表添加到工作簿
  let ws2 = XLSX.utils.table_to_sheet(tableDom2, xlsxParam);
  XLSX.utils.book_append_sheet(workbook, ws2, '菁鹰计划');
  let ws3 = XLSX.utils.table_to_sheet(tableDom3, xlsxParam);
  XLSX.utils.book_append_sheet(workbook, ws3, '雄鹰计划');
  // 设置列宽
  let wscols1 =  [
      { wpx : 60 },
      { wpx : 60 },
      { wpx : 440 },
      { wpx : 240 },
      { wpx : 100 },
      { wpx : 100 }
  ];
  ws1["!cols"] = wscols1
  ws2["!cols"] = wscols1
  ws3["!cols"] = wscols1
  let wbout = XLSX.write(workbook, {
      bookType: 'xlsx',
      bookSST: true,
      type: 'array'
  });
  try {
      FileSaver.saveAs(
      new Blob([wbout], {
          type: 'application/octet-stream;charset=utf-8"'
      }), '清单.xlsx');
  } catch (e) {
      if (typeof console !== 'undefined') console.log(e, wbOut);
  }
  return wbout;


}

/**
 * 判断是否是IE
 */
function isIE() {
  if (!!window.ActiveXobject || "ActiveXObject" in window) {
    return true;
  } else {
    return false;
  }
}
/**
 * 判断是否是IE11
 */
function isIE11(){
  if((/Trident\/7\./).test(navigator.userAgent)) {
    return true;
  } else {
    return false;
  }
}

页面上引用的时候:

   exportTable(){
      let tableDom1 = document.querySelector("#file1").cloneNode(true);
      let tableDom2 = document.querySelector("#file2").cloneNode(true);
      let tableDom3 = document.querySelector("#file3").cloneNode(true);
      exportPlanTable(tableDom1,tableDom2,tableDom3)
    },

这里没有把删除列的名字和sheet页的名字封装出来,有时间可以自己搞一下啊,仅是提供下思路,优化的地方比较多。