1 | <template>
|
2 | <div class="el-form-item" :class="[{
|
3 | 'el-form-item--feedback': elForm && elForm.statusIcon,
|
4 | 'is-error': validateState === 'error',
|
5 | 'is-validating': validateState === 'validating',
|
6 | 'is-success': validateState === 'success',
|
7 | 'is-required': isRequired || required
|
8 | },
|
9 | sizeClass ? 'el-form-item--' + sizeClass : ''
|
10 | ]">
|
11 | <label :for="labelFor" class="el-form-item__label" :style="labelStyle" v-if="label || $slots.label">
|
12 | <slot name="label">{{label + form.labelSuffix}}</slot>
|
13 | </label>
|
14 | <div class="el-form-item__content" :style="contentStyle">
|
15 | <slot></slot>
|
16 | <transition name="el-zoom-in-top">
|
17 | <slot
|
18 | v-if="validateState === 'error' && showMessage && form.showMessage"
|
19 | name="error"
|
20 | :error="validateMessage">
|
21 | <div
|
22 | class="el-form-item__error"
|
23 | :class="{
|
24 | 'el-form-item__error--inline': typeof inlineMessage === 'boolean'
|
25 | ? inlineMessage
|
26 | : (elForm && elForm.inlineMessage || false)
|
27 | }"
|
28 | >
|
29 | {{validateMessage}}
|
30 | </div>
|
31 | </slot>
|
32 | </transition>
|
33 | </div>
|
34 | </div>
|
35 | </template>
|
36 | <script>
|
37 | import AsyncValidator from 'async-validator';
|
38 | import emitter from 'element-ui/src/mixins/emitter';
|
39 | import objectAssign from 'element-ui/src/utils/merge';
|
40 | import { noop, getPropByPath } from 'element-ui/src/utils/util';
|
41 |
|
42 | export default {
|
43 | name: 'ElFormItem',
|
44 |
|
45 | componentName: 'ElFormItem',
|
46 |
|
47 | mixins: [emitter],
|
48 |
|
49 | provide() {
|
50 | return {
|
51 | elFormItem: this
|
52 | };
|
53 | },
|
54 |
|
55 | inject: ['elForm'],
|
56 |
|
57 | props: {
|
58 | label: String,
|
59 | labelWidth: String,
|
60 | prop: String,
|
61 | required: {
|
62 | type: Boolean,
|
63 | default: undefined
|
64 | },
|
65 | rules: [Object, Array],
|
66 | error: String,
|
67 | validateStatus: String,
|
68 | for: String,
|
69 | inlineMessage: {
|
70 | type: [String, Boolean],
|
71 | default: ''
|
72 | },
|
73 | showMessage: {
|
74 | type: Boolean,
|
75 | default: true
|
76 | },
|
77 | size: String
|
78 | },
|
79 | watch: {
|
80 | error: {
|
81 | immediate: true,
|
82 | handler(value) {
|
83 | this.validateMessage = value;
|
84 | this.validateState = value ? 'error' : '';
|
85 | }
|
86 | },
|
87 | validateStatus(value) {
|
88 | this.validateState = value;
|
89 | }
|
90 | },
|
91 | computed: {
|
92 | labelFor() {
|
93 | return this.for || this.prop;
|
94 | },
|
95 | labelStyle() {
|
96 | const ret = {};
|
97 | if (this.form.labelPosition === 'top') return ret;
|
98 | const labelWidth = this.labelWidth || this.form.labelWidth;
|
99 | if (labelWidth) {
|
100 | ret.width = labelWidth;
|
101 | }
|
102 | return ret;
|
103 | },
|
104 | contentStyle() {
|
105 | const ret = {};
|
106 | const label = this.label;
|
107 | if (this.form.labelPosition === 'top' || this.form.inline) return ret;
|
108 | if (!label && !this.labelWidth && this.isNested) return ret;
|
109 | const labelWidth = this.labelWidth || this.form.labelWidth;
|
110 | if (labelWidth) {
|
111 | ret.marginLeft = labelWidth;
|
112 | }
|
113 | return ret;
|
114 | },
|
115 | form() {
|
116 | let parent = this.$parent;
|
117 | let parentName = parent.$options.componentName;
|
118 | while (parentName !== 'ElForm') {
|
119 | if (parentName === 'ElFormItem') {
|
120 | this.isNested = true;
|
121 | }
|
122 | parent = parent.$parent;
|
123 | parentName = parent.$options.componentName;
|
124 | }
|
125 | return parent;
|
126 | },
|
127 | fieldValue() {
|
128 | const model = this.form.model;
|
129 | if (!model || !this.prop) { return; }
|
130 |
|
131 | let path = this.prop;
|
132 | if (path.indexOf(':') !== -1) {
|
133 | path = path.replace(/:/, '.');
|
134 | }
|
135 |
|
136 | return getPropByPath(model, path, true).v;
|
137 | },
|
138 | isRequired() {
|
139 | let rules = this.getRules();
|
140 | let isRequired = false;
|
141 |
|
142 | if (rules && rules.length) {
|
143 | rules.every(rule => {
|
144 | if (rule.required) {
|
145 | isRequired = true;
|
146 | return false;
|
147 | }
|
148 | return true;
|
149 | });
|
150 | }
|
151 | return isRequired;
|
152 | },
|
153 | _formSize() {
|
154 | return this.elForm.size;
|
155 | },
|
156 | elFormItemSize() {
|
157 | return this.size || this._formSize;
|
158 | },
|
159 | sizeClass() {
|
160 | return this.elFormItemSize || (this.$ELEMENT || {}).size;
|
161 | }
|
162 | },
|
163 | data() {
|
164 | return {
|
165 | validateState: '',
|
166 | validateMessage: '',
|
167 | validateDisabled: false,
|
168 | validator: {},
|
169 | isNested: false
|
170 | };
|
171 | },
|
172 | methods: {
|
173 | validate(trigger, callback = noop) {
|
174 | this.validateDisabled = false;
|
175 | const rules = this.getFilteredRule(trigger);
|
176 | if ((!rules || rules.length === 0) && this.required === undefined) {
|
177 | callback();
|
178 | return true;
|
179 | }
|
180 |
|
181 | this.validateState = 'validating';
|
182 |
|
183 | const descriptor = {};
|
184 | if (rules && rules.length > 0) {
|
185 | rules.forEach(rule => {
|
186 | delete rule.trigger;
|
187 | });
|
188 | }
|
189 | descriptor[this.prop] = rules;
|
190 |
|
191 | const validator = new AsyncValidator(descriptor);
|
192 | const model = {};
|
193 |
|
194 | model[this.prop] = this.fieldValue;
|
195 |
|
196 | validator.validate(model, { firstFields: true }, (errors, invalidFields) => {
|
197 | this.validateState = !errors ? 'success' : 'error';
|
198 | this.validateMessage = errors ? errors[0].message : '';
|
199 |
|
200 | callback(this.validateMessage, invalidFields);
|
201 | this.elForm && this.elForm.$emit('validate', this.prop, !errors);
|
202 | });
|
203 | },
|
204 | clearValidate() {
|
205 | this.validateState = '';
|
206 | this.validateMessage = '';
|
207 | this.validateDisabled = false;
|
208 | },
|
209 | resetField() {
|
210 | this.validateState = '';
|
211 | this.validateMessage = '';
|
212 |
|
213 | let model = this.form.model;
|
214 | let value = this.fieldValue;
|
215 | let path = this.prop;
|
216 | if (path.indexOf(':') !== -1) {
|
217 | path = path.replace(/:/, '.');
|
218 | }
|
219 |
|
220 | let prop = getPropByPath(model, path, true);
|
221 |
|
222 | this.validateDisabled = true;
|
223 | if (Array.isArray(value)) {
|
224 | prop.o[prop.k] = [].concat(this.initialValue);
|
225 | } else {
|
226 | prop.o[prop.k] = this.initialValue;
|
227 | }
|
228 |
|
229 | this.broadcast('ElTimeSelect', 'fieldReset', this.initialValue);
|
230 | },
|
231 | getRules() {
|
232 | let formRules = this.form.rules;
|
233 | const selfRules = this.rules;
|
234 | const requiredRule = this.required !== undefined ? { required: !!this.required } : [];
|
235 |
|
236 | const prop = getPropByPath(formRules, this.prop || '');
|
237 | formRules = formRules ? (prop.o[this.prop || ''] || prop.v) : [];
|
238 |
|
239 | return [].concat(selfRules || formRules || []).concat(requiredRule);
|
240 | },
|
241 | getFilteredRule(trigger) {
|
242 | const rules = this.getRules();
|
243 |
|
244 | return rules.filter(rule => {
|
245 | if (!rule.trigger || trigger === '') return true;
|
246 | if (Array.isArray(rule.trigger)) {
|
247 | return rule.trigger.indexOf(trigger) > -1;
|
248 | } else {
|
249 | return rule.trigger === trigger;
|
250 | }
|
251 | }).map(rule => objectAssign({}, rule));
|
252 | },
|
253 | onFieldBlur() {
|
254 | this.validate('blur');
|
255 | },
|
256 | onFieldChange() {
|
257 | if (this.validateDisabled) {
|
258 | this.validateDisabled = false;
|
259 | return;
|
260 | }
|
261 |
|
262 | this.validate('change');
|
263 | }
|
264 | },
|
265 | mounted() {
|
266 | if (this.prop) {
|
267 | this.dispatch('ElForm', 'el.form.addField', [this]);
|
268 |
|
269 | let initialValue = this.fieldValue;
|
270 | if (Array.isArray(initialValue)) {
|
271 | initialValue = [].concat(initialValue);
|
272 | }
|
273 | Object.defineProperty(this, 'initialValue', {
|
274 | value: initialValue
|
275 | });
|
276 |
|
277 | let rules = this.getRules();
|
278 |
|
279 | if (rules.length || this.required !== undefined) {
|
280 | this.$on('el.form.blur', this.onFieldBlur);
|
281 | this.$on('el.form.change', this.onFieldChange);
|
282 | }
|
283 | }
|
284 | },
|
285 | beforeDestroy() {
|
286 | this.dispatch('ElForm', 'el.form.removeField', [this]);
|
287 | }
|
288 | };
|
289 | </script>
|