UNPKG

fastlion-amis

Version:

一种MIS页面生成工具

560 lines (525 loc) 18.6 kB
/** * * @param str 字符串 * @param caseSensitive 是否小大写敏感 * @returns 返回哈希值 */ import BigNumber from "bignumber.js"; import { isNil } from "lodash"; import { Condition } from "../components/table/SecondFilter/types"; import { getMediaIcon, isImg } from "../renderers/Lion/utils/utils"; import { isMobile } from "./helper"; import { Shell } from "./shell"; import { tools } from "./shell/tools"; import { evalExpression } from "./tpl"; import { tokenize } from "./tpl-builtin"; // type ScanAfterPort = IExpress //字符串哈希函数 export function getHashCode(str: string = '', caseSensitive?: boolean) { if (!caseSensitive) { str = str.toLowerCase(); } var hash = 1315423911, i, ch; for (i = str.length - 1; i >= 0; i--) { ch = str.charCodeAt(i); hash ^= ((hash << 5) + ch + (hash >> 2)); } return (hash & 0x7FFFFFFF); } /** * * @param array 数据 * @param key 主键 * @returns boolean,是否重复 */ export const checkDuplicateKeys = (array: obj[], key: string) => { const keySet = new Set(); for (let i = 0; i < array.length; i++) { const item = array[i]; const keyValue = item[key]; if (keySet.has(keyValue)) { // 重复主键 return true; } keySet.add(keyValue); } // 没有重复主键 return false; }; enum JumpTarget { INCLINENT = 1, // 系统内部打开新应用 NEWWIN = 4, // 打开外部窗口 } // 判断是否可以跳转 export const linkJump = (linkId: string, data: any, dataDisabeExpress?: string) => { if (!linkId) return false const isVar = /\$\{.*?\}/g.test(linkId) const expressResValid = !dataDisabeExpress || !evalExpression(dataDisabeExpress || '', data) if (isVar) { const startIdx = linkId.indexOf('{') + 1 const endIdx = linkId.indexOf('}') const fieldName = linkId.substring(startIdx, endIdx) return data[fieldName] != undefined && expressResValid } else return expressResValid } // 判断跳转的方法tab/弹窗 export function ModleHandleClick(props: any, tbth?: any) { const { linkUrl, linkSize, linkType, data, env, handleJump, linkTitle, linkId, matrixId, classnames: cx, } = props if (linkUrl && !isNil(props.value)) { const drawer = tbth?.closest(`.${cx('Drawer')}`) const title = tokenize(linkTitle, data) if(linkType == JumpTarget.INCLINENT && !env.onPageLink) { sessionStorage.setItem('envDataStr', JSON.stringify(env)) } const url = tokenize(linkUrl, data) if (((isMobile() && matrixId && !drawer) || linkType == JumpTarget.INCLINENT || linkType == "1") && env.onPageLink) { const pageId = tokenize(linkId, data) const id = getHashCode(url).toString(); env.onPageLink(id, pageId, title ?? 'title', url, data, true) // 直接跳转外部页面 } else if (linkType == JumpTarget.NEWWIN) { const pageUrl = tokenize(linkId, data, '| url_decode') window.open(pageUrl, '__blank') } else { const { redirectRouter, redirectType, redirectApp } = env.parseLinkUrl?.(url) handleJump?.({ linkUrl: redirectRouter, linkSize: linkSize, linkTitle: title, bodydata: data, linkAppId: redirectApp, redirectType }) } } } export function handlelimitSize(max?: number, min?: number, body?: any, showModal?: (val: string) => void) { if (max !== undefined && min !== undefined) { if (min > max) { showModal?.('最小值不得大于最大值') return false } if (body.length < min) { showModal?.(`最少添加${min}个`) return false } else if (body.length > max) { showModal?.(`最多添加${max}个`) return false } } if (max !== undefined && min == undefined && body.length > max) { showModal?.(`最多添加${max}个`) return false } if (max == undefined && min !== undefined && body.length < min) { showModal?.(`最少添加${min}个`) return false } return true } export function dealBigMoney(n: number) { const fraction = ['角', '分']; const digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']; const unit = [['元', '万', '亿'], ['', '拾', '佰', '仟']]; const head = n < 0 ? '欠' : ''; n = Math.abs(n); let s = ''; for (var i = 0; i < fraction.length; i++) { s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, ''); } s = s || '整'; n = Math.floor(n); for (let i = 0; i < unit[0].length && n > 0; i++) { let p = ''; for (var j = 0; j < unit[1].length && n > 0; j++) { p = digit[n % 10] + unit[1][j] + p; n = Math.floor(n / 10); } s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s; } return head + s.replace(/(零.)*零元/, '元').replace(/(零.)+/g, '零').replace(/^整$/, '零元整'); } export function dealBigNumber(n: number) { if (n === 0) return '零' let num = Math.abs(n) const numArr = new Array("零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十"); const unitArr = new Array("", "十", "百", "千", "万", "亿", "点", ""); let a = ("" + num).replace(/(^0*)/g, "").split("."), k = 0, re = ""; for (let i = a[0].length - 1; i >= 0; i--) { switch (k) { case 0: re = unitArr[7] + re; break; case 4: if (!new RegExp("0{4}//d{" + (a[0].length - i - 1) + "}$") .test(a[0])) re = unitArr[4] + re; break; case 8: re = unitArr[5] + re; unitArr[7] = unitArr[5]; k = 0; break; } if (k % 4 == 2 && a[0].charAt(i + 2) != '0' && a[0].charAt(i + 1) == '0') { re = numArr[0] + re; } if (a[0].charAt(i) != '0') { re = numArr[a[0].charAt(i)] + unitArr[k % 4] + re; } k++; } if (a.length > 1) { re += unitArr[6]; for (var i = 0; i < a[1].length; i++) re += numArr[a[1].charAt(i)]; } if (re == '一十') re = "十"; if (re.match(/^一/) && re.length == 3) re = re.replace("一", ""); return (n < 0 ? '负' : '') + re; } /** * * @param value 数值 * @param showUppercase 转换类型 * @returns 转换后的值 */ export function translateNumber(value: any, showUppercase: 0 | 1 | 2) { if (showUppercase === 1) { value = dealBigNumber(Number(+value)) } else if (showUppercase === 2) { value = dealBigMoney(Number(+value)) } return value } export function calcFn(formula: 'sum' | 'avg' | 'max' | 'min' | 'count' | string, fieldName: string, items: any[]): number { if (!Array.isArray(items)) return 0 const tmpItems = items.filter(item => item[fieldName] != undefined) switch (formula) { case 'sum': return tmpItems.reduce((previous, current) => new BigNumber(previous).plus(Number(current[fieldName]) || 0).toNumber(), 0) case 'avg': const sum = tmpItems.reduce((previous, current) => new BigNumber(previous).plus(Number(current[fieldName]) || 0), 0) const count = items.length return new BigNumber(sum).dividedBy(count).toNumber() case 'max': return tmpItems.reduce((previous, current) => Math.max(previous, Number(current[fieldName]) || 0), Number.MIN_VALUE) case 'min': return tmpItems.length ? tmpItems.reduce((previous, current) => Math.min(previous, Number(current[fieldName]) || 0), Number.MAX_VALUE) : 0 case 'count': return tmpItems.length default: if (formula.includes('+') || formula.includes('-') || formula.includes('*') || formula.includes('/') || formula.includes('(') || formula.includes(')')) { return deepCalcFn(formula, items) } return 0 } } export function deepCalcFn(formula: string, items: any[]) { let str = formula let fns = str.match(/(AVG|SUM|MAX|MIN|COUNT)/g); let params = str.match(/\((.*?)\)/g)!.map(param => param.replace('(', '').replace(')', '')); if (fns && items.length > 0) { let calculatedStr = ''; fns.forEach((fn, index) => { if (index === 0) { calculatedStr += calcFn(fn.toLocaleLowerCase(), params[index], items); } else { let operator = str.match(/(\*|\+|\-|\/)/g)![index - 1]; calculatedStr += `${operator}${calcFn(fn.toLocaleLowerCase(), params[index], items)}` } }); let res; try { res = eval(calculatedStr) } catch (error) { res = 0; } return res } return 0 } export function sortFn(a: any, b: any, sortBy?: 'asc' | 'desc') { if (sortBy == undefined) return 0 if (typeof a === 'number' && typeof b === 'number') { return sortBy === 'asc' ? a - b : b - a; } return sortBy === 'asc' ? String(a ?? '').localeCompare(String(b ?? ''), undefined, { numeric: true, ignorePunctuation: false }) : String(b ?? '').localeCompare(String(a ?? ''), undefined, { numeric: true, ignorePunctuation: false }) } export function filterFn(items: any[], columnName: string, condition: Condition, _value1?: string | number, _value2?: string | number, caseSensitive = true) { return items.filter((item: any) => { // 如果转不动数字保持原来的值 let value = item[columnName] let value1 = _value1 let value2 = _value2 // 需要才转数字 分别是 大于 小于 等于 大于等于 小于等于 const tryToTransValueToNumber = () => { // 如果转不成数字就按原值处理 if (!isNil(value) && !isNaN(+value)) { value = +value if (!isNil(_value1) && !isNaN(+_value1)) { value1 = +_value1 } if (!isNil(_value2) && !isNaN(+_value2)) { value2 = +_value2 } } } const valueToUpperCase = () => { if (!caseSensitive) { if (typeof value1 == 'string') value1 = value1.toUpperCase() if (typeof value == 'string') value = value.toUpperCase() } } const tryParseExpr = () => { if (typeof value1 == 'string') { const fields = value1.match(/\[(.*?)\]/g) if (fields) { for (const field of fields) { const fieldValue = item[field.slice(1, field.length - 1)] //typeof fieldValue == 'number' ? `${fieldValue}` : `'${fieldValue}'` value1 = value1.replaceAll(field, String(fieldValue)) } try { value1 = eval(value1.replaceAll('||', '+')) } catch { } } } } switch (condition) { case Condition.GreaterThan: tryToTransValueToNumber() tryParseExpr() return value != null && (value) > (value1!) case Condition.LessThan: tryToTransValueToNumber() tryParseExpr() return value != null && (value) < (value1!) case Condition.Between: tryToTransValueToNumber() tryParseExpr() return (value) >= (value1!) && (value) <= (value2!) case Condition.Equal: valueToUpperCase() if (typeof value1 == 'string') { if (value1.includes(',')) { return value1.split(',').some(v => value == v) } else if (value1.includes(',')) { return value1.split(',').some(v => value == v) } tryParseExpr() } return value == value1 case Condition.NotEqual: valueToUpperCase() if (typeof value1 == 'string') { if (value1.includes(',')) { return value1.split(',').every(v => value != v) } else if (value1.includes(',')) { return value1.split(',').every(v => value != v) } tryParseExpr() } return value != value1 case Condition.GreaterThanOrEqual: tryToTransValueToNumber() tryParseExpr() return value != null && (value) >= (value1!) case Condition.LessThanOrEqual: tryToTransValueToNumber() tryParseExpr() return value != null && (value) <= (value1!) case Condition.Null: return value == null case Condition.NotNull: return value != null case Condition.Contain: valueToUpperCase() if (typeof value1 == 'string') { return value1.split(',').some(v => String(value).includes(String(v))) } return value != null && String(value).includes(String(value1)) case Condition.NotContain: valueToUpperCase() if (typeof value1 == 'string') { return value1.split(',').every(v => !String(value).includes(String(v))) } return value != null && !String(value).includes(String(value1)) case Condition.StartsWith: valueToUpperCase() return value != null && String(value).startsWith(String(value1)) case Condition.EndsWith: valueToUpperCase() return value != null && String(value).endsWith(String(value1)) } }) } /** * 查看类型转为表单控件的查看类型 * @param type 类型 * @returns type: string */ export function exchangeType(type: string) { switch (type) { case 'mapping': case 'html': case 'progress': case 'date': case 'time': case 'color': case 'datetime': return 'static-' + type; case 'number': return 'input-number'; default: return type || 'static'; } } //递归遍历super对象,查找某个属性的值 export function findField(obj: any, targetField: string): any { // 遍历对象的每一个属性 if (obj.hasOwnProperty(targetField)) { return obj[targetField] } else if (obj.__super && JSON.stringify(obj.__super) !== '{}') { return findField(obj.__super, targetField) } // 如果没有找到目标字段,返回 undefined return undefined; } //多列排序 /** * @param tableData 原始数据 * @param columnProperties 排序字段 * @returns 排序后数据 obj<any>[] */ export function multiColumnSort(tableData: obj<any>[], columnProperties: string[]) { return tableData.sort((a, b) => { for (const property of columnProperties) { const valueA = a[property]; const valueB = b[property]; // 处理 null 和 undefined 值 if (valueA === null || valueA === undefined) { if (valueB === null || valueB === undefined) { // 如果两者都是 null 或 undefined,则认为它们相等,继续比较下一列 continue; } else { // 如果 valueA 是 null 或 undefined 而 valueB 不是,则认为 valueA 应该排在 valueB 之前 return -1; } } else if (valueB === null || valueB === undefined) { // 如果 valueB 是 null 或 undefined 而 valueA 不是,则认为 valueA 应该排在 valueB 之后 return 1; } // 正常比较非 null/undefined 的值 if (typeof valueA === "number") { const comparison = valueA - valueB; if (comparison !== 0) return comparison; } else { const comparison = valueA.localeCompare(valueB, undefined, { sensitivity: 'accent' }); if (comparison !== 0) return comparison; } } // 如果所有列都相同(包括 null/undefined 处理),则认为两个记录相等,返回0保持原顺序 return 0; }); } // 计算笛卡儿积 export const cartesianProduct = (...arrays: any[]): any[] => { if (arrays.length === 0) return []; if (arrays.length === 1) return arrays[0].map((x: any) => [x]); return cartesianProduct(...arrays.slice(0, -1)).reduce((acc: any[], val: any[]) => acc.concat(arrays[arrays.length - 1].map((x: any) => val.concat(x))), []); } export const openImageEnlarge = (data: Array<any>, baseURL: string, onImageEnlarge: any, env: any) => { let isNotImg = false; let list: any = data.map((item: any) => { isNotImg = !isImg(item.name); const _imgUrl = item.thumbnailAddr || item?.addr // 如果开始时http不进行拼接处理 const imgUrl = _imgUrl?.startsWith?.("http") ? _imgUrl : ((baseURL ? baseURL : '') + _imgUrl) const originalSrc = item.preview?.startsWith?.("http") ? item.preview : ((baseURL ? baseURL : '') + item.preview) const downloadSrc = item?.addr?.startsWith?.("http") ? item?.addr : ((baseURL ? baseURL : '') + item?.addr) return { src: isNotImg ? getMediaIcon(item.name) : imgUrl, originalSrc: originalSrc, downloadSrc: downloadSrc, title: item.name || '', isNotImg }; }); if (isMobile()) { if (Shell.hasShell()) { if (tools.isAndroid) { Shell.previewFile({ urls: list.map((val: any) => { return val.originalSrc }) }) } else { const urls = list.map((attachment: any) => { const url = new URL(encodeURI(attachment.originalSrc)) const search = url.search == '' ? `?fileName=${Date.now() + attachment.title}` : `${url.search}&fileName=${Date.now() + attachment.title}` url.search = search return url.href }) Shell.previewFile({ urls, current: 0 }) } return } else if (env?.previewImagesMb) { const urls = list.map((val: any) => { return val.originalSrc }) env.previewImagesMb(urls, 0) return } } onImageEnlarge && onImageEnlarge({ src: list[0].src, originalSrc: list[0].originalSrc, index: 0, list }); } // 获取文本中的列名称 a[dd]ccccc[ee]ff 获取文本中的 dd 与 ee并返回 export const getNameInString = (valueStr: string): { orginStr: string, parseStr: string, parseObj: any, } => { // 文本起始位置 let index = 0 // 转化文本 let newStr = '' // 字段转化对应表 const parseObj = {} // 括号计数 let BracktCount = 0 // 括号中的变量名 let nameInBrackt = '' while (valueStr[index]) { const curStr = valueStr[index] // 如果遇到左括号计数+1 堆推入 跳过循环 if (curStr === '[') { index++ BracktCount++ continue } // 如果遇到左括号计数-1 堆弹出 if (curStr === ']' && BracktCount) BracktCount-- // 还有括号计数的情况下 说明还在括号里面 记录变量字符串 if (BracktCount) { nameInBrackt += curStr } else { // 如果没有括号计数但有暂存变量 增加新文本的值 if (nameInBrackt) { nameInBrackt = `[${nameInBrackt}]` parseObj[nameInBrackt] = nameInBrackt[nameInBrackt] || `{$${Object.values(parseObj).length}}` // 字符串变化 newStr += parseObj[nameInBrackt] nameInBrackt = '' } else { // 如果没有括号计数并且没有暂存变量 增加新文本的值 newStr += curStr } } index++ } return { orginStr: valueStr, parseStr: newStr, parseObj } }