先描述一下需求,看下图

elementui table 单元格合并 element table 合并行_数据

1:动态合并行,如图所示,第一列全部合并,第二列和第三列第四列根据名称进行合并

2:可以动态编辑第三列和第四列,并且只能编辑属于此合并行的第三列和第四列,比如第一个编辑按钮点击后,只有前三行可以编辑

3:第一列的数据是根据第三列相加得来的

4:点击快捷填写,可以快速填写第三列和第四列

思路

1:第二列和第三列第四列根据名称进行合并,后台根据名称进行排序,前台通过循环解析名称来知道哪一行需要合并,这个从网上找了一个自己改了下。

 第一列特殊情况根据上面的思路做简单修改

element代码: span-method="cellMerge"

<el-table
      border
      :data="tableData3"
      height="100%"
      width="100%"
      :cell-style="cellStyle"
      :row-style="getRowClass"
      :header-row-style="getRowClass"
      :header-cell-style="getRowClass"
      :span-method="cellMerge"
    >

vue代码:

//合并单元格,此方法需要后台进行名字进行排序
    cellMerge({ row, column, rowIndex, columnIndex }) {
      let length = this.tableData3.length;
      //第0列比较特殊,单独合并
      if (columnIndex === 0) {
        const _row = this.spanArrOne[rowIndex];
        const _col = _row > 0 ? 1 : 0;
        return {
          rowspan: _row,
          colspan: _col
        };
      }
      //1 2 5列进行合并
      if (columnIndex === 1 || columnIndex === 2 || columnIndex === 5) {
        const _row = this.spanArr[rowIndex];
        const _col = _row > 0 ? 1 : 0;
        return {
          rowspan: _row,
          colspan: _col
        };
      }
    },
    //1 2  5列合并的数据
    getSpanArr(data) {
      for (var i = 0; i < data.length; i++) {
        if (i === 0) {
          this.spanArr.push(1);
          this.pos = 0;
        } else {
          // 判断当前元素与上一个元素是否相同
          if (data[i].name === data[i - 1].name) {
            this.spanArr[this.pos] += 1; //需要合并的行数
            this.spanArr.push(0); //新增被合并的行
          } else {
            this.spanArr.push(1);
            this.pos = i; //新的需要合并的第几行数
          }
        }
      }
    },
    //0列合并的数据
    getSpanArrOne(data) {
      for (var i = 0; i < data.length; i++) {
        if (i === 0) {
          this.spanArrOne.push(1);
          this.posOne = 0;
        } else {
          this.spanArrOne[this.posOne] += 1; //需要合并的行数
          this.spanArrOne.push(0); //新增被合并的行
        }
      }

      console.log(this.spanArrOne, " this.spanArrOne");
    },

2:可以动态编辑第三列和第四列解决:

0-2行是0  3到5行是3  6到9行是6以此类推,可以发现其规律就是第一个开头的行,当你点击第一个编辑按钮的时候,传的index就是0,所以根据flagNum匹配0-2行可以编辑,下面的是一样的逻辑

element代码;

<el-table-column prop="value1" label="XXX" min-width="15%" align="center">
        <template slot-scope="scope">
          <template v-if="scope.row.editing">
            <el-input class="edit-input" v-model="scope.row.value1"></el-input>
          </template>
          <span v-else>{{ scope.row.value1 }}</span>
        </template>
      </el-table-column>

vue代码:

//判断可编辑作用域 用到的数据spanArr4Edit
    getSpanArr4Edit(data) {
      let ctx = this;
      var flagNum = 0;

      for (var i = 0; i < data.length; i++) {
        //首先不能编辑
        ctx.$set(data[i], "editing", false);

        if (i === 0) {
          ctx.spanArr4Edit.push(flagNum);
        } else {
          // 判断当前元素与上一个元素是否相同
          if (data[i].name === data[i - 1].name) {
            ctx.spanArr4Edit.push(flagNum); //还是同一个记录
          } else {
            ctx.spanArr4Edit.push(i);
            flagNum = i;
          }
        }
        //与编辑相对应
        ctx.$set(data[i], "flagNum", flagNum);
      }
      //最终赋值
      ctx.tableData3 = data;
      console.log(ctx.spanArr4Edit, "this.spanArr4Edit");
    },
