<template>
  <div class="default-layout">
    <template v-for="(layoutItem) in layout.showFields">
      <slot
        v-if="layoutItem.type === 'divider'"
        :field="layoutItem"
        :getPropValue="getPropValue"
        :formData="formData"
        :name="layoutItem.slotName || 'divider'"
      >
        <el-divider
          :key="layoutItem.key"
          :content-position="layoutItem.contentPosition"
        >
          {{ getPropValue(layoutItem.label) }}
        </el-divider>
      </slot>
      <el-row
        v-else-if="layoutItem.type === 'row'"
        :key="layoutItem.cols.map((item)=>{return item.key || item.prop}).join('')"
        :gutter="getValueByLevel(formConfig.gutter, 20)"
        :class="{
          ['easy-form-row-' + layoutItem.cols.map((item)=>{return getValueByLevel(item.prop, item.key, '') }).join('-')]: true,
          'next-is-divider': layoutItem.nextIsDivider
        }"
      >
        <el-col
          v-for="(layoutColItem) in layoutItem.cols"
          :key="layoutColItem.key"
          :span="layoutColItem.span"
          :style="(getPropValue(layoutColItem.styles) || {}).col"
          :class="['easy-form-col-' + layoutColItem.key, { 'easy-form-col-view': isView(layoutColItem) }]"
        >
          <el-form-item
            v-if="!layoutColItem.empty"
            :label="(getPropValue(layoutColItem.label) || '') + (getValueByLevel(layoutColItem.showColon, formConfig.showColon) ? '：' : '')"
            :prop="layoutColItem.prop"
            :style="{
              ...(getPropValue(layoutColItem.styles) || {}).formItem
            }"
            :class="{
              'hidden-label': getValueByLevel(layoutColItem.hiddenLabel, formConfig.hiddenLabel),
              ...(typeof (layoutColItem.formItemProp || {}).class === 'object' ? (layoutColItem.formItemProp || {}).class : {[(layoutColItem.formItemProp || {}).class]:true}),
              ['label-position-'+(formConfig.labelPosition || 'top')]: true,
              ['item-type-'+(layoutColItem.type || '')]: true,
              ['item-type-slot-' + (layoutColItem.slotName || '')]: !!layoutColItem.slotName
            }"
            v-bind="layoutColItem.formItemProp"
          >
            <template v-slot:label>
              <span v-if="getValueByLevel(layoutColItem.hiddenLabel, formConfig.hiddenLabel)" />
              <slot
                v-else
                :formData="formData"
                :field="layoutColItem"
                :getPropValue="getPropValue"
                :formConfig="formConfig"
                :name="getPropValue(getValueByLevel(layoutColItem.labelSlotName, formConfig.labelSlotName)) || 'label'"
              >
                <template>
                  <span v-if="getPropValue(layoutColItem.label)" class="el-form-item-label-inline">{{ getPropValue(layoutColItem.label) + (getValueByLevel(layoutColItem.showColon, formConfig.showColon) ? '：' : '') }}</span>
                  <span v-else>&nbsp;</span>
                </template>
              </slot>
            </template>
            <EasyFormItem
              :field="layoutColItem"
              :item-controller="itemController[layoutColItem.prop]"
              v-bind="easyFormItemBindData"
              @update:rules="(value)=>{ formInstance.updateRules(layoutColItem.prop, value) }"
              @created="(instance) => { formInstance.registerFormItem(layoutColItem.prop, instance) }"
              @beforeDestroy="(instance) => { formInstance.unRegisterFormItem(layoutColItem.prop, instance) }"
              @change="(scope, value, isFromUser)=>{
                formInstance.clearValidate(layoutColItem.prop)
                formInstance.callComponentOnChange(scope, layoutColItem, value, isFromUser)
              }"
            >
              <template v-slot:[getPropValue(layoutColItem.slotName)]="scope">
                <slot
                  :name="getPropValue(layoutColItem.slotName)"
                  :getField="getField"
                  :formConfig="formConfig"
                  :easyFormItemBindData="easyFormItemBindData"
                  v-bind="scope"
                />
              </template>
              <template
                v-for="beforeSlotName in getPropArrayValue(layoutColItem.beforeSlotName)"
                v-slot:[beforeSlotName]="scope"
              >
                <slot
                  :name="beforeSlotName"
                  :getField="getField"
                  :formConfig="formConfig"
                  :easyFormItemBindData="easyFormItemBindData"
                  v-bind="scope"
                />
              </template>
              <template
                v-for="afterSlotName in getPropArrayValue(layoutColItem.afterSlotName)"
                v-slot:[afterSlotName]="scope"
              >
                <slot
                  :name="afterSlotName"
                  :getField="getField"
                  :formConfig="formConfig"
                  :easyFormItemBindData="easyFormItemBindData"
                  v-bind="scope"
                />
              </template>
            </EasyFormItem>
          </el-form-item>
          <span v-else>&nbsp;</span>
        </el-col>
      </el-row>
    </template>
  </div>
</template>

