应用场景是这样
主表格的数据是所有的学校
然后点击展开的时候,获取学校下相应班级的数据
并且班级要能选择后生成图表,但所有的班级最多选择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>
在主表格type为expand的行(<el-table-column type="expand">
)下面添加子表格,并且添加方法
@selection-change="(val) => {return handleExpandSelectionChange(val, scope.row.id); }"
传入主表格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();
},
在方法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来生成图表
},
然后再看主表格的展开时触发的方法
@expand-change="handleRowClick"
在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;
});
},
注意在上面代码中
通过await _this.getClassList(row.id);
获取到班级数据
然后将数据赋值给对应的row
_this.$nextTick(() => {
_this.$set(row, "tableExpandData", _this.tableExpandData);
_this.$set(row, "isExpanded", true);
_this.expandLoading = false;
});
但这里会产生一个问题,用$set赋值后,页面会重新渲染,展开的table会收回去,我的想法是让展开的table保持展开的状态,
这里使用到的就是:expand-row-keys="expandedRows"
首先在主表格中添加
<el-table
...
:expand-row-keys="expandedRows"
:row-key="getRowKeys"
>
注意一定要设置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];
}
首先要将_this.expandedRows中对应主表展开行的数据清除掉
然后通过const isRowNowExpand = rows.some(r => r.id === row.id)
判断当前行是否展开
如果展开就把当前行添加到expandedRows 中,那样页面刷新后会保持展开状态
在getExpandClassData这个方法里还要注意的是
首先对展开的子table设置对应的ref
let expandTable = "expandTable" + schoolId;
然后,因为选中子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;
});
在上面代码中hasSelections 是判断是否有选中的选项,然后把展开子表格选中的id取出来,根据选中的id调用toggleRowSelection去点击
然后如果已经展开获取过数据了,就返回不要再调用接口获取了
if (row.isExpanded) {
return;
}
最后要限制选中的数量,就通过下面的方法
在展开的子表格中单选框对应的行中 添加:selectable
<el-table-column
:selectable="(row) => {return checkSelectable(row, 'id'); }"
type="selection"
>
然后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;
},
然后通过banNumber 控制限制的数量
最后还有一个搜索方法
watch: {
searchKey: function (val) {
this.tableDisplayData = this.filterTableData.filter(function (data) {
return data.schoolName.toLowerCase().includes(val.toLowerCase());
});
},
},
完整代码如下:
<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>
效果图如下: