封装一个vue的表单组件

in 前端javascript with 0 comment

封装一个vue的表单组件

smart-form

<template>
    <form>
        <slot></slot>
    </form>
</template>
<script>
export default {
    name: 'smart-form',
    props: {
        model: Object
    },
    componentName: 'smartForm',
    provide () {
        return {
            smartForm: this
        }
    },
    data () {
        return {
            fields: []
        }
    },
    created () {
        this.$on('smart.form.addField', field => {
            this.fields.push(field)
        })
        this.$on('smart.form.removeField', field => {
            const idx = this.fields.indexOf(field)
            if (idx > -1) {
                this.fields.splice(idx, 1)
            }
        })
    }
}
</script>

smart-form-item

<template>
    <div>
        <slot></slot>
        <smart-transition name='fade'>
            <div class="error" :style='{"color": errorColor, "font-size": errorSize+"px"}' v-if='validateMessage'>{{ validateMessage }}</div>
        </smart-transition>
    </div>
</template>
<script>
import AsyncValidator from 'async-validator'
export default {
    name: 'smart-form-item',
    components: {
        'smart-transition': () => import('@/components/smart-transition/smart-transition')
    },
    componentName: 'smartFormItem',
    props: {
        prop: String,
        rules: [Object, Array],
        errorColor: {
            type: String,
            default: 'red'
        },
        errorSize: {
            type: Number,
            default: 12
        }
    },
    inject: ['smartForm'],
    data () {
        return {
            validateMessage: '',
            triggerBlurs: [],
            triggerChanges: []
        }
    },
    computed: {
        form () {
            let parent = this.$parent
            let parentName = parent.$options.componentName
            while (parentName !== 'smartForm') {
                parent = parent.$parent
                parentName = parent.$options.componentName
            }

            return parent
        },
        fieldValue () {
            // const model = this.form.model
            const model = this.smartForm.model
            return model[this.prop]
        }
    },
    created () {
        if (this.rules && this.rules.length > 0) {
            this.triggerBlurs = this.rules.filter(rule => rule.trigger === 'blur')
            this.triggerChanges = this.rules.filter(rule => rule.trigger === 'change')
        }
    },
    methods: {
        validate (trigger = 'blur') {
            if (!(this.rules && this.rules.length > 0)) { return }
            const descriptor = {
                [this.prop]: trigger === 'blur' ? this.rules : this.triggerChanges
            }
            const validator = new AsyncValidator(descriptor)

            validator.validate({ [this.prop]: this.fieldValue }, (erros, field) => {
                if (erros) {
                    this.validateMessage = erros[0].message
                } else {
                    this.validateMessage = ''
                }
            })
        }
    },
    mounted () {
        if (this.prop) {
            this.smartForm.$emit('smart.form.addField', this)
        }

        this.$on('smart.form.blur', () => {
            this.validate()
        })

        this.$on('smart.form.input', () => {
            this.validate('change')
        })
    },
    beforeDestroy () {
        this.smartForm.$emit('smart.form.removeField', this)
    }
}
</script>

smart-input

<template>
    <div>
        <input :type="type"
            :placeholder='placeholder'
            @input='handleInput'
            @blur='handleBlur'>
    </div>
</template>
<script>
export default {
    name: 'smart-input',
    componentName: 'smartInput',
    props: {
        placeholder: {
            type: String,
            default: ''
        },
        type: {
            type: String,
            default: 'text'
        }
    },
    data () {
        return {

        }
    },
    methods: {
        dispatch (componentName, event, context) {
            function parentTarget (cn, currentTarget) {
                let parent = currentTarget.$parent
                let parentComponentName = parent.$options.componentName
                while (parentComponentName !== componentName && parent) {
                    parent = parent.$parent
                    parentComponentName = parent.$options.componentName
                }

                return parent
            }

            const parent = parentTarget(componentName, this)
            parent.$emit(event, context)
        },
        handleInput (e) {
            this.$emit('input', e.target.value)
            this.dispatch('smartFormItem', 'smart.form.input')
        },
        handleBlur () {
            this.dispatch('smartFormItem', 'smart.form.blur')
        }
    }
}
</script>

smart-transition

<style scoped>
.fade-enter-active,
.fade-leave-active {
    transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-to {
    opacity: 0;
}
</style>

<template>
    <transition :name='name'>
        <slot></slot>
    </transition>
</template>

<script>
export default {
    name: 'smart-transition',
    componentName: 'smartTransition'
    props: {
        name: {
            type: String,
            default: 'fade'
        }
    },
    componentName: 'smartTransition'
}
</script>
Responses