<script>
import { JSONStringify, getValueByLevel } from '../utils'
import EasyFormItem from '../EasyFormItem'
export default {
  components: {
    EasyFormItem
  },
  props: {
    fields: {
      type: Array,
      default: () => {
        return []
      }
    },
    componentScope: {
      type: Object,
      default: () => {
        return {}
      }
    },
    formConfig: {
      type: Object,
      default: () => {
        return {}
      }
    },
    itemController: {
      type: Object,
      default: () => {
        return {}
      }
    },
    easyFormItemBindData: {
      type: Object,
      default: () => {
        return {}
      }
    },
    formData: {
      type: Object,
      default: () => {
        return {}
      }
    }
  },
  data() {
    return {
      layoutCache: {
        data: null,
        stringify: '',
        count: 0,
        forceUpdate: false
      }
    }
  },
  computed: {
    layout() {
      return this.renderLayout()
    },
    formInstance() {
      return this.componentScope.formInstance
    }
  },
  methods: {
    getValueByLevel,
    getField(propName) {
      return this.fields.find((item) => {
        return item.prop === propName
      })
    },
    getPropValue(prop, ...args) {
      return typeof prop === 'function' ? prop(this.componentScope, ...args) : prop
    },
    renderLayout() {
      const filtered = this.fields.map((item, index) => {
        return {
          key: item.prop || index,
          ...item,
          formItemProp: this.getPropValue(item.formItemProp),
          slotName: this.getPropValue(item.slotName),
          keepOnForm: this.getPropValue(item.keepOnForm),
          showInForm: this.getPropValue(item.showInForm),
          formSort: this.getPropValue(item.formSort) || Infinity,
          type: this.getPropValue(item.type),
          span: this.getPropValue(item.span)
        }
      }).sort((a, b) => {
        // 从小到大
        return a.formSort - b.formSort
      })
      const showFields = []
      for (const field of filtered) {
        if (field.showInForm !== false) {
          showFields.push(field)
        }
      }
      const result = []
      let tmpLayoutItem = {
        type: 'row',
        cols: [] // 暂存span未达到24的项
      }
      for (const item of showFields) {
        if (item.type === 'divider' || item.span === undefined) {
          item.span = 24
        }
        if (item.span === 24) {
          if (tmpLayoutItem.cols.length > 0) {
            tmpLayoutItem.cols.push({
              empty: true,
              span: 24 - tmpLayoutItem.cols.reduce((a, b) => {
                return a + b.span
              }, 0)
            })
            result.push(tmpLayoutItem)
            tmpLayoutItem = {
              type: 'row',
              cols: [] // 暂存span未达到24的项
            }
          }
          if (item.type === 'divider') {
            if (result.length > 0) {
              result[result.length - 1].nextIsDivider = true
            }
            result.push(item)
          } else {
            result.push({
              type: 'row',
              cols: [item]
            })
          }
        } else {
          const itemspan = tmpLayoutItem.cols.reduce((a, b) => {
            return a + b.span
          }, 0)
          if (itemspan + item.span > 24) {
            tmpLayoutItem.cols.push({
              empty: true,
              span: 24 - itemspan
            })
            result.push(tmpLayoutItem)
            tmpLayoutItem = {
              type: 'row',
              cols: [item] // 暂存span未达到24的项
            }
          } else {
            tmpLayoutItem.cols.push(item)
            if (tmpLayoutItem.cols.reduce((a, b) => {
              return a + b.span
            }, 0) === 24) {
              result.push(tmpLayoutItem)
              tmpLayoutItem = {
                type: 'row',
                cols: [] // 暂存span未达到24的项
              }
            }
          }
        }
      }
      if (tmpLayoutItem.cols.length > 0) {
        tmpLayoutItem.cols.push({
          empty: true,
          span: 24 - tmpLayoutItem.cols.reduce((a, b) => {
            return a + b.span
          }, 0)
        })
        result.push(tmpLayoutItem)
        tmpLayoutItem = {
          type: 'row',
          cols: [] // 暂存span未达到24的项
        }
      }
      if (this.layoutCache.stringify === JSONStringify({
        showFields: result
      })) {
        if (this.layoutCache.count > 2) {
          return this.layoutCache.data
        } else {
          this.layoutCache.count++
          return {
            showFields: result,
            forceUpdate: this.layoutCache.forceUpdate
          }
        }
      } else {
        this.layoutCache.count = 0
        this.layoutCache.data = {
          showFields: result,
          forceUpdate: this.layoutCache.forceUpdate
        }
        this.layoutCache.stringify = JSONStringify({
          showFields: result
        })
        this.$nextTick(() => {
          this.layoutCache.forceUpdate = !this.layoutCache.forceUpdate
        })
        return {
          showFields: result,
          forceUpdate: this.layoutCache.forceUpdate
        }
      }
    },
    isView(field) {
      return field.type === 'view' || this.getValueByLevel(this.getPropValue(field.isView), this.getPropValue(this.formConfig.isView))
    },
    getPropArrayValue(prop, ...args) {
      const propValue = this.getPropValue(prop, ...args)
      if ([null, undefined].includes(propValue)) {
        return []
      } else {
        return Array.isArray(propValue) ? propValue : [propValue]
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.hidden-label.label-position-right{
  ::v-deep() .el-form-item__content{
    margin-left: 0!important;
  }
}
.el-row>.el-col>.el-form-item.hidden-label{
  &> ::v-deep() .el-form-item__label{
    display: none;
  }
}
.easy-form-col-view {
  ::v-deep() {
    .el-form-item__label::before{
      display: none;
    }
    .el-form-item__error{
      display: none;
    }
  }
}
.el-form-item.is-required{
  ::v-deep() .el-form-item__label:before{
    display: none;
  }
  .el-form-item-label-inline{
    position: relative;
  }
  .el-form-item-label-inline:before{
    content: '*';
    color: #F56C6C;
    margin-right: 4px;
  }
}
.el-form-item.label-position-right{
  display: flex;
  align-items: center;
  ::v-deep(){
    .el-form-item__label{
      flex-shrink: 0;
      line-height: unset;
    }
    .el-form-item__content{
      margin-left: 0!important;
      width: 100%;
    }
  }
}
</style>
