/*
 * @Author: housy
 * @Email: housy@guahao.com
 * @Date: 2019-06-19 20:05:48
 * @Last Modified by: zhangzc
 * @Last Modified time: 2019-07-18 17:05:57
 * @Description: 区域选择组件
 */

<template>
    <section class="area-select-box">
        <el-button
            v-if="!readonly"
            @click="openDialog"
            type="primary">
            选择
        </el-button>
        <!-- content show -->
        <select-content
            v-model="formParams[name]"
            :readonly="readonly"
            :handleRemove="handleRemove"
            :props="props" />
        <!-- set -->
        <el-dialog
            :visible.sync="dialogVisible"
            v-bind="dialogExtend">
            <!-- content -->
            <div>
                <!-- content show -->
                <select-content
                    v-model="dialogValue"
                    :handleRemove="handleDialogRemove"
                    :props="props" />
                <!-- tree -->
                <el-tree
                    :data="dialogTreeData"
                    :node-key="props.id"
                    :props="props"
                    :expand-on-click-node="false"
                    :load="(node, resolve) => { fetchTreeNode(node.data, list => { resolve(list) }) }"
                    :render-content="renderContent"
                    v-bind="getMergedObject(treeExtend, {lazy:true})"
                    class="area-tree" />
            </div>
            <!-- content end-->
            <span
                slot="footer"
                class="dialog-footer"
                style="text-align: center;">
                <el-button
                    @click="dialogVisible = false"
                    type="primary">取 消</el-button>
                <el-button
                    @click="handleSelectArea"
                    type="primary">确 定</el-button>
            </span>
        </el-dialog>
    </section>
</template>
<script>
import { getMergedObject } from 'utils/util';
import SelectContent from './select-content';

