<style lang="scss">
@import './index.scss';
</style>

<template>
  <div class="z-schema-page">
    <!-- 筛选组件 -->
    <div v-if="schema.filter" class="z-schema-page__filter">
      <z-schema-filter :schema="schema.filter" :value="valueFilter" @input="e => $emit('update:value-filter', e)" :loading="loading" @search="onSearch">
        <template v-for="item in getSlotKeys('filter-')" #[item.name]="slotScope">
          <slot :name="item.slot" v-bind="{ ..._slotScope, ...slotScope }"></slot>
        </template>
      </z-schema-filter>
    </div>
    <!-- 按钮区 -->
    <div v-if="schema.action !== false" class="z-schema-page__action">
      <slot name="action" v-bind="_slotScope">
        <el-button :size="_size" type="primary" @click="openNew">新增</el-button>
        <el-button :size="_size" plain :disabled="selection.length === 0" @click="onDeleteMultiple(selection)">删除</el-button>
        <slot name="action-button" v-bind="_slotScope"></slot>
      </slot>
    </div>
    <!-- 表格内容 -->
    <div v-if="schema.table" class="z-schema-page__table">
      <z-schema-table :schema="schema.table" v-model="tableData" v-loading="loading" @selection-change="onTableSelectionChange">
        <template #left>
          <el-table-column v-if="schema.selectable !== false" type="selection" width="40" align="center"></el-table-column>
        </template>
        <template v-for="item in getSlotKeys('table-')" #[item.name]="slotScope">
          <slot :name="item.slot" v-bind="{ ..._slotScope, ...slotScope }"></slot>
        </template>
        <slot v-if="schema.operation !== false" name="operation" v-bind="_slotScope">
          <el-table-column v-bind="{ label: '操作', width: '90', align: 'center', ...(schema.operation || {}) }">
            <template #default="{ row, column, $index }">
              <div class="z-schema-page__table-operation">
                <slot name="operation-left" v-bind="{ ..._slotScope, row, column, $index }"></slot>
                <slot name="operation-button" v-bind="{ ..._slotScope, row, column, $index }"></slot>
                <el-button type="text" icon="el-icon-edit" title="编辑" @click="openEdit(row)"></el-button>
                <el-popconfirm confirm-button-text="确定" cancel-button-text="取消" title="确定删除吗？" placement="top" @confirm="onDelete([row])">
                  <el-button slot="reference" type="text" icon="el-icon-delete" title="删除"></el-button>
                </el-popconfirm>
                <slot name="operation-right" v-bind="{ ..._slotScope, row, column, $index }"></slot>
              </div>
            </template>
          </el-table-column>
        </slot>
      </z-schema-table>
    </div>
    <!-- 底部区域 -->
    <div class="z-schema-page__footer">
      <slot name="pagination" v-bind="_slotScope">
        <div v-if="selection.length > 0" class="selection-info">
          <span>已选中</span>
          <span class="num">{{ selection.length }}</span>
          <span>项</span>
        </div>
        <!-- 分页器 -->
        <el-pagination
          v-if="schema.pagination !== false"
          @size-change="onSizeChange"
          @current-change="onCurrentChange"
          :current-page="currentPage"
          :page-sizes="pageSizes"
          :page-size="pageSize"
          :layout="layout"
          :total="total"
        >
        </el-pagination>
      </slot>
    </div>
    <el-dialog :title="elDialogTitle" :visible.sync="visible" v-bind="_dialogProps" @update:visible="onVisibleUpdate" @close="onDialogClose" @closed="onDialogClosed">
      <template v-if="elDialogRender">
        <slot v-if="getSlot(`dialog-${elDialogType}`)" :name="`dialog-${elDialogType}`" v-bind="_slotScope"></slot>
        <div v-else v-loading="dialogLoading">
          <template v-if="['new', 'edit'].includes(elDialogType)">
            <z-schema-form
              :key="`form-${elDialogType}`"
              ref="form"
              :value="valueForm"
              @input="e => $emit('update:value-form', e)"
              :schema="schema.form"
              @submit="onFormSubmit"
              @cancel="closeDialog"
            >
              <template v-for="item in getSlotKeys('form-')" #[item.name]="slotScope">
                <slot :name="item.slot" v-bind="{ ..._slotScope, ...slotScope }"></slot>
              </template>
              <template #footer="{ submit, cancel }">
                <div style="text-align: center">
                  <el-button :size="_size" type="primary" @click="submit" :loading="submitting">确定</el-button>
                  <el-button :size="_size" plain @click="cancel">取消</el-button>
                </div>
              </template>
            </z-schema-form>
          </template>
          <template v-else-if="elDialogType === 'detail'">
            <z-schema-form key="form-detail" ref="form" v-model="detail" :schema="schema.detail || detailSchema">
              <template v-for="item in getSlotKeys('detail-')" #[item.name]="slotScope">
                <slot :name="item.slot" v-bind="{ ..._slotScope, ...slotScope }"></slot>
              </template>
            </z-schema-form>
          </template>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<script>
