当前位置:   article > 正文

【vue组件】使用element-ui table 实现嵌套表格 点击展开时获取数据_element表格展开嵌套表格

element表格展开嵌套表格

应用场景是这样
主表格的数据是所有的学校
然后点击展开的时候,获取学校下相应班级的数据
并且班级要能选择后生成图表,但所有的班级最多选择5个

首先是嵌套表格

<div>
    <el-table
      :data="tableDisplayData"
      id="chartTableExpand"
      style="width: 100%"
      ref="chartTable"
      @expand-change="handleRowClick"
      :expand-row-keys="expandedRows"
      :row-key="getRowKeys"
    >
      <el-table-column type="expand">
        <template slot-scope="scope">
          <el-table
            :ref="'expandTable' + scope.row.id"
            :data="scope.row.tableExpandData"
            style="width: 100%"
            v-loading="expandLoading"
            @selection-change="
              (val) => {
                return handleExpandSelectionChange(val, scope.row.id);
              }
            "
          >
            <el-table-column
              :selectable="
                (row) => {
                  return checkSelectable(row, 'id');
                }
              "
              type="selection"
            >
            </el-table-column>
            <el-table-column
              prop="className"
              label="班级名称"
              width="180"
              fixed="left"
            >
              <template slot-scope="scope">
                <span>
                  {{ scope.row.Name }}
                </span>
              </template>
            </el-table-column>
            <el-table-column prop="studentCount" label="学生数量">
              <template slot-scope="scope">
                <span>
                  {{ scope.row.StudentCount }}
                </span>
              </template>
            </el-table-column>
            <el-table-column prop="answerCount" label="回答数量">
              <template slot-scope="scope">
                <span>
                  {{ scope.row.AnswerCount }}
                </span>
              </template>
            </el-table-column>
          </el-table>
        </template>
      </el-table-column>
      <el-table-column prop="schoolName" label="学校名">
        <template slot-scope="scope">
          <span>
            {{ scope.row.schoolName }}
          </span>
        </template>
      </el-table-column>
      <el-table-column prop="classCount" label="班级数量">
        <template slot-scope="scope">
          <span>
            {{ scope.row.classCount }}
          </span>
        </template>
      </el-table-column>
      <el-table-column prop="status" label="时间">
        <template slot-scope="scope">
          <span>
            {{ scope.row.date }}
          </span>
        </template>
      </el-table-column>
      <el-table-column prop="search">
        <template slot="header" slot-scope="scope">
          <el-input v-model="searchKey" size="medium" placeholder="Search" />
        </template>
        <template slot-scope="scope"> </template>
      </el-table-column>
    </el-table>
  </div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90

在主表格type为expand的行(<el-table-column type="expand">)下面添加子表格,并且添加方法

@selection-change="(val) => {return handleExpandSelectionChange(val, scope.row.id); }"
  • 1

传入主表格row的数据和row的id

在方法handleExpandSelectionChange中,将 multipleSelection的值对应相应的table存起来,也就是说一个table 对应它自己的 multipleSelection,键是tableId ;值是每个table自己的multipleSelection,这样能解决多个table共用一个multipleSelection时会出现前一个子table选中的值会被后一个子table选中的值替换掉的问题

