<template>
  <div class="mu-table-content" :class="currentConfig.size" v-mu-loading="isLoading">
    <el-form :model="ruleForm" :ref="'form'+ currentConfig.name">

      <el-table :data="value"
                :height="currentConfig.height"
                :maxHeight="currentConfig.maxHeight"
                :row-key="currentConfig.rowKey"
                :current-row-key="currentConfig.currentRowKey"
                :default-expand-all="currentConfig.defaultExpandAll"
                :highlight-current-row="currentConfig.highlightCurrentRow"
                :border="currentConfig.border"
                :stripe="currentConfig.stripe"
                :ref="'table'+ currentConfig.name"
                :lazy="currentConfig.isLazy"
                :tree-props="currentConfig.tabChildren"
                :cell-style="currentConfig.cellStyle"
                :summary-method="currentConfig.summaryMethod"
                :sum-text="currentConfig.sumText"
                :span-method="currentConfig.spanMethod"
                :show-summary="currentConfig.showSummary"
                @select="handleSelect"
                @select-all="handleSelectAll"
                @row-click="handleRowClick"
                @selection-change="handleSelectionChange"
                @current-change="handleCurrentChange">
        <el-table-column v-if="currentConfig.showSelection" type="selection" width="36" align="center"
                         :fixed="currentConfig.selectFixed" :selectable="getSelectable"></el-table-column>
        <el-table-column v-if="currentConfig.showIndex" :fixed="currentConfig.indexFixed" type="index"
                         align="center"></el-table-column>
        <template v-for="(item, index) in currentConfig.columns">
          <mu-column-item :config="item" :key="index" v-if="item.type !=='slot'"></mu-column-item>
          <slot :name="item.name" v-else></slot>
        </template>
        <slot></slot>
      </el-table>
    </el-form>
  </div>
