sunmao-sdk
Version:
榫卯-开箱即用赋能-sdk
258 lines (237 loc) • 8.18 kB
JavaScript
/**
* 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,
isEmail,
isUrl
} 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,
minLength, // string
maxLength, // string
minimum, // number
maximum, // number
minItems, // list
maxItems, // list
uniqueItems, // list
"ui:widget": widget,
"ui:options": options
} = schema;
// TODO: 这里要不要把 null 算进去呢?感觉算进去更合理一点
let finalValue = [undefined, null].indexOf(value) > -1 ? defaultValue : value;
// fix: number = 0 返回空字符串
if (type === "number" && value === 0) {
finalValue = 0;
}
const usePattern = 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,是不是可以考虑看看
// 添加检查,是否两侧有空格
const noTrim = options && options.noTrim; // 配置项,不需要trim
const trimedValue = _finalValue.trim();
if (trimedValue !== _finalValue && !noTrim) {
return (message && message.trim) || `输入的内容有多余空格`;
}
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 (format === "image") {
const imagePattern =
"([/|.|w|s|-])*.(?:jpg|gif|png|bmp|apng|webp|jpeg|json)";
// image 里也可以填写网络链接
const _isUrl = isUrl(finalValue);
const _isImg = new RegExp(imagePattern).test(finalValue);
if (usePattern) {
} else if (finalValue && !_isUrl && !_isImg) {
return (message && message.image) || "请输入正确的图片格式";
}
}
if (format === "url") {
if (usePattern) {
} else if (finalValue && !isUrl(finalValue)) {
return (message && message.url) || "请输入正确的url格式";
}
}
if (format === "email") {
if (usePattern) {
} else if (finalValue && !isEmail(finalValue)) {
return (message && message.email) || "请输入正确的email格式";
}
}
}
// 数字相关校验
if (type === "number" && !isEmptyValue(finalValue)) {
// 添加无输入不校验逻辑
if (typeof finalValue !== "number") {
return "请填写数字";
}
if (maximum && parseFloat(finalValue, 10) > maximum) {
return (message && message.maximum) || `数值不能大于 ${maximum}`;
}
if ((minimum || minimum === 0) && parseFloat(finalValue, 10) < minimum) {
return (message && message.minimum) || `数值不能小于 ${minimum}`;
}
}
// 正则只对数字和字符串有效果
// finalValue 有值的时候才去算 pattern。从场景反馈还是这样好
if (finalValue && usePattern && !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;
};