<template>
    <div class="flex flex-direction-column" style="height: 100%">
        <div class="layout-aside-header" v-if="opts.showTitle">
            <div class="layout-aside-header-title">{{ opts.title }}</div>
            <div class="layout-aside-header-icon">
                <template v-for="item in opts.toolbarBtn">
                    <template v-if="item=='unfold'">
                        <i class="el-icon-folder-opened" @click="expandCurrentNodeChildren" title="树展开"/>
                    </template>
                    <template v-if="item=='fold'">
                        <i class="el-icon-folder" @click="collapseAllNodes" title="树折叠"/>
                    </template>
                    <template v-if="item=='add'">
                        <i class="el-icon-plus" @click="openAddDialog"/>
                    </template>
                    <template v-if="item=='edit'">
                        <i class="el-icon-edit-outline" @click="openEditDialog"/>
                    </template>
                    <template v-if="item=='del'">
                        <i class="el-icon-delete" @click="deleteTreeNode"/>
                    </template>
                    <template v-if="item=='separate'">
                        <span style="border-left: 1px solid #e7e6e6;margin-left: 2px;"> </span>
                    </template>
                    <template v-if="typeof item=='object'">
                        <i :class="item.icon" @click="doItemClick(item.onClick)" :title="item.title"/>
                    </template>
                </template>
                <el-popover v-if="showSort" ref="popoverRef" placement="bottom" trigger="click">
                    <el-button-group>
                        <el-button type="primary" icon="el-icon-top" @click="doSortChange('prev')">上移
                        </el-button>
                        <el-button type="primary" icon="el-icon-bottom" @click="doSortChange('next')">下移
                        </el-button>
                        <el-button type="primary" icon="el-icon-s-operation" @click="sortByIndex">一键排序
                        </el-button>
                    </el-button-group>
                    <i class="el-icon-sort" slot="reference"/>
                </el-popover>
            </div>
        </div>
        <div>
            <el-input :placeholder="opts.placeholder" v-model="treeFilterText"></el-input>
        </div>
        <div class="layout-aside-content" v-loading="loadingTreeFlag">
            <el-tree
                ref="treeRef"
                v-bind:class="{'hide-checkbox':opts.hideCheckboxEl}"
                :node-key="opts.loadOptions.treeKey.idKey"
                :data="treeData"
                :props="{children: 'children',label:opts.loadOptions.treeKey.label, isLeaf:opts.loadOptions.treeKey.isLeaf}"
                :show-checkbox="opts.showCheckbox"
                :default-expand-all="opts.defaultExpandAll"
                :default-expanded-keys="opts.defaultExpandedKeys"
                :check-on-click-node="true"
                :highlight-current="true"
                :expand-on-click-node="false"
                :check-strictly="opts.checkStrictly"
                :render-after-expand="opts.renderAfterExpand"
                :filter-node-method="opts.filterNodeMethod"
                :indent="opts.indent"
                :load="opts.load"
                :lazy="opts.lazy"
                @node-click="clickTreeEvent"
                @check="checkTreeEvent"
                @check-change="opts.checkChange"
                :draggable="opts.draggable"
                @node-drop="opts.handleDrop"
                :allow-drop="opts.allowDrop"
            >
                <template slot-scope="{ node, data }">
                    <slot name="tree" :node="node" :data="data"></slot>
                </template>
            </el-tree>
        </div>
        <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" :width="opts.dialogWidth" :fullscreen="opts.fullscreen" ref="treeDialog">
            <slot name="form"></slot>
            <span slot="footer" class="dialog-footer">
                <el-button @click="dialogVisible = false">取 消</el-button>
                <el-button type="primary" @click="saveDialog" :loading="saveDialogLoadingFlag">确 定</el-button>
            </span>
        </el-dialog>

    </div>
</template>

<script>
import {convertToTreeData, getTreeParentIdsById} from "ap-util/util/DataUtil";
import {getDiffSortList, getNodeByCurrent, getSortList, getSortListByIndex} from "ap-util/util/TreeUtil";
import {handleDefaultParams} from "ap-util/util/ObjectUtil";

