<template>
  <div class="EasyForm">
    <el-form-scroll
      ref="form"
      :model="formData"
      :label-position="formConfig.labelPosition"
      :label-width="formConfig.labelWidth"
      :rules="rules"
      :disabled="formConfig.readOnly"
      :class="{ 'read-only': formConfig.readOnly }"
    >
      <template v-for="layoutItem in layout">
        <el-divider
          v-if="layoutItem.type === 'divider'"
          :key="layoutItem.prop"
          :content-position="layoutItem.contentPosition"
        >
          {{ getPropValue(layoutColItem.label) }}
        </el-divider>
        <el-row v-else-if="Array.isArray(layoutItem)" :key="layoutItem.map((item)=>{return item.prop}).join('')">
          <el-col
            v-for="(layoutColItem,index) in layoutItem"
            :key="layoutColItem.empty ? layoutItem[index-1].key + '_empty' : layoutColItem.prop"
            :span="layoutColItem.span"
          >
            <el-form-item v-if="!layoutColItem.empty" :label="getPropValue(layoutColItem.label)" :prop="layoutColItem.prop">
              <span
                v-if="layoutColItem.type === 'view' || getPropValue(layoutColItem.isView)"
                class="el-form-item-view"
              >
                {{ typeof layoutColItem.viewer === 'function' ? layoutColItem.viewer(formData[layoutColItem.prop], {
                  formData,
                  formConfig
                }) : formData[layoutColItem.prop] }}
              </span>
              <el-input
                v-else-if="layoutColItem.type === 'input'"
                v-model="formData[layoutColItem.prop]"
                :placeholder="getPlaceholder(layoutColItem)"
                v-bind="getPropValue(layoutColItem.formProps)"
                v-on="getPropValue(layoutColItem.formListeners)"
              >
                <template
                  v-if="layoutColItem.append"
                  slot="append"
                >{{ layoutColItem.append }}</template>
              </el-input>
              <el-select
                v-else-if="layoutColItem.type === 'select'"
                v-model="formData[layoutColItem.prop]"
                :placeholder="getPlaceholder(layoutColItem)"
                v-bind="getPropValue(layoutColItem.formProps)"
                v-on="getPropValue(layoutColItem.formListeners)"
              >
                <el-option
                  v-for="option in (getPropValue(layoutColItem.options) || []).map((item) => {return typeof item === 'object' ? item : { value: item, label: item }})"
                  :key="option.value"
                  :label="option.label"
                  :value="option.value"
                  :disabled="getPropValue(option.disabled)"
                />
              </el-select>
              <el-date-picker
                v-else-if="layoutColItem.type === 'date-picker'"
                v-model="formData[layoutColItem.prop]"
                :placeholder="getPlaceholder(layoutColItem)"
                v-bind="getPropValue(layoutColItem.formProps)"
                v-on="getPropValue(layoutColItem.formListeners)"
              />
              <el-radio-group
                v-else-if="layoutColItem.type === 'radio'"
                v-model="formData[layoutColItem.prop]"
                :placeholder="getPlaceholder(layoutColItem)"
                v-bind="getPropValue(layoutColItem.formProps)"
                v-on="getPropValue(layoutColItem.formListeners)"
              >
                <el-radio
                  v-for="option in (getPropValue(layoutColItem.options) || []).map((item) => {return typeof item === 'object' ? item : { value: item, label: item }})"
                  :key="option.value"
                  :label="option.value"
                  :disabled="getPropValue(option.disabled)"
                >{{ option.label }}
                </el-radio>
              </el-radio-group>
              <el-checkbox-group
                v-else-if="layoutColItem.type === 'checkbox'"
                v-model="formData[layoutColItem.prop]"
                v-bind="getPropValue(layoutColItem.formProps)"
                v-on="getPropValue(layoutColItem.formListeners)"
              >
                <el-checkbox
                  v-for="option in (getPropValue(layoutColItem.options) || []).map((item) => {return typeof item === 'object' ? item : { value: item, label: item }})"
                  :key="option.value"
                  :label="option.value"
                  :disabled="getPropValue(option.disabled)"
                >{{ option.label }}
                </el-checkbox>
              </el-checkbox-group>
              <el-upload
                v-else-if="layoutColItem.type === 'upload'"
                :class="{ 'el-upload-readonly': formConfig.readOnly }"
                :file-list="formData[layoutColItem.prop]"
                :on-change="(file, fileList)=>{$set(formData,layoutColItem.prop,fileList)}"
                :on-remove="(file, fileList)=>{$set(formData,layoutColItem.prop,fileList)}"
                :data="getPropValue(layoutColItem.uploadData)"
                v-bind="getPropValue(layoutColItem.formProps)"
                v-on="getPropValue(layoutColItem.formListeners)"
                style="width:50%"
              >
                <el-button v-if="!formConfig.readOnly" slot="trigger" size="small" type="primary">选取文件</el-button>
                <span style="width:1000px" v-if="!formConfig.readOnly && getPropValue(layoutColItem.tips)">{{ getPropValue(layoutColItem.tips) }}</span>
              </el-upload>
              <slot v-else-if="layoutColItem.type === 'slot'" :formData="formData" :name="layoutColItem.slotName" />
            </el-form-item>
          </el-col>
        </el-row>
      </template>
    </el-form-scroll>
    <div
      class="btns"
      :class="{
        'btn-position-center': formConfig.submitPosition === 'center',
        'btn-position-left': formConfig.submitPosition === 'left',
        'btn-position-right': formConfig.submitPosition === 'right',
      }"
    >
      <el-button v-if="formConfig.showSubmit" type="primary" @click="submitForm">提交</el-button>
      <el-button v-if="formConfig.showCancel" @click="$emit('cancel')">取消</el-button>
      <el-button
        v-for="button in buttons.filter((button) => { return getPropValue(button.show) !== false })"
        :key="button.label"
        :loading="getPropValue(button.loading)"
        :type="getPropValue(button.type)"
        :size="getPropValue(button.size)"
        :disabled="getPropValue(button.disabled)"
        @click="callButtonHandle(button)"
      >
        {{ button.label }}
      </el-button>
    </div>
  </div>

