iview里面的树组件思路跟vue官网上的
树形视图
示例类似,应用了组件的递归使用(好像是废话
整个树组件里有两个vue文件,一个是递归使用的node.vue,一个是父组件tree.vue
组件内的事件大致分为三种:expand(展开)、select(点击节点title触发)和check(复选框的选中与取消触发)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
<template> <collapse-transition> <ul :class="classes" v-show="visible"> <li> <span :class="arrowClasses" @click="handleExpand"> <Icon type="arrow-right-b"></Icon> </span> <Checkbox v-if="showCheckbox" :value="data.checked" :indeterminate="indeterminate" :disabled="data.disabled || data.disableCheckbox" @click.native.prevent="handleCheck"></Checkbox> <span :class="titleClasses" v-html="data.title" @click="handleSelect"></span> <Tree-node v-for="item in data.children" :key="item.nodeKey" :data="item" :visible="data.expand" :multiple="multiple" :show-checkbox="showCheckbox"> </Tree-node> </li> </ul> </collapse-transition> </template>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
props: { data: { type: Object, default () { return {}; } }, multiple: { type: Boolean, default: false }, showCheckbox: { type: Boolean, default: false }, visible: { type: Boolean, default: false } },
|
1 2 3 4 5 6
|
data () { return { prefixCls: prefixCls, indeterminate: false }; },
|
1 2 3 4 5 6 7 8 9 10
|
created () { if (!this.data.checked) this.$set(this.data, 'checked', false); }, mounted () { this.$on('indeterminate', () => { this.broadcast('TreeNode', 'indeterminate'); this.setIndeterminate(); }); }
|
1 2 3 4 5
|
handleExpand () { if (this.data.disabled) return; this.$set(this.data, 'expand', !this.data.expand); this.dispatch('Tree', 'toggle-expand', this.data); },
|
1 2 3 4 5 6 7 8 9 10 11 12
|
handleCheck () { if (this.disabled) return; const checked = !this.data.checked; if (!checked || this.indeterminate) { findComponentsDownward(this, 'TreeNode').forEach(node => node.data.checked = false); } else { findComponentsDownward(this, 'TreeNode').forEach(node => node.data.checked = true); } this.data.checked = checked; this.dispatch('Tree', 'checked'); this.dispatch('Tree', 'on-checked'); },
|
1 2 3 4 5 6 7 8 9 10 11
|
handleSelect () { if (this.data.disabled) return; if (this.data.selected) { this.data.selected = false; } else if (this.multiple) { this.$set(this.data, 'selected', !this.data.selected); } else { this.dispatch('Tree', 'selected', this.data); } this.dispatch('Tree', 'on-selected'); },
|
1 2 3 4 5 6 7 8 9 10 11 12 13
|
<template> <div :class="prefixCls"> <Tree-node v-for="item in data" :key="item.nodeKey" :data="item" visible :multiple="multiple" :show-checkbox="showCheckbox"> </Tree-node> <div :class="[prefixCls + '-empty']" v-if="!data.length">{{ localeEmptyText }}</div> </div> </template>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
mounted () { this.updateData(); this.$on('selected', ori => { const nodes = findComponentsDownward(this, 'TreeNode'); nodes.forEach(node => { this.$set(node.data, 'selected', false); }); this.$set(ori, 'selected', true); }); this.$on('on-selected', () => { this.$emit('on-select-change', this.getSelectedNodes()); }); this.$on('checked', () => { this.updateData(false); }); this.$on('on-checked', () => { this.$emit('on-check-change', this.getCheckedNodes()); }); this.$on('toggle-expand', (payload) => { this.$emit('on-toggle-expand', payload); }); },
|
node.vue methods
)
那些往外部触发事件的就不说了,来看看
selected
事件的处理函数
1 2 3 4 5 6 7
|
this.$on('selected', ori => { const nodes = findComponentsDownward(this, 'TreeNode'); nodes.forEach(node => { this.$set(node.data, 'selected', false); }); this.$set(ori, 'selected', true); });
|
首先找到所有的TreeNode子组件(包括子组件的子组件,也就是无论是迭代几层都包括在里面)
遍历子组件,将子组件的
data.selected
都改为
false
将触发事件的组件的
data.selected
改为
true
结合node.vue中的
handleSelect
,就不难理解select功能的工作原理了。
1 2 3 4 5 6 7 8
|
watch: { data () { this.$nextTick(() => { this.updateData(); this.broadcast('TreeNode', 'indeterminate'); }); } }
|
node.vue mounted
,在监听到
indeterminate
事件后,也向下broadcast了该事件,保证所有子节点都能接收到该事件的触发,并执行相应方法。
tree.vue就讲到这里吧。