sunmao-sdk
Version:
榫卯-开箱即用赋能-sdk
407 lines (365 loc) • 11.1 kB
JavaScript
/**
* Created by Tw93 on 2018-09-26.
* 常见功能函数
*/
// 克隆对象
export function clone(data) {
try {
return JSON.parse(JSON.stringify(data));
} catch (e) {
return data;
}
}
// 做一个url的弱判断,就判断有没有 “//”
export function isUrl(string) {
const protocolRE = /^(?:\w+:)?\/\/(\S+)$/;
// const domainRE = /^[^\s\.]+\.\S{2,}$/;
if (typeof string !== "string") return false;
return protocolRE.test(string);
}
// '3' => true, 3 => true, undefined => false
export function isLooselyNumber(num) {
if (typeof num === "number") return true;
if (typeof num === "string") {
return !Number.isNaN(Number(num));
}
return false;
}
export function isCssLength(str) {
if (typeof str !== "string") return false;
return str.match(/^([0-9])*(%|px|rem|em)$/i);
}
// 深度对比
export function isDeepEqual(param1, param2) {
if (param1 === undefined && param2 === undefined) return true;
else if (param1 === undefined || param2 === undefined) return false;
if (param1 === null && param2 === null) return true;
else if (param1 === null || param2 === null) return false;
else if (param1.constructor !== param2.constructor) return false;
if (param1.constructor === Array) {
if (param1.length !== param2.length) return false;
for (let i = 0; i < param1.length; i++) {
if (param1[i].constructor === Array || param1[i].constructor === Object) {
if (!isDeepEqual(param1[i], param2[i])) return false;
} else if (param1[i] !== param2[i]) return false;
}
} else if (param1.constructor === Object) {
if (Object.keys(param1).length !== Object.keys(param2).length) return false;
for (let i = 0; i < Object.keys(param1).length; i++) {
const key = Object.keys(param1)[i];
if (
param1[key] &&
typeof param1[key] !== "number" &&
(param1[key].constructor === Array ||
param1[key].constructor === Object)
) {
if (!isDeepEqual(param1[key], param2[key])) return false;
} else if (param1[key] !== param2[key]) return false;
}
} else if (param1.constructor === String || param1.constructor === Number) {
return param1 === param2;
}
return true;
}
// fusion 的时间 format
export function getFormatForFusion(format) {
let dateFormat;
switch (format) {
case "date":
dateFormat = "YYYY-MM-DD";
break;
case "time":
dateFormat = "HH:mm:ss";
break;
case "dateTime":
dateFormat = "YYYY-MM-DD HH:mm:ss";
break;
case "year":
dateFormat = "YYYY";
break;
case "quarter":
dateFormat = "YYYY-[Q]Q";
break;
case "month":
dateFormat = "YYYY-MM";
break;
case "week":
dateFormat = "YYYY-W[*]";
break;
default:
dateFormat = "YYYY-MM-DD";
if (format && typeof format === "string") {
dateFormat = format;
}
}
return dateFormat;
}
// 时间组件
export function getFormat(format) {
let dateFormat;
switch (format) {
case "date":
// dateFormat = 'YYYY-MM-DD';
dateFormat = undefined;
break;
case "time":
dateFormat = "HH:mm:ss";
break;
case "dateTime":
// dateFormat = 'YYYY-MM-DD HH:mm:ss';
dateFormat = undefined;
break;
case "year":
// dateFormat = 'YYYY';
dateFormat = undefined;
break;
case "quarter":
dateFormat = "YYYY-[Q]Q";
break;
case "month":
dateFormat = "YYYY-MM";
break;
case "week":
dateFormat = "YYYY-W[*]";
break;
default:
dateFormat = "YYYY-MM-DD";
if (format && typeof format === "string") {
dateFormat = format;
}
}
return dateFormat;
}
export function hasRepeat(list) {
return list.find(
(x, i, self) =>
i !== self.findIndex(y => JSON.stringify(x) === JSON.stringify(y))
);
}
// ----------------- schema 相关
// 合并propsSchema和UISchema。由于两者的逻辑相关性,合并为一个大schema能简化内部处理
export function combineSchema(propsSchema, uiSchema) {
const propList = getChildren(propsSchema);
const newList = propList.map(p => {
const { name } = p;
const { type, enum: options, properties, items } = p.schema;
const isObj = type === "object" && properties;
const isArr = type === "array" && items && !options; // enum + array 代表的多选框,没有sub
const ui = name && uiSchema[p.name];
if (!ui) {
return p;
}
// 如果是list,递归合并items
if (isArr) {
const newItems = combineSchema(items, ui.items || {});
return { ...p, schema: { ...p.schema, ...ui, items: newItems } };
}
// object递归合并整个schema
if (isObj) {
const newSchema = combineSchema(p.schema, ui);
return { ...p, schema: newSchema };
}
return { ...p, schema: { ...p.schema, ...ui } };
});
const newObj = {};
newList.forEach(s => {
newObj[s.name] = s.schema;
});
const topLevelUi = {};
Object.keys(uiSchema).forEach(key => {
if (typeof key === "string" && key.substring(0, 3) === "ui:") {
topLevelUi[key] = uiSchema[key];
}
});
if (isEmpty(newObj)) {
return { ...propsSchema, ...topLevelUi };
}
return { ...propsSchema, ...topLevelUi, properties: newObj };
}
function isEmpty(obj) {
return Object.keys(obj).length === 0;
}
// 获得propsSchema的children
function getChildren(schema) {
const {
// object
properties,
// array
items,
type
} = schema;
if (!properties && !items) {
return [];
}
let schemaSubs = {};
if (type === "object") {
schemaSubs = properties;
}
if (type === "array") {
schemaSubs = items;
}
return Object.keys(schemaSubs).map(name => ({
schema: schemaSubs[name],
name
}));
}
// 合并多个schema树,比如一个schema的树节点是另一个schema
export function combine() {}
export const isValidVariableName = param => /^[a-zA-Z]+$/g.test(param);
// 去掉所有的window上的api,出现用户在这段逻辑里报错了。
// export function safeEval(code) {
// let safeContextStr = '';
// if (typeof window !== 'undefined') {
// const windowContextAttr = Object.getOwnPropertyNames(window).filter(
// isValidVariableName
// );
// for (let i = 0, len = windowContextAttr.length; i < len; i++) {
// safeContextStr += `var ${windowContextAttr[i]} = undefined;`;
// }
// }
// return Function(`${safeContextStr} "use strict"; ${code}`)();
// }
export function safeEval(code) {
return Function(`"use strict"; ${code}`)();
}
// 代替eval的函数
export const parseString = string => safeEval(`return (${string})`);
// 解析函数字符串值
export const evaluateString = (string, formData, rootValue) =>
safeEval(`
const rootValue =${JSON.stringify(rootValue)};
const formData = ${JSON.stringify(formData)};
return (${string})
`);
// 判断schema的值是是否是“函数”
// JSON无法使用函数值的参数,所以使用"{{...}}"来标记为函数,也可使用@标记,不推荐。
export function isFunction(func) {
if (typeof func === "function") {
return true;
}
if (typeof func === "string" && func.substring(0, 1) === "@") {
return func.substring(1);
}
if (
typeof func === "string" &&
func.substring(0, 2) === "{{" &&
func.substring(func.length - 2, func.length) === "}}"
) {
return func.substring(2, func.length - 2);
}
return false;
}
// 判断schema中是否有属性值是函数表达式
// TODO: 这个util,没有考虑schema是array的情况,不过目前没有被用到
export function isFunctionSchema(schema) {
return Object.keys(schema).some(key => {
if (typeof schema[key] === "function") {
return true;
} else if (typeof schema[key] === "string") {
return isFunction(schema[key]);
} else if (typeof schema[key] === "object") {
return isFunctionSchema(schema[key]);
} else {
return false;
}
});
}
function stringContains(str, text) {
return str.indexOf(text) > -1;
}
export const isNumber = a => !Number.isNaN(Number(a));
export const isObj = a =>
stringContains(Object.prototype.toString.call(a), "Object");
// 函数表达式转换成值
export const convertValue = (item, formData, rootValue) => {
if (typeof item === "function") {
return item(formData, rootValue);
} else if (typeof item === "string" && isFunction(item) !== false) {
const _item = isFunction(item);
try {
return evaluateString(_item, formData, rootValue);
} catch (error) {
console.error(error.message);
console.error(`happen at ${item}`);
return item;
}
}
return item;
};
// getValueByKey(formData, 'a.b.c')
export function baseGet(object, path) {
path = castPath(path, object);
let index = 0;
const length = path.length;
while (object != null && index < length) {
object = object[toKey(path[index++])];
}
return index && index == length ? object : undefined;
}
function castPath(value, object) {
if (Array.isArray(value)) {
return value;
}
return isKey(value, object) ? [value] : value.match(/([^\.\[\]"']+)/g);
}
function toKey(value) {
if (typeof value === "string") {
return value;
}
const result = `${value}`;
return result == "0" && 1 / value == -INFINITY ? "-0" : result;
}
const reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/;
const reIsPlainProp = /^\w*$/;
function isKey(value, object) {
if (Array.isArray(value)) {
return false;
}
const type = typeof value;
if (type === "number" || type === "boolean" || value == null) {
return true;
}
return (
reIsPlainProp.test(value) ||
!reIsDeepProp.test(value) ||
(object != null && value in Object(object))
);
}
// 将schema内所有的 {{ ... }} 转换为正常函数,我们试着一次性在外部做好这件事,而不是在内部每次去计算,优化性能
export function funcfySchema(schema) {
let _schema = clone(schema);
if (isObj(_schema)) {
Object.keys(_schema).forEach(key => {
_schema[key] = funcfySchema(_schema[key]);
});
} else if (Array.isArray(_schema)) {
_schema = _schema.map(item => funcfySchema(item));
} else {
const funcBody = isFunction(schema);
if (typeof funcBody === "string") {
try {
_schema = new Function("formData", "rootValue", `return ${funcBody}`);
} catch (error) {
console.error("funcfySchema", error);
}
}
}
return _schema;
}
export const getEnum = schema => {
if (!schema) return undefined;
const itemEnum = schema && schema.items && schema.items.enum;
const schemaEnum = schema && schema.enum;
return itemEnum ? itemEnum : schemaEnum;
};
export const getArray = (arr, defaultValue = []) => {
if (Array.isArray(arr)) return arr;
return defaultValue;
};
export const isEmail = value => {
const regex = "^[.a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+$";
if (value && new RegExp(regex).test(value)) {
return true;
}
return false;
};