/*
 * @Author: qiuwt
 * @Email: qiuwt@guahao.com
 * @Date: 2019-07-02 21:19:42
 * @Last Modified by: mikey.zhaopeng
 * @Last Modified time: 2022-04-27 11:16:03
 * @Description: 功能按钮
 */

<template>
    <span v-if="currentVisiable"
          class="btn-item">
        <!-- 下拉按钮 -->
        <template v-if="scene === 'dropdown'">
            <el-dropdown @command="(val) => handleDropdownClick(val)"
                         :key="name || i"
                         v-bind="getMergedObject(extend, {trigger:'click'})">
                <el-button v-bind="getMergedObject(btnExtend, { type: 'primary', size: 'small' })"
                           class="btn-dropdown">
                    {{ label }}
                    <i class="el-icon-arrow-down el-icon--right" />
                </el-button>
                <el-dropdown-menu slot="dropdown">
                    <el-dropdown-item v-for="(subItem, subIndex) of data"
                                      :key="subIndex"
                                      :command="subIndex">
                        {{ subItem.label }}
                    </el-dropdown-item>
                </el-dropdown-menu>
            </el-dropdown>
        </template>
        <!-- 上传 -->
        <template v-else-if="scene === 'upload'">
            <bk-upload :key="name || i"
                       v-bind="getMergrdObjectUpload(this)" />
        </template>
        <!-- 其他按钮 -->
        <template v-else>
            <el-button :key="name|| i"
                       :class="{
                           'operation-delete': scene === 'delete'
                       }"
                       v-bind="getMergedObject(extend,
                                               { 
                                                   disabled: currentDisabled,
                                                   type: scene === 'delete' ? 'danger' : 'primary',
                                                   size: 'small'
                                               })"
                       @click=" () => handleBtnClick(this)">
                <template v-if="currentLabel !== ''">{{ currentLabel }}</template>
                <slot />
            </el-button>
        </template>
    </span>
</template>
<script>
import {
    DownLoader, getMergedObject, toUrlParam, isEmptyObj, hasField, downloadFile 
} from 'src/utils/util';

