<template>
    <el-select
        ref="select"
        :value="value"
        :placeholder="placeholder"
        :size="size"
        :clearable="clearable"
        :disabled="disabled"
        :filterable="filterable"
        :filter-method="filterMethod"
        style="width: 100%;"
        @clear="clear"
        @focus="focus"
        @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
        },
        clearable: {
            type: Boolean,
            default: true
        },
        // 树形的数据
        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'
                }
            }
        },
        placeholder:{
            type: String,
            default: '请选择'
        },
    },
    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', '')
        },
        // 聚焦
        focus(){
            this.$emit('focus')
        },
        // 自定义搜索方法
        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
::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>
