Skip to content

Commit

Permalink
feat: 树形数据的联动选择 (#75)
Browse files Browse the repository at this point in the history
  • Loading branch information
snowlocked authored Sep 10, 2020
1 parent 72abeb8 commit 9735eff
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 16 deletions.
127 changes: 127 additions & 0 deletions examples/docs/zh-CN/table.md
Original file line number Diff line number Diff line change
Expand Up @@ -1848,6 +1848,132 @@
```
:::

### 树形数据与勾选联动

:::demo 支持树类型的数据的显示。暂时不支持懒加载的联动。通过 `check-strictly` 控制是否严格的遵循父子不互相关联的做法,默认为 `false`

```html
<template>
<div>
<el-table
v-for="strictly in checkStrictly"
:data="tableData"
style="width: 100%;margin-bottom: 20px;"
row-key="id"
border
default-expand-all
:key="strictly"
:check-strictly="strictly"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
@selection-change="onSelectionChange"
>
<el-table-column
type="selection"
>
</el-table-column>
<el-table-column
prop="date"
label="日期"
sortable
width="180">
</el-table-column>
<el-table-column
prop="name"
label="姓名"
sortable
width="180">
</el-table-column>
<el-table-column
prop="address"
label="地址">
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
checkStrictly: [false, true],
tableData: [{
id: 1,
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
id: 2,
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
}, {
id: 3,
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄',
children: [{
id: 31,
date: '2016-05-11',
name: '王小虎-1',
address: '上海市普陀区金沙江路 1519-1 弄'
}, {
id: 32,
date: '2016-05-12',
name: '王小虎-2',
address: '上海市普陀区金沙江路 1519-2 弄',
children: [
{
id: 321,
date: '2016-05-21',
name: '王小虎-2-1',
address: '上海市普陀区金沙江路 1519-2-1 弄'
},{
id: 322,
date: '2016-05-22',
name: '王小虎-2-2',
address: '上海市普陀区金沙江路 1519-2-2 弄'
}
]
}]
}, {
id: 4,
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}],
tableData1: [{
id: 1,
date: '2016-05-02',
name: '王小虎',
address: '上海市普陀区金沙江路 1518 弄'
}, {
id: 2,
date: '2016-05-04',
name: '王小虎',
address: '上海市普陀区金沙江路 1517 弄'
}, {
id: 3,
date: '2016-05-01',
name: '王小虎',
address: '上海市普陀区金沙江路 1519 弄',
hasChildren: true
}, {
id: 4,
date: '2016-05-03',
name: '王小虎',
address: '上海市普陀区金沙江路 1516 弄'
}]
}
},
methods: {
onSelectionChange(selection){
console.log(selection)
}
},
}
</script>
```
:::

### Table Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------------- |---------- |-------------------------------- |-------- |
Expand Down Expand Up @@ -1884,6 +2010,7 @@
| lazy | 是否懒加载子节点数据 | Boolean |||
| load | 加载子节点数据的函数,lazy 为 true 时生效,函数第二个参数包含了节点的层级信息 | Function(row, treeNode, resolve) |||
| tree-props | 渲染嵌套数据的配置选项 | Object || { hasChildren: 'hasChildren', children: 'children' } |
| check-strictly | 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false | Boolean | - | false |

### Table Events
| 事件名 | 说明 | 参数 |
Expand Down
1 change: 1 addition & 0 deletions packages/table/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const cellForced = {
return <el-checkbox
nativeOn-click={ (event) => event.stopPropagation() }
value={ store.isSelected(row) }
indeterminate={ store.isHalfSelected(row) }
disabled={ column.selectable ? !column.selectable.call(null, row, $index) : false }
on-input={ () => { store.commit('rowSelectedChanged', row); } } />;
},
Expand Down
85 changes: 71 additions & 14 deletions packages/table/src/store/watcher.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Vue from 'vue';
import merge from 'element-ui/src/utils/merge';
import { getKeysMap, getRowIdentity, getColumnById, getColumnByKey, orderBy, toggleRowStatus } from '../util';
import { getKeysMap, getRowIdentity, getColumnById, getColumnByKey, orderBy, toggleRowStatus, findParentData } from '../util';
import expand from './expand';
import current from './current';
import tree from './tree';
Expand All @@ -25,6 +25,27 @@ const doFlattenColumns = (columns) => {
return result;
};

const changeAllSelectionState = (data, states, selection, status) => {
let selectionChanged = false;
// 这里递归解决
data.forEach((row, index) => {
if (states.selectable) {
if (states.selectable.call(null, row, index) && toggleRowStatus(selection, row, status)) {
selectionChanged = true;
}
} else {
if (toggleRowStatus(selection, row, status)) {
selectionChanged = true;
}
}
if (row[ states.childrenColumnName ]) {
selectionChanged = changeAllSelectionState(row[ states.childrenColumnName ], states, selection, status) || selectionChanged;
}
});

return selectionChanged;
};

export default Vue.extend({
data() {
return {
Expand Down Expand Up @@ -54,6 +75,7 @@ export default Vue.extend({
// 选择
isAllSelected: false,
selection: [],
halfSelection: [],
reserveSelection: false,
selectOnIndeterminate: false,
selectable: null,
Expand Down Expand Up @@ -122,6 +144,11 @@ export default Vue.extend({
return selection.indexOf(row) > -1;
},

isHalfSelected(row) {
const { halfSelection = [] } = this.states;
return halfSelection.indexOf(row) > -1;
},

clearSelection() {
const states = this.states;
states.isAllSelected = false;
Expand Down Expand Up @@ -156,7 +183,47 @@ export default Vue.extend({
},

toggleRowSelection(row, selected, emitChange = true) {
const changed = toggleRowStatus(this.states.selection, row, selected);
let changed = toggleRowStatus(this.states.selection, row, selected);
const {checkStrictly, childrenColumnName, data = []} = this.states;
selected = this.isSelected(row);
// 切换半选状态
const toggleHalfSelect = (row, isHalfSelected) => {
if (typeof isHalfSelected !== 'boolean') {
isHalfSelected = this.isHalfSelected(row) && !selected;
}
toggleRowStatus(this.states.halfSelection, row, isHalfSelected);
};
toggleHalfSelect(row);
// 同步子级数据及父级数据
if (!checkStrictly) {
// 同步子级数据
// 递归调用子数据
const asyncChildrenData = (row) => {
if (row[childrenColumnName]) {
row[childrenColumnName].forEach(child => {
changed = toggleRowStatus(this.states.selection, child, selected) || changed;
toggleHalfSelect(child);
asyncChildrenData(child);
});
}
};
asyncChildrenData(row);
// 同步父数据
// 兄弟数据状态查找
// 兄弟全为选中,父数据添加进selection,再递归同步父数据
// 兄弟不全为选中,selection移除父数据,父数据添加进halfSelection,递归父数据添加进halfSelection
const asyncParentData = (parentData, current, childKey) => {
const parent = findParentData(parentData, current, childKey);
if (parent) {
const isAllSelected = parent[childKey].every(this.isSelected);
const isSomeSelected = parent[childKey].some(child => this.isSelected(child) || this.isHalfSelected(child));
toggleHalfSelect(parent, isSomeSelected && !isAllSelected);
changed = toggleRowStatus(this.states.selection, parent, isAllSelected) || changed;
asyncParentData(parentData, parent, childKey);
}
};
asyncParentData(data, row, childrenColumnName);
}
if (changed) {
const newSelection = (this.states.selection || []).slice();
// 调用 API 修改选中值,不触发 select 事件
Expand All @@ -170,25 +237,15 @@ export default Vue.extend({
_toggleAllSelection() {
const states = this.states;
const { data = [], selection } = states;
states.halfSelection = [];
// when only some rows are selected (but not all), select or deselect all of them
// depending on the value of selectOnIndeterminate
const value = states.selectOnIndeterminate
? !states.isAllSelected
: !(states.isAllSelected || selection.length);
states.isAllSelected = value;

let selectionChanged = false;
data.forEach((row, index) => {
if (states.selectable) {
if (states.selectable.call(null, row, index) && toggleRowStatus(selection, row, value)) {
selectionChanged = true;
}
} else {
if (toggleRowStatus(selection, row, value)) {
selectionChanged = true;
}
}
});
const selectionChanged = changeAllSelectionState(data, states, selection, value);

if (selectionChanged) {
this.table.$emit('selection-change', selection ? selection.slice() : []);
Expand Down
10 changes: 8 additions & 2 deletions packages/table/src/table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,12 @@
lazy: Boolean,
load: Function
load: Function,
checkStrictly: {
type: Boolean,
default: false
}
},
components: {
Expand Down Expand Up @@ -668,7 +673,8 @@
indent: this.indent,
lazy: this.lazy,
lazyColumnIdentifier: hasChildren,
childrenColumnName: children
childrenColumnName: children,
checkStrictly: this.checkStrictly
});
const layout = new TableLayout({
store: this.store,
Expand Down
21 changes: 21 additions & 0 deletions packages/table/src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,24 @@ export function walkTreeNode(root, cb, childrenKey = 'children', lazyKey = 'hasC
}
});
}

export function findParentData(data, row, childKey = 'children') {
let parent = null;
for (let i = 0; i < data.length; i++) {
const item = data[i];
const children = item[childKey] || [];
// 如果children里的数据包含row,则找到parent了
if (children.indexOf(row) > -1) {
parent = item;
}
// 如果没找到并且该item有children,则继续查找该item的children
if (!parent && children.length) {
parent = findParentData(children, row, childKey);
}
// 找到后立刻返回
if (parent) {
return parent;
}
}
return null;
}

0 comments on commit 9735eff

Please sign in to comment.