<template>
  <el-form ref="form" @submit.native.prevent :label-width="realLabelWidth" :model="conditionConfig.model"
           :rules="realRules" class="x-condition" :validate-on-rule-change="false">
    <el-row>
      <el-col v-for="item in finalConditionList" :key="item.field" :span="item.span || 6">
        <!--:required="item.required"不在el-form-item中使用required属性，因为一旦使用该属性且为true,就会生成一个{required: true}的rule,不方便通过realRules来控制-->
        <x-form-item :class="item.labelClass" :label-width="item.labelWidth" :key="item.field" :label="item.label"
                     :prop="item.field" :iconRight="getIconRight(item)">
          <slot v-if="item.slot" :name="item.slot.name" :item="item"></slot>
          <template v-else>
            <el-input v-if="item.type==='input' || item.type === 'textarea'" :readonly="item.readonly || false"
                      @click.native="handleCommonClick(item)"
                      @focus="handleFocusChange(item, $event)" @blur="handleCommonBlur(item, $event)"
                      @keyup.enter.native="handleInputEnter(item, $event)" @clear="handleInputClear(item, $event)"
                      :disabled="isDisabled(item)"
                      :clearable="item.clearable || false" :placeholder="getPlaceholder(item, '请输入')"
                      v-model="conditionConfig.model[item.field]"
                      :maxlength="item.maxlength" :show-word-limit="item.showWordLimit"
                      @input="handleCommonChange(item, $event)"
                      :resize="item.resize" :autosize="item.autosize" :rows="item.rows" :type="item.type">
              <template v-if="item.suffixSlot" v-slot:suffix>
                <slot :name="item.suffixSlot.name" :item="item"></slot>
              </template>
            </el-input>
            <el-select v-if="item.type==='select' || item.type === 'multipleSelect'" class="x-condition-adjust-item"
                       @change="handleCommonChange(item, $event)"
                       :disabled="isReadonlyDisabled(item)" :clearable="item.clearable || false"
                       :multiple="item.type === 'multipleSelect'"
                       :placeholder="getPlaceholder(item, '请选择')" v-model="conditionConfig.model[item.field]">
              <el-option v-for="option in item.options" :key="option[item.props.key]"
                         :label="option[item.props.label]"
                         :value="option[item.props.value]" :disabled="isReadonlyDisabled(option)"></el-option>
            </el-select>
            <el-cascader v-if="item.type==='cascader'" @change="handleCommonChange(item, $event)"
                         :disabled="isReadonlyDisabled(item)" :clearable="item.clearable || false"
                         :placeholder="getPlaceholder(item, '请选择')" v-model="conditionConfig.model[item.field]"
                         :props="item.props" :options="item.options"
            ></el-cascader>
            <el-checkbox v-if="item.type ==='checkbox'" :true-label="item.trueLabel" :false-label="item.falseLabel"
                         :disabled="isReadonlyDisabled(item)"
                         v-model="conditionConfig.model[item.field]">{{item.text}}
            </el-checkbox>
            <el-checkbox-group v-if="item.type==='checkboxgroup'"
                               :disabled="item.readonly || item.disabled || conditionConfig.disabled || false"
                               v-model="conditionConfig.model[item.field]" @input="handleCommonChange(item, $event)">
              <el-checkbox v-for="checkboxItem in item.options" :key="checkboxItem.label"
                           :disabled="isReadonlyDisabled(checkboxItem)"
                           :label="checkboxItem.label">{{checkboxItem.text}}
              </el-checkbox>
            </el-checkbox-group>
            <el-radio-group v-if="item.type==='radio'" @change="handleCommonChange(item, $event)"
                            :disabled="isDisabled(item)"
                            v-model="conditionConfig.model[item.field]">
              <el-radio v-for="radio in item.options" :disabled="isReadonlyDisabled(radio)" :label="radio.label"
                        :key="radio.label">{{radio.text}}
              </el-radio>
            </el-radio-group>
            <el-date-picker v-if="item.type==='date'" @change="handleCommonChange(item, $event)" :format="item.format"
                            :picker-options="item.pickerOptions"
                            :value-format="item.valueFormat" class="x-condition-adjust-item"
                            :readonly="isReadonly(item)" :editable="isEditable(item)"
                            type="date" :disabled="isDisabled(item)" :clearable="item.clearable || false"
                            :placeholder="getPlaceholder(item, '请选择日期')"
                            v-model="conditionConfig.model[item.field]"></el-date-picker>
            <el-date-picker v-if="item.type==='year'" @change="handleCommonChange(item, $event)" :format="item.format"
                            :picker-options="item.pickerOptions"
                            :value-format="item.valueFormat" class="x-condition-adjust-item"
                            :readonly="isReadonly(item)" :editable="isEditable(item)"
                            type="year" :disabled="isDisabled(item)" :clearable="item.clearable || false"
                            :placeholder="getPlaceholder(item, '请选择年份')"
                            v-model="conditionConfig.model[item.field]"></el-date-picker>
            <el-date-picker v-if="item.type==='month'" @change="handleCommonChange(item, $event)" :format="item.format"
                            :picker-options="item.pickerOptions"
                            :value-format="item.valueFormat" class="x-condition-adjust-item"
                            :readonly="isReadonly(item)" :editable="isEditable(item)"
                            type="month" :disabled="isDisabled(item)" :clearable="item.clearable || false"
                            :placeholder="getPlaceholder(item, '请选择月份')"
                            v-model="conditionConfig.model[item.field]"></el-date-picker>
            <el-date-picker v-if="item.type==='datetime'" @change="handleCommonChange(item, $event)"
                            :picker-options="item.pickerOptions"
                            :format="item.format" :value-format="item.valueFormat" :readonly="isReadonly(item)"
                            :editable="isEditable(item)"
                            type="datetime" :disabled="isDisabled(item)" :clearable="item.clearable || false"
                            class="x-condition-adjust-item"
                            :placeholder="getPlaceholder(item, '请选择日期')"
                            v-model="conditionConfig.model[item.field]"></el-date-picker>
            <el-date-picker v-if="item.type==='daterange'" @change="handleCommonChange(item, $event)"
                            :picker-options="item.pickerOptions"
                            :format="item.format" :value-format="item.valueFormat" class="x-condition-adjust-item"
                            type="daterange" :disabled="isDisabled(item)" :clearable="item.clearable || false"
                            :editable="isEditable(item)"
                            :start-placeholder="getPlaceholder(item, '请选择开始日期')" :readonly="isReadonly(item)"
                            :end-placeholder="getPlaceholder(item, '请选择结束日期')"
                            v-model="conditionConfig.model[item.field]"></el-date-picker>
            <el-date-picker v-if="item.type==='datetimerange'" @change="handleCommonChange(item, $event)"
                            :picker-options="item.pickerOptions"
                            :format="item.format" :value-format="item.valueFormat" :readonly="isReadonly(item)"
                            type="datetimerange" :disabled="isDisabled(item)" :editable="isEditable(item)"
                            :clearable="item.clearable || false" class="x-condition-adjust-item"
                            :start-placeholder="getPlaceholder(item, '请选择开始日期')"
                            :end-placeholder="getPlaceholder(item, '请选择结束日期')"
                            v-model="conditionConfig.model[item.field]"></el-date-picker>
            <el-time-picker v-if="item.type==='time'" @change="handleCommonChange(item, $event)"
                            :picker-options="item.pickerOptions"
                            :value-format="item.valueFormat" :readonly="isReadonly(item)" :editable="isEditable(item)"
                            :disabled="isDisabled(item)" :clearable="item.clearable || false"
                            class="x-condition-adjust-item"
                            :placeholder="getPlaceholder(item, '请选择时间')"
                            v-model="conditionConfig.model[item.field]"></el-time-picker>
            <el-time-picker v-if="item.type==='timerange'" @change="handleCommonChange(item, $event)"
                            class="x-condition-adjust-item" :picker-options="item.pickerOptions"
                            :disabled="isDisabled(item)" :clearable="item.clearable || false" is-range
                            :editable="isEditable(item)"
                            :start-placeholder="getPlaceholder(item, '请选择开始时间')" :readonly="isReadonly(item)"
                            :end-placeholder="getPlaceholder(item, '请选择结束时间')"
                            v-model="conditionConfig.model[item.field]"></el-time-picker>
            <x-meta-search-list v-if="item.type === 'POP_ONE' || item.type === 'POP_MULTI'"
                                v-model="conditionConfig.model[item.field]" :data-code="item.dataCode"
                                :row-key="item.rowKey" :row-name="item.rowName"
                                :hasInput="item.hasInput"
                                :disabled="isDisabled(item)"
                                :append-to-body="item.appendToBody" :append-result="item.appendResult"
                                @click-icon="($event) => {handleClickIcon(item, $event)}"
                                :search-option-type="item.searchOptionType"
                                :preset-config="item.presetConfig"
                                :page-config="item.pageConfig"
                                :placeholder="getPlaceholder(item, undefined)"
                                @select="(data) => {handleSearchListSelect(item, data)}"
                                :mode="item.mode"></x-meta-search-list>
            <x-search-tree v-if="item.type==='TREE_POP_ONE' || item.type === 'TREE_POP_MULTI'"
                           v-model="conditionConfig.model[item.field]" :row-key="item.rowKey" :row-name="item.rowName"
                           :append-to-body="item.appendToBody || true" :append-result="item.appendResult"
                           @click-icon="($event) => {handleClickIcon(item, $event)}"
                           :search-option-type="item.searchOptionType" :data="item.data"
                           :preset-config="item.presetConfig" @select="(data) => {handleSearchTreeSelect(item, data)}"
                           :mode="item.mode" :placeholder="getPlaceholder(item, undefined)"></x-search-tree>
            <x-search-list v-if="item.type==='LIST_POP_ONE' || item.type === 'LIST_POP_MULTI'"
                           v-model="conditionConfig.model[item.field]" :row-key="item.rowKey" :row-name="item.rowName"
                           :append-to-body="item.appendToBody || true" :append-result="item.appendResult"
                           @click-icon="($event) => {handleClickIcon(item, $event)}" :columns="item.columns"
                           :search-option-type="item.searchOptionType" :conditionConfig="item.conditionConfig" :api="item.api"
                           :preset-query-params="item.presetQueryParams" @select="(data) => {handleSearchListSelect(item, data)}"
                           :mode="item.mode" :placeholder="getPlaceholder(item, undefined)"></x-search-list>
          </template>
          <template v-if="item.errorSlot" v-slot:[item.errorSlot.name]="scope">
            <slot :name="item.errorSlot.name" v-bind="scope"></slot>
          </template>
        </x-form-item>
      </el-col>
    </el-row>
    <el-row v-if="realDrawerConfig.show && realConditionList.length > 4">
      <el-col :span="24" class="x-condition-drawer-wrapper">
        <span class="x-condition-drawer" @click="drawerFlag = !drawerFlag">
            <span class="x-condition-drawer-icon"
                  :class="{'el-icon-caret-top': drawerFlag, 'el-icon-caret-bottom': !drawerFlag}"></span>
            <span class="x-condition-drawer-text"
                  v-text="drawerFlag ? realDrawerConfig.closeText : realDrawerConfig.openText"></span>
          </span>
      </el-col>
    </el-row>
  </el-form>