</template>

<script>
import ElFormScroll from '../ElFormScroll'
export default {
  name: 'EasyForm',
  components: {
    ElFormScroll
  },
  props: {
    fields: {
      type: Array,
      default: () => {
        return []
      }
    },
    value: {
      type: Object,
      required: true
    },
    formConfig: {
      type: Object,
      default: () => {
        return {}
      }
    },
    buttons: {
      type: Array,
      default: () => {
        return []
      }
    }
  },
  data() {
    return {

    }
  },
  computed: {
    formData() {
      for (const field of this.fields) {
        if (field.prop && !this.value[field.prop] && field.defaultValue !== undefined) {
          this.$set(this.value, field.prop, typeof field.defaultValue === 'function' ? field.defaultValue({
            formData: this.value,
            formConfig: this.formConfig
          }) : field.defaultValue)
        } else if (field.praseValue) {
          this.$set(this.value, field.prop, field.praseValue(this.value[field.prop]))
        }
      }
      return this.value
    },
    layout() {
      const filtered = this.fields.filter((item) => {
        return this.getPropValue(item.showInForm) === true
      }).sort((a, b) => {
        return a.formSort - b.formSort
      })
      const result = []
      let tmpLayoutItem = [] // 暂存span未达到24的项
      for (const item of filtered) {
        if (item.type === 'divider') {
          item.span = 24
        }
        if (item.span === 24) {
          if (tmpLayoutItem.length > 0) {
            tmpLayoutItem.push({
              empty: true,
              span: 24 - tmpLayoutItem.reduce((a, b) => {
                return a + b.span
              }, 0)
            })
            result.push(tmpLayoutItem)
            tmpLayoutItem = []
          }
          if (item.type === 'divider') {
            result.push(item)
          } else {
            result.push([item])
          }
        } else {
          const itemspan = tmpLayoutItem.reduce((a, b) => {
            return a + b.span
          }, 0)
          if (itemspan + item.span > 24) {
            tmpLayoutItem.push({
              empty: true,
              span: 24 - itemspan
            })
            result.push(tmpLayoutItem)
            tmpLayoutItem = [item]
          } else {
            tmpLayoutItem.push(item)
            if (tmpLayoutItem.reduce((a, b) => {
              return a + b.span
            }, 0) === 24) {
              result.push(tmpLayoutItem)
              tmpLayoutItem = []
            }
          }
        }
      }
      if (tmpLayoutItem.length > 0) {
        tmpLayoutItem.push({
          empty: true,
          span: 24 - tmpLayoutItem.reduce((a, b) => {
            return a + b.span
          }, 0)
        })
        result.push(tmpLayoutItem)
        tmpLayoutItem = []
      }
      return result
    },
    rules() {
      const result = {}
      for (const item of this.fields) {
        if (item.prop) {
          result[item.prop] = (this.getPropValue(item.rule) || []).map((item) => {
            if (item.trigger === undefined) {
              item.trigger = 'submit'
            }
            return item
          })

          if (item.required) {
            result[item.prop].push({
              required: true,
              message: this.getFieldTips(item) + this.getPropValue(item.label),
              trigger: 'submit'
            })
          }
        }
      }
      return result
    }
  },
  methods: {
    /**
     * private
     */
    getPropValue(prop, ...args) {
      return typeof prop === 'function' ? prop(...args, {
        formData: this.formData,
        formConfig: this.formConfig,
        componentName: 'EasyForm'
      }) : prop
    },
    getFieldTips(field) {
      if (field.fieldTips) {
        return field.fieldTips
      }
      switch (field.type) {
        case 'input': {
          return '请输入'
        }
        case 'select':
        case 'radio':
        case 'checkbox':
        case 'date-picker': {
          return '请选择'
        }
        default: {
          return '请录入'
        }
      }
    },
    getPlaceholder(layoutColItem) {
      return this.formConfig.readOnly ? '未录入' : this.getFieldTips(layoutColItem) + this.getPropValue(layoutColItem.label)
    },
    callButtonHandle(button) {
      button.handler(this.formData, button, this)
    },

    /**
     * public
     */
    submitForm() {
      /**
       * 检验成功时会返回经过valueFormat字段处理过的parseFormData
       */
      return new Promise((resolve, reject) => {
        this.$refs.form.validateScroll((valid, rules) => {
          if (valid) {
            this.$emit('submit', this.formData)
            resolve(this.getParseFormData())
          } else {
            this.$emit('valid', rules)
            if (this.formConfig.showValidMessage) {
              this.$message({
                type: 'warning',
                message: rules[Object.keys(rules)[0]][0].message,
                duration: 5000
              })
            }
            reject(rules)
          }
        })
      })
    },
    getParseFormData() {
      const parseFormData = {
        ...this.formData
      }
      for (const field of this.fields) {
        if (typeof field.valueFormat === 'function') {
          parseFormData[field.prop] = field.valueFormat(parseFormData[field.prop])
        }
      }
      return parseFormData
    },
    resetFormData(formData = {}) {
      this.$emit('input', formData)
      this.$nextTick(() => {
        this.$refs.form.clearValidate()
      })
    },
    clearValidate() {
      return this.$refs.form.clearValidate()
    }
  }
}

