Tree: checkbox can be disabled

pull/5962/head
Dreamacro 2017-07-19 18:36:26 +08:00 committed by 杨奕
parent 6f724ea6a3
commit 476f76875c
7 changed files with 327 additions and 75 deletions

View File

@ -93,6 +93,35 @@
}]
}];
const data3 = [{
id: 1,
label: 'Level one 1',
children: [{
id: 3,
label: 'Level two 2-1',
children: [{
id: 4,
label: 'Level three 3-1-1'
}, {
id: 5,
label: 'Level three 3-1-2',
disabled: true
}]
}, {
id: 2,
label: 'Level two 2-2',
disabled: true,
children: [{
id: 6,
label: 'Level three 3-2-1'
}, {
id: 7,
label: 'Level three 3-2-2',
disabled: true
}]
}]
}];
let id = 1000;
const regions = [{
@ -211,6 +240,7 @@
return {
data,
data2,
data3,
regions,
defaultProps,
props,
@ -363,6 +393,63 @@ Used for node selection. In the following example, data for each layer is acquir
```
:::
### Can disable checkbox
The checkbox of a node can be set as disabled. In the example, 'disabled' property is declared in defaultProps, and some nodes are set as 'disabled:true'. The corresponding checkbox is disabled and can't be clicked.
::: demo
```html
<el-tree
:data="data3"
:props="defaultProps"
show-checkbox
@check-change="handleCheckChange">
</el-tree>
<script>
export default {
data() {
return {
data3: [{
id: 1,
label: 'Level one 1',
children: [{
id: 3,
label: 'Level two 2-1',
children: [{
id: 4,
label: 'Level three 3-1-1'
}, {
id: 5,
label: 'Level three 3-1-2',
disabled: true
}]
}, {
id: 2,
label: 'Level two 2-2',
disabled: true,
children: [{
id: 6,
label: 'Level three 3-2-1'
}, {
id: 7,
label: 'Level three 3-2-2',
disabled: true
}]
}]
}],
defaultProps: {
children: 'children',
label: 'label',
disabled: 'disabled',
},
};
}
};
</script>
```
:::
### Default expanded and default checked
Tree nodes can be initially expanded or checked

View File

@ -93,6 +93,35 @@
}]
}];
const data3 = [{
id: 1,
label: '一级 2',
children: [{
id: 3,
label: '二级 2-1',
children: [{
id: 4,
label: '三级 3-1-1'
}, {
id: 5,
label: '三级 3-1-2',
disabled: true
}]
}, {
id: 2,
label: '二级 2-2',
disabled: true,
children: [{
id: 6,
label: '三级 3-2-1'
}, {
id: 7,
label: '三级 3-2-2',
disabled: true
}]
}]
}];
let id = 1000;
const regions = [{
@ -211,6 +240,7 @@
return {
data,
data2,
data3,
regions,
defaultProps,
props,
@ -427,6 +457,62 @@
```
:::
### 禁用状态
可将 Tree 的某些节点设置为禁用状态
::: demo 通过`disabled`设置禁用状态。
```html
<el-tree
:data="data3"
show-checkbox
node-key="id"
:default-expanded-keys="[2, 3]"
:default-checked-keys="[5]">
</el-tree>
<script>
export default {
data() {
return {
data3: [{
id: 1,
label: '一级 2',
children: [{
id: 3,
label: '二级 2-1',
children: [{
id: 4,
label: '三级 3-1-1'
}, {
id: 5,
label: '三级 3-1-2',
disabled: true
}]
}, {
id: 2,
label: '二级 2-2',
disabled: true,
children: [{
id: 6,
label: '三级 3-2-1'
}, {
id: 7,
label: '三级 3-2-2',
disabled: true
}]
}]
}],
defaultProps: {
children: 'children',
label: 'label'
}
};
}
};
</script>
```
:::
### 树节点的选择
::: demo 本例展示如何获取和设置选中节点。获取和设置各有两种方式:通过 node 或通过 key。如果需要通过 key 来获取或设置,则必须设置`node-key`。

View File

@ -1,28 +1,45 @@
import objectAssign from 'element-ui/src/utils/merge';
import { markNodeData, NODE_KEY } from './util';
const reInitChecked = function(node) {
const siblings = node.childNodes;
export const getChildState = node => {
let all = true;
let none = true;
let allWithoutDisable = true;
for (let i = 0, j = siblings.length; i < j; i++) {
const sibling = siblings[i];
if (sibling.checked !== true || sibling.indeterminate) {
for (let n of node) {
if (n.checked !== true || n.indeterminate) {
all = false;
if (!n.disabled) {
allWithoutDisable = false;
}
}
if (sibling.checked !== false || sibling.indeterminate) {
if (n.checked !== false || n.indeterminate) {
none = false;
}
}
return { all, none, allWithoutDisable, half: !all && !none };
};
const reInitChecked = function(node) {
const {all, none, half} = getChildState(node.childNodes);
if (all) {
node.setChecked(true);
} else if (!all && !none) {
node.setChecked('half');
node.checked = true;
node.indeterminate = false;
} else if (half) {
node.checked = false;
node.indeterminate = true;
} else if (none) {
node.setChecked(false);
node.checked = false;
node.indeterminate = false;
}
const parent = node.parent;
if (!parent || parent.level === 0) return;
if (!node.store.checkStrictly) {
reInitChecked(parent);
}
};
@ -145,6 +162,10 @@ export default class Node {
return null;
}
get disabled() {
return getPropertyFromData(this, 'disabled');
}
insertChild(child, index) {
if (!child) throw new Error('insertChild error: child is required.');
@ -260,16 +281,30 @@ export default class Node {
this.isLeaf = false;
}
setChecked(value, deep) {
setChecked(value, deep, recursion, passValue) {
this.indeterminate = value === 'half';
this.checked = value === true;
let { allWithoutDisable } = getChildState(this.childNodes);
if (this.childNodes.length && allWithoutDisable) {
this.checked = false;
value = false;
}
const handleDescendants = () => {
if (deep) {
const childNodes = this.childNodes;
for (let i = 0, j = childNodes.length; i < j; i++) {
const child = childNodes[i];
child.setChecked(value !== false, deep);
passValue = passValue || value !== false;
const isCheck = child.disabled ? child.checked : passValue;
child.setChecked(isCheck, deep, true, passValue);
}
const { half, all } = getChildState(childNodes);
console.log(this.data.label, all);
if (!all) {
this.checked = all;
this.indeterminate = half;
}
}
};
@ -288,7 +323,7 @@ export default class Node {
const parent = this.parent;
if (!parent || parent.level === 0) return;
if (!this.store.checkStrictly) {
if (!this.store.checkStrictly && !recursion) {
reInitChecked(parent);
}
}

View File

@ -1,4 +1,4 @@
import Node from './node';
import Node, { getChildState } from './node';
import { getNodeKey } from './util';
export default class TreeStore {
@ -188,61 +188,47 @@ export default class TreeStore {
}
_setCheckedKeys(key, leafOnly = false, checkedKeys) {
const allNodes = this._getAllNodes();
allNodes.sort((a, b) => b.level - a.level);
let allNodes = this._getAllNodes().sort((a, b) => a.level - b.level);
const keys = Object.keys(checkedKeys);
allNodes.forEach((node) => {
let checked = keys.indexOf(node.data[key] + '') > -1;
for (let node of allNodes) {
let checked = keys.indexOf(node.data[key].toString()) > -1;
if (!checked) {
node.setChecked(false, false);
continue;
}
if (!node.isLeaf) {
if (!this.checkStrictly) {
if (node.isLeaf || this.checkStrictly) {
node.setChecked(checked, false);
continue;
}
const { all, none, half } = getChildState(node.childNodes);
if (all) {
node.setChecked(true, !this.checkStrictly);
} else if (half) {
checked = checked ? true : 'half';
node.setChecked(checked, !this.checkStrictly && checked === true);
} else if (none) {
node.setChecked(checked, !this.checkStrictly);
}
if (leafOnly) {
node.setChecked(false, false);
const traverse = function(node) {
const childNodes = node.childNodes;
let all = true;
let none = true;
for (let i = 0, j = childNodes.length; i < j; i++) {
const child = childNodes[i];
if (child.checked !== true || child.indeterminate) {
all = false;
childNodes.forEach((child) => {
if (!child.isLeaf) {
child.setChecked(false, false);
}
if (child.checked !== false || child.indeterminate) {
none = false;
}
}
if (all) {
node.setChecked(true, !this.checkStrictly);
} else if (!all && !none) {
checked = checked ? true : 'half';
node.setChecked(checked, !this.checkStrictly && checked === true);
} else if (none) {
node.setChecked(checked, !this.checkStrictly);
}
} else {
node.setChecked(checked, false);
}
if (leafOnly) {
node.setChecked(false, false);
const traverse = function(node) {
const childNodes = node.childNodes;
childNodes.forEach((child) => {
if (!child.isLeaf) {
child.setChecked(false, false);
}
traverse(child);
});
};
traverse(node);
}
} else {
node.setChecked(checked, false);
traverse(child);
});
};
traverse(node);
}
});
}
}
setCheckedNodes(array, leafOnly = false) {

View File

@ -18,8 +18,8 @@
v-if="showCheckbox"
v-model="node.checked"
:indeterminate="node.indeterminate"
@change="handleCheckChange"
@click.native.stop="handleUserClick">
:disabled="!!node.disabled"
@change="handleCheckChange">
</el-checkbox>
<span
v-if="node.loading"
@ -155,16 +155,8 @@
}
},
handleUserClick() {
if (this.node.indeterminate) {
this.node.setChecked(this.node.checked, !this.tree.checkStrictly);
}
},
handleCheckChange(ev) {
if (!this.node.indeterminate) {
this.node.setChecked(ev.target.checked, !this.tree.checkStrictly);
}
this.node.setChecked(ev.target.checked, !this.tree.checkStrictly);
},
handleChildNodeExpand(nodeData, node, instance) {

View File

@ -69,7 +69,8 @@
return {
children: 'children',
label: 'label',
icon: 'icon'
icon: 'icon',
disabled: 'disabled'
};
}
},

View File

@ -61,6 +61,61 @@ describe('Tree', () => {
}, options), true);
};
const getDisableTreeVm = (props, options) => {
return createVue(Object.assign({
template: `
<el-tree ref="tree" :data="data" ${ props }></el-tree>
`,
data() {
return {
defaultExpandedKeys: [],
defaultCheckedKeys: [],
clickedNode: null,
count: 1,
data: [{
id: 1,
label: '一级 1',
children: [{
id: 11,
label: '二级 1-1',
children: [{
id: 111,
label: '三级 1-1',
disabled: true
}]
}]
}, {
id: 2,
label: '一级 2',
children: [{
id: 21,
label: '二级 2-1'
}, {
id: 22,
label: '二级 2-2'
}]
}, {
id: 3,
label: '一级 3',
children: [{
id: 31,
label: '二级 3-1'
}, {
id: 32,
label: '二级 3-2'
}]
}],
defaultProps: {
children: 'children',
label: 'label',
disabled: 'disabled'
}
};
}
}, options), true);
};
const ALL_NODE_COUNT = 9;
it('create', () => {
@ -344,6 +399,16 @@ describe('Tree', () => {
}, 0);
});
it('set disabled checkbox', done => {
vm = getDisableTreeVm(':props="defaultProps" show-checkbox node-key="id"');
const node = document.querySelectorAll('.el-tree-node__content')[2];
const nodeCheckbox = node.querySelector('.el-checkbox input');
vm.$nextTick(() => {
expect(nodeCheckbox.disabled).to.equal(true);
done();
});
});
it('check strictly', (done) => {
vm = getTreeVm(':props="defaultProps" show-checkbox check-strictly');
const tree = vm.$children[0];