<template>
    <div class="es-tree" ref="esTree">
        <Input
            ref="searchInput"
            class="mb-5"
            search
            clearable
            v-show="search"
            v-model="searchModel"
            @on-search="onSearch(searchModel)"
            :placeholder="placeholder"
        />
        <div class="tree-wrapper" ref="treeWrapper">
            <Tree
                :data="dataList"
                :multiple="multiple"
                :show-checkbox="showCheckbox"
                :empty-text="emptyText"
                :load-data="
                    separateData && !showCheckbox
                        ? loadData || syncLoadData
                        : loadData
                "
                :render="
                    showEdit
                        ? renderForEdit
                        : renderContent
                        ? renderContent
                        : null
                "
                @on-select-change="selectChange"
                @on-check-change="checkChange"
                @on-toggle-expand="toggleExpand"
                ref="tree"
                class="text-left"
            >
            </Tree>
        </div>
    </div>
</template>
<script>
export default {
    // 1、支持搜索
    // 2、支持增删节点。对应的树形图节点数据源_add属性为true表示支持增加，_remove属性为false支持删除
    name: 'es-tree',
    props: {
        // 数据源
        dataList: {
            type: Array,
            default() {
                return [];
            }
        },
        // 是否支持多选
        multiple: {
            type: Boolean,
            default: false
        },
        // 是否展示选择框
        showCheckbox: {
            type: Boolean,
            default: false
        },
        // 是否展示编辑按钮，点击的回调事件包含_editFlag参数，值为'add' or 'remove'
        showEdit: {
            type: Boolean,
            default: false
        },
        // 空数据文本
        emptyText: {
            type: String,
            default: '暂无数据'
        },
        // 异步加载
        loadData: {
            type: Function
        },
        // 是否支持搜索
        search: {
            type: Boolean,
            default: false
        },
        // 搜索回调
        onSearch: {
            type: Function
        },
        // 搜索默认文本
        placeholder: {
            type: String,
            default: '请输入搜索条件'
        },
        // 树形图高度
        height: {
            type: String,
            default: '100%'
        },
        // 重绘， todo：命名为render报异常
        renderContent: {
            type: Function
        },
        // 数据量大的时候分割数据
        separateData: {
            type: Boolean,
            default: true
        }
    },
    data() {
        return {
            // 搜索数据
            searchModel: '',
            // 某节点下的大量数据
            bigData: [],
            // 数据阈值
            thresholdValue: 50,
            // 当前收缩的节点
            currentCloseRow: {}
        };
    },
    watch: {
        dataList: {
            handler(value) {
                let self = this;
                if (self.separateData && !self.showCheckbox) {
                    if (value && value.length > 0) {
                        self.handleBigData(value, true);
                    }
                }
            }
        },
        height: {
            handler(value) {
                this.updateStyle();
            }
        }
    },
    methods: {
        updateStyle() {
            let self = this;
            let siblingsHeight = self.$common.getSiblingsTotalHeight(
                self.$refs.treeWrapper
            );
            self.$refs.esTree.style.height = self.height;
            self.$refs.treeWrapper.style.height =
                'calc(100% - ' + siblingsHeight + 'px)';
        },
        // 异步加载
        syncLoadData(item, callback) {
            let self = this;
            let bigDataIndex = self.$lodash.findIndex(self.bigData, {
                id: item.id
            });
            if (bigDataIndex >= 0) {
                setTimeout(() => {
                    callback(
                        self.$lodash.slice(
                            self.bigData[bigDataIndex].children,
                            0,
                            self.thresholdValue
                        )
                    );
                    self.handleTreeData(item, bigDataIndex);
                }, 10);
            } else {
                item.loading = false;
            }
        },
        // 大数据节点暂存, 并置空
        handleBigData(list, needSave) {
            let self = this;
            list.forEach(item => {
                if (item.children && item.children.length > 0) {
                    if (item.children.length > self.thresholdValue) {
                        needSave &&
                            self.bigData.push(JSON.parse(JSON.stringify(item)));
                        item.children = [];
                        item.loading = false;
                        item.isSeparateData = true;
                    }
                    self.handleBigData(item.children, needSave);
                }
            });
        },
        // 处理节点
        handleTreeData(item, bigDataIndex) {
            let self = this;
            let start = 0;
            let end = self.thresholdValue;
            let interval = setInterval(() => {
                start = end;
                end = Math.min(
                    start + self.thresholdValue,
                    self.bigData[bigDataIndex].children.length
                );
                self.dataList.forEach((company, index) => {
                    company.children.forEach((area, idx) => {
                        if (area.id === item.id) {
                            let list = area.children.concat(
                                self.$lodash.slice(
                                    self.bigData[bigDataIndex].children,
                                    start,
                                    end
                                )
                            );
                            if (item.id === self.currentCloseRow.id) {
                                area.children = [];
                            } else {
                                area.children = list;
                            }
                        }
                    });
                });
                if (
                    end - start < self.thresholdValue ||
                    item.id === self.currentCloseRow.id
                ) {
                    clearInterval(interval);
                }
            }, 500);
        },
        // 点击树节点时触发
        selectChange(selectedList, row) {
            this.$emit('on-select-change', selectedList, row);
        },
        // 点击复选框时触发
        checkChange(checkedList, row) {
            this.$emit('on-check-change', checkedList, row);
        },
        // 展开和收起子列表时触发
        toggleExpand(row) {
            let self = this;
            if (self.separateData && !self.showCheckbox) {
                let bigDataIdx = self.$lodash.findIndex(self.bigData, {
                    id: row.id
                });
                if (bigDataIdx >= 0) {
                    if (row.expand) {
                        if (row.id === self.currentCloseRow.id) {
                            self.currentCloseRow = {};
                        }
                    } else {
                        self.handleBigData(self.dataList, false);
                        self.currentCloseRow = row;
                    }
                }
            }
            self.$emit('on-toggle-expand', row);
        },
        // 渲染节点增删功能，_add表示节点支持增，_remove表示节点支持删，_disableEdit表示节点是否支持交互
        renderForEdit(h, { root, node, data }) {
            let self = this;
            let cls = '';
            let childrenDisableEdit = false;
            if (data.selected) {
                cls = 'ivu-tree-title-selected';
            }
            if (data.children && data.children.length > 0) {
                childrenDisableEdit = data.children[0]._disableEdit;
            }
            if (this.showEdit) {
                return h(
                    'div',
                    {
                        class: 'tree-row'
                    },
                    [
                        h(
                            'div',
                            {
                                class: 'tree-content'
                            },
                            [
                                h(
                                    'span',
                                    {
                                        class: {
                                            ['ivu-tree-title tree-span ' +
                                            cls]: true
                                        },
                                        on: {
                                            click: function(e) {
                                                // todo 1、多选 2、多个tree
                                                [
                                                    ...document.getElementsByClassName(
                                                        'ivu-tree-title-selected'
                                                    )
                                                ].forEach(value => {
                                                    value.classList.remove(
                                                        'ivu-tree-title-selected'
                                                    );
                                                });
                                                e.target.classList.add(
                                                    'ivu-tree-title-selected'
                                                );
                                                root.forEach(value => {
                                                    value.node.selected = false;
                                                });
                                                data.selected = true;
                                                self.$emit(
                                                    'on-select-change',
                                                    null,
                                                    data
                                                );
                                            }
                                        }
                                    },
                                    data.title
                                ),
                                h(
                                    'div',
                                    {
                                        class: 'btn-wrapper',
                                        props: {
                                            icon: 'ios-add',
                                            type: 'default',
                                            size: 'small'
                                        }
                                    },
                                    [
                                        h('Button', {
                                            props: {
                                                icon: 'ios-add',
                                                type: 'default',
                                                size: 'small'
                                            },
                                            style: {
                                                display: data._add
                                                    ? 'inline-block'
                                                    : 'none'
                                            },
                                            class: {
                                                add: true,
                                                'disabled-ele':
                                                    data._disableEdit ||
                                                    childrenDisableEdit
                                            },
                                            on: {
                                                click: function(e) {
                                                    [
                                                        ...document.getElementsByClassName(
                                                            'ivu-tree-title-selected'
                                                        )
                                                    ].forEach(value => {
                                                        value.classList.remove(
                                                            'ivu-tree-title-selected'
                                                        );
                                                    });
                                                    e.target
                                                        .closest('.btn-wrapper')
                                                        .previousSibling.classList.add(
                                                            'ivu-tree-title-selected'
                                                        );
                                                    let params = {
                                                        ...data,
                                                        _editFlag: 'add'
                                                    };
                                                    root.forEach(value => {
                                                        value.node.selected = false;
                                                    });
                                                    data.selected = true;
                                                    self.$emit(
                                                        'on-select-change',
                                                        null,
                                                        params
                                                    );
                                                }
                                            }
                                        }),
                                        h('Button', {
                                            props: {
                                                icon: 'ios-remove',
                                                type: 'default',
                                                size: 'small'
                                            },
                                            style: {
                                                display: data._remove
                                                    ? 'inline-block'
                                                    : 'none'
                                            },
                                            class: {
                                                remove: true,
                                                'disabled-ele':
                                                    data._disableEdit
                                            },
                                            on: {
                                                click: function(e) {
                                                    [
                                                        ...document.getElementsByClassName(
                                                            'ivu-tree-title-selected'
                                                        )
                                                    ].forEach(value => {
                                                        value.classList.remove(
                                                            'ivu-tree-title-selected'
                                                        );
                                                    });
                                                    e.target
                                                        .closest('.btn-wrapper')
                                                        .previousSibling.classList.add(
                                                            'ivu-tree-title-selected'
                                                        );
                                                    root.forEach(value => {
                                                        value.node.selected = false;
                                                    });
                                                    data.selected = true;
                                                    let params = {
                                                        ...data,
                                                        _editFlag: 'remove'
                                                    };
                                                    self.$emit(
                                                        'on-select-change',
                                                        null,
                                                        params
                                                    );
                                                }
                                            }
                                        })
                                    ]
                                )
                            ]
                        )
                    ]
                );
            } else {
                return h('span', data.title);
            }
        }
    }
};
</script>
<style lang="scss" scoped>
@mixin buttonType($color) {
    border-color: $color;
    color: $color;
}
@mixin arrowType($size) {
    font-size: $size;
}
.text-left {
    text-align: left;
}
.tree-wrapper {
    width: 100%;
    height: 100%;
    overflow: auto;
}
.es-tree {
    width: 100%;
    height: 100%;
    overflow: hide;
    position: relative;
}
.es-tree /deep/ .tree-row {
    display: inline-block;
    width: calc(100% - 20px);
}
.es-tree /deep/ .tree-content {
    display: flex;
    justify-content: space-between;
}
.es-tree /deep/ .tree-span {
    height: 24px;
    line-height: 24px;
    max-width: 200px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.es-tree /deep/ .btn-wrapper {
    // display: inline-flex;
    // position: absolute;
    // right: 5px;
}
.es-tree /deep/ .ivu-tree-arrow-open i {
    @include arrowType(16px);
}
.es-tree /deep/ .ivu-tree-arrow i {
    @include arrowType(16px);
}
.es-tree /deep/ .add-main {
    @include buttonType(#3895e9);
    width: 61px;
}
.es-tree /deep/ .add {
    @include buttonType(#3895e9);
}
.es-tree /deep/ .remove {
    @include buttonType(#f54e40);
    margin-left: 5px;
}
.es-tree /deep/ .button-wrapper {
    display: inline-flex;
    width: calc(100% - 50px);
    justify-content: space-between;
}
</style>