import { cloneDeep, get } from '../utils';
import { filterout } from '../utils/schema';
import MIX_ORGIN from '../mixins/origin';

let propsMap = {};

const setKeysDefault = function(keys, value) {
  keys.reduce(function(result, current) {
    result[current] = value;
    return result;
  }, propsMap);
};
setKeysDefault(['value-filter', 'value-form', 'value-detail'], {
  type: Object,
  default() {
    return {};
  },
});
setKeysDefault(['value-table'], {
  type: Array,
  default() {
    return [];
  },
});
setKeysDefault(['size', 'dialogTitle', 'dialogType'], String);
setKeysDefault(['dialogVisible', 'auto'], Boolean);
setKeysDefault(['api-search', 'api-submit', 'api-new', 'api-edit', 'api-get', 'api-detail', 'api-delete'], Function);

export default {
  name: 'SchemaPage',
  mixins: [MIX_ORGIN],
  props: {
    ...propsMap,
    schema: {
      type: Object,
      required: true,
    },
  },
  data() {
    return {
      selection: [],
      // 分页参数
      currentPage: get(this.schema, 'pagination.currentPage') || 1,
      pageSizes: get(this.schema, 'pagination.pageSizes') || [10, 20, 50, 100],
      pageSize: get(this.schema, 'pagination.pageSize') || 10,
      layout: get(this.schema, 'pagination.layout') || 'total, sizes, prev, pager, next, jumper',
      total: get(this.schema, 'pagination.total') || 0,
      visible: this.dialogVisible,
      elDialogRender: true,
      elDialogType: this.dialogType || 'none',
      elDialogTitle: this.dialogTitle || '',
      elDialogProps: {},
      detailSchema: filterout(cloneDeep(this.schema.form), ['is', 'rules']),
      detail: this.valueDetail || {},
      tableData: this.valueTable || [],
      loading: false,
      submitting: false,
      dialogLoading: false,
    };
  },
  created() {
    if (this.auto || this.schema.auto) {
      this.onSearch();
    }
    console.log(this);
  },
  watch: {
    detailValue(val) {
      this.detail = val;
    },
    detail(val) {
      this.$emit('update:value-detail', val);
    },
    dialogVisible(val) {
      this.visible = val;
    },
    visible(val) {
      this.$emit('update:dialog-visible', val);
    },
    dialogType(val) {
      this.elDialogType = val;
    },
    elDialogType(val) {
      this.$emit('update:dialog-type', val);
    },
    dialogTitle(val) {
      this.elDialogTitle = val;
    },
    elDialogTitle(val) {
      this.$emit('update:dialog-title', val);
    },
    valueTable(val) {
      this.tableData = val;
    },
    tableData(val) {
      this.$emit('update:value-table', val);
    },
  },
  computed: {
    slotKeys() {
      return Object.keys(this.$scopedSlots);
    },
    _size() {
      return this.size || get(this.schema, 'props.size') || (this.$ELEMENT || {}).size || 'small';
    },
    _slotScope() {
      const properties = ['selection', 'currentPage', 'pageSizes', 'pageSize', 'layout', 'total', 'loading'];
      const methods = ['search', 'onSearch', 'onDelete', 'onDeleteMultiple', 'openNew', 'openEdit', 'openDetail', 'openDialog', 'closeDialog'];
      const defaultScope = {
        size: this._size,
      };
      return [...properties, ...methods].reduce((result, current) => {
        result[current] = this[current];
        return result;
      }, defaultScope);
    },
    _dialogProps() {
      return {
        'destroy-on-close': true,
        'append-to-body': true,
        'lock-scroll': false,
        'close-on-click-modal': false,
        ...(this.schema.dialog || {}),
        ...this.elDialogProps,
      };
    },
  },
  methods: {
    getSlot(name) {
      return this.$slots[name] || this.$scopedSlots[name];
    },
    getSlotKeys(prefix) {
      return this.slotKeys.reduce((result, current) => {
        if (current.indexOf(prefix) === 0) {
          result.push({
            slot: current,
            name: current.substring(prefix.length),
          });
        }
        return result;
      }, []);
    },
    // 空Promise
    emptyPromise() {
      return new Promise(resolve => resolve());
    },
    // 查询
    search() {
      if (!this.loading) {
        this.loading = true;
        const params = {
          ...this.valueFilter,
          currentPage: this.currentPage,
          pageSize: this.pageSize,
        };
        const searchAPI = this.apiSearch || this.emptyPromise;
        searchAPI(params)
          .then(res => {
            const response = res || {};
            this.tableData = response[this.schema.listKey || 'list'] || [];
            this.total = response[this.schema.totalKey || 'total'] || 0;
          })
          .finally(() => {
            this.loading = false;
          });
      }
    },
    // 重置查询
    onSearch() {
      this.currentPage = 1;
      this.search();
    },
    // 表单提交
    onFormSubmit(value) {
      if (this.$listeners['form-submit']) {
        this.$emit('form-submit', value);
      } else {
        this.submitting = true;
        let submitAPI = this.apiSubmit || this.emptyPromise;
        if (this.elDialogType === 'new') {
          submitAPI = this.apiNew || this.apiSubmit || this.emptyPromise;
        } else if (this.elDialogType === 'edit') {
          submitAPI = this.apiEdit || this.apiSubmit || this.emptyPromise;
        }
        submitAPI(this.valueForm, { type: this.elDialogType })
          .then(() => {
            if (this.$listeners['submit-success']) {
              this.$emit('submit-success');
            } else {
              this.$message.success('保存成功');
            }
            this.closeDialog();
            this.search();
          })
          .finally(() => {
            this.submitting = false;
          });
      }
    },
    // 打开新增
    openNew() {
      this.openDialog('new', '新增');
    },
    // 打开编辑
    openEdit(row) {
      this.dialogLoading = true;
      this.openDialog('edit', '编辑');
      const getRow = () =>
        new Promise(resolve => {
          resolve(cloneDeep(row));
        });
      const getAPI = this.apiGet || getRow;
      getAPI(cloneDeep(row))
        .then(result => {
          if (result) {
            this.$emit('update:value-form', result);
          }
        })
        .finally(() => {
          this.dialogLoading = false;
        });
    },
    // 打开详情
    openDetail(row) {
      this.dialogLoading = true;
      this.openDialog('detail', '详情');
      const getRow = () =>
        new Promise(resolve => {
          resolve(cloneDeep(row));
        });
      const getAPI = this.apiDetail || this.apiGet || getRow;
      getAPI(cloneDeep(row))
        .then(result => {
          if (result) {
            this.detail = result;
            this.$emit('update:value-detail', result);
          }
        })
        .finally(() => {
          this.dialogLoading = false;
        });
    },
    // 打开弹出框
    openDialog(type, title, config) {
      this.elDialogRender = true;
      this.elDialogType = type;
      this.elDialogTitle = title;
      this.elDialogProps = config || {};
      this.visible = true;
      this.$emit('dialog-change', type);
    },
    // 关闭弹出框
    closeDialog() {
      this.visible = false;
    },
    // 弹出框显示状态更新
    onVisibleUpdate(visible) {
      this.visible = visible;
    },
    // 弹出框关闭
    onDialogClose() {
      this.elDialogType = 'none';
      this.$emit('dialog-change', 'none');
    },
    // 弹出框关闭动画结束
    onDialogClosed() {
      if (this.$refs.form) {
        this.$refs.form.resetFields();
      }
      this.elDialogRender = false;
      this.elDialogProps = {};
      this.$emit('update:value-form', this.cloneDeep(this.originProps).valueForm);
      this.$emit('update:value-detail', this.cloneDeep(this.originProps).detailValue);
    },
    // 表格选中状态
    onTableSelectionChange(selection, type) {
      this.selection = selection;
    },
    // 分页-每页个数
    onSizeChange(val) {
      this.pageSize = val;
      this.currentPage = 1;
      this.$nextTick(this.search);
    },
    // 分页-当前页数
    onCurrentChange(val) {
      this.currentPage = val;
      this.$nextTick(this.search);
    },
    // 删除数据
    onDelete(selection) {
      const loading = this.$loading({
        lock: true,
        text: '处理中',
        spinner: 'el-icon-loading',
        customClass: 'z-loading-toast',
        background: 'rgba(0, 0, 0, 0)',
      });
      const deleteAPI = this.apiDelete || this.emptyPromise;
      deleteAPI(selection)
        .then(() => {
          this.search();
          if (this.$listeners['delete-success']) {
            this.$emit('delete-success');
          } else {
            this.$message.success('删除成功');
          }
        })
        .finally(() => {
          loading.close();
        });
    },
    // 批量删除
    onDeleteMultiple(selection) {
      this.$confirm(`是否删除这 [${selection.length}] 项?`, '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      })
        .then(() => {
          this.onDelete(selection);
        })
        .catch(() => {});
    },
  },
};
</script>