</template>

<script>
import {
  Row,
  Col,
  Form,
  Input,
  Select,
  Option,
  CheckboxGroup,
  Checkbox,
  Cascader,
  RadioGroup,
  Radio,
  DatePicker,
  TimePicker
} from 'element-ui';
import { XFormItem } from '@newpearl/ui';
import { getShowList } from '../../util/componentUtil';
import elementResizeDetector from 'element-resize-detector';
import { debounce } from '@newpearl/utils/lib/util';
import {
  FIELD_TYPE_CASCADER,
  FIELD_TYPE_CHECKBOX,
  FIELD_TYPE_DATE,
  FIELD_TYPE_DATERANGE,
  FIELD_TYPE_DATETIME,
  FIELD_TYPE_DATETIME_RANGE, FIELD_TYPE_LIST_POP_MULTI, FIELD_TYPE_LIST_POP_ONE, FIELD_TYPE_MONTH, FIELD_TYPE_MULTIPLE_SELECT,
  FIELD_TYPE_POP_MULTI,
  FIELD_TYPE_POP_ONE, FIELD_TYPE_SELECT, FIELD_TYPE_TREE_POP_MULTI, FIELD_TYPE_TREE_POP_ONE, FIELD_TYPE_YEAR
} from './x-condition-constant';
import * as appUtils from '../../util/appUtils';

