/** * @author 冷 (https://github.com/LengYXin) * @email lengyingxin8966@gmail.com * @create date
2019-03-12 17:20:02 * @modify date 2019-03-12 17:20:02 * @desc 搜索条件 表单 */
<template>
  <a-form-model
    ref="formRef"
    :layout="layout"
    :rules="rules"
    :model="formState"
    :label-col="labelCol"
    :wrapper-col="wrapperCol"
    @submit.native.prevent
    @submit="onFinish(formState)"
    v-bind="$attrs"
    v-on="$listeners"
  >
    <a-row type="flex" :gutter="4">
      <slot />
      <slot name="more" v-if="moreOpen" />
      <a-col :offset="offset" :span="colProps.colSpan" style="text-align: right">
        <a-form-model-item :wrapper-col="{ span: 24 }">
          <!-- <div slot="label" style="opacity: 0;">搜索</div> -->
          <a-space align="center">
            <!-- 重置按钮 -->
            <a-button @click.stop.prevent="onReset" :loading="Pagination.loading">
              <i18n-t :keypath="$Enumlocales.action_reset" />
            </a-button>
            <!-- 搜索按钮 -->
            <a-button type="primary" html-type="submit" :loading="Pagination.loading">
              <i18n-t :keypath="$Enumlocales.action_search" />
            </a-button>
            <!-- 导出按钮 -->
            <a-button v-if="isExport" @click.stop.prevent="onExport" :loading="Pagination.loading">
              <a-icon type="cloud-download" />
              <i18n-t :keypath="$Enumlocales.action_export" />
            </a-button>
            <!-- 展开按钮 -->
            <a-button @click.stop.prevent="onMoreOpen" type="link" v-show="moreShow">
              <i18n-t
                :keypath="moreOpen ? $Enumlocales.action_retract : $Enumlocales.action_open"
              />
            </a-button>
          </a-space>
        </a-form-model-item>
      </a-col>
    </a-row>
  </a-form-model>
</template>

<script lang="ts">
import { BasesController, Regulars } from '@mamba/clients';
import lodash from 'lodash';
import { observer } from 'mobx-vue';
import moment from 'moment';
import { fromEvent, Subscription } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { Component, Emit, Inject, Prop, Provide, Ref, Vue } from 'vue-property-decorator';
export type IrangePicker = Array<{
  key: string;
  start: string;
  end: string;
  format?: string;
  showTime?: boolean;
}>;
// const CONFIG_SPAN_BREAKPOINTS = {
//   xs: 513,
//   sm: 513,
//   md: 785,
//   lg: 992,
//   xl: 1057,
//   xxl: Infinity,
// };
/** 配置表单列变化的容器宽度断点 */
const BREAKPOINTS = {
  vertical: [
    // [breakpoint, cols, layout]
    [513, 1, 'vertical'],
    [785, 2, 'vertical'],
    [1057, 2, 'horizontal'],
    [1352, 3, 'horizontal'],
    [1800, 3, 'horizontal'],
    [Infinity, 4, 'horizontal'],
  ],
  default: [
    [513, 1, 'vertical'],
    [701, 2, 'vertical'],
    [1062, 2, 'horizontal'],
    [1352, 3, 'horizontal'],
    [1800, 3, 'horizontal'],
    [Infinity, 4, 'horizontal'],
  ],
};
@observer
@Component({
  components: {},
})
export default class extends Vue {
  @Inject({ from: 'PageController', default: null })
  readonly InjectPageController: BasesController;
  /** 直接传递的 控制器 */
  @Prop({}) readonly PageController: BasesController;
  get Controller() {
    if (this.PageController) {
      return this.PageController;
    }
    return this.InjectPageController;
  }
  get Pagination() {
    return this.Controller.Pagination;
  }
  get ParamsOptions() {
    return this.Pagination.PaginationParams;
  }
  /** 表单状态 */
  @Inject() readonly formState;
  /** 表单 ref */
  @Ref('formRef') readonly formRef;
  /** 处理 时间 区间 key */
  @Prop() rangePicker: IrangePicker;
  /** 参数 合并到 query */
  @Prop({ default: true }) isMergeQuery: boolean;
  /** 表单 rules */
  @Prop({}) readonly rules;
  /** 替换默认的 BREAKPOINTS 配置 */
  @Prop({ type: Function }) readonly getBREAKPOINTS;
  @Prop({ default: () => ({ xs: 24, sm: 24, md: 7, lg: 7, xl: 7, xxl: 7 }) })
  labelCol;
  @Prop({
    default: () => ({ xs: 24, sm: 24, md: 15, lg: 15, xl: 15, xxl: 15 }),
  })
  wrapperCol;
  /** 告诉 field 使用 a-col */
  @Provide() colProps = {
    colItem: true,
    colSpan: 6,
  };
  layout: 'vertical' | 'horizontal' = 'vertical';
  offset = 0;
  // activeKey = 'search'
  // 更多条件
  moreOpen = false;
  // 首次加载
  firstFinish = true;
  ResizeEvent: Subscription;
  // 显示 展开更多按钮
  get moreShow() {
    if (lodash.isFunction(this.$slots.more)) {
      return this.$slots.more().filter((x) => x.tag).length;
    } else if (lodash.isArray(this.$slots.more)) {
      return this.$slots.more.filter((x) => x.tag).length;
    }
    return false;
  }
  /** 存在数据导出事件 */
  get isExport() {
    return lodash.hasIn(this, '_events.export');
  }
  get formEl(): HTMLFormElement {
    return this.$el as any;
  }