export default {
    name: 'BkButton',
    props: {
        mode: {
            type: String,
            default: 'btn'
        },
        // 表格行数据
        tableRow: {
            type: Object,
            default: () => {}
        },
        // 下拉按钮样式
        btnExtend: {
            type: Object,
            default: () => {}
        },
        // 下拉列表组
        data: {
            type: Array,
            default: () => []
        },
        // 对话框配置
        dialogConfig: {
            type: Object,
            default: () => {}
        },
        // 对话框显示
        dialogVisible: {
            type: Object,
            default: () => {}
        },
        // 对应的对话宽name
        target: {
            type: String,
            default: ''
        },
        // 上传前置请求
        fileBefore: {
            type: Object,
            default: () => {}
        },
        // 类型
        scene: {
            type: String,
            default: ''
        },
        // 唯一标示
        name: {
            type: String,
            required: true
        },
        // 按钮文本 列表会出现空的情况
        label: {
            type: [String, Number, Function],
            default: ''
        },
        // 请求时显示
        loadingText: {
            type: String,
            default: ''
        },
        // 是否需要展示文件列表
        hasFileList: {
            type: Boolean,
            default: true
        },
        // element Ui扩展配 按钮时指向按钮，下拉时指向下拉
        extend: {
            type: Object,
            default: () => {}
        },
        // 配置请求
        http: {
            type: Object,
            default: () => {}
        },
        // 导出功能使用
        search: {
            type: Object,
            default: () => {}
        },
        // 触发回调
        callback: {
            type: Function,
            default: () => {}
        },
        disabled: {
            type: [Boolean, Function],
            default: false
        },
        visible: {
            type: [Boolean, Function],
            default: true
        },
        maxSize: {
            type: [Number],
            default: 5
        },
        // list组件静态标识
        tableDataTotalFlag: {
            type: Boolean,
            default: false
        }
    },
    data() {
        const bkConfig = this.$root.bkConfig || this.$bkConfig;
        return {
            bkConfig,
            currentProps: Object.assign(
                {
                    data: 'data',
                    fileId: 'kanoId',
                    fileUrl: 'fileUrl' // 文件地址
                },
                bkConfig.responseProps,
                bkConfig.responseFileProps
            ),
            currentLabel: this.getLabelFn(),
            currentDisabled: this.hangdleAttrFn(this.disabled),
            currentVisiable: this.hangdleAttrFn(this.visible)
        };
    },
    watch: {
        label(val, old) {
            if (val === old) return;
            this.currentLabel = this.getLabelFn();
        },
        visible(val, old) {
            if (val === old) return;
            this.currentVisiable = this.hangdleAttrFn(val);
        },
        disabled(val, old) {
            if (val === old) return;
            this.currentDisabled = this.hangdleAttrFn(val);
        },
        tableRow(val) {
            if (!val) return;
            this.currentDisabled = this.hangdleAttrFn(this.disabled);
            this.currentVisiable = this.hangdleAttrFn(this.visible);
        }
    },
    methods: {
        hasField,
        isEmptyObj,
        getMergedObject,
        /**
         * 属性支持function
         * @param { Functon,Boolean } attr
         */
        hangdleAttrFn(attr) {
            if (typeof attr === 'function') {
                return attr(this);
            }
            return attr;
        },
        /**
         * 下拉点击事件
         * @param {Number} 获得按钮索引
         *  */

        handleDropdownClick(val) {
            const cell = this.data[val];
            this.handleBtnClick(cell);
        },
        // 按钮新点击事件
        async handleBtnClick(btn) {
            const { http, scene, callback } = btn;
            let currentParams = '';
            const { params, method, onBefore } = http || {};
            // before钩子事件
            if (onBefore) {
                currentParams = await onBefore(btn, this);
                // before 返回false跳出操作
                if (currentParams === false) return;
            } else {
                currentParams = params;
            }
            // 生产配置
            const config = {
                url: http ? http.url : '', // 获取before处理后的before
                params: currentParams || {},
                method: method || 'get'
            };
            // 弹窗按钮，支持回显数据
            switch (scene) {
            case 'dialog':
                this.handleDialogBtn(btn, config);
                break;
            case 'skip':
                this.handleSkipBtn(btn, config);
                break;
            case 'normal':
                this.handleNormalBtn(btn, config);
                break;
            case 'export':
                this.handleExportBtn(btn, config);
                break;
            case 'download':
                this.handleDownloadBtn(btn);
                break;
            case 'delete':
                if (!this.tableDataTotalFlag) {
                    this.handleNormalBtn(btn, config);
                } else {
                    this.handleStaticDeleteBtn(btn, config);
                }
                break;
            default:
                if (callback) callback(this);
            }
        },
        // 弹窗型按钮
        handleDialogBtn(btn, config) {
            const { dialogConfig, dialogVisible } = this;
            const { target } = btn;
            dialogVisible[target] = true;
            const { params, url } = config;
            if (url) {
                // 请求获取数据
                this.handleBtnFetch({
                    config,
                    btn,
                    callback: res => {
                        // 弹窗内只有一个且是表单模块时向内填充数据
                        const targetDialog = dialogConfig.data.find(item => item.name === target);
                        if (
                            Array.isArray(targetDialog.data)
                            && targetDialog.data.length === 1
                            && targetDialog.data[0].type === 'form'
                        ) {
                            const { formConfig } = targetDialog.data[0] || {};
                            if (formConfig) this.$set(formConfig, 'results', res.data);
                        }
                    }
                });
            } else {
                // 弹窗内只有一个且是表单模块时向内填充数据
                const targetDialog = dialogConfig.data.find(item => item.name === target);
                if (
                    Array.isArray(targetDialog.data)
                    && targetDialog.data.length === 1
                    && targetDialog.data[0].type === 'form'
                ) {
                    const { formConfig } = targetDialog.data[0] || {};
                    if (formConfig) this.$set(formConfig, 'results', params);
                }
            }
        },
        // 跳转型按钮
        handleSkipBtn(btn, config) {
            const { params, url } = config;
            if (!url) {
                console.error('http.url is undefined;');
                return;
            }
            // 外链
            if (url.match(/\/\//g)) {
                const baseUrl = `${url}?${toUrlParam(params)}`;
                window.open(baseUrl);
            } else {
                this.$router.push({
                    path: url,
                    query: params
                });
            }
        },
        // 请求型按钮
        handleNormalBtn(btn, config, row) {
            this.handleConfirm(btn, config, row, () => {
                // 添加loading状态
                this.triggerLoading();
                this.handleBtnFetch({
                    config,
                    btn
                });
            });
        },
        // 静态删除型按钮
        handleStaticDeleteBtn(btn, config, row) {
            if (!this.tableDataTotalFlag) return;
            this.handleConfirm(btn, config, row, () => {
                if (!isEmptyObj(this.tableRow)) {
                    // 行内删除
                    this.$emit('delete', this.tableRow);
                } else {
                    // 批量删除
                    this.$emit('delete');
                }
            });
        },
        /**
         * 发送请求前的二次确认
         */
        handleConfirm(btn, config, row, fn) {
            const { http } = btn;
            const { confirm } = http;
            let currentConfirm = true;
            let message = '请确认你的操作！';
            let title = '提示';
            const options = {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                dangerouslyUseHTMLString: true
            };
            if (confirm && typeof confirm === 'string') {
                // 添加提示内容
                message = confirm;
            } else if (confirm === false) {
                currentConfirm = confirm;
            } else if (typeof confirm === 'function') {
                message = confirm(row, this);
            } else if (typeof confirm === 'object' && !isEmptyObj(confirm)) {
                message = confirm.message || '请确认你的操作！';
                title = confirm.title || '提示';
                Object.assign(options, confirm);
            }
            if (currentConfirm) {
                this.$confirm(message, title, options)
                    .then(() => {
                        fn();
                    })
                    .catch(() => {});
            } else {
                fn();
            }
        },
        triggerLoading(disabled = true) {
            const { loadingText } = this;
            const label = this.getLabelFn();
            this.currentLabel = (disabled === true ? loadingText : label) || label;
            this.currentDisabled = disabled;
        },
        // labe 为fn时执行
        getLabelFn() {
            if (typeof this.label === 'function') {
                this.$watch(`tableRow.${this.name}`, () => {
                    this.currentLabel = this.getLabelFn();
                });
                return this.label(this.tableRow, this);
            }
            return this.label;
        },
        /**
         * 扩展上传按钮配置默认配置
         * @param {Object} item  type upload btn配置参数
         *
         */
        getMergrdObjectUpload(item) {
            const obj = item.$props || item;
            const extend = item.extend || {};
            extend.limit = 2; // 为了保证一致时按钮的状态
            // const { type = 'primary' } = extend;
            // 0317 处理额外参数
            if (item.http && item.http.params) {
                extend.data = item.http.params;
            }
            const cfg = Object.assign(
                {
                    buttonLabel: item.label,
                    name: item.name,
                    http: item.http,
                    imgList: false,
                    btnType: extend.type,
                    uploadBefore: file => this.handleUploadBefore(file, item),
                    uploadSuccess: (res, file, fileList) => this.handleUploadSuccess(res, file, fileList, item)
                },
                obj,
                {
                    extend,
                    disabled: this.currentDisabled
                }
            );
            return cfg;
        },
        /**
         * 文件上传说明
         * 上传分两种模式，同步与异步，这里的同异步指后端进程，非前端进程
         * 异步上传流程：1、接口校验是否可以上传 2、通过则进行上传 3、上传成功文件后保存kanoID
         * 同步上传流程：1、上传，页面进入等待状态 2、上传成功后保存kanoID
         * @param {object} [file] 上传文件的信息
         * @param {Object} [item] 上传按钮的配置信息
         */
        // 文件上传前的钩子函数
        handleUploadBefore(file, item) {
            // 如果不存在，则执行默认行为，urlBefore参数为完成的请求参数
            const { fileBefore, http } = item;
            const { onBefore } = http;
            let bool = true;
            if (onBefore) {
                item.fileObject = file;
                const resBool = onBefore(item, file);
                if (resBool !== undefined) bool = resBool;
            }
            if (fileBefore && fileBefore.url) {
                return new Promise((resolve, reject) => {
                    this.$fetch(item.fileBefore).then(res => {
                        if (!res) {
                            this.$message.error('当前存在执行中的任务，无法上传');
                            return reject();
                        }
                        return resolve();
                    });
                });
            }
            return bool;
        },
        // 文件上传成功的钩子函数
        handleUploadSuccess(res, file, fileList, item) {
            // const { fileId, data } = this.currentProps;
            const { http } = item;
            // 如果不存在，则执行默认的成功时间，将kanoID保存 0317
            // if (http && http.url) {
            //     const { params, onSuccess } = http;
            //     if (params) {
            //         // eslint-disable-next-line
            //         http.params = Object.assign({}, params, {
            //             [fileId]: res[data][fileId]
            //         });
            //     } else {
            //         // eslint-disable-next-line
            //         http.params = {
            //             [fileId]: res[data][fileId]
            //         };
            //     }

            //     this.$fetch(http).then(resp => {
            //         if (!resp) {
            //             this.$message.error('文件上传失败，请重新尝试');
            //         } else {
            //             this.$message.success('文件上传成功');
            //         }
            //         // 上传完毕自定义回调
            //         if (onSuccess) onSuccess(resp, this);
            //     });
            // }
            if (http && http.onSuccess) http.onSuccess(res, this);
            return true;
        },
        /*
         * 文件下载流程说明
         * 1、接口获取对应的kanoId
         * 2、通过kanoId查询对应的kano文件地址
         */
        // 导出型按钮
        handleExportBtn(btn, config) {
            let { file } = btn.http || {};
            if (!file) file = {};
            // 避免污染下一次请求
            const currentConfig = JSON.parse(JSON.stringify(file));

            this.triggerLoading();
            // 获取kanoId
            this.handleBtnFetch({
                config,
                btn,
                callback: res => {
                    if (!res) return null;
                    // 提交文件服务器时字段
                    let { props } = currentConfig;
                    props = Object.assign({}, this.currentProps, props);

                    // 文件服务器
                    if (!currentConfig.url) {
                        currentConfig.url = this.bkConfig.filePath;
                    }
                    if (res[props.data]) {
                        currentConfig.params = Object.assign({}, currentConfig.params, {
                            [props.fileId]: res[props.data]
                        });
                        const fileId = res[props.data];
                        if (this.bkConfig.downloadPath && fileId) {
                            const downloadUrl = encodeURI(`${this.bkConfig.downloadPath}?ids[]=${fileId}`);
                            downloadFile(downloadUrl);
                            return null;
                        }
                        // 通过kanoId查询下载地址
                        return this.handleBtnFetch({
                            config: currentConfig,
                            btn: Object.assign({}, btn, { http: currentConfig }),
                            callback: server => {
                                if (!server) return;
                                const { data, fileUrl, fileName } = props;
                                // 根据地址下载文件
                                if (this.bkConfig && this.bkConfig.downloadFlag) {
                                    DownLoader(server[data][fileUrl], server[data][fileName]);
                                } else {
                                    downloadFile(server[data][fileUrl]);
                                }
                            }
                        });
                    }
                    return null;
                }
            });
        },
        // 下载型按钮
        handleDownloadBtn(btn) {
            if (isEmptyObj(this.tableRow)) return;
            const { tableRow } = this;
            const {
                targetKeys, url, params, method, onFaild, onSuccess 
            } = btn.http || {};
            const fileId = tableRow[targetKeys];
            if (this.bkConfig.downloadPath && fileId) {
                const downloadUrl = encodeURI(`${this.bkConfig.downloadPath}?ids[]=${fileId}`);
                downloadFile(downloadUrl);
                return;
            }
            const fileParams = (this.bkConfig && this.bkConfig.responseFileProps && this.bkConfig.responseFileProps.fileId)
                || 'fileId';
            const realParams = Object.assign({}, params, {
                [(params && params.default) || fileParams]: fileId
            });
            if (hasField(realParams, 'default')) {
                delete realParams.default;
            }
            this.$fetch({
                url: url || this.bkConfig.filePath,
                params: realParams,
                method
            }).then(res => {
                if (!res) {
                    if (typeof onFaild === 'function') {
                        onFaild('查询失败', this);
                    }
                    return;
                }
                if (typeof onSuccess === 'function') {
                    const ret = onSuccess(res, this);
                    if (ret === false) return;
                }
                const { data, fileUrl, fileName } = this.currentProps;
                if (this.bkConfig && this.bkConfig.downloadFlag) {
                    DownLoader(res[data][fileUrl], res[data][fileName]);
                } else {
                    downloadFile(res[data][fileUrl]);
                }
            });
        },
        // 按钮请求事件
        handleBtnFetch(params) {
            const { config, btn, callback } = params;
            const { onSuccess, onFaild, url } = btn.http || {};
            if (!url) {
                console.error('http.url is undefined;');
                return;
            }
            this.$fetch(config).then(
                res => {
                    if (res !== false) {
                        this.$emit('change');
                        let resp = res;
                        if (onSuccess) {
                            resp = onSuccess(res, this);
                        }
                        if (callback) callback(resp, this);
                    } else if (typeof onFaild === 'function') {
                        onFaild(res, this);
                    }
                    this.triggerLoading(false);
                },
                () => {
                    this.triggerLoading(false);
                }
            );
        }
    }
};
</script>
