<template> <div :class="classes"> <label :class="[prefixCls + '-label']" :style="labelStyles" v-if="label">{{ label }}</label> <div :class="[prefixCls + '-content']" :style="contentStyles"> <slot></slot> <div transition="fade" :class="[prefixCls + '-error-tip']" v-if="validateState === 'error'">{{ validateMessage }}</div> </div> </div> </template> <script> // https://github.com/ElemeFE/element/blob/dev/packages/form/src/form-item.vue import AsyncValidator from 'async-validator'; const prefixCls = 'ivu-form-item'; function getPropByPath(obj, path) { let tempObj = obj; path = path.replace(/\[(\w+)\]/g, '.$1'); path = path.replace(/^\./, ''); let keyArr = path.split('.'); let i = 0; for (let len = keyArr.length; i < len - 1; ++i) { let key = keyArr[i]; if (key in tempObj) { tempObj = tempObj[key]; } else { throw new Error('[iView warn]: please transfer a valid prop path to form item!'); } } return { o: tempObj, k: keyArr[i], v: tempObj[keyArr[i]] }; } export default { props: { label: { type: String, default: '' }, labelWidth: { type: Number }, prop: { type: String }, required: { type: Boolean, default: false }, rules: { type: [Object, Array] }, error: { type: String }, validateStatus: { type: Boolean } }, data () { return { prefixCls: prefixCls, isRequired: false, validateState: '', validateMessage: '', validateDisabled: false, validator: {} }; }, watch: { error (val) { this.validateMessage = val; this.validateState = 'error'; }, validateStatus (val) { this.validateState = val; } }, computed: { classes () { return [ `${prefixCls}`, { [`${prefixCls}-required`]: this.required || this.isRequired, [`${prefixCls}-error`]: this.validateState === 'error', [`${prefixCls}-validating`]: this.validateState === 'validating' } ]; }, form() { let parent = this.$parent; while (parent.$options.name !== 'iForm') { parent = parent.$parent; } return parent; }, fieldValue: { cache: false, get() { const model = this.form.model; if (!model || !this.prop) { return; } let path = this.prop; if (path.indexOf(':') !== -1) { path = path.replace(/:/, '.'); } return getPropByPath(model, path).v; } }, labelStyles () { let style = {}; const labelWidth = this.labelWidth || this.form.labelWidth; if (labelWidth) { style.width = `${labelWidth}px`; } return style; }, contentStyles () { let style = {}; const labelWidth = this.labelWidth || this.form.labelWidth; if (labelWidth) { style.marginLeft = `${labelWidth}px`; } return style; } }, methods: { getRules () { let formRules = this.form.rules; const selfRules = this.rules; formRules = formRules ? formRules[this.prop] : []; return [].concat(selfRules || formRules || []); }, getFilteredRule (trigger) { const rules = this.getRules(); return rules.filter(rule => !rule.trigger || rule.trigger.indexOf(trigger) !== -1); }, validate(trigger, callback = function () {}) { const rules = this.getFilteredRule(trigger); if (!rules || rules.length === 0) { callback(); return true; } this.validateState = 'validating'; let descriptor = {}; descriptor[this.prop] = rules; const validator = new AsyncValidator(descriptor); let model = {}; model[this.prop] = this.fieldValue; validator.validate(model, { firstFields: true }, errors => { this.validateState = !errors ? 'success' : 'error'; this.validateMessage = errors ? errors[0].message : ''; callback(this.validateMessage); }); }, resetField () { this.validateState = ''; this.validateMessage = ''; let model = this.form.model; let value = this.fieldValue; let path = this.prop; if (path.indexOf(':') !== -1) { path = path.replace(/:/, '.'); } let prop = getPropByPath(model, path); if (Array.isArray(value) && value.length > 0) { this.validateDisabled = true; prop.o[prop.k] = []; } else if (value) { this.validateDisabled = true; prop.o[prop.k] = this.initialValue; } }, onFieldBlur() { this.validate('blur'); }, onFieldChange() { if (this.validateDisabled) { this.validateDisabled = false; return; } this.validate('change'); } }, ready () { if (this.prop) { this.$dispatch('on-form-item-add', this); Object.defineProperty(this, 'initialValue', { value: this.fieldValue }); let rules = this.getRules(); if (rules.length) { rules.every(rule => { if (rule.required) { this.isRequired = true; return false; } }); this.$on('on-form-blur', this.onFieldBlur); this.$on('on-form-change', this.onFieldChange); } } }, beforeDestroy () { this.$dispatch('on-form-item-remove', this); } }; </script>