diff --git a/examples/docs/zh-cn/tree.md b/examples/docs/zh-cn/tree.md index 518bf5fc4..aac4f7b4e 100644 --- a/examples/docs/zh-cn/tree.md +++ b/examples/docs/zh-cn/tree.md @@ -235,6 +235,8 @@ | props | 配置选项,具体看下表 | object | — | — | | load | 加载子树数据的方法 | function(node, resolve) | — | — | | show-checkbox | 节点是否可被选择 | boolean | — | false | +| render-content | 树节点的内容区的渲染 Function,会传入两个参数,h 与 { node: node }。 | Function | - | - | +| highlight-current | 是否高亮当前选中节点,默认值是 false。| boolean | - | false | ### props @@ -253,5 +255,5 @@ ### Events | 事件名称 | 说明 | 回调参数 | |---------- |-------- |---------- | -| node-click | 节点被点击时的回调 | 传递给 `data` 属性的数组中该节点所对应的对象 | +| node-click | 节点被点击时的回调 | 共三个参数,依次为:传递给 `data` 属性的数组中该节点所对应的对象、节点对应的 Node、节点组件本身。 | | check-change | 节点选中状态发生变化时的回调 | 共三个参数,依次为:传递给 `data` 属性的数组中该节点所对应的对象、节点本身是否被选中、节点的子树中是否有被选中的节点 | diff --git a/packages/theme-default/src/tree.css b/packages/theme-default/src/tree.css index 3ce48520b..020b76cec 100644 --- a/packages/theme-default/src/tree.css +++ b/packages/theme-default/src/tree.css @@ -3,7 +3,6 @@ @component-namespace el { @b tree { - overflow: auto; cursor: default; background: #ffffff; border: 1px solid #d3dce6; @@ -82,6 +81,11 @@ } } } + +.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content { + background-color: #eff7ff; +} + .collapse-transition { transition: 0.3s height ease-in-out, 0.3s padding-top ease-in-out, 0.3s padding-bottom ease-in-out; } diff --git a/packages/tree/src/model/node.js b/packages/tree/src/model/node.js index 2fa2f7138..7d6c69e9e 100644 --- a/packages/tree/src/model/node.js +++ b/packages/tree/src/model/node.js @@ -1,8 +1,8 @@ -let idSeed = 0; +let nodeIdSeed = 0; import objectAssign from 'object-assign'; const reInitChecked = function(node) { - const siblings = node.children; + const siblings = node.childNodes; let all = true; let none = true; @@ -42,7 +42,7 @@ const getPropertyFromData = function(node, prop) { export default class Node { constructor(options) { - this.id = idSeed++; + this.id = nodeIdSeed++; this.text = null; this.checked = false; this.indeterminate = false; @@ -61,7 +61,7 @@ export default class Node { // internal this.level = -1; this.loaded = false; - this.children = []; + this.childNodes = []; this.loading = false; if (this.parent) { @@ -74,8 +74,17 @@ export default class Node { } setData(data) { + if (!Array.isArray(data) && !data.$treeNodeId) { + Object.defineProperty(data, '$treeNodeId', { + value: this.id, + enumerable: false, + configurable: false, + writable: false + }); + } + this.data = data; - this.children = []; + this.childNodes = []; let children; if (this.level === -1 && this.data instanceof Array) { @@ -85,14 +94,7 @@ export default class Node { } for (let i = 0, j = children.length; i < j; i++) { - const child = children[i]; - this.insertChild(new Node({ - data: child, - parent: this, - lazy: this.lazy, - load: this.load, - props: this.props - })); + this.insertChild({ data: children[i] }); } } @@ -107,26 +109,47 @@ export default class Node { insertChild(child, index) { if (!child) throw new Error('insertChild error: child is required.'); - if (!child instanceof Node) { - throw new Error('insertChild error: child should an instance of Node.'); + if (!(child instanceof Node)) { + objectAssign(child, { + parent: this, + lazy: this.lazy, + load: this.load, + props: this.props + }); + child = new Node(child); } - child.parent = this; child.level = this.level + 1; if (typeof index === 'undefined') { - this.children.push(child); + this.childNodes.push(child); } else { - this.children.splice(index, 0, child); + this.childNodes.splice(index, 0, child); } } removeChild(child) { - const index = this.children.indexOf(child); + const index = this.childNodes.indexOf(child); if (index > -1) { child.parent = null; - this.children.splice(child, index); + this.childNodes.splice(index, 1); + } + } + + removeChildByData(data) { + let nodeIndex = -1; + let targetNode = null; + this.childNodes.forEach((node, index) => { + if (node.data === data) { + nodeIndex = index; + targetNode = node; + } + }); + + if (nodeIndex > -1) { + targetNode.parent = null; + this.childNodes.splice(nodeIndex, 1); } } @@ -147,13 +170,7 @@ export default class Node { doCreateChildren(array, defaultProps = {}) { array.forEach((item) => { - const node = new Node(objectAssign({ - data: item, - lazy: this.lazy, - load: this.load, - props: this.props - }, defaultProps)); - this.insertChild(node); + this.insertChild(objectAssign({ data: item }, defaultProps)); }); } @@ -170,9 +187,9 @@ export default class Node { } hasChild() { - const children = this.children; + const childNodes = this.childNodes; if (!this.lazy || (this.lazy === true && this.loaded === true)) { - return children && children.length > 0; + return childNodes && childNodes.length > 0; } return true; } @@ -183,9 +200,9 @@ export default class Node { const handleDeep = () => { if (deep) { - const children = this.children; - for (let i = 0, j = children.length; i < j; i++) { - const child = children[i]; + const childNodes = this.childNodes; + for (let i = 0, j = childNodes.length; i < j; i++) { + const child = childNodes[i]; child.setChecked(value !== false, deep); } } @@ -225,15 +242,33 @@ export default class Node { return data[children]; } + updateChildren() { + const newData = this.getChildren() || []; + const oldData = this.childNodes.map((node) => node.data); + + const newDataMap = {}; + const newNodes = []; + + newData.forEach((item, index) => { + if (item.$treeNodeId) { + newDataMap[item.$treeNodeId] = { index, data: item }; + } else { + newNodes.push({ index, data: item }); + } + }); + + oldData.forEach((item) => { if (!newDataMap[item.$treeNodeId]) this.removeChildByData(item); }); + newNodes.forEach(({ index, data }) => this.insertChild({ data }, index)); + } + loadData(callback, defaultProps = {}) { if (this.lazy === true && this.load && !this.loaded) { this.loading = true; - const loadFn = this.load; const resolve = (children) => { this.loaded = true; this.loading = false; - this.children = []; + this.childNodes = []; this.doCreateChildren(children, defaultProps); @@ -242,7 +277,7 @@ export default class Node { } }; - loadFn(this, resolve); + this.load(this, resolve); } else { if (callback) { callback.call(this); diff --git a/packages/tree/src/model/tree.js b/packages/tree/src/model/tree.js index 19bf797d6..4c84091d7 100644 --- a/packages/tree/src/model/tree.js +++ b/packages/tree/src/model/tree.js @@ -8,8 +8,6 @@ export default class Tree { } } - this._isTree = true; - this.root = new Node({ data: this.data, lazy: this.lazy, @@ -28,9 +26,9 @@ export default class Tree { getCheckedNodes(leafOnly) { const checkedNodes = []; const walk = function(node) { - const children = node.root ? node.root.children : node.children; + const childNodes = node.root ? node.root.childNodes : node.childNodes; - children.forEach(function(child) { + childNodes.forEach(function(child) { if ((!leafOnly && child.checked) || (leafOnly && child.isLeaf && child.checked)) { checkedNodes.push(child.data); } diff --git a/packages/tree/src/transition.js b/packages/tree/src/transition.js index 6d413e138..e6c31a6ff 100644 --- a/packages/tree/src/transition.js +++ b/packages/tree/src/transition.js @@ -58,7 +58,7 @@ class Transition { el.style.paddingTop = el.dataset.oldPaddingTop; el.style.paddingBottom = el.dataset.oldPaddingBottom; } -}; +} export default { functional: true, diff --git a/packages/tree/src/tree-node.vue b/packages/tree/src/tree-node.vue index bb1ff7238..1f5acff2f 100644 --- a/packages/tree/src/tree-node.vue +++ b/packages/tree/src/tree-node.vue @@ -1,29 +1,42 @@ - - - + @click.stop="handleClick" + :class="{ expanded: childNodeRendered && expanded, 'is-current': $tree.currentNode === _self }"> + + + + + + class="el-tree-node__icon el-icon-loading"> - + - - + + -