<template lang="pug">
el-form.smart-form(:model='data', :size="size || 'mini'", :inline='isInline', :label-position='labelPosition', :label-width='labelWidth' :rules="rules" ref="form")
  el-row(:gutter="gutter")
    el-col(v-for="(item, index) in availableConfig"
      :key="index + 1"
      :span="item.span || span"
      :push="item.push || 0"
      :pull="item.pull || 0"
      :offset="item.offset || 0")
      el-divider(v-if="item.type === 'divider'") {{item.text}}
      el-form-item(v-else :prop="item.prop" :class="[item.type]")
        template(#label v-if="item.label") {{item.label}}
          el-popover(width='200', trigger='hover', :placement="item.placement")
            span.el-icon-question(v-if='item.reminder', slot="reference" tabindex="-1")
            div(v-html='item.reminder')
        slot(v-if="item.type === 'slot'", :name="item.prop" :data="{index, ...item}") {{item}}
        //- 渲染 checkbox 组件
        el-checkbox-group.checkbox-group(v-else-if="item.type === 'checkbox'", :min="item.min" :max="item.max" v-model='data[item.prop]' @change='selectChange.bind(this, item)()')
          component(
            :is="item.button ? 'elCheckboxButton': 'elCheckbox'"
            v-for="i in customOptions.bind(this, item)()"
            :label="i.value"
            :disabled='disabled.bind(this, item)()'
            :key="i.value | hash") {{i.label}}
        //- 渲染 radio 组件
        el-radio-group.radio-group(v-else-if="item.type === 'radio'", :min="item.min" :max="item.max" v-model='data[item.prop]' @change='selectChange.bind(this, item)()')
          component(
            :is="item.button ? 'elRadioButton': 'elRadio'"
            v-for="i in customOptions.bind(this, item)()"
            :label="i.value"
            :disabled='disabled.bind(this, item)()'
            :key="i.value | hash") {{i.label}}
        //- 渲染  time 组件
        el-time-select(v-else-if="item.type === 'time'", v-model='data[item.prop]', :disabled='disabled.bind(this, item)()',  :picker-options="item.pickerOptions", :placeholder='item.placeholder' @change='selectChange.bind(this, item)()')
        //- 渲染 select 组件
        el-select(v-else-if="item.type === 'select'", :multiple='item.multiple', :allow-create="item.allowCreate", :value-key='item.key || "value"', :remote="!!item.remote", :remote-method="item.remote", :loading="selectLoading", :default-first-option="item.defaultFirstOption", clearable, :filterable='item.filterable || item.allowCreate', v-model='data[item.prop]', :placeholder='item.placeholder', :disabled='disabled.bind(this, item)()', @change='selectChange.bind(this, item)()', :popper-class="item.prop", :no-data-text="item.noDataText")
          template(v-if="item.groups")
            el-option-group(v-for='group in customGroups.bind(this, item)()', :key='group.label | hash', :label='group.label')
              el-option(v-for='option in group.children', :key='option.value | hash', :label='option.label', v-html='option.custom && option.custom.bind(this, option)() || option.label', :value='option.value')
          template(v-else)
            el-option(v-for='option in customOptions.bind(this, item)()', :key='option.value | hash', :label='option.label' :value='option.value')
              slot(:name="item.optionsSlot" :data="option") {{option.label}}
        //- 渲染  autocomplete 组件
        el-autocomplete(v-else-if="item.type === 'autocomplete'", v-model='data[item.prop]', :fetch-suggestions="item.suggestions.bind(data)" @select="item.select" :disabled='disabled.bind(this, item)()', :placeholder='item.placeholder', highlight-first-item)
        //- 渲染 upload 组件
        el-upload(v-else-if="item.type === 'upload'" :action='item.action', :list-type='item.listType', :limit='item.limit' :on-remove='handlePictureCardRemove.bind(this, item.prop)' :on-preview='handlePictureCardPreview' :on-success='handlePictureSuccess.bind(this, item.prop)')
          img.thumb(v-if="item.thumb" :src="item.thumb.bind(this, data)()")
          i.el-icon-plus
        component(
          v-else
          :is="item.type | componentTransfer"
          v-bind="getItem.bind(this, item)()"
          v-model="data[item.prop]"
          @change="selectChange.bind(this, item)()")
          template(v-if='item.append', #append) {{item.append}}
          template(v-if='item.prepend', #prepend) {{item.prepend}}
        el-dialog(:visible.sync='dialogVisible')
          img(width='100%', :src='dialogImageUrl', alt='')
</template>
<script>
import Cron from './smart-cron'
import Location from './smart-location'
import modelMixin from '../utils/model-mixin'
import SmartMultiForm from './smart-multi-form'
import clone from 'lodash.clone'
const md5 = require('md5')

export default {
  name: 'SmartForm',
  mixins: [modelMixin],
  props: {
    isInline: {
      type: Boolean,
      default: false
    },
    size: {
      type: String,
      default: 'mini'
    },
    rules: {
      type: Object,
      default: () => {}
    },
    refName: {
      type: String,
      default: ''
    },
    gutter: {
      type: Number,
      default: 24
    },
    labelPosition: {
      type: String,
      default: 'left'
    },
    labelWidth: {
      type: [String, Number],
      default: '80px'
    },
    config: {
      type: Array,
      required: true
    },
    value: {
      type: Object,
      required: true
    },
    span: {
      type: Number,
      default: 6
    }
  },
  data () {
    return {
      selectLoading: false,
      dialogImageUrl: '',
      dialogVisible: false,
      locationOptions: []
    }
  },
  components: {
    Cron,
    Location,
    SmartMultiForm
  },
  filters: {
    hash (data) {
      if (typeof data === 'object') data = JSON.stringify(data)
      return md5(data || Math.random())
    },
    componentTransfer (type) {
      switch (type) {
        case 'cron':
        case 'location':
          return type
        case 'text':
        case 'textarea':
        case 'number':
        case 'password':
          return 'el-input'
        case 'time':
          return 'el-time-select'
        case 'date':
        case 'datetime':
        case 'daterange':
        case 'datetimerange':
          return 'el-date-picker'
        case 'upload':
          return 'el-upload'
        case 'multiform':
          return 'SmartMultiForm'
        case 'color':
          return 'el-color-picker'
        case 'form':
          return 'SmartForm'
        default:
          return `el-${type}`
      }
    }
  },
  created () {
    this.realConfig.forEach(item => {
      if (!this.value[item.prop]) {
        if (item.value) {
          this.value[item.prop] = item.value
        }
        switch (item.type) {
          case 'checkbox':
            this.value[item.prop] = []
            break
        }
      }
    })
    this.data = clone(this.value)
  },
  async mounted () {
    this.do('createRef', {
      name: this.refName,
      ref: this.$refs['form']
    })
  },
  methods: {
    getItem (item) {
      let type = ''
      if (
        /text|textarea|number|password|date|datetime|daterange|datetimerange/.test(
          item.type
        )
      ) {
        type = item.type
      }
      return {
        type,
        size: this.size || 'mini',
        options: this.customOptions(item),
        ...item,
        disabled: this.disabled(item)
      }
    },
    disabled (item) {
      if (typeof item.disabled === 'function') {
        return item.disabled.call(this.data, item)
      }
      return item.disabled
    },
    selectChange (item) {
      if (item.trigger) {
        item.trigger.call(this.data, item)
      }
    },
    localSelect (item, data) {
      if (item.select) {
        item.select.call(data, item)
      }
    },
    customOptions (item) {
      if (!item.options) return []
      if (typeof item.options === 'function') {
        return item.options.call(this.data)
      } else if (!Array.isArray(item.options)) {
        return Object.entries(item.options).map(([label, value]) => {
          return {
            label,
            value
          }
        })
      } else {
        return item.options
      }
    },
    customGroups (item) {
      if (typeof item.groups === 'function') {
        return item.groups.call(this.data)
      } else {
        return item.groups
      }
    },
    changeNumber (prop, data) {
      this.data[prop] = parseFloat(data)
    },
    handlePictureCardPreview (file) {
      this.dialogImageUrl = file.url
      this.dialogVisible = true
    },
    handlePictureCardRemove (prop, file, fileList) {
      this.$set(this.data, prop, fileList)
    },
    handlePictureSuccess (prop, res, file, fileList) {
      this.$set(this.data, prop, fileList)
    }
  }
}
</script>
<style lang="stylus" scoped>
.smart-form
.el-select,
.el-autocomplete,
.el-date-editor.el-input,
.el-cascader {
  width: 100%;
}
>>>.el-form-item__content .el-input-group {
  vertical-align: bottom;
}
>>>.el-form-item--mini .el-form-item__error {
  padding-top 4px
}

>>>.el-form-item__label {
  width 100px
  font-size 12px
  color #999
  line-height 29px
}
>>>.el-form-item__label {
  float none
}
>>>.el-form-item__content {
  margin-left 0!important
}
.el-icon-question {
  margin-left 5px
}
.checkbox-group {
  // margin-top 10px
}
.el-divider {
  margin-top 6px
}
>>>.slot {
  .el-row {
    margin-left 0!important
    .el-col {
      padding-left 0!important
    }
    &:last-child{
      .el-form-item {
        margin-bottom 0!important
      }
    }
  }
}
.SmartConfForm {
  .el-row {
    margin-left 0!important
    &:last-child{
      .el-form-item {
        margin-bottom 18px!important
      }
    }
  }
}
.el-upload {
  position relative
  .thumb {
    position absolute
    top 1px
    left 1px
    width 146px
    height 146px
  }
}
</style>
