Tree: add defaultExpandKeys & defaultCheckedKeys. (#1088)

pull/1105/head
FuryBean 2016-11-16 15:35:46 +08:00 committed by cinwell.li
parent af4bca8abe
commit 5203280af1
12 changed files with 385 additions and 97 deletions

View File

@ -1,5 +1,5 @@
<script> <script>
var data = [{ const data = [{
label: 'Level one 1', label: 'Level one 1',
children: [{ children: [{
label: 'Level two 1-1' label: 'Level two 1-1'
@ -20,23 +20,17 @@
}] }]
}]; }];
var regions = [{ const regions = [{
'name': 'region1' 'name': 'region1'
}, { }, {
'name': 'region2' 'name': 'region2'
}]; }];
var count = 1; let count = 1;
var props = { const props = {
label: 'name', label: 'name',
children: 'zones', children: 'zones'
icon(data, node) {
if (node.isLeaf) {
return 'el-icon-close';
}
return 'el-icon-search';
}
}; };
var defaultProps = { var defaultProps = {
@ -53,10 +47,10 @@
console.log(data); console.log(data);
}, },
loadNode(node, resolve) { loadNode(node, resolve) {
if (node.level === -1) { if (node.level === 0) {
return resolve([{ name: 'region1' }, { name: 'region2' }]); return resolve([{ name: 'Root1' }, { name: 'Root2' }]);
} }
if (node.level > 4) return resolve([]); if (node.level > 3) return resolve([]);
var hasChild; var hasChild;
if (node.data.name === 'region1') { if (node.data.name === 'region1') {
hasChild = true; hasChild = true;
@ -67,7 +61,7 @@
} }
setTimeout(function() { setTimeout(function() {
var data; let data;
if (hasChild) { if (hasChild) {
data = [{ data = [{
name: 'zone' + count++ name: 'zone' + count++
@ -185,10 +179,10 @@ Used for node selection. In the following example, data for each layer is acquir
console.log(data); console.log(data);
}, },
loadNode(node, resolve) { loadNode(node, resolve) {
if (node.level === -1) { if (node.level === 0) {
return resolve([{ name: 'region1' }, { name: 'region2' }]); return resolve([{ name: 'Root1' }, { name: 'Root2' }]);
} }
if (node.level > 4) return resolve([]); if (node.level > 3) return resolve([]);
var hasChild; var hasChild;
if (node.data.name === 'region1') { if (node.data.name === 'region1') {

View File

@ -11,7 +11,7 @@
</style> </style>
<script> <script>
var data = [{ const data = [{
label: '一级 1', label: '一级 1',
children: [{ children: [{
label: '二级 1-1' label: '二级 1-1'
@ -32,23 +32,17 @@
}] }]
}]; }];
var regions = [{ const regions = [{
'name': 'region1' 'name': 'region1'
}, { }, {
'name': 'region2' 'name': 'region2'
}]; }];
var count = 1; let count = 1;
var props = { const props = {
label: 'name', label: 'name',
children: 'zones', children: 'zones'
icon(data, node) {
if (node.isLeaf) {
return 'el-icon-close';
}
return 'el-icon-search';
}
}; };
var defaultProps = { var defaultProps = {
@ -65,10 +59,10 @@
console.log(data); console.log(data);
}, },
loadNode(node, resolve) { loadNode(node, resolve) {
if (node.level === -1) { if (node.level === 0) {
return resolve([{ name: 'region1' }, { name: 'region2' }]); return resolve([{ name: 'region1' }, { name: 'region2' }]);
} }
if (node.level > 4) return resolve([]); if (node.level > 3) return resolve([]);
var hasChild; var hasChild;
if (node.data.name === 'region1') { if (node.data.name === 'region1') {
hasChild = true; hasChild = true;
@ -197,10 +191,10 @@
console.log(data); console.log(data);
}, },
loadNode(node, resolve) { loadNode(node, resolve) {
if (node.level === -1) { if (node.level === 0) {
return resolve([{ name: 'region1' }, { name: 'region2' }]); return resolve([{ name: 'region1' }, { name: 'region2' }]);
} }
if (node.level > 4) return resolve([]); if (node.level > 3) return resolve([]);
var hasChild; var hasChild;
if (node.data.name === 'region1') { if (node.data.name === 'region1') {
@ -236,11 +230,18 @@
| 参数 | 说明 | 类型 | 可选值 | 默认值 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------------- |---------- |-------------------------------- |-------- | |---------- |-------------- |---------- |-------------------------------- |-------- |
| data | 展示数据 | array | — | — | | data | 展示数据 | array | — | — |
| empty-text | 内容为空的时候展示的文本 | String | — | — |
| node-key | 每个树节点用来作为唯一标识的属性,整颗树应该是唯一的 | String | — | — |
| props | 配置选项,具体看下表 | object | — | — | | props | 配置选项,具体看下表 | object | — | — |
| load | 加载子树数据的方法 | function(node, resolve) | — | — | | load | 加载子树数据的方法 | function(node, resolve) | — | — |
| show-checkbox | 节点是否可被选择 | boolean | — | false |
| render-content | 树节点的内容区的渲染 Function | Function(h, { node } | - | - | | render-content | 树节点的内容区的渲染 Function | Function(h, { node } | - | - |
| highlight-current | 是否高亮当前选中节点,默认值是 false。| boolean | - | false | | highlight-current | 是否高亮当前选中节点,默认值是 false。| boolean | - | false |
| default-expand-all | 是否默认展开所有节点 | boolean | - | false |
| auto-expand-parent | 展开子节点的时候是否自动展开父节点 | boolean | — | true |
| default-expand-keys | 默认展开的节点的 key 的数组 | array | — | — |
| show-checkbox | 节点是否可被选择 | boolean | — | false |
| check-strictly | 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false | boolean | — | false |
| default-checked-keys | 默认勾选的节点的 key 的数组 | array | — | — |
### props ### props
| 参数 | 说明 | 类型 | 可选值 | 默认值 | | 参数 | 说明 | 类型 | 可选值 | 默认值 |
@ -253,6 +254,7 @@
| 方法名 | 说明 | 参数 | | 方法名 | 说明 | 参数 |
|------|--------|------| |------|--------|------|
| getCheckedNodes | 若节点可被选择(即 `show-checkbox``true`<br>则返回目前被选中的节点所组成的数组 | 接收一个 boolean 类型的参数,若为 `true`<br>仅返回被选中的叶子节点,默认值为 `false` | | getCheckedNodes | 若节点可被选择(即 `show-checkbox``true`<br>则返回目前被选中的节点所组成的数组 | 接收一个 boolean 类型的参数,若为 `true`<br>仅返回被选中的叶子节点,默认值为 `false` |
| setCheckedNodes | 设置目前勾选的节点,使用此方法必须设置 node-keys 属性 | 接收勾选节点数据的数组 |
### Events ### Events
| 事件名称 | 说明 | 回调参数 | | 事件名称 | 说明 | 回调参数 |

View File

@ -6,6 +6,20 @@
cursor: default; cursor: default;
background: #ffffff; background: #ffffff;
border: 1px solid #d3dce6; border: 1px solid #d3dce6;
@e empty-block {
display: table;
min-height: 60px;
text-align: center;
width: 100%;
height: 100%;
}
@e empty-text {
display: table-cell;
vertical-align: middle;
color: #5e6d82;
}
} }
@b tree-node { @b tree-node {
@ -63,9 +77,10 @@
display: inline-block; display: inline-block;
} }
@e icon { @e loading-icon {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
margin-right: 4px;
font-size: 14px; font-size: 14px;
color: #99a9bf; color: #99a9bf;
} }
@ -77,7 +92,7 @@
display: none; display: none;
} }
&.expanded > .el-tree-node__children { &.is-expanded > .el-tree-node__children {
display: block; display: block;
} }
} }

View File

@ -1,4 +1,3 @@
let nodeIdSeed = 0;
import objectAssign from 'element-ui/src/utils/merge'; import objectAssign from 'element-ui/src/utils/merge';
const reInitChecked = function(node) { const reInitChecked = function(node) {
@ -27,7 +26,7 @@ const reInitChecked = function(node) {
}; };
const getPropertyFromData = function(node, prop) { const getPropertyFromData = function(node, prop) {
const props = node.props; const props = node._tree.props;
const data = node.data || {}; const data = node.data || {};
const config = props[prop]; const config = props[prop];
@ -40,6 +39,8 @@ const getPropertyFromData = function(node, prop) {
} }
}; };
let nodeIdSeed = 0;
export default class Node { export default class Node {
constructor(options) { constructor(options) {
this.id = nodeIdSeed++; this.id = nodeIdSeed++;
@ -48,9 +49,7 @@ export default class Node {
this.indeterminate = false; this.indeterminate = false;
this.data = null; this.data = null;
this.expanded = false; this.expanded = false;
this.props = null;
this.parent = null; this.parent = null;
this.lazy = false;
for (let name in options) { for (let name in options) {
if (options.hasOwnProperty(name)) { if (options.hasOwnProperty(name)) {
@ -59,7 +58,7 @@ export default class Node {
} }
// internal // internal
this.level = -1; this.level = 0;
this.loaded = false; this.loaded = false;
this.childNodes = []; this.childNodes = [];
this.loading = false; this.loading = false;
@ -68,8 +67,39 @@ export default class Node {
this.level = this.parent.level + 1; this.level = this.parent.level + 1;
} }
if (this.lazy !== true && this.data) { const tree = this._tree;
if (!tree) {
throw new Error('[Node]_tree is required!');
}
tree.registerNode(this);
if (tree.lazy !== true && this.data) {
this.setData(this.data); this.setData(this.data);
if (tree.defaultExpandAll) {
this.expanded = true;
}
} else if (this.level > 0 && tree.lazy && tree.defaultExpandAll) {
this.expand();
}
if (!this.data) return;
const defaultExpandedKeys = tree.defaultExpandedKeys;
const key = tree.key;
if (key && defaultExpandedKeys && defaultExpandedKeys.indexOf(this.key) !== -1) {
if (tree.autoExpandParent) {
let parent = this.parent;
while (parent.level > 0) {
parent.expanded = true;
parent = parent.parent;
}
}
this.expand();
}
if (tree.lazy) {
tree._initDefaultCheckedNode(this);
} }
} }
@ -87,7 +117,7 @@ export default class Node {
this.childNodes = []; this.childNodes = [];
let children; let children;
if (this.level === -1 && this.data instanceof Array) { if (this.level === 0 && this.data instanceof Array) {
children = this.data; children = this.data;
} else { } else {
children = getPropertyFromData(this, 'children') || []; children = getPropertyFromData(this, 'children') || [];
@ -106,15 +136,19 @@ export default class Node {
return getPropertyFromData(this, 'icon'); return getPropertyFromData(this, 'icon');
} }
get key() {
const nodeKey = this._tree.key;
if (this.data) return this.data[nodeKey];
return null;
}
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.');
if (!(child instanceof Node)) { if (!(child instanceof Node)) {
objectAssign(child, { objectAssign(child, {
parent: this, parent: this,
lazy: this.lazy, _tree: this._tree
load: this.load,
props: this.props
}); });
child = new Node(child); child = new Node(child);
} }
@ -132,6 +166,7 @@ export default class Node {
const index = this.childNodes.indexOf(child); const index = this.childNodes.indexOf(child);
if (index > -1) { if (index > -1) {
this._tree && this._tree.deregisterNode(child);
child.parent = null; child.parent = null;
this.childNodes.splice(index, 1); this.childNodes.splice(index, 1);
} }
@ -154,14 +189,13 @@ export default class Node {
if (this.shouldLoadData()) { if (this.shouldLoadData()) {
this.loadData((data) => { this.loadData((data) => {
if (data instanceof Array) { if (data instanceof Array) {
callback(); this.expanded = true;
if (callback) callback();
} }
}); });
} else { } else {
this.expanded = true; this.expanded = true;
if (callback) { if (callback) callback();
callback();
}
} }
} }
@ -176,7 +210,7 @@ export default class Node {
} }
shouldLoadData() { shouldLoadData() {
return this.lazy === true && this.load && !this.loaded; return this._tree.lazy === true && this._tree.load && !this.loaded;
} }
get isLeaf() { get isLeaf() {
@ -185,7 +219,7 @@ export default class Node {
hasChild() { hasChild() {
const childNodes = this.childNodes; const childNodes = this.childNodes;
if (!this.lazy || (this.lazy === true && this.loaded === true)) { if (!this._tree.lazy || (this._tree.lazy === true && this.loaded === true)) {
return childNodes && childNodes.length > 0; return childNodes && childNodes.length > 0;
} }
return true; return true;
@ -195,7 +229,7 @@ export default class Node {
this.indeterminate = value === 'half'; this.indeterminate = value === 'half';
this.checked = value === true; this.checked = value === true;
const handleDeep = () => { 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++) {
@ -205,28 +239,30 @@ export default class Node {
} }
}; };
if (this.shouldLoadData()) { if (!this._tree.checkStrictly && this.shouldLoadData()) {
// Only work on lazy load data. // Only work on lazy load data.
this.loadData(() => { this.loadData(() => {
handleDeep(); handleDescendants();
}, { }, {
checked: value !== false checked: value !== false
}); });
} else { } else {
handleDeep(); handleDescendants();
} }
const parent = this.parent; const parent = this.parent;
if (parent.level === -1) return; if (!parent || parent.level === 0) return;
reInitChecked(parent); if (!this._tree.checkStrictly) {
reInitChecked(parent);
}
} }
getChildren() { // this is data getChildren() { // this is data
const data = this.data; const data = this.data;
if (!data) return null; if (!data) return null;
const props = this.props; const props = this._tree.props;
let children = 'children'; let children = 'children';
if (props) { if (props) {
children = props.children || 'children'; children = props.children || 'children';
@ -259,7 +295,7 @@ export default class Node {
} }
loadData(callback, defaultProps = {}) { loadData(callback, defaultProps = {}) {
if (this.lazy === true && this.load && !this.loaded) { if (this._tree.lazy === true && this._tree.load && !this.loaded && !this.loading) {
this.loading = true; this.loading = true;
const resolve = (children) => { const resolve = (children) => {
@ -274,7 +310,7 @@ export default class Node {
} }
}; };
this.load(this, resolve); this._tree.load(this, resolve);
} else { } else {
if (callback) { if (callback) {
callback.call(this); callback.call(this);

View File

@ -8,37 +8,111 @@ export default class Tree {
} }
} }
this.nodesMap = {};
this.root = new Node({ this.root = new Node({
data: this.data, data: this.data,
lazy: this.lazy, _tree: this
props: this.props,
load: this.load
}); });
if (this.lazy && this.load) { if (this.lazy && this.load) {
const loadFn = this.load; const loadFn = this.load;
loadFn(this.root, (data) => { loadFn(this.root, (data) => {
this.root.doCreateChildren(data); this.root.doCreateChildren(data);
this._initDefaultCheckedNodes();
}); });
} else {
this._initDefaultCheckedNodes();
} }
} }
setData(newVal) {
const instanceChanged = newVal !== this.root.data;
this.root.setData(newVal);
if (instanceChanged) {
this._initDefaultCheckedNodes();
}
}
_initDefaultCheckedNodes() {
const defaultCheckedKeys = this.defaultCheckedKeys || [];
const nodesMap = this.nodesMap;
defaultCheckedKeys.forEach((checkedKey) => {
const node = nodesMap[checkedKey];
if (node) {
node.setChecked(true, !this.checkStrictly);
}
});
}
_initDefaultCheckedNode(node) {
const defaultCheckedKeys = this.defaultCheckedKeys || [];
if (defaultCheckedKeys.indexOf(node.key) !== -1) {
node.setChecked(true, !this.checkStrictly);
}
}
setDefaultCheckedKey(newVal) {
if (newVal !== this.defaultCheckedKeys) {
this.defaultCheckedKeys = newVal;
this._initDefaultCheckedNodes();
}
}
registerNode(node) {
const key = this.key;
if (!key || !node || !node.data) return;
this.nodesMap[node.key] = node;
}
deregisterNode(node) {
const key = this.key;
if (!key || !node || !node.data) return;
delete this.nodesMap[node.key];
}
getCheckedNodes(leafOnly) { getCheckedNodes(leafOnly) {
const checkedNodes = []; const checkedNodes = [];
const walk = function(node) { const traverse = function(node) {
const childNodes = node.root ? node.root.childNodes : node.childNodes; const childNodes = node.root ? node.root.childNodes : node.childNodes;
childNodes.forEach(function(child) { childNodes.forEach((child) => {
if ((!leafOnly && child.checked) || (leafOnly && child.isLeaf && child.checked)) { if ((!leafOnly && child.checked) || (leafOnly && child.isLeaf && child.checked)) {
checkedNodes.push(child.data); checkedNodes.push(child.data);
} }
walk(child); traverse(child);
}); });
}; };
walk(this); traverse(this);
return checkedNodes; return checkedNodes;
} }
setCheckedNodes(array) {
const key = this.key;
const checkedKeys = {};
array.forEach((item) => {
checkedKeys[(item || {})[key]] = true;
});
const allNodes = [];
const nodesMap = this.nodesMap;
for (let nodeKey in nodesMap) {
if (nodesMap.hasOwnProperty(nodeKey)) {
allNodes.push(nodesMap[nodeKey]);
}
}
allNodes.sort((a, b) => a.level > b.level ? -1 : 1);
allNodes.forEach((node) => {
node.setChecked(!!checkedKeys[(node.data || {})[key]], !this.checkStrictly);
});
}
}; };

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="el-tree-node" <div class="el-tree-node"
@click.stop="handleClick" @click.stop="handleClick"
:class="{ expanded: childNodeRendered && expanded, 'is-current': $tree.currentNode === _self }"> :class="{ 'is-expanded': childNodeRendered && expanded, 'is-current': $tree.currentNode === _self }">
<div class="el-tree-node__content" <div class="el-tree-node__content"
:style="{ 'padding-left': node.level * 16 + 'px' }" :style="{ 'padding-left': (node.level - 1) * 16 + 'px' }"
@click="handleExpandIconClick"> @click="handleExpandIconClick">
<span <span
class="el-tree-node__expand-icon" class="el-tree-node__expand-icon"
@ -18,7 +18,7 @@
</el-checkbox> </el-checkbox>
<span <span
v-if="node.loading" v-if="node.loading"
class="el-tree-node__icon el-icon-loading"> class="el-tree-node__loading-icon el-icon-loading">
</span> </span>
<node-content :node="node"></node-content> <node-content :node="node"></node-content>
</div> </div>
@ -29,6 +29,7 @@
<el-tree-node <el-tree-node
:render-content="renderContent" :render-content="renderContent"
v-for="child in node.childNodes" v-for="child in node.childNodes"
:key="getNodeKey(child)"
:node="child"> :node="child">
</el-tree-node> </el-tree-node>
</div> </div>
@ -89,10 +90,25 @@
'node.checked'(val) { 'node.checked'(val) {
this.handleSelectChange(val, this.node.indeterminate); this.handleSelectChange(val, this.node.indeterminate);
},
'node.expanded'(val) {
this.expanded = val;
if (val) {
this.childNodeRendered = true;
}
} }
}, },
methods: { methods: {
getNodeKey(node, index) {
const nodeKey = this.$tree.nodeKey;
if (nodeKey && node) {
return node.data[nodeKey];
}
return index;
},
handleSelectChange(checked, indeterminate) { handleSelectChange(checked, indeterminate) {
if (this.oldChecked !== checked && this.oldIndeterminate !== indeterminate) { if (this.oldChecked !== checked && this.oldIndeterminate !== indeterminate) {
this.$tree.$emit('check-change', this.node.data, checked, indeterminate); this.$tree.$emit('check-change', this.node.data, checked, indeterminate);
@ -112,31 +128,27 @@
target.nodeName.toUpperCase() === 'LABEL') return; target.nodeName.toUpperCase() === 'LABEL') return;
if (this.expanded) { if (this.expanded) {
this.node.collapse(); this.node.collapse();
this.expanded = false;
} else { } else {
this.node.expand(() => { this.node.expand();
this.expanded = true;
this.childNodeRendered = true;
});
} }
this.$tree.$emit('node-click', this.node.data, this.node, this); this.$tree.$emit('node-click', this.node.data, this.node, this);
}, },
handleUserClick() { handleUserClick() {
if (this.node.indeterminate) { if (this.node.indeterminate) {
this.node.setChecked(this.node.checked, true); this.node.setChecked(this.node.checked, !this.$tree.checkStrictly);
} }
}, },
handleCheckChange(ev) { handleCheckChange(ev) {
if (!this.node.indeterminate) { if (!this.node.indeterminate) {
this.node.setChecked(ev.target.checked, true); this.node.setChecked(ev.target.checked, !this.$tree.checkStrictly);
} }
} }
}, },
created() { created() {
var parent = this.$parent; const parent = this.$parent;
if (parent.$isTree) { if (parent.$isTree) {
this.$tree = parent; this.$tree = parent;
@ -157,6 +169,11 @@
} }
this.showCheckbox = tree.showCheckbox; this.showCheckbox = tree.showCheckbox;
if (this.node.expanded) {
this.expanded = true;
this.childNodeRendered = true;
}
} }
}; };
</script> </script>

View File

@ -6,11 +6,15 @@
:props="props" :props="props"
:render-content="renderContent"> :render-content="renderContent">
</el-tree-node> </el-tree-node>
<div class="el-tree__empty-block" v-if="!tree.root.childNodes || tree.root.childNodes.length === 0">
<span class="el-tree__empty-text">{{ emptyText }}</span>
</div>
</div> </div>
</template> </template>
<script type="text/ecmascript-6"> <script type="text/ecmascript-6">
import Tree from './model/tree'; import Tree from './model/tree';
import { t } from 'element-ui/src/locale';
export default { export default {
name: 'el-tree', name: 'el-tree',
@ -19,6 +23,19 @@
data: { data: {
type: Array type: Array
}, },
emptyText: {
type: String,
default: t('el.tree.emptyText')
},
nodeKey: String,
checkStrictly: Boolean,
defaultExpandAll: Boolean,
autoExpandParent: {
type: Boolean,
default: true
},
defaultCheckedKeys: Array,
defaultExpandedKeys: Array,
renderContent: Function, renderContent: Function,
showCheckbox: { showCheckbox: {
type: Boolean, type: Boolean,
@ -38,19 +55,23 @@
default: false default: false
}, },
highlightCurrent: Boolean, highlightCurrent: Boolean,
load: { load: Function
type: Function
}
}, },
created() { created() {
this.$isTree = true; this.$isTree = true;
this.tree = new Tree({ this.tree = new Tree({
key: this.nodeKey,
data: this.data, data: this.data,
lazy: this.lazy, lazy: this.lazy,
props: this.props, props: this.props,
load: this.load load: this.load,
checkStrictly: this.checkStrictly,
defaultCheckedKeys: this.defaultCheckedKeys,
defaultExpandedKeys: this.defaultExpandedKeys,
autoExpandParent: this.autoExpandParent,
defaultExpandAll: this.defaultExpandAll
}); });
}, },
@ -78,13 +99,20 @@
watch: { watch: {
data(newVal) { data(newVal) {
this.tree.root.setData(newVal); this.tree.setData(newVal);
},
defaultCheckedKeys(newVal) {
this.tree.setDefaultCheckedKey(newVal);
} }
}, },
methods: { methods: {
getCheckedNodes(leafOnly) { getCheckedNodes(leafOnly) {
return this.tree.getCheckedNodes(leafOnly); return this.tree.getCheckedNodes(leafOnly);
},
setCheckedNodes(nodes) {
if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCheckedNodes');
this.tree.setCheckedNodes(nodes);
} }
} }
}; };

View File

@ -77,6 +77,9 @@ export default {
confirmFilter: 'filtern', confirmFilter: 'filtern',
resetFilter: 'rücksetzen', resetFilter: 'rücksetzen',
clearFilter: 'alles' clearFilter: 'alles'
},
tree: {
emptyText: 'keine Daten'
} }
} }
}; };

View File

@ -77,6 +77,9 @@ export default {
confirmFilter: 'Confirm', confirmFilter: 'Confirm',
resetFilter: 'Reset', resetFilter: 'Reset',
clearFilter: 'All' clearFilter: 'All'
},
tree: {
emptyText: 'No Data'
} }
} }
}; };

View File

@ -77,6 +77,9 @@ export default {
confirmFilter: 'Confirmar', confirmFilter: 'Confirmar',
resetFilter: 'Limpar', resetFilter: 'Limpar',
clearFilter: 'Todos' clearFilter: 'Todos'
},
tree: {
emptyText: 'Sem dados'
} }
} }
}; };

View File

@ -77,6 +77,9 @@ export default {
confirmFilter: '筛选', confirmFilter: '筛选',
resetFilter: '重置', resetFilter: '重置',
clearFilter: '全部' clearFilter: '全部'
},
tree: {
emptyText: '暂无数据'
} }
} }
}; };

View File

@ -1,5 +1,7 @@
import { createVue, destroyVM } from '../util'; import { createVue, destroyVM } from '../util';
const DELAY = 10;
describe('Tree', () => { describe('Tree', () => {
let vm; let vm;
afterEach(() => { afterEach(() => {
@ -9,30 +11,44 @@ describe('Tree', () => {
const getTreeVm = (props, options) => { const getTreeVm = (props, options) => {
return createVue(Object.assign({ return createVue(Object.assign({
template: ` template: `
<el-tree :data="data" ${ props }></el-tree> <el-tree ref="tree" :data="data" ${ props }></el-tree>
`, `,
data() { data() {
return { return {
defaultExpandedKeys: [],
defaultCheckedKeys: [],
clickedNode: null, clickedNode: null,
count: 1, count: 1,
data: [{ data: [{
id: 1,
label: '一级 1', label: '一级 1',
children: [{ children: [{
label: '二级 1-1' id: 11,
label: '二级 1-1',
children: [{
id: 111,
label: '三级 1-1'
}]
}] }]
}, { }, {
id: 2,
label: '一级 2', label: '一级 2',
children: [{ children: [{
id: 21,
label: '二级 2-1' label: '二级 2-1'
}, { }, {
id: 22,
label: '二级 2-2' label: '二级 2-2'
}] }]
}, { }, {
id: 3,
label: '一级 3', label: '一级 3',
children: [{ children: [{
id: 31,
label: '二级 3-1' label: '二级 3-1'
}, { }, {
id: 32,
label: '二级 3-2' label: '二级 3-2'
}] }]
}], }],
@ -45,11 +61,13 @@ describe('Tree', () => {
}, options), true); }, options), true);
}; };
const ALL_NODE_COUNT = 9;
it('create', () => { it('create', () => {
vm = getTreeVm(':props="defaultProps"'); vm = getTreeVm(':props="defaultProps"');
expect(document.querySelector('.el-tree')).to.exist; expect(document.querySelector('.el-tree')).to.exist;
expect(document.querySelectorAll('.el-tree > .el-tree-node').length).to.equal(3); expect(document.querySelectorAll('.el-tree > .el-tree-node').length).to.equal(3);
expect(document.querySelectorAll('.el-tree .el-tree-node').length).to.equal(8); expect(document.querySelectorAll('.el-tree .el-tree-node').length).to.equal(ALL_NODE_COUNT);
vm.data[1].children = [{ label: '二级 2-1' }]; vm.data[1].children = [{ label: '二级 2-1' }];
const tree = vm.$children[0]; const tree = vm.$children[0];
expect(tree.children).to.deep.equal(vm.data); expect(tree.children).to.deep.equal(vm.data);
@ -63,16 +81,25 @@ describe('Tree', () => {
} }
} }
}); });
const firstNode = document.querySelector('.el-tree-node__content'); const firstNode = vm.$el.querySelector('.el-tree-node__content');
firstNode.click(); firstNode.click();
expect(vm.clickedNode.label).to.equal('一级 1'); expect(vm.clickedNode.label).to.equal('一级 1');
vm.$nextTick(() => { setTimeout(() => {
expect(document.querySelector('.el-tree-node').classList.contains('expanded')).to.true; expect(vm.$el.querySelector('.el-tree-node').classList.contains('is-expanded')).to.true;
firstNode.click(); firstNode.click();
vm.$nextTick(() => { setTimeout(() => {
expect(document.querySelector('.el-tree-node').classList.contains('expanded')).to.false; expect(vm.$el.querySelector('.el-tree-node').classList.contains('is-expanded')).to.false;
done(); done();
}); }, DELAY);
}, DELAY);
});
it('emptyText', (done) => {
vm = getTreeVm(':props="defaultProps"');
vm.data = [];
vm.$nextTick(() => {
expect(vm.$el.querySelectorAll('.el-tree__empty-block').length).to.equal(1);
done();
}); });
}); });
@ -86,12 +113,62 @@ describe('Tree', () => {
}); });
}); });
it('defaultExpandAll', () => {
vm = getTreeVm(':props="defaultProps" default-expand-all');
expect(vm.$el.querySelectorAll('.el-tree-node.is-expanded').length).to.equal(ALL_NODE_COUNT);
});
it('defaultExpandedKeys', () => {
vm = getTreeVm(':props="defaultProps" :default-expanded-keys="defaultExpandedKeys" node-key="id"', {
created() {
this.defaultExpandedKeys = [1, 3];
}
});
expect(vm.$el.querySelectorAll('.el-tree-node.is-expanded').length).to.equal(2);
});
it('autoExpandParent = true', () => {
vm = getTreeVm(':props="defaultProps" :default-expanded-keys="defaultExpandedKeys" node-key="id"', {
created() {
this.defaultExpandedKeys = [111];
}
});
expect(vm.$el.querySelectorAll('.el-tree-node.is-expanded').length).to.equal(3);
});
it('autoExpandParent = false', () => {
vm = getTreeVm(':props="defaultProps" :default-expanded-keys="defaultExpandedKeys" node-key="id" :auto-expand-parent="false"', {
created() {
this.defaultExpandedKeys = [111];
}
});
expect(vm.$el.querySelectorAll('.el-tree-node.is-expanded').length).to.equal(1);
});
it('defaultCheckedKeys & check-strictly = false', () => {
vm = getTreeVm(':props="defaultProps" default-expand-all show-checkbox :default-checked-keys="defaultCheckedKeys" node-key="id"', {
created() {
this.defaultCheckedKeys = [1];
}
});
expect(vm.$el.querySelectorAll('.el-checkbox .is-checked').length).to.equal(3);
});
it('defaultCheckedKeys & check-strictly', () => {
vm = getTreeVm(':props="defaultProps" default-expand-all show-checkbox :default-checked-keys="defaultCheckedKeys" node-key="id" check-strictly', {
created() {
this.defaultCheckedKeys = [1];
}
});
expect(vm.$el.querySelectorAll('.el-checkbox .is-checked').length).to.equal(1);
});
it('show checkbox', done => { it('show checkbox', done => {
vm = getTreeVm(':props="defaultProps" show-checkbox'); vm = getTreeVm(':props="defaultProps" show-checkbox');
const tree = vm.$children[0]; const tree = vm.$children[0];
const secondNode = document.querySelectorAll('.el-tree-node__content')[2]; const secondNode = document.querySelectorAll('.el-tree-node__content')[3];
const nodeCheckbox = secondNode.querySelector('.el-checkbox'); const nodeCheckbox = secondNode.querySelector('.el-checkbox');
expect(nodeCheckbox).to.exist; expect(nodeCheckbox).to.be.exist;
nodeCheckbox.click(); nodeCheckbox.click();
expect(tree.getCheckedNodes().length).to.equal(3); expect(tree.getCheckedNodes().length).to.equal(3);
expect(tree.getCheckedNodes(true).length).to.equal(2); expect(tree.getCheckedNodes(true).length).to.equal(2);
@ -104,6 +181,39 @@ describe('Tree', () => {
}); });
}); });
it('setCheckedNodes', (done) => {
vm = getTreeVm(':props="defaultProps" show-checkbox node-key="id"');
const tree = vm.$children[0];
const secondNode = document.querySelectorAll('.el-tree-node__content')[3];
const nodeCheckbox = secondNode.querySelector('.el-checkbox');
expect(nodeCheckbox).to.be.exist;
nodeCheckbox.click();
expect(tree.getCheckedNodes().length).to.equal(3);
expect(tree.getCheckedNodes(true).length).to.equal(2);
vm.$nextTick(() => {
tree.setCheckedNodes([]);
expect(tree.getCheckedNodes().length).to.equal(0);
done();
});
});
it('check strictly', (done) => {
vm = getTreeVm(':props="defaultProps" show-checkbox check-strictly');
const tree = vm.$children[0];
const secondNode = document.querySelectorAll('.el-tree-node__content')[3];
const nodeCheckbox = secondNode.querySelector('.el-checkbox');
nodeCheckbox.click();
expect(tree.getCheckedNodes().length).to.equal(1);
expect(tree.getCheckedNodes(true).length).to.equal(0);
const firstLeaf = secondNode.nextElementSibling.querySelector('.el-tree-node__content');
const leafCheckbox = firstLeaf.querySelector('.el-checkbox');
vm.$nextTick(() => {
leafCheckbox.click();
expect(tree.getCheckedNodes().length).to.equal(2);
done();
});
});
it('render content', () => { it('render content', () => {
vm = getTreeVm(':props="defaultProps" :render-content="renderContent"', { vm = getTreeVm(':props="defaultProps" :render-content="renderContent"', {
methods: { methods: {
@ -127,10 +237,10 @@ describe('Tree', () => {
vm = getTreeVm(':props="defaultProps" lazy :load="loadNode" show-checkbox', { vm = getTreeVm(':props="defaultProps" lazy :load="loadNode" show-checkbox', {
methods: { methods: {
loadNode(node, resolve) { loadNode(node, resolve) {
if (node.level === -1) { if (node.level === 0) {
return resolve([{ label: 'region1' }, { label: 'region2' }]); return resolve([{ label: 'region1' }, { label: 'region2' }]);
} }
if (node.level > 3) return resolve([]); if (node.level > 4) return resolve([]);
setTimeout(() => { setTimeout(() => {
resolve([{ resolve([{
label: 'zone' + this.count++ label: 'zone' + this.count++