fastlion-amis
Version:
一种MIS页面生成工具
560 lines (525 loc) • 18.6 kB
text/typescript
/**
*
* @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
}
}