</template>
<script lang="ts">
  import { Component, Vue, Prop, Watch } from 'vue-property-decorator';
  import util from '../../common/utils/util';
  import { SystemApi } from '../../api';
  // import { PageModule } from '@/store/modules';
  import MuColumnItem from './column-item.vue';

  interface TableConfig {
    name?: string,
    rowKey: string | number,
    size?: string,
    height?: string | number,
    maxHeight?: string | number,
    showIndex?: boolean,
    showSelection?: boolean,
    selectFixed?: boolean,
    indexFixed?: boolean,
    disabled?: boolean,
    sourceConfigs?: any,
    selection?: any,
    tabChildren?: any,
    isLazy?: boolean,
    cellStyle?: any,
    tabChildrenIndex?: any,
    load?: Function,
    currentChange?: Function,
    isCurrentChange?: Function,
    highlightCurrentRow?: boolean,
    defaultExpandAll?: boolean,
    expandRowKeys?: any,
    currentRowKey: string | number,
    showSummary?: boolean,
    sumText?: string,
    summaryMethod?: Function,
    spanMethod?: Function,
    onSelect?: Function,
    onSelectAll?: Function,
    rowClick?: Function
    border?: boolean,
    stripe?: boolean,
    columns?: any
  }

  @Component({
    components: { MuColumnItem }
  })
  export default class MuTable extends Vue {
    @Prop({ default: () => [] }) private value: any;
    @Prop({ default: () => [] }) private config: TableConfig;
    @Prop() private extension?: (table: any, value: any) => void;
    defaultConfig: any = {
      name: util.generateUUID(),
      border: true,
      stripe: true,
      expandRowKeys: []
    };
    currentSearchParams: any = {};
    isLoading: boolean = false;
    isCurrentLoading: boolean = false;
    defaultValue = JSON.parse(JSON.stringify(this.value || []));
    currentEditList: any = [];
    currentAddList: any = [];
    currentNode: any = {};
    ruleForm: any = {
      data: this.value
    };

    // 检测值
    @Watch("value")
    onValueChanged(val: any) {
      this.ruleForm.data = val;
      this.defaultValue = JSON.parse(JSON.stringify(this.value || []));
    }

    // 合并默认数据
    get currentConfig() {
      const result = this.config;
      Object.keys(this.defaultConfig).forEach((key: string) => {
        if (this.config[key] === undefined) result[key] = this.defaultConfig[key];
      });
      if (this.config.currentChange && this.config.highlightCurrentRow === undefined) result.highlightCurrentRow = true;
      return result;
    }

    mounted() {
      // onHandleData为操作数据， onHandleEdit操作编辑, onHandleRefresh为刷新
      const params = {
        onHandleData: this.onHandleData,
        onHandleEdit: this.onHandleEdit,
        onHandleAdd: this.onHandleAdd,
        onHandleRefresh: (callback?: any) => {
          this.onHandleData('load', this.currentSearchParams, callback);
        },
        getSelection: () => this.config.selection,
        getSelectionKeys: () => this.config.selection.map((item: any) => item[this.config.rowKey]),
        setSelection: (data: any, isChecked: boolean) =>
          data.forEach(row => (this.$refs[`table${this.config.name}`] as any).toggleRowSelection(row, isChecked)),
        clearSelection: () => (this.$refs[`table${this.config.name}`] as any).clearSelection(),
        clearCurrentRow: () => (this.$refs[`table${this.config.name}`] as any).setCurrentRow({}),
        setCurrentRow: (row: any) => (this.$refs[`table${this.config.name}`] as any).setCurrentRow(row),
        getCurrentRow: () => this.currentNode,
        getEditStatus: () => !!this.currentEditList.length,
        getAddStatus: () => !!this.currentAddList.length,
        onShowEdit: async (isEdit: boolean, callback?: Function) => {
          this.config.disabled = !isEdit;
          callback && callback();
        }
      };
      this.extension && this.extension(this.$refs[`table${this.config.name}`], params);
      // 收集当前页的form数据
      // PageModule.setFormLog({
      //   name: this.config.name,
      //   form: this.$refs[`form${this.config.name}`],
      //   extension: params,
      //   type: 'table'
      // });
    }

    /**
     * 处理表格数据，增删改查
     * @author tanxj
     * @param type load/add/update/delete
     * @param params 数据参数
     * @param callback 回调
     */
    onHandleData(type: string, params: any, callback?: Function) {
      const currentSource = (this.config.sourceConfigs || []).find((item: any) => item.type === type);
      switch (type) {
        case "load":
          this.onHandleEdit('cancel');
          this.onHandleAdd('cancel');
          if (!currentSource) return callback && callback();
          this.currentSearchParams = params;
          return this.handleLoad(currentSource, params, callback);
        case "add":
          params = params || this.currentAddList;
          return this.handleAdd(currentSource, params, callback);
        case "update":
          params = params || this.currentEditList;
          return this.handleUpdate(currentSource, params, callback);
        case "delete":
          params = params || this.config.selection;
          return this.handleDelete(currentSource, params, callback);
        default:
          return;
      }
    }

    /**
     * 加载数据
     * @author tanxj
     * @param source 数据源配置
     * @param options 加载数据参数
     * @param callback 回调
     */
    handleLoad(source: any, options: any, callback?: Function) {
      this.isLoading = true;
      const { api, onBeforeSubmit, onSuccess, onError } = source;
      const next = (currentParams: any) => {
        const { serviceName, funcName, config } = api;
        SystemApi.handleApi({ serviceName, funcName, params: currentParams }, config).then(async (ret: any) => {
          this.isLoading = false;
          this.defaultValue = JSON.parse(JSON.stringify(this.value || []));
          const result = onSuccess ? onSuccess(ret.data, this) : undefined;
          callback && callback(ret, result);
        }, (error: any) => {
          this.isLoading = false;
          onError && onError(error);
        });
      };
      onBeforeSubmit ? onBeforeSubmit({ ...api.params, ...options }, next, this.value) : next({ ...api.params, ...options });
    }

    /**
     * 新增数据
     * @author tanxj
     * @param source 数据源配置
     * @param data 可批量或者单条
     * @param callback 回调
     */
    handleAdd(source: any, data: any, callback?: Function) {
      const { api, onBeforeSubmit, onSuccess, onErrorValid, onError } = (source || {}) as any;
      const currentForm: any = this.$refs[`form${this.config.name}`];
      const handlerRestApi = () => {
        if (!source) {
          this.onHandleAdd('close', data);
          return callback && callback();
        }
        const next = (params: any) => {
          const { serviceName, funcName, config } = api;
          SystemApi.handleApi({ serviceName, funcName, params: params }, config).then((ret: any) => {
            onSuccess && onSuccess(ret.data, this);
            this.onHandleAdd('close', data);
            callback && callback(ret);
          }, (error: any) => {
            onError && onError(error);
          });
        };
        // 处理参数
        if (data instanceof Array) {
          data = data.map((item: any) => {
            delete item.isEdit;
            delete item.id;
            delete item.isAdd;
            return item;
          });
        } else {
          delete data.isEdit;
          delete data.id;
          delete data.isAdd;
          data = { ...api.params, ...data };
        }
        onBeforeSubmit ? onBeforeSubmit(data, next, this) : next(data);
      };
      // 批量处理或单条
      if (!(data instanceof Array)) {
        const index = this.value.findIndex((item: any) => item[this.config.rowKey] === data[this.config.rowKey]);
        const result = currentForm.fields.filter((fieldItem: any) => {
          if (parseInt(fieldItem.prop.split('.')[1]) === index) return fieldItem.prop;
        }).map((fieldItem: any) => fieldItem.prop);
        let count = 0;
        currentForm.validateField(result, (valid: any) => {
          if (valid) return onErrorValid && onErrorValid(valid, currentForm, this);
          // 处理数据
          if (++count === result.length) handlerRestApi();
        });
      } else {
        currentForm.validate((valid: any) => {
          if (!valid) return onErrorValid && onErrorValid(valid, currentForm, this);
          // 处理数据
          handlerRestApi();
        });
      }
    }

    /**
     * 修改数据
     * @author tanxj
     * @param source 数据源配置
     * @param data 可批量或者单条
     * @param callback 回调
     */
    handleUpdate(source: any, data: any, callback?: Function) {
      const currentForm: any = this.$refs[`form${this.config.name}`];
      const { api, onValid, onErrorValid, onBeforeSubmit, onSuccess, onError } = (source || {}) as any;
      onValid && onValid(currentForm, this);
      const handlerRestApi = () => {
        if (!source) {
          this.onHandleEdit('close', data);
          return callback && callback();
        }
        const next = (params: any) => {
          const { serviceName, funcName, config } = api;
          SystemApi.handleApi({ serviceName, funcName, params: params }, config).then((ret: any) => {
            onSuccess && onSuccess(ret.data, this);
            this.onHandleEdit('close', data);
            callback && callback(ret);
          }, (error: any) => {
            onError && onError(error);
          });
        };
        data = (data instanceof Array) ? data : { ...api.params, id: data.id, ...data };
        onBeforeSubmit ? onBeforeSubmit(data, next, this) : next(data);
      };
      // 批量处理或单条
      if (!(data instanceof Array)) {
        const index = this.value.findIndex((item: any) => item[this.config.rowKey] === data[this.config.rowKey]);
        // 找到
        const result = currentForm.fields.filter((fieldItem: any) => {
          if (parseInt(fieldItem.prop.split('.')[1]) === index) return fieldItem.prop;
        }).map((fieldItem: any) => fieldItem.prop);
        let count = 0;
        currentForm.validateField(result, (valid: any) => {
          if (valid) return onErrorValid && onErrorValid(valid, currentForm, this);
          // 处理数据
          if (++count === result.length) handlerRestApi();
        });
      } else {
        currentForm.validate((valid: any) => {
          if (!valid) return onErrorValid && onErrorValid(valid, currentForm, this);
          // 处理数据
          handlerRestApi();
        });
      }
    }

    /**
     * 删除数据
     * @author tanxj
     * @param source 数据源配置
     * @param data 可批量或者单条
     * @param callback 回调
     */
    handleDelete(source: any, data: any, callback?: Function) {
      if (!source) {
        if (!(data instanceof Array)) data = [data];
        const currentIds = data.map((dataItem: any) => dataItem[this.config.rowKey]);
        for (let i = this.value.length - 1; i >= 0; i--) {
          if (currentIds.includes(this.value[i][this.config.rowKey])) this.value.splice(i, 1);
        }
        this.value.forEach((item: any, index: number) => {
          if (currentIds.includes(item[this.config.rowKey])) this.value.splice(index, 1);
        });
        return callback && callback();
      }
      const { api, onBeforeSubmit, onSuccess, onError } = source;
      const next = (params: any) => {
        const { serviceName, funcName, config } = api;
        SystemApi.handleApi({ serviceName, funcName, params: params }, config).then((ret: any) => {
          onSuccess && onSuccess(ret.data, this);
          if (!(data instanceof Array)) data = [data];
          const currentIds = data.map((dataItem: any) => dataItem[this.config.rowKey]);
          for (let i = this.value.length - 1; i >= 0; i--) {
            if (currentIds.includes(this.value[i][this.config.rowKey])) this.value.splice(i, 1);
          }
          this.defaultValue = JSON.parse(JSON.stringify(this.value || []));
          callback && callback(ret);
        }, (error: any) => {
          onError && onError(error);
        });
      };
      const params = (data instanceof Array)
        ? data.map((item: any) => item[this.config.rowKey])
        : { ...api.params, id: data[this.config.rowKey] };
      onBeforeSubmit ? onBeforeSubmit(params, next, data) : next(params);
    }

    /**
     * 操作编辑
     * @author tanxj
     * @param type edit|close|cancel
     * @param data 可批量或者单条
     * @param callback 回调
     */
    onHandleEdit(type: string, data?: any, callback?: Function) {
      switch (type) {
        case "edit": {
          data = data || this.config.selection;
          if (!(data instanceof Array)) data = [data];
          this.currentEditList = this.currentEditList.concat(data);
          const currentIds = this.currentEditList.map((item: any) => item[this.config.rowKey]);
          this.value.forEach((item: any, index: number) => {
            if (currentIds.includes(item[this.config.rowKey])) {
              this.value[index].isEdit = true;
              this.value.splice(index, 1, Object.assign({}, this.value[index]));
            }
          });
          this.$forceUpdate();
          return;
        }
        case 'close': {
          if (!(data instanceof Array)) data = [data];
          const currentIds = data.map((dataItem: any) => dataItem[this.config.rowKey]);
          this.currentEditList = this.currentEditList.filter((item: string) => !currentIds.includes(item[this.config.rowKey]));
          this.value.forEach((item: any) => {
            if (currentIds.includes(item[this.config.rowKey])) item.isEdit = false;
          });
          this.defaultValue = JSON.parse(JSON.stringify(this.value || []));
          return;
        }
        case 'cancel': {
          if (!data) {
            this.currentEditList = [];
            this.value.forEach((item: any, index: number) => {
              this.value[index] = Object.assign(item, {
                ...this.defaultValue[index],
                isEdit: false
              });
            });
            return;
          } else {
            this.currentEditList = this.currentEditList.filter((item: string) =>
              data[this.config.rowKey] !== item[this.config.rowKey]);
            this.value.forEach((item: any, index: number) => {
              if (data[this.config.rowKey] === item[this.config.rowKey]) {
                this.value[index] = Object.assign(item, {
                  ...this.defaultValue[index],
                  isEdit: false
                });
              }
            });
            return;
          }
        }
        default:
          return;
      }
    }

    /**
     * 操作新增
     * @author tanxj
     * @param type edit|close|cancel
     * @param data 可批量或者单条
     * @param callback 回调
     */
    onHandleAdd(type: string, data?: any, callback?: Function) {
      switch (type) {
        case "add": {
          const params: any = {};
          ((this.$refs[`table${this.config.name}`] as any).columns || []).forEach((item: any) => {
            if (item.property) params[item.property] = "";
          });
          params[this.config.rowKey] = util.generateUUID();
          params.isEdit = true;
          params.isAdd = true;
          this.currentAddList.push(params);
          this.value.unshift(params);
          return;
        }
        case 'close': {
          data = (!(data instanceof Array)) ? [data[this.config.rowKey]] : data.map((dataItem: any) => dataItem[this.config.rowKey]);
          this.currentAddList = this.currentAddList.filter((item: string) => !data.includes(item[this.config.rowKey]));
          this.value.forEach((item: any, index: number) => {
            if (data.includes(item[this.config.rowKey])) {
              item.isEdit = false;
              item.isAdd = false;
              this.value.splice(index, 1, item);
            }
          });
          this.defaultValue = JSON.parse(JSON.stringify(this.value || []));
          return;
        }
        case 'cancel': {
          if (!data) {
            this.value.splice(0, this.currentAddList.length);
            this.currentAddList = [];
            return;
          } else {
            this.currentAddList = this.currentAddList.filter((item: string) =>
              data[this.config.rowKey] !== item[this.config.rowKey]);
            this.value.forEach((item: any, index: number) => {
              if (data[this.config.rowKey] === item[this.config.rowKey]) {
                this.value.splice(index, 1);
              }
            });
            return;
          }
        }
        default:
          return;
      }
    }

    /**
     * 获取当选复选框是否禁止
     * @author tanxj
     * @param row 复选框对应行
     * @param index 复选框对应行序号
     */
    getSelectable(row: any, index: number) {
      return !this.config.disabled;
    }

    /**
     * 选中checkbox操作
     * @author tanxj
     * @param selection 选中数据
     * @param row 选中行
     */
    handleSelect(selection: any, row: any) {
      this.config.onSelect &&
      this.config.onSelect(!!selection.find((item: any) => item[this.config.rowKey] === row[this.config.rowKey]), row);
    }

    /**
     * 选中所有
     * @author tanxj
     * @param selection 选中数据
     */
    handleSelectAll(selection: any) {
      this.config.onSelectAll && this.config.onSelectAll(selection);
    }

    /**
     * 多选操作
     * @author tanxj
     * @param data 多选的操作数据
     */
    handleSelectionChange(data: any) {
      this.config.selection = data;
    }

    /**
     * 选中操作
     * @author tanxj
     * @param row 当前行
     * @param oldCurrentRow 选中旧的行数据
     */
    handleCurrentChange(row: any, oldCurrentRow: any) {
      if (this.isCurrentLoading) return this.isCurrentLoading = false;
      if (this.config.isCurrentChange && !this.config.isCurrentChange(row, oldCurrentRow)) {
        this.isCurrentLoading = true;
        return (this.$refs[`table${this.config.name}`] as any).setCurrentRow(oldCurrentRow);
      }
      this.config.currentChange && this.config.currentChange(row);
      this.currentNode = row;
    }

    /**
     * 行点击事件
     * @author tanxj
     * @param row 点击行
     * @param column 点击列
     * @param event 原生事件
     */
    handleRowClick(row: any, column: any, event: any) {
      this.config.rowClick && this.config.rowClick(row, column, event);
    }
  }
</script>

<style lang="scss" scoped>
  .mu-table-content {
    position: relative;
  }
</style>
