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

<template>
  <div class="zee-scheme">
    <div v-if="$scopedSlots.header || $slots.header" class="zee-scheme__header">
      <slot name="header" :filterModel="filterModel" v-bind="_slotScope"></slot>
    </div>
    <div class="zee-scheme__filter">
      <z-filter v-if="filter" :value="_filterModel" :list="listMap.filter | noRulesFilter" :size="size" @input="onFilterInput" @search="search" :loading="loading"></z-filter>
    </div>
    <div v-if="action" class="zee-scheme__action">
      <slot v-if="hadSlot('action')" name="action" v-bind="_slotScope"></slot>
      <template v-else>
        <el-button :size="size" type="primary" @click="openNew">新增</el-button>
        <slot name="button" :size="size" v-bind="_slotScope"></slot>
      </template>
    </div>
    <div class="zee-scheme__table">
      <z-table
        ref="table"
        v-model="tableData"
        v-loading="loading"
        :list="listMap.table"
        :tableProps="{ border: true, 'row-key': 'id', ...tableProps }"
        :size="size"
        @selection-change="onTableSelectionChange"
        @selection="onTableSelection"
      >
        <slot></slot>
        <template #column-end>
          <el-table-column prop="$operation" label="操作" :width="140" fixed="right">
            <div class="zee-scheme__table-operation" slot-scope="slotScope">
              <el-button type="text" icon="el-icon-edit" title="编辑" @click="openEdit(slotScope)"></el-button>
              <el-popconfirm confirmButtonText="确定" cancelButtonText="取消" title="确定删除吗？" placement="top" @onConfirm="handleDelete(slotScope)">
                <el-button slot="reference" type="text" icon="el-icon-delete" title="删除"></el-button>
              </el-popconfirm>
            </div>
          </el-table-column>
        </template>
      </z-table>
    </div>
    <div v-if="pagination" class="zee-scheme__footer">
      <div v-if="selection.length > 0" class="selection-info">
        <span>已选中</span>
        <span class="num">{{ selection.length }}</span>
        <span>项</span>
        <el-popconfirm confirmButtonText="确定" cancelButtonText="取消" title="确定清除吗？" placement="top" @onConfirm="clearSelection">
          <el-button slot="reference" :size="size" type="text">清除</el-button>
        </el-popconfirm>
      </div>
      <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="currentPage"
        :page-sizes="pageSizes"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total"
      >
      </el-pagination>
    </div>
    <el-dialog :visible.sync="dialogVisible" :title="dialogTitle" destroy-on-close append-to-body :lock-scroll="false" :close-on-click-modal="false" @closed="onDialogClosed">
      <div v-loading="dialogLoading">
        <slot v-if="hadSlot('dialog-title')" slot="title" name="dialog-title" :dialogType="dialogType" v-bind="_slotScope"></slot>
        <template v-if="dialogRender">
          <slot v-if="hadSlot(`dialog-${dialogType}`)" :name="`dialog-${dialogType}`" :model="_formModel" v-bind="_slotScope"></slot>
          <z-form v-else ref="form" :value="_formModel" :list="listMap.form" label-width="80px" :span="12" @input="onFormInput" @validate="onFormValidate"></z-form>
          <div class="zee-scheme__dialog-button" v-if="['new', 'edit'].includes(dialogType)">
            <el-button :size="size" type="primary" @click="handleConfirm" :loading="submitting">确定</el-button>
            <el-button :size="size" plain @click="closeDialog">取消</el-button>
          </div>
        </template>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { cloneDeep } from '../_utils';
import { clear } from '../_utils/param';

