Skip to content

Commit 9735eff

Browse files
authored
feat: 树形数据的联动选择 (#75)
1 parent 72abeb8 commit 9735eff

File tree

5 files changed

+228
-16
lines changed

5 files changed

+228
-16
lines changed

Diff for: examples/docs/zh-CN/table.md

+127
Original file line numberDiff line numberDiff line change
@@ -1848,6 +1848,132 @@
18481848
```
18491849
:::
18501850

1851+
### 树形数据与勾选联动
1852+
1853+
:::demo 支持树类型的数据的显示。暂时不支持懒加载的联动。通过 `check-strictly` 控制是否严格的遵循父子不互相关联的做法,默认为 `false`
1854+
1855+
```html
1856+
<template>
1857+
<div>
1858+
<el-table
1859+
v-for="strictly in checkStrictly"
1860+
:data="tableData"
1861+
style="width: 100%;margin-bottom: 20px;"
1862+
row-key="id"
1863+
border
1864+
default-expand-all
1865+
:key="strictly"
1866+
:check-strictly="strictly"
1867+
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
1868+
@selection-change="onSelectionChange"
1869+
>
1870+
<el-table-column
1871+
type="selection"
1872+
>
1873+
</el-table-column>
1874+
<el-table-column
1875+
prop="date"
1876+
label="日期"
1877+
sortable
1878+
width="180">
1879+
</el-table-column>
1880+
<el-table-column
1881+
prop="name"
1882+
label="姓名"
1883+
sortable
1884+
width="180">
1885+
</el-table-column>
1886+
<el-table-column
1887+
prop="address"
1888+
label="地址">
1889+
</el-table-column>
1890+
</el-table>
1891+
</div>
1892+
</template>
1893+
<script>
1894+
export default {
1895+
data() {
1896+
return {
1897+
checkStrictly: [false, true],
1898+
tableData: [{
1899+
id: 1,
1900+
date: '2016-05-02',
1901+
name: '王小虎',
1902+
address: '上海市普陀区金沙江路 1518 弄'
1903+
}, {
1904+
id: 2,
1905+
date: '2016-05-04',
1906+
name: '王小虎',
1907+
address: '上海市普陀区金沙江路 1517 弄'
1908+
}, {
1909+
id: 3,
1910+
date: '2016-05-01',
1911+
name: '王小虎',
1912+
address: '上海市普陀区金沙江路 1519 弄',
1913+
children: [{
1914+
id: 31,
1915+
date: '2016-05-11',
1916+
name: '王小虎-1',
1917+
address: '上海市普陀区金沙江路 1519-1 弄'
1918+
}, {
1919+
id: 32,
1920+
date: '2016-05-12',
1921+
name: '王小虎-2',
1922+
address: '上海市普陀区金沙江路 1519-2 弄',
1923+
children: [
1924+
{
1925+
id: 321,
1926+
date: '2016-05-21',
1927+
name: '王小虎-2-1',
1928+
address: '上海市普陀区金沙江路 1519-2-1 弄'
1929+
},{
1930+
id: 322,
1931+
date: '2016-05-22',
1932+
name: '王小虎-2-2',
1933+
address: '上海市普陀区金沙江路 1519-2-2 弄'
1934+
}
1935+
]
1936+
}]
1937+
}, {
1938+
id: 4,
1939+
date: '2016-05-03',
1940+
name: '王小虎',
1941+
address: '上海市普陀区金沙江路 1516 弄'
1942+
}],
1943+
tableData1: [{
1944+
id: 1,
1945+
date: '2016-05-02',
1946+
name: '王小虎',
1947+
address: '上海市普陀区金沙江路 1518 弄'
1948+
}, {
1949+
id: 2,
1950+
date: '2016-05-04',
1951+
name: '王小虎',
1952+
address: '上海市普陀区金沙江路 1517 弄'
1953+
}, {
1954+
id: 3,
1955+
date: '2016-05-01',
1956+
name: '王小虎',
1957+
address: '上海市普陀区金沙江路 1519 弄',
1958+
hasChildren: true
1959+
}, {
1960+
id: 4,
1961+
date: '2016-05-03',
1962+
name: '王小虎',
1963+
address: '上海市普陀区金沙江路 1516 弄'
1964+
}]
1965+
}
1966+
},
1967+
methods: {
1968+
onSelectionChange(selection){
1969+
console.log(selection)
1970+
}
1971+
},
1972+
}
1973+
</script>
1974+
```
1975+
:::
1976+
18511977
### Table Attributes
18521978
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
18531979
|---------- |-------------- |---------- |-------------------------------- |-------- |
@@ -1884,6 +2010,7 @@
18842010
| lazy | 是否懒加载子节点数据 | Boolean |||
18852011
| load | 加载子节点数据的函数,lazy 为 true 时生效,函数第二个参数包含了节点的层级信息 | Function(row, treeNode, resolve) |||
18862012
| tree-props | 渲染嵌套数据的配置选项 | Object || { hasChildren: 'hasChildren', children: 'children' } |
2013+
| check-strictly | 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false | Boolean | - | false |
18872014

18882015
### Table Events
18892016
| 事件名 | 说明 | 参数 |

Diff for: packages/table/src/config.js

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const cellForced = {
3939
return <el-checkbox
4040
nativeOn-click={ (event) => event.stopPropagation() }
4141
value={ store.isSelected(row) }
42+
indeterminate={ store.isHalfSelected(row) }
4243
disabled={ column.selectable ? !column.selectable.call(null, row, $index) : false }
4344
on-input={ () => { store.commit('rowSelectedChanged', row); } } />;
4445
},

Diff for: packages/table/src/store/watcher.js

+71-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Vue from 'vue';
22
import merge from 'element-ui/src/utils/merge';
3-
import { getKeysMap, getRowIdentity, getColumnById, getColumnByKey, orderBy, toggleRowStatus } from '../util';
3+
import { getKeysMap, getRowIdentity, getColumnById, getColumnByKey, orderBy, toggleRowStatus, findParentData } from '../util';
44
import expand from './expand';
55
import current from './current';
66
import tree from './tree';
@@ -25,6 +25,27 @@ const doFlattenColumns = (columns) => {
2525
return result;
2626
};
2727

28+
const changeAllSelectionState = (data, states, selection, status) => {
29+
let selectionChanged = false;
30+
// 这里递归解决
31+
data.forEach((row, index) => {
32+
if (states.selectable) {
33+
if (states.selectable.call(null, row, index) && toggleRowStatus(selection, row, status)) {
34+
selectionChanged = true;
35+
}
36+
} else {
37+
if (toggleRowStatus(selection, row, status)) {
38+
selectionChanged = true;
39+
}
40+
}
41+
if (row[ states.childrenColumnName ]) {
42+
selectionChanged = changeAllSelectionState(row[ states.childrenColumnName ], states, selection, status) || selectionChanged;
43+
}
44+
});
45+
46+
return selectionChanged;
47+
};
48+
2849
export default Vue.extend({
2950
data() {
3051
return {
@@ -54,6 +75,7 @@ export default Vue.extend({
5475
// 选择
5576
isAllSelected: false,
5677
selection: [],
78+
halfSelection: [],
5779
reserveSelection: false,
5880
selectOnIndeterminate: false,
5981
selectable: null,
@@ -122,6 +144,11 @@ export default Vue.extend({
122144
return selection.indexOf(row) > -1;
123145
},
124146

147+
isHalfSelected(row) {
148+
const { halfSelection = [] } = this.states;
149+
return halfSelection.indexOf(row) > -1;
150+
},
151+
125152
clearSelection() {
126153
const states = this.states;
127154
states.isAllSelected = false;
@@ -156,7 +183,47 @@ export default Vue.extend({
156183
},
157184

158185
toggleRowSelection(row, selected, emitChange = true) {
159-
const changed = toggleRowStatus(this.states.selection, row, selected);
186+
let changed = toggleRowStatus(this.states.selection, row, selected);
187+
const {checkStrictly, childrenColumnName, data = []} = this.states;
188+
selected = this.isSelected(row);
189+
// 切换半选状态
190+
const toggleHalfSelect = (row, isHalfSelected) => {
191+
if (typeof isHalfSelected !== 'boolean') {
192+
isHalfSelected = this.isHalfSelected(row) && !selected;
193+
}
194+
toggleRowStatus(this.states.halfSelection, row, isHalfSelected);
195+
};
196+
toggleHalfSelect(row);
197+
// 同步子级数据及父级数据
198+
if (!checkStrictly) {
199+
// 同步子级数据
200+
// 递归调用子数据
201+
const asyncChildrenData = (row) => {
202+
if (row[childrenColumnName]) {
203+
row[childrenColumnName].forEach(child => {
204+
changed = toggleRowStatus(this.states.selection, child, selected) || changed;
205+
toggleHalfSelect(child);
206+
asyncChildrenData(child);
207+
});
208+
}
209+
};
210+
asyncChildrenData(row);
211+
// 同步父数据
212+
// 兄弟数据状态查找
213+
// 兄弟全为选中,父数据添加进selection,再递归同步父数据
214+
// 兄弟不全为选中,selection移除父数据,父数据添加进halfSelection,递归父数据添加进halfSelection
215+
const asyncParentData = (parentData, current, childKey) => {
216+
const parent = findParentData(parentData, current, childKey);
217+
if (parent) {
218+
const isAllSelected = parent[childKey].every(this.isSelected);
219+
const isSomeSelected = parent[childKey].some(child => this.isSelected(child) || this.isHalfSelected(child));
220+
toggleHalfSelect(parent, isSomeSelected && !isAllSelected);
221+
changed = toggleRowStatus(this.states.selection, parent, isAllSelected) || changed;
222+
asyncParentData(parentData, parent, childKey);
223+
}
224+
};
225+
asyncParentData(data, row, childrenColumnName);
226+
}
160227
if (changed) {
161228
const newSelection = (this.states.selection || []).slice();
162229
// 调用 API 修改选中值,不触发 select 事件
@@ -170,25 +237,15 @@ export default Vue.extend({
170237
_toggleAllSelection() {
171238
const states = this.states;
172239
const { data = [], selection } = states;
240+
states.halfSelection = [];
173241
// when only some rows are selected (but not all), select or deselect all of them
174242
// depending on the value of selectOnIndeterminate
175243
const value = states.selectOnIndeterminate
176244
? !states.isAllSelected
177245
: !(states.isAllSelected || selection.length);
178246
states.isAllSelected = value;
179247

180-
let selectionChanged = false;
181-
data.forEach((row, index) => {
182-
if (states.selectable) {
183-
if (states.selectable.call(null, row, index) && toggleRowStatus(selection, row, value)) {
184-
selectionChanged = true;
185-
}
186-
} else {
187-
if (toggleRowStatus(selection, row, value)) {
188-
selectionChanged = true;
189-
}
190-
}
191-
});
248+
const selectionChanged = changeAllSelectionState(data, states, selection, value);
192249

193250
if (selectionChanged) {
194251
this.table.$emit('selection-change', selection ? selection.slice() : []);

Diff for: packages/table/src/table.vue

+8-2
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,12 @@
331331
332332
lazy: Boolean,
333333
334-
load: Function
334+
load: Function,
335+
336+
checkStrictly: {
337+
type: Boolean,
338+
default: false
339+
}
335340
},
336341
337342
components: {
@@ -668,7 +673,8 @@
668673
indent: this.indent,
669674
lazy: this.lazy,
670675
lazyColumnIdentifier: hasChildren,
671-
childrenColumnName: children
676+
childrenColumnName: children,
677+
checkStrictly: this.checkStrictly
672678
});
673679
const layout = new TableLayout({
674680
store: this.store,

Diff for: packages/table/src/util.js

+21
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,24 @@ export function walkTreeNode(root, cb, childrenKey = 'children', lazyKey = 'hasC
253253
}
254254
});
255255
}
256+
257+
export function findParentData(data, row, childKey = 'children') {
258+
let parent = null;
259+
for (let i = 0; i < data.length; i++) {
260+
const item = data[i];
261+
const children = item[childKey] || [];
262+
// 如果children里的数据包含row,则找到parent了
263+
if (children.indexOf(row) > -1) {
264+
parent = item;
265+
}
266+
// 如果没找到并且该item有children,则继续查找该item的children
267+
if (!parent && children.length) {
268+
parent = findParentData(children, row, childKey);
269+
}
270+
// 找到后立刻返回
271+
if (parent) {
272+
return parent;
273+
}
274+
}
275+
return null;
276+
}

0 commit comments

Comments
 (0)