<template>
  <component
    :is="!autoContent?'kyb-layout':'div'"
    type="column"
    :class="autoContent?'auto-content':''"
  >
    <component
      :is="!autoContent?'kyb-layout-item':'div'"
      type="top"
      size="40"
      v-if="showToolbar"
      class="bg-grey-3"
    >
      <slot name="top">
        <div class="row">
        <kyb-toolbar :items="toolbarList" v-if="showToolbar" class="kyb-form-header">
          <template #save="{isVisble}" v-if="canEdit">
            <!-- 保存 -->
            <q-btn
              :style="{visibility:isVisble?'visible':'hidden'}"
              :icon="isEdit?'lock_open':'lock'"
              title="编辑"
              dense
              flat
              :color="isEdit?'primary':'negative'"
              @click="isEdit=!isEdit"
            />
          </template>
        </kyb-toolbar>
        <slot name="top-right"></slot>
        </div>
      </slot>
    </component>
    <component :is="!autoContent?'kyb-layout-item':'div'" type="center">
      <form
        class="q-ma-sm kyb-form"
        :class="(mdata.isGroup?'form-group':'')+(mdata.listClass?' '+mdata.listClass:'')"
        @submit.prevent="save"
      >
        <kyb-group
          ref="groupItems"
          :is-group="mdata.isGroup"
          :items="showMDataItems"
          item-class="bg-grey-2"
        >
          <template #default="{group,groupIndex}">
            <slot :name="`group-${group.type}-${group.inputType}`" v-bind="{group,groupIndex}">
              <template v-for="(item,index) in group.items">
                <slot :name="item.inputType" v-bind="{item,group,fdata}">
                  <kyb-form-item
                    :ref="`form_item_${item.name}`"
                    v-show="group.extend"
                    :entity="entity"
                    :key="index"
                    :mdata="item"
                    :showonly="item.showonly || readonlyFields.findIndex(rf=>rf === item.name)!==-1 || !isEdit"
                    @submit="save"
                    v-model="fdata"
                    @input="item.inputType !== 'input' && $emit('item-change', item, fdata[item.name], fdata)"
                    @item-blur="$emit('item-blur', item, fdata[item.name], fdata)"
                    @option-click="$emit('item-option-click', item, fdata[item.name])"
                  >
                    <template #item-title="mdata">
                      <slot name="form-item-title" v-bind="mdata"></slot>
                    </template>
                    <template #input-linkEntity="{mdata, fdata, props}">
                      <slot name="form-item-input-linkEntity" v-bind="{item, group, mdata, fdata, props}"></slot>
                    </template>
                  </kyb-form-item>
                </slot>
              </template>
            </slot>
          </template>
        </kyb-group>
      </form>
      <slot name="content-ext"></slot>
    </component>
    <component
      :is="!autoContent?'kyb-layout-item':'div'"
      type="bottom"
      size="44"
      v-if="isEdit&&!hideBottom"
      class="bg-grey-1"
    >
      <div class="kyb-form-bottom q-pa-xs">
        <slot name="bottom" v-bind="{save,submitting,resetValue,resetValue}">
          <template>
            <q-btn
              label="提交"
              icon="save"
              unelevated
              color="primary"
              @click="save"
              :loading="submitting"
              class="q-mr-xs"
            ></q-btn>
            <q-btn
              label="重置"
              icon="clear"
              @click="resetValue"
              unelevated
              color="grey-7"
              class="q-mr-xs"
            ></q-btn>
            <!-- 暂时注释，用户容易误操作 -->
            <!-- <q-btn
              label="新建一条"
              v-if="fdata._id"
              icon
              @click="resetValue"
              unelevated
              color="grey-7"
              class="q-mr-xs"
            ></q-btn>-->
          </template>
          <!-- {{fdata._id}} -->
        </slot>
      </div>
    </component>
  </component>
</template>

<script>
import { uid } from 'quasar'
import _ from 'underscore'
import kybFormToolbar from './kyb-form-toolbar.js'
import KybFormItem from './kyb-form-item.vue'
import KybMdata from '../kyb-mdata.js'