export default {
  name: 'Scheme',
  props: {
    list: Array,
    filter: {
      type: Boolean,
      default: true,
    },
    action: {
      type: Boolean,
      default: true,
    },
    pagination: {
      type: Boolean,
      default: true,
    },
    size: {
      type: String,
      default: 'mini',
    },
    tableProps: {
      type: Object,
      default: () => ({}),
    },
    formModel: Object,
    filterModel: Object,
    auto: Boolean,
    realSelection: Boolean,
    /* 模板API */
    url: String, // 请求地址
    http: [Function, Promise], // http库
    /* 自定义API */
    searchApi: Function, // 搜索
    submitApi: Function, // 提交
    addApi: Function, // 新增
    modifyApi: Function, // 修改
    getApi: Function, // 查询详情
    deleteApi: Function, // 删除
    alias: Object, // 别名配置
  },
  data() {
    return {
      filterForm: {},
      editForm: {},
      dialogVisible: false,
      dialogRender: true,
      dialogType: 'none',
      dialogLoading: false,
      dialogTitle: '',
      currentPage: 1,
      pageSize: 10,
      total: 0,
      pageSizes: [10, 20, 50],
      tableData: [],
      submitting: false,
      loading: false,
      selection: [],
    };
  },
  created() {
    if (this.auto) {
      this.search();
    }
  },
  filters: {
    // 无规则过滤器，过滤掉筛选条件表单中的必填规则等
    noRulesFilter(val = []) {
      let list = cloneDeep(val);
      const clearRules = list => {
        list.forEach(item => {
          if (item.list) {
            clearRules(item.list);
          } else {
            delete item.rules;
          }
        });
      };
      clearRules(list);
      return list;
    },
  },
  computed: {
    listMap() {
      // 默认作用域
      const LIST_SPACE = ['filter', 'form', 'table'];
      const array = {
        filter: [], // 筛选
        form: [], // 表单
        table: [], // 表格
      };
      this.list.forEach(item => {
        // 可以在列表中通过include或exclude设置当前配置的作用域
        const { include = LIST_SPACE, exclude = [] } = item;
        // 判断include
        let _inclue = [];
        if (include instanceof String || typeof include === 'string') {
          _inclue = [include];
        } else if (include instanceof Array && typeof include === 'object') {
          _inclue = include;
        }
        // 判断exclude转换为include的情况
        let _exclude_include = [];
        if (exclude instanceof String || typeof exclude === 'string') {
          _exclude_include = LIST_SPACE.filter(item => item !== exclude);
        } else if (exclude instanceof Array && typeof exclude === 'object') {
          _exclude_include = LIST_SPACE.filter(item => !exclude.includes(item));
        }
        // 作用域交集
        const _intersection = _inclue.filter(v => _exclude_include.includes(v));
        // 返回改配置项的作用域
        const _list_space = cloneDeep(_intersection);
        // 将配置项按需分配至各作用域下
        _list_space.forEach(name => {
          array[name].push({ ...item, ...(item[name] || {}) });
        });
      });
      return array;
    },
    _filterModel() {
      return this.filterModel || this.filterForm || {};
    },
    _formModel() {
      return this.formModel || this.editForm || {};
    },
    _slotScope() {
      return {
        openDialog: this.openDialog,
        closeDialog: this.closeDialog,
      };
    },
    _alias() {
      const alias = this.alias;
      const zAlias = this.zAlias;
      if (alias && zAlias) {
        return { ...zAlias, ...alias };
      }
      return this.alias || this.zAlias || {};
    },
  },
  methods: {
    // 空Promise
    emptyPromise() {
      return new Promise(resolve => resolve());
    },
    // 设置表格选中行
    toggleRowSelection() {
      this.tableData.forEach(row => {
        if (this.selection.find(item => item.id === row.id)) {
          this.$refs.table && this.$refs.table.toggleRowSelection(row);
        }
      });
    },
    // 表格选中状态
    onTableSelectionChange(selection, type) {
      if (this.realSelection) {
        if (type === 'check') {
          const result = this.selection || [];
          selection.forEach(item => {
            if (!result.find(i => i.id === item.id)) {
              result.push(item);
            }
          });
          this.selection = result;
        } else if (type === 'uncheck') {
          selection.forEach(i => {
            this.selection = this.selection.filter(item => item.id !== i.id);
          });
        }
      }
    },
    // 表格选中
    onTableSelection(selection) {
      if (!this.realSelection) {
        this.selection = selection;
      }
    },
    // 清除表格选中
    clearSelection() {
      this.$refs.table && this.$refs.table.clearSelection();
      this.selection = [];
    },
    // 内置搜索接口
    _searchAPI(params) {
      if (this.url && (this.http || this.zHttp)) {
        const _http = this.http || this.zHttp;
        return _http({ url: `${clear(this.url)}/${this._alias.pageUrl || 'page'}`, params });
      }
      return undefined;
    },
    // 搜索
    async search() {
      this.loading = true;
      const params = {
        ...this._filterModel,
        currentPage: this.currentPage,
        pageSize: this.pageSize,
      };
      const searchAPI = this.searchApi || this._searchAPI || this.emptyPromise;
      await searchAPI(params)
        .then(res => {
          const response = res || {};
          this.tableData = response[this._alias.list || 'list'] || [];
          this.total = response[this._alias.total || 'total'] || 0;
          this.$nextTick(this.toggleRowSelection);
        })
        .catch(() => {
          this.$message.error('查询失败');
        });
      this.loading = false;
    },
    // 更新筛选model
    onFilterInput(val) {
      this.filterForm = val || {};
      this.$emit('update:filterModel', val || {});
    },
    // 更新表单model
    onFormInput(val) {
      this.editForm = val || {};
      this.$emit('update:formModel', val || {});
    },
    // 内置新增保存接口
    _addAPI(data) {
      if (this.url && (this.http || this.zHttp)) {
        const _http = this.http || this.zHttp;
        return _http({ url: `${clear(this.url)}/${this._alias.addUrl || 'add'}`, method: 'post', data });
      }
      return undefined;
    },
    // 内置修改保存接口
    _modifyAPI(data) {
      if (this.url && (this.http || this.zHttp)) {
        const _http = this.http || this.zHttp;
        return _http({ url: `${clear(this.url)}/${this._alias.modifyUrl || 'modify'}`, method: 'post', data });
      }
      return undefined;
    },
    // 表单提交且通过校验
    async onFormValidate(valid, model) {
      if (valid) {
        this.submitting = true;
        let submitAPI = this.submitApi || this.emptyPromise;
        if (this.dialogType === 'new') {
          submitAPI = this.addApi || this.submitApi || this._addAPI || this.emptyPromise;
        } else if (this.dialogType === 'edit') {
          submitAPI = this.modifyApi || this.submitApi || this._modifyAPI || this.emptyPromise;
        }
        submitAPI(model, { type: this.dialogType })
          .then(() => {
            this.$message.success('保存成功');
            this.closeDialog();
          })
          .catch(() => {
            this.$message.error('保存失败');
          })
          .finally(() => {
            this.submitting = false;
          });
      }
    },
    // 表单按钮确定
    handleConfirm() {
      this.$refs.form && this.$refs.form.validate();
    },
    // 表单按钮取消
    handleCancel() {
      this.closeDialog();
    },
    // 查询是否有某个插槽
    hadSlot(name) {
      return !!this.$slots[name] || !!this.$scopedSlots[name];
    },
    // 打开新增弹出框
    openNew() {
      this.openDialog('new', '新增');
    },
    // 内置查询详情接口
    _getAPI(row) {
      if (this.url && (this.http || this.zHttp)) {
        const _http = this.http || this.zHttp;
        const _getKey = this._alias.getKey || this._alias.primaryKey || 'id';
        const _resultKey = this._alias.result || 'result';
        return _http({ url: `${clear(this.url)}/${this._alias.getUrl || 'queryById'}`, params: { [_getKey]: row[_getKey] } }).then(response => response[_resultKey] || {});
      }
      return undefined;
    },
    // 打开编辑弹出框
    openEdit({ row }) {
      this.dialogLoading = true;
      this.openDialog('edit', '编辑');
      const getRow = () =>
        new Promise(resolve => {
          resolve({ ...row });
        });
      const getAPI = this.getApi || this._getAPI || getRow;
      getAPI(row)
        .then(result => {
          this.editForm = { ...result };
        })
        .finally(() => {
          this.dialogLoading = false;
        });
    },
    // 内置删除接口
    _deleteAPI(keys) {
      if (this.url && (this.http || this.zHttp)) {
        const _http = this.http || this.zHttp;
        return _http({ url: `${clear(this.url)}/${this._alias.modifyUrl || 'delete'}`, method: 'post', data: keys });
      }
      return undefined;
    },
    // 删除
    handleDelete({ row, $index }) {
      const loading = this.$loading({
        text: '处理中',
        spinner: 'el-icon-loading',
        background: 'rgba(255, 255, 255, 0.5)',
      });
      const deleteAPI = this.deleteApi || this._deleteAPI || this.emptyPromise;
      const _deleteKey = this._alias.deleteKey || this._alias.primaryKey || 'id';
      const keys = [row[_deleteKey]];
      deleteAPI(keys)
        .then(() => {
          this.search();
          this.$message.success('删除成功');
        })
        .finally(() => {
          loading.close();
        });
    },
    // 打开弹出框
    openDialog(type, title) {
      this.dialogVisible = true;
      this.dialogRender = true;
      this.dialogType = type;
      this.dialogTitle = title;
    },
    // 关闭弹出框
    closeDialog() {
      this.dialogVisible = false;
    },
    // 清空表单
    clearEditForm() {
      this.editForm = {};
    },
    // 弹出框关闭动画结束
    onDialogClosed() {
      this.clearEditForm();
      this.dialogRender = false;
      this.dialogType = 'none';
    },
    // 分页-每页个数
    handleSizeChange(val) {
      this.pageSize = val;
      this.currentPage = 1;
      this.$nextTick(this.search);
    },
    // 分页-当前页数
    handleCurrentChange(val) {
      this.currentPage = val;
      this.$nextTick(this.search);
    },
  },
};
</script>
