<template>
    <div class="flex flex-direction-column" style="height: 100%;">
        <div class="ap-table-header flex flex-align-center flex-pack-justify">
            <div class="flex-justify-start flex-1">
                <div v-html="opts.titleStr" class="ap-table-header-title"></div>
                <div v-if="opts.code != null && opts.code !=''" class="ap-table-col-config" @click="showTableLayoutEvent">
                    <i class="el-icon-peizhiguanli"/> <span style="font-size: 12px;">{{ opts.myLayout }}</span>
                </div>
            </div>

            <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" :loading="status[btn.loading]"
                                   :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 class="flex-1 ap-table"
                  height="100%"
                  :data="opts.data"
                  :ref="opts.id"
                  :id="opts.elementId"
                  :fit="opts.fit"
                  :border="opts.border"
                  :stripe="opts.stripe"
                  :highlight-current-row="opts.highlightCurrentRow"
                  v-loading="status.tableLoading"

                  :row-key="opts.rowKey"
                  :default-expand-all="opts.defaultExpandAll"
                  :tree-props="{children:opts.rowChildren}"

                  :show-summary="opts.showSummary"
                  :summary-method="opts.summaryMethod"
                  :span-method="opts.spanMethod"
                  :row-class-name="opts.rowClassName"
                  :row-style="opts.rowStyle"
                  :cell-class-name="opts.cellClassName"
                  :cell-style="opts.cellStyle"

                  @selection-change="selectionChangeEvent"
                  @select-all="selectAllEvent"
                  @sort-change="sortChangeEvent"
                  @row-click="opts.rowClickEvent"
                  @row-dblclick="opts.rowDblClickEvent"
                  @cell-click="opts.cellClickEvent"
                  @cell-dblclick="opts.cellDblClickEvent"
                  @header-contextmenu="headerMenuEvent"
        >
            <!-- 列选择checkbox、列编号 -->
            <el-table-column v-if="opts.showSelection" type="selection" width="30" align="center"
                             class="ap-table-selection-col"></el-table-column>
            <!-- 列编号：No. -->
            <el-table-column v-if="opts.showIndex" prop="dataIdx" label="No." :width="opts.indexWidth"
                             header-align="center" :align="opts.indexAlign" :fixed="opts.indexFixed">
                <template slot-scope="scope">{{ scope.row.dataIdx }}</template>
            </el-table-column>
            <template v-for="col in opts.columns">
                <template v-if="!col.hide && col.isShow">
                    <!-- 可编辑列：input、select、slot、cascader、inputNumber -->
                    <!-- 只有一级表头 -->
                    <template v-if="col.children == null || col.children.length <1">
                        <el-table-column :prop="col.prop" :label="col.label"
                                         :width="col.width" :min-width="col.minWidth"
                                         :align="col.align" :header-align="col.headerAlign"
                                         :fixed="col.fixed"
                                         :show-overflow-tooltip="col.showOverflowTooltip"
                                         :sortable="getSortValue(col.sortable)"
                                         :sort-method="(a,b) => sortMethod(a ,b ,col.prop)"
                                         :resizable="col.resizable"
                                         :render-header="col.renderHeader"
                                         :type="col.type == 'expand'? 'expand':''">

                            <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>

                            <div slot-scope="scope">
                                <!-- 1.自定义slot和展开行：只读和编辑都是自定义 -->
                                <ap-table-slot v-if="col.type == 'slot'"
                                               :key="'slot' + scope.row.dataIdx"
                                               :colOptions.sync="col"
                                               :rowData.sync="scope.row">
                                    <template #[col.slot]="colSlotProps">
                                        <slot :name="col.slot" :rowData="colSlotProps.rowData"></slot>
                                    </template>
                                </ap-table-slot>
                                <ap-table-expand v-else-if="col.type == 'expand'" :colOptions.sync="col"
                                                 :rowData.sync="scope.row">
                                    <template #[col.expandSlot]="colExpandProps">
                                        <slot :name="col.expandSlot" :rowData="colExpandProps.rowData"></slot>
                                    </template>
                                </ap-table-expand>

                                <!-- 2.添加/编辑：按照type确定编辑时的组件 -->
                                <template v-else-if="checkRowEdit(scope.row, col)">
                                    <ap-table-input v-if="col.type == 'input'"
                                                    :colOptions.sync="col"
                                                    :rowData.sync="scope.row"></ap-table-input>
                                    <ap-table-date-picker v-if="col.type == 'datePicker'"
                                                          :colOptions.sync="col"
                                                          :rowData.sync="scope.row"></ap-table-date-picker>
                                    <ap-table-search v-else-if="col.type == 'search'"
                                                     :colOptions.sync="col"
                                                     :rowData.sync="scope.row">
                                        <template #[col.searchSlot]="colSearchProps">
                                            <slot :name="col.searchSlot" :rowData="colSearchProps.rowData"></slot>
                                        </template>
                                    </ap-table-search>
                                    <ap-table-search-table v-else-if="col.type == 'searchTable'"
                                                           :colOptions.sync="col"
                                                           :rowData.sync="scope.row">
                                    </ap-table-search-table>
                                    <ap-table-search-tree v-else-if="col.type == 'searchTree'"
                                                          :colOptions.sync="col"
                                                          :rowData.sync="scope.row"
                                                          :colData.sync="opts.colData">
                                    </ap-table-search-tree>
                                    <ap-table-search-picker v-else-if="col.type == 'searchPicker'"
                                                            :colOptions.sync="col"
                                                            :rowData.sync="scope.row"
                                                            :colData.sync="opts.colData">
                                    </ap-table-search-picker>
                                    <ap-table-input-number v-else-if="col.type == 'inputNumber'"
                                                           :colOptions.sync="col"
                                                           :rowData.sync="scope.row"></ap-table-input-number>
                                    <!-- select下拉：attr额外属性 -->
                                    <ap-table-select v-else-if="col.type == 'select'"
                                                     :colOptions.sync="col"
                                                     :rowData.sync="scope.row"
                                                     :attr="opts.attr"></ap-table-select>
                                    <ap-table-cascader v-else-if="col.type == 'cascader'"
                                                       :colOptions.sync="col"
                                                       :rowData.sync="scope.row"></ap-table-cascader>
                                </template>

                                <!-- 3.只读：所有类型通用 -->
                                <template v-else>
                                    <!-- 2.1 html渲染 -->
                                    <template v-if="typeof col.html === 'function'">
                                        <div v-html="col.html(scope.row[col.prop], scope.row)"
                                             v-bind:class="{ 'ap-table-col-tooltip': col.showOverflowTooltip }">>
                                        </div>
                                    </template>

                                    <!-- 2.2 文本显示 -->
                                    <template v-else>
                                        <div v-html="renderText(scope.row,col)"
                                             v-bind:class="{ 'ap-table-col-tooltip': col.showOverflowTooltip }">
                                        </div>
                                    </template>
                                </template>
                            </div>
                        </el-table-column>
                    </template>

                    <!-- 多级表头 -->
                    <template v-else>
                        <el-table-column :label="col.label" :prop="col.prop" :fixed="col.fixed"
                                         header-align="center">
                            <template v-for="child in col.children">
                                <template v-if="!child.hide && child.isShow">
                                    <el-table-column :prop="child.prop" :label="child.label"
                                                     :width="child.width" :min-width="child.minWidth"
                                                     :align="child.align" :header-align="child.headerAlign"
                                                     :fixed="child.fixed"
                                                     :show-overflow-tooltip="col.showOverflowTooltip"
                                                     :sortable="getSortValue(col.sortable)"
                                                     :sort-method="(a,b) => sortMethod(a ,b ,child.prop)"
                                                     :resizable="child.resizable"
                                                     :render-header="col.renderHeader"
                                                     :type="col.type == 'expand'? 'expand':''">
                                        <template slot="header" slot-scope="scope">
                                            {{ child.label }}
                                            <template v-if="child.help != null">
                                                <el-popover trigger="hover">
                                                    <div v-html="child.help"></div>
                                                    <i slot="reference" class="el-icon-wenhao"/>
                                                </el-popover>
                                            </template>
                                        </template>
                                        <div slot-scope="scope">
                                            <!-- 1.自定义slot和展开行：只读和编辑都是自定义 -->
                                            <ap-table-slot v-if="child.type == 'slot'"
                                                           :key="'slot' + scope.row.dataIdx"
                                                           :colOptions.sync="child"
                                                           :rowData.sync="scope.row">
                                                <template #[child.slot]="colSlotProps">
                                                    <slot :name="child.slot" :rowData="colSlotProps.rowData"></slot>
                                                </template>
                                            </ap-table-slot>
                                            <ap-table-expand v-else-if="child.type == 'expand'"
                                                             :colOptions.sync="col"
                                                             :rowData.sync="scope.row">
                                                <template #[child.expandSlot]="colExpandProps">
                                                    <slot :name="child.expandSlot"
                                                          :rowData="colExpandProps.rowData"></slot>
                                                </template>
                                            </ap-table-expand>

                                            <!-- 2.添加/编辑：按照type确定编辑时的组件 -->
                                            <template v-else-if="checkRowEdit(scope.row, child)">
                                                <ap-table-input v-if="child.type == 'input'"
                                                                :colOptions.sync="child"
                                                                :rowData.sync="scope.row"></ap-table-input>
                                                <ap-table-date-picker v-if="col.type == 'datePicker'"
                                                                      :colOptions.sync="col"
                                                                      :rowData.sync="scope.row"></ap-table-date-picker>
                                                <ap-table-search v-else-if="child.type == 'search'"
                                                                 :colOptions.sync="child"
                                                                 :rowData.sync="scope.row">
                                                    <template #[col.searchSlot]="colSearchProps">
                                                        <slot :name="child.searchSlot"
                                                              :rowData="colSearchProps.rowData"></slot>
                                                    </template>
                                                </ap-table-search>
                                                <ap-table-search-table v-else-if="child.type == 'searchTable'"
                                                                       :colOptions.sync="child"
                                                                       :rowData.sync="scope.row">
                                                </ap-table-search-table>
                                                <ap-table-search-tree v-else-if="child.type == 'searchTree'"
                                                                      :colOptions.sync="child"
                                                                      :rowData.sync="scope.row"
                                                                      :colData.sync="opts.colData">
                                                </ap-table-search-tree>
                                                <ap-table-search-picker v-else-if="child.type == 'searchPicker'"
                                                                        :colOptions.sync="child"
                                                                        :rowData.sync="scope.row"
                                                                        :colData.sync="opts.colData">
                                                </ap-table-search-picker>
                                                <ap-table-input-number v-else-if="child.type == 'inputNumber'"
                                                                       :colOptions.sync="child"
                                                                       :rowData.sync="scope.row"></ap-table-input-number>
                                                <!-- select下拉：attr额外属性 -->
                                                <ap-table-select v-else-if="child.type == 'select'"
                                                                 :colOptions.sync="child"
                                                                 :rowData.sync="scope.row"
                                                                 :attr="opts.attr"></ap-table-select>
                                                <ap-table-cascader v-else-if="child.type == 'cascader'"
                                                                   :colOptions.sync="child"
                                                                   :rowData.sync="scope.row"></ap-table-cascader>
                                            </template>

                                            <!-- 3.只读：所有类型通用 -->
                                            <template v-else>
                                                <!-- 2.1 html渲染 -->
                                                <template v-if="typeof child.html === 'function'">
                                                    <div v-html="child.html(scope.row[child.prop], scope.row)"
                                                         v-bind:class="{ 'ap-table-col-tooltip': child.showOverflowTooltip }"></div>
                                                </template>

                                                <!-- 2.2 文本显示 -->
                                                <template v-else>
                                                    <div v-html="renderText(scope.row,child)"
                                                         v-bind:class="{ 'ap-table-col-tooltip': child.showOverflowTooltip }">
                                                    </div>
                                                </template>
                                            </template>
                                        </div>
                                    </el-table-column>
                                </template>
                            </template>
                        </el-table-column>

                    </template>
                </template>
            </template>
        </el-table>
        <div class="flex-justify-start">
            <el-pagination
                v-show="opts.showPagination"
                class="ap-table-pagination"
                @size-change="opts.pagination.sizeChange"
                @current-change="opts.pagination.currentChange"
                :current-page="opts.pagination.currentPage"
                :page-sizes="opts.pagination.pageSizes"
                :page-size="opts.pagination.pageSize"
                :layout="opts.pagination.layout"
                :total="opts.pagination.total">
            </el-pagination>
            <i v-show="opts.showPagination" class="el-icon-excel ap-table-export" @click="exportExcel"/>
        </div>

        <!--弹出框-->
        <div class="ap-comp-search"></div>

        <!--表头右键菜单-->
        <div class="ap-table-header-menu" v-bind:style="headerMenuStyle">
            <div class="ap-table-header-menu-close">
                <i class="el-icon-circle-close" @click="headerMenuCloseEvent"/>
            </div>
            <div class="ap-table-header-menu-li" @click="headerMenuFilterDataEvent">
                <i class="el-icon-tiaojian2"/>&nbsp;筛选(当前分页)
            </div>
            <div class="ap-table-header-menu-line"></div>
            <div class="ap-table-header-menu-li" @click="headerMenuHideColEvent">
                <i class="el-icon-eye-slash"/>&nbsp;隐藏当前列
            </div>
            <div class="ap-table-header-menu-li" @click="headerMenuOperateColEvent">
                <i class="el-icon-check-square-o"/>&nbsp;全部列
            </div>
            <div class="ap-table-header-menu-line"></div>
            <template v-if="headerMenuShowFixed">
                <div class="ap-table-header-menu-li" @click="headerMenuLeftFixedEvent">
                    <i class="el-icon-fixed-left"/>&nbsp;左侧固定列
                </div>
                <div class="ap-table-header-menu-li" @click="headerMenuRightFixedEvent">
                    <i class="el-icon-fixed-right"/>&nbsp;右侧固定列
                </div>
                <div class="ap-table-header-menu-li" @click="headerMenuUnFixedEvent">
                    <i class="el-icon-unlock"/>&nbsp;解除固定
                </div>
            </template>
            <template v-else>
                <div class="ap-table-header-menu-msg">不支持固定列</div>
            </template>
        </div>
        <!--表头右键菜单：全部列-->
        <div class="ap-table-header-menu-col" v-bind:style="headerMenuColStyle">
            <template v-for="col in opts.columns">
                <template v-if="col.children == null || col.children.length <1">
                    <div class="ap-table-header-menu-box">
                        <el-checkbox v-model="col.isShow">{{ col.label }}</el-checkbox>
                    </div>
                </template>
                <template v-else>
                    <template v-for="child in col.children">
                        <div class="ap-table-header-menu-box">
                            <el-checkbox v-model="child.isShow">{{ child.label }}</el-checkbox>
                        </div>
                    </template>
                </template>
            </template>
        </div>
        <!--表头右键菜单：筛选数据-->
        <div class="ap-table-header-menu-col ap-table-header-menu-filter flex-col" v-bind:style="headerMenuFilterDataStyle">
            <div>
                <el-input v-model="filterText" placeholder="搜索，空格分隔多个关键词"/>
            </div>
            <div class="flex-1" style="min-height: 0px;overflow: scroll">
                <el-tree
                    :ref="opts.id + 'HeaderFilterTree'"
                    node-key="id"
                    :data="filterTextList"
                    :props="{children: 'children',label: 'label'}"
                    default-expand-all
                    :show-checkbox="true"
                    :check-on-click-node="true"
                    :indent="1"
                    @check-change="filterRowCheckChange"
                    :filter-node-method="filterRowByCol">
                    <div slot-scope="{ node, data }" class="flex-justify-start" style="max-width: 140px;">
                        <div class="flex-1 ap-ellipse" :title="data.label">{{ data.label }}</div>
                        <div class="ap-table-header-menu-filter-count"> ({{ data.count }})</div>
                    </div>
                </el-tree>
            </div>
            <div style="margin-top: 5px" class="flex-justify-evenly">
                <el-button size="mini" type="info" plain style="padding: 5px;" @click="resetFilterText">重置筛选</el-button>
                <el-button size="mini" type="info" plain style="padding: 5px;" @click="cancelFilterText">取消</el-button>
                <el-button size="mini" type="primary" plain style="padding: 5px;" @click="confirmFilterText">确定
                </el-button>
            </div>
        </div>

        <!-- 表格列布局-->
        <el-dialog title="自定义布局" :visible.sync="tableLayout.visible" width="700px" :append-to-body="true">
            <div class="ap-table-layout-ctn flex-justify-start">
                <div class="ap-table-layout-my flex-col-align-start">
                    <div class="ap-table-layout-header">
                        可选布局
                    </div>
                    <div class="flex-1 ap-table-layout-configed">
                        <el-radio-group v-model="tableLayout.selectedTableLayout" @change="loadTableLayoutBySelected">
                            <template v-for="(item, i) in tableLayout.configed">
                                <el-radio :label="item.fdId"><span v-if="item.fdType ==1">(通用)</span>{{ item.fdName }}</el-radio>
                                <br>
                            </template>
                        </el-radio-group>
                    </div>
                    <div class="ap-table-layout-footer" style="text-align: center;width: 100%;">
                        <el-button icon="el-icon-check" type="primary" @click="selectTableLayout" style="margin-right: 15px;">选择布局</el-button>
                        <el-button icon="el-icon-plus" type="primary" @click="newTableLayout" style="margin-right: 15px;">新建布局</el-button>

                    </div>
                </div>
                <div v-loading="tableLayout.loading" class="ap-table-layout-edit flex-1 flex-col">
                    <div class="flex-1 flex-justify-start" style="min-height: 0px">
                        <div class="ap-table-layout-col flex-1 flex-col-align-start">
                            <div class="ap-table-layout-header">全部列</div>
                            <div class="flex-1" style="width: 100%;overflow: auto">
                                <el-tree :ref="opts.id + 'TableLayoutColTree'" :key="tableLayout.colsKey" :data="tableLayout.cols"
                                         show-checkbox default-expand-all check-on-click-node node-key="id"></el-tree>
                            </div>
                        </div>
                        <div class="flex-col-center" v-show="tableLayout.selectedTableLayout != 'default' && tableLayout.canEdit">
                            <div>
                                <el-button type="primary" @click="addTablaLayoutCol"><i class="el-icon-d-arrow-right"/></el-button>
                            </div>
                            <div style="margin-top: 10px">
                                <el-button type="primary" @click="delTablaLayoutCol"><i class="el-icon-d-arrow-left"/></el-button>
                            </div>
                        </div>
                        <div class="ap-table-layout-col flex-1 flex-col-align-start" v-show="tableLayout.selectedTableLayout != 'default'">
                            <div class="ap-table-layout-header">
                                {{ tableLayout.editHeader }}<span style="font-size: 12px;color: #909399"> (拖拽排序)</span>
                            </div>
                            <div class="flex-1" style="width: 100%;overflow: auto">
                                <el-tree :ref="opts.id + 'TableLayoutEditTree'" :data="tableLayout.editData"
                                         show-checkbox default-expand-all draggable check-on-click-node node-key="id" :allow-drop="tablaLayoutDropEvent">
                                </el-tree>
                            </div>
                        </div>
                    </div>
                    <div style="width: 100%;margin-top: 5px;" v-show="tableLayout.selectedTableLayout != 'default' && tableLayout.canEdit">
                        <div>
                            <span>布局名称:</span>
                            <el-input v-model="tableLayout.editObj.fdName" placeholder="布局名称" style="width: 150px;margin: 0 10px;"></el-input>
                            <el-radio-group v-show="tableLayout.editCommon" v-model="tableLayout.editObj.fdType" style="margin-left: 15px;">
                                <el-radio :label="1" style="margin-right: 20px;">通用布局</el-radio>
                                <el-radio :label="2">自定义</el-radio>
                            </el-radio-group>
                        </div>
                        <div class="ap-table-layout-footer">
                            <el-button-group style="float: right;margin-right: 10px">
                                <el-button icon="el-icon-close" type="danger" @click="delTableLayout(tableLayout.editObj.fdId)">删除</el-button>
                                <el-button icon="el-icon-check" @click="saveTableLayout">保存</el-button>
                            </el-button-group>
                        </div>
                    </div>
                </div>
            </div>
        </el-dialog>
    </div>
