amis-formula
Version:
负责 amis 里面的表达式实现,内置公式,编辑器等
1,625 lines (1,623 loc) • 80.9 kB
JavaScript
/**
* 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>&")`,
*
* 返回 `<start>&`。
*
* @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