export const template = {
  data() {
    return {
      formData: {},
      formConfig: {
        labelPosition: 'left',
        labelWidth: '150px',
        gutter: 0,
        showSubmit: true, // 是否显示预设的提交按钮
        showCancel: true,
        submitPosition: 'right', // 页脚按钮位置
        showValidMessage: true // 校验失败时是否使用Message显示提示错误消息
      },
      fields: [
        {
          type: 'input',
          prop: 'name',
          label: '数量',
          isView: (scope) => {
            return false
          }, // 忽略type属性使用不可编辑的展示模式
          showInForm: true, // 是否显示在表单中
          append: '个',
          formSort: 0,
          defaultValue: () => { // 初始化时当前项为undefined时调用
            return []
          },
          valueFormat: (value) => { // 调用submitForm时会调用这个方法获取parseFormData
            return value + 1
          },
          praseValue: (value) => { // 表单初始化时调用该方法进行格式化值，当调用defaultValue时不会再调用此项
            return typeof value === 'string' ? value.split(',') : value
          },
          span: 12, // 同el-row
          required: true, // 自动生成中文错误提示
          rule: [
            { validator: (rule, value, callback) => {
              callback(value > 5 ? '数量不能大于5' : undefined)
            }, trigger: 'blur' }
          ]
        },
        {
          type: 'select',
          prop: 'sex',
          label: '性别',
          defaultValue: '1',
          required: true,
          options: (formData) => {
            return [
              { label: '男', value: '1' },
              {
                label: '女',
                value: '0',
                disabled: (formData) => {
                  return true
                }
              }
            ]
          },
          showInForm: true,
          span: 12,
          rule: []
        },
        {
          type: 'divider', // 分割线，自动占一行
          label: 'test divider',
          prop: 'divider-1',
          contentPosition: 'center',
          showInForm: true
        },
        {
          type: 'date-picker',
          prop: 'date',
          label: '日期',
          required: true,
          showInForm: true,
          span: 12,
          praseValue: (value) => {
            return new Date(value)
          },
          formProps: {
            type: 'date',
            valueFormat: 'yyyy-MM-dd'
          },
          rule: []
        },
        {
          type: 'radio',
          prop: 'radio',
          label: '单选',
          showInForm: true,
          required: true,
          options: (formData) => {
            return [
              { label: '男', value: '1' },
              { label: '女', value: '0' }
            ]
          },
          span: 24,
          rule: []
        },
        {
          type: 'checkbox',
          prop: 'checkbox',
          label: '多选',
          showInForm: true,
          required: true,
          fieldTips: '请选择',
          defaultValue: () => {
            return []
          },
          options: (formData) => {
            return [
              { label: '多选A', value: '2' },
              { label: '多选B', value: '1' },
              { label: '多选C', value: '0' }
            ]
          },
          span: 24,
          rule: []
        },
        {
          type: 'slot',
          showInForm: true,
          slotName: 'testSlot',
          span: 12
        }
      ],
      buttons: [ // 页脚追加的按钮
        {
          label: '取消',
          type: 'danger',
          loading: false,
          handler: (formData, button, easyFormInstance) => {
            console.log(formData, button, easyFormInstance)
            button.loading = true
            button.label = '正在取消'
            setTimeout(() => {
              button.loading = false
              button.label = '取消'
            }, 5000)
          }
        }
      ]
    }
  },
  methods: {
    onSubmit(formData) {
      console.log('检验成功', formData)
    },
    onValid(rules) {
      console.log('检验失败', rules)
    }
  }
}
</script>

<style lang="scss" scoped>
.btns{
  margin-top: 20px;
}
.btn-position-center{
  text-align: center;
}
.btn-position-left{
  text-align: left;
}
.btn-position-right{
  text-align: right;
}
.el-upload-readonly{
  /deep/ .el-upload{
    display: none;
  }
}
.EasyForm{
  .el-col-12{
    border-left:1px solid #9cccfc;
  }
  .el-col-12:nth-child(2n+1){
    border-left:0!important;
  }
  .el-row:not(:last-child){
    border-left: 1px solid #9cccfc;
    border-right: 1px solid #9cccfc;
    border-top: 1px solid #9cccfc;
    border-bottom:1px solid #9cccfc;
  }
  .el-form-item{
    margin-bottom: 0!important;
  }
}
</style>
<style lang="scss">
.EasyForm{
  .el-form-item{
    // height: 50px;
    background: #eaf4fe;
  }
}
.EasyForm .el-form-item .el-form-item__label{
  background: #eaf4fe;
  padding: 10px;
  width: 150px!important;
}
.EasyForm .el-form-item .el-form-item__content{
  margin: 0!important;
  background: #fff!important;
  width: 100%;
  // height: 50px;
  padding-left: 10px;
}

</style>
