mirror of https://github.com/ElemeFE/element
Tree: checkbox can be disabled
parent
6f724ea6a3
commit
476f76875c
|
@ -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;
|
let id = 1000;
|
||||||
|
|
||||||
const regions = [{
|
const regions = [{
|
||||||
|
@ -211,6 +240,7 @@
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
data2,
|
data2,
|
||||||
|
data3,
|
||||||
regions,
|
regions,
|
||||||
defaultProps,
|
defaultProps,
|
||||||
props,
|
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
|
### Default expanded and default checked
|
||||||
Tree nodes can be initially expanded or checked
|
Tree nodes can be initially expanded or checked
|
||||||
|
|
||||||
|
|
|
@ -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;
|
let id = 1000;
|
||||||
|
|
||||||
const regions = [{
|
const regions = [{
|
||||||
|
@ -211,6 +240,7 @@
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
data2,
|
data2,
|
||||||
|
data3,
|
||||||
regions,
|
regions,
|
||||||
defaultProps,
|
defaultProps,
|
||||||
props,
|
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`。
|
::: demo 本例展示如何获取和设置选中节点。获取和设置各有两种方式:通过 node 或通过 key。如果需要通过 key 来获取或设置,则必须设置`node-key`。
|
||||||
|
|
|
@ -1,28 +1,45 @@
|
||||||
import objectAssign from 'element-ui/src/utils/merge';
|
import objectAssign from 'element-ui/src/utils/merge';
|
||||||
import { markNodeData, NODE_KEY } from './util';
|
import { markNodeData, NODE_KEY } from './util';
|
||||||
|
|
||||||
const reInitChecked = function(node) {
|
export const getChildState = node => {
|
||||||
const siblings = node.childNodes;
|
|
||||||
|
|
||||||
let all = true;
|
let all = true;
|
||||||
let none = true;
|
let none = true;
|
||||||
|
let allWithoutDisable = true;
|
||||||
|
|
||||||
for (let i = 0, j = siblings.length; i < j; i++) {
|
for (let n of node) {
|
||||||
const sibling = siblings[i];
|
if (n.checked !== true || n.indeterminate) {
|
||||||
if (sibling.checked !== true || sibling.indeterminate) {
|
|
||||||
all = false;
|
all = false;
|
||||||
|
if (!n.disabled) {
|
||||||
|
allWithoutDisable = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (sibling.checked !== false || sibling.indeterminate) {
|
if (n.checked !== false || n.indeterminate) {
|
||||||
none = false;
|
none = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { all, none, allWithoutDisable, half: !all && !none };
|
||||||
|
};
|
||||||
|
|
||||||
|
const reInitChecked = function(node) {
|
||||||
|
const {all, none, half} = getChildState(node.childNodes);
|
||||||
|
|
||||||
if (all) {
|
if (all) {
|
||||||
node.setChecked(true);
|
node.checked = true;
|
||||||
} else if (!all && !none) {
|
node.indeterminate = false;
|
||||||
node.setChecked('half');
|
} else if (half) {
|
||||||
|
node.checked = false;
|
||||||
|
node.indeterminate = true;
|
||||||
} else if (none) {
|
} 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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get disabled() {
|
||||||
|
return getPropertyFromData(this, 'disabled');
|
||||||
|
}
|
||||||
|
|
||||||
insertChild(child, index) {
|
insertChild(child, index) {
|
||||||
if (!child) throw new Error('insertChild error: child is required.');
|
if (!child) throw new Error('insertChild error: child is required.');
|
||||||
|
|
||||||
|
@ -260,16 +281,30 @@ export default class Node {
|
||||||
this.isLeaf = false;
|
this.isLeaf = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setChecked(value, deep) {
|
setChecked(value, deep, recursion, passValue) {
|
||||||
this.indeterminate = value === 'half';
|
this.indeterminate = value === 'half';
|
||||||
this.checked = value === true;
|
this.checked = value === true;
|
||||||
|
let { allWithoutDisable } = getChildState(this.childNodes);
|
||||||
|
|
||||||
|
if (this.childNodes.length && allWithoutDisable) {
|
||||||
|
this.checked = false;
|
||||||
|
value = false;
|
||||||
|
}
|
||||||
|
|
||||||
const handleDescendants = () => {
|
const handleDescendants = () => {
|
||||||
if (deep) {
|
if (deep) {
|
||||||
const childNodes = this.childNodes;
|
const childNodes = this.childNodes;
|
||||||
for (let i = 0, j = childNodes.length; i < j; i++) {
|
for (let i = 0, j = childNodes.length; i < j; i++) {
|
||||||
const child = childNodes[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;
|
const parent = this.parent;
|
||||||
if (!parent || parent.level === 0) return;
|
if (!parent || parent.level === 0) return;
|
||||||
|
|
||||||
if (!this.store.checkStrictly) {
|
if (!this.store.checkStrictly && !recursion) {
|
||||||
reInitChecked(parent);
|
reInitChecked(parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Node from './node';
|
import Node, { getChildState } from './node';
|
||||||
import { getNodeKey } from './util';
|
import { getNodeKey } from './util';
|
||||||
|
|
||||||
export default class TreeStore {
|
export default class TreeStore {
|
||||||
|
@ -188,61 +188,47 @@ export default class TreeStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
_setCheckedKeys(key, leafOnly = false, checkedKeys) {
|
_setCheckedKeys(key, leafOnly = false, checkedKeys) {
|
||||||
const allNodes = this._getAllNodes();
|
let allNodes = this._getAllNodes().sort((a, b) => a.level - b.level);
|
||||||
allNodes.sort((a, b) => b.level - a.level);
|
|
||||||
|
|
||||||
const keys = Object.keys(checkedKeys);
|
const keys = Object.keys(checkedKeys);
|
||||||
allNodes.forEach((node) => {
|
for (let node of allNodes) {
|
||||||
let checked = keys.indexOf(node.data[key] + '') > -1;
|
let checked = keys.indexOf(node.data[key].toString()) > -1;
|
||||||
|
if (!checked) {
|
||||||
|
node.setChecked(false, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!node.isLeaf) {
|
if (node.isLeaf || this.checkStrictly) {
|
||||||
if (!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;
|
const childNodes = node.childNodes;
|
||||||
|
|
||||||
let all = true;
|
childNodes.forEach((child) => {
|
||||||
let none = true;
|
if (!child.isLeaf) {
|
||||||
|
child.setChecked(false, false);
|
||||||
for (let i = 0, j = childNodes.length; i < j; i++) {
|
|
||||||
const child = childNodes[i];
|
|
||||||
if (child.checked !== true || child.indeterminate) {
|
|
||||||
all = false;
|
|
||||||
}
|
}
|
||||||
if (child.checked !== false || child.indeterminate) {
|
traverse(child);
|
||||||
none = false;
|
});
|
||||||
}
|
};
|
||||||
}
|
traverse(node);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setCheckedNodes(array, leafOnly = false) {
|
setCheckedNodes(array, leafOnly = false) {
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
v-if="showCheckbox"
|
v-if="showCheckbox"
|
||||||
v-model="node.checked"
|
v-model="node.checked"
|
||||||
:indeterminate="node.indeterminate"
|
:indeterminate="node.indeterminate"
|
||||||
@change="handleCheckChange"
|
:disabled="!!node.disabled"
|
||||||
@click.native.stop="handleUserClick">
|
@change="handleCheckChange">
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
<span
|
<span
|
||||||
v-if="node.loading"
|
v-if="node.loading"
|
||||||
|
@ -155,16 +155,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleUserClick() {
|
|
||||||
if (this.node.indeterminate) {
|
|
||||||
this.node.setChecked(this.node.checked, !this.tree.checkStrictly);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
handleCheckChange(ev) {
|
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) {
|
handleChildNodeExpand(nodeData, node, instance) {
|
||||||
|
|
|
@ -69,7 +69,8 @@
|
||||||
return {
|
return {
|
||||||
children: 'children',
|
children: 'children',
|
||||||
label: 'label',
|
label: 'label',
|
||||||
icon: 'icon'
|
icon: 'icon',
|
||||||
|
disabled: 'disabled'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -61,6 +61,61 @@ describe('Tree', () => {
|
||||||
}, options), true);
|
}, 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;
|
const ALL_NODE_COUNT = 9;
|
||||||
|
|
||||||
it('create', () => {
|
it('create', () => {
|
||||||
|
@ -344,6 +399,16 @@ describe('Tree', () => {
|
||||||
}, 0);
|
}, 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) => {
|
it('check strictly', (done) => {
|
||||||
vm = getTreeVm(':props="defaultProps" show-checkbox check-strictly');
|
vm = getTreeVm(':props="defaultProps" show-checkbox check-strictly');
|
||||||
const tree = vm.$children[0];
|
const tree = vm.$children[0];
|
||||||
|
|
Loading…
Reference in New Issue