export default {
    name: "ApAsideTree",
    props: {
        options: {
            type: Object,
            required: true
        }
    },
    data() {
        return {
            opts: {},
            loadingTreeFlag: false,
            saveDialogLoadingFlag: false,
            dialogVisible: false,
            dialogTitle: '',
            isAddDialog: true,
            treeFilterText: '',
            treeData: [],
            showSort: false,
            customTreeNode: false

        }
    },
    watch: {
        opts: {
            handler(value) {
                // 深度监听，opts有修改，对应修改父组件options参数
                this.$emit('update:options', this.opts)
            },
            deep: true
        },
        treeFilterText(val) {
            // 过滤值修改，更新tree
            this.$refs.treeRef.filter(val);
        }
    },
    created() {
        this.initOptions();
        if (this.opts.initData) {
            this.loadTreeData();
        }
    },
    methods: {
        // 修改默认参数
        initOptions() {
            let defaultParams = {
                title: "列表",
                dialogTitle: "表单",
                dialogWidth: "350px",
                placeholder: "关键字过滤",
                fullscreen: false,
                toolbarBtn: [],
                showCheckbox: false,
                defaultExpandAll: true,
                defaultExpandedKeys: [],
                draggable: false,
                sortByDrag: false,
                reLoadAfterModify: true,
                reLoadAfterSort: false,
                reLoadByResponsive: false,
                checkStrictly: false,
                renderAfterExpand: false,
                showTitle: true,
                indent: 10,
                initData: true, // 默认加载数据
                lazy: false,
                hideCheckboxEl: false, // 隐藏checkbox元素(占位)
                load: (node, resolve) => {
                },
                allowDrop: (draggingNode, dropNode, type) => {
                    return true;
                },
                handleDrop: (draggingNode, dropNode, dropType, ev) => {
                },
                onClick: (data, node, comp) => {
                },
                onCheck: (data, node, comp, halfCheckedKeys, checkedKeys) => {
                },
                checkChange: (data, checked, indeterminate) => {
                },
                loadOptions: {
                    url: "",
                    data: {},
                    success: (response) => {
                    },
                    error: () => {
                    },
                    treeKey: {
                        idKey: "fdId",
                        parentKey: "fdParentId",
                        sortKey: "fdSort",
                        childrenKey: "children",
                        isLeaf: "isLeaf",
                        label: "fdName"
                    },
                },
                editOptions: {
                    url: "",
                    data: (node) => {
                        return {}
                    },
                    success: (response) => {
                    },
                    error() {
                    },
                },

                insertUrl: "",
                insertData: () => {
                    return {};
                },
                updateUrl: "",
                updateData: () => {
                    return {};
                },
                deleteUrl: "",
                deleteData: (node) => {
                    return {}
                },
                updateSortUrl: '',
                updateSortListUrl: '',
                sortByTree: true,
                resetForm: () => {
                },
                afterOpenAddDialog: (node) => {
                },
                filterNodeMethod: (value, data) => {
                    // 树节点过滤
                    let label = this.opts.loadOptions.treeKey.label;
                    if (!value) return true;
                    return data[label] != null && data[label].indexOf(value) !== -1;
                },
            };
            // 初始化组件前修改默认参数
            this.opts = handleDefaultParams(this.options, defaultParams);

            for (let optKey in defaultParams) {
                if (optKey == "loadOptions") {
                    this.opts.loadOptions = handleDefaultParams(this.options.loadOptions, defaultParams.loadOptions)
                    for (let loadOptionsKey in defaultParams.loadOptions) {
                        if (loadOptionsKey == "treeKey") {
                            this.opts.loadOptions.treeKey = handleDefaultParams(this.options.loadOptions.treeKey, defaultParams.loadOptions.treeKey);
                        }
                    }
                    continue;
                }
                if (optKey == "editOptions") {
                    this.opts.editOptions = handleDefaultParams(this.options.editOptions, defaultParams.editOptions)

                }
            }


            for (let i = 0; i < this.opts.toolbarBtn.length; i++) {
                if (this.opts.toolbarBtn[i] == "sort") {
                    this.showSort = true;
                    break;
                }
            }

            if (this.opts.sortByDrag) {
                this.opts.draggable = true;
                this.opts.handleDrop = this.handleDrop;
                // this.opts.sortByTree = false;
            }

        },
        getParams(params, data) {
            let resParams = {};
            if (typeof params === 'function') {
                resParams = params(data);
            } else {
                resParams = params || {};
            }
            return resParams
        },
        // 左侧树：加载数据
        loadTreeData() {
            this.loadingTreeFlag = true;
            this.treeData = [];
            // 加载数据
            this.$request({
                url: this.opts.loadOptions.url,
                method: 'post',
                data: this.getParams(this.opts.loadOptions.data)
            }).then(response => {
                // 数据格式转换：数组 -> tree
                this.treeData = convertToTreeData(response.data, this.opts.loadOptions.treeKey);
                // 成功回调
                this.opts.loadOptions.success(response);
            }).catch(() => {
                // 失败回调
                this.opts.loadOptions.error();
            }).finally(() => {
                this.loadingTreeFlag = false;
            })
        },
        // 左侧树：重新加载数据 -> 先清空再加载数据（重置节点展开和滚动位置）
        reLoadTreeData() {
            // 重置当前选择节点
            this.$refs.treeRef.setCurrentKey();
            this.loadTreeData();
        },
        // 左侧树：重新加载数据 -> 响应式（保留节点的展开）
        reLoadTreeDataResponsive() {
            // 1. 保存当前展开状态
            const expandedKeys = this.getExpandedIds();

            // 2. 更新节点
            this.loadingTreeFlag = true;
            // 加载数据
            this.$request({
                url: this.opts.loadOptions.url,
                method: 'post',
                data: this.getParams(this.opts.loadOptions.data)
            }).then(response => {
                // 数据格式转换：数组 -> tree
                let newTreeData = convertToTreeData(response.data, this.opts.loadOptions.treeKey);
                this.treeData = newTreeData;
                this.$forceUpdate();
                // 成功回调
                this.opts.loadOptions.success(response);
            }).catch(() => {
                // 失败回调
                this.opts.loadOptions.error();
            }).finally(() => {
                this.loadingTreeFlag = false;
                // 3. 恢复展开状态
                this.$nextTick(() => {
                    expandedKeys.forEach(key => {
                        const node = this.$refs.treeRef.getNode(key);
                        if (node && !node.expanded) {
                            node.expand();
                        }
                    });
                })
            })
        },

        // 获取当前所有展开节点的ID数组
        getExpandedIds() {
            let idKey = this.opts.loadOptions.treeKey.idKey;
            const expandedIds = [];
            // 递归遍历所有节点
            const traverse = (nodes) => {
                nodes.forEach(node => {
                    if (node.expanded) {
                        expandedIds.push(node.data[idKey]);
                        if (node.childNodes.length > 0) {
                            traverse(node.childNodes);
                        }
                    }
                });
            };

            // 从根节点开始遍历
            if (this.$refs.treeRef.store) {
                traverse(this.$refs.treeRef.store.root.childNodes);
            }

            return expandedIds;
        },
        // 左侧树：click事件
        clickTreeEvent(data, node, comp) {
            this.opts.onClick(data, node, comp);
        },
        // 左侧树：选择勾选事件
        checkTreeEvent(data, node, comp) {
            let halfCheckedKeys = this.$refs.treeRef.getHalfCheckedKeys();
            let checkedKeys = this.$refs.treeRef.getCheckedKeys();
            this.opts.onCheck(data, node, comp, halfCheckedKeys, checkedKeys);
        },
        // 左侧树：拖拽结束
        handleDrop(draggingNode, dropNode, dropType, ev) {
            this.$nextTick(() => {
                this.sortByIndexAndDrop(draggingNode, dropNode, dropType);
            });
        },
        // 获取树的节点信息
        getCurrentNode() {
            return this.$refs.treeRef.getCurrentNode() || {}
        },
        // dialog：添加数据
        openAddDialog() {
            this.opts.resetForm();
            this.dialogVisible = true;
            this.dialogTitle = '添加' + this.opts.dialogTitle;
            this.isAddDialog = true;
            this.opts.afterOpenAddDialog(this.getCurrentNode());
        },
        // dialog：编辑数据
        openEditDialog() {
            let currentNode = this.getCurrentNode();
            if (!currentNode || JSON.stringify(currentNode) == '{}') {
                this.$message.error('请选择需要编辑的数据！');
                return
            }

            this.opts.resetForm();
            this.dialogVisible = true;
            this.dialogTitle = '编辑' + this.opts.dialogTitle;
            this.isAddDialog = false;

            this.$request({
                url: this.opts.editOptions.url,
                method: 'post',
                data: this.getParams(this.opts.editOptions.data, currentNode)
            }).then(response => {
                this.opts.editOptions.success(response);
            }).catch(() => {
                this.opts.editOptions.error();
            })
        },
        deleteTreeNode() {
            let idKey = this.opts.loadOptions.treeKey.idKey;
            let label = this.opts.loadOptions.treeKey.label;
            let currentNode = this.getCurrentNode();
            let currentNodeFdId = currentNode[idKey];
            if (!currentNodeFdId) {
                this.$message.error('请选择需要删除的数据！');
                return
            }

            this.$confirm(`确定要删除【${currentNode[label]}】?`, '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                this.$request({
                    url: this.opts.deleteUrl,
                    method: 'post',
                    data: this.getParams(this.opts.deleteData, currentNode)
                }).then(response => {
                    this.$message.success(`删除成功！已删除${response.data}条数据！`);
                    if (this.opts.reLoadAfterModify) {
                        if (this.opts.reLoadByResponsive) {
                            this.reLoadTreeDataResponsive();
                        } else {
                            this.loadTreeData();
                        }
                    }
                })
            })
        },
        saveDialog() {
            this.saveDialogLoadingFlag = true;
            this.$refs.treeDialog.$children[2].validate((valid) => {
                if (!valid) {
                    this.$message.error(`数据校验失败，请重新填写信息！`);
                    this.saveDialogLoadingFlag = false;
                    return false;
                }

                if (this.isAddDialog) {
                    // 添加数据
                    let insertData = this.getParams(this.opts.insertData)
                    this.$request({
                        url: this.opts.insertUrl,
                        method: 'post',
                        data: insertData
                    }).then(response => {
                        this.$message.success(`添加成功！已添加${response.data}条数据！`)
                        this.dialogVisible = false;
                        if (this.opts.reLoadAfterModify) {
                            if (this.opts.reLoadByResponsive) {
                                this.reLoadTreeDataResponsive();
                            } else {
                                this.reLoadTreeData();
                            }
                        }
                    }).finally(() => {
                        this.saveDialogLoadingFlag = false;
                    })
                } else {
                    // 更新数据
                    let updateData = this.getParams(this.opts.updateData);
                    this.$request({
                        url: this.opts.updateUrl,
                        method: 'post',
                        data: updateData
                    }).then(response => {
                        this.$message.success(`更新成功！已更新${response.data}条数据！`)
                        this.dialogVisible = false;
                        if (this.opts.reLoadAfterModify) {
                            if (this.opts.reLoadByResponsive) {
                                this.reLoadTreeDataResponsive();
                            } else {
                                this.reLoadTreeData();
                            }
                        }
                    }).finally(() => {
                        this.saveDialogLoadingFlag = false;
                    })
                }
            });
        },

        // 修改排序
        doSortChange(type) {
            let idKey = this.opts.loadOptions.treeKey.idKey;
            let sortKey = this.opts.loadOptions.treeKey.sortKey;
            let currentNode = this.getCurrentNode();
            if (!currentNode[idKey]) {
                this.$message.error(`请选择需要操作的节点！`);
                return;
            }
            let exchangeNode = getNodeByCurrent(this.$refs.treeRef, type);
            if (exchangeNode == null) {
                this.$message.error(`无需排序！`);
                return;
            }

            this.$request({
                url: this.opts.updateSortUrl,
                method: 'post',
                data: {
                    rowFdId: currentNode[idKey],
                    rowFdSort: exchangeNode[sortKey],
                    exchangeRowFdId: exchangeNode[idKey],
                    exchangeRowFdSort: currentNode[sortKey]
                }
            }).then(response => {
                this.$message.success(`排序成功！`)
                if (this.showSort) {
                    this.$refs.popoverRef.doClose();
                }
                if (this.opts.reLoadByResponsive) {
                    this.reLoadTreeDataResponsive();
                } else {
                    this.loadTreeData();
                }
            })
        },
        // 拖拽后自动排序
        sortByIndexAndDrop(draggingNode, dropNode, dropType) {
            let idKey = this.opts.loadOptions.treeKey.idKey;
            let parentKey = this.opts.loadOptions.treeKey.parentKey;
            let sortKey = this.opts.loadOptions.treeKey.sortKey;
            let childrenKey = this.opts.loadOptions.treeKey.childrenKey;

            let list = [];
            if (this.opts.sortByTree) {
                list = getDiffSortList(this.treeData, idKey, childrenKey, sortKey);
            } else {
                list = getSortListByIndex(this.treeData, idKey, childrenKey, sortKey);
            }
            this.$request({
                url: this.opts.updateSortListUrl,
                method: 'post',
                data: {list: JSON.stringify(list)}
            }).then(response => {
                // 判断是否更新父节点，inner、父节点不相同时
                let dropParentId = dropNode.data[parentKey] == null ? "" : dropNode.data[parentKey];
                let draggingParentId = draggingNode.data[parentKey] == null ? "" : draggingNode.data[parentKey];
                if (dropType == "inner" || dropParentId != draggingParentId) {
                    // 更新父节点
                    let updateItem = {};
                    updateItem[idKey] = draggingNode.data[idKey];
                    // dropType：before、after、inner
                    if (dropType == "inner") {
                        // 拖拽到节点内部
                        updateItem[parentKey] = dropNode.data[idKey];
                    } else {
                        updateItem[parentKey] = dropParentId;
                    }

                    this.$request({
                        url: this.opts.updateUrl,
                        method: 'post',
                        data: updateItem
                    }).then(response => {
                        this.$message.success(`排序成功！`);
                        if (this.showSort) {
                            this.$refs.popoverRef.doClose();
                        }
                        if (this.opts.reLoadAfterSort) {
                            if (this.opts.reLoadByResponsive) {
                                this.reLoadTreeDataResponsive();
                            } else {
                                this.loadTreeData();
                            }
                        }
                    })
                } else {
                    this.$message.success(`排序成功！`);
                    if (this.showSort) {
                        this.$refs.popoverRef.doClose();
                    }
                    if (this.opts.reLoadAfterSort) {
                        if (this.opts.reLoadByResponsive) {
                            this.reLoadTreeDataResponsive();
                        } else {
                            this.loadTreeData();
                        }
                    }
                }

            })


        },
        // 一键排序
        sortByIndex() {
            let idKey = this.opts.loadOptions.treeKey.idKey;
            let sortKey = this.opts.loadOptions.treeKey.sortKey;
            let childrenKey = this.opts.loadOptions.treeKey.childrenKey;

            let list = [];
            if (this.opts.sortByTree) {
                list = getDiffSortList(this.$refs.treeRef.data, idKey, childrenKey, sortKey);
            } else {
                list = getSortListByIndex(this.$refs.treeRef.data, idKey, childrenKey, sortKey);
            }


            this.$request({
                url: this.opts.updateSortListUrl,
                method: 'post',
                data: {list: JSON.stringify(list)}
            }).then(response => {
                this.$message.success(`排序成功！`);
                if (this.showSort) {
                    this.$refs.popoverRef.doClose();
                }
                if (this.opts.reLoadByResponsive) {
                    this.reLoadTreeDataResponsive();
                } else {
                    this.loadTreeData();
                }
            })
        },
        // 展开节点的所有父节点：id 节点id、是否触发click事件(节点元素需要使用slot设定id)
        expandParentNodesByKey(id, triggerClick) {
            this.$nextTick(() => {
                let parentIds = getTreeParentIdsById(this.treeData, id);
                parentIds.forEach(parentId => {
                    let node = this.getNode(parentId)
                    node.expand();
                });

                if (triggerClick) {
                    let ele = document.getElementById(id);
                    if (ele != null) {
                        ele.click();
                    }
                } else {
                    this.$refs.treeRef.setCurrentKey(id);
                }
            });
        },
        doItemClick(clickFn) {
            clickFn();
        },
        setCurrentKey(key) {
            this.$refs.treeRef.setCurrentKey(key)
        },
        getData() {
            return this.treeData;
        },
        getCheckedNodes() {
            return this.$refs.treeRef.getCheckedNodes()
        },
        getNode(key) {
            return this.$refs.treeRef.getNode(key)
        },
        setChecked(key, checked, deep) {
            return this.$refs.treeRef.setChecked(key, checked, deep)
        },
        clear() {
            // 清空数据
            this.treeData = [];
        },
        collapseAllNodes() {
            let nodes = this.$refs.treeRef.store._getAllNodes();
            for (let i = 0; i < nodes.length; i++) {
                nodes[i].expanded = false;
            }
        },
        expandAllNodes() {
            let nodes = this.$refs.treeRef.store._getAllNodes();
            for (let i = 0; i < nodes.length; i++) {
                nodes[i].expanded = true;
            }
        },
        // 展开当前选中节点的所有子节点（递归展开嵌套节点）
        expandCurrentNodeChildren() {
            const tree = this.$refs.treeRef;
            // 获取当前选中节点
            const currentNode = tree.getCurrentNode();
            console.log(currentNode);
            if (!currentNode) {
                this.$confirm('是否展开全部节点?', '提示', {
                    confirmButtonText: '展开全部',
                    cancelButtonText: '取消',
                }).then(() => {
                    // 展开全部节点
                    this.expandAllNodes();
                });
                return
            }
            // 从选中节点开始递归展开
            this.expandNode(currentNode);
        },
        // 递归展开所有子节点
        expandNode(node) {
            let childrenKey = this.opts.loadOptions.treeKey.childrenKey;
            if (node[childrenKey] && node[childrenKey].length) {
                let storNode = this.$refs.treeRef.store.getNode(node);
                storNode.expanded = true; // 展开当前节点
                node[childrenKey].forEach(child => {
                    this.expandNode(child); // 递归展开子节点
                });
            }
        },
        getTreeRef() {
            return this.$refs.treeRef
        },
    }
}
</script>

<style scoped>
.layout-aside-content .hide-checkbox /deep/ .el-icon-caret-right {
    display: none;
}
</style>
