mirror of
https://github.com/getrebuild/rebuild.git
synced 2025-10-06 13:40:00 +08:00
Form calc date 109 (#689)
* feat: eval-calc-formula --------- Co-authored-by: devezhao <zhaofang123@gmail.com>
This commit is contained in:
parent
3f7feaf90b
commit
2690f0791d
8 changed files with 303 additions and 101 deletions
|
@ -49,11 +49,19 @@ public class EasyFieldConfigProps {
|
||||||
* 日期格式
|
* 日期格式
|
||||||
*/
|
*/
|
||||||
public static final String DATE_FORMAT = "dateFormat";
|
public static final String DATE_FORMAT = "dateFormat";
|
||||||
|
/**
|
||||||
|
* 表单公式
|
||||||
|
*/
|
||||||
|
public static final String DATE_CALCFORMULA = NUMBER_CALCFORMULA;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日期格式
|
* 日期格式
|
||||||
*/
|
*/
|
||||||
public static final String DATETIME_FORMAT = "datetimeFormat";
|
public static final String DATETIME_FORMAT = "datetimeFormat";
|
||||||
|
/**
|
||||||
|
* 表单公式
|
||||||
|
*/
|
||||||
|
public static final String DATETIME_CALCFORMULA = NUMBER_CALCFORMULA;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 时间格式
|
* 时间格式
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.googlecode.aviator.AviatorEvaluator;
|
||||||
import com.googlecode.aviator.AviatorEvaluatorInstance;
|
import com.googlecode.aviator.AviatorEvaluatorInstance;
|
||||||
import com.googlecode.aviator.Options;
|
import com.googlecode.aviator.Options;
|
||||||
import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
|
import com.googlecode.aviator.exception.ExpressionSyntaxErrorException;
|
||||||
|
import com.googlecode.aviator.lexer.token.OperatorType;
|
||||||
import com.googlecode.aviator.runtime.function.system.AssertFunction;
|
import com.googlecode.aviator.runtime.function.system.AssertFunction;
|
||||||
import com.googlecode.aviator.runtime.type.AviatorFunction;
|
import com.googlecode.aviator.runtime.type.AviatorFunction;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -20,7 +21,7 @@ import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* // https://www.yuque.com/boyan-avfmj/aviatorscript
|
* https://www.yuque.com/boyan-avfmj/aviatorscript
|
||||||
*
|
*
|
||||||
* @author devezhao
|
* @author devezhao
|
||||||
* @since 2021/4/12
|
* @since 2021/4/12
|
||||||
|
@ -43,6 +44,9 @@ public class AviatorUtils {
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AVIATOR.addOpFunction(OperatorType.ADD, new OverOperatorType.DateAdd());
|
||||||
|
AVIATOR.addOpFunction(OperatorType.SUB, new OverOperatorType.DateSub());
|
||||||
|
|
||||||
addCustomFunction(new DateDiffFunction());
|
addCustomFunction(new DateDiffFunction());
|
||||||
addCustomFunction(new DateAddFunction());
|
addCustomFunction(new DateAddFunction());
|
||||||
addCustomFunction(new DateSubFunction());
|
addCustomFunction(new DateSubFunction());
|
||||||
|
@ -55,13 +59,21 @@ public class AviatorUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表达式计算
|
|
||||||
*
|
|
||||||
* @param expression
|
* @param expression
|
||||||
* @return
|
* @return
|
||||||
|
* @see #eval(String, Map, boolean)
|
||||||
*/
|
*/
|
||||||
public static Object evalQuietly(String expression) {
|
public static Object eval(String expression) {
|
||||||
return eval(expression, null, true);
|
return eval(expression, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param expression
|
||||||
|
* @return
|
||||||
|
* @see #eval(String, Map, boolean)
|
||||||
|
*/
|
||||||
|
public static Object eval(String expression, Map<String, Object> env) {
|
||||||
|
return eval(expression, env, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,13 +86,13 @@ public class AviatorUtils {
|
||||||
*/
|
*/
|
||||||
public static Object eval(String expression, Map<String, Object> env, boolean quietly) {
|
public static Object eval(String expression, Map<String, Object> env, boolean quietly) {
|
||||||
try {
|
try {
|
||||||
return AVIATOR.execute(expression, env);
|
return AVIATOR.execute(expression, env == null ? Collections.emptyMap() : env);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
if (ex instanceof AssertFunction.AssertFailed) {
|
if (ex instanceof AssertFunction.AssertFailed) {
|
||||||
throw new AssertFailedException((AssertFunction.AssertFailed) ex);
|
throw new AssertFailedException((AssertFunction.AssertFailed) ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.error("Bad aviator expression : \n{}\n<< {}", expression, env, ex);
|
log.error("Bad aviator expression : \n>> {}\n>> {}\n>> {}", expression, env, ex.getLocalizedMessage());
|
||||||
if (!quietly) throw ex;
|
if (!quietly) throw ex;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*!
|
||||||
|
Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
|
||||||
|
|
||||||
|
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
||||||
|
See LICENSE and COMMERCIAL in the project root for license information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.rebuild.core.service.trigger.aviator;
|
||||||
|
|
||||||
|
import cn.devezhao.commons.CalendarUtils;
|
||||||
|
import com.googlecode.aviator.lexer.token.OperatorType;
|
||||||
|
import com.googlecode.aviator.runtime.function.AbstractFunction;
|
||||||
|
import com.googlecode.aviator.runtime.type.AviatorLong;
|
||||||
|
import com.googlecode.aviator.runtime.type.AviatorObject;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作符重载
|
||||||
|
*
|
||||||
|
* @author RB
|
||||||
|
* @since 2023/12/6
|
||||||
|
*/
|
||||||
|
public class OverOperatorType {
|
||||||
|
|
||||||
|
private OverOperatorType() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期加
|
||||||
|
*/
|
||||||
|
static class DateAdd extends AbstractFunction {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return OperatorType.ADD.getToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
|
||||||
|
Object $argv1 = arg1.getValue(env);
|
||||||
|
Object $argv2 = arg2.getValue(env);
|
||||||
|
|
||||||
|
if ($argv1 instanceof Date && $argv2 instanceof Number) {
|
||||||
|
return opDate((Date) $argv1, ((Number) $argv2).intValue());
|
||||||
|
} else if ($argv2 instanceof Date && $argv1 instanceof Number) {
|
||||||
|
return opDate((Date) $argv2, ((Number) $argv1).intValue());
|
||||||
|
} else {
|
||||||
|
return arg1.add(arg2, env); // Use default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期减
|
||||||
|
*/
|
||||||
|
static class DateSub extends AbstractFunction {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return OperatorType.SUB.getToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
|
||||||
|
Object $argv1 = arg1.getValue(env);
|
||||||
|
Object $argv2 = arg2.getValue(env);
|
||||||
|
|
||||||
|
if ($argv1 instanceof Date && $argv2 instanceof Number) {
|
||||||
|
return opDate((Date) $argv1, -((Number) $argv2).intValue());
|
||||||
|
} else if ($argv2 instanceof Date && $argv1 instanceof Number) {
|
||||||
|
return opDate((Date) $argv2, -((Number) $argv1).intValue());
|
||||||
|
} else if ($argv1 instanceof Date && $argv2 instanceof Date) {
|
||||||
|
int diff = CalendarUtils.getDayLeft((Date) $argv1, (Date) $argv2);
|
||||||
|
return AviatorLong.valueOf(diff);
|
||||||
|
} else {
|
||||||
|
return arg1.add(arg2, env); // Use default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static AviatorDate opDate(Date date, int num) {
|
||||||
|
Date d = CalendarUtils.addDay(date, num);
|
||||||
|
return new AviatorDate(d);
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,22 +8,32 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
||||||
package com.rebuild.web.general;
|
package com.rebuild.web.general;
|
||||||
|
|
||||||
import cn.devezhao.bizz.privileges.impl.BizzPermission;
|
import cn.devezhao.bizz.privileges.impl.BizzPermission;
|
||||||
|
import cn.devezhao.commons.CalendarUtils;
|
||||||
|
import cn.devezhao.commons.ObjectUtils;
|
||||||
|
import cn.devezhao.commons.web.ServletUtils;
|
||||||
import cn.devezhao.persist4j.Entity;
|
import cn.devezhao.persist4j.Entity;
|
||||||
import cn.devezhao.persist4j.Field;
|
import cn.devezhao.persist4j.Field;
|
||||||
import cn.devezhao.persist4j.engine.ID;
|
import cn.devezhao.persist4j.engine.ID;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONAware;
|
import com.alibaba.fastjson.JSONAware;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.rebuild.api.RespBody;
|
import com.rebuild.api.RespBody;
|
||||||
import com.rebuild.core.Application;
|
import com.rebuild.core.Application;
|
||||||
import com.rebuild.core.configuration.general.AutoFillinManager;
|
import com.rebuild.core.configuration.general.AutoFillinManager;
|
||||||
import com.rebuild.core.metadata.MetadataHelper;
|
import com.rebuild.core.metadata.MetadataHelper;
|
||||||
|
import com.rebuild.core.metadata.easymeta.DisplayType;
|
||||||
|
import com.rebuild.core.metadata.easymeta.EasyDecimal;
|
||||||
import com.rebuild.core.metadata.easymeta.EasyEntity;
|
import com.rebuild.core.metadata.easymeta.EasyEntity;
|
||||||
|
import com.rebuild.core.metadata.easymeta.EasyField;
|
||||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||||
|
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||||
import com.rebuild.core.privileges.UserHelper;
|
import com.rebuild.core.privileges.UserHelper;
|
||||||
import com.rebuild.core.privileges.bizz.User;
|
import com.rebuild.core.privileges.bizz.User;
|
||||||
import com.rebuild.core.service.general.RepeatedRecordsException;
|
import com.rebuild.core.service.general.RepeatedRecordsException;
|
||||||
import com.rebuild.core.service.general.transform.RecordTransfomer;
|
import com.rebuild.core.service.general.transform.RecordTransfomer;
|
||||||
|
import com.rebuild.core.service.trigger.aviator.AviatorUtils;
|
||||||
|
import com.rebuild.core.support.general.ContentWithFieldVars;
|
||||||
import com.rebuild.core.support.i18n.I18nUtils;
|
import com.rebuild.core.support.i18n.I18nUtils;
|
||||||
import com.rebuild.core.support.i18n.Language;
|
import com.rebuild.core.support.i18n.Language;
|
||||||
import com.rebuild.utils.JSONUtils;
|
import com.rebuild.utils.JSONUtils;
|
||||||
|
@ -31,7 +41,9 @@ import com.rebuild.web.BaseController;
|
||||||
import com.rebuild.web.EntityParam;
|
import com.rebuild.web.EntityParam;
|
||||||
import com.rebuild.web.IdParam;
|
import com.rebuild.web.IdParam;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@ -39,7 +51,9 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表单/视图 功能扩展
|
* 表单/视图 功能扩展
|
||||||
|
@ -195,4 +209,80 @@ public class ModelExtrasController extends BaseController {
|
||||||
}
|
}
|
||||||
return allowed;
|
return allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("eval-calc-formula")
|
||||||
|
public RespBody evalCalcFormula(@EntityParam Entity entity, HttpServletRequest request) {
|
||||||
|
String targetField = getParameterNotNull(request, "field");
|
||||||
|
if (!entity.containsField(targetField)) return RespBody.error();
|
||||||
|
|
||||||
|
JSONObject post = (JSONObject) ServletUtils.getRequestJson(request);
|
||||||
|
Map<String, Object> varsInFormula = post.getInnerMap();
|
||||||
|
for (Object value : varsInFormula.values()) {
|
||||||
|
if (value == null || StringUtils.isBlank(value.toString())) {
|
||||||
|
return RespBody.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EasyField easyField = EasyMetaFactory.valueOf(entity.getField(targetField));
|
||||||
|
String formula = easyField.getExtraAttr(EasyFieldConfigProps.DATE_CALCFORMULA);
|
||||||
|
|
||||||
|
boolean canCalc = true;
|
||||||
|
Set<String> fieldVars = ContentWithFieldVars.matchsVars(formula);
|
||||||
|
for (String field : fieldVars) {
|
||||||
|
if (!entity.containsField(field)) {
|
||||||
|
canCalc = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object fieldValue = varsInFormula.get(field);
|
||||||
|
if (fieldValue == null) {
|
||||||
|
canCalc = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fieldValue2 = fieldValue.toString();
|
||||||
|
DisplayType dt = EasyMetaFactory.valueOf(entity.getField(field)).getDisplayType();
|
||||||
|
if (dt == DisplayType.DATE || dt == DisplayType.DATETIME) {
|
||||||
|
fieldValue = CalendarUtils.parse(fieldValue2, CalendarUtils.UTC_DATETIME_FORMAT.substring(0, fieldValue2.length()));
|
||||||
|
} else if (dt == DisplayType.NUMBER || dt == DisplayType.DECIMAL) {
|
||||||
|
fieldValue = EasyDecimal.clearFlaged(fieldValue2);
|
||||||
|
if (StringUtils.isNotBlank((String) fieldValue)) {
|
||||||
|
if (dt == DisplayType.NUMBER) fieldValue = ObjectUtils.toLong(fieldValue);
|
||||||
|
else fieldValue = ObjectUtils.toDouble(fieldValue);
|
||||||
|
} else {
|
||||||
|
fieldValue = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldValue == null) {
|
||||||
|
canCalc = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
varsInFormula.put(field, fieldValue);
|
||||||
|
}
|
||||||
|
if (!canCalc) return RespBody.ok();
|
||||||
|
|
||||||
|
formula = formula
|
||||||
|
.replace("{", "").replace("}", "")
|
||||||
|
.replace("×", "*").replace("÷", "/");
|
||||||
|
|
||||||
|
Object evalVal = AviatorUtils.eval(formula, varsInFormula, true);
|
||||||
|
if (evalVal == null) return RespBody.ok();
|
||||||
|
|
||||||
|
DisplayType dt = easyField.getDisplayType();
|
||||||
|
if (dt == DisplayType.DATE || dt == DisplayType.DATETIME) {
|
||||||
|
if (evalVal instanceof Date) {
|
||||||
|
evalVal = easyField.wrapValue(evalVal);
|
||||||
|
return RespBody.ok(evalVal);
|
||||||
|
}
|
||||||
|
} else if (dt == DisplayType.NUMBER || dt == DisplayType.DECIMAL) {
|
||||||
|
if (evalVal instanceof Number) {
|
||||||
|
evalVal = easyField.wrapValue(evalVal);
|
||||||
|
return RespBody.ok(evalVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.warn("Bad eval value `{}` for field : {}", evalVal, easyField.getRawMeta());
|
||||||
|
return RespBody.ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
</label>
|
</label>
|
||||||
<label class="custom-control custom-control-sm custom-radio custom-control-inline mb-0 mr-1">
|
<label class="custom-control custom-control-sm custom-radio custom-control-inline mb-0 mr-1">
|
||||||
<input class="custom-control-input" type="radio" name="decimalType" value="¥" />
|
<input class="custom-control-input" type="radio" name="decimalType" value="¥" />
|
||||||
<span class="custom-control-label">[[${bundle.L('货币')}]]</span>
|
<span class="custom-control-label">[[${bundle.L('符号')}]]</span>
|
||||||
</label>
|
</label>
|
||||||
<span>
|
<span>
|
||||||
<select class="underline-sm J_decimalTypeFlag">
|
<select class="underline-sm J_decimalTypeFlag">
|
||||||
|
@ -359,14 +359,17 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div th:if="${fieldType == 'DECIMAL' or fieldType == 'NUMBER'}" class="form-group row J_for-DECIMAL J_for-NUMBER">
|
<div
|
||||||
|
th:if="${fieldType == 'DECIMAL' or fieldType == 'NUMBER' or fieldType == 'DATE' or fieldType == 'DATETIME'}"
|
||||||
|
class="form-group row J_for-DECIMAL J_for-NUMBER J_for-DATE J_for-DATETIME"
|
||||||
|
>
|
||||||
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right">[[${bundle.L('表单计算公式')}]]</label>
|
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right">[[${bundle.L('表单计算公式')}]]</label>
|
||||||
<div class="col-md-12 col-xl-6 col-lg-8">
|
<div class="col-md-12 col-xl-6 col-lg-8">
|
||||||
<input type="hidden" class="form-control" id="calcFormula" />
|
<input type="hidden" class="form-control" id="calcFormula" />
|
||||||
<div class="form-control-plaintext formula" id="calcFormula2" th:_title="${bundle.L('无')}">[[${calcFormula ?: calcFormula}]]</div>
|
<div class="form-control-plaintext formula" id="calcFormula2" th:_title="${bundle.L('无')}">[[${calcFormula ?: calcFormula}]]</div>
|
||||||
<p
|
<p
|
||||||
class="form-text"
|
class="form-text"
|
||||||
th:utext="${bundle.L('本公式仅做前端计算,如公式中所用字段未布局/未显示,则无法进行计算。你也可以通过 [触发器 (字段更新)](/admin/robot/triggers) 实现更强大的计算规则')}"
|
th:utext="${bundle.L('如公式中所用字段未布局/未显示,则无法进行计算。本公式适用前端简单计算,你可以通过 [触发器 (字段更新)](/admin/robot/triggers) 实现更强大的计算规则')}"
|
||||||
></p>
|
></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1165,80 +1165,8 @@ class RbFormNumber extends RbFormText {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
super.componentDidMount()
|
super.componentDidMount()
|
||||||
|
|
||||||
// 表单计算(视图下无效)
|
|
||||||
if (this.props.calcFormula && !this.props.onView) {
|
|
||||||
const calcFormula = this.props.calcFormula.replace(new RegExp('×', 'ig'), '*').replace(new RegExp('÷', 'ig'), '/')
|
|
||||||
const fixed = this.props.decimalFormat ? (this.props.decimalFormat.split('.')[1] || '').length : 0
|
|
||||||
|
|
||||||
// 等待字段初始化完毕
|
|
||||||
setTimeout(() => {
|
|
||||||
const calcFormulaValues = {}
|
|
||||||
const watchFields = calcFormula.match(/\{([a-z0-9]+)}/gi) || []
|
|
||||||
|
|
||||||
watchFields.forEach((item) => {
|
|
||||||
const name = item.substr(1, item.length - 2)
|
|
||||||
const fieldComp = this.props.$$$parent.refs[`fieldcomp-${name}`]
|
|
||||||
if (fieldComp && !$emptyNum(fieldComp.state.value)) {
|
|
||||||
calcFormulaValues[name] = this._removeComma(fieldComp.state.value)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表单计算
|
// 表单计算
|
||||||
let _timer
|
if (this.props.calcFormula && !this.props.onView) __calcFormula(this)
|
||||||
this.props.$$$parent.onFieldValueChange((s) => {
|
|
||||||
if (!watchFields.includes(`{${s.name}}`)) {
|
|
||||||
if (rb.env === 'dev') console.log('onFieldValueChange ignored :', s, this.props.field)
|
|
||||||
return false
|
|
||||||
} else if (rb.env === 'dev') {
|
|
||||||
console.log('onFieldValueChange for calcFormula :', s, this.props.field)
|
|
||||||
}
|
|
||||||
|
|
||||||
// // fix: 3.2 字段相互使用导致死循环
|
|
||||||
// this.__fixUpdateDepth = (this.__fixUpdateDepth || 0) + 1
|
|
||||||
// if (this.__fixUpdateDepth > 9) {
|
|
||||||
// console.log(`Maximum update depth exceeded : ${this.props.field}=${this.props.calcFormula}`)
|
|
||||||
// setTimeout(() => (this.__fixUpdateDepth = 0), 100)
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
|
|
||||||
if ($emptyNum(s.value)) {
|
|
||||||
delete calcFormulaValues[s.name]
|
|
||||||
} else {
|
|
||||||
calcFormulaValues[s.name] = this._removeComma(s.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
let formula = calcFormula
|
|
||||||
for (let key in calcFormulaValues) {
|
|
||||||
formula = formula.replace(new RegExp(`{${key}}`, 'ig'), calcFormulaValues[key] || 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_timer) {
|
|
||||||
clearTimeout(_timer)
|
|
||||||
_timer = null
|
|
||||||
}
|
|
||||||
|
|
||||||
// v34 延迟执行
|
|
||||||
_timer = setTimeout(() => {
|
|
||||||
// 还有变量无值
|
|
||||||
if (formula.includes('{')) {
|
|
||||||
this.setValue(null)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
let calcv = null
|
|
||||||
eval(`calcv = ${formula}`)
|
|
||||||
if (!isNaN(calcv)) this.setValue(calcv.toFixed(fixed))
|
|
||||||
} catch (err) {
|
|
||||||
if (rb.env === 'dev') console.log(err)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}, 200)
|
|
||||||
// _timer end
|
|
||||||
})
|
|
||||||
}, 200) // init
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移除千分为位
|
// 移除千分为位
|
||||||
|
@ -1496,6 +1424,12 @@ class RbFormDateTime extends RbFormElement {
|
||||||
wt = $(this._fieldValue).offset().top
|
wt = $(this._fieldValue).offset().top
|
||||||
return wt + 280 < wh ? 'bottom-right' : 'top-right'
|
return wt + 280 < wh ? 'bottom-right' : 'top-right'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
super.componentDidMount()
|
||||||
|
// 表单计算
|
||||||
|
if (this.props.calcFormula && !this.props.onView) __calcFormula(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RbFormTime extends RbFormDateTime {
|
class RbFormTime extends RbFormDateTime {
|
||||||
|
@ -3002,3 +2936,59 @@ const __addRecentlyUse = function (id) {
|
||||||
$.post(`/commons/search/recently-add?id=${id}`)
|
$.post(`/commons/search/recently-add?id=${id}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 表单计算(视图下无效)
|
||||||
|
const __calcFormula = function (_this) {
|
||||||
|
const watchFields = _this.props.calcFormula.match(/\{([a-z0-9]+)}/gi) || []
|
||||||
|
const $$$parent = _this.props.$$$parent
|
||||||
|
|
||||||
|
const evalUrl = `/app/entity/extras/eval-calc-formula?entity=${$$$parent.props.entity}&field=${_this.props.field}`
|
||||||
|
setTimeout(() => {
|
||||||
|
const calcFormulaValues = {}
|
||||||
|
let _timer
|
||||||
|
|
||||||
|
// init
|
||||||
|
watchFields.forEach((item) => {
|
||||||
|
const name = item.substr(1, item.length - 2)
|
||||||
|
const fieldComp = $$$parent.refs[`fieldcomp-${name}`]
|
||||||
|
if (fieldComp && !$empty(fieldComp.state.value)) {
|
||||||
|
calcFormulaValues[name] = fieldComp.state.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// onchange
|
||||||
|
$$$parent.onFieldValueChange((s) => {
|
||||||
|
if (!watchFields.includes(`{${s.name}}`)) {
|
||||||
|
if (rb.env === 'dev') console.log('onFieldValueChange ignored :', s, _this.props.field)
|
||||||
|
return false
|
||||||
|
} else if (rb.env === 'dev') {
|
||||||
|
console.log('onFieldValueChange for calcFormula :', s, _this.props.field)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($empty(s.value)) delete calcFormulaValues[s.name]
|
||||||
|
else calcFormulaValues[s.name] = s.value
|
||||||
|
|
||||||
|
if (_timer) {
|
||||||
|
clearTimeout(_timer)
|
||||||
|
_timer = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// v36
|
||||||
|
_timer = setTimeout(() => {
|
||||||
|
$.post(evalUrl, JSON.stringify(calcFormulaValues), (res) => {
|
||||||
|
if (res.data) _this.setValue(res.data)
|
||||||
|
else _this.setValue(null)
|
||||||
|
})
|
||||||
|
}, 400)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 新建时
|
||||||
|
if (_this._isNew) {
|
||||||
|
$.post(evalUrl, JSON.stringify(calcFormulaValues), (res) => {
|
||||||
|
if (res.data) _this.setValue(res.data)
|
||||||
|
else _this.setValue(null)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 400) // delay for init
|
||||||
|
}
|
||||||
|
|
|
@ -232,6 +232,7 @@ $(document).ready(function () {
|
||||||
} else if (dt === 'SERIES') {
|
} else if (dt === 'SERIES') {
|
||||||
_handleSeries()
|
_handleSeries()
|
||||||
} else if (dt === 'DATE' || dt === 'DATETIME' || dt === 'TIME') {
|
} else if (dt === 'DATE' || dt === 'DATETIME' || dt === 'TIME') {
|
||||||
|
if (dt === 'DATE' || dt === 'DATETIME') _handleCalcFormula(extConfig.calcFormula)
|
||||||
_handleDatetime(dt)
|
_handleDatetime(dt)
|
||||||
} else if (dt === 'FILE' || dt === 'IMAGE') {
|
} else if (dt === 'FILE' || dt === 'IMAGE') {
|
||||||
_handleFile(extConfig.uploadNumber)
|
_handleFile(extConfig.uploadNumber)
|
||||||
|
@ -247,8 +248,7 @@ $(document).ready(function () {
|
||||||
} else if (dt === 'BARCODE') {
|
} else if (dt === 'BARCODE') {
|
||||||
$('.J_fieldAttrs input').attr('disabled', true)
|
$('.J_fieldAttrs input').attr('disabled', true)
|
||||||
} else if (dt === 'NUMBER' || dt === 'DECIMAL') {
|
} else if (dt === 'NUMBER' || dt === 'DECIMAL') {
|
||||||
_handleNumber(extConfig.calcFormula)
|
_handleCalcFormula(extConfig.calcFormula)
|
||||||
|
|
||||||
if (dt === 'DECIMAL') {
|
if (dt === 'DECIMAL') {
|
||||||
if (!extConfig.decimalType || extConfig.decimalType === 0 || extConfig.decimalType === '0' || extConfig.decimalType === '%') {
|
if (!extConfig.decimalType || extConfig.decimalType === 0 || extConfig.decimalType === '0' || extConfig.decimalType === '%') {
|
||||||
// 数字、百分比
|
// 数字、百分比
|
||||||
|
@ -580,7 +580,7 @@ const _loadRefsLabel = function ($dv, $dvClear) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let FIELDS_CACHE
|
let FIELDS_CACHE
|
||||||
const _handleNumber = function (calcFormula) {
|
const _handleCalcFormula = function (formula) {
|
||||||
const $el = $('#calcFormula2')
|
const $el = $('#calcFormula2')
|
||||||
function _call(s) {
|
function _call(s) {
|
||||||
$('#calcFormula').val(s || '')
|
$('#calcFormula').val(s || '')
|
||||||
|
@ -591,13 +591,13 @@ const _handleNumber = function (calcFormula) {
|
||||||
$.get(`/commons/metadata/fields?entity=${wpc.entityName}`, (res) => {
|
$.get(`/commons/metadata/fields?entity=${wpc.entityName}`, (res) => {
|
||||||
const fs = []
|
const fs = []
|
||||||
res.data.forEach((item) => {
|
res.data.forEach((item) => {
|
||||||
if ((item.type === 'NUMBER' || item.type === 'DECIMAL') && item.name !== wpc.fieldName) {
|
if (['NUMBER', 'DECIMAL', 'DATE', 'DATETIME'].includes(item.type) && !['approvalLastTime', 'modifiedOn'].includes(item.name) && item.name !== wpc.fieldName) {
|
||||||
fs.push(item)
|
fs.push(item)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
FIELDS_CACHE = fs
|
FIELDS_CACHE = fs
|
||||||
if (calcFormula) _call(calcFormula)
|
if (formula) _call(formula)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,13 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
||||||
|
|
||||||
package com.rebuild.core.service.trigger.aviator;
|
package com.rebuild.core.service.trigger.aviator;
|
||||||
|
|
||||||
|
import cn.devezhao.commons.CalendarUtils;
|
||||||
import com.googlecode.aviator.exception.ExpressionRuntimeException;
|
import com.googlecode.aviator.exception.ExpressionRuntimeException;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,7 +24,7 @@ class AviatorUtilsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void eval() {
|
void eval() {
|
||||||
System.out.println(AviatorUtils.evalQuietly("123*123"));
|
System.out.println(AviatorUtils.eval("123*123"));
|
||||||
|
|
||||||
System.out.println(AviatorUtils.eval(
|
System.out.println(AviatorUtils.eval(
|
||||||
"abc12_.abc+123", Collections.singletonMap("abc12_.abc", 100), true));
|
"abc12_.abc+123", Collections.singletonMap("abc12_.abc", 100), true));
|
||||||
|
@ -36,30 +38,30 @@ class AviatorUtilsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void func() {
|
void func() {
|
||||||
AviatorUtils.evalQuietly("p(DATEDIFF('2021-03-04 00:00:00', '2022-03-05 23:59:59', 'D'))");
|
AviatorUtils.eval("p(DATEDIFF('2021-03-04 00:00:00', '2022-03-05 23:59:59', 'D'))");
|
||||||
AviatorUtils.evalQuietly("p(DATEDIFF('2021-03-04 00:00:00', '2022-03-05 23:59:59', 'M'))");
|
AviatorUtils.eval("p(DATEDIFF('2021-03-04 00:00:00', '2022-03-05 23:59:59', 'M'))");
|
||||||
AviatorUtils.evalQuietly("p(DATEDIFF('2021-03-04 00:00:00', '2022-03-09 23:59:59', 'Y'))");
|
AviatorUtils.eval("p(DATEDIFF('2021-03-04 00:00:00', '2022-03-09 23:59:59', 'Y'))");
|
||||||
|
|
||||||
AviatorUtils.evalQuietly("p(DATEADD('2021-01-01 18:17:00', '2H'))");
|
AviatorUtils.eval("p(DATEADD('2021-01-01 18:17:00', '2H'))");
|
||||||
|
|
||||||
AviatorUtils.evalQuietly("p(DATESUB('2021-01-01 18:17:00', '1'))");
|
AviatorUtils.eval("p(DATESUB('2021-01-01 18:17:00', '1'))");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void funcComplex() {
|
void funcComplex() {
|
||||||
AviatorUtils.evalQuietly("p(100 + DATEDIFF('2021-01-01 18:17:00', '2021-01-01 16:17:00', 'H'))");
|
AviatorUtils.eval("p(100 + DATEDIFF('2021-01-01 18:17:00', '2021-01-01 16:17:00', 'H'))");
|
||||||
AviatorUtils.evalQuietly("p(DATEADD(DATEADD('2021-01-01 18:17:00', '2H'), '1D'))");
|
AviatorUtils.eval("p(DATEADD(DATEADD('2021-01-01 18:17:00', '2H'), '1D'))");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void funcRequestFunction() {
|
void funcRequestFunction() {
|
||||||
AviatorUtils.evalQuietly("p(REQUEST('https://www.baidu.com/'))");
|
AviatorUtils.eval("p(REQUEST('https://www.baidu.com/'))");
|
||||||
AviatorUtils.evalQuietly("p(REQUEST('https://www.google.com/', 'imdefault'))");
|
AviatorUtils.eval("p(REQUEST('https://www.google.com/', 'imdefault'))");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void funcLocationDistanceFunction() {
|
void funcLocationDistanceFunction() {
|
||||||
AviatorUtils.evalQuietly("p(LOCATIONDISTANCE('123.456789,123.456789', '地址$$$$123.456789,123.456789'))");
|
AviatorUtils.eval("p(LOCATIONDISTANCE('123.456789,123.456789', '地址$$$$123.456789,123.456789'))");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -75,6 +77,19 @@ class AviatorUtilsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testJava() {
|
void testJava() {
|
||||||
AviatorUtils.evalQuietly("p(StringUtils.upperCase('abcd'));");
|
AviatorUtils.eval("p(StringUtils.upperCase('abcd'));");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testDateOp() {
|
||||||
|
Map<String, Object> env = new HashMap<>();
|
||||||
|
env.put("date1", CalendarUtils.now());
|
||||||
|
AviatorUtils.eval("p(date1 + 8)", env, true);
|
||||||
|
AviatorUtils.eval("p(date1 - 8)", env, true);
|
||||||
|
AviatorUtils.eval("p(date1 - date1)", env, true);
|
||||||
|
|
||||||
|
// BAD
|
||||||
|
Assertions.assertThrows(ExpressionRuntimeException.class,
|
||||||
|
() -> AviatorUtils.eval("date1 + date1", env, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue