<template>
    <el-form
        label-width="auto"
        ref="form"
        :model="form"
    >
        <el-form-item
            :class="{ mb0: outType(item.type), sub: subType(item.type) }"
            v-for="(item, index) in list"
            :key="index"
            :prop="index + ''"
            :rules="!form[index].hide ? rules(item) : []"
            :info.prop="info(title(item), form[index].value, item.type, form[index].hide)"
            v-show="!form[index].hide"
            v-bind="form[index].hide ? { 'label-width': '100px' } : {}"
        >
            <template slot="label">
                <el-tooltip :content="item.info" v-if="item.info">
                    <i class="info el-icon-info"></i>
                </el-tooltip>
                {{title(item)}}{{item.type !== 'info' ? '：' : ''}}
            </template>
            <component
                :is="'form-' + item.type"
                :form="form"
                :subform="form"
                :list-item="item"
                :form-item="form[index]"
                :form-id="formId"
                ref="component"
            >
                <!-- 子表单 -->
                <template v-if="item.type === 'subform'">
                    <el-form
                        class="subform"
                        ref="subform"
                        label-width="auto"
                        v-for="(valueItem, valueIndex) in form[index].value"
                        :key="valueIndex"
                        :model="valueItem"
                    >
                        <div class="title">
                            <span>{{title(item)}} {{valueIndex + 1}}</span>
                            <el-link
                                type="danger"
                                icon="el-icon-delete"
                                @click="form[index].value.splice(valueIndex, 1)"
                                v-if="valueIndex"
                            >删除</el-link>
                        </div>
                        <el-form-item
                            :class="{ mb0: outType(subItem.type), sub: subType(subItem.type) }"
                            v-for="(subItem, subIndex) in item.list"
                            :key="subIndex"
                            :prop="subIndex + ''"
                            :rules="!valueItem[subIndex].hide ? rules(subItem) : []"
                            :info.prop="info(title(subItem), valueItem[subIndex].value, subItem.type, valueItem[subIndex].hide)"
                            v-show="!valueItem[subIndex].hide"
                        >
                            <template slot="label">
                                <el-tooltip :content="subItem.info" v-if="subItem.info">
                                    <i class="info el-icon-info"></i>
                                </el-tooltip>
                                {{title(subItem)}}{{subItem.type !== 'info' ? '：' : ''}}
                            </template>
                            <component
                                :is="'form-' + subItem.type"
                                :form="form"
                                :list-item="subItem"
                                :form-item="valueItem[subIndex]"
                                :form-id="formId"
                                :subform="valueItem"
                                :subform-uuid="item.uuid"
                                :value-index="valueIndex"
                                ref="component"
                            ></component>
                        </el-form-item>
                    </el-form>
                </template>
            </component>
        </el-form-item>
        <el-form-item v-if="!camera && !hideBtn">
            <slot name="before"></slot>
            <slot>
                <el-button type="primary" @click="submit">{{ btnText }}</el-button>
            </slot>
            <slot name="after"></slot>
        </el-form-item>
        <template v-if="camera">{{ cameraTips }}</template>
    </el-form>
</template>

<script>
import mixin from './mixin.js'
export default {
    name: 'FormRender',
    components: {
        FormInput: () => import('./components/FormInput'),
        FormTextarea: () => import('./components/FormTextarea'),
        FormRadio: () => import('./components/FormRadio'),
        FormCheckbox: () => import('./components/FormCheckbox'),
        FormDate: () => import('./components/FormDate'),
        FormDateRange: () => import('./components/FormDateRange'),
        FormAddress: () => import('./components/FormAddress'),
        FormImg: () => import('./components/FormImg'),
        FormFile: () => import('./components/FormFile'),
        FormContacts: () => import('./components/FormContacts'),
        FormInfo: () => import('./components/FormInfo'),
        FormSubform: () => import('./components/FormSubform'),
        FormCalculate: () => import('./components/FormCalculate')
    },
    props: {
        formId: { type: String, default: '' }, // 表单 id
        applicable: { type: String, default: 'Form' }, // 控件类型，暂定 Form 和 Question
        btnText: { type: String, default: '提交' }, // 提交按钮文字
        hideBtn: { type: Boolean, default: false }, // 是否隐藏提交按钮
        watch: { type: Array, default: () => [] } // 监听指定对象的值
    },
    mixins: [mixin],
    data () {
        return {
            list: [], // 表单列表
            form: {}, // 表单数据
            ready: false, // form 是否准备好
            watchList: [], // 监听回调，避免重复监听
            camera: false, // 是否必需要拍照则不渲染表单
            cameraTips: '本表单必须要拍照上传图片，请在手机端进行填写表单'
        }
    },
    watch: {
        formId: {
            handler (formId) {
                if (this.formId) this.getList()
            },
            immediate: true
        },
        watch () {
            // 如果 form 准备好了可以监听，否则 form 准备好会监听
            if (this.ready) this.watchValue()
        }
    },
    methods: {
        // 获取表单列表
        getList () {
            this.$api.commonApi.formInfoGet({
                b_id: this.formId
            }).then(res => {
                if (res.data.Status) {
                    const list = JSON.parse(res.data.Data.content)
                    this.verifyCamera(list)
                    if (!this.camera) {
                        this.list = list
                        this.init()
                    }
                }
            })
        },
        // 判断上传图片如果必填且仅拍照则整个表单都不渲染
        verifyCamera (dataList) {
            const verifyCamera = list => {
                list.forEach(item => {
                    const { type, verify } = item
                    if (type === 'img' && verify.required && verify.onlyCamera) this.camera = true
                    if (type === 'subform') verifyCamera(item.list)
                })
            }
            verifyCamera(dataList)
        },
        // 初始化表单
        init () {
            const dataForm = {}
            const objectType = type => /dateRange|address/.test(type)
            const arrayType = type => /checkbox|img|file|contacts|subform/.test(type)
            // 表单数据对象
            const addForm = (list, form) => {
                list.forEach((item, index) => {
                    form[index] = {
                        uuid: item.uuid,
                        type: item.type,
                        value: arrayType(item.type) ? [] : objectType(item.type) ? {} : (item.value || '')
                    }
                    const { value } = form[index]
                    if (item.type === 'dateRange') {
                        value.start = ''
                        value.end = ''
                        if (item.date.time) {
                            value.result = ''
                            value.type = item.date.unit
                        }
                    }
                    if (item.type === 'address') {
                        value.region = item.value
                        if (item.address.type === 1) value.detail = item.value2 || ''
                    }
                    if (item.type === 'contacts') {
                        if (item.verify.value.length) value.push(...item.verify.value)
                    }
                    if (item.type === 'subform') {
                        value.push({})
                        addForm(item.list, value[0])
                    }
                })
            }
            // 单选关联
            const addRadio = (list, form) => {
                list.forEach((item, index) => {
                    if (item.type === 'radio') this.radioHide(form, item.option.list, item.option.checked) // mixin
                    if (item.type === 'subform') addRadio(item.list, form[index].value[0])
                })
            }
            addForm(this.list, dataForm)
            addRadio(this.list, dataForm)
            this.form = dataForm
            this.ready = true
            this.watchValue()
            this.$emit('init', this.list, this.form)
        },
        // 获取标题
        title (item) {
            if (item.type === 'dateRange') {
                const title = [item.title, item.title2]
                if (item.date.time) title.push(item.date.title)
                return title.reduce((long, n) => long.length > n.length ? long : n, '')
            } else {
                return item.title
            }
        },
        // 外部表单（去掉下边距避免和内部表单的重复）
        outType (type) {
            return /input|dateRange|address/.test(type)
        },
        // 内部表单（label 内部显示并靠左对齐）
        subType (type) {
            return /dateRange/.test(type)
        },
        // 获取校验规则（mixin）
        rules (item) {
            const rules = []
            if (item.verify && item.verify.required) {
                const required = this.outType(item.type) ? { required: true } : this.required(this.title(item), true)
                rules.push(required)
            }
            if (item.type === 'textarea' && item.verify.length) {
                if (item.verify.lengthMin) rules.push(this.lengthMin(item.verify.lengthMin, true))
                if (item.verify.lengthMax) rules.push(this.lengthMax(item.verify.lengthMax, true))
            }
            if (item.type === 'checkbox' && item.verify.length) {
                if (item.verify.lengthMin) rules.push(this.checkboxMin(item.verify.lengthMin))
                if (item.verify.lengthMax) rules.push(this.checkboxMax(item.verify.lengthMax))
            }
            if (item.type === 'img') rules.push(this.img(item))
            return rules
        },
        // 校验表单
        verify () {
            return new Promise((resolve, reject) => {
                if (this.camera) {
                    this.$message(this.cameraTips)
                } else {
                    const componentList = !this.$refs.component ? [] : this.$refs.component
                        .filter(item => item.formItem && !item.formItem.hide && item.verify)
                        .map(item => item.verify())
                    const subformList = !this.$refs.subform ? [] : this.$refs.subform.map(item => item.validate())
                    const form = this.$refs.form.validate()
                    Promise.all([...componentList, ...subformList, form]).then(resolve).catch(() => {})
                }
            })
        },
        // form 转数组（排除隐藏的和说明文字）
        formatForm () {
            const dataForm = JSON.parse(JSON.stringify(this.form))
            const dataList = []
            const addList = (list, form) => {
                for (let i in form) {
                    const item = form[i]
                    if (!item.hide && item.type !== 'info') {
                        if (item.hide === false) delete item.hide
                        if (item.type === 'subform') {
                            const value = []
                            item.value.forEach((subform, subIndex) => {
                                value.push([])
                                addList(value[subIndex], subform)
                            })
                            item.value = value
                        }
                        list.push(item)
                    }
                }
            }
            addList(dataList, dataForm)
            return JSON.stringify(dataList)
        },
        // 提交
        submit () {
            this.verify().then(() => {
                this.$api.commonApi.formRecordCreate({
                    form_id: this.formId,
                    value: this.formatForm()
                }).then(res => {
                    if (res.data.Status) {
                        const $item = [...this.$el.querySelectorAll('.el-form-item')]
                        const infoList = $item.filter(item => item.info).map(item => JSON.parse(item.info))
                        this.$emit('submit', res.data.Data, infoList)
                    }
                })
            })
        },
        // 监听指定的值
        watchValue () {
            if (this.watch.length) {
                // 取消之前的监听
                if (this.watchList.length) {
                    for (let item of this.watchList) item.unwatch()
                    this.watchList = []
                }
                // 监听指定的值
                for (let index in this.form) {
                    const item = this.form[index]
                    if (this.watch.indexOf(item.uuid) > -1) {
                        const unwatch = this.$watch(() => item.value, this.emitWatch, { deep: true })
                        this.watchList.push({ index, unwatch })
                    }
                }
                // 第一次触发一次（控件有默认值的情况）
                this.emitWatch()
            }
        },
        // 触发回调
        emitWatch () {
            const valueList = this.watchList.map(item => this.form[item.index])
            this.$emit('watch', valueList)
        }
    }
}
</script>

<style lang="less" scoped>
/deep/ .sub > .el-form-item__label-wrap {
    height: 0;
    overflow: hidden;
}
.el-form .mb0 {
    margin-bottom: 0;
}
.el-form .el-select {
    display: block;
}
/deep/ .el-date-editor.el-input {
    width: 100%;
}
/deep/ .el-form-item__label::before {
    margin-right: 0 !important;
}
.info {
    margin-left: 3px;
}
.subform {
    padding: 0 20px;
    .title {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 0 15px;
        margin: 0 -20px 18px;
        background-color: #EBEEF5;
        .el-link {
            float: right;
            line-height: 20px;
        }
    }
}
</style>