let d = {
  name: 'kyb-form',
  mixins: [KybMdata],
  model: {
    prop: 'initData',
    event: 'input'
  },
  components: { KybFormItem },
  props: {
    title: String,
    entityId: String,
    entity: String,
    entityDataId: String,
    includeField: Array,
    excludeField: Array, // 如果同时设置了includeField和excludeField，则忽略excludeField；
    isAdd: Boolean, // 是否添加数据
    showToolbar: {
      type: Boolean,
      default: true
    },
    // 模式
    kybIsEdit: {
      type: Boolean,
      default: true
    },
    // 是否能编辑
    kybCanEdit: {
      type: Boolean,
      default: true
    },
    // 是否隐藏底部
    hideBottom: {
      type: Boolean,
      default: false
    },
    kybMdata: {
      type: Object,
      default: () => {
        return {}
      }
    },
    initData: {
      type: Object,
      default: () => {
        return {}
      }
    },
    submitUrl: String,
    submitAction: {
      type: Function
    },
    submitBefore: {
      type: Function
    },
    submitCheck: {
      type: Function
    },
    readonlyFields: {
      type: Array,
      default: () => {
        return []
      }
    },
    toolbarExt: {
      type: Array,
      default: () => {
        return ['save', 'refresh']
      }
    },
    autoContent: Boolean,
    formatMdata: Function
  },
  data: function() {
    let d = {
      publicDate: null,
      publicTime: null,
      submitting: false,
      showInputError: false,
      selectField: null,
      mdata: this.kybMdata, // JSON.parse(JSON.stringify(this.kybMdata)),
      fdata: this.initData,
      isEdit: this.kybIsEdit,
      canEdit: this.kybCanEdit
    }
    return d
  },
  computed: {
    toolbarList() {
      const toolbar = kybFormToolbar({ comp: this })
      let title = this.title
      if (!title && this.fdata) {
        title = this.fdata.title ? this.fdata.title : this.fdata.name
      }
      let li = [
        {
          type: 'text',
          iconColor: 'grey-6',
          icon: 'view_list',
          name: title,
          class: 'ellipsis',
          style: { 'max-width': '428px' }
        }
      ]
      for (const item of this.toolbarExt) {
        if (typeof item === 'string') {
          toolbar.itemsMap[item] && li.push(toolbar.itemsMap[item])
        } else if (toolbar.itemsMap[item.name]) {
          li.push(Object.assign({}, toolbar.itemsMap[item.name], item))
        } else {
          li.push(item)
        }
      }
      return li
    },
    showMDataItems() {
      let inMap = null,
        exMap = null
      if (this.includeField && this.includeField.length) {
        inMap = {}
        this.includeField.forEach(item => {
          inMap[item] = true
        })
      } else {
        if (this.excludeField && this.excludeField.length) {
          exMap = {}
          this.excludeField.forEach(item => {
            exMap[item] = true
          })
        }
      }
      let f = item => {
        // 控制表单项是否显示
        let otherShow =
          (!item.hide || item.hide === 'show') &&
          (!this.isEdit || !item.setByServer)
        // let isShow = false
        if (inMap) {
          return inMap[item.name] && otherShow
        } else {
          if (exMap) {
            return !exMap[item.name] && otherShow
          } else {
            return otherShow
          }
        }
      }
      let m = this.mdata && this.mdata.items ? this.mdata.items.filter(f) : []
      return m
    }
  },
  beforeCreate: function() {},
  created: async function() {
    if (this.entity) {
      await this.queryMData()
      if (this.entityDataId) {
        this.fdata || (this.fdata = {})
        this.fdata._id = this.entityDataId
        await this.refresh()
      }
    } else {
      this.fixMData()
    }
    this.initDefaultValue()
  },
  watch: {
    entity(old) {
      this.queryMData()
    },
    async entityDataId(old) {
      this.fdata || (this.fdata = {})
      this.fdata._id = old
      if (this.$refs.groupItems) {
        this.$refs.groupItems.$forceUpdate()
      }
      await this.refresh()
    },
    kybMdata(newVal) {
      this.mdata = newVal
      this.fixMData()
    },
    kybCanEdit(old) {
      this.canEdit = old
    },
    kybIsEdit(old) {
      this.isEdit = old
    },
    initData(old) {
      this.fdata = old
    },
    includeField(old) {
      this.includeField = old
    },
    excludeField(old) {
      this.excludeField = old
    }
  },
  methods: {
    async queryMData() {
      if (this.entity) {
        let response = await this.$axios.get(await this.genBaseUrl('/mdata'))
        // 初始化
        this.mdata = response.data
        this.fixMData()
      }
    },
    async fixMData() {
      if (!this.mdata || !this.mdata.items) {
        return
      }
      this.mdata.itemMap || (this.mdata.itemMap = {})
      this.mdata.items.forEach(async item => {
        this.mdata.itemMap[item.name] = item
      })
      this.formatMdata && (await this.formatMdata(this.mdata))
      this.$forceUpdate()
    },
    // 选择实体时，选择变化的事件
    entitySelectedChange(changeObj) {
      if (this.selectField) {
        this.fdata[this.selectField.name] = _.map(
          changeObj,
          (v, k) => v
        ).filter(v => v)
        console.log(changeObj, this.fdata[this.selectField.name])
      }
    },
    // 通过服务器获取数据的最新版本
    refresh: async function() {
      try {
        if (!this.entity) {
          return
        }
        let re = await this.$axios.get(
          (await this.genBaseUrl('/show')) + '?_id=' + this.fdata._id
        )
        // Object.assign(this.fdata, re.data)
        this.fdata = re.data
        this.initDefaultValue()
        this.$emit('refresh-after', this.fdata)
        this.$forceUpdate()
      } catch (e) {
        console.error(e)
        // throw new Error(e)
      }
    },
    enterSave($event) {
      console.log($event)
      if ($event.ctrlKey) return
      this.save()
    },
    save: async function() {
      if (this.submitting) {
        this.$q.showNotifi('表单提交中, 请稍后...')
        return
      }
      let errorNum = 0
      this.showInputError = false
      // 额外检查
      if (this.submitCheck) {
        let error = await this.submitCheck(this.mdata, this.fdata)
        if (error) {
          return
        }
      }
      let owner = this
      this.mdata.items.forEach(v => {
        if (!v.setByServer && !v.hide) {
          let obj = owner.$refs[`form_item_${v.name}`]
          obj = obj && obj.length ? owner.$refs[`form_item_${v.name}`][0] : null
          if (obj && obj.error && Object.keys(obj.error).length > 0) {
            errorNum++
          }
        }
      })
      if (errorNum > 0) {
        this.$q.showNotifi(`表单 ${errorNum} 项输入错误, 请检查...`)
        this.showInputError = true
        return
      }

      this.submitting = true
      try {
        // let fdata = JSON.parse(JSON.stringify(this.fdata))
        let mdata = JSON.parse(JSON.stringify(this.mdata))
        // 如果有_id字段，就初始化字段
        const fdata = JSON.parse(JSON.stringify(this.fdata))
        if (
          mdata.items.findIndex(item => item.name === '_id') > -1 &&
          !fdata._id
        ) {
          // this.isAdd = true
          fdata._id = uid()
        }
        this.$emit('save-before', 'save', mdata, fdata)
        if (this.submitBefore) {
          this.submitBefore(mdata, fdata)
        }
        if (this.submitAction) {
          this.submitAction(mdata, fdata)
        } else if (this.submitUrl || this.entity) {
          let re = await this.$axios.post(
            this.submitUrl ? this.submitUrl : await this.genBaseUrl('/save'),
            fdata
          )
          this.$q.showNotifi('操作成功')
          if (this.entity) {
            await this.refresh()
          }

          // fdata = JSON.parse(JSON.stringify(this.fdata))
          this.$emit(
            'save-after',
            this.isAdd ? 'add' : 'save',
            mdata,
            fdata,
            re
          )
        } else {
          console.log('warn: 未设定提交事件')
          this.$emit(
            'save-after',
            this.isAdd ? 'add' : 'save',
            mdata,
            fdata
          )
        }
        // if (this.submitBefore) {
        //   this.submitBefore(this.fdata, mdata)
        // }
        this.$emit('input', fdata)
        // console.log('提交后数据为：', fdata)
      } catch (e) {
        throw e
      } finally {
        this.submitting = false
      }
    },
    initDefaultValue: function() {
      // 初始化默认值
      // 根据元数据定义的初始值来初始化数据
      // alert(JSON.stringify(this.fdata));
      if (this.mdata && this.mdata.items && this.fdata) {
        this.mdata.items.forEach(item => {
          if (item.inputType === 'password') {
            this.fdata[item.name] = ''
          }
          if (item.defaultValue && !this.fdata[item.name]) {
            // eslint-disable-next-line no-template-curly-in-string
            if (item.defaultValue === '${uuid}') {
              this.fdata[item.name] = uid()
              // eslint-disable-next-line no-template-curly-in-string
            } else {
              this.fdata[item.name] = item.defaultValue
            }
          }
        })
      }
    },
    async resetValue() {
      this.$q.showConfirm('是否重置表单？', async () => {
        if (this.submitting) {
          this.$q.showNotifi('表单提交中, 请稍后...')
          return
        }
        if (this.isAdd) {
          this.$set(this, 'fdata', {
            _id: ''.genUUID()
          })
          console.log('重置后数据为：', this.fdata)
        } else {
          await this.refresh()
          this.initDefaultValue()
        }
      })
    }
  }
}
export default d
</script>
<style lang="stylus">
.kyb-form, .kyb-form-header, .kyb-form-bottom {
  flex: 1 1 auto;
  min-width: 250px;
  max-width: 800px;
  margin: auto;
}
</style>