</template>

<script>
import schema from "async-validator";
import {formatDate} from 'element-ui/src/utils/date-util';
import {deepClone, handleDefaultParams} from 'ap-util/util/ObjectUtil'
import {getSortListByIndex} from 'ap-util/util/TreeUtil'
import ApTableInput from './column/ApTableInput'
import ApTableDatePicker from './column/ApTableDatePicker'
import ApTableSelect from "./column/ApTableSelect";
import ApTableSearch from "./column/ApTableSearch";
import ApTableSearchTable from "./column/ApTableSearchTable";
import ApTableSearchTree from "./column/ApTableSearchTree";
import ApTableSearchPicker from "./column/ApTableSearchPicker";
import ApTableCascader from "./column/ApTableCascader";
import ApTableSlot from "./column/ApTableSlot";
import ApTableInputNumber from "./column/ApTableInputNumber";
import ApTableExpand from "./column/ApTableExpand";
import {addClass, removeClass, hasClass} from "ap-util/util/StyleUtil";
import FileSaver from 'file-saver';
import XLSX from 'xlsx';
import {hasPermission} from "ap-util/util/LoginUserUtil";

export default {
    name: 'ApTable',
    components: {
        ApTableSlot,
        ApTableSelect,
        ApTableInput,
        ApTableCascader,
        ApTableInputNumber,
        ApTableSearch,
        ApTableSearchTable,
        ApTableSearchTree,
        ApTableSearchPicker,
        ApTableExpand,
        ApTableDatePicker
    },
    data() {
        return {
            opts: {},
            oldGridData: "",                       // 表格：编辑前数据
            status: {
                tableLoading: false,               // 表格：加载状态
                saveBtnLoading: false,             // 保存按钮：加载状态
                refreshBtnLoading: false,          // 刷新按钮：加载状态
                deleteBtnLoading: false,           // 刷新按钮：加载状态
                sortAutoBtnLoading: false,         // 自动排序按钮：加载状态
                sortUpBtnLoading: false,           // 上移按钮：加载状态
                sortDownBtnLoading: false,         // 下移按钮：加载状态
            },
            tempData: {
                triggerSelectionChange: true,      // 避免单选时多次触发selectionChange事件
                validateCheckCount: 0,             // 校验时异步计数
                validateCount: 0,                  // 保存时异步计数
                currentHeaderContextMenuCol: {},   // 当前右键列
                addRowCount: 0,                    // 添加行计数
                deleteDBRows: [],                  // 前端删除行信息
                selectedRows: [],                  // 选中行
            },
            headerMenuStyle: {
                left: 0,
                top: 0,
                display: 'none',
            },
            headerMenuColStyle: {
                left: 0,
                top: 0,
                display: 'none',
            },
            headerMenuFilterDataStyle: {
                left: 0,
                top: 0,
                display: 'none',
            },
            headerMenuShowFixed: true,
            currentMenuTarget: null,
            selectAll: false, // 全选状态
            filterText: "", // 当前列筛选值
            filterTextList: [], // 当前分页筛选列表
            selectedFilterHeaderEle: {}, // 当前选中筛选的表头
            tableLayout: {
                oldColumns: '',         // 表格默认列
                visible: false,         // 自定义布局
                loading: false,         // 编辑加载
                selectedTableLayout: 'default', // 已选择表格布局
                width: "340px",          // dialog宽度
                editHeader: "新建布局",  // 表头名称
                configed: [],            // 已配置布局
                editData: [],            // 编辑布局
                canEdit: false,          // 是否可编辑
                editCommon: hasPermission("AP-TABLE-LAYOUT"), // 通用布局编辑权限
                editObj: {
                    fdId: "",
                    fdCode: "",
                    fdName: "",
                    fdType: 2, // 默认自定义布局
                    fdValue: "",
                },
                colsKey: 0,
                cols: [], // 全部列
            },
        }
    },
    props: {
        options: {
            type: Object,
            required: true
        }
    },
    created() {
        // 初始化组件前修改默认参数
        this.handleDefaultOptions();
    },
    mounted() {
        // 初始化完成是否加载数据
        this.mountedLoadData();
    },
    watch: {
        opts: {
            handler(value) {
                // 深度监听，opts有修改，对应修改父组件options参数
                this.$emit('update:options', this.opts);
            },
            deep: true
        },
        filterText(val) {
            // 列筛选
            this.$refs[this.opts.id + 'HeaderFilterTree'].filter(val);
        }
    },
    methods: {
        // ------------------------------- 1、工具方法 start -------------------------------
        // ---------- 1.1 数据处理类型 start ----------
        // 参数设定：表格默认参数
        handleDefaultOptions() {
            let options = deepClone(this.options);

            // 默认参数：列
            let defaultColOpts = {
                add: true,
                edit: true,
                headerAlign: 'center', // 列表头对齐方式
                align: 'left',         // 列内容对齐方式
                fixed: false,          // 固定列
                hide: false,          // 隐藏列
                isShow: true,          // 表头右键：隐藏列
                // 过滤方法
                filterMethod: (value, row, column) => {
                    const property = column['property'];
                    return row[property] === value;
                }
            };

            let randomVal = parseInt(Math.random() * 10000000);// 随机ID

            // 默认参数：表格
            let defaultOpts = {
                id: "ref" + randomVal,
                elementId: "Table" + randomVal,
                title: "",
                titleStr: "", // title字符串
                myLayout: "", // 我的布局
                fit: true,
                border: true,
                stripe: false,
                highlightCurrentRow: false,
                method: "POST",
                showPagination: true,
                pagination: {
                    currentPage: 1,
                    pageSizes: [30, 60, 90],
                    pageSize: 30,
                    layout: "total, sizes, prev, pager, next, jumper",
                    sizeChange: this.sizeChangeEvent,
                    currentChange: this.pageChangeEvent,
                    orderBy: ""
                },
                colData: [],
                data: [],
                remoteData: '',
                toolbarBtn: [],
                initData: false,
                params: {},
                showIndex: true,
                showSelection: true,
                reserveSelection: true,

                refreshAfterSave: true,
                refreshAfterDelete: false,
                multiSelect: true,
                deletePk: "fdId",
                deleteRemote: true,
                editPk: "fdId",
                rowKey: "fdId",
                sortKey: "fdSort",

                rowChildren: "apTableChildren",
                indexWidth: 40,
                indexAlign: "center",
                indexFixed: false,
                addDefaultObj: {},

                summaryMethod() {
                },
                spanMethod() {
                },
                beforeLoadData() {
                },
                afterLoadData() {
                },
                afterAll() {
                },
                beforeSave() {
                    return true
                },
                afterSave() {
                },
                beforeAdd() {
                    return true
                },
                beforeEdit() {
                    return true
                },
                beforeDelete() {
                    return true
                },
                afterDelete(opts, response) {
                },
                rowDblClickEvent() {
                },
                rowClickEvent() {
                },
                selectionChangeEvent() {
                },
                cellDblClickEvent() {
                },
                cellClickEvent() {
                },
            };

            // 默认参数覆盖：列
            let colSort = 0;
            let columns = options.columns;
            for (let i = 0; i < columns.length; i++) {
                let children = columns[i].children;
                if (children != null && children.length > 0) {
                    // 多级表头列属性：默认显示
                    columns[i].hide = false;
                    columns[i].isShow = true;
                    columns[i].prop = 'propAuto' + i;
                    columns[i].sort = colSort++;
                    // 多级表头
                    for (let j = 0; j < children.length; j++) {
                        for (let optKey in defaultColOpts) {
                            // 替换默认
                            if (children[j][optKey] == null) {
                                children[j][optKey] = defaultColOpts[optKey];
                            }
                        }
                    }
                } else {
                    columns[i].sort = colSort++;
                    // 一级表头
                    for (let optKey in defaultColOpts) {
                        // 替换默认
                        if (columns[i][optKey] == null) {
                            columns[i][optKey] = defaultColOpts[optKey];
                        }
                    }
                }
            }
            // 默认参数覆盖：表格
            for (let optKey in defaultOpts) {
                // 1、分页：属性层级单独处理
                if (optKey == "pagination") {
                    options.pagination = handleDefaultParams(options.pagination, defaultOpts.pagination)

                    // 不显示分页，将pageSize设为0
                    if (!options.showPagination) {
                        // localData时，不分页，显示全部数据
                        if (this.opts.dataUrl == null) {
                            options.pagination.pageSize = 99999;
                        } else {
                            options.pagination.pageSize = 0;
                        }
                    }
                    continue;
                }
                // 2、顶部工具：参数处理
                let customOpt = options[optKey];
                if (optKey == "toolbarBtn") {
                    this.handleToolbarBtn(options);
                    continue;
                }

                if (optKey == "title") {
                    if (typeof customOpt === 'function') {
                        options.titleStr = customOpt();
                    } else {
                        options.titleStr = customOpt;
                    }
                }

                // 3、其他参数：直接替换默认
                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.style = defaultStyle;
                    item.type = tool;
                    switch (tool) {
                        case "add":
                            item.text = "添加";
                            item.icon = "el-icon-plus";
                            item.btnType = "primary";
                            break;
                        case "edit":
                            item.text = "编辑";
                            item.icon = "el-icon-edit-outline";
                            item.btnType = "success";
                            break;
                        case "refresh":
                            item.text = "刷新";
                            item.icon = "el-icon-refresh";
                            item.btnType = "primary";
                            item.loading = "refreshBtnLoading";
                            break;
                        case "del":
                            item.text = "删除";
                            item.icon = "el-icon-close";
                            item.btnType = "danger";
                            item.loading = "deleteBtnLoading";
                            break;
                        case "cancel":
                            item.text = "取消";
                            item.icon = "el-icon-refresh-left";
                            item.btnType = "warning";
                            break;
                        case "save":
                            item.text = "保存";
                            item.icon = "el-icon-check";
                            item.btnType = "";
                            item.loading = "saveBtnLoading";
                            break;
                        case "sortAuto":
                            item.text = "自动排序";
                            item.icon = "el-icon-s-operation";
                            item.btnType = "primary";
                            item.loading = "sortAutoBtnLoading";
                            break;
                        case "sortUp":
                            item.text = "上移";
                            item.icon = "el-icon-top";
                            item.btnType = "primary";
                            item.loading = "sortUpBtnLoading";
                            break;
                        case "sortDown":
                            item.text = "下移";
                            item.icon = "el-icon-bottom";
                            item.btnType = "primary";
                            item.loading = "sortDownBtnLoading";
                            break;
                        default:
                            item = null;
                    }
                    if (item != null) {
                        newToolbarBtns.push(item);
                    }
                }
                // 对象，自定义
                if (typeof (tool) == "object") {
                    tool.style = defaultStyle;
                    // 覆盖默认type
                    switch (tool.type) {
                        case "add":
                            tool.text = tool.text || "添加";
                            tool.icon = tool.icon || "el-icon-plus";
                            tool.btnType = tool.btnType || "primary";
                            break;
                        case "edit":
                            tool.text = tool.text || "编辑";
                            tool.icon = tool.icon || "el-icon-edit-outline";
                            tool.btnType = tool.btnType || "success";
                            break;
                        case "refresh":
                            tool.text = tool.text || "刷新";
                            tool.icon = tool.icon || "el-icon-refresh";
                            tool.btnType = tool.btnType || "primary";
                            break;
                        case "del":
                            tool.text = tool.text || "删除";
                            tool.icon = tool.icon || "el-icon-close";
                            tool.btnType = tool.btnType || "danger";
                            break;
                        case "cancel":
                            tool.text = tool.text || "取消";
                            tool.icon = tool.icon || "el-icon-refresh-left";
                            tool.btnType = tool.btnType || "warning";
                            break;
                        case "save":
                            tool.text = tool.text || "保存";
                            tool.icon = tool.icon || "el-icon-check";
                            tool.btnType = tool.btnType || "";
                            break;
                    }
                    newToolbarBtns.push(tool);
                }
                defaultStyle = tool == "separator" ? "margin-left: 10px;" : "";
            }
            options["toolbarBtn"] = newToolbarBtns;
        },

        // 保存方法：处理add数据。无关字段不传到后台。
        handelAddData(addList) {
            let list = JSON.parse(JSON.stringify(addList));
            for (let i = 0; i < list.length; i++) {
                let obj = list[i];
                for (let key in obj) {
                    if (key == "isAdd" || key == "changeArr") {
                        delete obj[key]
                    }
                }
            }
            return JSON.stringify(list);
        },
        // 保存方法：处理edit数据。未修改的字段不传到后台。editPk除外
        handelEditData(editList) {
            let list = JSON.parse(JSON.stringify(editList));
            let newList = [];
            let childrenArr = this.opts.rowChildren;
            for (let i = 0; i < list.length; i++) {
                let newObj = {};
                let obj = list[i];
                let dataIdx = obj.dataIdx;
                let oldObj = this.getOldDataByDataIdx(dataIdx);
                for (let key in obj) {
                    // 过滤自定义的属性。主键会传到后台
                    if ((obj[key] != oldObj[key] && key != "isEdit" && key != "changeArr" && key != childrenArr) || this.isPkKey(key)) {
                        newObj[key] = obj[key];
                    }
                }
                newList.push(newObj);
            }
            return JSON.stringify(newList);
        },
        // 删除方法：处理delete数据。只传deletePk到后台。
        handelDeleteData(delList) {
            let list = JSON.parse(JSON.stringify(delList));
            let newList = [];
            for (let i = 0; i < list.length; i++) {
                let newObj = {};
                let obj = list[i];
                for (let key in obj) {
                    // deletePk会传到后台
                    if (this.isDeletePkKey(key)) {
                        newObj[key] = obj[key];
                    }
                }
                newList.push(newObj);
            }
            return JSON.stringify(newList);
        },

        // 分页排序
        doSort(data) {
            let orderBy = this.opts.pagination.orderBy;
            // 排序
            if (orderBy != "") {
                let orderByArr = orderBy.split(" ");
                let orderProp = orderByArr[0];
                let orderType = orderByArr[1];
                data.sort((a, b) => {
                    if (a[orderProp] == b[orderProp]) {
                        return 0;
                    }
                    let flag = 0;
                    if (orderType == "asc") {
                        flag = a[orderProp] > b[orderProp]
                    }
                    if (orderType == "desc") {
                        flag = a[orderProp] < b[orderProp];
                    }

                    if (flag) {
                        return 1;
                    } else {
                        return -1;
                    }

                })
            }

            return data;
        },
        // 设定表格初始数据
        setOldGridData() {
            this.$nextTick(() => {
                this.oldGridData = JSON.stringify(this.opts.data);
            });
        },
        // 保存选中行数据
        setSelection() {
            this.tempData.selectedRows = this.getSelectionKeys();
        },
        // 恢复选中行
        reserveSelection() {
            let selectedRows = this.tempData.selectedRows;
            for (let i = 0; i < selectedRows.length; i++) {
                this.toggleRowSelectionByRowKey(selectedRows[i], true);
            }
        },
        // handelGridData：递归 -> 重置表格数据
        resetDataWithTree(data) {
            if (data == null || data.length < 1) {
                return;
            }

            let childrenArr = this.opts.rowChildren; // 默认子节点数组

            for (let i = 0; i < data.length; i++) {
                let row = data[i];
                if (row.isEdit) {
                    row.isEdit = false;
                }
                if (row.isAdd) {
                    row.isAdd = false;
                }
                this.resetDataWithTree(row[childrenArr]);
            }
        },
        // handelGridData：递归 -> 前端删除数据
        removeSelectDataWithTree(data, dataIdx) {
            if (data == null || data.length < 1) {
                return;
            }

            let childrenArr = this.opts.rowChildren; // 默认子节点数组
            // 从最后一个开始删除防止越界
            for (let i = data.length - 1; i >= 0; i--) {
                if (data[i].dataIdx === dataIdx) {
                    data.splice(i, 1);
                    return;
                }
                this.removeSelectDataWithTree(data[i][childrenArr], dataIdx);
            }
        },
        // handelGridData：递归 -> 设定表格dataIdx
        setDataIdxWithTree(data, obj) {
            if (data == null || data.length < 1) {
                return;
            }

            let childrenArr = this.opts.rowChildren; // 默认子节点数组
            for (let i = 0; i < data.length; i++) {
                data[i].dataIdx = ++obj.dataIdx;
                this.setDataIdxWithTree(data[i][childrenArr], obj);
            }
        },

        // handelGridData：后台分页数据(后台分页)
        remotePaging(newData) {
            if (newData == null) {
                return [];
            }
            let dataIdxStart = (this.opts.pagination.currentPage - 1) * this.opts.pagination.pageSize;
            for (let i = 0; i < newData.length; i++) {
                newData[i].isEdit = false;
                newData[i].isAdd = false;
                newData[i].changeArr = [];
                newData[i].dataIdx = ++dataIdxStart;
            }
            return newData;
        },
        // handelGridData：表格数据状态改为显示：isEdit、isAdd为false
        resetData() {
            this.resetDataWithTree(this.opts.data)
        },
        // handelGridData：前端删除数据
        removeSelectData(delRow) {
            if (delRow == null) {
                return;
            }
            for (let i = 0; i < delRow.length; i++) {
                this.removeSelectDataWithTree(this.opts.data, delRow[i].dataIdx)
            }
        },
        // handelGridData：前端删除全部数据
        removeAllData() {
            this.opts.data = [];
            this.opts.pagination.total = 0;
        },
        // handelTreeData：树形数据转换
        handelTreeData(dataParam) {
            let data = JSON.parse(JSON.stringify(dataParam));

            let rowKey = this.opts.rowKey;
            let parentKey = this.opts.rowParentKey;
            let childrenArr = this.opts.rowChildren; // 默认子节点数组
            let newList = []
            for (let i = 0; i < data.length; i++) {
                let parentId = data[i][parentKey];
                // 根节点
                if (!parentId) {
                    newList.push(data[i]);
                } else {
                    let hasParent = false;
                    // 子节点
                    for (let j = 0; j < data.length; j++) {
                        // 查找父节点
                        if (data[j][rowKey] == parentId) {
                            if (!data[j][childrenArr]) {
                                data[j][childrenArr] = []
                            }
                            data[j][childrenArr].push(data[i]);
                            hasParent = true;
                        }
                    }
                    // 存在parentId，但是未找到父节点。都放在根节点
                    if (!hasParent) {
                        newList.push(data[i]);
                    }
                }
            }
            // 设定dataIdx
            this.handelGridData("setDataIdx", {data: newList});
            return newList;
        },
        // handelGridData：设定行得dataIdx
        setDataIdx(data) {
            let dataIdxStart = (this.opts.pagination.currentPage - 1) * this.opts.pagination.pageSize;
            let obj = {
                dataIdx: dataIdxStart
            }
            this.setDataIdxWithTree(data, obj);
        },
        // handelGridData：************** 表格数据处理统一入口 **************
        handelGridData(type, param) {
            // type:操作类型； param:{data:表格数据。addObj:添加对象。delRows:删除行}
            switch (type) {
                // 加载数据：后台分页数据(后台分页)
                case "remotePaging":
                    this.opts.data = this.handelTreeData(this.remotePaging(param.data));
                    break;

                // 加载数据：后台全部数据(前端分页) -> 参数：param.data
                case "remoteAll":
                // 加载数据：前端全部数据(前端分页) -> 参数：param.data
                case "local":
                    this.opts.data = this.handelTreeData(this.getCurrentPageData(param.data));
                    break;

                // 加载数据：数据恢复到编辑前
                case "recoverGridData":
                    // 删除数据列表还原
                    this.tempData.deleteDBRows = []
                    this.opts.data = JSON.parse(this.oldGridData);
                    break;

                // 添加数据：添加行 -> 参数：param.addObj
                case "unshift":
                    this.opts.data.unshift(param.addObj);
                    break;

                // 修改数据：设定dataIdx的值
                case "setDataIdx":
                    this.setDataIdx(param.data);
                    break;

                // 修改数据：表格数据状态改为显示：isEdit、isAdd为false
                case "resetData":
                    this.resetData();
                    break;

                // 删除数据：前端删除
                case "removeSelectData":
                    this.removeSelectData(param.delRows);
                    break;

                // 删除数据：前端删除全部数据
                case "removeAllData":
                    this.removeAllData();
                    break;
            }

        },
        filterRowByCol(value, data) {
            if (!value) return true
            let list = value.split(" ");
            for (let i = 0; i < list.length; i++) {
                if (list[i] == '') {
                    continue;
                }
                if (data.label.indexOf(list[i]) > -1) {
                    return true;
                }
            }
            return false;
        },
        filterRowCheckChange(data, checked) {
            if (data.id == "#all#") {
                let nodes = this.filterTextList;
                for (let i = 0; i < nodes.length; i++) {
                    this.$refs[this.opts.id + 'HeaderFilterTree'].setChecked(nodes[i].id, checked);
                }
            }
        },
        resetFilterText() {
            let filterTextList = this.filterTextList;
            for (let i = 0; i < filterTextList.length; i++) {
                let nodes = filterTextList[i].node;
                for (let j = 0; j < nodes.length; j++) {
                    this.filterRowHide(nodes[j])
                }
            }
            removeClass(this.selectedFilterHeaderEle, "ap-table-header-filter")
            this.headerMenuCloseEvent();
        },
        // 过滤隐藏行
        filterRowHide(node) {
            if (!hasClass(node, "ap-table-ceil-hide")) {
                return;
            }
            removeClass(node, "ap-table-ceil-hide");
            let children = node.parentElement.children;
            for (let i = 0; i < children.length; i++) {
                // 存在其他单元格有隐藏，说明该行仍需要隐藏。直接返回
                if (hasClass(children[i], "ap-table-ceil-hide")) {
                    return;
                }
            }
            removeClass(node.parentElement, "ap-table-row-hide")
        },
        // 过滤显示行
        filterRowShow(node) {
            addClass(node, "ap-table-ceil-hide");
            addClass(node.parentElement, "ap-table-row-hide");
        },
        cancelFilterText() {
            this.headerMenuCloseEvent();
        },
        confirmFilterText() {
            let checkedNodes = this.$refs[this.opts.id + 'HeaderFilterTree'].getCheckedNodes();

            let hasFilter = false; // 判断是否存在过滤
            let filterTextList = this.filterTextList;
            for (let i = 0; i < filterTextList.length; i++) {
                let isChecked = false;
                for (let j = 0; j < checkedNodes.length; j++) {
                    if (checkedNodes[j].label == filterTextList[i].label) {
                        isChecked = true;
                        break
                    }
                }
                let nodes = filterTextList[i].node
                for (let j = 0; j < nodes.length; j++) {
                    if (!isChecked) {
                        this.filterRowShow(nodes[j]);
                        hasFilter = true;
                    } else {
                        this.filterRowHide(nodes[j])
                    }
                }
            }
            if (hasFilter) {
                addClass(this.selectedFilterHeaderEle, "ap-table-header-filter")
            } else {
                removeClass(this.selectedFilterHeaderEle, "ap-table-header-filter")
            }
            this.headerMenuCloseEvent();
        },
        setTitle(html) {
            this.opts.titleStr = html;
        },
        // ---------- 1.1 数据处理类型 end ----------

        // ---------- 1.2 获取数据 start ----------
        // 获取当前分页的数据：参数->全部数据
        getCurrentPageData(dataParam) {
            // 排序
            let data = this.doSort(JSON.parse(JSON.stringify(dataParam)));

            let currentPageData = [];
            let dataIdxStart = (this.opts.pagination.currentPage - 1) * this.opts.pagination.pageSize;
            let start = dataIdxStart;
            let end = start + this.opts.pagination.pageSize - 1;
            if (end > data.length - 1) {
                end = data.length - 1;
            }
            for (let i = start; i <= end; i++) {
                data[i].isEdit = false;
                data[i].isAdd = false;
                data[i].changeArr = [];
                data[i].dataIdx = ++dataIdxStart;
                currentPageData.push(data[i]);
            }
            return currentPageData;
        },
        // 保存、删除参数处理
        getSaveOrDeleteParams() {
            let params = deepClone(this.getOptParams());
            // 保存、删除不需要分页和排序的参数
            delete params.currentPage;
            delete params.pageSize;
            delete params.orderBy;
            return params;
        },
        // 获取表格参数
        getOptParams() {
            let resParams = {};
            let params = this.opts.params;
            if (typeof params === 'function') {
                resParams = params();
            } else {
                resParams = params || {};
            }

            resParams.currentPage = this.opts.pagination.currentPage;
            resParams.pageSize = this.opts.pagination.pageSize;
            resParams.pageSize = this.opts.pagination.pageSize;
            resParams.orderBy = this.opts.pagination.orderBy;
            return resParams;
        },
        // 根据dataIdx获取旧数据对象-> 树递归
        getOldDataByDataIdxWithTree(data, dataIdx) {
            if (data == null || data.length < 1) {
                return null;
            }
            let childrenArr = this.opts.rowChildren;
            for (let i = 0; i < data.length; i++) {
                if (dataIdx == data[i].dataIdx) {
                    return data[i];
                }
                let tempData = this.getOldDataByDataIdxWithTree(data[i][childrenArr], dataIdx);
                if (tempData != null) {
                    return tempData;
                }
            }

        },
        // 根据dataIdx获取旧数据对象
        getOldDataByDataIdx(dataIdx) {
            let oldList = JSON.parse(this.oldGridData);
            return this.getOldDataByDataIdxWithTree(oldList, dataIdx);
        },
        // 根据rowKey获取数据对象-> 树递归
        getRowWithTree(data, rowKeyValue) {
            if (data == null || data.length < 1) {
                return null;
            }
            let childrenArr = this.opts.rowChildren;
            let rowKey = this.opts.rowKey;
            for (let i = 0; i < data.length; i++) {
                if (rowKeyValue == data[i][rowKey]) {
                    return data[i];
                }
                let tempData = this.getRowWithTree(data[i][childrenArr], rowKeyValue);
                if (tempData != null) {
                    return tempData;
                }
            }

        },
        // 根据rowKey获取数据对象
        getRow(rowKeyValue) {
            return this.getRowWithTree(this.opts.data, rowKeyValue);
        },

        // 获得column
        getColByProp(prop) {
            let columns = this.opts.columns;
            for (let i = 0; i < columns.length; i++) {
                let col = columns[i];
                if (col.prop == prop) {
                    return col;
                }
                // 二级表头
                if (col.children == null || col.children.length == 0) {
                    continue;
                }
                for (let j = 0; j < col.children.length; j++) {
                    let childCol = col.children[j];
                    if (childCol.prop == prop) {
                        return childCol;
                    }
                }

            }
            return {};
        },
        // 获得selection列
        getSelectionCol() {
            let columns = this.opts.columns;
            for (let i = 0; i < columns.length; i++) {
                if (columns[i].type == "selection") {
                    return columns[i];
                }
            }
            return {};
        },
        // 获得当前右键column
        getCurrentHeaderMenuCol() {
            let col = this.tempData.currentHeaderContextMenuCol;
            if (col.type == "selection") {
                return this.getSelectionCol();
            }
            return this.getColByProp(col.prop);
        },
        // 右键column：多级表头只有一个子节点时，父表头一并隐藏
        hideCurrentHeaderMenuParentCol() {
            let prop = this.tempData.currentHeaderContextMenuCol.prop;

            let col = {};

            let columns = this.opts.columns;
            for (let i = 0; i < columns.length; i++) {
                col = columns[i];
                // 二级表头
                if (col.children == null || col.children.length == 0) {
                    continue;
                }
                let showCount = 0;
                let isCol = false;
                for (let j = 0; j < col.children.length; j++) {

                    let childCol = col.children[j];
                    if (childCol.isShow) {
                        showCount++;
                    }
                    if (childCol.prop == prop) {
                        isCol = true;
                    }
                }

                if (isCol && showCount <= 1) {
                    col.isShow = false;
                }
            }


        },
        // 验证rules规则拼接
        getValidateRules() {
            let rules = {};
            let columns = this.opts.columns;
            for (let i = 0; i < columns.length; i++) {
                let col = columns[i];
                if (col.rules) {
                    rules[col.prop] = col.rules;
                }
            }
            return rules;
        },
        // 获取添加行的临时唯一id
        getAddRowId() {
            return "+" + (++this.tempData.addRowCount);
        },
        // 递归：获取编辑行：树结构
        getEditRowListWithTree(list, data) {
            if (data == null || data.length < 1) {
                return;
            }

            let childrenArr = this.opts.rowChildren; // 默认子节点数组
            for (let i = 0; i < data.length; i++) {
                let row = data[i];
                if (row.isEdit && row.changeArr.length > 0) {
                    // 只添加有修改的数据
                    list.push(row)
                }
                this.getEditRowListWithTree(list, row[childrenArr]);
            }
        },
        // 获得树节点的数组
        getDataItems(item) {
            let arr = [];
            arr.push(item);
            let childs = item[this.opts.rowChildren];
            if (childs == null || childs.length < 1) {
                return arr;
            }
            for (let i = 0; i < childs.length; i++) {
                arr = arr.concat(this.getDataItems(childs[i]));
            }
            return arr;
        },
        // 获取唯一选中值
        getSelectOne() {
            let selection = this.getSelection();
            if (selection.length == 0) {
                this.$message.error(`请选择需要操作的行！`);
                return null;
            } else if (selection.length > 1) {
                this.$message.error(`只能选择一行！`);
                return null;
            }
            return selection[0];
        },
        // 获取行的下一行：递归方法
        getNextRowItem(rowData, list, idKey, childrenKey, sortKey) {
            if (list == null || list.length < 1) {
                return null;
            }
            for (let i = 0; i < list.length; i++) {
                if (list[i][idKey] == rowData[idKey]) {
                    if (i == list.length - 1) {
                        return null;
                    } else {
                        return list[i + 1];
                    }
                }

                let nextRow = this.getNextRowItem(rowData, list[i][childrenKey], idKey, childrenKey, sortKey)
                if (nextRow != null) {
                    return nextRow;
                }
            }
        },
        // 获取行的下一行：树形时->同一层级的下一行
        getNextRow(rowData) {
            if (rowData == null) {
                return null;
            }
            return this.getNextRowItem(rowData, this.getAllData(), this.opts.rowKey, this.opts.rowChildren, this.opts.sortKey)
        },
        // 获取行的上一行：递归方法
        getPreviousRowItem(rowData, list, idKey, childrenKey, sortKey) {
            if (list == null || list.length < 1) {
                return null;
            }
            for (let i = 0; i < list.length; i++) {
                if (list[i][idKey] == rowData[idKey]) {
                    if (i == 0) {
                        return null;
                    } else {
                        return list[i - 1];
                    }
                }

                let nextRow = this.getPreviousRowItem(rowData, list[i][childrenKey], idKey, childrenKey, sortKey)
                if (nextRow != null) {
                    return nextRow;
                }
            }
        },
        // 获取行的上一行：树形时->同一层级的上一行
        getPreviousRow(rowData) {
            if (rowData == null) {
                return null;
            }
            return this.getPreviousRowItem(rowData, this.getAllData(), this.opts.rowKey, this.opts.rowChildren, this.opts.sortKey)
        },
        // ---------- 1.2 获取数据 end ----------

        // ---------- 1.3 数据判断 start ----------
        // 判断str是否为编辑的主键
        isPkKey(str) {
            let pk = this.opts.editPk;
            if (pk == null) {
                return false;
            }
            let split = pk.split(",");
            return split.indexOf(str) >= 0 ? true : false;
        },
        // 判断str是否为删除的主键
        isDeletePkKey(str) {
            let pk = this.opts.deletePk;
            if (pk == null) {
                return false;
            }
            let split = pk.split(",");
            return split.indexOf(str) >= 0 ? true : false;
        },
        // 编辑或添加时：判断是否可以编辑或修改
        checkRowEdit(rowData, col) {
            if (rowData.isEdit == true) {
                if (typeof col.edit === 'function' && col.edit(rowData)) {
                    return true;
                } else if (typeof col.edit === 'boolean' && col.edit) {
                    return true;
                }
            }

            if (rowData.isAdd == true) {
                if (typeof col.add === 'function' && col.add()) {
                    return true;
                } else if (typeof col.add === 'boolean' && col.add) {
                    return true;
                }
            }
            return false;

        },
        // ---------- 1.3 数据判断 end ----------

        // ---------- 1.4 页面状态 start ----------
        // 保存后：显示提示信息
        showSaveMsg(data) {
            let msg = "保存成功！";
            let type = "success";
            if (data == null) {
                this.$message({
                    message: msg,
                    type: type
                });
                return;
            }

            let insertCount = data.insertCount;
            let updateCount = data.updateCount;
            let hasInsert = insertCount > 0;
            let hasUpdate = updateCount > 0;

            if (hasInsert && !hasUpdate) {
                msg = `保存成功！添加${insertCount}条数据。`;
            } else if (!hasInsert && hasUpdate) {
                msg = `保存成功！更新${updateCount}条数据。`;
            } else if (hasInsert && hasUpdate) {
                msg = `保存成功！添加${insertCount}条数据。更新${updateCount}条数据。`;
            }

            this.$message({
                message: msg,
                type: type
            });
        },
        // 删除后：显示提示信息
        showDeleteMsg(data) {
            let count = data.deleteCount;
            // 后台未返回数字，默认成功
            if (typeof count != "number") {
                this.$message.success("删除成功！")
                this.handelGridData("removeSelectData", {delRows: this.tempData.deleteDBRows});
                return;
            }
            // 后台返回数字
            if (typeof count == "number" && count > 0) {
                this.$message.success("删除成功！已删除" + count + "条数据！")
                this.handelGridData("removeSelectData", {delRows: this.tempData.deleteDBRows});
                return;
            }

            this.$message.error("删除失败！未找到匹配的数据！")
        },
        // 组件状态修改：type-> 类型：save、refresh等;  isLoading-> true：加载中，false：已完成
        changeLoadingStatus(type, isLoading) {
            switch (type) {
                // 初始化：mount、分页
                case "init":
                    this.status.tableLoading = isLoading;
                    break;
                // 保存
                case "save":
                    this.status.tableLoading = isLoading;
                    this.status.saveBtnLoading = isLoading;
                    break;
                // 自动排序
                case "sortAuto":
                    this.status.tableLoading = isLoading;
                    this.status.sortAutoBtnLoading = isLoading;
                    break;
                // 上移
                case "sortUp":
                    this.status.tableLoading = isLoading;
                    this.status.sortUpBtnLoading = isLoading;
                    break;
                // 下移
                case "sortDown":
                    this.status.tableLoading = isLoading;
                    this.status.sortDownBtnLoading = isLoading;
                    break;
                // 刷新
                case "refresh":
                    this.status.tableLoading = isLoading;
                    this.status.refreshBtnLoading = isLoading;
                    break;
                // 删除
                case "delete":
                    // this.status.tableLoading = isLoading;
                    this.status.deleteBtnLoading = isLoading;
                    break;
            }

        },
        // 获取验证错误信息
        getValidateMsg(rowData, fields) {
            let msg = [];
            for (let key in fields) {
                let field = fields[key];
                for (let j = 0; j < field.length; j++) {
                    msg.push("\"" + this.getColByProp(field[j].field).label + "\"列，" + field[j].message);
                }
            }
            return `第[${rowData.dataIdx}]行。${msg.join("；")}。`;
        },
        // ---------- 1.4 页面状态 end ----------

        // ---------- 1.5 列相关处理 start ----------
        // 获取所有列的远程数据：同步方法
        async loadColData() {
            // 表格设为加载状态
            this.changeLoadingStatus("init", true);

            let cols = this.opts.columns;
            for (let i = 0; i < cols.length; i++) {
                switch (cols[i].type) {
                    case "searchTree":
                        await this.loadColDataSearchTree(cols[i]);
                        break;
                    case "searchPicker":
                        await this.loadColDataSearchPicker(cols[i]);
                        break;
                }
            }
            // 表格设为加载状态
            this.changeLoadingStatus("init", false);
        },

        // searchTree：数据预加载
        loadColDataSearchTree(col) {
            return new Promise((resolve) => {
                // 1、是否初始加载
                let loadTime = col.searchTreeLoadTime || "init"; // 默认init
                if (loadTime != "init") {
                    resolve();
                    return;
                }
                // 2、自定义请求参数
                let params = {};
                let searchTreeParams = col.searchTreeParams;
                if (typeof searchTreeParams === 'function') {
                    params = searchTreeParams(null, col);
                } else if (searchTreeParams != null) {
                    params = searchTreeParams;
                }
                // 3、请求数据
                this.$request({
                    url: col.searchTreeUrl,
                    method: col.searchTreeMethod || 'post',
                    data: params
                }).then(response => {
                    this.opts.colData[col.prop] = response.data;
                }).finally(() => {
                    resolve();
                })
            });
        },
        // searchPicker：数据预加载
        loadColDataSearchPicker(col) {
            return new Promise((resolve) => {
                // 1、是否初始加载
                let loadTime = col.searchPickerLoadTime || "init"; // 默认init
                if (loadTime != "init") {
                    resolve();
                    return;
                }
                // 2、自定义请求参数
                let params = {};
                let searchPickerParams = col.searchPickerParams;
                if (typeof searchPickerParams === 'function') {
                    params = searchPickerParams(null, col);
                } else if (searchPickerParams != null) {
                    params = searchPickerParams;
                }
                this.$request({
                    url: col.searchPickerUrl,
                    method: col.searchPickerMethod || 'post',
                    data: params
                }).then(response => {
                    this.opts.colData[col.prop] = response.data;
                }).finally(() => {
                    resolve();
                })
            });
        },

        // select自定义渲染
        renderSelectHtml(value, text, rowData, col) {
            if (typeof col.selectHtml === 'function') {
                return col.selectHtml(value, text, rowData);
            }
            return text;
        },
        // options文本渲染
        renderTextByOptions(rowData, col) {
            // 默认renderOptions，否则取renderOptionsRemote
            let options = col.renderOptions || this.opts.attr[col.renderOptionsRemote];
            let label = col.renderKey.label;
            let value = col.renderKey.value;
            let key = col.renderKey.key;
            for (let i = 0; i < options.length; i++) {
                if (options[i][value] == rowData[key]) {
                    rowData[col.prop] = options[i][label];
                    if (typeof col.renderHtml === 'function') {
                        return col.renderHtml(rowData[key], options[i][label], rowData);
                    } else {
                        return options[i][label];
                    }
                }
            }

            // 未找到匹配项，返回原值
            return rowData[col.prop];
        },
        getSelectLabel(options, rowValue, isSelectMultiple, valueStr, labelStr) {
            if (rowValue == null || rowValue == undefined) {
                return "";
            }
            // 多选
            if (isSelectMultiple) {
                let textStr = "";
                for (let i = 0; i < options.length; i++) {
                    for (let m = 0; m < rowValue.length; m++) {
                        let opt = options[i];
                        if (opt[valueStr] == rowValue[m]) {
                            textStr += opt[labelStr] + ",";
                        }
                    }
                }
                if (textStr != "") {
                    textStr = textStr.substring(0, textStr.length - 1)
                }
                return textStr;
            }
            // 单选
            for (let i = 0; i < options.length; i++) {
                let opt = options[i];
                if (opt[valueStr] == rowValue) {
                    return opt[labelStr];
                }
            }
        },
        // select文本渲染
        renderSelectText(rowData, col) {
            // 默认selectOptions，否则取selectOptionsRemote
            let options = col.selectOptions || this.opts.attr[col.selectOptionsRemote];
            let selectOptionsKey = col.selectOptionsKey || {label: "label", value: "value"}
            let valueStr = selectOptionsKey.value;
            let labelStr = selectOptionsKey.label;
            let rowValue = rowData[col.prop];
            // 分组
            if (col.selectIsGroup) {
                let newOpts = [];
                for (let i = 0; i < options.length; i++) {
                    let opts = options[i];
                    let childOpts = opts.options;
                    if (childOpts != null && childOpts.length > 0) {
                        newOpts = newOpts.concat(childOpts);
                    } else {
                        newOpts.push(opts)
                    }
                }
                let textStr = this.getSelectLabel(newOpts, rowValue, col.selectMultiple, valueStr, labelStr);
                return this.renderSelectHtml(rowValue, textStr, rowData, col);

            }

            // 非分组
            let labelText = this.getSelectLabel(options, rowValue, col.selectMultiple, valueStr, labelStr);
            return this.renderSelectHtml(rowValue, labelText, rowData, col);
        },
        // datePicker文本渲染
        renderDatePickerText(rowData, col) {
            let value = rowData[col.prop]
            if (col.datePickerValueFormat === 'timestamp') {
                value = new Date(value)
            }
            return formatDate(value, col.datePickerFormat);
        },
        // searchTree文本渲染
        renderSearchTreeText(rowData, col) {
            // 默认不渲染
            let renderKey = col.searchTreeRenderKey;
            if (renderKey == null || renderKey == "") {
                return rowData[col.prop];
            }

            // 文本渲染
            let colData = this.opts.colData[col.prop];
            if (colData == null || colData.length < 1) {
                return rowData[col.prop];
            }
            // key默认值设定：同组件设置
            let treeKey = col.searchTreeKey;
            let idKey = treeKey == null || treeKey.idKey ? "fdId" : treeKey.idKey;
            let labelKey = treeKey == null || treeKey.labelKey ? "fdName" : treeKey.labelKey;
            let renderIdKey = renderKey.idKey;
            let renderLabelKey = renderKey.labelKey == null ? labelKey : renderKey.labelKey;
            for (let i = 0; i < colData.length; i++) {
                if (rowData[renderIdKey] == colData[i][idKey]) {
                    // 赋值到row
                    rowData[col.prop] = colData[i][renderLabelKey];
                    return colData[i][renderLabelKey];
                }
            }
        },
        // 列只读渲染
        renderText(rowData, col) {
            // 存在renderKey使用自定义options渲染
            if (col.renderKey != null) {
                return this.renderTextByOptions(rowData, col);
            }

            switch (col.type) {
                // select渲染
                case "select":
                    return this.renderSelectText(rowData, col);
                // datePicker渲染
                case "datePicker":
                    return this.renderDatePickerText(rowData, col);
                // datePicker渲染
                case "searchTree":
                    return this.renderSearchTreeText(rowData, col);
                default:
                    return rowData[col.prop];
            }
        },
        // 获取排序值
        getSortValue(sortParam) {
            if (sortParam == "local") {
                // 本地排序
                return true;
            }
            if (sortParam == true) {
                return 'custom';
            }
            return false;
        },
        // ---------- 1.5 列相关处理 end ----------
        // ------------------------------- 1、工具方法 end -------------------------------


        // ------------------------------- 2、通用方法：开放前端接口 start -------------------------------
        // 分页跳转
        pageJump(type) {
            // 数据加载前处理
            this.beforeLoadData(type);

            // 加载数据：本地或服务端
            if (this.opts.dataUrl == null) {
                this.loadLocalData(type, this.opts.localData);
            } else if (this.opts.remoteData.length > 0) {
                this.loadLocalData(type, JSON.parse(this.opts.remoteData));
            } else {
                this.loadRemoteData(type);
            }
        },
        // 加载数据：入口
        loadData(type) {
            // 数据加载前处理
            this.beforeLoadData(type);

            // 设定选中行数据
            this.setSelection();

            // 加载数据：本地或服务端
            if (this.opts.dataUrl == null) {
                this.loadLocalData(type, this.opts.localData);
            } else {
                this.loadRemoteData(type);
            }
            // 删除数据列表还原
            this.tempData.deleteDBRows = []
        },
        // 加载数据：加载远程数据
        loadRemoteData(type) {
            // 加载远程数据
            this.$request({
                url: this.opts.dataUrl,
                method: this.opts.method,
                data: this.getOptParams(),
            }).then(response => {
                let dataList = response.data.list;

                if (response.data.total == 0 && dataList.length > 0) {
                    // 前端分页
                    this.handelGridData("remoteAll", {data: dataList});
                    this.opts.remoteData = JSON.stringify(dataList); // 分页跳转使用：不再请求数据 -> 直接赋值对象，双向绑定会失效
                    this.opts.pagination.total = dataList.length;
                } else {
                    // 后台分页
                    this.handelGridData("remotePaging", {data: dataList});
                    this.opts.pagination.total = response.data.total;
                }

                this.opts.attr = response.data.attr;
                this.afterLoadData(type);
            }).finally(() => {
                // 数据加载完成回调
                this.changeLoadingStatus(type, false);
                this.afterAll();
            })
        },
        // 加载数据：本地数据
        loadLocalData(type, data) {
            // 获取当前页数据
            this.handelGridData("local", {data: data});
            this.opts.pagination.total = data.length;

            // 数据加载完成回调
            this.afterLoadData(type);
            this.changeLoadingStatus(type, false);
            this.afterAll();
        },
        // 初始化完成判断是否加载数据
        async mountedLoadData() {
            await this.loadDefaultLayout();

            // 加载所有列的远程数据：同步处理
            await this.loadColData();
            // 加载表格数据
            if (this.opts.dataUrl == null || this.opts.initData) {
                this.loadData("init");
            } else {
                // 表格设为加载完成状态：处理列的预加载状态
                this.changeLoadingStatus("init", false);
            }
        },
        // 表格数据是否修改：true有修改；false未修改
        hasChange() {
            return JSON.stringify(this.opts.data) != this.oldGridData;
        },
        // 获取数据：获得添加行
        getAddRowList() {
            let dataList = this.opts.data;
            let addList = [];
            for (let i = 0; i < dataList.length; i++) {
                let row = dataList[i];
                if (row.isAdd) {
                    addList.push(row)
                }
            }
            return addList;
        },
        // 获取数据：获得编辑行
        getEditRowList() {
            let editList = [];
            this.getEditRowListWithTree(editList, this.opts.data)
            return editList;
        },
        // 获取数据：获得删除行
        getDeleteRowList() {
            return this.tempData.deleteDBRows;
        },
        // 添加行
        addRow() {
            // 添加前回调
            if (!this.beforeAdd()) {
                return;
            }

            let columns = this.opts.columns;
            let newObj = {
                changeArr: []
            };
            // 添加列默认值设定
            for (let i = 0; i < columns.length; i++) {
                if (columns[i].type == "selection" || columns[i].type == "index") {
                    continue;
                }
                let addDefaultValue = columns[i].addDefaultValue;
                if (addDefaultValue != undefined) {
                    newObj[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) {
                    newObj[key] = tempAddDefaultObj[key];
                }
            }

            newObj.isAdd = true;
            newObj.dataIdx = this.getAddRowId();
            this.handelGridData("unshift", {addObj: newObj});
        },
        // 编辑行
        editRow() {
            let selects = this.getSelection();
            if (selects == null || selects.length == 0) {
                this.$message({
                    showClose: true,
                    message: '请选择需要编辑的数据！',
                    type: 'error'
                });
                return
            }
            // 删除前回调
            if (!this.beforeEdit(selects)) {
                return;
            }
            // 行数据：修改为编辑状态
            for (let i = 0; i < selects.length; i++) {
                // 已编辑的无需修改
                if (!selects[i].isEdit) {
                    selects[i].isEdit = true;
                    selects[i].changeArr = [];
                }
            }
        },
        // 删除行：已选择的行 入口
        doDelRow() {
            this.changeLoadingStatus("delete", true);
            let selectRows = this.getSelection();
            if (selectRows == null || selectRows.length == 0) {
                this.$message({
                    showClose: true,
                    message: '请选择需要删除的数据！',
                    type: 'error'
                });
                this.endDelRow();
                return;
            }


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

            this.$confirm('确定删除已选择数据?', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                this.delRow(selectRows);
            }).catch(() => {
                this.$message({
                    type: 'info',
                    message: '已取消删除'
                });
                this.endDelRow();
            });

        },
        // 删除行：实现
        delRow(deleteRows) {
            // 后台删除数据
            let deleteDBRows = [];
            // 前台删除数据：添加未保存的数据或者前端localData数据
            let deleteAddRows = [];
            for (let i = 0; i < deleteRows.length; i++) {
                let row = deleteRows[i];
                if (row["isAdd"] || this.opts.dataUrl == null) {
                    deleteAddRows.push(row);
                } else {
                    deleteDBRows.push(row);
                }
            }
            // 新添加行isAdd：前端直接删除
            this.handelGridData("removeSelectData", {delRows: deleteAddRows});

            // 保存时删除数据
            if (!this.opts.deleteRemote) {
                this.tempData.deleteDBRows = this.tempData.deleteDBRows.concat(deleteDBRows);
                // 删除前端显示数据
                this.handelGridData("removeSelectData", {delRows: deleteRows});
                this.endDelRow();
                return;
            }

            // 后台数据实时删除数据
            this.tempData.deleteDBRows = deleteDBRows;
            if (deleteDBRows == null || deleteDBRows.length == 0) {
                this.endDelRow();
                return;
            }

            // 参数处理
            let params = this.getSaveOrDeleteParams();
            params.deleteList = this.handelDeleteData(deleteDBRows);
            // true：直接后台删除   false：前端删除，点击保存时后台处理删除
            this.$request({
                url: this.opts.deleteUrl,
                method: this.opts.method,
                data: params
            }).then(response => {
                // 提示删除条数
                this.showDeleteMsg(response.data);
                // 旧数据保存
                this.setOldGridData();
                // 删除回调
                this.afterDelete(response);
            }).finally(() => {
                this.endDelRow();
            })

        },
        // 删除行：完成
        endDelRow() {
            this.changeLoadingStatus("delete", false);
        },
        validate(callbackFn) {
            let addList = this.getAddRowList();
            let editList = this.getEditRowList();
            // 为空验证：直接返回true
            if (addList.length == 0 && editList.length == 0) {
                return true;
            }
            // rule验证
            let validator = new schema(this.getValidateRules());
            let allData = addList.concat(editList);
            this.tempData.validateCheckCount = allData.length;
            for (let i = 0; i < allData.length; i++) {
                validator.validate(allData[i], (errors, fields) => {
                    if (fields) {
                        callbackFn(this.getValidateMsg(allData[i], fields));
                    } else {
                        this.tempData.validateCheckCount--;
                        // 异步校验全部完成再保存
                        if (this.tempData.validateCheckCount == 0) {
                            callbackFn();
                        }
                    }
                });
            }
        },
        // 保存方法：数据验证
        doSave() {
            let addList = this.getAddRowList();
            let editList = this.getEditRowList();
            let deleteList = this.getDeleteRowList();
            // 1、为空验证
            if (addList.length == 0 && editList.length == 0 && deleteList.length == 0) {
                this.$message.info('没有需要保存的数据！');
                this.changeLoadingStatus("save", false);
                return;
            }

            // 只有删除数据，没有添加或编辑数据，无需校验Rule
            if (addList.length == 0 && editList.length == 0) {
                this.save();
                return;
            }

            // 2、rule验证
            let validator = new schema(this.getValidateRules());
            let allData = addList.concat(editList);
            this.tempData.validateCount = allData.length;
            for (let i = 0; i < allData.length; i++) {
                validator.validate(allData[i], (errors, fields) => {
                    if (fields) {
                        let msg = this.getValidateMsg(allData[i], fields);
                        this.$message({
                            dangerouslyUseHTMLString: true,
                            type: 'error',
                            message: "保存失败，数据校验错误！<br>" + msg
                        });
                    } else {
                        this.tempData.validateCount--;
                        // 异步校验全部完成再保存
                        if (this.tempData.validateCount == 0) {
                            this.save();
                        }
                    }
                });
            }


        },
        // 保存方法：实现
        save() {
            // ------- 回调：保存前 start -------
            let beforeSaveF = this.beforeSave();
            if (!beforeSaveF) {
                this.changeLoadingStatus("save", false);
                return;
            }
            // ------- 回调：保存前 end -------

            // ------- 保存数据 start -------
            let params = this.getSaveOrDeleteParams();
            params.addList = this.handelAddData(this.getAddRowList());
            params.editList = this.handelEditData(this.getEditRowList());
            params.deleteList = this.handelDeleteData(this.getDeleteRowList());

            this.$request({
                url: this.opts.saveUrl,
                method: this.opts.method,
                data: params,
            }).then(response => {
                // 请求成功回调
                this.afterSave(response);
            }).finally(() => {
                this.changeLoadingStatus("save", false);
            })
            // ------- 保存数据 end -------
        },
        // 刷新
        refresh(keepCurrentPage) {
            // 保持当前page
            this.opts.pagination.currentPage = keepCurrentPage ? this.opts.pagination.currentPage : 1;
            this.loadData("refresh");
            this.refreshTitle();
        },
        // 刷新表格标题
        refreshTitle() {
            if (typeof this.opts.title === 'function') {
                this.opts.titleStr = this.opts.title();
            } else {
                this.opts.titleStr = this.opts.title;
            }
        },
        // 清空
        clear() {
            this.handelGridData("removeAllData");
        },
        // 清空选中
        clearSelected() {
            let selectedRows = this.getSelection();
            // 倒序：selectedRows长度会跟随选中状态变化
            for (let i = (selectedRows.length - 1); i >= 0; i--) {
                this.$refs[this.opts.id].toggleRowSelection(selectedRows[i], false);
            }
        },
        // 清空当前行选中式样
        clearHighlight() {
            let currentRow = this.$refs[this.opts.id].$el.getElementsByClassName('current-row');
            if (currentRow == null || currentRow.length < 1) {
                return;
            }
            removeClass(currentRow[0], 'current-row')
        },
        // 取消
        cancel() {
            if (!this.hasChange()) {
                this.$message({
                    type: 'info',
                    message: '没有编辑的数据!'
                });
                return;
            }

            this.$confirm('确定取消编辑？', '提示', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                // 恢复数据
                this.handelGridData("recoverGridData");
                this.$message({
                    type: 'success',
                    message: '编辑内容已取消!'
                });
            }).catch(() => {
                this.$message({
                    type: 'info',
                    message: '操作已取消'
                });
            });

        },
        // 自动排序
        sortAuto() {
            this.changeLoadingStatus("sortAuto", true);
            let list = getSortListByIndex(this.getAllData(), this.opts.rowKey, this.opts.rowChildren, this.opts.sortKey);
            this.$request({
                url: this.opts.sortUrl,
                method: this.opts.method,
                data: {list: JSON.stringify(list)}
            }).then(response => {
                this.$message.success(`排序成功！`);
                this.refresh();
            }).finally(() => {
                this.changeLoadingStatus("sortAuto", false);
            })
        },
        // 上移
        sortUp() {
            let rowData = this.getSelectOne();
            if (rowData == null) {
                return null;
            }
            let previousRow = this.getPreviousRow(rowData);
            if (previousRow == null) {
                this.$message.warning(`向上移动失败！`);
                return;
            }
            let rowKey = this.opts.rowKey;
            let sortKey = this.opts.sortKey;
            let list = [];
            let selectedRow = {};
            selectedRow[rowKey] = rowData[rowKey];
            selectedRow[sortKey] = previousRow[sortKey];
            list.push(selectedRow);
            let changeRow = {};
            changeRow[rowKey] = previousRow[rowKey];
            changeRow[sortKey] = rowData[sortKey];
            list.push(changeRow);
            this.changeLoadingStatus("sortUp", true);
            this.$request({
                url: this.opts.sortUrl,
                method: this.opts.method,
                data: {list: JSON.stringify(list)}
            }).then(response => {
                this.$message.success(`排序成功！`);
                this.refresh();
            }).finally(() => {
                this.changeLoadingStatus("sortUp", false);
            });
        },
        // 下移
        sortDown() {
            let rowData = this.getSelectOne();
            if (rowData == null) {
                return null;
            }
            let nextRow = this.getNextRow(rowData);
            if (nextRow == null) {
                this.$message.warning(`向下移动失败！`);
                return;
            }
            let rowKey = this.opts.rowKey;
            let sortKey = this.opts.sortKey;
            let list = [];
            let selectedRow = {};
            selectedRow[rowKey] = rowData[rowKey];
            selectedRow[sortKey] = nextRow[sortKey];
            list.push(selectedRow);
            let changeRow = {};
            changeRow[rowKey] = nextRow[rowKey];
            changeRow[sortKey] = rowData[sortKey];
            list.push(changeRow);
            this.changeLoadingStatus("sortDown", true);
            this.$request({
                url: this.opts.sortUrl,
                method: this.opts.method,
                data: {list: JSON.stringify(list)}
            }).then(response => {
                this.$message.success(`排序成功！`);
                this.refresh();
            }).finally(() => {
                this.changeLoadingStatus("sortDown", false);
            });
        },
        // 导出表格数据
        exportExcel() {
            this.$confirm(`是否将当前分页数据，导出excle？`, '提示', {
                confirmButtonText: '导出',
                cancelButtonText: '取消',
                type: 'warning'
            }).then(() => {
                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)
            }
            return wbout;
        },
        // 重载表格：重新渲染
        reload(loadFlag) {
            // 初始化组件前修改默认参数
            this.handleDefaultOptions();
            if (loadFlag == undefined || loadFlag) {
                this.mountedLoadData();
            } else {
                // 加载所有列的远程数据：同步处理
                this.loadColData();
            }
        },

        // 获取选中行数据
        getSelection() {
            let selectRows = this.$refs[this.opts.id].selection;
            return selectRows;
        },
        // 获取选中行数据
        getSelectionKeys() {
            let selectRows = this.$refs[this.opts.id].selection;
            let keys = [];
            for (let i = 0; i < selectRows.length; i++) {
                keys.push(selectRows[i][this.opts.rowKey])
            }
            return keys;
        },
        // 获取当前页数据
        getData() {
            return this.opts.data;
        },
        // 获取当前页数据：如果是树自动转为数组形式
        getDataWithArray() {
            let data = JSON.parse(JSON.stringify(this.opts.data));
            let res = [];
            for (let i = 0; i < data.length; i++) {
                res = res.concat(this.getDataItems(data[i]));
            }
            return res;
        },
        // 获取所有数据：前端分页
        getAllData() {
            if (this.opts.dataUrl == null) {
                return this.opts.localData;
            } else if (this.opts.remoteData.length > 0) {
                return this.opts.remoteData;
            } else {
                return this.opts.data
            }
        },
        // 获取编辑前全部数据
        getOldAllData() {
            return JSON.parse(this.oldGridData);
        },
        // 获取保存数据：json对象
        getSaveData() {
            let params = {};
            params.addList = JSON.parse(this.handelAddData(this.getAddRowList()));
            params.editList = JSON.parse(this.handelEditData(this.getEditRowList()));
            if (!this.opts.deleteRemote) {
                params.deleteList = JSON.parse(this.handelDeleteData(this.getDeleteRowList()));
            }
            return params;
        },
        // 获取保存数据：json字符串
        getSaveDataStr() {
            let params = {};
            params.addList = this.handelAddData(this.getAddRowList());
            params.editList = this.handelEditData(this.getEditRowList());
            if (!this.opts.deleteRemote) {
                params.deleteList = this.handelDeleteData(this.getDeleteRowList());
            }
            return params;
        },
        // 根据列prop：获取合并列属性
        getSpanMethod({row, column, rowIndex, columnIndex}, key, mergeProps) {
            let colProp = column.property;
            // 不是合并列直接返回
            if (mergeProps.indexOf(column.property) < 0) {
                // 1正常显示，不合并也不隐藏
                return {rowspan: 1, colspan: 1}
            }

            let datas = this.getData();

            let okObj = {};
            let startRowIndex = 0;
            for (let i = 0; i < datas.length; i++) {
                // 对比键相同 且 合并列值相同的 行
                if (datas[i][key] == row[key] && datas[i][colProp] == row[colProp]) {
                    okObj[i] = datas[i][colProp];
                }
            }

            // 1正常显示 0隐藏
            // 显示单元格：{rowspan: 1, colspan: 1}
            // 隐藏单元格：{rowspan: 0, colspan: 0}
            // 合并单元格：{rowspan: n, colspan: 1}

            // 不匹配
            if (okObj[rowIndex] == null) {
                // 显示单元格
                return {rowspan: 1, colspan: 1}
            }
            // 场景1：前一行不匹配或首行，后一行不匹配  -->  正常显示
            if ((rowIndex == 0 || okObj[rowIndex - 1] == null) && okObj[rowIndex + 1] == null) {
                return {rowspan: 1, colspan: 1}
            }
            // 场景2：前一行匹配，后一行不匹配  -->  隐藏
            // 场景3：前一行匹配，后一行匹配  -->  隐藏
            if (okObj[rowIndex - 1] != null) {
                return {rowspan: 0, colspan: 0}
            }
            // 场景4：前一行不匹配或首行，后一行匹配  -->  合并
            let lastIndex = rowIndex;
            while (okObj[lastIndex + 1] != null) {
                lastIndex++;
                if (lastIndex >= datas.length - 1) {
                    break;
                }
            }
            let rowspan = lastIndex - rowIndex + 1;
            return {rowspan: rowspan, colspan: 1}
        },
        // 触发选中行
        toggleRowSelection(row, selected) {
            this.$refs[this.opts.id].toggleRowSelection(row, selected);
        },
        // 触发选中行
        toggleRowSelectionByRowKey(rowKey, selected) {
            let row = this.getRow(rowKey);
            if (row == null) {
                return;
            }
            this.toggleRowSelection(row, selected);
        },
        // 编辑行
        toggleEditRow(row) {
            row.isEdit = true;
            row.changeArr = [];
        },
        // 编辑行：根据rowKey
        toggleEditRowByRowKey(rowKey) {
            let row = this.getRow(rowKey);
            row.isEdit = true;
            row.changeArr = [];
        },
        // 添加行
        toggleAddRow(row) {
            row.isAdd = true;
            row.dataIdx = this.getAddRowId();
            this.handelGridData("unshift", {addObj: row});
        },
        // 对Table进行重新布局。当 Table 或其祖先元素由隐藏切换为显示时，可能需要调用此方法
        doLayout() {
            this.$nextTick(() => {
                this.$refs[this.opts.id].doLayout();
            })
        },
        // ------------------------------- 2、通用方法：开放前端接口 end -------------------------------

        // ------------------------------- 3、回调函数 start -------------------------------
        // 回调：添加前
        beforeAdd() {
            // 自定义回调方法
            return this.opts.beforeAdd(this.opts);
        },
        // 回调：编辑前
        beforeEdit(selectRows) {
            // 自定义回调方法
            return this.opts.beforeEdit(this.opts, selectRows);
        },
        // 回调：删除前
        beforeDelete(selectRows) {
            // 自定义回调方法
            return this.opts.beforeDelete(this.opts, selectRows);
        },
        // 回调：删除后
        afterDelete(response) {
            // 自定义回调方法
            this.opts.afterDelete(this.opts, response);
            // 删除成功后是否刷新
            if (this.opts.refreshAfterDelete) {
                this.refresh()
            }
        },

        // 回调：保存前
        beforeSave() {
            this.changeLoadingStatus("save", true);
            // 自定义回调方法
            return this.opts.beforeSave(this.opts);
        },
        // 回调：保存后
        afterSave(response) {
            // 提示保存条数
            this.showSaveMsg(response.data);

            // 重置编辑状态
            this.handelGridData("resetData");

            // 旧数据保存
            this.setOldGridData();
            // 自定义回调方法
            if (this.opts.afterSave) {
                this.opts.afterSave(this.opts, response);
            }

            // 保存成功后是否刷新
            if (this.opts.refreshAfterSave) {
                this.refresh()
            }
        },

        // 回调：数据加载前
        beforeLoadData(type) {
            this.changeLoadingStatus(type, true)

            // 自定义回调方法
            this.opts.beforeLoadData(this.opts);
        },
        // 回调：数据加载完成
        afterLoadData() {
            // 自定义回调方法
            this.opts.afterLoadData(this.opts);
            this.setOldGridData();
        },
        // 回调：数据加载完成，dom加载完成
        afterAll() {
            this.$nextTick(this.doAfterAll(this.opts))
        },
        // 回调：数据加载完成，dom加载完成
        doAfterAll(opts) {
            // 勾选选中行
            if (this.opts.reserveSelection) {
                this.reserveSelection();
            }
            this.opts.afterAll(opts)
            // 避免偶发布局问题
            this.doLayout();
        },
        // ------------------------------- 3、回调函数 end -------------------------------
        // ------------------------------- 4、事件 start -------------------------------
        // 工具栏：操作表格数据入口
        toolbarBtnFn(type) {
            switch (type) {
                case "add" :
                    this.addRow();
                    break;
                case "edit" :
                    this.editRow();
                    break;
                case "save" :
                    this.doSave();
                    break;
                case "del" :
                    this.doDelRow();
                    break;
                case "cancel" :
                    this.cancel();
                    break;
                case "refresh" :
                    this.refresh();
                    break;
                case "sortAuto" :
                    this.sortAuto();
                    break;
                case "sortUp" :
                    this.sortUp();
                    break;
                case "sortDown" :
                    this.sortDown();
                    break;
            }
        },
        // 分页变化：修改每页条数
        sizeChangeEvent(val) {
            this.opts.pagination.pageSize = val;
            this.pageJump("init");
        },
        // 分页变化：修改当前页
        pageChangeEvent(val) {
            this.opts.pagination.currentPage = val;
            this.pageJump("init");
        },
        // 选择修改事件
        selectionChangeEvent(selection) {
            // 单选操作特殊处理
            if (!this.opts.multiSelect) {
                if (selection.length > 1) {
                    this.tempData.triggerSelectionChange = false;
                    this.$refs[this.opts.id].clearSelection();
                    this.tempData.triggerSelectionChange = false;
                    let newSelection = selection[selection.length - 1];
                    selection.shift();
                    this.$refs[this.opts.id].toggleRowSelection(newSelection, true);
                }

            }
            if (this.tempData.triggerSelectionChange) {
                this.opts.selectionChangeEvent(selection);
            } else {
                this.tempData.triggerSelectionChange = true;
            }
        },
        // 全选事件
        selectAllEvent(selection) {
            // 非树形结构不处理
            if (this.opts.rowParentKey == null || this.opts.rowParentKey == '') {
                return;
            }
            this.selectAll = !this.selectAll;
            if (this.selectAll) {
                let data = this.getData();
                for (let i = 0; i < data.length; i++) {
                    // 递归设定所有子节点为选中状态
                    this.setChildSelect(data[i]);
                }
            } else {
                this.clearSelected();
            }
        },
        setChildSelect(data) {
            this.toggleRowSelection(data, true);
            let children = data[this.opts.rowChildren];
            if (children != null && children.length > 0) {
                for (let i = 0; i < children.length; i++) {
                    this.setChildSelect(children[i]);
                }
            }
        },
        // 排序事件
        sortChangeEvent(obj) {
            if (this.oldGridData != "" && this.hasChange()) {
                this.$message.error('存在未保存的数据，请保存后再排序！');
                return;
            }

            // 前端排序
            if (obj.column.sortable === true) {
                return;
            }

            // element：ascending默认, descending   对应mysql：asc、desc
            let orderType = ""
            if (obj.order == "ascending") {
                orderType = obj.prop + " asc";
            } else if (obj.order == "descending") {
                orderType = obj.prop + " desc";
            }
            this.opts.pagination.orderBy = orderType;

            this.pageJump("init");
        },
        // 排序方法
        sortMethod(a, b, prop) {
            let p1 = a[prop];
            let p2 = b[prop];
            p1 = p1 == null || p1 == undefined ? '' : p1;
            p2 = p2 == null || p2 == undefined ? '' : p2;
            if (p1 == p2) {
                return 0;
            }
            if (p1 > p2) {
                return 1;
            }
            return -1;
        },
        // 表头菜单：右键
        headerMenuEvent(column, event) {
            event.preventDefault();
            // 当前选择节点
            this.currentMenuTarget = event.currentTarget;
            // 隐藏子菜单
            this.headerMenuColStyle.display = 'none';
            this.headerMenuFilterDataStyle.display = 'none';

            // 不支持固定列：selection、index、多级表头
            if (column.type == "selection" || column.property == "dataIdx"
                || column.level == 2
                || (column.children != null && column.children.length > 0)
            ) {
                this.headerMenuShowFixed = false;
            } else {
                this.headerMenuShowFixed = true;
            }

            // 菜单固定宽度
            let menuWidth = 135;
            let clientX = event.clientX;
            let clientWidth = document.body.clientWidth;
            let left = (clientX + menuWidth) > clientWidth ? (clientX - menuWidth) : clientX;
            this.headerMenuStyle.left = left + 'px';
            this.headerMenuStyle.top = event.clientY + 'px';
            this.headerMenuStyle.display = 'inline';
            this.tempData.currentHeaderContextMenuCol = {
                prop: column.property,
                type: column.type,
                level: column.level,
            };
        },
        // 表头菜单：关闭
        headerMenuCloseEvent(e) {
            this.headerMenuStyle.display = 'none';
            this.headerMenuHideChild();
        },
        // 表头菜单：隐藏子菜单
        headerMenuHideChild() {
            this.headerMenuColStyle.display = 'none';
            this.headerMenuFilterDataStyle.display = 'none';
        },
        // 表头菜单：获取子菜单位置
        headerMenuChildPosition(e) {
            let temp = e.currentTarget.getBoundingClientRect();
            // 菜单固定宽度
            let menuWidth = 131;
            let menuColWidth = 200; // 等于弹出框的宽度

            let clientWidth = document.body.clientWidth;
            let left = (temp.left + menuWidth + menuColWidth) > clientWidth
                ? (temp.left - menuColWidth) : temp.left + menuWidth;
            return {
                top: temp.top,
                left: left
            }
        },
        // 表头菜单：筛选数据
        headerMenuFilterDataEvent(e) {
            this.filterTextList = []; // 重置列表
            this.headerMenuHideChild();

            let position = this.headerMenuChildPosition(e);
            this.headerMenuFilterDataStyle.left = position.left + 'px';
            this.headerMenuFilterDataStyle.top = position.top + 'px';
            this.headerMenuFilterDataStyle.display = 'flex';
            let classList = this.currentMenuTarget.classList;
            let colClassName = "";
            for (let i = 0; i < classList.length; i++) {
                if (classList[i].indexOf('_column_') > -1) {
                    colClassName = classList[i];
                    break;
                }
            }
            let currentNode = this.currentMenuTarget;
            let parentNode = this.currentMenuTarget.parentElement;
            // 获取父节点
            for (let i = 0; i < 10; i++) {
                parentNode = parentNode.parentElement;
                if (parentNode.className != undefined && parentNode.className.indexOf('ap-table') > -1) {
                    break;
                }
            }
            let cellList = parentNode.getElementsByClassName(colClassName);
            if (cellList == null || cellList.length < 2) {
                // 无数据
                return;
            }

            let distinctTextList = [];
            this.selectedFilterHeaderEle = cellList[0];
            // 从1开始：表头不包含表头
            for (let i = 1; i < cellList.length; i++) {
                let hasAdd = false;
                for (let j = 0; j < distinctTextList.length; j++) {
                    if (cellList[i].innerText == distinctTextList[j].label) {
                        distinctTextList[j].count = distinctTextList[j].count + 1;
                        distinctTextList[j].node.push(cellList[i]);
                        hasAdd = true;
                        break;
                    }
                }
                if (!hasAdd) {
                    distinctTextList.push({
                        id: "id" + i,
                        label: cellList[i].innerText,
                        count: 1,
                        node: [cellList[i]]
                    });
                }
            }

            // 排序
            distinctTextList = distinctTextList.sort((a, b) => {
                if (a.label < b.label) {
                    return -1;
                } else if (a.label > b.label) {
                    return 1;
                } else {
                    return 0;
                }
            })

            // 全部
            this.filterTextList.push({
                label: "全部",
                id: "#all#",
                count: cellList.length - 1,
                node: []
            });
            this.filterTextList = this.filterTextList.concat(distinctTextList);
        },
        // 表头菜单：隐藏列
        headerMenuHideColEvent(e) {
            this.hideCurrentHeaderMenuParentCol();
            let col = this.getCurrentHeaderMenuCol();
            col.isShow = false;
            this.headerMenuCloseEvent();
        },
        // 表头菜单：全部列
        headerMenuOperateColEvent(e) {
            this.headerMenuHideChild();

            let position = this.headerMenuChildPosition(e);
            this.headerMenuColStyle.left = position.left + 'px';
            this.headerMenuColStyle.top = position.top + 'px';
            this.headerMenuColStyle.display = 'inline';
        },
        // 表头菜单：右侧固定
        headerMenuRightFixedEvent(e) {
            let col = this.getCurrentHeaderMenuCol();
            col.fixed = 'right';
            this.headerMenuCloseEvent();
        },
        // 表头菜单：左侧固定
        headerMenuLeftFixedEvent(e) {
            let col = this.getCurrentHeaderMenuCol();
            col.fixed = 'left';
            this.headerMenuCloseEvent();
        },
        // 表头菜单：解除固定
        headerMenuUnFixedEvent(e) {
            let col = this.getCurrentHeaderMenuCol();
            col.fixed = false;
            this.headerMenuCloseEvent();
        },
        // 表格布局：显示dialog
        showTableLayoutEvent() {
            this.tableLayout.visible = true;
            this.initTableLayoutTree();
            this.initTableLayoutList();
        },
        // 表格布局：显示dialog
        initTableLayoutList() {
            this.$request({
                url: "/apm/sys/TSysUserTableLayout/getTSysUserTableLayoutList",
                method: 'post',
                data: {
                    fdCode: this.opts.code,
                    selectedId: this.tableLayout.selectedTableLayout
                }
            }).then(response => {
                let list = [{
                    "fdId": "default",
                    "fdName": "[默认]全部列",
                    "fdValue": this.tableLayout.oldColumns
                }];
                this.tableLayout.configed = list.concat(response.data.list);
                this.loadTableLayoutByInfo(response.data.selectedLayout);
            })
        },
        // 初始化新建/编辑布局、全部列
        initTableLayoutTree() {
            // 编辑数据
            this.tableLayout.editObj = {
                fdId: "",
                fdCode: this.opts.code,
                fdName: "",
                fdType: 2, // 默认自定义布局
                fdValue: "",
            }
            this.tableLayout.editData = [];

            this.tableLayout.cols = JSON.parse(this.tableLayout.oldColumns);
        },
        // 表格布局：树平级拖拽
        tablaLayoutDropEvent(draggingNode, dropNode, type) {
            return type != 'inner';
        },
        // 表格布局：新建布局
        newTableLayout() {
            this.tableLayout.loading = true;
            this.initTableLayoutTree();
            this.tableLayout.editObj.fdId = "";
            this.tableLayout.selectedTableLayout = "";
            this.tableLayout.editHeader = "新建布局";
            this.tableLayout.loading = false;
        },
        // 表格布局：加载布局明细
        loadTableLayoutByInfo(obj) {
            let fdId = this.tableLayout.selectedTableLayout;
            if (obj == null) {
                fdId = "default";
            }
            // 默认布局
            if (fdId == "default") {
                this.tableLayout.canEdit = false;
                this.initTableLayoutTree();
                return;
            }
            this.tableLayout.loading = true;
            this.initTableLayoutTree();
            this.tableLayout.editObj.fdId = fdId;
            this.tableLayout.editHeader = "编辑布局";
            this.loadTableLayoutDetail(obj)
        },
        // 表格布局：加载布局明细
        loadTableLayoutBySelected() {
            let fdId = this.tableLayout.selectedTableLayout;
            if (fdId == "" || fdId == null) {
                fdId = "default";
            }

            // 默认布局
            if (fdId == "default") {
                this.tableLayout.canEdit = false;
                this.initTableLayoutTree();
                return;
            }
            this.tableLayout.loading = true;
            this.initTableLayoutTree();
            this.tableLayout.editObj.fdId = fdId;
            this.tableLayout.editHeader = "编辑布局";
            this.$request({
                url: "/apm/sys/TSysUserTableLayout/getTSysUserTableLayoutByFdId",
                method: 'post',
                data: {
                    fdId: fdId
                }
            }).then(response => {
                this.loadTableLayoutDetail(response.data)
            }).finally(() => {
                this.tableLayout.loading = false;
            })
        },
        loadTableLayoutDetail(data) {
            this.tableLayout.canEdit = this.tableLayout.editCommon || data.fdType != 1;

            this.tableLayout.editObj = data;
            this.tableLayout.editData = this.getTablaLayoutEditData(data.fdValue);
            // 已添加设为失效，避免重复添加
            this.setTablaLayoutColDisabled(this.tableLayout.editData);
            this.tableLayout.loading = false;
        },
        // 表格布局：处理后台ID，防止前端表格布局更新后，原布局服务器布局失效
        getTablaLayoutEditData(value) {
            let ids = JSON.parse(value);
            let editData = [];
            let cols = this.tableLayout.cols;
            for (let i = 0; i < ids.length; i++) {
                for (let j = 0; j < cols.length; j++) {
                    if (ids[i].id == cols[j].id) {
                        editData.push({
                            id: ids[i].id,
                            label: cols[j].label
                        });
                        break;
                    }
                }
            }
            return editData;
        },
        // 表格布局：添加列到新建/编辑
        addTablaLayoutCol() {
            let colTreeRef = this.opts.id + 'TableLayoutColTree';
            let checkNodes = this.$refs[colTreeRef].getCheckedNodes();
            if (checkNodes.length < 1) {
                this.$message.error("请选择需要显示的列");
                return;
            }
            // 添加到新建/编辑tree
            for (let i = 0; i < checkNodes.length; i++) {
                this.tableLayout.editData.push({
                    id: checkNodes[i].id,
                    label: checkNodes[i].label,
                    prop: checkNodes[i].prop
                });
            }
            // 已添加设为失效，避免重复添加
            this.setTablaLayoutColDisabled(this.$refs[colTreeRef].getCheckedNodes());
        },
        // 表格布局：已添加设为失效，避免重复添加
        setTablaLayoutColDisabled(checkNodes) {
            let colTreeRef = this.opts.id + 'TableLayoutColTree';
            let cols = this.tableLayout.cols;
            for (let i = 0; i < cols.length; i++) {
                this.$refs[colTreeRef].setChecked(cols[i].id, false, false);
                for (let j = 0; j < checkNodes.length; j++) {
                    if (cols[i].id == checkNodes[j].id) {
                        cols[i].disabled = true;
                        break;
                    }
                }
            }
            // 刷新组件，否则失效状态不会更新
            this.tableLayout.colsKey++;
        },
        // 表格布局：删除新建/编辑中的列
        delTablaLayoutCol() {
            let checkNodes = this.$refs[this.opts.id + 'TableLayoutEditTree'].getCheckedNodes();
            // 删除设为有效
            let cols = this.tableLayout.cols;
            for (let i = 0; i < cols.length; i++) {
                for (let j = 0; j < checkNodes.length; j++) {
                    if (cols[i].id == checkNodes[j].id) {
                        delete cols[i].disabled;
                        break;
                    }
                }
            }
            // 刷新组件，否则失效状态不会更新
            this.tableLayout.colsKey++;

            // 删除新建/编辑列表
            let editData = this.tableLayout.editData;
            for (let i = 0; i < checkNodes.length; i++) {
                for (let j = 0; j < editData.length; j++) {
                    if (checkNodes[i].id == editData[j].id) {
                        editData.splice(j, 1);
                        break;
                    }
                }
            }
        },
        // 表格布局：删除布局
        delTableLayout() {
            this.$request({
                url: "/apm/sys/TSysUserTableLayout/deleteTSysUserTableLayoutByFdId",
                method: 'post',
                data: {
                    fdId: this.tableLayout.selectedTableLayout
                }
            }).then(response => {
                this.$message.success("删除布局成功！");
                this.tableLayout.selectedTableLayout = "default";
                this.showTableLayoutEvent();
            })
        },
        // 表格布局：保存布局
        saveTableLayout() {
            let editData = this.tableLayout.editData;
            if (editData == null || editData.length < 1) {
                this.$message.error("布局列为空，请选择需要显示的列。");
                return;
            }
            let fdName = this.tableLayout.editObj.fdName;
            if (fdName == null || fdName.length < 1) {
                this.$message.error("布局名称为空，请填写布局名称。");
                return;
            }
            this.tableLayout.editObj.fdValue = JSON.stringify(editData);
            this.$request({
                url: "/apm/sys/TSysUserTableLayout/saveTSysUserTableLayout",
                method: 'post',
                data: this.tableLayout.editObj
            }).then(response => {
                this.$message.success("布局保存成功！");
                this.tableLayout.selectedTableLayout = response.data; // 选择列赋值：新建时
                this.initTableLayoutList();
            })
        },
        // 表格布局：保存默认布局
        saveDefaultTableLayout() {
            this.$request({
                url: "/apm/sys/TSysUserTableLayout/saveDefaultTableLayout",
                method: 'post',
                data: {
                    fdDefaultId: this.tableLayout.selectedTableLayout,
                    fdCode: this.opts.code
                }
            }).then(response => {
                console.log("默认布局保存成功");
            })
        },
        getTableLayoutConfig(fdId) {
            let list = this.tableLayout.configed;
            for (let i = 0; i < list.length; i++) {
                if (list[i].fdId == fdId) {
                    return list[i];
                }
            }
            return {};
        },
        // 表格布局：选中展示的布局
        selectTableLayout() {
            let config = this.getTableLayoutConfig(this.tableLayout.selectedTableLayout);
            this.renderTableLayout(config);
            this.saveDefaultTableLayout();
        },
        // 表格布局：渲染布局
        renderTableLayout(info) {
            if (info == null || info.fdValue == null) {
                this.tableLayout.visible = false;
                return;
            }
            if (info.fdId === "default") {
                this.opts.myLayout = "";
            } else {
                this.opts.myLayout = info.fdName;
            }
            this.tableLayout.selectedTableLayout = info.fdId;
            let columns = this.opts.columns;

            let showCols = JSON.parse(info.fdValue);
            let newCols = [];
            // 需要显示的列表
            for (let i = 0; i < showCols.length; i++) {
                for (let j = 0; j < columns.length; j++) {
                    let col = columns[j];
                    col.isShow = true;
                    if (showCols[i].id == col.id) {
                        newCols.push(col);
                        break;
                    }
                }
            }
            // 隐藏的列
            let count = showCols.length;
            for (let i = 0; i < columns.length; i++) {
                let isShow = false;
                let col = columns[i];
                for (let j = 0; j < showCols.length; j++) {
                    if (showCols[j].id == col.id) {
                        isShow = true;
                        break;
                    }
                }
                if (!isShow) {
                    col.isShow = false;
                    newCols.push(col);
                }
            }
            this.opts.columns = newCols;
            this.tableLayout.visible = false;
            this.reload(false); // 强制刷新避免列顺序修改后没刷新
        },
        // 表格布局：加载用户默认布局
        async loadDefaultLayout() {
            return new Promise((resolve) => {
                let code = this.opts.code;
                if (code == null || code == '') {
                    resolve();
                    return;
                }
                this.tableLayout.oldColumns = JSON.stringify(this.opts.columns);
                this.$request({
                    url: "/apm/sys/TSysUserTableLayout/getDefaultLayout",
                    method: 'post',
                    data: {
                        fdCode: this.opts.code
                    }
                }).then(response => {
                    this.renderTableLayout(response.data)
                }).finally(() => {
                    resolve();
                })
            });
        },
        // ------------------------------- 4、事件 end -------------------------------
    }
}
</script>

