UNPKG

amis-formula

Version:

负责 amis 里面的表达式实现,内置公式,编辑器等

1,625 lines (1,623 loc) 80.9 kB
/** * amis-formula v6.13.0 * Copyright 2021-2025 fex */ import { __assign, __values } from 'tslib'; import moment from 'moment'; import upperFirst from 'lodash/upperFirst'; import padStart from 'lodash/padStart'; import capitalize from 'lodash/capitalize'; import escape from 'lodash/escape'; import truncate from 'lodash/truncate'; import uniqWith from 'lodash/uniqWith'; import uniqBy from 'lodash/uniqBy'; import isEqual from 'lodash/isEqual'; import isPlainObject from 'lodash/isPlainObject'; import get from 'lodash/get'; import { FormulaEvalError } from './error.js'; /** * @file 公式内置函数 */ var Evaluator = /** @class */ (function () { function Evaluator(context, options) { if (options === void 0) { options = { defaultFilter: 'html' }; } this.options = options; this.functions = {}; this.contextStack = []; this.context = context; this.contextStack.push(function (varname) { return varname === '&' ? context : context === null || context === void 0 ? void 0 : context[varname]; }); this.filters = __assign(__assign(__assign({}, Evaluator.defaultFilters), this.filters), options === null || options === void 0 ? void 0 : options.filters); this.functions = __assign(__assign(__assign({}, Evaluator.defaultFunctions), this.functions), options === null || options === void 0 ? void 0 : options.functions); } Evaluator.extendDefaultFilters = function (filters) { Evaluator.defaultFilters = __assign(__assign({}, Evaluator.defaultFilters), filters); }; Evaluator.extendDefaultFunctions = function (funtions) { Evaluator.defaultFunctions = __assign(__assign({}, Evaluator.defaultFunctions), funtions); }; // 主入口 Evaluator.prototype.evalute = function (ast) { if (ast && ast.type) { var name_1 = ast.type.replace(/(?:_|\-)(\w)/g, function (_, l) { return l.toUpperCase(); }); var fn = this.functions[name_1] || this[name_1]; if (!fn) { throw new Error("".concat(ast.type, " unkown.")); } return fn.call(this, ast); } else { return ast; } }; Evaluator.prototype.document = function (ast) { var _this = this; if (!ast.body.length) { return undefined; } var isString = ast.body.length > 1; var content = ast.body.map(function (item) { var result = _this.evalute(item); if (isString && result == null) { // 不要出现 undefined, null 之类的文案 return ''; } return result; }); return content.length === 1 ? content[0] : content.join(''); }; Evaluator.prototype.filter = function (ast) { var _this = this; var input = this.evalute(ast.input); var filters = ast.filters.concat(); var context = { filter: undefined, data: this.context, restFilters: filters }; while (filters.length) { var filter = filters.shift(); var fn = this.filters[filter.name]; if (!fn) { throw new Error("filter `".concat(filter.name, "` not exists.")); } context.filter = filter; input = fn.apply(context, [input].concat(filter.args.map(function (item) { if ((item === null || item === void 0 ? void 0 : item.type) === 'mixed') { return item.body .map(function (item) { return typeof item === 'string' ? item : _this.evalute(item); }) .join(''); } else if (item.type) { return _this.evalute(item); } return item; }))); } return input; }; Evaluator.prototype.raw = function (ast) { return ast.value; }; Evaluator.prototype.script = function (ast) { var _a; var defaultFilter = this.options.defaultFilter; // 只给简单的变量取值用法自动补fitler if (defaultFilter && ~['getter', 'variable'].indexOf((_a = ast.body) === null || _a === void 0 ? void 0 : _a.type)) { ast = __assign(__assign({}, ast), { body: { type: 'filter', input: ast.body, filters: [ { name: defaultFilter.replace(/^\s*\|\s*/, ''), args: [] } ] } }); } return this.evalute(ast.body); }; Evaluator.prototype.expressionList = function (ast) { var _this = this; return ast.body.reduce(function (prev, current) { return _this.evalute(current); }); }; Evaluator.prototype.template = function (ast) { var _this = this; return ast.body.map(function (arg) { return _this.evalute(arg); }).join(''); }; Evaluator.prototype.templateRaw = function (ast) { return ast.value; }; // 下标获取 Evaluator.prototype.getter = function (ast) { var _a; var host = this.evalute(ast.host); var key = this.evalute(ast.key); if (typeof key === 'undefined' && ((_a = ast.key) === null || _a === void 0 ? void 0 : _a.type) === 'variable') { key = ast.key.name; } return host === null || host === void 0 ? void 0 : host[key]; }; // 位操作如 +2 ~3 ! Evaluator.prototype.unary = function (ast) { var value = this.evalute(ast.value); switch (ast.op) { case '+': return +value; case '-': return -value; case '~': return ~value; case '!': return !value; } }; Evaluator.prototype.formatNumber = function (value, int) { if (int === void 0) { int = false; } var typeName = typeof value; if (typeName === 'string') { return (int ? parseInt(value, 10) : parseFloat(value)) || 0; } else if (typeName === 'number' && int) { return Math.round(value); } return value !== null && value !== void 0 ? value : 0; }; // 判断是否是数字或者字符串数字 Evaluator.prototype.isValidValue = function (value) { return (typeof value === 'number' || (typeof value === 'string' && /^\d+(\.\d+)?$/.test(value))); }; Evaluator.prototype.power = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); if (!this.isValidValue(left) || !this.isValidValue(right)) { return left; } return Math.pow(left, right); }; Evaluator.prototype.multiply = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); return stripNumber(this.formatNumber(left) * this.formatNumber(right)); }; Evaluator.prototype.divide = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); return stripNumber(this.formatNumber(left) / this.formatNumber(right)); }; Evaluator.prototype.remainder = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); return this.formatNumber(left) % this.formatNumber(right); }; Evaluator.prototype.add = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); // 如果有一个不是数字就变成字符串拼接 if (isNaN(left) || isNaN(right)) { return left + right; } return stripNumber(this.formatNumber(left) + this.formatNumber(right)); }; Evaluator.prototype.minus = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); return stripNumber(this.formatNumber(left) - this.formatNumber(right)); }; Evaluator.prototype.shift = function (ast) { var left = this.evalute(ast.left); var right = this.formatNumber(this.evalute(ast.right), true); if (ast.op === '<<') { return left << right; } else if (ast.op == '>>') { return left >> right; } else { return left >>> right; } }; Evaluator.prototype.lt = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); // todo 如果是日期的对比,这个地方可以优化一下。 return left < right; }; Evaluator.prototype.gt = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); // todo 如果是日期的对比,这个地方可以优化一下。 return left > right; }; Evaluator.prototype.le = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); // todo 如果是日期的对比,这个地方可以优化一下。 return left <= right; }; Evaluator.prototype.ge = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); // todo 如果是日期的对比,这个地方可以优化一下。 return left >= right; }; Evaluator.prototype.eq = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); // todo 如果是日期的对比,这个地方可以优化一下。 return left == right; }; Evaluator.prototype.ne = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); // todo 如果是日期的对比,这个地方可以优化一下。 return left != right; }; Evaluator.prototype.streq = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); // todo 如果是日期的对比,这个地方可以优化一下。 return left === right; }; Evaluator.prototype.strneq = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); // todo 如果是日期的对比,这个地方可以优化一下。 return left !== right; }; Evaluator.prototype.binary = function (ast) { var left = this.evalute(ast.left); var right = this.evalute(ast.right); if (ast.op === '&') { return left & right; } else if (ast.op === '^') { return left ^ right; } else { return left | right; } }; Evaluator.prototype.and = function (ast) { var left = this.evalute(ast.left); return left && this.evalute(ast.right); }; Evaluator.prototype.or = function (ast) { var left = this.evalute(ast.left); return left || this.evalute(ast.right); }; Evaluator.prototype.number = function (ast) { // todo 以后可以在这支持大数字。 return ast.value; }; /** * 名字空间下获取变量,可能存在变量名中带-的特殊情况,目前无法直接获取 ${ns:xxx-xxx} * 想借助 ${ns:&['xxx-xxx']} 用法来支持特殊字符。 * * 而 cookie, localstorage, sessionstorage 都不支持获取全量数据,如 ${ns: &} * 所以当存在上述用法时,将 & 作为一个占位 * * 比如 cookie 中有一个 key 为 xxx-xxx 的值,那么可以通过 &['xxx-xxx'] 来获取。 * 而无法通过 ${cookie:xxx-xxx} 来获取。 因为这样会被认为是减操作 * @param ast * @returns */ Evaluator.prototype.convertHostGetterToVariable = function (ast) { var _a, _b; if (ast.type !== 'getter') { return ast; } var gettter = ast; var keys = []; while (((_a = gettter.host) === null || _a === void 0 ? void 0 : _a.type) === 'getter') { keys.push('host'); gettter = gettter.host; } if (((_b = gettter.host) === null || _b === void 0 ? void 0 : _b.type) === 'variable' && gettter.host.name === '&') { var ret = { host: ast }; var host = keys.reduce(function (host, key) { host[key] = __assign({}, host[key]); return host[key]; }, ret); host.host = { start: host.host.start, end: host.host.end, type: 'variable', name: this.evalute(host.host.key) }; return ret.host; } return ast; }; Evaluator.prototype.nsVariable = function (ast) { var _this = this; var body = ast.body; if (ast.namespace === 'window') { this.contextStack.push(function (name) { return name === '&' ? window : window[name]; }); } else if (ast.namespace === 'cookie') { // 可能会利用 &['xxx-xxx'] 来取需要特殊变量 body = this.convertHostGetterToVariable(body); this.contextStack.push(function (name) { return getCookie(name); }); } else if (ast.namespace === 'ls' || ast.namespace === 'ss') { var ns_1 = ast.namespace; // 可能会利用 &['xxx-xxx'] 来取需要特殊变量 body = this.convertHostGetterToVariable(body); this.contextStack.push(function (name) { var raw = ns_1 === 'ss' ? sessionStorage.getItem(name) : localStorage.getItem(name); if (typeof raw === 'string') { // 判断字符串是否一个纯数字字符串,如果是,则对比parse后的值和原值是否相同, // 如果不同则返回原值,因为原值如果是一个很长的纯数字字符串,则 parse 后可能会丢失精度 if (/^\d+$/.test(raw)) { var parsed = JSON.parse(raw); return "".concat(parsed) === raw ? parsed : raw; } return parseJson(raw, raw); } return undefined; }); } else { throw new Error('Unsupported namespace: ' + ast.namespace); } var result = this.evalute(body); (result === null || result === void 0 ? void 0 : result.then) ? result.then(function () { return _this.contextStack.pop(); }) : this.contextStack.pop(); return result; }; Evaluator.prototype.variable = function (ast) { var contextGetter = this.contextStack[this.contextStack.length - 1]; return contextGetter(ast.name); }; Evaluator.prototype.identifier = function (ast) { return ast.name; }; Evaluator.prototype.array = function (ast) { var _this = this; return ast.members.map(function (member) { return _this.evalute(member); }); }; Evaluator.prototype.literal = function (ast) { return ast.value; }; Evaluator.prototype.string = function (ast) { return ast.value; }; Evaluator.prototype.object = function (ast) { var _this = this; var object = {}; ast.members.forEach(function (_a) { var key = _a.key, value = _a.value; object[_this.evalute(key)] = _this.evalute(value); }); return object; }; Evaluator.prototype.conditional = function (ast) { return this.evalute(ast.test) ? this.evalute(ast.consequent) : this.evalute(ast.alternate); }; Evaluator.prototype.funcCall = function (ast) { var _this = this; var fnName = "fn".concat(ast.identifier); var fn = this.functions[fnName] || this[fnName] || (this.filters.hasOwnProperty(ast.identifier) && this.filters[ast.identifier]); if (!fn) { throw new FormulaEvalError("".concat(ast.identifier, "\u51FD\u6570\u6CA1\u6709\u5B9A\u4E49")); } var args = ast.args; // 逻辑函数特殊处理,因为有时候有些运算是可以跳过的。 if (~['IF', 'AND', 'OR', 'XOR', 'IFS'].indexOf(ast.identifier)) { args = args.map(function (a) { return function () { return _this.evalute(a); }; }); } else { args = args.map(function (a) { return _this.evalute(a); }); } return fn.apply(this, args); }; Evaluator.prototype.anonymousFunction = function (ast) { return ast; }; Evaluator.prototype.callAnonymousFunction = function (ast, args) { var ctx = createObject(this.contextStack[this.contextStack.length - 1]('&') || {}, {}); ast.args.forEach(function (arg) { if (arg.type !== 'variable') { throw new Error('expected a variable as argument'); } ctx[arg.name] = args.shift(); }); this.contextStack.push(function (varName) { return varName === '&' ? ctx : ctx[varName]; }); var result = this.evalute(ast.return); this.contextStack.pop(); return result; }; /** * 如果满足条件condition,则返回consequent,否则返回alternate,支持多层嵌套IF函数。 * * 等价于直接用JS表达式如:condition ? consequent : alternate。 * * @example IF(condition, consequent, alternate) * @param {expression} condition 条件表达式。例如:语文成绩>80 * @param {any} consequent 条件判断通过的返回结果 * @param {any} alternate 条件判断不通过的返回结果 * @namespace 逻辑函数 * * @returns {any} 根据条件返回不同的结果 */ Evaluator.prototype.fnIF = function (condition, trueValue, falseValue) { return condition() ? trueValue() : falseValue(); }; /** * 条件全部符合,返回 true,否则返回 false。 * * 示例:AND(语文成绩>80, 数学成绩>80), * * 语文成绩和数学成绩都大于 80,则返回 true,否则返回 false, * * 等价于直接用JS表达式如:语文成绩>80 && 数学成绩>80。 * * @example AND(expression1, expression2, ...expressionN) * @param {...expression} conditions 条件表达式,多个用逗号隔开。例如:语文成绩>80, 数学成绩>80 * @namespace 逻辑函数 * * @returns {boolean} */ Evaluator.prototype.fnAND = function () { var condtions = []; for (var _i = 0; _i < arguments.length; _i++) { condtions[_i] = arguments[_i]; } return condtions.every(function (c) { return c(); }); }; /** * 条件任意一个满足条件,返回 true,否则返回 false。 * * 示例:OR(语文成绩>80, 数学成绩>80), * * 语文成绩和数学成绩任意一个大于 80,则返回 true,否则返回 false, * * 等价于直接用JS表达式如:语文成绩>80 || 数学成绩>80。 * * @example OR(expression1, expression2, ...expressionN) * @param {...expression} conditions 条件表达式,多个用逗号隔开。例如:语文成绩>80, 数学成绩>80 * @namespace 逻辑函数 * * @returns {boolean} */ Evaluator.prototype.fnOR = function () { var condtions = []; for (var _i = 0; _i < arguments.length; _i++) { condtions[_i] = arguments[_i]; } return condtions.some(function (c) { return c(); }); }; /** * 异或处理,多个表达式组中存在奇数个真时认为真。 * * 示例:XOR(语文成绩 > 80, 数学成绩 > 80, 英语成绩 > 80) * * 三门成绩中有一门或者三门大于 80,则返回 true,否则返回 false。 * * @example XOR(condition1, condition2, ...expressionN) * @param {...expression} condition 条件表达式,多个用逗号隔开。例如:语文成绩>80, 数学成绩>80 * @namespace 逻辑函数 * * @returns {boolean} */ Evaluator.prototype.fnXOR = function () { var condtions = []; for (var _i = 0; _i < arguments.length; _i++) { condtions[_i] = arguments[_i]; } return !!(condtions.filter(function (c) { return c(); }).length % 2); }; /** * 判断函数集合,相当于多个 else if 合并成一个。 * * 示例:IFS(语文成绩 > 80, "优秀", 语文成绩 > 60, "良", "继续努力"), * * 如果语文成绩大于 80,则返回优秀,否则判断大于 60 分,则返回良,否则返回继续努力。 * * @example IFS(condition1, result1, condition2, result2,...conditionN, resultN) * @param {...expression} condition 条件表达式 * @param {...any} result 返回值 * @namespace 逻辑函数 * @returns {any} 第一个满足条件的结果,没有命中的返回 false。 */ Evaluator.prototype.fnIFS = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (args.length % 2) { args.splice(args.length - 1, 0, function () { return true; }); } while (args.length) { var c = args.shift(); var v = args.shift(); if (c()) { return v(); } } return; }; /** * 返回传入数字的绝对值。 * * @example ABS(num) * @param {number} num - 数值 * @namespace 数学函数 * * @returns {number} 传入数值的绝对值 */ Evaluator.prototype.fnABS = function (a) { a = this.formatNumber(a); return Math.abs(a); }; /** * 获取最大值,如果只有一个参数且是数组,则计算这个数组内的值。 * * @example MAX(num1, num2, ...numN) or MAX([num1, num2, ...numN]) * @param {...number} num - 数值 * @namespace 数学函数 * * @returns {number} 所有传入值中最大的那个 */ Evaluator.prototype.fnMAX = function () { var _this = this; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var arr = normalizeArgs(args); return Math.max.apply(Math, arr.map(function (item) { return _this.formatNumber(item); })); }; /** * 获取最小值,如果只有一个参数且是数组,则计算这个数组内的值。 * * @example MIN(num1, num2, ...numN) or MIN([num1, num2, ...numN]) * @param {...number} num - 数值 * @namespace 数学函数 * * @returns {number} 所有传入值中最小的那个 */ Evaluator.prototype.fnMIN = function () { var _this = this; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var arr = normalizeArgs(args); return Math.min.apply(Math, arr.map(function (item) { return _this.formatNumber(item); })); }; /** * 求和,如果只有一个参数且是数组,则计算这个数组内的值。 * * @example SUM(num1, num2, ...numN) or SUM([num1, num2, ...numN]) * @param {...number} num - 数值 * @namespace 数学函数 * * @returns {number} 所有传入数值的总和 */ Evaluator.prototype.fnSUM = function () { var _this = this; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var arr = normalizeArgs(args); return arr.reduce(function (sum, a) { return sum + _this.formatNumber(a) || 0; }, 0); }; /** * 将数值向下取整为最接近的整数。 * * @example INT(num) * @param {number} num - 数值 * @namespace 数学函数 * * @returns {number} 数值对应的整形 */ Evaluator.prototype.fnINT = function (n) { return Math.floor(this.formatNumber(n)); }; /** * 返回两数相除的余数,参数 number 是被除数,divisor 是除数。 * * @example MOD(num, divisor) * @param {number} num - 被除数 * @param {number} divisor - 除数 * @namespace 数学函数 * * @returns {number} 两数相除的余数 */ Evaluator.prototype.fnMOD = function (a, b) { return this.formatNumber(a) % this.formatNumber(b); }; /** * 圆周率 3.1415...。 * * @example PI() * @namespace 数学函数 * * @returns {number} 圆周率数值 */ Evaluator.prototype.fnPI = function () { return Math.PI; }; /** * 将数字四舍五入到指定的位数,可以设置小数位。 * * @example ROUND(num[, numDigits = 2]) * @param {number} num - 要处理的数字 * @param {number} numDigits - 小数位数,默认为2 * @namespace 数学函数 * * @returns {number} 传入数值四舍五入后的结果 */ Evaluator.prototype.fnROUND = function (a, b) { if (b === void 0) { b = 2; } a = this.formatNumber(a); b = this.formatNumber(b); var bResult = Math.round(b); if (bResult) { var c = Math.pow(10, bResult); return Math.round(a * c) / c; } return Math.round(a); }; /** * 将数字向下取整到指定的位数,可以设置小数位。 * * @example FLOOR(num[, numDigits=2]) * @param {number} num - 要处理的数字 * @param {number} numDigits - 小数位数,默认为2 * @namespace 数学函数 * * @returns {number} 传入数值向下取整后的结果 */ Evaluator.prototype.fnFLOOR = function (a, b) { if (b === void 0) { b = 2; } a = this.formatNumber(a); b = this.formatNumber(b); var bResult = Math.round(b); if (bResult) { var c = Math.pow(10, bResult); return Math.floor(a * c) / c; } return Math.floor(a); }; /** * 将数字向上取整到指定的位数,可以设置小数位。 * * @example CEIL(num[, numDigits=2]) * @param {number} num - 要处理的数字 * @param {number} numDigits - 小数位数,默认为2 * @namespace 数学函数 * * @returns {number} 传入数值向上取整后的结果 */ Evaluator.prototype.fnCEIL = function (a, b) { if (b === void 0) { b = 2; } a = this.formatNumber(a); b = this.formatNumber(b); var bResult = Math.round(b); if (bResult) { var c = Math.pow(10, bResult); return Math.ceil(a * c) / c; } return Math.ceil(a); }; /** * 开平方,参数 number 为非负数 * * @example SQRT(num) * @param {number} num - 要处理的数字 * @namespace 数学函数 * * @returns {number} 开平方的结果 */ Evaluator.prototype.fnSQRT = function (n) { return Math.sqrt(this.formatNumber(n)); }; /** * 返回所有参数的平均值,如果只有一个参数且是数组,则计算这个数组内的值。 * * @example AVG(num1, num2, ...numN) or AVG([num1, num2, ...numN]) * @param {...number} num - 要处理的数字 * @namespace 数学函数 * * @returns {number} 所有数值的平均值 */ Evaluator.prototype.fnAVG = function () { var _this = this; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var arr = normalizeArgs(args); return (this.fnSUM.apply(this, arr.map(function (item) { return _this.formatNumber(item); })) / arr.length); }; /** * 返回数据点与数据均值点之差(数据偏差)的平方和,如果只有一个参数且是数组,则计算这个数组内的值。 * * @example DEVSQ(num1, num2, ...numN) * @param {...number} num - 要处理的数字 * @namespace 数学函数 * * @returns {number} 所有数值的平均值 */ Evaluator.prototype.fnDEVSQ = function () { var e_1, _a; var _this = this; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (args.length === 0) { return null; } var arr = normalizeArgs(args); var nums = arr.map(function (item) { return _this.formatNumber(item); }); var sum = nums.reduce(function (sum, a) { return sum + a || 0; }, 0); var mean = sum / nums.length; var result = 0; try { for (var nums_1 = __values(nums), nums_1_1 = nums_1.next(); !nums_1_1.done; nums_1_1 = nums_1.next()) { var num = nums_1_1.value; result += Math.pow(num - mean, 2); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (nums_1_1 && !nums_1_1.done && (_a = nums_1.return)) _a.call(nums_1); } finally { if (e_1) throw e_1.error; } } return result; }; /** * 数据点到其算术平均值的绝对偏差的平均值。 * * @example AVEDEV(num1, num2, ...numN) * @param {...number} num - 要处理的数字 * @namespace 数学函数 * * @returns {number} 所有数值的平均值 */ Evaluator.prototype.fnAVEDEV = function () { var e_2, _a; var _this = this; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (args.length === 0) { return null; } var arr = args; if (args.length === 1 && Array.isArray(args[0])) { arr = args[0]; } var nums = arr.map(function (item) { return _this.formatNumber(item); }); var sum = nums.reduce(function (sum, a) { return sum + a || 0; }, 0); var mean = sum / nums.length; var result = 0; try { for (var nums_2 = __values(nums), nums_2_1 = nums_2.next(); !nums_2_1.done; nums_2_1 = nums_2.next()) { var num = nums_2_1.value; result += Math.abs(num - mean); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (nums_2_1 && !nums_2_1.done && (_a = nums_2.return)) _a.call(nums_2); } finally { if (e_2) throw e_2.error; } } return result / nums.length; }; /** * 数据点的调和平均值,如果只有一个参数且是数组,则计算这个数组内的值。 * * @example HARMEAN(num1, num2, ...numN) * @param {...number} num - 要处理的数字 * @namespace 数学函数 * * @returns {number} 所有数值的平均值 */ Evaluator.prototype.fnHARMEAN = function () { var e_3, _a; var _this = this; var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (args.length === 0) { return null; } var arr = args; if (args.length === 1 && Array.isArray(args[0])) { arr = args[0]; } var nums = arr.map(function (item) { return _this.formatNumber(item); }); var den = 0; try { for (var nums_3 = __values(nums), nums_3_1 = nums_3.next(); !nums_3_1.done; nums_3_1 = nums_3.next()) { var num = nums_3_1.value; den += 1 / num; } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (nums_3_1 && !nums_3_1.done && (_a = nums_3.return)) _a.call(nums_3); } finally { if (e_3) throw e_3.error; } } return nums.length / den; }; /** * 数据集中第 k 个最大值。 * * @example LARGE(array, k) * @param {array} nums - 要处理的数字 * @param {number} k - 第几大 * @namespace 数学函数 * * @returns {number} 所有数值的平均值 */ Evaluator.prototype.fnLARGE = function (nums, k) { var _this = this; if (nums.length === 0) { return null; } var numsFormat = nums.map(function (item) { return _this.formatNumber(item); }); if (k < 0 || numsFormat.length < k) { return null; } return numsFormat.sort(function (a, b) { return b - a; })[k - 1]; }; /** * 将数值转为中文大写金额。 * * @example UPPERMONEY(num) * @param {number} num - 要处理的数字 * @namespace 数学函数 * * @returns {string} 数值中文大写字符 */ Evaluator.prototype.fnUPPERMONEY = function (n) { var _a; n = this.formatNumber(n); var maxLen = 14; if (((_a = n.toString().split('.')[0]) === null || _a === void 0 ? void 0 : _a.length) > maxLen) { return "\u6700\u5927\u6570\u989D\u53EA\u652F\u6301\u5230\u5146(\u65E2\u5C0F\u6570\u70B9\u524D".concat(maxLen, "\u4F4D)"); } var fraction = ['角', '分']; var digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']; var unit = [ ['元', '万', '亿', '兆'], ['', '拾', '佰', '仟'] ]; var head = n < 0 ? '欠' : ''; n = Math.abs(n); var 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 (var i = 0; i < unit[0].length && n > 0; i++) { var 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(/^整$/, '零元整')); }; /** * 返回大于等于 0 且小于 1 的均匀分布随机实数。每一次触发计算都会变化。 * * 示例:`RAND()*100`, * * 返回 0-100 之间的随机数。 * * @example RAND() * @namespace 数学函数 * * @returns {number} 随机数 */ Evaluator.prototype.fnRAND = function () { return Math.random(); }; /** * 取数据最后一个。 * * @example LAST(array) * @param {...number} arr - 要处理的数组 * @namespace 数学函数 * * @returns {any} 最后一个值 */ Evaluator.prototype.fnLAST = function (arr) { return arr.length ? arr[arr.length - 1] : null; }; /** * 返回基数的指数次幂,参数base为基数,exponent为指数,如果参数值不合法则返回基数本身,计算结果不合法,则返回NaN。 * * @example POW(base, exponent) * @param {number} base 基数 * @param {number} exponent 指数 * @namespace 数学函数 * * @returns {number} 基数的指数次幂 */ Evaluator.prototype.fnPOW = function (base, exponent) { if (!this.isValidValue(base) || !this.isValidValue(exponent)) { return base; } return Math.pow(this.formatNumber(base), this.formatNumber(exponent)); }; // 文本函数 Evaluator.prototype.normalizeText = function (raw) { if (raw instanceof Date) { return moment(raw).format(); } return "".concat(raw); }; /** * 返回传入文本左侧的指定长度字符串。 * * @example LEFT(text, len) * @param {string} text - 要处理的文本 * @param {number} len - 要处理的长度 * @namespace 文本函数 * * @returns {string} 对应字符串 */ Evaluator.prototype.fnLEFT = function (text, len) { text = this.normalizeText(text); return text.substring(0, len); }; /** * 返回传入文本右侧的指定长度字符串。 * * @example RIGHT(text, len) * @param {string} text - 要处理的文本 * @param {number} len - 要处理的长度 * @namespace 文本函数 * * @returns {string} 对应字符串 */ Evaluator.prototype.fnRIGHT = function (text, len) { text = this.normalizeText(text); return text.substring(text.length - len, text.length); }; /** * 计算文本的长度。 * * @example LEN(text) * @param {string} text - 要处理的文本 * @namespace 文本函数 * * @returns {number} 长度 */ Evaluator.prototype.fnLEN = function (text) { if (text === undefined || text === null) { return 0; } text = this.normalizeText(text); return text === null || text === void 0 ? void 0 : text.length; }; /** * 计算文本集合中所有文本的长度。 * * @example LENGTH(textArr) * @param {string[]} textArr - 要处理的文本集合 * @namespace 文本函数 * * @returns {number[]} 长度集合 */ Evaluator.prototype.fnLENGTH = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return this.fnLEN.apply(this, args); }; /** * 判断文本是否为空。 * * @example ISEMPTY(text) * @param {string} text - 要处理的文本 * @namespace 文本函数 * * @returns {boolean} 判断结果 */ Evaluator.prototype.fnISEMPTY = function (text) { return !text || !String(text).trim(); }; /** * 将多个传入值连接成文本。 * * @example CONCATENATE(text1, text2, ...textN) * @param {...string} text - 文本集合 * @namespace 文本函数 * * @returns {string} 连接后的文本 */ Evaluator.prototype.fnCONCATENATE = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return args.map(this.normalizeText).join(''); }; /** * 返回计算机字符集的数字代码所对应的字符。 * * 示例:`CHAR(97)` 等价于 "a"。 * * @example CHAR(code) * @param {number} code - 编码值 * @namespace 文本函数 * * @returns {string} 指定位置的字符 */ Evaluator.prototype.fnCHAR = function (code) { return String.fromCharCode(code); }; /** * 将传入文本转成小写。 * * @example LOWER(text) * @param {string} text - 文本 * @namespace 文本函数 * * @returns {string} 结果文本 */ Evaluator.prototype.fnLOWER = function (text) { text = this.normalizeText(text); return text.toLowerCase(); }; /** * 将传入文本转成大写。 * * @example UPPER(text) * @param {string} text - 文本 * @namespace 文本函数 * * @returns {string} 结果文本 */ Evaluator.prototype.fnUPPER = function (text) { text = this.normalizeText(text); return text.toUpperCase(); }; /** * 将传入文本首字母转成大写。 * * @example UPPERFIRST(text) * @param {string} text - 文本 * @namespace 文本函数 * * @returns {string} 结果文本 */ Evaluator.prototype.fnUPPERFIRST = function (text) { text = this.normalizeText(text); return upperFirst(text); }; /** * 向前补齐文本长度。 * * 示例 `PADSTART("6", 2, "0")`, * * 返回 `06`。 * * @example PADSTART(text) * @param {string} text - 文本 * @param {number} num - 目标长度 * @param {string} pad - 用于补齐的文本 * @namespace 文本函数 * * @returns {string} 结果文本 */ Evaluator.prototype.fnPADSTART = function (text, num, pad) { text = this.normalizeText(text); return padStart(text, num, pad); }; /** * 将文本转成标题。 * * 示例 `CAPITALIZE("star")`, * * 返回 `Star`。 * * @example CAPITALIZE(text) * @param {string} text - 文本 * @namespace 文本函数 * * @returns {string} 结果文本 */ Evaluator.prototype.fnCAPITALIZE = function (text) { text = this.normalizeText(text); return capitalize(text); }; /** * 对文本进行 HTML 转义。 * * 示例 `ESCAPE("<star>&")`, * * 返回 `&lt;start&gt;&amp;`。 * * @example ESCAPE(text) * @param {string} text - 文本 * @namespace 文本函数 * * @returns {string} 结果文本 */ Evaluator.prototype.fnESCAPE = function (text) { text = this.normalizeText(text); return escape(text); }; /** * 对文本长度进行截断。 * * 示例 `TRUNCATE("amis.baidu.com", 6)`, * * 返回 `amis...`。 * * @example TRUNCATE(text, 6) * @param {string} text - 文本 * @param {number} text - 最长长度 * @namespace 文本函数 * * @returns {string} 结果文本 */ Evaluator.prototype.fnTRUNCATE = function (text, length) { text = this.normalizeText(text); return truncate(text, { length: length }); }; /** * 取在某个分隔符之前的所有字符串。 * * @example BEFORELAST(text, '.') * @param {string} text - 文本 * @param {string} delimiter - 结束文本 * @namespace 文本函数 * * @returns {string} 判断结果 */ Evaluator.prototype.fnBEFORELAST = function (text, delimiter) { if (delimiter === void 0) { delimiter = '.'; } text = this.normalizeText(text); delimiter = this.normalizeText(delimiter); return text.split(delimiter).slice(0, -1).join(delimiter) || text + ''; }; /** * 将文本根据指定片段分割成数组。 * * 示例:`SPLIT("a,b,c", ",")`, * * 返回 `["a", "b", "c"]`。 * * @example SPLIT(text, ',') * @param {string} text - 文本 * @param {string} delimiter - 文本片段 * @namespace 文本函数 * * @returns {Array<string>} 文本集 */ Evaluator.prototype.fnSPLIT = function (text, sep) { if (sep === void 0) { sep = ','; } text = this.normalizeText(text); return text.split(sep); }; /** * 将文本去除前后空格。 * * @example TRIM(text) * @param {string} text - 文本 * @namespace 文本函数 * * @returns {string} 处理后的文本 */ Evaluator.prototype.fnTRIM = function (text) { text = this.normalizeText(text); return text.trim(); }; /** * 去除文本中的 HTML 标签。 * * 示例:`STRIPTAG("<b>amis</b>")`, * * 返回:`amis`。 * * @example STRIPTAG(text) * @param {string} text - 文本 * @namespace 文本函数 * * @returns {string} 处理后的文本 */ Evaluator.prototype.fnSTRIPTAG = function (text) { text = this.normalizeText(text); return text.replace(/<\/?[^>]+(>|$)/g, ''); }; /** * 将字符串中的换行转成 HTML `<br>`,用于简单换行的场景。 * * 示例:`LINEBREAK("\n")`, * * 返回:`<br/>`。 * * @example LINEBREAK(text) * @param {string} text - 文本 * @namespace 文本函数 * * @returns {string} 处理后的文本 */ Evaluator.prototype.fnLINEBREAK = function (text) { text = this.normalizeText(text); return text.replace(/(?:\r\n|\r|\n)/g, '<br/>'); }; /** * 判断字符串(text)是否以特定字符串(startString)开始,是则返回 true,否则返回 false。 * * @example STARTSWITH(text, '片段') * @param {string} text - 文本 * @param {string} startString - 起始文本 * @namespace 文本函数 * * @returns {boolean} 判断结果 */ Evaluator.prototype.fnSTARTSWITH = function (text, search) { search = this.normalizeText(search); if (!search) { return false; } text = this.normalizeText(text); return text.indexOf(search) === 0; }; /** * 判断字符串(text)是否以特定字符串(endString)结束,是则返回 true,否则返回 false。 * * @example ENDSWITH(text, '片段') * @param {string} text - 文本 * @param {string} endString - 结束文本 * @namespace 文本函数 * * @returns {boolean} 判断结果 */ Evaluator.prototype.fnENDSWITH = function (text, search) { search = this.normalizeText(search); if (!search) { return false; } text = this.normalizeText(text); return text.indexOf(search, text.length - search.length) !== -1; }; /** * 判断参数 1 中的文本是否包含参数 2 中的文本,是则返回 true,否则返回 false。 * * @example CONTAINS(text, searchText) * @param {string} text - 文本 * @param {string} searchText - 搜索文本 * @namespace 文本函数 * * @returns {boolean} 判断结果 */ Evaluator.prototype.fnCONTAINS = function (text, search) { search = this.normalizeText(search); if (!search) { return false; } text = this.normalizeText(text); return !!~text.indexOf(search); }; /** * 对文本进行全量替换。 * * @example REPLACE(text, search, replace) * @param {string} text - 要处理的文本 * @param {string} search - 要被替换的文本 * @param {string} replace - 要替换的文本 * @namespace 文本函数 * * @returns {string} 处理结果 */ Evaluator.prototype.fnREPLACE = function (text, search, replace) { text = this.normalizeText(text); search = this.normalizeText(search); replace = this.normalizeText(replace); var result = text; if (typeof replace === 'undefined' || !search) { return result; } var shouldLoop = !(typeof replace === 'string' && replace.includes(search)); while (true) { var idx = result.indexOf(search); if (!~idx) { break; } result = result.substring(0, idx) + replace + result.substring(idx + search.length); if (!shouldLoop) { break; } } return result; }; /** * 对文本进行搜索,返回命中的位置。 * * @example SEARCH(text, search, 0) * @param {string} text - 要处理的文本 * @param {string} search - 用来搜索的文本 * @param {number} start - 起始位置 * @namespace 文本函数 * * @returns {number} 命中的位置 */ Evaluator.prototype.fnSEARCH = function (text, search, start) { if (start === void 0) { start = 0; } search = this.normalizeText(search); text = this.normalizeText(text); start = this.formatNumber(start); var idx = text.indexOf(search, start); if (~idx && search) { return idx; } return -1; }; /** * 返回文本字符串中从指定位置开始的特定数目的字符。 * * 示例:`MID("amis.baidu.com", 6, 3)`, * * 返回 `aid`。 * * @example MID(text, from, len) * @param {string} text - 要处理的文本 * @param {number} from - 起始位置 * @param {number} len - 处理长度 * @namespace 文本函数 * * @returns {string} 命中的位置 */ Evaluator.prototype.fnMID = function (text, from, len) { text = this.normalizeText(text); from = this.formatNumber(from); len = this.formatNumber(len); return text.substring(from, from + len); }; /** * 返回路径中的文件名。 * * 示例:`/home/amis/a.json`, * * 返回:`a.json`。 * * @example BASENAME(text) * @param {string} text - 要处理的文本 * @namespace 文本函数 * * @returns {string} 文件名 */ Evaluator.prototype.fnBASENAME = function (text) { text = this.normalizeText(text); return text.split(/[\\/]/).pop(); }; /** * 生成UUID字符串 * * @param {number} length - 生成的UUID字符串长度,默认为32位 * @example UUID() * @example UUID(8) * @namespace 文本函数 * * @returns {string} 生成的UUID字符串 */ Evaluator.prototype.fnUUID = function (length) { if (length === void 0) { length = 36; } var len = Math.min(Math.max(length, 0), 36); return uuidv4().slice(0, len); }; // 日期函数 /** * 创建日期对象,可以通过特定格式的字符串,或者数值。 * * 需要注意的是,其中月份的数值是从0开始的, * 即如果是12月份,你应该传入数值11。 * * @example DATE(2021, 11, 6, 8, 20, 0) * @example DATE('2021-12-06 08:20:00') * @namespace 日期函数 * * @returns {Date} 日期对象 */ Evaluator.prototype.fnDATE = function (year, month, day, hour, minute, second) { if (month === undefined) { return new Date(year); } return new Date(year, month, day, hour, minute, second); }; /** * 返回时间的时间戳。 * * @example TIMESTAMP(date[, format = "X"]) * @namespace 日期函数 * @param {date} date 日期对象 * @param {string} format 时间戳格式,带毫秒传入 'x'。默认为 'X' 不带毫秒的。 * * @returns {number} 时间戳 */ Evaluator.prototype.fnTIMESTAMP = function (date, format) { return parseInt(moment(this.normalizeDate(date)).format(format === 'x' ? 'x' : 'X'), 10); }; /** * 返回今天的日期。 * * @example TODAY() * @namespace 日期函数 * * @returns {number} 日期 */ Evaluator.prototype.fnTODAY = function () { return new Date(); }; /** * 返回现在的日期 * * @example NOW() * @namespace 日期函数 * * @returns {number} 日期 */ Evaluator.prototype.fnNOW = function () { return new Date(); }; /** * 获取日期的星期几。 * * 示例 * * WEEKDAY('2023-02-27') 得到 0。 * WEEKDAY('2023-02-27', 2) 得到 1。 * * @example WEEKDAY(date) * @namespace 日期函数 * @param {any} date 日期 * @param {number} type 星期定义类型,默认为1,1表示0至6代表星期一到星期日,2表示1至7代表星期一到星期日 * * @returns {number} 星期几的数字标识 */ Evaluator.prototype.fnWEEKDAY = function (date, type) { var md = moment(this.normalizeDate(date)); return type === 2 ? md.isoWeekday() : md.weekday(); }; /** * 获取年份的星期,即第几周。 * * 示例 * * WEEK('2023-03-05') 得到 9。 * * @example WEEK(date) * @namespace 日期函数 * @param {any} date 日期 * @param {boolean} isISO 是否ISO星期 * * @returns {number} 星期几的数字标识 */ Evaluator.prototype.fnWEEK = function (date, isISO) { if (isISO === void 0) { isISO = false; } var md = moment(this.normalizeDate(date)); return isISO ? md.isoWeek() : md.week(); }; /** * 对日期、日期字符串、时间戳进行格式化。 * * 示例 * * DATETOSTR('12/25/2022', 'YYYY-MM-DD') 得到 '2022.12.25', * DATETOSTR(1676563200, 'YYYY.MM.DD') 得到 '2023.02.17', * DATETOSTR(1676563200000, 'YYYY.MM.DD hh:mm:ss') 得到 '2023.02.17 12:00:00', * DATETOSTR(DATE('2021-12-21'), 'YYYY.MM.DD hh:mm:ss') 得到 '2021.12.21 08:00:00'。 * * @example DATETOSTR(date, 'YYYY-MM-DD') * @namespace 日期函数 * @param {any} date 日期对象、日期字符串、时间戳 * @param {string} format 日期格式,默认为 "YYYY-MM-DD HH:mm:ss" * * @returns {string} 日期字符串 */ Evaluator.prototype.fnDATETOSTR = function (date, format) { if (format === void 0) { format = 'YYYY-MM-DD HH:mm:ss'; } date = this.normalizeDate(date); return moment(date).format(format); }; /** * 获取日期范围字符串中的开始时间、结束时间。 * * 示例: * * DATERANGESPLIT('1676563200, 1676735999') 得到 [1676563200, 1676735999], * DATERANGESPLIT('1676563200, 1676735999', undefined , 'YYYY.MM.DD hh:mm:ss') 得到 [2023.02.17 12:00:00, 2023.02.18 11:59:59], * DATERANGESPLIT('1676563200, 1676735999', 0 , 'YYYY.MM.DD hh:mm:ss') 得到 '2023.02.17 12:00:00', * DATERANGESPLIT('1676563200, 1676735999', 'start' , 'YYYY.MM.DD hh:mm:ss') 得到 '2023.02.17 12:00:00', * DATERANGESPLIT('1676563200, 1676735999', 1 , 'YYYY.MM.DD hh:mm:ss') 得到 '2023.02.18 11:59:59', * DATERANGESPLIT('1676563200, 1676735999', 'end' , 'YYYY.MM.DD hh:mm:ss') 得到 '2023.02.18 11:59:59'。 * * @example DATERANGESPLIT(date, 'YYYY-MM-DD') * @namespace 日期函数 * @param {string} date 日期范围字符串 * @param {string} key 取值标识,0或'start'表示获取开始时间,1或'end'表示获取结束时间 * @param {string} format 日期格式,可选 * @param {string} delimiter 分隔符,可选,默认为',' * * @returns {string} 日期字符串 */ Evaluator.prototype.fnDATERANGESPLIT = function (daterange, key, format, delimiter) { var _this = this; if (delimiter === void 0) { delimiter = ','; } if (!daterange || typeof daterange !== 'string') { return daterange; } var dateArr = daterange .split(delimiter) .map(function (item) { return item && format ? moment(_this.normalizeDate(item.trim())).format(format) : item.trim(); }); if ([0, '0', 'start'].includes(key)) { return dateArr[0]; } if ([1, '1', 'end'].includes(key)) { return dateArr[1]; } return dateArr; }; /** * 返回日期的指定范围的开端。 * * @namespace 日期函数 * @example STARTOF(date[unit = "day"]) * @param {date} date 日期对象 * @param {string} unit 比如可以传入 'day'、'month'、'year' 或者 `week` 等等 * @param {string} format 日期格式,可选 * @returns {any} 新的日期对象, 如果传入 format 则返回格式化后的日期字符串 */ Evaluator.prototype.fnSTARTOF = function (date, u