<template>
    <div class="flex-col" style="height: 100%;border-bottom: 1px solid #dfe6ec;">
        <div class="ap-table-header flex-justify-between">
            <span class="ap-table-header-title">
                {{ opts.title }}
                <i v-if="opts.showExport" class="el-icon-excel ap-table-export" @click="exportExcel"/>
            </span>

            <div class="ap-table-header-ctn">
                <el-button-group class="ap-table-toolbar">
                    <template v-for="(btn, index) in opts.toolbarBtn">
                        <ap-button v-if="btn.apButton" :type="btn.btnType" :icon="btn.icon"
                                   :style="btn.style"
                                   :click="btn.onClick">
                            {{ btn.text }}
                        </ap-button>
                        <el-button v-else :type="btn.btnType" :icon="btn.icon"
                                   :style="btn.style"
                                   @click="typeof btn.onClick === 'function' ? btn.onClick() :toolbarBtnFn(btn.type)">
                            {{ btn.text }}
                        </el-button>
                    </template>
                </el-button-group>
            </div>
        </div>

        <el-table :ref="opts.id"
                  :id="opts.elementId"
                  :data="localData"
                  height="100%" width="100%" border class="ap-data-table flex-1"
                  :highlight-current-row="opts.highlightCurrentRow"
                  v-loading="tableLoading"
                  :show-summary="opts.showSummary"
                  :summary-method="opts.summaryMethod"
                  :span-method="opts.spanMethod"
                  @row-click="rowClickEvent">
            <el-table-column v-if="opts.showSelection" type="selection" width="35" align="center"></el-table-column>
            <el-table-column v-if="opts.isDefaultIndex" type="index" label="No." width="35" header-align="center" align="center"></el-table-column>
            <el-table-column v-if="!opts.isDefaultIndex" prop="dataIdx" label="No." width="35" header-align="center" align="center">
                <template slot-scope="scope">{{ scope.row.dataIdx }}</template>
            </el-table-column>
            <template v-for="col in opts.columns">
                <el-table-column :prop="col.prop" :label="col.label"
                                 :width="col.width" :min-width="col.minWidth"
                                 :align="col.align" header-align="center">
                    <template slot="header" slot-scope="scope">
                        {{col.label}}
                        <template v-if="col.help != null">
                            <el-popover trigger="hover">
                                <div v-html="col.help"></div>
                                <i slot="reference" class="el-icon-wenhao"/>
                            </el-popover>
                        </template>
                    </template>

                    <template slot-scope="scope">
                        <slot v-if="col.type === 'slot'" :name="col.slot" :rowData.sync="scope.row"></slot>

                        <div v-else-if="isText(col, scope.row)" v-html="renderColHtml(col, scope.row)"></div>

                        <el-input v-else-if="col.type === 'input'" v-model="scope.row[col.prop]"
                                  :disabled="getDisableStatus(col, scope.row)" class="ap-table-col-input"
                                  @change="changeEvent(scope.row)"></el-input>

                        <el-input-number v-else-if="col.type === 'inputNumber'" v-model="scope.row[col.prop]"
                                         :disabled="getDisableStatus(col, scope.row)" class="ap-table-col-input-number"
                                         :controls="false" @change="changeEvent(scope.row)"></el-input-number>

                        <el-date-picker
                            v-else-if="col.type === 'datePicker'" v-model="scope.row[col.prop]"
                            :disabled="getDisableStatus(col, scope.row)" type="date" value-format="timestamp" :format="col.datePickerFormat"
                            placeholder="选择日期" class="ap-table-col-datepicker"
                            @change="changeEvent(scope.row)">
                        </el-date-picker>

                        <el-select v-else-if="col.type === 'select'" v-model="scope.row[col.prop]" placeholder="请选择"
                                   :disabled="getDisableStatus(col, scope.row)" class="ap-table-col-select"
                                   @change="changeEvent(scope.row)">
                            <template v-for="selectItem in col.selectOptions">
                                <el-option :key="selectItem.value" :label="selectItem.label"
                                           :value="selectItem.value"></el-option>
                            </template>
                        </el-select>

                    </template>

                </el-table-column>
            </template>
        </el-table>
    </div>
</template>

<script>

import {deepClone} from "ap-util/util/ObjectUtil";
import {formatDate} from "ap-util/util/DateUtil";
import FileSaver from 'file-saver';
import XLSX from 'xlsx';

