element实现树形选择器,支持多选、回显正常显示
element有tree树形控件和select下拉选择器,但是,没有树形选择器。使用过
ant vue
的朋友会知道,它的组件库中有很完善的树形选择器。在实际项目中,树形选择器常用来做一些复杂分类(包含下级)和角色权限管理选择等。
本文仅在参考文章基础上根据实际项目进行了props
值的预设,以及解决了选择器回显的问题。
相关问题
我在个人博客文章进行编辑分类时,发现传入的选中数组值,tree
选择器未显示选中值。
解决过程
一番调试后,我发现是传入的数组值没有触发tree的check-change
方法
1 2 3 4 5 6 |
// 多选,勾选上传进来的节点 checkSelectedNodes(checkedKeys) { this.$refs.tree.setCheckedKeys(checkedKeys) // 此处未触发 handleCheckChange方法所致 // console.log('checkSelectedNodes', checkedKeys, this.selectedData) }, |
既然找到问题所在,那就容易解决了。我先是尝试直接调用handleCheckChange
方法,发现不起作用,再使用 this.$nextTick
的方式,还是不起作用。最后,只能动用大招使用延时来获取DOM元素值了。
1 2 3 |
setTimeout(function() { that.$refs.tree.setCheckedKeys(checkedKeys) }, 10) |
这里,我知道是因为在传入数组值设置keys时,未获取到相应的dom
节点元素,所以无法触发el-tree
组件的check-change
方法。但相关原理知识,我还是不太理解,不知道各位大神,能否给小羽解惑哦~
别着急,下文有完整代码哦。
总结延伸
遗留问题
- tree选择器单选时,当传入的值为数组、字符串等格式,控制台都会有警告信息;
- tree选择器支持搜索过滤
完整代码
tree树形选择器组件封装
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
<!-- /** * 树形下拉选择组件,下拉框展示树形结构,提供选择某节点功能,方便其他模块调用 * @author sxy https://bysjb.cn * @date 2020-12-02 */ --> <template> <div> <div v-show="isShowSelect" class="mask" @click="isShowSelect = !isShowSelect" /> <el-popover v-model="isShowSelect" placement="bottom-start" :width="width" trigger="manual" style="padding: 12px 0;" @hide="popoverHide" > <el-tree ref="tree" class="common-tree" :style="style" :data="data" :props="defaultProps" :show-checkbox="multiple" :node-key="nodeKey" :check-strictly="checkStrictly" default-expand-all :expand-on-click-node="false" :check-on-click-node="multiple" :highlight-current="true" @node-click="handleNodeClick" @check-change="handleCheckChange" /> <el-select slot="reference" ref="select" v-model="selectedData" :style="selectStyle" :size="size" :multiple="multiple" :clearable="clearable" :collapse-tags="collapseTags" class="tree-select" @click.native="isShowSelect = !isShowSelect" @remove-tag="removeSelectedNodes" @clear="removeSelectedNode" @change="changeSelectedNodes" > <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-popover> </div> </template> <script> export default { props: { // 树结构数据 data: { type: Array, default() { return [] } }, defaultProps: { type: Object, default() { return { children: 'children', label: 'name' } } }, // 配置是否可多选 multiple: { type: Boolean, default() { return false } }, // 配置是否可清空选择 clearable: { type: Boolean, default() { return false } }, // 配置多选时是否将选中值按文字的形式展示 collapseTags: { type: Boolean, default() { return false } }, nodeKey: { type: String, default() { return 'id' } }, // 显示复选框情况下,是否严格遵循父子不互相关联 checkStrictly: { type: Boolean, default() { return false } }, // 默认选中的节点key数组 checkedKeys: { type: Array, default() { return [] } }, size: { type: String, default() { return 'medium' } }, width: { type: Number, default() { return 250 } }, height: { type: Number, default() { return 300 } } }, data() { return { isShowSelect: false, // 是否显示树状选择器 options: [], selectedData: [], // 选中的节点 style: 'width:' + (this.width - 24) + 'px;' + 'height:' + this.height + 'px;', selectStyle: 'width:' + (this.width + 24) + 'px;', checkedIds: [], checkedData: [] } }, watch: { isShowSelect(val) { // 隐藏select自带的下拉框 this.$refs.select.blur() }, checkedKeys(val) { console.log('checkedKeys', val) if (!val) return this.checkedKeys = val this.initCheckedData() } }, mounted() { this.initCheckedData() }, methods: { // 单选时点击tree节点,设置select选项 setSelectOption(node) { const tmpMap = {} tmpMap.value = node.key tmpMap.label = node.label this.options = [] this.options.push(tmpMap) this.selectedData = node.key }, // 单选,选中传进来的节点 checkSelectedNode(checkedKeys) { var item = checkedKeys[0] this.$refs.tree.setCurrentKey(item) var node = this.$refs.tree.getNode(item) this.setSelectOption(node) }, // 多选,勾选上传进来的节点 checkSelectedNodes(checkedKeys) { // this.$refs.tree.setCheckedKeys(checkedKeys) // 优化select回显显示 有个延迟的效果 const that = this setTimeout(function() { that.$refs.tree.setCheckedKeys(checkedKeys) }, 10) // console.log('checkSelectedNodes', checkedKeys, this.selectedData) }, // 单选,清空选中 clearSelectedNode() { this.selectedData = [] this.$refs.tree.setCurrentKey(null) }, // 多选,清空所有勾选 clearSelectedNodes() { var checkedKeys = this.$refs.tree.getCheckedKeys() // 所有被选中的节点的 key 所组成的数组数据 for (let i = 0; i < checkedKeys.length; i++) { this.$refs.tree.setChecked(checkedKeys[i], false) } }, initCheckedData() { if (this.multiple) { // 多选 // console.log(this.checkedKeys.length) if (this.checkedKeys.length > 0) { this.checkSelectedNodes(this.checkedKeys) } else { this.clearSelectedNodes() } } else { // 单选 if (this.checkedKeys.length > 0) { this.checkSelectedNode(this.checkedKeys) } else { this.clearSelectedNode() } } }, popoverHide() { if (this.multiple) { this.checkedIds = this.$refs.tree.getCheckedKeys() // 所有被选中的节点的 key 所组成的数组数据 this.checkedData = this.$refs.tree.getCheckedNodes() // 所有被选中的节点所组成的数组数据 } else { this.checkedIds = this.$refs.tree.getCurrentKey() this.checkedData = this.$refs.tree.getCurrentNode() } this.$emit('popoverHide', this.checkedIds, this.checkedData) }, // 单选,节点被点击时的回调,返回被点击的节点数据 handleNodeClick(data, node) { if (!this.multiple) { this.setSelectOption(node) this.isShowSelect = !this.isShowSelect this.$emit('change', this.selectedData) } }, // 多选,节点勾选状态发生变化时的回调 handleCheckChange() { var checkedKeys = this.$refs.tree.getCheckedKeys() // 所有被选中的节点的 key 所组成的数组数据 // console.log('handleCheckChange', checkedKeys) this.options = checkedKeys.map((item) => { var node = this.$refs.tree.getNode(item) // 所有被选中的节点对应的node const tmpMap = {} tmpMap.value = node.key tmpMap.label = node.label return tmpMap }) this.selectedData = this.options.map((item) => { return item.value }) this.$emit('change', this.selectedData) }, // 多选,删除任一select选项的回调 removeSelectedNodes(val) { this.$refs.tree.setChecked(val, false) var node = this.$refs.tree.getNode(val) if (!this.checkStrictly && node.childNodes.length > 0) { this.treeToList(node).map(item => { if (item.childNodes.length <= 0) { this.$refs.tree.setChecked(item, false) } }) this.handleCheckChange() } this.$emit('change', this.selectedData) }, treeToList(tree) { var queen = [] var out = [] queen = queen.concat(tree) while (queen.length) { var first = queen.shift() if (first.childNodes) { queen = queen.concat(first.childNodes) } out.push(first) } return out }, // 单选,清空select输入框的回调 removeSelectedNode() { this.clearSelectedNode() this.$emit('change', this.selectedData) }, // 选中的select选项改变的回调 changeSelectedNodes(selectedData) { // 多选,清空select输入框时,清除树勾选 if (this.multiple && selectedData.length <= 0) { this.clearSelectedNodes() } this.$emit('change', this.selectedData) } } } </script> <style scoped> .mask { width: 100%; height: 100%; position: fixed; top: 0; left: 0; opacity: 0; z-index: 11; } .common-tree { overflow: auto; } .tree-select { z-index: 111; } </style> |
调用组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<!-- * <tree-select :height="400" // 下拉框中树形高度 * :width="200" // 下拉框中树形宽度 * size="small" // 输入框的尺寸: medium/small/mini * :data="data" // 树结构的数据 * :defaultProps="defaultProps" // 树结构的props * multiple // 多选 * clearable // 可清空选择 * collapseTags // 多选时将选中值按文字的形式展示 * checkStrictly // 多选时,严格遵循父子不互相关联 * :nodeKey="nodeKey" // 绑定nodeKey,默认绑定'id' * :checkedKeys="defaultCheckedKeys" // 传递默认选中的节点key组成的数组 * @popoverHide="popoverHide"> // 事件有两个参数:第一个是所有选中的节点ID,第二个是所有选中的节点数据 * </tree-select> */ --> <tree-select :data="cateTreeList" :width="200" clearable @popoverHide="onCateSelect" /> |
其他功能,建议多看一下element下tree组件的api。
参考资料
【树形选择器封装】https://blog.csdn.net/sleepwalker_1992/article/details/87894588
【树形选择器回显问题解决】https://blog.csdn.net/jasmine0178/article/details/103600508