export default {
    name: 'BkAreaSelect',
    inject: ['elFormItem'],
    components: {
        SelectContent
    },
    props: {
        materialConfig: {
            type: Object,
            required: true
        },
        readonly: {
            type: Boolean
        },
        formParams: {
            type: Object,
            required: true
        },
        // 弹窗配置
        dialogExtend: {
            type: Object,
            default: () => ({
                width: '40%',
                top: '20px',
                title: '请选择'
            })
        },
        treeExtend: {
            type: Object,
            default: () => ({})
        },
        // 字段配置
        props: {
            type: Object,
            default: () => ({
                label: 'label',
                children: 'children',
                id: 'value',
                leaf: 'leaf'
            })
        }
    },
    data() {
        return {
            getMergedObject,
            dialogVisible: false,
            name: '',
            dialogValue: [],
            dialogTreeData: []
        };
    },
    watch: {
        dialogVisible(val) {
            // 第一次显示弹窗则请求options数据
            if (val && !this.dialogTreeData.length) {
                this.fetchTreeNode({}, list => {
                    this.dialogTreeData = list;
                });
            }
        }
    },
    created() {
        this.name = this.materialConfig.name;
        if (!this.formParams[this.name]) this.formParams[this.name] = [];
    },
    methods: {
        /**
         * 值发生变化向父组件推送并触发校验
         * @param {Array} val  选择的地区对象数组
         */
        handleValueChange(val) {
            this.$emit('change', val);
            if (this.elFormItem) {
                // 触发表单校验
                this.elFormItem.onFieldChange();
            }
        },
        
        /**
         * 表单展示区域的删除操作
         * @param {Number} index 要删除数据的索引
         */
        handleRemove(index) {
            this.formParams[this.name].splice(index, 1);
            this.handleValueChange(this.formParams[this.name]);
        },

        /**
         * 弹窗区域的删除操作
         * @param {Number} index 要删除数据的索引
         */
        handleDialogRemove(index) {
            this.dialogValue.splice(index, 1);
        },

        /**
         * 发送异步请求，获取子节点
         * @param {Object} obj  请求子节点的父级对象
         * @param {Function} callback  请求成功的回调函数
         */
        fetchTreeNode(obj, callback) {
            const { http } = this.materialConfig;
            const { params, url, method } = http || {};
            if (!url) return;
            const realParams = {
                ...params,
                [(params && params.default) || 'parentId']: obj[this.props.id]
            };
            if (realParams.default) delete realParams.default;
            this.$fetch({
                url,
                params: realParams,
                method
            }).then(res => {
                if (!res.data) return;
                const { data } = res;
                let list = [];
                if (Array.isArray(data)) {
                    list = data;
                } else if (Array.isArray(data.results)) {
                    list = data.results;
                }
                list.forEach((item, i) => {
                    list[i].leaf = true;
                });
                if (callback instanceof Function) callback(list);
            });
        },

        /**
         * 节点的选择事件
         * @param {Object} data 选择节点的数据对象
         * @param {Object} node 选择节点的node对象
         */
        appendSelectedObj(data, node) {
            const obj = {
                [this.props.label]: data[this.props.label],
                [this.props.id]: data[this.props.id],
                parents: []
            };
            // 判断当前选择的节点是否已被选择
            const selectedObj = this.dialogValue.find(val => val[this.props.id] === obj[this.props.id]);
            if (selectedObj) {
                return this.$message.warning(`${selectedObj[this.props.label]}已经存在`);
            }
            // 所有父级id
            this.setParents(node, obj.parents);
            if (this.dialogValue.length) {
                // 父级已被选中则提示警告信息
                const parentObj = this.selectedParentObj(obj);
                if (parentObj) {
                    return this.$message.warning(` ${obj[this.props.label]}的父级${
                        parentObj[this.props.label]
                    }已经存在`);
                }
                // 子级已被选中则删除子级
                this.deleteChildren(obj);
            }
            this.dialogValue.push(obj);
            return this.dialogValue;
        },

        /**
         * 返回数据对象的所有父级中已被选择的父级对象
         * @param {Object} obj  数据对象
         */
        selectedParentObj(obj) {
            const { parents } = obj;
            let parentObj;
            const that = this;

            function findSelectedParent(i = 0) {
                parentObj = that.dialogValue.find(val => {
                    if (!parents[i]) return false;
                    return val[that.props.id] === parents[i][that.props.id];
                });
                if (!parentObj && i < parents.length) {
                    findSelectedParent(i + 1);
                }
            }
            findSelectedParent();
            return parentObj;
        },
        
        /**
         * 数据对象的子级已被选择则删除子级
         * @param {Object} obj  数据对象
         */
        deleteChildren(obj) {
            const id = obj[this.props.id];
            const index = this.dialogValue.findIndex(val => {
                const parents = val.parents.map(item => item[this.props.id]);
                return parents.includes(id);
            });
            if (index > -1) {
                this.dialogValue.splice(index, 1);
            }
        },
        
        /**
         * 为节点设置所有父级对象
         * @param {Object} node  选择的节点node
         * @param {Array} parentsList  选择节点的所有父节点容器
         */
        setParents(node, parentsList) {
            if (node.level > 1 && !Array.isArray(node.data)) {
                parentsList.unshift({
                    [this.props.id]: node.parent.data[this.props.id],
                    [this.props.label]: node.parent.data[this.props.label]
                });
                this.setParents(node.parent, parentsList);
            }
        },

        /**
         * tree节点的布局设置
         * @param {Function} h  返回创建的标签元素
         * @param {Object}  [node] 节点node
         *                  [data] 节点数据
         */
        renderContent(h, { node, data }) {
            return (
                <span class="custom-tree-node">
                    <span>{node.label}</span>
                    <span class="custom-tree-node_set">
                        <el-button
                            size="mini"
                            type="text"
                            on-click={() => this.appendSelectedObj(data, node)}
                        >
                            选择
                        </el-button>
                    </span>
                </span>
            );
        },

        /**
         * 显示弹窗并同步弹窗选择区域和表单选择区域的值
         */
        openDialog() {
            this.dialogValue = JSON.parse(JSON.stringify(this.formParams[this.name]));
            this.dialogVisible = true;
        },

        /**
         * 弹框确定按钮点击事件
         */
        handleSelectArea() {
            this.dialogVisible = false;
            this.formParams[this.name] = JSON.parse(JSON.stringify(this.dialogValue));
            this.handleValueChange(this.formParams[this.name]);
        }
    }
};
</script>