export default {
    name: 'ApDataTable',
    props: {
        localData: {
            type: Array,
            required: true
        },
        options: {
            type: Object,
            required: true
        },
    },
    created() {
        this.handleDefaultOptions();
        // 设定唯一id
        this.setDataIdx();
    },
    data() {
        return {
            opts: {},
            tableLoading: false, // 表格加载状态
            delRows: [], // 已删除行
            tempData: {
                addRowCount: 0,                    // 添加行计数
                oldReadonly: false,
            },
        }
    },
    methods: {
        // 参数设定：表格默认参数
        handleDefaultOptions() {
            let options = deepClone(this.options);

            // 默认参数：列
            let defaultColOpts = {
                disabled: false,
                align: 'left',         // 列内容对齐方式
            };

            let randomVal = parseInt(Math.random() * 10000000);// 随机ID
            // 默认参数：表格
            let defaultOpts = {
                id: "ref" + randomVal,
                elementId: "Table" + randomVal,
                toolbarBtn: [],
                highlightCurrentRow: true,
                isReadonly: false, // 表格是否只读
                showSelection: true, // 不显示选择列
                showExport: false, // 显示导出
                isDefaultIndex: false, // No列，默认dataIdx
                summaryMethod() {
                },
                spanMethod() {
                },
                changeEvent(row) {
                },
                rowClickEvent() {
                },
                beforeAdd() {
                    return true
                },
                beforeDelete() {
                    return true
                },
                afterDelete() {
                },
            };

            // 默认参数覆盖：列
            let columns = options.columns;
            for (let i = 0; i < columns.length; i++) {
                for (let optKey in defaultColOpts) {
                    // 替换默认
                    if (columns[i][optKey] == null) {
                        columns[i][optKey] = defaultColOpts[optKey];
                    }
                }
            }
            // 默认参数覆盖：表格
            for (let optKey in defaultOpts) {
                // 顶部工具：参数处理
                let customOpt = options[optKey];
                if (optKey == "toolbarBtn") {
                    this.handleToolbarBtn(options);
                    continue;
                }

                // 其他参数：直接替换默认
                if (customOpt == null) {
                    options[optKey] = defaultOpts[optKey];
                }
            }

            this.opts = options;
        },
        // 参数设定：工具栏
        handleToolbarBtn(options) {
            let btns = options["toolbarBtn"];
            if (btns == null && !(btns instanceof Array)) {
                return
            }

            let newToolbarBtns = [];
            let defaultStyle = "";
            for (let tIndex = 0; tIndex < btns.length; tIndex++) {
                let tool = btns[tIndex];
                let item = {};
                // 字符串形式
                if (typeof (tool) == "string") {
                    item.type = tool;
                    switch (tool) {
                        case "add":
                            item.text = "添加";
                            item.icon = "el-icon-plus";
                            item.btnType = "primary";
                            break;
                        case "del":
                            item.text = "删除";
                            item.icon = "el-icon-close";
                            item.btnType = "danger";
                            break;
                        default:
                            item = null;
                    }
                    if (item != null) {
                        newToolbarBtns.push(item);
                    }
                }
                // 对象，自定义
                if (typeof (tool) == "object") {
                    tool.style = defaultStyle;
                    newToolbarBtns.push(tool);
                }
            }
            options["toolbarBtn"] = newToolbarBtns;
        },
        // 工具栏：操作表格数据入口
        toolbarBtnFn(type) {
            switch (type) {
                case "add" :
                    this.addRow();
                    break;
                case "del" :
                    this.doDelRow();
                    break;
            }
        },
        setDataIdx() {
            let dataIdx = 1;
            let data = this.localData;
            for (let i = 0; i < data.length; i++) {
                data[i].dataIdx = dataIdx;
                ++dataIdx;
            }
        },
        // 获取添加行的临时唯一id
        getAddRowId() {
            return "+" + (++this.tempData.addRowCount);
        },
        // 列是否失效
        getDisableStatus(col, row) {
            let disableObj = col.disabled;
            if (disableObj != undefined) {
                if (typeof disableObj === 'function') {
                    return disableObj(row);
                } else {
                    return disableObj;
                }
            } else {
                return true;
            }
        },
        // 是否显示为文本
        isText(col, row) {
            let renderObj = col.renderText;

            if (this.opts.isReadonly) {
                return true;
            }

            if (renderObj == undefined) {
                return false;
            }
            if (typeof renderObj === 'function') {
                return renderObj(row);
            } else {
                return renderObj;
            }
        },
        // 渲染文本格式的列
        renderColHtml(col, row) {
            let renderObj = col.renderText;
            // 方法表示自定义
            if (typeof renderObj === 'function') {
                return renderObj(row);
            }
            switch (col.type) {
                case "select":
                    return this.getSelectColValue(col, row);
                case "datePicker":
                    return this.getDatePickerColValue(col, row);
            }
            return row[col.prop];
        },
        getDatePickerColValue(col, row){
            if (row[col.prop] == null) {
                return "";
            }
            let fmt = col.datePickerFormat;
            if (fmt == null) {
                fmt = "yyyy-MM-dd";
            }
            return formatDate(new Date(row[col.prop]), fmt)
        },
        getSelectColValue(col, row){
            let options = col.selectOptions;
            if (options == null || options.length <1) {
                return "";
            }
            for (let i = 0; i < options.length; i++) {
                if (options[i].value == row[col.prop]) {
                    return options[i].label;
                }
            }
            return "";
        },
        // 添加行：指定数据。isHead顶部添加
        addRows(newRows, isHead) {
            if (newRows == null || newRows.length < 1) {
                return;
            }
            if (isHead) {
                for (let i = (newRows.length-1); i >= 0; i--) {
                    this.addRowData(newRows[i], isHead);
                }
            } else {
                for (let i = 0; i < newRows.length; i++) {
                    this.addRowData(newRows[i], isHead);
                }
            }

        },
        // 添加行：指定数据。isHead顶部添加
        addRowData(newRow, isHead) {
            // 添加前回调
            if (!this.beforeAdd()) {
                return;
            }

            let columns = this.opts.columns;
            if (newRow == null) {
                newRow = {};
            }
            // 添加列默认值设定
            for (let i = 0; i < columns.length; i++) {
                let addDefaultValue = columns[i].addDefaultValue;
                if (addDefaultValue != undefined) {
                    // 属性为空，进行默认值处理
                    if (newRow[columns[i].prop] == null || newRow[columns[i].prop] == undefined) {
                        newRow[columns[i].prop] = typeof addDefaultValue === 'function' ? addDefaultValue() : addDefaultValue;
                    }
                }
            }
            // 添加时默认值
            let addDefaultObj = this.opts.addDefaultObj;
            if (addDefaultObj != undefined) {
                let tempAddDefaultObj = typeof addDefaultObj === 'function' ? addDefaultObj() : addDefaultObj;
                for (let key in tempAddDefaultObj) {
                    // 属性为空，进行默认值处理
                    if (newRow[key] || newRow[key] == undefined) {
                        newRow[key] = tempAddDefaultObj[key];
                    }
                }
            }

            newRow.isAdd = true;
            newRow.dataIdx = this.getAddRowId();
            if (isHead) {
                this.localData.unshift(newRow);
            } else {
                this.localData.push(newRow);
            }
        },
        addRow() {
            this.addRowData(null, true);
        },
        doDelRow() {
            let selectRows = this.getSelection();
            if (selectRows == null || selectRows.length == 0) {
                this.$message({
                    showClose: true,
                    message: '请选择需要删除的数据！',
                    type: 'error'
                });
                return;
            }

            // 删除前回调
            if (!this.beforeDelete(selectRows)) {
                return;
            }

            this.$confirm('确定删除已选择数据?', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                this.delRow(selectRows);
            }).catch(() => {
                this.$message({
                    type: 'info',
                    message: '已取消删除'
                });
            });
        },
        delRow(selectRows) {
            let data = this.localData;
            selectRows = JSON.parse(JSON.stringify(selectRows));
            for (let i = 0; i < selectRows.length; i++) {
                for (let j = data.length - 1; j >= 0; j--) {
                    if (data[j].dataIdx === selectRows[i].dataIdx) {
                        // 手动添加的不记录
                        if (data[j].isAdd == undefined) {
                            this.delRows.push(data[j]);
                        }

                        data.splice(j, 1);
                        break;
                    }
                }
            }
            // 删除回调
            this.afterDelete();
        },
        // 替换行：等同于先执行删除，再进行添加
        replaceRows(addRows) {
            this.delRow(this.localData);
            if (addRows == null || addRows.length < 1) {
                return;
            }
            for (let i = 0; i < addRows.length; i++) {
                this.addRowData(addRows[i], false);
            }
        },
        // 回调：添加前
        beforeAdd() {
            // 自定义回调方法
            return this.opts.beforeAdd();
        },
        // 回调：删除前
        beforeDelete(selectRows) {
            // 自定义回调方法
            return this.opts.beforeDelete(selectRows);
        },
        // 回调：删除后
        afterDelete() {
            // 自定义回调方法
            this.opts.afterDelete();
        },
        rowClickEvent(row, column, event) {
            this.opts.rowClickEvent(row, column, event);
        },
        // 修改事件
        changeEvent(row) {
            // 添加行：不设定isEdit属性
            if (!row.isAdd) {
                row.isEdit = true;
            }
            this.opts.changeEvent(row);
        },
        getAddRowList() {
            let data = this.localData;
            let res = [];
            for (let i = 0; i < data.length; i++) {
                if (data[i].isAdd) {
                    res.push(data[i]);
                }
            }
            return res;
        },
        getEditRowList() {
            let data = this.localData;
            let res = [];
            for (let i = 0; i < data.length; i++) {
                if (data[i].isEdit) {
                    res.push(data[i]);
                }
            }
            return res;
        },
        getDelRowList() {
            return this.delRows;
        },
        getSelection() {
            let selectRows = this.$refs[this.opts.id].selection;
            return selectRows;
        },
        // 刷新表格
        refresh() {
            this.delRows = [];
            this.handleDefaultOptions();
            // 设定唯一id
            this.setDataIdx();
            this.$nextTick(() => {
                this.doLayout();
            });
        },
        // 重载表格：重新渲染
        reload() {
            // 初始化组件前修改默认参数
            this.handleDefaultOptions();
            this.$nextTick(() => {
                this.doLayout();
            });
        },
        // 修改表格标题
        setTitle(title) {
            this.opts.title = title;
        },
        // 显示加载状态
        showLoading() {
            this.tableLoading = true;
        },
        // 隐藏加载状态
        hideLoading() {
            this.tableLoading = false;
        },
        doLayout() {
            if (this.$refs[this.opts.id] != null) {
                this.$refs[this.opts.id].doLayout();
            }
        },
        // 导出表格数据
        exportExcel() {
            if (this.getAddRowList().length > 0 || this.getEditRowList().length > 0 || this.getDelRowList().length > 0) {
                this.$message.error("导出失败，请先保存数据！")
                return;
            }

            this.$confirm(`是否导出当前表格数据？`, '提示', {
                confirmButtonText: '导出',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                let isReadonly = this.options.isReadonly;
                this.tempData.oldReadonly = isReadonly;
                if (!isReadonly) {
                    this.options.isReadonly = true;
                    this.reload();
                }
                this.$nextTick(() => {
                    this.doExportExcel();
                });
            })
        },
        doExportExcel() {
            let id = "#" + this.opts.elementId;
            let fix = document.querySelector('.el-table__fixed');
            let wb;
            if (fix) {
                wb = XLSX.utils.table_to_book(document.querySelector(id).removeChild(fix));
                document.querySelector(id).appendChild(fix);
            } else {
                wb = XLSX.utils.table_to_book(document.querySelector(id));
            }
            let wbout = XLSX.write(wb, {
                bookType: 'xlsx',
                bookSST: true,
                type: 'array'
            });
            // 文件名：表格title + 日期时间
            let fileName = "";
            let title = this.opts.title;
            if (title == null || title == undefined || title == "") {
                fileName = "File_";
            } else {
                fileName = title + "_";
            }
            fileName += formatDate(new Date(),'yyyyMMddHHmmss');
            fileName += '.xlsx';
            try {
                FileSaver.saveAs(new Blob([wbout], {
                    type: 'application/octet-stream'
                }),  fileName);
            } catch (e) {
                this.$message.error(e);
                console.log(e, wbout)
            } finally {
                let isReadonly = this.tempData.oldReadonly;
                if (!isReadonly) {
                    this.options.isReadonly = false;
                    this.reload();
                }
            }
            return wbout;
        },
    }
}
</script>

