import qs from 'qs'; import { notNilEmpty } from 'qx-util'; import { clone } from 'ramda'; import type { IContext, IParam } from '../interface'; import type { ControlVOBase } from '../modules'; import { PluralizeRule } from './pluralize-rule'; import { VerifyUtil } from './verify-util'; const pluralizeRule = new PluralizeRule(); /** * @description 克隆方法 * @export * @param {Record} data * @return {*} {Record} */ export function deepCopy(data: Record): Record { return clone(data); } /** * 对象是否有属性 * @description 适配使用get set的Object对象 * @export * @param {*} obj 对象 * @param {string} key 属性标识 * @return {*} {boolean} */ export function hasKey(obj: any, key: string): boolean { return obj.hasOwnProperty(key) || key in obj; } /** * 名称格式化 * * @export * @param {string} name 名称 * @return {*} {string} */ export function srfFilePath2(name: string): string { if (!name || (name && Object.is(name, ''))) { console.error('名称异常'); return ''; } name = name.replace(/[_]/g, '-'); let state = 0; let _str = ''; const uPattern = /^[A-Z]{1}$/; const str1 = name.substring(0, 1); const str2 = name.substring(1); state = uPattern.test(str1) ? 1 : 0; _str = `${_str}${str1.toLowerCase()}`; for (const chr of str2) { if (uPattern.test(chr)) { if (state === 1) { _str = `${_str}${chr.toLowerCase()}`; } else { _str = `${_str}-${chr.toLowerCase()}`; } state = 1; } else { _str = `${_str}${chr.toLowerCase()}`; state = 0; } } _str = _str.replace(/---/g, '-').replace(/--/g, '-'); return _str; } /** * 创建UUID * * @export * @return {*} {string} */ export function createUUID(): string { function s4() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) .substring(1); } return ( s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4() ); } /** * 判断对象是否为空,避免发生数值0误判 * * @param obj */ export function isExist(obj: any): boolean { return obj !== undefined && obj !== null; } /** * 字符串不为空并且对象存在 * * @param str */ export function isExistAndNotEmpty(str: string | undefined | null): boolean { return isExist(str) && !isEmpty(str); } /** * @description 是否拥有某个方法 * @param {*} arg 校验对象 * @param fnName 方法名 * @return {*} */ export function hasFunction(arg: any, fnName: string): boolean { return arg[fnName] && arg[fnName] instanceof Function; } /** * 判断指定值是否为空 * @param value * @returns */ export function isEmpty(value: any): boolean { return ( isUndefined(value) || Object.is(value, '') || value === null || value !== value ); } /** * 判断是否为空对象 * * @export * @param {*} arg * @return {*} {boolean} */ export function isEmptyObj(arg: any): boolean { if (!(arg instanceof Object)) { return false; } return Object.is(JSON.stringify(arg), '{}'); } /** * 判断指定值是否为未定义 * @param value * @returns */ export function isUndefined(value: any): boolean { return typeof value === 'undefined'; } /** * 判断指定值是否存在 * @param arg * @returns */ export function notEmpty(arg: any): boolean { return isExist(arg) && arg != ''; } /** * @description 获取当前数据值类型 * @export * @param {*} obj 当前数据 * @return {*} {string} */ export function typeOf(obj: any): string { const toString = Object.prototype.toString; const map: any = { '[object Boolean]': 'boolean', '[object Number]': 'number', '[object String]': 'string', '[object Function]': 'function', '[object Array]': 'array', '[object Date]': 'date', '[object RegExp]': 'regExp', '[object Undefined]': 'undefined', '[object Null]': 'null', '[object Object]': 'object', }; return map[toString.call(obj)]; } /** * 日期格式化 * * @export * @param {*} date 日期对象 * @param {string} [fmt='YYYY-MM-DD HH:mm:ss'] 格式化 * @return {*} {string} */ export function dateFormat(date: any, fmt = 'YYYY-MM-DD HH:mm:ss'): string { let ret; const opt: any = { 'Y+': date.getFullYear().toString(), // 年 'M+': (date.getMonth() + 1).toString(), // 月 'd+': date.getDate().toString(), // 日 'D+': date.getDate().toString(), // 日 'H+': date.getHours().toString(), // 时 'h+': date.getHours().toString(), // 时 'm+': date.getMinutes().toString(), // 分 's+': date.getSeconds().toString(), // 秒 'S+': date.getSeconds().toString(), // 有其他格式化字符需求可以继续添加,必须转化成字符串 }; for (const k in opt) { ret = new RegExp('(' + k + ')').exec(fmt); if (ret) { fmt = fmt.replace( ret[1], ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, '0') ); } } return fmt; } /** * 校验动态逻辑 * * @export * @param {IParam} data 数据 * @param {IParam} logic 动态逻辑 * @return {*} {boolean} */ export function verifyDynamicLogic(data: IParam, logic: IParam): boolean { if (logic.logicType == 'GROUP' && logic.childLogics?.length > 0) { let result = true; if (logic.groupOP == 'AND') { const falseItem = logic.childLogics.find((childLogic: IParam) => { return !verifyDynamicLogic(data, childLogic); }); result = falseItem ? false : true; } else if (logic.groupOP == 'OR') { const trueItem = logic.childLogics.find((childLogic: IParam) => { return verifyDynamicLogic(data, childLogic); }); result = trueItem ? true : false; } // 是否取反 return logic.notMode ? !result : result; } else if (logic.logicType == 'SINGLE') { return VerifyUtil.testCond(data[logic.dEFDName], logic.condOP, logic.value); } return false; } /** * 校验界面逻辑连接条件 * * @export * @param {IParam} linkCond * @return {*} {boolean} */ export function verifyNodeLinkCond(linkCond: IParam): boolean { if (linkCond.logicType == 'GROUP' && linkCond.childLogics?.length > 0) { let result = true; if (linkCond.groupOP == 'AND') { const falseItem = linkCond.childLogics.find((childLogic: IParam) => { return !verifyNodeLinkCond(childLogic); }); result = falseItem ? false : true; } else if (linkCond.groupOP == 'OR') { const trueItem = linkCond.childLogics.find((childLogic: IParam) => { return verifyNodeLinkCond(childLogic); }); result = trueItem ? true : false; } // 是否取反 return linkCond.notMode ? !result : result; } else if (linkCond.logicType == 'SINGLE') { return VerifyUtil.testCond( linkCond.verifyValue, linkCond.condOP, linkCond.value ); } return false; } const map = new WeakMap(); /** * 防抖 * * @param {any} fun 函数 * @param {any} params 参数 * @param {number} wait 延迟时间,默认300 * @static * @memberof Util */ export const debounce: Function = ( fun: any, params: any, context: any, wait = 300 ) => { if (typeof fun === 'function') { if (map.has(fun)) { const num = map.get(fun); clearTimeout(num); } map.set( fun, setTimeout(() => { map.delete(fun); fun.apply(context, params); }, wait) ); } }; /** * 节流 * * @param {any} fun 函数 * @param {any} params 参数 * @param {any} context 方法上下文 * @param {number} delay 延迟时间,默认300 * @static * @memberof Util */ export const throttle: Function = ( fun: any, params: any[], context: any, delay = 300 ) => { const now: any = Date.now(); if (!fun.last || now - fun.last >= delay) { if (typeof fun === 'function') { return fun.apply(context, params); } fun.last = now; } }; /** * 根据标识获取sessionStorage数据 * * @export * @param {string} key 标识 * @return {*} */ export function getSessionStorage(key: string) { if (!key) { return null; } const value = sessionStorage.getItem(key); if (value) { return JSON.parse(value); } else { return value; } } /** * 设置sessionStorage数据 * * @export * @param {string} key * @param {*} value * @return {*} */ export function setSessionStorage(key: string, value: any) { if (!value) { return; } sessionStorage.setItem(key, JSON.stringify(value)); } /** * @description 计算单词复数 * @export * @param {string} word * @return {*} */ export function srfpluralize(word: string) { const wordStr = word.trim().toLowerCase(); if (wordStr.length == 0) { return wordStr; } if (pluralizeRule.isUncountable(wordStr)) { return wordStr; } const newWordStr = pluralizeRule.irregularChange(wordStr); if (newWordStr) { return newWordStr; } else { return pluralizeRule.ruleChange(wordStr); } } /** * 深度合并对象 * * @param FirstOBJ 目标对象 * @param SecondOBJ 原对象 * @returns {Object} * @memberof Util */ export function deepObjectMerge(FirstOBJ: any, SecondOBJ: any) { for (const key in SecondOBJ) { FirstOBJ[key] = FirstOBJ[key] && FirstOBJ[key].toString() === '[object Object]' ? deepObjectMerge(FirstOBJ[key], SecondOBJ[key]) : (FirstOBJ[key] = SecondOBJ[key]); } return FirstOBJ; } /** * 计算导航数据 * * @export * @param {*} data 表单数据 * @param {*} parentContext 上下文 * @param {*} parentParam 视图参数 * @param {*} params 额外参数 * @return {*} {*} */ export function computedNavData( data: any, parentContext: any, parentParam: any, params: any ): any { const _data: any = {}; if (params && Object.keys(params).length > 0) { Object.keys(params).forEach((name: string) => { if (!name) { return; } let value: string | null = params[name]; if ( value && value.toString().startsWith('%') && value.toString().endsWith('%') ) { const key = value.substring(1, value.length - 1).toLowerCase(); if (data && data.hasOwnProperty(key)) { value = data[key]; } else if (parentContext && parentContext[key]) { value = parentContext[key]; } else if (parentParam && parentParam[key]) { value = parentParam[key]; } else { value = null; } } Object.assign(_data, { [name.toLowerCase()]: value }); }); } return _data; } /** * 获取路由参数(http://127.0.0.1:8080/?aaa=bbb&ccc=ddd#/index/views/viewtypeindexview) * 解析?和#之间的参数作为应用级参数 * * @export * @param {string} path 路径 * @return {*} */ export function getRouteParams(path: string) { const returnParams = {}; if (path.indexOf('?') !== -1 && path.indexOf('#') !== -1) { const tempPath: string = path.slice( path.indexOf('?') + 1, path.indexOf('#') ); const pathArray: Array = tempPath.split('&'); if (pathArray && pathArray.length > 0) { pathArray.forEach((item: any) => { if (item && item.indexOf('=') !== -1) { Object.assign(returnParams, qs.parse(item)); } }); } } return returnParams; } /** * @description 填充字符串中的数据 * @export * @param {string} str 路径 * @param {IContext} context 上下文 * @param {IParam} viewParams 视图参数 * @return {*} {string} */ export function fillStrData( str: string, context: IContext, viewParams: IParam ): string { if (notNilEmpty(str)) { if (notNilEmpty(context)) { const contextReg = /\$\{context.[a-zA-Z_$][a-zA-Z0-9_$]{1,}\}/g; const strArr = str.match(contextReg); strArr?.forEach((_key) => { const key = _key.slice(10, _key.length - 1); str = str.replace(`\${context.${key}}`, context[key] || ''); }); } if (notNilEmpty(viewParams)) { const dataReg = /\$\{data.[a-zA-Z_$][a-zA-Z0-9_$]{1,}\}/g; const strArr = str.match(dataReg); strArr?.forEach((_key) => { const key = _key.slice(7, _key.length - 1); str = str.replace(`\${data.${key}}`, viewParams[key] || ''); }); } } return str; } /** * 格式化数据 * * @private * @static * @param {*} actionTarget * @param {*} args * @param parentContext * @param parentParams * @param {*} _params * @returns {*} */ export function formatActionData( actionTarget: string, args: IParam[], parentContext: IContext, parentParams: IParam, _params: IParam ): IParam { const _data: IParam = {}; if ( Object.is(actionTarget, 'SINGLEKEY') || Object.is(actionTarget, 'NONE') || Object.is(actionTarget, 'SINGLEDATA') ) { let arg: IParam = {}; if (args && args.length > 0) { [arg] = args; } Object.keys(_params).forEach((name: string) => { let hasProperty = true; if (!name) { return; } let value: string | null = _params[name]; if ( value && typeof value === 'string' && value.startsWith('%') && value.endsWith('%') ) { const key = value.substring(1, value.length - 1); if (arg && hasKey(arg, key)) { value = arg[key] !== null && arg[key] !== undefined ? arg[key] : null; } else if (parentContext && hasKey(parentContext, key)) { value = parentContext[key] !== null && parentContext[key] !== undefined ? parentContext[key] : null; } else if (parentParams && hasKey(parentParams, key)) { value = parentParams[key] !== null && parentParams[key] !== undefined ? parentParams[key] : null; } else { hasProperty = false; } } if (hasProperty) { Object.assign(_data, { [name.toLowerCase()]: value }); } }); } else if (Object.is(actionTarget, 'MULTIKEY')) { Object.keys(_params).forEach((name: string) => { let noPropertyNum = 0; if (!name) { return; } let value: string | null = _params[name]; const values: any[] = []; if ( value && typeof value === 'string' && value.startsWith('%') && value.endsWith('%') ) { const key = value.substring(1, value.length - 1); args.forEach((arg: any) => { if (arg && hasKey(arg, key)) { value = arg[key] !== null && arg[key] !== undefined ? arg[key] : null; } else if (parentContext && hasKey(parentContext, key)) { value = parentContext[key] !== null && parentContext[key] !== undefined ? parentContext[key] : null; } else if (parentParams && hasKey(parentParams, key)) { value = parentParams[key] !== null && parentParams[key] !== undefined ? parentParams[key] : null; } else { value = null; noPropertyNum++; } values.push(value); }); } if (values.length !== noPropertyNum) { Object.assign(_data, { [name.toLowerCase()]: values.length > 0 ? values.join(',') : value, }); } }); } return _data; } /** * @description 获取当前国际化 * @return {*} {string} * @memberof EditorBase */ export function getLocal(): string { //todo return 'zh_CN'; } /** * @description 首字母大写 * @return {*} {string} */ export function UpperFirstLetter(str: string) { const str1 = str.substring(0, 1).toUpperCase(); const str2 = str.substring(1); return str1 + str2; } /** * 获取盒子大小(宽高模式针对面板项,按钮等项模型是控制的布局内容的宽高) * @param mode 模式 "WIDTH" | "HEIGHT" * @param style 样式 "FULL"(全部宽度) | "PX"(像素) | "PERCENTAGE"(百分比) * @param value * @returns */ export function getBoxSize( mode: 'WIDTH' | 'HEIGHT', style: string, value: number ) { if (!mode) { return {}; } if (!style) { style = 'PX'; } if (style === 'FULL') { return { [mode.toLowerCase()]: '100%' }; } else { if (!value) { return {}; } else if (style === 'PERCENTAGE') { return { [mode.toLowerCase()]: `${value}%` }; } else { return { [mode.toLowerCase()]: `${value}px` }; } } } /** * 获取VO原始数据 * * @export * @param {(ControlVOBase | ControlVOBase[] | IParam | IParam[])} data * @return {*} {(IParam | IParam[])} */ export function transformRawData( data: ControlVOBase | ControlVOBase[] | IParam | IParam[] ): IParam | IParam[] { if (!data || data.length === 0) { return data; } const handle = (item: ControlVOBase | IParam): IParam => { // 有此两项属性表明该对象是VO对象 if (item.$ownKeys && item.$DO) { return item.toObject(); } return item; }; if (Object.prototype.toString.call(data) === '[object Array]') { const items: IParam[] = []; data.forEach((item: ControlVOBase | IParam) => { items.push(handle(item)); }); return items; } if (Object.prototype.toString.call(data) === '[object Object]') { return handle(data); } return data; } /** * VO转换成DO数据 * * @export * @param {(ControlVOBase | ControlVOBase[])} data * @return {*} {(IParam | IParam[])} */ export function transformDoData( data: ControlVOBase | ControlVOBase[] | IParam | IParam[] ): T { if (!data || data.length === 0) { return data as T; } const handle = (item: ControlVOBase | IParam): IParam => { // 有此两项属性表明该对象是VO对象 if ( item.$ownKeys && item.$DO && item.getDo && item.getDo instanceof Function ) { return item.getDo(); } if (item.$DO) { delete item.$DO; } if (item.$ownKeys) { delete item.$ownKeys; } return item; }; if (Object.prototype.toString.call(data) === '[object Array]') { const items: IParam[] = []; data.forEach((item: ControlVOBase | IParam) => { items.push(handle(item)); }); return items as T; } if (Object.prototype.toString.call(data) === '[object Object]') { return handle(data) as T; } return data as T; } /** * 格式化URL字符串(适配老配置) * * @export * @param {string} url * @param {IContext} [context={}] * @param {IParam} [viewParams={}] * @param {IParam} [data={}] * @return {*} {string} */ export function formatUrlString( url: string, context: IContext = {}, viewParams: IParam = {}, data: IParam = {} ): string { if (!url || url === '') { return url; } const reg = /\$\{context.[a-zA-Z_]*[a-zA-Z0-9-_]*}|\$\{viewParams.[a-zA-Z_]*[a-zA-Z0-9-_]*}|\$\{viewparams.[a-zA-Z_]*[a-zA-Z0-9-_]*}|\$\{data.[a-zA-Z_]*[a-zA-Z0-9-_]*}/g; const results = url.match(reg); if (results && results.length > 0) { results.forEach((result: string) => { const keys = result.split('.'); if (keys && keys.length === 2) { const key = keys[0].replace('${', '').toLowerCase(); const value = keys[1].replace('}', ''); if (key === 'context') { url = url.replace(result, context[value]); } else if (key === 'viewparams') { url = url.replace(result, viewParams[value]); } else if (key === 'data') { url = url.replace(result, data[value]); } } }); } return url; } /** * 合并视图参数 * * @export * @param {IParam} data 业务数据 * @param {IParam} [viewParams={}] 视图参数 * @return {*} */ export function mergeViewParams(data: IParam, viewParams: IParam = {}) { if ( !data || Object.keys(data).length === 0 || !viewParams || Object.keys(viewParams).length === 0 ) { return; } Object.keys(viewParams).forEach((key: string) => { if (!data.hasOwnProperty(key)) { Object.assign(data, { [key]: viewParams[key] }); } }); } /** * @description 处理层级代码表 * @export * @param {IParam[]} items * @param {IParam[]} result */ export function handleLevelCodeList(items: IParam[]) { if(items && items.length >0){ const hasChildren = items.some((item: IParam) =>{ return item.pvalue; }) if(hasChildren){ let list: IParam[] = []; items.forEach((codeItem: IParam) =>{ if(!codeItem.pvalue){ let valueField: string = codeItem.value; setChildCodeItems(valueField,items,codeItem); list.push(codeItem); } }) return list; } else { return items; } } } /** * @description 设置子项代码表 * @param {string} pValue * @param {Array} result * @param {*} codeItem */ function setChildCodeItems(pValue:string,result:Array,codeItem:any){ result.forEach((item:any) =>{ if(item.pvalue == pValue){ let valueField:string = item.value; setChildCodeItems(valueField,result,item); if(!codeItem.children){ codeItem.children = []; } codeItem.children.push(item); } }) } /** * 格式化视图关系参数 * * @export * @param {*} context 应用上下文数据 * @param {any[][]} appDERSPaths 关系路径数据 * @return {*} {*} */ export function formatAppDERSPath( context: any, appDERSPaths: any[] | undefined ): any { if (!appDERSPaths || appDERSPaths.length === 0) { return []; } let counter: number = 0; for (const appDERSPath of appDERSPaths) { let tempData: any = { isInclude: true, data: [] }; for (const singleAppDERSPath of appDERSPath) { tempData.isInclude = context[singleAppDERSPath.parameterName.toLowerCase()] && tempData.isInclude; tempData.data.push(singleAppDERSPath); } counter++; if (tempData.isInclude) { return tempData.data; } else { if (counter === appDERSPaths.length) { return []; } } } }