<style scoped>
.ap-comp-search /deep/ .el-input__suffix {
    right: 0px;
}

.ap-table {
    width: 100%;
    border: 1px solid #dfe6ec;
}

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

.ap-table-header {
    padding: 5px 10px;
}

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

.ap-table-col-config {
    color: #1890ff;
    margin-left: 5px;
}

.ap-table-col-config:hover {
    cursor: pointer;
    color: #46A6FF;
}

.ap-table-layout-ctn {
    height: 350px;
}

.ap-table-layout-my {
    width: 230px;
    height: 100%;
}

.ap-table-layout-edit {
    height: 100%;
    border-left: 1px solid #E2E7EF;
    padding: 0 10px;
    margin-left: 10px;
}

.ap-table-layout-type:hover {
    cursor: pointer;
    color: #59afff;
}

.ap-table-layout-edit-btn:hover {
    cursor: pointer;
    color: #46A6FF;
}

.ap-table-layout-col {
    height: 100%;
    padding: 0 10px;
}

.ap-table-layout-ctn /deep/ .el-button--mini {
    padding: 5px;
}

.ap-table-layout-default {
    font-size: 12px;
}

.ap-table-layout-header {
    font-size: 15px;
    font-weight: bold;
    padding: 3px 0;
    border-bottom: 1px solid #E2E7EF;
    width: 100%;
    margin-bottom: 5px;
}

