<!--
 * @Description: In User Settings Edit
 * @Author: your name
 * @Date: 2019-10-10 14:46:44
 * @LastEditTime: 2019-11-18 15:16:46
 * @LastEditors: Please set LastEditors
 -->
<template>
  <div class="zte-form" :class="{ 'border': border && disabled }">
    <el-form
      :id="id || 'form'"
      :size="size"
      v-show="currentInfo.length"
      :model="currentForm"
      :rules="currentRules"
      ref="ruleForm"
      :label-position="labelPosition || (isCn ? 'right' : 'top')"
      :label-width="labelWidth * (isLanguage ? 1.2 : 1) + 'px'"
      v-bind="$attrs"
    >
      <template v-for="info of currentInfo.filter(({ show }) => show !== false)">
        <div
          v-if="info.title"
          :key="info.code"
          :class="info.code"
          class="group-title">
          {{ $t(info.label) }}
        </div>
        <el-form-item
          v-else
          :key="info.code"
          :class="info.code"
          :style="'width: ' + widthPercent * (info.grow || 1) + '%;'"
          :label="$t(info.label)"
          :prop="info.code"
        >
          <zte-item-range
            :ex-id="`${id || 'form'}_range`"
            v-if="info.range"
            :disabled="disabled"
            :props="info"
            :form="currentForm"
            :same="info.same"
          >
          </zte-item-range>
          <zte-item
            v-else
            :ex-id="`${id || 'form'}`"
            :props="info"
            :disabled="disabled"
            v-model="currentForm[info.code]"
            :show="currentForm[info.showLabel]"
            :default-value="currentForm[info.defaultCode]"
            :params="Object.assign({}, info.params, params[info.code])"
            :prevcode="_prevcode[info.code]"
            @prev-need="warningPrev"
            @show-change="showChange"
            @picker-change="pickerChange"
            @single-picker-change="singlePickerChange"
            @sum-to-form-filed="sumToFormFiled"
            @enter="debounceSubmit"
            @tree-pick-change="treeChange"
            @select-label-change="setLabel"
            @search-change="handleSearchChange"
            @select-change="handlerSelectChange($event, info)"
            @check-change="handlerCheckChange($event, info)"
            @range-change="handlerRangeChange($event, info)">
            <template #[info.code]>
              <slot :name="info.code" :info="info" :form="currentForm"></slot>
            </template>
          </zte-item>
        </el-form-item>
      </template>
      <!-- 隐藏域input解决浏览器默认提交bug -->
      <input type="text" style="display: none" v-if="currentInfo.length === 1">
      <div class="content" v-if="$slots.content">
        <slot name="content" :form="currentForm"></slot>
      </div>
      <el-form-item class="button-list" :style="'width: ' + widthPercent + '%;'" v-if="!disabled">
        <slot name="button" :form="currentForm"></slot>
        <el-button @click.native="resetForm" v-if="isCancel && !disabled">{{ $t(cancelText) || $t('重置') }}</el-button>
        <el-button type="primary" :loading="currentLoading" @click.native="debounceSubmit" v-if="isSubmit && !disabled">{{ $t(submitText) || $t('提交') }}</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
import qs from 'qs';
import deepCopy from './es/deepCopy.js';
import evalString from './es/calculate.js';
import debounce from 'throttle-debounce/debounce';
import zteItem from './zte-item';
import zteItemRange from './zte-item-range';
import i18n from '@/mixins/i18n';
import selection from './mixins/selection';

export default {
  name: 'zteForm',
  mixins: [i18n, selection],
  components: {
    zteItem,
    zteItemRange,
  },
  props: {
    value: {
      type: Object,
      default: () => ({}),
    },
    itemInfo: {
      type: Array,
      default: () => ([]),
    },
    form: {
      type: Object,
      default: () => ({}),
    },
    rules: {
      type: Object,
      default: () => ({}),
    },
    rowSize: {
      type: Number,
      default: 1,
    },
    labelWidth: {
      type: Number,
      default: 120,
    },
    labelPosition: String,
    isSubmit: {
      type: Boolean,
      default: true,
    },
    isCancel: {
      type: Boolean,
      default: true,
    },
    submitText: {
      type: String,
      default: '',
    },
    cancelText: {
      type: String,
      default: '',
    },
    border: {
      type: Boolean,
      default: false,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    isLanguage: {
      type: Boolean,
      default: false,
    },
    saveForm: {
      type: Boolean,
      default: false,
    },
    localRequire: {
      type: Boolean,
      default: true,
    },
    loading: {
      type: Boolean,
      default: false,
    },
    change: Function,
    id: String,
    size: {
      type: String,
      default: 'small',
    },
  },
  computed: {
    widthPercent() {
      return (100 / this.rowSize).toFixed(2);
    },
    isCn() {
      return this.$i18n.locale === 'zh-CN';
    },
    params() {
      const params = {};
      for (const info of this.currentInfo) {
        if (info.prev && Object.keys(info.prev).length) { // prev: { code: 'param1'  }
          params[info.code] = {};
          for (const key of Object.keys(info.prev)) {
            params[info.code][info.prev[key]] = this.currentForm[key];
            // params[info.code]._prevcode[key] = this.findPrev(key).required;
          }
        }
      }
      return params;
    },
    _prevcode() {
      const _prevcode = {};
      for (const info of this.currentInfo) {
        if (info.prev && Object.keys(info.prev).length) { // prev: { code: 'param1'  }
          _prevcode[info.code] = {
          };
          for (const key of Object.keys(info.prev)) {
            const previtem = this.findPrev(key)
            _prevcode[info.code][key] = previtem && previtem.required;
          }
        }
      }
      return _prevcode;
    },
  },
  data() {
    return {
      currentRules: {},
      currentForm: this.value,
      currentInfo: [],
      currentLoading: false,
    };
  },
  created() {
    this.initItemInfo();
    this.debounceSubmit = debounce(300, () => {
      this.submitForm();
    });
  },
  watch: {
    form: {
      deep: true,
      handler() {
        this.initForm();
        // this.$nextTick(() => {
        //   this.$refs.ruleForm.clearValidate();
        // });
      },
    },
    itemInfo() {
      this.initItemInfo();
      this.$nextTick(() => {
        this.$refs.ruleForm.clearValidate();
      });
    },
    isLanguage() {
      this.initItemInfo();
      this.$nextTick(() => {
        this.$refs.ruleForm.clearValidate();
      });
    },
    value(form) {
      this.currentForm = form;
    },
    currentForm: {
      deep: true,
      handler(form) {
        if (this.change) {
          this.change(form);
        }
        this.$emit('change', form);
        this.$emit('input', form);
      },
    },
    loading(val) {
      this.currentLoading = val;
    },
  },
  methods: {
    initSelectOptions() {
      this.itemInfo.forEach((info) => {
        if (!info.prev) {
          this.getOptions(info);
        } else {
          this.getOptions(info, this.params[info.code]);
        }
      });
    },
    initItemInfo() {
      this.currentInfo = this.isLanguage ? this.langItemInfo(this.itemInfo) : this.itemInfo;
      this.initForm();
      this.initSelectOptions();
      if (!this.disabled) {
        this.initRules();
      }
    },
    initRules() {
      const rules = {};
      const inputList = ['text', 'num', 'textarea'];
      this.currentInfo.forEach((info) => {
        const rule = this.rules[info.code] && deepCopy(this.rules[info.code]) || [];
        if (info.range && info.required) {
          rules[info.start] = [
            {
              type: info.dataType || 'string',
              required: true,
              message: this.$t('请选择'),
              trigger: ['blur', 'change'],
            },
          ];
          rules[info.end] = [
            {
              type: info.dataType || 'string',
              required: true,
              message: this.$t('请选择'),
              trigger: ['blur', 'change'],
            },
          ];
        } else if (info.required) {
          let message = (inputList.indexOf(info.type) > -1 ? this.$t('请输入') : this.$t('请选择')) + this.$t(info.label);
          if (info.type === 'temp') {
            message = info.validateMessage || message;
          }
          const newRule = {
            type: info.dataType || 'string',
            required: true,
            message,
            trigger: ['blur', 'change'],
          }
          if (['num', 'amount'].includes(info.type)) {
            delete newRule.type;
          }
          rule.push(newRule);
        }
        if (rule.length) {
          rules[info.code] = rule;
        }
      });
      this.currentRules = rules;
    },
    initForm() {
      const form = {};
      this.currentInfo.forEach((info) => {
        form[info.code] = '';
        if (info.showLabel) form[info.showLabel] = '';
        if (info.type === 'upload') form[info.code] = [];
        if (info.type === 'amount') form[info.code] = 0;
      });
      this.currentForm = Object.assign({}, form, this.form, this.saveForm ? this.currentForm : {});
    },
    submitForm() {
      this.$refs.ruleForm.validate((valid) => {
        if (valid) {
          if (this.isLanguage) {
            this.handlerSubmit(this.currentForm);
          } else {
            this.$emit('update:loading', true);
            this.currentLoading = true;
            this.$emit('submit', this.currentForm, this.stopLoading);
          }
        } else {
          return false;
        }
        return true;
      });
    },
    resetForm() {
      this.$refs.ruleForm.resetFields();
      this.$refs.ruleForm.clearValidate();
      this.$emit('reset');
    },
    // select后置字段，清空及重新请求数据
    handlerSelectChange(val, info) {
      if (info.next) {
        for (const code in info.next) {
          const item = this.itemInfo.find(item => item.code === code);
          if (item && item.type === 'select') {
            // const item = this.itemInfo[index];
            const params = {};
            params[info.code] = val;
            this.getOptions(item, params);
            this.currentForm[code] = '';
          } else if (info.next[code]) {
            if (info.multiple) {
              const options = info.options.filter(opt => val.indexOf(opt[info.optionProps.value]) > -1);
              this.currentForm[code] = options.map(opt => opt[info.next[code]]).join(',');
            } else {
              const option = info.options.filter(opt => opt[info.optionProps.value] === val)[0];
              this.currentForm[code] = option ? option[info.next[code]] : '';
            }
          } else {
            this.currentForm[code] = '';
          }
        }
      }
      this.$emit('select-change', val, info);
    },
    handlerCheckChange(val, info) {
      this.$emit('check-change', val, info);
    },
    handlerRangeChange(val, info) {
      if (info.subCode && info.subCode.length === 2) {
        this.currentForm[info.subCode[0]] = val[0];
        this.currentForm[info.subCode[1]] = val[1];
      }
    },
    handleSearchChange(info) {
      this.currentForm = Object.assign(this.currentForm, info);
      this.$emit('city-change', info);
    },
    // 多语言封装
    langItemInfo(itemInfo) {
      const result = [];
      itemInfo.forEach((info) => {
        if (info.lang) {
          result.push(...this.langs.map(lang => ({
            ...info,
            code: `${info.code}_${lang.value}`,
            label: (this.langs && this.langs.length > 1) ? `${this.$t(info.label)}(${lang.label})` : info.label,
            required: (this.localRequire && lang.value !== this.$i18n.locale) ? false : info.required,
          })));
        } else {
          result.push(info);
        }
      });
      return result;
    },
    handlerSubmit(form) {
      let result = {
        languages: [],
      };
      this.langs.forEach((lang) => {
        const obj = {
          language: lang.value,
        };
        this.itemInfo.forEach((info) => {
          if (!info.lang) {
            result[info.code] = form[info.code];
          } else {
            obj[info.code] = form[`${info.code}_${lang.value}`];
          }
        });
        result.languages.push(obj);
      });
      result = Object.assign({}, form, result);
      this.$emit('update:loading', true);
      this.currentLoading = true;
      this.$emit('submit', result, this.stopLoading);
    },
    setLabel(labelCode, value) {
      this.$set(this.currentForm, labelCode, value);
    },
    sumToFormFiled(toFiledCode) {
      const obj = this.itemInfo.filter(item => toFiledCode === item.code);
      if (obj && obj.length >0 ) {
        const targetComputed = obj[0].computed;
        const reg = /(\${)([\w+.]+\w+)(})/g;
        const computed = targetComputed.replace(reg, (res,$1,$2) => {
          return this.currentForm[$2] || 0;
        });
        this.currentForm[toFiledCode] = parseFloat(evalString(computed).toFixed(2));
      }
    },
    showChange(showLabel, value, defaultCode, defaultValue) {
      this.currentForm[showLabel] = value;
      if (defaultCode) {
        this.currentForm[defaultCode] = defaultValue;
      }
    },
    treeChange(showLabel, checked, halfId, halfChecked) {
      this.currentForm[showLabel] = checked;
      this.currentForm[halfId] = halfChecked;
    },
    pickerChange(rows, item) {
      this.$emit('mulit-picker-change', rows, item);
    },
    singlePickerChange(row, item) {
      const keys = Object.keys(row);
      keys.forEach((key) => {
        if (this.currentForm[key] !== undefined) {
          this.currentForm[key] = row[key];
        } else {
          this.$set(this.currentForm, key, row[key]);
        }
      });
      this.$emit('picker-change', this.currentForm, item);
    },
    warningPrev(prevcode) {
      const itemIn = this.findPrev(prevcode);
      const label = itemIn && itemIn.label || '';
      this.$message.warning(this.$t('请先选择',{ label }));
    },
    findPrev(prevcode) {
      return this.itemInfo.find((item) => {
        let finded = false;
        finded = item.code === prevcode || item.showLabel === prevcode;
        if (!finded && item.trans && item.trans.length) {
          finded = item.trans.some(tr => tr.to === prevcode);
        }
        return finded;
      });
    },
    stopLoading() {
      this.$emit('update:loading', false);
      this.currentLoading = false;
    },
  },
};
</script>