  /**
   * 处理时间区间 搜索 参数
   * @param values
   * @param back
   */
  onRangePickerValues(values) {
    values = lodash.cloneDeep(values);
    function valueOf(date: moment.Moment, format?) {
      if (format) {
        return date.format(format);
      }
      return date.valueOf();
    }
    lodash.map(this.rangePicker, (rp) => {
      lodash.update(values, rp.key, (val) => {
        let start: moment.Moment = lodash.head(val);
        let end: moment.Moment = lodash.last(val);
        if (start) {
          start = moment(Regulars.number.test(String(start)) ? lodash.toNumber(start) : start);
          lodash.set(
            values,
            rp.start,
            rp.showTime ? valueOf(start, rp.format) : valueOf(start.startOf('days'), rp.format),
          );
        }
        if (end) {
          end = moment(Regulars.number.test(String(end)) ? lodash.toNumber(end) : end);
          lodash.set(
            values,
            rp.end,
            rp.showTime ? valueOf(end, rp.format) : valueOf(end.endOf('days'), rp.format),
          );
        }
      });
    });
    return values;
  }
  /**
   * 默认的 分页参数
   */
  defaultParams() {
    return {
      [this.ParamsOptions.currentKey]: this.ParamsOptions.defaultCurrent,
      [this.ParamsOptions.pageSizeKey]: this.ParamsOptions.defaultPageSize,
    };
  }
  /**
   * 继承 参数
   */
  assignParams(reset = false) {
    // 初始化使用一次
    this.firstFinish = false;
    /** 重置 不需要 路由参数 */
    let keys = [this.ParamsOptions.currentKey, this.ParamsOptions.pageSizeKey];
    // 重置从第一页开始
    if (reset) {
      keys = lodash.drop(keys);
    }
    const query = lodash.pick(this.$route.query, keys);
    const values = lodash.assign(this.defaultParams(), query, this.formState);
    return values;
  }
  /**
   * 合并 参数到地址栏
   */
  toMergeQuery(values, omit = false) {
    if (this.isMergeQuery) {
      if (!omit) {
        values = lodash.mapValues(values, (item) => {
          if (lodash.isArray(item)) {
            return lodash.map(item, (x) => lodash.result(x, 'valueOf', x));
          }
          // momment 的 valueOf
          return lodash.result(item, 'valueOf', item);
        });
        // 分页参数和默认对等则清空
        if (
          lodash.eq(
            String(lodash.get(values, this.ParamsOptions.currentKey)),
            String(this.ParamsOptions.defaultCurrent),
          )
        ) {
          lodash.set(values, this.ParamsOptions.currentKey, null);
        }
        if (
          lodash.eq(
            String(lodash.get(values, this.ParamsOptions.pageSizeKey)),
            String(this.ParamsOptions.defaultPageSize),
          )
        ) {
          lodash.set(values, this.ParamsOptions.pageSizeKey, null);
        }
      }
      this.$MergeQuery(values, omit);
    }
  }
  /**
   * 查询事件 表单提交
   * @param values
   * @param query
   */
  @Emit('finish')
  onFinish(values) {
    values = this.assignParams(!this.firstFinish);
    this.toMergeQuery(values);
    return this.onRangePickerValues(values);
  }
  /**
   * 导出事件 获取的当前参数 排除分页
   */
  @Emit('export')
  onExport() {
    return lodash.omit(this.onRangePickerValues(this.assignParams()), [
      this.ParamsOptions.currentKey,
      this.ParamsOptions.pageSizeKey,
    ]);
  }
  /**
   * 重置按钮
   */
  @Emit('reset')
  async onReset() {
    await lodash.result(this.formRef, 'resetFields');
    await lodash.result(this.formRef, 'validateFields');
    const values = this.assignParams(true);
    this.toMergeQuery(values, true);
    return this.onRangePickerValues(values);
  }
  onMoreOpen() {
    this.moreOpen = !this.moreOpen;
    this.breakPoint();
    this.$nextTick(() => {
      dispatchEvent(new CustomEvent('resize'));
    });
  }
  /** 回填 url 数据 */
  backfillQuery() {
    if (this.isMergeQuery) {
      lodash.assign(this.formState, lodash.pick(this.$route.query, lodash.keys(this.formState)));
    }
  }
  /**
   * 计算 搜索按钮的位置
   */
  breakPoint() {
    let length = 0;
    // 默认搜索条件 x.type !== "symbol" 排除空格
    if (lodash.isFunction(this.$slots.default)) {
      length = this.$slots.default().filter((x) => x.tag).length;
    } else if (lodash.isArray(this.$slots.default)) {
      length = this.$slots.default.filter((x) => x.tag).length;
    }
    // 隐藏的搜索条件
    if (this.moreOpen) {
      if (lodash.isFunction(this.$slots.more)) {
        length += this.$slots.more().filter((x) => x.tag).length;
      }
      if (lodash.isArray(this.$slots.more)) {
        length += this.$slots.more.filter((x) => x.tag).length;
      }
    }
    // 这里取当前容器大小
    const width = this.$el.clientWidth; // window.innerWidth;
    const config = lodash.result(this, 'getBREAKPOINTS', BREAKPOINTS);
    const breakPoint: [number, number, string] = lodash.find<any>(config.vertical, (item) => {
      return width < (item[0] as number) + 16;
    });
    this.colProps.colSpan = 24 / breakPoint[1];
    this.layout = breakPoint[2] as any;
    const offset = (length * this.colProps.colSpan) % 24;
    switch (true) {
      // case offset === 0:
      //   this.offset = this.colProps.colSpan * (breakPoint[1] - 1);
      //   break;
      case this.colProps.colSpan + offset === 24:
        this.offset = 0;
        break;
      case this.colProps.colSpan + offset < 24:
        this.offset = 24 - (this.colProps.colSpan + offset);
        break;
      default:
        break;
    }
  }
  created() {}
  async mounted() {
    this.breakPoint();
    this.backfillQuery();
    // this.onFinish(this.assignParams(), false);

    this.ResizeEvent = fromEvent(window, 'resize')
      .pipe(debounceTime(200))
      .subscribe(this.breakPoint);
    try {
      await this.formRef.validate();
      this.formRef.onSubmit();
    } catch (error) {}
  }
  unmounted() {
    this.ResizeEvent && this.ResizeEvent.unsubscribe();
    this.ResizeEvent = undefined;
  }
}
</script>
<style lang="less">
.w-filter-collapse {
  .ant-collapse-content-box {
    @media screen and (min-width: 785px) {
      padding: 16px 8px 0 8px !important;
    }
  }
  margin-bottom: 8px !important;
}
</style>