.ap-table-layout-configed {
    width: 100%;
    overflow: auto;
    padding-right: 5px;
}

.ap-table-layout-footer {
    margin-top: 5px;
    padding-top: 5px;
    border-top: 1px solid #E2E7EF;
}

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

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

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

.ap-table-pagination {
    /*overflow: hidden;*/
}

.ap-table-export {
    color: #83868d;
    margin-left: 10px;
}

.ap-table-export:hover {
    cursor: pointer;
    color: rgb(64, 158, 255);
}

.ap-table-col-tooltip {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* -------- 表头右键菜单 start -------- */
.ap-table-header-menu {
    width: 132px;
    position: fixed;
    background-color: #fff;
    padding: 5px 0px;
    font-size: 14px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
    z-index: 900;
}

.ap-table-header-menu-line {
    height: 1px;
    width: 100%;
    background-color: #dfe6ec;
}

.ap-table-header-menu-li {
    padding: 3px 8px;
    cursor: pointer;
}

.ap-table-header-menu-li:hover {
    color: #1890ff;
    background-color: #e8f4ff;
}

.ap-table-header-menu-msg {
    padding: 3px 8px;
    font-size: 12px;
    color: gray;
}

.ap-table-header-menu-close {
    text-align: right;
    padding: 1px 5px;
}

.ap-table-header-menu-close i {
    cursor: pointer;
    color: #606266;
    font-size: 17px;
}

.ap-table-header-menu-close i:hover {
    color: #1890ff;
}

.ap-table-header-menu-col {
    width: 150px;
    position: fixed;
    background-color: #fff;
    padding: 5px 10px;
    font-size: 14px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
    z-index: 901;
    overflow: hidden;
}

.ap-table-header-menu-filter {
    width: 200px;
    max-height: 380px;
}

.ap-table-header-menu-filter-count {
    color: #c0c4cc;
}

/* -------- 表头右键菜单 end -------- */

.ap-table-selection-col {
    padding-left: 0px;
}

.ap-table-layout-edit /deep/ .el-tree-node__expand-icon.is-leaf {
    display: none;
}
</style>
<style>

.ap-table-header-menu-col .el-tree-node__expand-icon {
    display: none;
}

.ap-table-header-filter {
    color: #ffba00;
}

.ap-table-row-hide {
    display: none;
}

</style>
