/
validate.js
217 lines (199 loc) · 6.76 KB
/
validate.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/**
* Created by Tw93 on 2018-09-04.
* 校验表单格式
*/
import isLength from 'validator/lib/isLength';
import Color from 'color';
import { isHidden } from './isHidden';
import { hasRepeat, isFunction, baseGet, convertValue } from './utils';
const isNotEmpty = val => [undefined, null].indexOf(val) === -1;
const isEmptyObject = obj =>
Object.keys(obj).length === 0 && obj.constructor === Object;
// 值是是否为空
const isEmptyValue = (value, schema) => {
// 多选组件的值为 [] 时,也判断为空值
if (schema.type === 'array' && schema.enum) {
return !value || value.length === 0;
}
// boolean里的false, number里的0, 都不要认为是空值
if (value === 0 || value === false) {
return false;
}
return !value;
};
export const getValidateText = (obj = {}) => {
const { value, defaultValue, required, schema = {} } = obj;
const {
type,
pattern,
message,
format,
'ui:widget': widget,
minLength, // string
maxLength, // string
minimum, // number
maximum, // number
minItems, // list
maxItems, // list
uniqueItems, // list
} = schema;
// TODO: 这里要不要把 null 算进去呢?感觉算进去更合理一点
let finalValue = [undefined, null].indexOf(value) > -1 ? defaultValue : value;
// fix: number = 0 返回空字符串
if (type === 'number' && value === 0) {
finalValue = 0;
}
const needPattern = pattern && ['string', 'number'].indexOf(type) > -1;
// schema 里面没有内容的,直接退出
if (isEmptyObject(schema)) {
return false;
}
// 校验是否为required
if (required && isEmptyValue(finalValue, schema)) {
return (message && message.required) || '不能为空';
}
// 字符串相关校验
if (type === 'string') {
// TODO: 考虑了下,目前先允许 string 类的填入值是 undefined null 和 数字,校验的时候先转成 string
let _finalValue = finalValue;
if (typeof finalValue !== 'string') {
if (finalValue === null || finalValue === undefined) {
_finalValue = '';
} else {
_finalValue = String(finalValue);
// return '内容不是字符串,请修改'; // 这里可以强制提示,但旧项目有修改成本
}
}
// TODO: 为了一个 isLength 去引入一个包有点过分了,有空自己改写一下,而且 antd 用的 async-validator,是不是可以考虑看看
if (_finalValue && maxLength) {
if (!isLength(_finalValue, 0, parseInt(maxLength, 10))) {
return (message && message.maxLength) || `长度不能大于 ${maxLength}`;
}
}
if (_finalValue && (minLength || minLength === 0)) {
if (
!_finalValue ||
!isLength(_finalValue, parseInt(minLength, 10), undefined)
) {
return (message && message.minLength) || `长度不能小于 ${minLength}`;
}
}
// TODO: 为了一个Color引入了一个挺大的包,可以优化
if (format === 'color' || widget === 'color') {
try {
Color(finalValue || null); // 空字符串无法解析会报错,出现空的情况传 null
} catch (e) {
return '请填写正确的颜色格式';
}
}
}
// 数字相关校验
if (type === 'number') {
if (typeof finalValue !== 'number') {
return '请填写数字';
}
if (maximum && parseInt(finalValue, 10) > maximum) {
return (message && message.maximum) || `数值不能大于 ${maximum}`;
}
if ((minimum || minimum === 0) && parseInt(finalValue, 10) < minimum) {
return (message && message.minimum) || `数值不能小于 ${minimum}`;
}
}
// 正则只对数字和字符串有效果
// finalValue 有值的时候才去算 pattern。从场景反馈还是这样好
if (finalValue && needPattern && !new RegExp(pattern).test(finalValue)) {
return (message && message.pattern) || '格式不匹配';
}
// 数组项目相关校验
if (type === 'array') {
if (maxItems && finalValue && finalValue.length > maxItems) {
return (message && message.maxItems) || `组数长度不能大于 ${maxItems}`;
}
if (
(minItems || minItems === 0) &&
finalValue &&
finalValue.length < minItems
) {
return (message && message.minItems) || `组数长度不能小于 ${minItems}`;
}
if (uniqueItems && Array.isArray(finalValue) && finalValue.length > 1) {
if (typeof uniqueItems === 'boolean') {
if (hasRepeat(finalValue)) {
return '存在重复元素';
}
}
if (typeof uniqueItems === 'string') {
try {
const nameList = finalValue.map(item => baseGet(item, uniqueItems));
// 只考虑非object的情况
const isRepeat = nameList.find(
(x, index) => nameList.indexOf(x) !== index
);
if (isRepeat) {
return uniqueItems + ' 的值存在重复的';
}
} catch (e) {}
}
}
}
return false;
};
export const dealTypeValidate = (key, value, schema = {}, _formData) => {
const checkList = [];
const { type, items } = schema;
const obj = {
value,
schema,
};
if (type === 'object') {
const list = getValidateList(value, schema, _formData); // eslint-disable-line
checkList.push(...list);
} else if (type === 'array') {
value.forEach(v => {
const list = dealTypeValidate(key, v, items, _formData);
checkList.push(...list);
});
}
if (getValidateText(obj)) {
checkList.push(key);
}
return checkList;
};
// for backward compatibility
const keyHidden = (schema, val) => {
let hidden = schema && schema['ui:hidden'];
if (typeof hidden === 'string' && isFunction(hidden) === false) {
hidden = isHidden({ hidden, rootValue: val });
}
return hidden;
};
export const getValidateList = (val = {}, schema = {}, formData) => {
const _formData = formData || val;
const checkList = [];
const { properties, required } = schema;
// 校验必填(required 属性只在 type:object 下存在)
if (required && required.length > 0) {
required.forEach(key => {
const schema = (properties && properties[key]) || {};
const hidden = keyHidden(schema, val);
const _hidden = convertValue(hidden, _formData, val);
const itemValue = val && val[key];
if (isEmptyValue(itemValue, schema) && !_hidden) {
checkList.push(key);
}
});
}
if (properties && val && Object.keys(val) && Object.keys(val).length > 0) {
Object.keys(val).forEach(key => {
const value = val[key];
const schema = properties[key] || {};
const hidden = keyHidden(schema, val);
const _hidden = convertValue(hidden, _formData, val);
if (!_hidden) {
const list = dealTypeValidate(key, value, schema, _formData);
checkList.push(...list);
}
});
}
return checkList;
};