handleExpandSelectionChange(val, tableId) {
      let _this = this;
      // 如果是表格展开时去点击的话,就不要改变selections的值
      if (!_this.isClassTableExpanded) {
        // 这里将 multipleSelection的值对应相应的table存起来
        // 也就是说一个table 对应它自己的 multipleSelection
        // 键是tableId 值是 multipleSelection
        _this.selections[tableId] = val;
      }
      _this.updateMultipleSelection();
    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在方法updateMultipleSelection中,会将各个表格的multipleSelection汇总,形成一个总的multipleSelection,再根据这个汇总的multipleSelection进行后面的处理

updateMultipleSelection() {
      let _this = this;
      // 把selections里的row取出来汇总
      _this.multipleSelection = [].concat.apply(
        [],
        Object.keys(_this.selections).map(function (key) {
          return _this.selections[key];
        })
      );
      // 用汇总后的multipleSelection来生成图表
    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

然后再看主表格的展开时触发的方法

@expand-change="handleRowClick"
  • 1

在handleRowClick方法中
通过方法getExpandClassData中获取数据

	// 点击展开
     handleRowClick(row,rows) {
      let _this = this;

      _this.getExpandClassData(row,rows);
    },

    // 获取学校或班级汇总数据
    async getExpandClassData(row,rows) {
      let _this = this;
      let schoolId = row.id
      // 展开class table对应的ref
      let expandTable = "expandTable" + schoolId;

      // table展开时,根据之前选中的选项通过toggleRowSelection点击checkbox
      _this.$nextTick(function () {
        if (_this.$refs[expandTable]) {
          let hasSelections =
            _this.selections.length > 0 ||
            _this.selections[schoolId] ||
            (_this.selections[schoolId]
              ? _this.selections[schoolId].length
              : undefined) > 0;
          if (hasSelections) {
            _this.isClassTableExpanded = true;
            let selectedIds = _this.selections[schoolId].map(
              (mSelect) => mSelect.id
            );
            row.tableExpandData.forEach((row) => {
              if (selectedIds.includes(row.id)) {
                _this.$refs[expandTable].toggleRowSelection(row, true);
              }
            });
          }
        }
        _this.isClassTableExpanded = false;
      });
      const delIndex = _this.expandedRows.findIndex((item)=>{return item === schoolId});
      if (delIndex > -1) {
        _this.expandedRows.splice(delIndex, 1);
      }
      
      const isRowNowExpand = rows.some(r => r.id === row.id) // 判断当前行展开状态
      if (isRowNowExpand) {
          _this.expandedRows = [schoolId,..._this.expandedRows];
      }

      // 如果已经展开获取或数据了,就返回不要再获取了
      if (row.isExpanded) {
        return;
      }
      _this.expandLoading = true;
      await _this.getClassList(row.id);

      // 将school下对应的class表格数据,赋值到相应的school下
      // 作为tableExpandData存起来
      _this.$nextTick(() => {
        _this.$set(row, "tableExpandData", _this.tableExpandData);
        _this.$set(row, "isExpanded", true);
        _this.expandLoading = false;
      });
    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

注意在上面代码中
通过await _this.getClassList(row.id);获取到班级数据
然后将数据赋值给对应的row

_this.$nextTick(() => {
   _this.$set(row, "tableExpandData", _this.tableExpandData);
   _this.$set(row, "isExpanded", true);
   _this.expandLoading = false;
 });
  • 1
  • 2
  • 3
  • 4
  • 5

但这里会产生一个问题,用$set赋值后,页面会重新渲染,展开的table会收回去,我的想法是让展开的table保持展开的状态,
这里使用到的就是:expand-row-keys="expandedRows"

首先在主表格中添加

<el-table
      ...
      :expand-row-keys="expandedRows"
      :row-key="getRowKeys"
    >
  • 1
  • 2
  • 3
  • 4
  • 5

注意一定要设置row-key
然后在下面代码里

const delIndex = _this.expandedRows.findIndex((item)=>{return item === schoolId});
if (delIndex > -1) {
  _this.expandedRows.splice(delIndex, 1);
}

const isRowNowExpand = rows.some(r => r.id === row.id) // 判断当前行展开状态
if (isRowNowExpand) {
    _this.expandedRows = [schoolId,..._this.expandedRows];
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

首先要将_this.expandedRows中对应主表展开行的数据清除掉
然后通过const isRowNowExpand = rows.some(r => r.id === row.id)判断当前行是否展开
如果展开就把当前行添加到expandedRows 中,那样页面刷新后会保持展开状态

在getExpandClassData这个方法里还要注意的是
首先对展开的子table设置对应的ref

let expandTable = "expandTable" + schoolId;
  • 1

然后,因为选中子table的单选框后,把展开的子table收齐再展开时,单选框的选中样式会丢失,这时我想的办法是根据之前选中的选项,调用toggleRowSelection这个方法,再把单选框选中

// table展开时,根据之前选中的选项通过toggleRowSelection点击checkbox
   _this.$nextTick(function () {
     if (_this.$refs[expandTable]) {
       let hasSelections =
         _this.selections.length > 0 ||
         _this.selections[schoolId] ||
         (_this.selections[schoolId]
           ? _this.selections[schoolId].length
           : undefined) > 0;
       if (hasSelections) {
         _this.isClassTableExpanded = true;
         let selectedIds = _this.selections[schoolId].map(
           (mSelect) => mSelect.id
         );
         row.tableExpandData.forEach((row) => {
           if (selectedIds.includes(row.id)) {
             _this.$refs[expandTable].toggleRowSelection(row, true);
           }
         });
       }
     }
     _this.isClassTableExpanded = false;
   });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

在上面代码中hasSelections 是判断是否有选中的选项,然后把展开子表格选中的id取出来,根据选中的id调用toggleRowSelection去点击

然后如果已经展开获取过数据了,就返回不要再调用接口获取了

  if (row.isExpanded) {
    return;
  }
  • 1
  • 2
  • 3

最后要限制选中的数量,就通过下面的方法
在展开的子表格中单选框对应的行中 添加:selectable

<el-table-column
    :selectable="(row) => {return checkSelectable(row, 'id'); }"
    type="selection"
  >
  • 1
  • 2
  • 3
  • 4

然后checkSelectable方法的实现如下:

// 是否禁用多选
checkSelectable: function (row, key) {
  let _this = this;
  let flag = true;
  // 多选最多选 banNumber 个
  if (_this.multipleSelection.length >= _this.banNumber) {
    if (!Array.isArray(row)) {
      flag = _this.multipleSelection.some(
        (selection) => row[key] === selection[key]
      );
    }
  }
  return flag;
},
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

然后通过banNumber 控制限制的数量

最后还有一个搜索方法

 watch: {
    searchKey: function (val) {
      this.tableDisplayData = this.filterTableData.filter(function (data) {
        return data.schoolName.toLowerCase().includes(val.toLowerCase());
      });
    },
  },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

完整代码如下:


<template>
  <div>
    <el-table
      :data="tableDisplayData"
      id="chartTableExpand"
      style="width: 100%"
      ref="chartTable"
      @expand-change="handleRowClick"
      :expand-row-keys="expandedRows"
      :row-key="getRowKeys"
    >
      <el-table-column type="expand">
        <template slot-scope="scope">
          <el-table
            :ref="'expandTable' + scope.row.id"
            :data="scope.row.tableExpandData"
            style="width: 100%"
            v-loading="expandLoading"
            @selection-change="
              (val) => {
                return handleExpandSelectionChange(val, scope.row.id);
              }
            "
          >
            <el-table-column
              :selectable="
                (row) => {
                  return checkSelectable(row, 'id');
                }
              "
              type="selection"
            >
            </el-table-column>
            <el-table-column
              prop="className"
              label="班级名称"
              width="180"
              fixed="left"
            >
              <template slot-scope="scope">
                <span>
                  {{ scope.row.Name }}
                </span>
              </template>
            </el-table-column>
            <el-table-column prop="studentCount" label="学生数量">
              <template slot-scope="scope">
                <span>
                  {{ scope.row.StudentCount }}
                </span>
              </template>
            </el-table-column>
            <el-table-column prop="answerCount" label="回答数量">
              <template slot-scope="scope">
                <span>
                  {{ scope.row.AnswerCount }}
                </span>
              </template>
            </el-table-column>
          </el-table>
        </template>
      </el-table-column>
      <el-table-column prop="schoolName" label="学校名">
        <template slot-scope="scope">
          <span>
            {{ scope.row.schoolName }}
          </span>
        </template>
      </el-table-column>
      <el-table-column prop="classCount" label="班级数量">
        <template slot-scope="scope">
          <span>
            {{ scope.row.classCount }}
          </span>
        </template>
      </el-table-column>
      <el-table-column prop="status" label="时间">
        <template slot-scope="scope">
          <span>
            {{ scope.row.date }}
          </span>
        </template>
      </el-table-column>
      <el-table-column prop="search">
        <template slot="header" slot-scope="scope">
          <el-input v-model="searchKey" size="medium" placeholder="Search" />
        </template>
        <template slot-scope="scope"> </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
import { getClassData, getSchoolData } from "@/api/api";
export default {
  name: "embededTable",
  props: {
    tooltip: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      multipleSelection: [],
      selections: [],
      banNumber: 5,
      isTableSelected: false,
      tableExpandData: [],
      filterTableData: [],
      searchKey: "",
      tableDisplayData: [],
      isClassTableExpanded: false,
      expandedRows: [],
      expandLoading: false,
    };
  },
  async created() {
    let _this = this;
    await _this.getData();
  },
  mounted() {
    let _this = this;
  },
  watch: {
    searchKey: function (val) {
      this.tableDisplayData = this.filterTableData.filter(function (data) {
        return data.schoolName.toLowerCase().includes(val.toLowerCase());
      });
    },
  },

  components: {},
  methods: {
    getRowKeys: function (row) {
      return row.id;
    },
    async getClassList(id) {
      let _this = this;
      await getClassData(id)
        .then((res) => {
          _this.tableExpandData = res;
        })
        .catch((err) => {
          console.log(err, "err");
        });
    },
    async getSchoolList() {
      let _this = this;
      await getSchoolData()
        .then((res) => {
          _this.tableData = res;
          _this.filterTableData = _this.tableData;
          _this.tableDisplayData = _this.tableData;
        })
        .catch((err) => {
          console.log(err, "err");
        });
    },
    async getData() {
      let _this = this;
      await _this.getSchoolList();
    },

    // 点击展开
     handleRowClick(row,rows) {
      let _this = this;

      _this.getExpandClassData(row,rows);
    },

    // 获取学校或班级汇总数据
    async getExpandClassData(row,rows) {
      let _this = this;
      let schoolId = row.id
      // 展开class table对应的ref
      let expandTable = "expandTable" + schoolId;

      // table展开时,根据之前选中的选项通过toggleRowSelection点击checkbox
      _this.$nextTick(function () {
        if (_this.$refs[expandTable]) {
          let hasSelections =
            _this.selections.length > 0 ||
            _this.selections[schoolId] ||
            (_this.selections[schoolId]
              ? _this.selections[schoolId].length
              : undefined) > 0;
          if (hasSelections) {
            _this.isClassTableExpanded = true;
            let selectedIds = _this.selections[schoolId].map(
              (mSelect) => mSelect.id
            );
            row.tableExpandData.forEach((row) => {
              if (selectedIds.includes(row.id)) {
                _this.$refs[expandTable].toggleRowSelection(row, true);
              }
            });
          }
        }
        _this.isClassTableExpanded = false;
      });
      const delIndex = _this.expandedRows.findIndex((item)=>{return item === schoolId});
      if (delIndex > -1) {
        _this.expandedRows.splice(delIndex, 1);
      }
      
      const isRowNowExpand = rows.some(r => r.id === row.id) // 判断当前行展开状态
      if (isRowNowExpand) {
          _this.expandedRows = [schoolId,..._this.expandedRows];
      }
      console.log(_this.expandedRows)

      // 如果已经展开获取或数据了,就返回不要再获取了
      if (row.isExpanded) {
        return;
      }
      _this.expandLoading = true;
      await _this.getClassList(row.id);

      // 将school下对应的class表格数据,赋值到相应的school下
      // 作为tableExpandData存起来

      // row.tableExpandData = _this.tableExpandData;
      // row.isExpanded = true;
      _this.$nextTick(() => {
        _this.$set(row, "tableExpandData", _this.tableExpandData);
        _this.$set(row, "isExpanded", true);
        // _this.expandedRows = [schoolId,..._this.expandedRows];
        _this.expandLoading = false;
      });
    },

    // 单选
    handleExpandSelectionChange(val, tableId) {
      let _this = this;
      // 如果是表格展开时去点击的话,就不要改变selections的值
      if (!_this.isClassTableExpanded) {
        // 这里将 multipleSelection的值对应相应的table存起来
        // 也就是说一个table 对应它自己的 multipleSelection
        // 键是tableId 值是 multipleSelection
        _this.selections[tableId] = val;
      }
      _this.updateMultipleSelection();
    },

    updateMultipleSelection() {
      let _this = this;
      // 把selections里的row取出来汇总
      _this.multipleSelection = [].concat.apply(
        [],
        Object.keys(_this.selections).map(function (key) {
          return _this.selections[key];
        })
      );
    },

    // 是否禁用多选
    checkSelectable: function (row, key) {
      let _this = this;
      let flag = true;
      // 多选最多选 banNumber 个
      if (_this.multipleSelection.length >= _this.banNumber) {
        if (!Array.isArray(row)) {
          flag = _this.multipleSelection.some(
            (selection) => row[key] === selection[key]
          );
        }
      }
      return flag;
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
/* 去掉全选按钮 */
.el-table__fixed-header-wrapper .el-table__header th .el-checkbox .el-checkbox__input .el-checkbox__inner{
  display: none;
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283

效果图如下:
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/279908?site
推荐阅读
相关标签
  

闽ICP备14008679号