export default {
  name: 'XCondition',
  components: {
    [Form.name]: Form,
    [XFormItem.name]: XFormItem,
    [Input.name]: Input,
    [Select.name]: Select,
    [Option.name]: Option,
    [CheckboxGroup.name]: CheckboxGroup,
    [Checkbox.name]: Checkbox,
    [Cascader.name]: Cascader,
    [RadioGroup.name]: RadioGroup,
    [Radio.name]: Radio,
    [DatePicker.name]: DatePicker,
    [TimePicker.name]: TimePicker,
    [Row.name]: Row,
    [Col.name]: Col
  },
  props: {
    conditionConfig: {
      type: Object,
      default() {
        return {
          /* model: {}, // form的model属性, 如果是checkbox-group对应的field,最好声明为一个空数组，如果np-search-list，就声明为null
            rules: {}, // 整个表单校验规则
            labelWidth: '100px', // 左边文字统一宽度,
            disabled: false,
            list: [
              {
                labelClass: {}, // label样式
                readonly: false, // 是否只读, radio, select cascader等组件没有readonly属性，这些组件的readonly会传入disabled中
                disabled: false, // 是否不可使用
                field: '', // 字段，与queryParams中的属性对应, form也是对应这个属性
                fieldCode: '', // 字段，主要用于配置化查询，譬如开始时间和结束时间，都是同一个fieldCode，但是不同的field
                label: '', // 左边的文字
                placeholder: '', // 提示文字
                type: 'input', // 类型，input,select,multipleSelect,cascader,radio,date,datetime,daterange,time,timerange,slot
                inputType: 'text', // 输入框的类型text textarea
                value: '', // 用于初始化model的值
                optionList: [], // select的选项 或者 使用el-checkboxgroup时的checkbox选项列表, radio-group[{text: '男',label: 'man'},{text: '女',label: 'women'}]
                text: '', // checkbox显示的文字，避免和el-form-item的label冲突
                props： {label, value, key} // 渲染el-option对应的字段
                enter: () => {},
                change: ({item, value}) => {},
                errorSlot: {
                  name: '' // 错误插槽的名称
                },
                editable, // el-date-picker的输入框是否可以编辑
                toQuery: false // 是否用于查询，除了false以外，undefined或者其它值都当作是true
              }
            ] */
        };
      }
    },
    drawerConfig: {
      type: Object,
      default() {
        return {
          show: false,
          openText: '展开',
          closeText: '收起'
        };
      }
    }
  },
  data() {
    return {
      // drawer状态，true表示展开，false表示收起
      drawerFlag: true
    };
  },
  methods: {
    /**
     * 部分没有readonly属性的组件，如果传人readonly也当作disabled处理
     * @param item
     * @return {boolean|*}
     */
    isReadonlyDisabled(item) {
      if (Object.prototype.hasOwnProperty.call(item, 'readonly') && item.readonly) {
        return item.readonly;
      }
      if (Object.prototype.hasOwnProperty.call(item, 'disabled') && item.disabled) {
        return item.disabled;
      }
      if (Object.prototype.hasOwnProperty.call(this.conditionConfig, 'disabled') && this.conditionConfig.disabled) {
        return this.conditionConfig.disabled;
      }
      return false;
    },
    isDisabled(item) {
      if (Object.prototype.hasOwnProperty.call(item, 'disabled') && item.disabled) {
        return item.disabled;
      }
      if (Object.prototype.hasOwnProperty.call(this.conditionConfig, 'disabled') && this.conditionConfig.disabled) {
        return this.conditionConfig.disabled;
      }
      return false;
    },
    isReadonly(item) {
      return item.readonly || false;
    },
    isEditable(item) {
      return item.editable || false;
    },
    getPlaceholder(item, defaultPlaceholder) {
      if (item.placeholder === undefined || item.placeholder === null) {
        return defaultPlaceholder;
      }
      return item.placeholder;
    },
    handleInputEnter(item, event) {
      if (!item.enter) {
        return;
      }
      item.enter({ item, event });
    },
    handleInputClear(item, event) {
      if (!item.clear) {
        return;
      }
      item.clear({ item, event });
    },
    handleCommonChange(item, value) {
      if (!item.change) {
        return;
      }
      item.change({ item, value });
    },
    handleCommonBlur(item, event) {
      if (!item.blur) {
        return;
      }
      item.blur({ item, event });
    },
    handleFocusChange(item, event) {
      if (!item.focus) {
        return;
      }
      item.focus({ item, event });
    },
    handleCommonClick(item) {
      if (!item.click) {
        return;
      }
      item.click({ item });
    },
    getFormInstance() {
      return this.$refs.form;
    },
    handleClickIcon(item, {flag, value}) {
      if (!item.clickIcon) {
        return;
      }
      item.clickIcon({ item, flag, value });
    },
    /**
     * @desc 表单校验，由于el-form返回的promise只是带有true/false, 因此这里增加返回的信息
     * @param callback
     * @return {promise}
     */
    validate (callback) {
      let resolveFn = null;
      let rejectFn = null;
      const promise = new Promise((resolve, reject) => {
        resolveFn = resolve;
        rejectFn = reject;
      });
      const $callback = (valid, invalidFields) => {
        callback && callback(valid, invalidFields);
        if (valid) {
          resolveFn();
        } else {
          const ruleError = new RuleError('请正确填写表单');
          // 加入label属性
          for (const key in invalidFields) {
            const invalidFieldArray = invalidFields[key];
            for (const item of this.conditionConfig.list) {
              if (item.field !== key) {
                continue;
              }
              for (const invalidField of invalidFieldArray) {
                invalidField.label = item.label;
              }
            }
          }
          ruleError.invalidFields = invalidFields;
          rejectFn(ruleError);
        }
      };
      // 空格在el-form的非空校验中通过，因此这里需要将前后空格去掉
      const model = this.conditionConfig.model;
      for (const key in model) {
        const value = model[key];
        if (appUtils.isString(value)) {
          model[key] = value.trim();
        }
      }
      this.$refs.form.validate($callback);
      return promise;
    },
    handleSearchListSelect(item, data) {
      if (!item.searchListSelect) {
        return;
      }
      item.searchListSelect({item, data, model: this.conditionConfig.model});
    },
    handleSearchTreeSelect(item, data) {
      if (!item.searchTreeSelect) {
        return;
      }
      item.searchTreeSelect({ item, data });
    },
    getIconRight(item) {
      if (item.iconRight) {
        return item.iconRight;
      }
      const type = item.type;
      if (type === FIELD_TYPE_POP_ONE || type === FIELD_TYPE_POP_MULTI || type === FIELD_TYPE_TREE_POP_ONE || type === FIELD_TYPE_TREE_POP_MULTI || type === FIELD_TYPE_LIST_POP_ONE || type === FIELD_TYPE_LIST_POP_MULTI) {
        return 25;
      }
    }
  },
  created() {
    const model = this.conditionConfig.model;
    const list = this.conditionConfig.list;
    for (const item of list) {
      const field = item.field;
      if (Object.hasOwnProperty.call(model, field)) {
        if (Object.prototype.hasOwnProperty.call(item, 'value')) {
          model[field] = item.value;
        }
        continue;
      }
      let value = '';
      switch (item.type) {
        case FIELD_TYPE_CHECKBOX:
          if (Object.prototype.hasOwnProperty.call(item, 'falseLabel')) {
            value = item.falseLabel;
          } else {
            value = false;
          }
          break;
        case FIELD_TYPE_POP_ONE:
        case FIELD_TYPE_DATE:
        case FIELD_TYPE_DATETIME:
        case FIELD_TYPE_YEAR:
        case FIELD_TYPE_MONTH:
          value = null;
          break;
        case FIELD_TYPE_DATERANGE:
        case FIELD_TYPE_DATETIME_RANGE:
        case FIELD_TYPE_POP_MULTI:
        case FIELD_TYPE_MULTIPLE_SELECT:
        case FIELD_TYPE_CASCADER:
          value = [];
          break;
        default:
          break;
      }
      if (Object.prototype.hasOwnProperty.call(item, 'value')) {
        value = item.value;
      }
      this.$set(model, field, value);
    }
  },
  mounted() {
    if (this.erd) {
      this.erd.uninstall(this.$el);
      this.erd = null;
    }
    this.erd = elementResizeDetector();
    this.erd.listenTo(this.$el, debounce((element) => {
      this.$emit('resize', { element, context: this });
    }, 250));
  },
  computed: {
    realDrawerConfig() {
      const defaultConfig = { show: false, openText: '展开', closeText: '收起' };
      if (!this.drawerConfig) {
        return defaultConfig;
      }
      return Object.assign(defaultConfig, this.drawerConfig);
    },
    realConditionList() {
      const list = getShowList(this.conditionConfig.list);
      // 对于type === 'select'的选项，如果没有设置props, 需要设置默认值
      for (const item of list) {
        const type = item.type;
        if (type === FIELD_TYPE_SELECT || type === FIELD_TYPE_MULTIPLE_SELECT) {
          if (!item.props) {
            item.props = { value: 'value', label: 'label', key: 'value' };
          }
        }
        // 如果是日期类型，没有pickerOptions属性就加上，如果有这个属性或者是null，就不处理
        if (!Object.hasOwnProperty.call(item, 'pickerOptions')) {
          if (type === FIELD_TYPE_DATE || type === FIELD_TYPE_DATETIME) {
            item.pickerOptions = {
              shortcuts: [
                {
                  text: '今天',
                  onClick(picker) {
                    picker.$emit('pick', new Date());
                  }
                },
                {
                  text: '昨天',
                  onClick(picker) {
                    const date = new Date();
                    date.setDate(date.getDate() - 1);
                    picker.$emit('pick', date);
                  }
                }
              ]
            };
          }
          if (type === FIELD_TYPE_DATERANGE || type === FIELD_TYPE_DATETIME_RANGE) {
            item.pickerOptions = {
              shortcuts: [
                {
                  text: '最近一周',
                  onClick(picker) {
                    const end = new Date();
                    const start = new Date();
                    start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
                    picker.$emit('pick', [start, end]);
                  }
                },
                {
                  text: '最近一个月',
                  onClick(picker) {
                    const end = new Date();
                    const start = new Date();
                    start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
                    picker.$emit('pick', [start, end]);
                  }
                }
              ]
            };
          }
        }
      }
      return list;
    },
    /**
     * 最终显示的查询条件，用户通过点击展开和收起按钮来控制finalConditionList的数量
     * 由于使用v-expand只能做到dom操作，无法做到根据条件的数量自动判断是否显示展开和收起按钮，另外对于多选框，当选中item的数量较多时，
     * 高度和相邻的条件会不一致，因此直接操作dom会相对麻烦
     * @return {array}
     */
    finalConditionList() {
      // 不显示展开/收起，或者展开状态，直接显示所有条件
      if (!this.realDrawerConfig.show || this.drawerFlag) {
        return this.realConditionList;
      }
      // 当realConditionList的数量小于5时，直接显示
      if (this.realConditionList.length < 5) {
        return this.realConditionList;
      }
      return this.realConditionList.slice(0, 4);
    },
    // realModel () {
    //   return this.conditionConfig.model || {};
    // },
    realRules() {
      return this.conditionConfig.rules || {};
    },
    realLabelWidth() {
      return this.conditionConfig.labelWidth || '80px';
    }
  },
  beforeDestroy() {
    if (this.erd) {
      this.erd.uninstall(this.$el);
      this.erd = null;
    }
  }
};
</script>
