Browse Source

Tree树型选择框组件封装10.30

caojunjie 1 year ago
parent
commit
014a5e085c
1 changed files with 233 additions and 0 deletions
  1. 233 0
      src/components/iosbasic-data/TreeSelect.vue

+ 233 - 0
src/components/iosbasic-data/TreeSelect.vue

@@ -0,0 +1,233 @@
+<template>
+    <el-select
+        ref="select"
+        :value="value"
+        placeholder="请选择"
+        :size="size"
+        clearable
+        :disabled="disabled"
+        :filterable="filterable"
+        :filter-method="filterMethod"
+        style="width: 100%;"
+        @clear="clear"
+        @visible-change="visibleChange"
+    >
+        <el-option
+            ref="option"
+            class="tree-select__option"
+            :value="optionData.id"
+            :label="optionData.name"
+        >
+            <el-tree
+                ref="tree"
+                class="tree-select__tree"
+                :class="`tree-select__tree--${multiple ? 'checked' : 'radio'}`"
+                :node-key="nodeKey"
+                :data="data"
+                :props="props"
+                :default-expanded-keys="[value]"
+                :show-checkbox="multiple"
+                :highlight-current="!multiple"
+                :expand-on-click-node="multiple"
+                :filter-node-method="filterNode"
+                @node-click="handleNodeClick"
+                @check-change="handleCheckChange"
+            ></el-tree>
+        </el-option>
+    </el-select>
+</template>
+
+<script>
+export default {
+    name: 'TreeSelect',
+    props: {
+        // v-model绑定
+        value: {
+            type: [String, Number],
+            default: ''
+        },
+        // 节点是否可被选择 多选还是单选
+        multiple: {
+            type: Boolean,
+            default: false
+        },
+        // 树形的数据
+        data: {
+            type: Array,
+            default: function () {
+                return []
+            }
+        },
+        // 每个树节点用来作为唯一标识的属性
+        nodeKey: {
+            type: [String, Number],
+            default: 'id'
+        },
+        // 组件大小
+        size:{
+            type: String,
+            default: 'small'
+        },
+        // 是否可搜索
+        filterable: {
+            type: Boolean,
+            default: false
+        },
+        // 是否禁用
+        disabled: {
+            type: Boolean,
+            default: false
+        },
+        // tree的props配置
+        props: {
+            type: Object,
+            default: function () {
+                return {
+                    label: 'label',
+                    children: 'children'
+                }
+            }
+        }
+    },
+    data() {
+        return {
+            optionData: {
+                id: '',
+                name: ''
+            },
+            filterFlag: false
+        }
+    },
+    watch: {
+        value: {
+            // 选择的数据发生变化触发
+            handler(val) {
+                if (!this.isEmpty(this.data)) {
+                    this.init(val)
+                }
+            },
+            immediate: true
+        },
+        data: function (val) {
+            if (!this.isEmpty(val)) {
+                this.init(this.value)
+            }
+        }
+    },
+    created() {},
+    methods: {
+        // 是否为空
+        isEmpty(val) {
+            for (let key in val) {
+                return false
+            }
+            return true
+        },
+        // 节点被点击时的回调
+        handleNodeClick(data) {
+            if (this.multiple) {
+                return
+            }
+            this.$emit('input', data[this.nodeKey])
+            this.$refs.select.visible = false
+            // 当点击选中树的节点时,下拉框不会自动隐藏,感觉体验不太好。查看文档,
+            // select组件也没有控制下拉框显示隐藏的属性,然后在select组件源码中找到了visible属性,用来控制下拉框显示隐藏。
+            // 点击选中树的节点时设置 this.$refs.select.visible = false 即可。
+        },
+        // 节点选中状态发生变化时的回调
+        handleCheckChange() {
+            const nodes = this.$refs.tree.getCheckedNodes()
+            const value = nodes.map((item) => item[this.nodeKey]).join(',')
+            this.$emit('input', value)
+        },
+        init(val) {
+            // 多选
+            if (this.multiple) {
+                const arr = val.toString().split(',')
+                this.$nextTick(() => {
+                    // setCurrentKey() 通过 key 设置某个节点的当前选中状态,使用此方法必须设置 node-key 属性
+                    this.$refs.tree.setCheckedKeys(arr)
+                    // getCheckedNodes() 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点所组成的数组
+                    const nodes = this.$refs.tree.getCheckedNodes()
+                    this.optionData.id = val
+                    this.optionData.name = nodes
+                        .map((item) => item[this.props.label])
+                        .join(',')
+                })
+            }
+            // 单选
+            else {
+                val = val === '' ? null : val
+                this.$nextTick(() => {
+                    // setCurrentKey() 通过 key 设置某个节点的当前选中状态,使用此方法必须设置 node-key 属性
+                    this.$refs.tree.setCurrentKey(val)
+                    if (val === null) {
+                        return
+                    }
+                    // getNode() 根据 data 或者 key 拿到 Tree 组件中的 node
+                    const node = this.$refs.tree.getNode(val)
+                    this.optionData.id = val
+                    this.optionData.name = node.label
+                })
+            }
+        },
+        // 下拉框出现/隐藏时触发
+        visibleChange(e) {
+            if (e) {
+                const tree = this.$refs.tree
+                this.filterFlag && tree.filter('')
+                this.filterFlag = false
+                let selectDom = null
+                if(this.multiple) {
+                    // $el.querySelector() == document.querySelector() // 获取 dom 元素
+                    selectDom = tree.$el.querySelector('.el-tree-node.is-checked')
+                } else {
+                    selectDom = tree.$el.querySelector('.is-current')
+                }
+                setTimeout(() => {
+                    this.$refs.select.scrollToOption({ $el: selectDom })
+                }, 0)
+            }
+        },
+        // 下拉框点击
+        clear() {
+            this.$emit('input', '')
+        },
+        // 自定义搜索方法
+        filterMethod(val) {
+            this.filterFlag = true
+            this.$refs.tree.filter(val)
+        },
+        // 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏
+        filterNode(value, data) {
+            if (!value) return true
+            const label = this.props.label || 'name'
+            return data[label].indexOf(value) !== -1
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+// 得用 ::v-deep
+.tree-select__option {
+    &.el-select-dropdown__item {
+        height: auto;
+        line-height: 1;
+        padding: 0;
+        background-color: #fff;
+    }
+}
+ // 得用 ::v-deep
+.tree-select__tree {
+    padding: 4px 20px;
+    font-weight: 400;
+    &.tree-select__tree--radio {
+        .el-tree-node.is-current > .el-tree-node__content {
+            //选中字体的样式
+            color:red;
+            font-weight: 700;
+        }
+    }
+}
+</style>