Recently developed a requirement, the element tree table, when the table is fully selected, all items (including all child nodes) are selected, when the parent node of the tree table is selected, all child nodes under the parent node must also be selected, if a parent If there are not all child nodes under the node, the parent node is half-selected
Renderings:
1.HTML
<template> <el-table v-loading="loading" :data="orderList" @selection-change="handleSelectionChange" :row-key='rowKeyFunc' :tree-props="{children: 'children', hasChildren: 'hasChildren'}" :row-class-name="rowClassNameFun" ref="table" @select-all="selectAllFun" @select="selectFun" > <el-table-column type="selection" width="55" align="center"/> <el-table-column label="operator" align="center" prop="providerType" width="120px"/> <el-table-column label="Creation time" align="center" prop="gmtCreate" width="180" > </el-table-column> <el-table-column label="Customer ID" align="center" width="200" prop="customerNo" /> <el-table-column label="system tracking number" align="center" width="200" prop="orderNo" /> </el-table> </template>
2. JS
<script> export default { name: "index", data() { return { // mask layer loading: true, // select the array ids: [], // Non-single disabled single: true, // non-multiple disabled multiple: true, // total number total: 0, // waybill management form data orderList: [], // query parameters queryParams: { }, page: { pageNum: 1, pageSize: 15, }, oneProductIsSelect:[], isFun: false }; }, created() { this. getList(); }, methods: { // query list getList() { this.loading = true; listOrder(this.queryParams, this.page).then((response) => { this.orderList=response.rows //This step is to connect the parent and the child, define a taskId for the child, and match the taskId of the child with the id of the parent this.orderList.forEach((item, index) => { if (item. children) { item.children.forEach((cItem, cIndex) => { cItem.taskId = item.id; }); } }); // Since the array id returned by the backend is not unique (the id in the parent is the same as the id of one of the children), then if the id in: row-key='id' is a unique value, it has been processed and the parent The id of the array is changed, and the id in the array is unique. Of course, you can discuss with the backend and return a unique value to you. This processing code can be omitted. this.orderList = this.orderList.map((item,index)=>{ return { ...item, uuid:`${index}-${this.guid()}` } }) this.total = response.total; //number of pages this.loading = false; // overlay this.initData(this.orderList) }); }, //Generate a unique ID guid() { return Number( Math.random().toString().substr(3, 3) + Date.now() ).toString(36); }, //row-key unique value rowKeyFunc(row){ if(row.uuid){ return row.uuid } else { return row.id } }, //Initialize the data, mark the data with isSelect, if isSelect is false, unselected, true selected, half half selected initData(data) { data.forEach((item) => { item.isSelect = false; //The default is not selected if (item. children & amp; & amp; item. children. length) { this.initData(item.children); } }); }, // Determine whether to select all checkIsAllSelect() { this.oneProductIsSelect = []; this.orderList.forEach((item) => { this.oneProductIsSelect.push(item.isSelect); }); //Judge whether the first-level product is all selected. If all the first-level products are true, set to cancel all selection, otherwise select all let isAllSelect = this.oneProductIsSelect.every((selectStatusItem) => { return true == selectStatusItem; }); return isAllSelect; }, // Select all or none (this is grandpa's check) selectAllFun(selection) { let isAllSelect = this. checkIsAllSelect(); this.orderList.forEach((item) => { item.isSelect = isAllSelect; this.$refs.table.toggleRowSelection(item, !isAllSelect); this. selectFun(selection, item); }); }, selectFun(selection, row) { this.setRowIsSelect(row); }, setRowIsSelect(row) { //When clicking the checkbox of the parent point, the current state may be unknown, so the current row state is set to false and selected to achieve the effect of selecting all child points if (row.isSelect == "half") { row.isSelect = false; this.$refs.table.toggleRowSelection(row, true); } row.isSelect = !row.isSelect; //Judging whether the operation is a child point check box or a parent point check box, if it is a parent point, then control the selection or non-selection of all child points if (row. children & amp; & amp; row. children. length > 0) { row.children.forEach((item) => { item.isSelect = row.isSelect; this.$refs.table.toggleRowSelection(item, row.isSelect); }); } else { //The operation is child node 1, get parent node 2, judge the number of selected child nodes, if all are selected, the parent node will be selected, if none are selected, it will be unselected, if part of the selection, then set Unclear state let parentId = row.taskId; this.orderList.forEach((item) => { let isAllSelect = []; if (item.id == parentId) { if(item. children){ item.children.forEach((databaseSourceListItem) => { isAllSelect.push(databaseSourceListItem.isSelect); }); } if ( isAllSelect.every((selectItem) => { return true == selectItem; }) ) { item.isSelect = true; this.$refs.table.toggleRowSelection(item, true); } else if ( isAllSelect.every((selectItem) => { return false == selectItem; }) ) { item.isSelect = false; this.$refs.table.toggleRowSelection(item, false); } else{ item.isSelect ="half"; } } }); } }, rowClassNameFun({row}){ if(row.isSelect=='half'){ return "indeterminate"; } }, // Select the data in the multi-select box, the logic in this is mentioned below handleSelectionChange(selection) { console.log(selection,'selection') if(this. isFun) { this.isFun = false return } // Determine whether the currently selected node has child nodes if(selection. length === 0) { selection = [] this.ids = []; this.isFun = true this.multiple = !selection.length return } this.single = selection.length !== 1; this.multiple = !selection.length this.ids = selection.map((item) => item.trackNumber); }, }, }; </script>
There is a point to pay attention to here. When there are two children in the parent, check the parent, and the handleSelectionChange of @selection-change=”handleSelectionChange” will run three times, one time for the parent, one for the parent and one for the child, and one for the child
// Multi-selection box selects data handleSelectionChange(selection) { console.log(selection,'selection') }
It is still quite confusing here, and it is printed out three times, which I can understand, because three are checked, but the order of printing, I don’t quite understand, because after one is checked, the operation batch download and batch cancel, When not checked, it is forbidden to click batch download and batch cancel
renderings
original code logic
// Multi-selection box selects data handleSelectionChange(selection) { console.log(selection,'selection') this.single = selection.length !== 1; this.multiple = !selection.length this.ids = selection.map((item) => item.trackNumber); },
It stands to reason that this can realize the above functions, but if it is not realized, I uncheck it and print it three times, and the last time has a value, so the above code cannot realize this function
So I improved the above code
//Multiple selection box selects data handleSelectionChange(selection) { if(this. isFun) { this.isFun = false return } // Determine whether the currently selected node has child nodes if(selection. length === 0) { selection = [] this.ids = []; this.isFun = true this.multiple = !selection.length return } this.single = selection.length !== 1; this.multiple = !selection.length this.ids = selection.map((item) => item.trackNumber); },
Of course, because handleSelectionChange will run multiple times, the ids passed to the backend will have duplicate values, so it needs to be deduplicated.
//Array deduplication unlink(arr) { return arr. filter(function (item, index, arr) { //The current element, the first index in the original array == the current index value, otherwise return the current element return arr. indexOf(item, 0) === index; }); }, handleCancal(row) { //Cancel inside the operation let trackNumber //batch cancel let trackNumbers if (row. trackNumber) { trackNumber = row.trackNumber.split(',') } else { trackNumbers = this.unlink(this.ids.toString().split(','))//Sort the data and de-duplicate } let parmas = { trackNumbers:trackNumber || trackNumbers } this.$confirm("Are you sure to cancel the waybill whose waybill number is " + (trackNumber || trackNumbers) + "? (Note: After confirming the application, it will be accepted after 15 days)", { confirmButtonText: "OK", cancelButtonText: "Cancel", type: "warning", }) .then(function () { return cancalOrder(parmas); }) .then(() => { this. getList(); this.msgSuccess("Apply successfully"); }) .catch(function () {}); },
3. CSS
half-selected style
<style lang="scss" scoped > /deep/.indeterminate { .el-table-column --selection .cell .el-checkbox { display: block !important; } .el-checkbox__input .el-checkbox__inner { background-color: #4a97eb !important; border-color: #4a97eb !important; color: #fff !important; } } /deep/.indeterminate.el-checkbox__input.is-checked.el-checkbox__inner::after { transform: scale(0.5); } /deep/.indeterminate.el-checkbox__input.el-checkbox__inner::after { border-color: #c0c4cc !important; background-color: #c0c4cc; } /deep/.indeterminate.el-checkbox__input.el-checkbox__inner::after { content: ""; position: absolute; display: block; background-color: #fff; height: 2px; transform: scale(0.5); left: 0; right: 0; top: 5px; width: auto !important; } </style>
The above can realize the selection of the parent node and child nodes in the tree form, and the child nodes are not fully selected. The parent node is half-selected, but for handleSelectionChange, the data processing inside is still very confusing. Although it is solved, it feels not the best way. If there is Good solutions, you can share, welcome to share and correct.