handleEdit(index, row) {
      let ctx = this;
      this.setEditFlag(index, 1);
      console.log(index);
 
    },
/编辑输入框显示与隐藏0 隐藏 1显示
    setEditFlag: function(index, flag) {
      let ctx = this;
      let datalength = ctx.tableData3.length;
      let flagNum = ctx.spanArr4Edit[index];

      for (let i = 0; i < datalength; i++) {
        if (flag == 1) {
          if (flagNum === ctx.tableData3[i].flagNum) {
            //循环遍历改变可编辑的标记
            this.$set(ctx.tableData3[i], "editing", true);
          }
        } else {
          //循环遍历改变可编辑的标记
          this.$set(ctx.tableData3[i], "editing", false);
        }
      }
    }

3:第一列的数据是根据第三列相加得来的

可以用监听器深度监听后台传过来的数据

代码:

watch: {
    tableData3: {
      handler(newValue, oldValue) {
        let ctx = this;
        let length = oldValue.length;
        var num = 0;

        if (length > 0) {
          for (let i = 0; i < length; i++) {
            //取出编辑后的对象数据
            num = Number(num) + Number(ctx.tableData3[i].value1);
          }
          if (num != 0) {
             //赋值对象数据
            ctx.tableData3[0].all = num;
          } else {
            ctx.tableData3[0].all = "";
          }
        }
      },
      deep: true //深度监听对象里面的属性
    }
  },

4;此逻辑比较简单,就是弹出个输入框,点击确认把值带到这个页面进行填写

直接上代码:

//快捷填写保存
    addSubmit: function() {
      let ctx = this;
      //赋值数据,取出哪一行需要进行赋值
      let idx = this.quickFlag;

      let datalength = ctx.tableData3.length;
      //每次快捷填写某一个区域,所以此变量在此定义
      var initFlag = 0;
      for (let i = 0; i < datalength; i++) {
        if (idx === ctx.tableData3[i].flagNum) {
          if (initFlag == 0) {
            //value1 只赋值一次就行
            this.$set(ctx.tableData3[i], "value1", this.addform.w);
          }
          initFlag++;
          this.$set(ctx.tableData3[i], "value2", this.addform.q);
        }
      }
      this.dialogQuick = false;
      ctx.$message.success("操作成功");
      console.log(this.addform, "this.addform");
    },

-----------------------------------分割线------------------------------------------------

贴上完整代码 

<template>
  <div class="waterApplyTable">

    <el-table
      border
      :data="tableData3"
      height="100%"
      width="100%"
      :cell-style="cellStyle"
      :row-style="getRowClass"
      :header-row-style="getRowClass"
      :header-cell-style="getRowClass"
      :span-method="cellMerge"
    > 
    <el-table-column  prop="title"  :label="title" align="center">
      <el-table-column  prop="all" label="xx" min-width="20%">
        <template slot-scope="scope">
          <span>{{ scope.row.all }}</span>
        </template>
      </el-table-column>
      <el-table-column prop="name" label="xx" min-width="15%" align="center" show-overflow-tooltip></el-table-column>
      <el-table-column prop="value1" label="xx" min-width="15%" align="center">
        <template slot-scope="scope">
          <template v-if="scope.row.editing">
            <el-input class="edit-input" v-model="scope.row.value1"></el-input>
          </template>
          <span v-else>{{ scope.row.value1 }}</span>
        </template>
      </el-table-column>
      <el-table-column prop="value2" label="xx" min-width="15%" align="center">
        <template slot-scope="scope">
          <template v-if="scope.row.editing">
            <el-input class="edit-input" v-model="scope.row.value2"></el-input>
          </template>
          <span v-else>{{ scope.row.value2 }}</span>
        </template>
      </el-table-column>
      <el-table-column prop="value3" label="xx" min-width="15%" align="center"></el-table-column>

      <el-table-column prop="editing" label="操作" min-width="20%" align="center">
        <template slot-scope="scope">
          <el-button
            type="primary"
            v-if="!scope.row.editing"
            @click.native="handleEdit(scope.$index, scope.row)"
          >编辑</el-button>

          <el-button type="primary" v-else @click.native="savemodify(scope.$index, scope.row)">保存</el-button>

          <el-button type="primary" @click.native="clickTodo(scope.$index, scope.row)">快捷填写</el-button>
        </template>
      </el-table-column>
    </el-table-column>
    </el-table>

    <el-dialog
      class="dialog"
      :visible="dialogQuick"
       top="15vh"
      @close="dialogQuickClose"
      width="20%"
      title="快捷填写"
      :append-to-body="true"
    >
      <el-form :model="addform" class="demo-form-inline">
        <el-form-item label="x">
          <el-input v-model.trim="addform.w"></el-input>
        </el-form-item>
        <el-form-item label="xx">
          <el-input v-model.trim="addform.q"></el-input>
        </el-form-item>
      </el-form>

      <div slot="footer" class="dialog-footer">
        <el-button @click.native="addSubmit">确定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "waterApplyTable",
  props: {
    total: {
      type: Number,
      default: 100
    }
  },
  data() {
    return {
      rules: {},
      page: 1,
      pageSize: 20,
      pageCount: 10,
      formInline: {
        area: "青岛市",
        year: "2018-2019"
      },
      tableData3: [],
      dialogQuick: false,
      addform: {},
      spanArr: [],
      pos: "",
      spanArrOne: [],
      posOne: "",
      spanArr4Edit: [],
      pos4Edit: "",
      //快捷填写flag,记录哪一行数据的快捷填写
      quickFlag: "",
      title:'我是表头',
    };
  },
  watch: {
    tableData3: {
      handler(newValue, oldValue) {
        let ctx = this;
        let length = oldValue.length;
        var num = 0;

        if (length > 0) {
          for (let i = 0; i < length; i++) {
            num = Number(num) + Number(ctx.tableData3[i].value1);
          }
          if (num != 0) {
            ctx.tableData3[0].all = num;
          } else {
            ctx.tableData3[0].all = "";
          }
        }
      },
      //immediate: true,
      deep: true //深度监听对象里面的属性
    }
  },
  created() {
    this.getData();
  },
  mounted() {
    // this.getData();
  },
  methods: {
    getData() {
      this.getTableDataList();
    },
    // 列表, 
    getTableDataList() {
      let ctx = this;

      // ctx.$api
      //   .getTableDataList()
      //   .then(res => {

      //     ctx.total = res.totalRows;
      //     ctx.pageCount = ctx.total / ctx.pageSize + 1;
      //     ctx.tableData = res.rows;
      //   })
      //   .catch(() => {

      //   });
      let _data = [
        {
          all: "",
          name: "名字1",
          value1: "",
          value2: "",
          value3: "2017年-10月"
        },
        {
          all: "",
          name: "名字1",
          value1: "",
          value2: "",
          value3: "2017年-11月"
        },
        {
          all: "",
          name: "名字1",
          value1: "",
          value2: "",
          value3: "2017年-12月"
        },
        {
          all: "",
          name: "名字2",
          value1: "",
          value2: "",
          value3: "2017年-10月"
        },
        {
          all: "",
          name: "名字2",
          value1: "",
          value2: "",
          value3: "2017年-11月"
        },
        {
          all: "",
          name: "名字2",
          value1: "",
          value2: "",
          value3: "2017年-12月"
        },
        {
          all: "",
          name: "名字3",
          value1: "",
          value2: "",
          value3: "2017年-10月"
        },
        {
          all: "",
          name: "名字3",
          value1: "",
          value2: "",
          value3: "2017年-11月"
        },
        {
          all: "",
          name: "名字3",
          value1: "",
          value2: "",
          value3: "2017年-12月"
        },
        {
          all: "",
          name: "名字4",
          value1: "",
          value2: "",
          value3: "2017年-10月"
        },
        {
          all: "",
          name: "名字4",
          value1: "",
          value2: "",
          value3: "2017年-11月"
        },
        {
          all: "",
          name: "名字4",
          value1: "",
          value2: "",
          value3: "2017年-12月"
        }
      ];

      //进行赋值,为了后面编辑用,此方法位置不要动
      ctx.getSpanArr4Edit(_data);

      console.log(ctx.tableData3, "ctx.tableData3");
      ctx.getSpanArr(ctx.tableData3);
      ctx.getSpanArrOne(ctx.tableData3);
    },
    //改变分页事件已办理
    clickChangePage(currPage) {
      this.getGateStationList(currPage);
    },
    cellStyle({ row, column, rowIndex, columnIndex }) {
      return "padding:0px";
    },
    getRowClass({ row, column, rowIndex, columnIndex }) {
      return "height:0";
    },
    searchData: function() {
      let searchVal = this.formInline.searchVal;
    },
    setClass(gateState) {
      if (gateState == 0) {
        return "stateClass-a";
      }

      return "stateClass-b";
    },
    dialogQuickClose: function() {
      this.dialogQuick = false;
    },

    //前行row、当前列column、当前行号rowIndex、当前列号columnIndex四个属性
    //index 0开始
    objectSpanMethod: function({ row, column, rowIndex, columnIndex }) {
      let length = this.tableData3.length - 1;

      // //合并第一列
      // if (columnIndex === 0) {
      //   console.log(columnIndex, "columnIndex");
      //   alert(columnIndex);
      //   if (rowIndex % length === 0) {
      //     return {
      //       rowspan: length + 1,
      //       colspan: 1
      //     };
      //   } else {
      //     return {
      //       rowspan: 0,
      //       colspan: 0
      //     };
      //   }
      // }

      //合并第一列
      if (columnIndex === 0) {
        alert(columnIndex);
        if (rowIndex % 2 === 0) {
          return {
            rowspan: 2,
            colspan: 1
          };
        } else {
          return {
            rowspan: 0,
            colspan: 0
          };
        }
      }
      //合并第二三列
    },
    //合并单元格,此方法需要后台进行名字进行排序
    cellMerge({ row, column, rowIndex, columnIndex }) {
      let length = this.tableData3.length;
      //第0列比较特殊,单独合并
      if (columnIndex === 0) {
        const _row = this.spanArrOne[rowIndex];
        const _col = _row > 0 ? 1 : 0;
        return {
          rowspan: _row,
          colspan: _col
        };
      }
      //1 2 5列进行合并
      if (columnIndex === 1 || columnIndex === 2 || columnIndex === 5) {
        const _row = this.spanArr[rowIndex];
        const _col = _row > 0 ? 1 : 0;
        return {
          rowspan: _row,
          colspan: _col
        };
      }
    },
    //1 2  5列合并的数据
    getSpanArr(data) {
      for (var i = 0; i < data.length; i++) {
        if (i === 0) {
          this.spanArr.push(1);
          this.pos = 0;
        } else {
          // 判断当前元素与上一个元素是否相同
          if (data[i].name === data[i - 1].name) {
            this.spanArr[this.pos] += 1; //需要合并的行数
            this.spanArr.push(0); //新增被合并的行
          } else {
            this.spanArr.push(1);
            this.pos = i; //新的需要合并的第几行数
          }
        }
      }
    },
    //0列合并的数据
    getSpanArrOne(data) {
      for (var i = 0; i < data.length; i++) {
        if (i === 0) {
          this.spanArrOne.push(1);
          this.posOne = 0;
        } else {
          this.spanArrOne[this.posOne] += 1; //需要合并的行数
          this.spanArrOne.push(0); //新增被合并的行
        }
      }

      console.log(this.spanArrOne, " this.spanArrOne");
    },
    //编辑用到的数据
    getSpanArr4Edit(data) {
      let ctx = this;
      var flagNum = 0;

      for (var i = 0; i < data.length; i++) {
        //首先不能编辑
        ctx.$set(data[i], "editing", false);

        if (i === 0) {
          ctx.spanArr4Edit.push(flagNum);
        } else {
          // 判断当前元素与上一个元素是否相同
          if (data[i].name === data[i - 1].name) {
            ctx.spanArr4Edit.push(flagNum); //还是同一个记录
          } else {
            ctx.spanArr4Edit.push(i);
            flagNum = i;
          }
        }
        //与编辑相对应
        ctx.$set(data[i], "flagNum", flagNum);
      }
      //最终赋值
      ctx.tableData3 = data;
      console.log(ctx.spanArr4Edit, "this.spanArr4Edit");
    },
    //快捷填写
    clickTodo: function(index, row) {
      this.dialogQuick = true;
      this.quickFlag = index;
      this.addform = {};
    },
    //快捷填写保存
    addSubmit: function() {
      let ctx = this;
      //赋值数据,取出哪一行需要进行赋值
      let idx = this.quickFlag;

      let datalength = ctx.tableData3.length;
      //每次快捷填写某一个分水口,所以此变量在此定义
      var initFlag = 0;
      for (let i = 0; i < datalength; i++) {
        if (idx === ctx.tableData3[i].flagNum) {
          if (initFlag == 0) {
            //value1 只赋值一次就行
            this.$set(ctx.tableData3[i], "value1", this.addform.w);
          }
          initFlag++;
          this.$set(ctx.tableData3[i], "value2", this.addform.q);
        }
      }
      this.dialogQuick = false;
      ctx.$message.success("操作成功");
      console.log(this.addform, "this.addform");
    },
    handleEdit(index, row) {
      let ctx = this;
      this.setEditFlag(index, 1);
      console.log(index);
      // this.prevValue = JSON.parse(JSON.stringify(row));//保存之前的数据
    },
    handleCancle(index, row) {
      row.editing = false;
      // let prevContent = this.prevValue.bookname;
      // this.$set(row,"bookname",prevContent);
    },
    savemodify(index, row) {
      this.setEditFlag(index, 0);
      console.log(row, "row");
      console.log(this.tableData3, "改变后的table数据");
      console.log(
        JSON.stringify(this.tableData3),
        "JSON.stringify(this.tableData3)"
      );
    },
    //提交数据到后台
    submitToServe() {
      let ctx = this;
      let dataResult = JSON.parse(JSON.stringify(this.tableData3));
      var result = 0;
      //调用后台保存方法
      // ctx.$api.addWaterApply(dataResult).then(res => {
      //   if (res.code == "0") {
      //       result =1;
      //   }
      // });
      console.log(dataResult, "提交到后台的数据");
      //return result;
      return 1;
    },
    //编辑输入框显示与隐藏0 隐藏 1显示
    setEditFlag: function(index, flag) {
      let ctx = this;
      let datalength = ctx.tableData3.length;
      let flagNum = ctx.spanArr4Edit[index];

      for (let i = 0; i < datalength; i++) {
        if (flag == 1) {
          if (flagNum === ctx.tableData3[i].flagNum) {
            //循环遍历改变可编辑的标记
            this.$set(ctx.tableData3[i], "editing", true);
          }
        } else {
          //循环遍历改变可编辑的标记
          this.$set(ctx.tableData3[i], "editing", false);
        }
      }
    }
  },

  filters: {
    iswarnFlt(val) {
      return val == 0 ? "否" : "是";
    }
  }
};
</script>

<style lang="scss" scoped>
.waterApplyTable {
  height: 100%;
}
</style>