FieldAggregation calc

This commit is contained in:
devezhao 2020-01-14 18:11:03 +08:00
parent ad02c47a4b
commit 948afddcb1
4 changed files with 119 additions and 37 deletions

View file

@ -54,6 +54,11 @@ public class FieldAggregation implements TriggerAction {
private static final Log LOG = LogFactory.getLog(FieldAggregation.class);
/**
* 归集到自己
*/
public static final String SOURCE_SELF = "$SELF$";
// 此触发器可能产生连锁反应
// 如触发器 A 调用 B B 又调用了 C ... 以此类推此处记录其深度
private static final ThreadLocal<Integer> CALL_CHAIN_DEPTH = new ThreadLocal<>();

View file

@ -21,6 +21,7 @@ package com.rebuild.web.admin.robot;
import cn.devezhao.persist4j.Entity;
import cn.devezhao.persist4j.Field;
import com.alibaba.fastjson.JSON;
import com.rebuild.server.business.trigger.impl.FieldAggregation;
import com.rebuild.server.configuration.RobotApprovalManager;
import com.rebuild.server.metadata.MetadataHelper;
import com.rebuild.server.metadata.MetadataSorter;
@ -36,9 +37,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author devezhao zhaofang123@gmail.com
@ -53,7 +52,6 @@ public class FieldAggregationControll extends BaseControll {
Entity sourceEntity = MetadataHelper.getEntity(getParameterNotNull(request, "source"));
List<String[]> entities = new ArrayList<>();
Map<String, Integer> hasMany = new HashMap<>();
for (Field refField : MetadataSorter.sortFields(sourceEntity, DisplayType.REFERENCE)) {
if (MetadataHelper.isApprovalField(refField.getName())) {
continue;
@ -62,27 +60,11 @@ public class FieldAggregationControll extends BaseControll {
Entity refEntity = refField.getReferenceEntity();
String entityLabel = EasyMeta.getLabel(refEntity) + " (" + EasyMeta.getLabel(refField) + ")";
entities.add(new String[] { refEntity.getName(), entityLabel, refField.getName() });
Integer many = hasMany.get(refEntity.getName());
if (many == null) {
many = 0;
}
hasMany.put(refEntity.getName(), many + 1);
}
// 会出现同实体中多个字段引用同一实体的情况
// 只有一个引用则不显示字段名称
for (Map.Entry<String, Integer> e : hasMany.entrySet()) {
if (e.getValue() == 1) {
String entityName = e.getKey();
for (String[] item : entities) {
if (entityName.equals(item[0])) {
item[1] = EasyMeta.getLabel(MetadataHelper.getEntity(entityName));
}
}
}
}
// 可归集到自己通过主键字段
entities.add(new String[] { sourceEntity.getName(), EasyMeta.getLabel(sourceEntity) + " (源实体)", FieldAggregation.SOURCE_SELF});
writeSuccess(response, entities);
}

View file

@ -108,6 +108,52 @@
color: #222;
}
.field-aggregation .form-control-plaintext.FORMULA {
border: 1px dotted #4c8bf5 !important;
border-radius: 2px;
background-color: #fff;
padding: 9px 10px;
height: 37px !important;
cursor: pointer;
}
.formula-calc ul li {
width: 23%;
display: inline-block;
font-size: 0;
padding: 0;
margin: 1% !important;
}
.formula-calc ul li>a {
display: inline-block;
width: 100%;
padding: 5px;
font-weight: bold;
text-align: center;
background-color: #eee;
border-radius: 2px;
font-size: 1rem;
}
.formula-calc ul li>a:hover {
background-color: #ccc;
}
.formula-calc ul.fields li {
width: 100%;
}
.formula-calc ul.fields li>a {
font-weight: normal;
text-align: left;
padding-left: 10px;
}
.field-aggregation .form-control-plaintext.FORMULA:hover {
opacity: .8;
}
.auto-assign .select2-selection__rendered,
.auto-share .select2-selection__rendered {
display: block !important;

View file

@ -1,11 +1,13 @@
const CALC_MODES = { 'SUM': '求和', 'COuNT': '计数', 'AVG': '平均值', 'MAX': '最大', 'MIN': '最小' }
const CALC_MODES = { 'SUM': '求和', 'COUNT': '计数', 'AVG': '平均值', 'MAX': '最大', 'MIN': '最小', 'FORMULA': '计算公式' }
// ~~
// eslint-disable-next-line no-undef
class ContentFieldAggregation extends ActionContentSpec {
constructor(props) {
super(props)
}
render() {
return <div className="field-aggregation">
<form className="simple">
@ -30,13 +32,13 @@ class ContentFieldAggregation extends ActionContentSpec {
<div className="items">
{(!this.state.items || this.state.items.length === 0) ? null : this.state.items.map((item) => {
return (<div key={'item-' + item.targetField}><div className="row">
<div className="col-5"><span className="badge badge-warning">{this.__getFieldLabel(this.state.targetFields, item.targetField)}</span></div>
<div className="col-5"><span className="badge badge-warning">{this.getFieldLabel(this.state.targetFields, item.targetField)}</span></div>
<div className="col-2">
<span className="zmdi zmdi-forward zmdi-hc-rotate-180"></span>
<span className="badge badge-warning">{CALC_MODES[item.calcMode]}</span>
</div>
<div className="col-5">
<span className="badge badge-warning">{this.__getFieldLabel(this.state.sourceFields, item.sourceField)}</span>
<span className="badge badge-warning">{this.getFieldLabel(this.state.sourceFields, item.sourceField)}</span>
<a className="del" title="移除" onClick={() => this.delItem(item.targetField)}><span className="zmdi zmdi-close"></span></a>
</div>
</div></div>)
@ -61,12 +63,18 @@ class ContentFieldAggregation extends ActionContentSpec {
<p>聚合方式</p>
</div>
<div className="col-5">
<select className="form-control form-control-sm" ref={(c) => this._sourceField = c}>
{(this.state.sourceFields || []).map((item) => {
return <option key={'sf-' + item[0]} value={item[0]}>{item[1]}</option>
})}
</select>
<p>源字段</p>
<div className={this.state.calcMode === 'FORMULA' ? '' : 'hide'}>
<div className="form-control-plaintext FORMULA" onClick={() => renderRbcomp(<FormulaCalc fields={this.state.sourceFields} />)}>计算公式</div>
<p>计算公式</p>
</div>
<div className={this.state.calcMode === 'FORMULA' ? 'hide' : ''}>
<select className="form-control form-control-sm" ref={(c) => this._sourceField = c}>
{(this.state.sourceFields || []).map((item) => {
return <option key={'sf-' + item[0]} value={item[0]}>{item[1]}</option>
})}
</select>
<p>源字段</p>
</div>
</div>
</div>
<div className="mt-1">
@ -101,9 +109,7 @@ class ContentFieldAggregation extends ActionContentSpec {
$.get(`${rb.baseUrl}/admin/robot/trigger/field-aggregation-entities?source=${this.props.sourceEntity}`, (res) => {
this.setState({ targetEntities: res.data }, () => {
let s2te = $(this._targetEntity).select2({ placeholder: '选择聚合目标实体' })
.on('change', () => {
this.__changeTargetEntity()
})
.on('change', () => this.changeTargetEntity())
s2te.trigger('change')
if (this.props.content && this.props.content.targetEntity) {
@ -123,7 +129,7 @@ class ContentFieldAggregation extends ActionContentSpec {
}
}
__changeTargetEntity() {
changeTargetEntity() {
//
this.setState({ items: [] })
@ -138,6 +144,7 @@ class ContentFieldAggregation extends ActionContentSpec {
this.setState({ sourceFields: res.data.source, targetFields: res.data.target }, () => {
let s2sf = $(this._sourceField).select2({ placeholder: '选择源字段' })
let s2cm = $(this._calcMode).select2({ placeholder: '选择聚合方式' })
.on('change', (e) => this.setState({ calcMode: e.target.value }))
let s2tf = $(this._targetField).select2({ placeholder: '选择目标字段' })
this.__select2.push(s2sf)
this.__select2.push(s2cm)
@ -150,7 +157,8 @@ class ContentFieldAggregation extends ActionContentSpec {
}
})
}
__getFieldLabel(list, field) {
getFieldLabel(list, field) {
for (let i = 0; i < list.length; i++) {
if (list[i][0] === field) {
return list[i][1]
@ -179,6 +187,7 @@ class ContentFieldAggregation extends ActionContentSpec {
this.setState({ items: items })
}
}
delItem(targetField) {
let items = (this.state.items || []).filter((item) => {
return item.targetField !== targetField
@ -206,6 +215,7 @@ class ContentFieldAggregation extends ActionContentSpec {
filter={that._advFilter__data}
confirm={that._saveAdvFilter} />, null, function () { that._advFilter = this })
}
_saveAdvFilter = (filter) => {
this._advFilter__data = filter
let num = filter && filter.items ? filter.items.length : 0
@ -213,6 +223,45 @@ class ContentFieldAggregation extends ActionContentSpec {
}
}
// ~
class FormulaCalc extends RbAlert {
constructor(props) {
super(props)
this.state = { ...props }
}
renderContent() {
return (
<div className="row formula-calc">
<div className="col-6">
<ul className="list-unstyled fields">
{this.props.fields.map((item) => {
return <li key={`flag-${item}`}><a onClick={() => this.handleInput(item)}>{item[1]}</a></li>
})}
</ul>
</div>
<div className="col-6">
<ul className="list-unstyled numbers">
{['+', 1, 2, 3, '-', 4, 5, 6, '×', 7, 8, 9, '÷', '(', ')', 0].map((item) => {
return <li className="list-inline-item" key={`flag-${item}`}><a onClick={() => this.handleInput(item)}>{item}</a></li>
})}
</ul>
</div>
</div>
)
}
handleInput(v) {
console.log(v)
}
confirm = () => {
typeof this.props.call === 'function' && this.props.call(this.state || {})
this.hide()
}
}
// eslint-disable-next-line no-undef
renderContentComp = function (props) {
// eslint-disable-next-line no-undef