<style scoped>
.ap-table-header {
    padding: 5px 10px;
}



.ap-table-header-title {
    font-weight: bold;
}

.ap-table-header-ctn {
    text-align: right;
}

.ap-table-toolbar {
    margin-left: 10px;
}

.ap-table-toolbar /deep/ .el-button--mini {
    padding: 7px;
}

.ap-data-table .el-table-column--selection /deep/ .cell {
    padding: 0px !important;
}

.ap-data-table /deep/ .cell {
    padding: 0px 2px !important;
}

.ap-data-table /deep/ .el-input-number--mini {
    width: 100%;
}

.ap-data-table .ap-table-col-select /deep/ .el-input__inner,
.ap-data-table .ap-table-col-input-number /deep/ .el-input__inner,
.ap-data-table .ap-table-col-input /deep/ .el-input__inner {
    padding: 0px 2px;
}

.ap-table-col-datepicker {
    width: 100%;
}
.ap-data-table .ap-table-col-datepicker /deep/ .el-input__icon {
    width: 12px;
}
.ap-data-table .ap-table-col-datepicker /deep/ .el-input__inner {
    padding-left: 20px;
    padding-right: 0px;
}
.ap-data-table .ap-table-col-input-number /deep/ .el-input__inner {
    text-align: left;
}

.ap-table-export {
    color: #83868d;
    margin-left: 10px;
}
.ap-table-export:hover {
    cursor: pointer;
    color: rgb(64, 158, 255);
}

</style>
