Merge branch 'master' into develop

This commit is contained in:
RB 2022-12-01 14:25:03 +08:00
commit cc466a4d48
54 changed files with 446 additions and 229 deletions

2
@rbv

@ -1 +1 @@
Subproject commit 09da8d287be7103eeb3bfe727cdb75d5e19e3bb1
Subproject commit 0f0dfa3fed8bad766e863c5db1a9b472e8b1f018

View file

@ -10,8 +10,9 @@ package com.rebuild.core.configuration.general;
import cn.devezhao.persist4j.engine.ID;
import com.alibaba.fastjson.JSON;
import com.rebuild.core.Application;
import com.rebuild.core.RebuildException;
import com.rebuild.core.configuration.ConfigBean;
import com.rebuild.core.configuration.ConfigurationException;
import com.rebuild.core.privileges.bizz.ZeroEntry;
/**
* 基础布局管理
@ -36,14 +37,13 @@ public class BaseLayoutManager extends ShareToManager {
public static final String TYPE_LISTSTATS = "LISTSTATS";
// 列表-查询面板
public static final String TYPE_LISTFILTERPANE = "LISTFILTERPANE";
// 列表-图表 of Widget
// 列表-图表
public static final String TYPE_WCHARTS = "WCHARTS";
// 视图-相关项
public static final String TYPE_TAB = "TAB";
// 视图-新建相关
public static final String TYPE_ADD = "ADD";
@Override
protected String getConfigEntity() {
return "LayoutConfig";
@ -82,13 +82,19 @@ public class BaseLayoutManager extends ShareToManager {
* @return
*/
public ConfigBean getLayout(ID user, String belongEntity, String applyType) {
ID detected = detectUseConfig(user, belongEntity, applyType);
if (detected == null) {
return null;
// 221125 无权限不允许使用自有配置
boolean firstUseSelf = true;
if (TYPE_NAV.equals(applyType)) {
firstUseSelf = Application.getPrivilegesManager().allow(user, ZeroEntry.AllowCustomNav);
} else if (TYPE_DATALIST.equals(applyType)) {
firstUseSelf = Application.getPrivilegesManager().allow(user, ZeroEntry.AllowCustomDataList);
}
ID detected = detectUseConfig(user, belongEntity, applyType, firstUseSelf);
if (detected == null) return null;
Object[][] cached = getAllConfig(belongEntity, applyType);
return findEntry(cached, detected);
return findConfigBean(cached, detected);
}
/**
@ -100,12 +106,10 @@ public class BaseLayoutManager extends ShareToManager {
"select belongEntity,applyType from LayoutConfig where configId = ?")
.setParameter(1, cfgid)
.unique();
if (o == null) {
throw new RebuildException("No config found : " + cfgid);
}
if (o == null) throw new ConfigurationException("No config found : " + cfgid);
Object[][] cached = getAllConfig((String) o[0], (String) o[1]);
return findEntry(cached, cfgid);
return findConfigBean(cached, cfgid);
}
/**
@ -113,7 +117,7 @@ public class BaseLayoutManager extends ShareToManager {
* @param cfgid
* @return
*/
protected ConfigBean findEntry(Object[][] uses, ID cfgid) {
protected ConfigBean findConfigBean(Object[][] uses, ID cfgid) {
for (Object[] c : uses) {
if (c[0].equals(cfgid)) {
return new ConfigBean()

View file

@ -64,13 +64,18 @@ public class DataListCategory {
} else {
String cf = EasyMetaFactory.valueOf(entity).getExtraAttr(EasyEntityConfigProps.ADV_LIST_SHOWCATEGORY);
String[] ff = cf.split(":");
String ffField = ff[0];
String ffFormat = ff.length > 1 ? ff[1] : null;
String sql;
if (dt == DisplayType.N2NREFERENCE) {
sql = MessageFormat.format(
"select distinct referenceId from NreferenceItem where belongEntity = ''{0}'' and belongField = ''{1}''",
entity.getName(), categoryField.getName());
entity.getName(), ffField);
} else {
String wrapField = categoryField.getName();
String wrapField = ffField;
if (dt == DisplayType.DATETIME) {
wrapField = String.format("DATE_FORMAT(%s, '%%Y-%%m-%%d')", wrapField);
}
@ -85,10 +90,6 @@ public class DataListCategory {
: Application.getQueryFactory().createQuery(sql, user);
Object[][] array = query.array();
String cf = EasyMetaFactory.valueOf(entity).getExtraAttr(EasyEntityConfigProps.ADV_LIST_SHOWCATEGORY);
String[] ff = cf.split(":");
String format = ff.length > 1 ? ff[1] : null;
Set<Object> unique = new HashSet<>();
for (Object[] o : array) {
@ -96,11 +97,11 @@ public class DataListCategory {
Object label;
if (dt == DisplayType.DATE || dt == DisplayType.DATETIME) {
format = StringUtils.defaultIfBlank(format, CalendarUtils.UTC_DATE_FORMAT);
ffFormat = StringUtils.defaultIfBlank(ffFormat, CalendarUtils.UTC_DATE_FORMAT);
if (id instanceof Date) {
label = CalendarUtils.format(format, (Date) id);
label = CalendarUtils.format(ffFormat, (Date) id);
} else {
label = id.toString().substring(0, format.length());
label = id.toString().substring(0, ffFormat.length());
}
id = label;

View file

@ -47,19 +47,21 @@ public class DataListManager extends BaseLayoutManager {
* @param entity
* @param user
* @return
* @see #formatListFields(String, ID, boolean, ConfigBean)
*/
public JSON getFieldsLayout(String entity, ID user) {
return getFieldsLayout(entity, user, true);
public JSON getListFields(String entity, ID user) {
return getListFields(entity, user, true);
}
/**
* @param entity
* @param user
* @param filter 过滤无读取权限的字段
* @param filter
* @return
* @see #formatListFields(String, ID, boolean, ConfigBean)
*/
public JSON getFieldsLayout(String entity, ID user, boolean filter) {
return formatFieldsLayout(entity, user, filter, getLayoutOfDatalist(user, entity));
public JSON getListFields(String entity, ID user, boolean filter) {
return formatListFields(entity, user, filter, getLayoutOfDatalist(user, entity));
}
/**
@ -69,7 +71,7 @@ public class DataListManager extends BaseLayoutManager {
* @param config
* @return
*/
public JSON formatFieldsLayout(String entity, ID user, boolean filter, ConfigBean config) {
public JSON formatListFields(String entity, ID user, boolean filter, ConfigBean config) {
List<Map<String, Object>> columnList = new ArrayList<>();
Entity entityMeta = MetadataHelper.getEntity(entity);
Field namedField = entityMeta.getNameField();
@ -259,7 +261,7 @@ public class DataListManager extends BaseLayoutManager {
* @return
*/
public JSON getFieldsLayoutMode2(Entity entity) {
JSONObject emptyConfig = (JSONObject) formatFieldsLayout(entity.getName(), null, true, null);
JSONObject emptyConfig = (JSONObject) formatListFields(entity.getName(), null, true, null);
JSONArray fields = emptyConfig.getJSONArray("fields");
if (entity.containsField(EntityHelper.ApprovalState)) {

View file

@ -179,10 +179,17 @@ public class FormsBuilder extends FormsManager {
// 自动只读
Set<String> roAutos = EasyMetaFactory.getAutoReadonlyFields(entity);
Set<String> roAutosWithout = record == null ? null : Collections.emptySet();
for (Object o : elements) {
JSONObject field = (JSONObject) o;
if (roAutos.contains(field.getString("field"))) {
field.put("readonly", true);
// 前端可收集值
if (roAutosWithout == null) roAutosWithout = AutoFillinManager.instance.getAutoReadonlyFields(entity);
if (roAutosWithout.contains(field.getString("field"))) {
field.put("readonlyw", true);
}
}
}

View file

@ -73,16 +73,30 @@ public abstract class ShareToManager implements ConfigManager {
* @param belongEntity
* @param applyType
* @return
* @see #detectUseConfig(ID, String, String, boolean)
*/
public ID detectUseConfig(ID user, String belongEntity, String applyType) {
return detectUseConfig(user, belongEntity, applyType, Boolean.TRUE);
}
/**
* @param user
* @param belongEntity
* @param applyType
* @param firstUseSelf
* @return
*/
protected ID detectUseConfig(ID user, String belongEntity, String applyType, boolean firstUseSelf) {
final Object[][] alls = getAllConfig(belongEntity, applyType);
if (alls.length == 0) return null;
// 1.优先使用自己的
for (Object[] d : alls) {
ID createdBy = (ID) d[2];
if (UserHelper.isSelf(user, createdBy)) {
return (ID) d[0];
if (firstUseSelf) {
for (Object[] d : alls) {
ID createdBy = (ID) d[2];
if (UserHelper.isSelf(user, createdBy)) {
return (ID) d[0];
}
}
}

View file

@ -23,7 +23,7 @@ import java.util.Date;
/**
* @author Zhao Fangfang
* @see MetadataHelper
* @since 1.0, 2013-6-26
* @since 1.0, 2018-6-26
*/
public class EntityHelper {

View file

@ -35,7 +35,7 @@ import java.util.regex.Pattern;
* JSON 解析 Record
*
* @author Zhao Fangfang
* @since 1.0, 2013-6-26
* @since 1.0, 2018-6-26
* @see RecordBuilder
*/
@Slf4j

View file

@ -39,7 +39,7 @@ import java.util.Set;
* 基于角色权限的查询过滤器
*
* @author Zhao Fangfang
* @since 1.0, 2013-6-21
* @since 1.0, 2018-6-21
*/
@Slf4j
public class RoleBaseQueryFilter implements Filter, QueryFilter {

View file

@ -220,7 +220,7 @@ public class DataExporter extends SetUser {
@Override
protected DataListWrapper createDataListWrapper(int totalRows, Object[][] data, Query query) {
DataListWrapper wrapper = super.createDataListWrapper(totalRows, data, query);
wrapper.setSecWrapper(false);
wrapper.setMixWrapper(false);
return wrapper;
}
}

View file

@ -125,9 +125,7 @@ public abstract class ObservableService extends Observable implements ServiceSpe
fields.add(base.getEntity().getPrimaryField().getName());
Record snap = Application.getQueryFactory().recordNoFilter(primaryId, fields.toArray(new String[0]));
if (snap == null) {
throw new NoRecordFoundException(primaryId);
}
if (snap == null) throw new NoRecordFoundException(primaryId);
return snap;
}

View file

@ -12,10 +12,13 @@ import cn.devezhao.commons.ObjectUtils;
import cn.devezhao.persist4j.Field;
import com.rebuild.core.Application;
import com.rebuild.core.support.KVStorage;
import com.rebuild.core.support.RebuildConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 数字自增系列
@ -23,9 +26,11 @@ import java.util.Map;
* @author devezhao
* @since 12/24/2018
*/
@Slf4j
public class IncreasingVar extends SeriesVar {
private static final Map<String, Object> LOCKs = new HashMap<>();
private static final Object LOCK = new Object();
private static final Map<String, AtomicInteger> INCREASINGS = new ConcurrentHashMap<>();
private Field field;
private String zeroFlag;
@ -57,51 +62,40 @@ public class IncreasingVar extends SeriesVar {
}
final String nameKey = String.format("Series-%s.%s", field.getOwnEntity().getName(), field.getName());
Object keyLock = null;
synchronized (LOCKs) {
if (null == keyLock) {
keyLock = LOCKs.computeIfAbsent(nameKey, k -> new Object());
}
}
int nextValue;
synchronized (keyLock) {
String val = KVStorage.getCustomValue(nameKey);
if (val != null) {
nextValue = ObjectUtils.toInt(val);
} else {
nextValue = countFromDb();
}
nextValue += 1;
synchronized (LOCK) {
AtomicInteger incr = INCREASINGS.get(nameKey);
if (incr == null) {
String val = KVStorage.getCustomValue(nameKey);
int init = val == null ? countFromDb() : ObjectUtils.toInt(val);
// TODO 使用缓存避免频繁更新数据库
KVStorage.setCustomValue(nameKey, nextValue);
incr = new AtomicInteger(init);
INCREASINGS.put(nameKey, incr);
}
int nextValue = incr.incrementAndGet();
RebuildConfiguration.setCustomValue(nameKey, nextValue, Boolean.TRUE);
return StringUtils.leftPad(nextValue + "", getSymbols().length(), '0');
}
return StringUtils.leftPad(nextValue + "", getSymbols().length(), '0');
}
/**
* 清空序号缓存
*/
protected void clean() {
if (this.field == null) {
return;
}
if (this.field == null) return;
final String nameKey = String.format("Series-%s.%s", field.getOwnEntity().getName(), field.getName());
Object keyLock = null;
synchronized (LOCKs) {
if (null == keyLock) {
keyLock = LOCKs.computeIfAbsent(nameKey, k -> new Object());
}
}
synchronized (keyLock) {
KVStorage.setCustomValue(nameKey, 0);
synchronized (LOCK) {
INCREASINGS.remove(nameKey);
RebuildConfiguration.setCustomValue(nameKey, 0, Boolean.TRUE);
}
}
/**
* NOTE
* 例如有100条记录序号也为100
* 但是删除了10条后调用此方法所生产的序号只有 90直接采用 count 记录数
*
@ -123,7 +117,8 @@ public class IncreasingVar extends SeriesVar {
dateLimit = "(1=1)";
}
String sql = String.format("select count(%s) from %s where %s", field.getName(), field.getOwnEntity().getName(), dateLimit);
String sql = String.format("select count(%s) from %s where %s",
field.getName(), field.getOwnEntity().getName(), dateLimit);
Object[] count = Application.createQueryNoFilter(sql).unique();
return ObjectUtils.toInt(count[0]);
}

View file

@ -264,6 +264,16 @@ public class FieldAggregation extends TriggerAction {
targetRecordId = (ID) o[0];
}
// fix: v3.1.3 清空字段值以后无法找到记录
if (o != null && targetRecordId == null
&& operatingContext.getAction() == BizzPermission.UPDATE && this.getClass() == FieldAggregation.class) {
ID beforeValue = operatingContext.getBeforeRecord().getID(followSourceField);
ID afterValue = operatingContext.getAfterRecord().getID(followSourceField);
if (beforeValue != null && afterValue == null) {
targetRecordId = beforeValue;
}
}
this.followSourceWhere = String.format("%s = '%s'", followSourceField, targetRecordId);
}

View file

@ -45,14 +45,15 @@ public class FieldAggregationRefresh {
// FIELD.ENTITY
String[] targetFieldEntity = ((JSONObject) parentAc.getActionContent()).getString("targetEntity").split("\\.");
String followSourceField = targetFieldEntity[0];
ID beforeRefreshedId = operatingContext.getBeforeRecord().getID(targetFieldEntity[0]);
ID afterRefreshedId = operatingContext.getAfterRecord().getID(targetFieldEntity[0]);
final ID beforeValue = operatingContext.getBeforeRecord().getID(followSourceField);
final ID afterValue = operatingContext.getAfterRecord().getID(followSourceField);
// 之前未聚合
if (beforeRefreshedId == null) return;
if (beforeValue == null) return;
// 未更新
if (beforeRefreshedId.equals(afterRefreshedId)) return;
if (beforeValue.equals(afterValue)) return;
ActionContext actionContext = new ActionContext(null,
parentAc.getSourceEntity(), parentAc.getActionContent(), parentAc.getConfigId());
@ -60,17 +61,14 @@ public class FieldAggregationRefresh {
FieldAggregation fa = new FieldAggregation(actionContext, true);
fa.sourceEntity = parent.sourceEntity;
fa.targetEntity = parent.targetEntity;
fa.targetRecordId = beforeRefreshedId;
fa.followSourceWhere = String.format("%s = '%s'", targetFieldEntity[0], beforeRefreshedId);
fa.targetRecordId = beforeValue;
fa.followSourceWhere = String.format("%s = '%s'", followSourceField, beforeValue);
Record fakeSourceRecord = EntityHelper.forUpdate(beforeRefreshedId, triggerUser, false);
Record fakeSourceRecord = EntityHelper.forUpdate(beforeValue, triggerUser, false);
OperatingContext oCtx = OperatingContext.create(triggerUser, BizzPermission.NONE, fakeSourceRecord, fakeSourceRecord);
try {
fa.execute(oCtx);
// } catch (Throwable ex) {
// // v3.1 出现异常可能导致事物回滚执行因此此处 catch 并无意义
// log.error("Error on trigger ({}) refresh", parentAc.getConfigId(), ex);
} finally {
fa.clean();
}

View file

@ -11,6 +11,7 @@ import cn.devezhao.bizz.privileges.impl.BizzPermission;
import cn.devezhao.commons.CalendarUtils;
import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.engine.ID;
import cn.devezhao.persist4j.engine.NullValue;
import cn.devezhao.persist4j.metadata.MissingMetaExcetion;
import com.alibaba.fastjson.JSONObject;
import com.rebuild.core.Application;
@ -107,11 +108,11 @@ public class GroupAggregation extends FieldAggregation {
// 1.源记录数据
String ql = String.format("select %s from %s where %s = ?",
String sql = String.format("select %s from %s where %s = ?",
StringUtils.join(groupFieldsMapping.keySet().iterator(), ","),
sourceEntity.getName(), sourceEntity.getPrimaryField().getName());
Record sourceRecord = Application.createQueryNoFilter(ql)
final Record sourceRecord = Application.createQueryNoFilter(sql)
.setParameter(1, actionContext.getSourceRecord())
.record();
@ -122,10 +123,24 @@ public class GroupAggregation extends FieldAggregation {
List<String[]> qFieldsRefresh = new ArrayList<>();
boolean allNull = true;
final boolean isGroupUpdate = operatingContext.getAction() == BizzPermission.UPDATE && this.getClass() == GroupAggregation.class;
// 保存前/后值
Map<String, Object[]> valueChanged = new HashMap<>();
for (Map.Entry<String, String> e : groupFieldsMapping.entrySet()) {
String sourceField = e.getKey();
String targetField = e.getValue();
if (isGroupUpdate) {
Object beforeValue = operatingContext.getBeforeRecord().getObjectValue(sourceField);
Object afterValue = operatingContext.getAfterRecord().getObjectValue(sourceField);
if (NullValue.isNull(beforeValue) && NullValue.isNull(afterValue)) {
} else {
valueChanged.put(sourceField, new Object[] { beforeValue, afterValue });
}
}
Object val = sourceRecord.getObjectValue(sourceField);
if (val == null) {
qFields.add(String.format("%s is null", targetField));
@ -211,21 +226,26 @@ public class GroupAggregation extends FieldAggregation {
}
if (allNull) {
log.warn("All group-fields are null");
// 如果分组字段值全空将会触发全量更新
if (!valueChanged.isEmpty()) {
this.groupAggregationRefresh = new GroupAggregationRefresh(this, qFieldsRefresh);
} else {
log.warn("All group-fields are null, ignored");
}
return;
}
this.followSourceWhere = StringUtils.join(qFieldsFollow.iterator(), " and ");
if (operatingContext.getAction() == BizzPermission.UPDATE && this.getClass() == GroupAggregation.class) {
if (isGroupUpdate) {
this.groupAggregationRefresh = new GroupAggregationRefresh(this, qFieldsRefresh);
}
ql = String.format("select %s from %s where ( %s )",
sql = String.format("select %s from %s where ( %s )",
targetEntity.getPrimaryField().getName(), targetEntity.getName(),
StringUtils.join(qFields.iterator(), " and "));
Object[] targetRecord = Application.createQueryNoFilter(ql).unique();
Object[] targetRecord = Application.createQueryNoFilter(sql).unique();
if (targetRecord != null) {
targetRecordId = (ID) targetRecord[0];
return;

View file

@ -59,7 +59,7 @@ public class GroupAggregationRefresh {
}
}
// 部刷新
// 量刷新性能较低
if (targetWhere.size() <= 1) {
targetWhere.clear();
targetWhere.add("(1=1)");
@ -72,6 +72,7 @@ public class GroupAggregationRefresh {
entity.getPrimaryField().getName(),
entity.getName(),
StringUtils.join(targetWhere, " or "));
Object[][] targetArray = Application.createQueryNoFilter(sql).array();
log.info("Maybe refresh target record(s) : {}", targetArray.length);
@ -98,6 +99,7 @@ public class GroupAggregationRefresh {
}
ID fakeUpdateReferenceId = null;
// 1.尝试获取触发源
for (int i = 0; i < o.length - 1; i++) {
Object mayId = o[i];
@ -140,9 +142,6 @@ public class GroupAggregationRefresh {
try {
ga.execute(oCtx);
// } catch (Throwable ex) {
// // v3.1 出现异常可能导致事物回滚执行因此此处 catch 并无意义
// log.error("Error on trigger ({}) refresh record : {}", parentAc.getConfigId(), targetRecordId, ex);
} finally {
ga.clean();
}

View file

@ -13,45 +13,67 @@ import com.rebuild.core.Application;
import com.rebuild.core.BootEnvironmentPostProcessor;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.privileges.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
/**
* K/V 对存储
*
* @author devezhao
* @since 2019/11/22
*/
@Slf4j
public class KVStorage {
public static final Object SETNULL = new Object();
private static final String CUSTOM_PREFIX = "custom.";
/**
* 存储
*
*
* @param key 会自动加 `custom.` 前缀
* @return
*/
public static String getCustomValue(String key) {
return getValue("custom." + key, false, null);
return getValue(CUSTOM_PREFIX + key, false, null);
}
/**
* 获取
*
* @param key 会自动加 `custom.` 前缀
*
* @param key
* @param value
*/
public static void setCustomValue(String key, Object value) {
setValue("custom." + key, value);
setValue(CUSTOM_PREFIX + key, value);
}
/**
*
* @param key
* @param value
* @param throttled 是否节流
*/
public static void setCustomValue(String key, Object value, boolean throttled) {
if (throttled) THROTTLED_QUEUE.put(key, value);
else setCustomValue(key, value);
}
/**
*
* @param key
*/
public static void removeCustomValue(String key) {
setCustomValue(key, SETNULL);
}
// -- RAW
/**
* @param key
* @param value
@ -130,4 +152,26 @@ public class KVStorage {
return value;
}
// -- ASYNC
private static final Timer THROTTLED_TIMER = new Timer("KVStorage-Timer");
private static final Map<String, Object> THROTTLED_QUEUE = new ConcurrentHashMap<>();
static {
THROTTLED_TIMER.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (THROTTLED_QUEUE.isEmpty()) return;
final Map<String, Object> queue = new HashMap<>(THROTTLED_QUEUE);
THROTTLED_QUEUE.clear();
log.info("Synchronize KV pairs ... {}", queue);
for (Map.Entry<String, Object> e : queue.entrySet()) {
RebuildConfiguration.setCustomValue(e.getKey(), e.getValue());
}
}
}, 1000, 1000);
}
}

View file

@ -24,8 +24,8 @@ import java.util.Locale;
import java.util.Map;
/**
* @author ZHAO
* @since 2019-08-23
* !!!! 请勿修改或删除本文件
* !!!! 请严格遵守REBUILD 用户服务协议https://getrebuild.com/legal/service-terms
*/
@Slf4j
public final class License {

View file

@ -12,7 +12,7 @@ import com.alibaba.fastjson.JSON;
/**
* @author Zhao Fangfang
* @since 1.0, 2013-6-20
* @since 1.0, 2018-6-20
*/
public interface DataListBuilder {

View file

@ -34,7 +34,7 @@ import java.util.Map;
* 数据列表数据构建
*
* @author Zhao Fangfang
* @since 1.0, 2013-6-20
* @since 1.0, 2018-6-20
*/
public class DataListBuilderImpl implements DataListBuilder {

View file

@ -13,6 +13,7 @@ import cn.devezhao.persist4j.dialect.FieldType;
import cn.devezhao.persist4j.engine.ID;
import cn.devezhao.persist4j.query.compiler.SelectItem;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.rebuild.core.Application;
import com.rebuild.core.configuration.ConfigBean;
@ -26,6 +27,7 @@ import com.rebuild.core.privileges.bizz.ZeroEntry;
import com.rebuild.core.support.ConfigurationItem;
import com.rebuild.core.support.RebuildConfiguration;
import com.rebuild.utils.JSONUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import java.util.ArrayList;
@ -36,7 +38,7 @@ import java.util.Map;
* 数据包装
*
* @author Zhao Fangfang
* @since 1.0, 2013-6-20
* @since 1.0, 2018-6-20
*/
public class DataListWrapper {
@ -52,7 +54,7 @@ public class DataListWrapper {
// 信息脱敏
protected boolean useDesensitized = false;
private boolean secWrapper = true;
private boolean mixWrapper = true;
/**
@ -93,6 +95,8 @@ public class DataListWrapper {
*/
public JSON toJson() {
final Field nameFiled = entity.getNameField();
final EasyField nameFieldEasy = EasyMetaFactory.valueOf(nameFiled);
final int joinFieldsLen = queryJoinFields == null ? 0 : queryJoinFields.size();
final int selectFieldsLen = selectFields.length - joinFieldsLen;
@ -119,26 +123,31 @@ public class DataListWrapper {
continue;
}
SelectItem fieldItem = selectFields[colIndex];
Field field = fieldItem.getField();
if (field.equals(nameFiled) && !fieldItem.getFieldPath().contains(".")) {
final SelectItem fieldItem = selectFields[colIndex];
final Field fieldMeta = fieldItem.getField();
// 名称字段值
if (fieldMeta.equals(nameFiled) && !fieldItem.getFieldPath().contains(".")) {
nameValue = value;
}
// 如果最终没能取得名称字段则补充
if (field.getType() == FieldType.PRIMARY) {
// At last
if (fieldMeta.getType() == FieldType.PRIMARY) {
// 如无名称字段值则补充
if (nameValue == null) {
nameValue = FieldValueHelper.getLabel((ID) value, StringUtils.EMPTY);
} else {
nameValue = FieldValueHelper.wrapFieldValue(nameValue, nameFiled, true);
if (nameValue == null) {
nameValue = StringUtils.EMPTY;
}
}
((ID) value).setLabel(nameValue);
if (nameValue != null && isUseDesensitized(nameFieldEasy)) {
nameValue = FieldValueHelper.desensitized(nameFieldEasy, nameValue);
}
((ID) value).setLabel(ObjectUtils.defaultIfNull(nameValue, StringUtils.EMPTY));
}
row[colIndex] = wrapFieldValue(value, field);
row[colIndex] = wrapFieldValue(value, fieldMeta);
}
}
@ -156,21 +165,32 @@ public class DataListWrapper {
return FieldValueHelper.wrapMixValue((ID) value, null);
}
final DisplayType dt = easyField.getDisplayType();
final Object originValue = value;
boolean unpack = easyField.getDisplayType() == DisplayType.CLASSIFICATION
|| easyField.getDisplayType() == DisplayType.PICKLIST
|| easyField.getDisplayType() == DisplayType.STATE
|| easyField.getDisplayType() == DisplayType.BOOL;
boolean unpack = dt == DisplayType.CLASSIFICATION || dt == DisplayType.PICKLIST
|| dt == DisplayType.STATE || dt == DisplayType.BOOL;
value = FieldValueHelper.wrapFieldValue(value, easyField, unpack);
if (value != null && isUseDesensitized(easyField)) {
value = FieldValueHelper.desensitized(easyField, value);
if (value != null) {
if (isUseDesensitized(easyField)) {
value = FieldValueHelper.desensitized(easyField, value);
}
// v3.1.3 引用字段使用名称字段作为脱敏依据
if (this.useDesensitized
&& (dt == DisplayType.REFERENCE || dt == DisplayType.N2NREFERENCE)) {
Field useNameField = easyField.getRawMeta().getReferenceEntity().getNameField();
EasyField useNameFieldEasy = EasyMetaFactory.valueOf(useNameField);
if (useNameFieldEasy.isDesensitized()) {
desensitizedMixValue((JSON) value, useNameFieldEasy);
}
}
}
// v2.10 Color
if (value != null && this.secWrapper) {
if (value != null && this.mixWrapper) {
if (easyField.getDisplayType() == DisplayType.PICKLIST) {
String color = PickListManager.instance.getColor((ID) originValue);
if (StringUtils.isNotBlank(color)) {
@ -205,6 +225,19 @@ public class DataListWrapper {
return value;
}
private void desensitizedMixValue(JSON value, EasyField easyField) {
if (value instanceof JSONObject) {
String text = ((JSONObject) value).getString("text");
if (text != null) {
((JSONObject) value).put("text", FieldValueHelper.desensitized(easyField, text));
}
}
// N2N
else if (value instanceof JSONArray) {
for (Object o : (JSONArray) value) desensitizedMixValue((JSON) o, easyField);
}
}
/**
* @see FieldValueHelper#isUseDesensitized(EasyField, ID)
*/
@ -237,9 +270,9 @@ public class DataListWrapper {
/**
* 进一步封装查询结果
*
* @param secWrapper
* @param mixWrapper
*/
public void setSecWrapper(boolean secWrapper) {
this.secWrapper = secWrapper;
public void setMixWrapper(boolean mixWrapper) {
this.mixWrapper = mixWrapper;
}
}

View file

@ -190,18 +190,27 @@ public class ProtocolFilterParser {
protected String parseCategory(String entity, String value) {
Entity rootEntity = MetadataHelper.getEntity(entity);
Field categoryField = DataListCategory.instance.getFieldOfCategory(rootEntity);
if (categoryField == null) return "(9=9)";
if (categoryField == null || StringUtils.isBlank(value)) return "(9=9)";
DisplayType dt = EasyMetaFactory.getDisplayType(categoryField);
value = StringEscapeUtils.escapeSql(value);
if (dt == DisplayType.MULTISELECT) {
return String.format("%s && %d", categoryField.getName(), ObjectUtils.toInt(value));
} else if (dt == DisplayType.N2NREFERENCE) {
return String.format(
"exists (select recordId from NreferenceItem where ^%s = recordId and belongField = '%s' and referenceId = '%s')",
rootEntity.getPrimaryField().getName(), categoryField.getName(), StringEscapeUtils.escapeSql(value));
} else {
return String.format("%s = '%s'", categoryField.getName(), StringEscapeUtils.escapeSql(value));
rootEntity.getPrimaryField().getName(), categoryField.getName(), value);
} else if (dt == DisplayType.DATETIME || dt == DisplayType.DATE) {
String s = value + "0000-01-01 00:00:00".substring(value.length());
String e = value + "0000-12-31 23:59:59".substring(value.length());
if (dt == DisplayType.DATE) {
s = s.split(" ")[0];
e = e.split(" ")[0];
}
return MessageFormat.format("({0} >= ''{1}'' and {0} <= ''{2}'')", categoryField.getName(), s, e);
} else {
return String.format("%s = '%s'", categoryField.getName(), value);
}
}

View file

@ -28,7 +28,7 @@ import java.util.*;
* 列表查询解析
*
* @author Zhao Fangfang
* @since 1.0, 2013-6-20
* @since 1.0, 2018-6-20
*/
public class QueryParser {

View file

@ -43,7 +43,7 @@ public class LoginLogController extends EntityController {
public ModelAndView pageList(HttpServletRequest request) {
ID user = getRequestUser(request);
ModelAndView mv = createModelAndView("/admin/audit/login-logs", "LoginLog", user);
JSON config = DataListManager.instance.getFieldsLayout("LoginLog", user);
JSON config = DataListManager.instance.getListFields("LoginLog", user);
mv.getModel().put("DataListConfig", JSON.toJSONString(config));
return mv;
}

View file

@ -41,7 +41,7 @@ public class DepartmentController extends EntityController {
final ID user = getRequestUser(request);
ModelAndView mv = createModelAndView("/admin/bizuser/dept-list", "Department", user);
JSON config = DataListManager.instance.getFieldsLayout("Department", user);
JSON config = DataListManager.instance.getListFields("Department", user);
mv.getModel().put("DataListConfig", JSON.toJSONString(config));
return mv;
}

View file

@ -46,7 +46,7 @@ public class TeamController extends EntityController {
final ID user = getRequestUser(request);
ModelAndView mv = createModelAndView("/admin/bizuser/team-list", "Team", user);
JSON config = DataListManager.instance.getFieldsLayout("Team", user);
JSON config = DataListManager.instance.getListFields("Team", user);
mv.getModel().put("DataListConfig", JSON.toJSONString(config));
return mv;
}

View file

@ -48,7 +48,7 @@ public class UserController extends EntityController {
final ID user = getRequestUser(request);
ModelAndView mv = createModelAndView("/admin/bizuser/user-list", "User", user);
JSON config = DataListManager.instance.getFieldsLayout("User", user);
JSON config = DataListManager.instance.getListFields("User", user);
mv.getModel().put("DataListConfig", JSON.toJSONString(config));
mv.getModel().put("serviceMail", SMSender.availableMail());
return mv;

View file

@ -15,10 +15,7 @@ import com.rebuild.core.Application;
import com.rebuild.core.support.RebuildConfiguration;
import com.rebuild.core.support.i18n.Language;
import com.rebuild.core.support.integration.QiniuCloud;
import com.rebuild.utils.AppUtils;
import com.rebuild.utils.ImageView2;
import com.rebuild.utils.OkHttpUtils;
import com.rebuild.utils.RbAssert;
import com.rebuild.utils.*;
import com.rebuild.web.BaseController;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
@ -129,7 +126,7 @@ public class FileDownloader extends BaseController {
// 共享查看
if (request.getRequestURI().contains("/filex/access/")) {
String e = getParameter(request, "e");
if (StringUtils.isBlank(e) || Application.getCommonsCache().get(e) == null) {
if (!checkEsign(e)) {
response.sendError(HttpStatus.FORBIDDEN.value(), Language.L("分享的文件已过期"));
return;
}
@ -161,16 +158,22 @@ public class FileDownloader extends BaseController {
@GetMapping(value = "read-raw")
public void readRaw(HttpServletRequest request, HttpServletResponse response) throws IOException {
RbAssert.isAllow(checkUser(request), "Unauthorized access");
String filePath = getParameterNotNull(request, "url");
boolean fullUrl = CommonsUtils.isExternalUrl(filePath);
String charset = getParameter(request, "charset", AppUtils.UTF8);
String content;
if (QiniuCloud.instance().available()) {
String privateUrl = QiniuCloud.instance().makeUrl(filePath);
String privateUrl = fullUrl ? filePath : QiniuCloud.instance().makeUrl(filePath);
content = OkHttpUtils.get(privateUrl, null, charset);
} else {
if (fullUrl) {
String e = filePath.split("\\?e=")[1];
RbAssert.is(checkEsign(e), "Unauthorized access");
filePath = filePath.split("/filex/access/")[1].split("\\?")[0];
}
// Local storage
filePath = checkFilePath(filePath);
File file = RebuildConfiguration.getFileOfData(filePath);
@ -209,6 +212,11 @@ public class FileDownloader extends BaseController {
return user != null;
}
private static boolean checkEsign(String e) {
String check = e == null ? null : Application.getCommonsCache().get(e);
return "rb".equalsIgnoreCase(check);
}
private static String checkFilePath(String filepath) {
filepath = CodecUtils.urlDecode(filepath);
filepath = filepath.replace("\\", "/");

View file

@ -139,7 +139,7 @@ public class ShowFieldsController extends BaseController implements ShareTo {
raw = DataListManager.instance.getLayoutOfDatalist(user, entity);
}
JSONObject config = (JSONObject) DataListManager.instance.formatFieldsLayout(entity, user, false, raw);
JSONObject config = (JSONObject) DataListManager.instance.formatListFields(entity, user, false, raw);
Map<String, Object> ret = new HashMap<>();
ret.put("fieldList", fieldList);

View file

@ -27,9 +27,7 @@ import com.rebuild.core.service.query.ParseHelper;
import com.rebuild.core.support.general.DataListBuilder;
import com.rebuild.core.support.general.DataListBuilderImpl;
import com.rebuild.core.support.i18n.Language;
import com.rebuild.utils.JSONUtils;
import com.rebuild.web.EntityController;
import org.apache.commons.codec.language.bm.Lang;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
@ -81,7 +79,7 @@ public class GeneralListController extends EntityController {
JSON listConfig = null;
if (listMode == 1) {
listConfig = DataListManager.instance.getFieldsLayout(entity, user);
listConfig = DataListManager.instance.getListFields(entity, user);
// 扩展配置
String advListHideFilters = easyEntity.getExtraAttr(EasyEntityConfigProps.ADV_LIST_HIDE_FILTERS);

View file

@ -284,7 +284,7 @@ public class ReferenceSearchController extends EntityController {
ModelAndView mv = createModelAndView("/general/reference-search");
putEntityMeta(mv, searchEntity);
JSON config = DataListManager.instance.getFieldsLayout(searchEntity.getName(), user);
JSON config = DataListManager.instance.getListFields(searchEntity.getName(), user);
mv.getModel().put("DataListConfig", JSON.toJSONString(config));
// 可新建

View file

@ -170,7 +170,7 @@ public class RelatedListController extends BaseController {
@GetMapping("related-list-config")
public RespBody getDataListConfig(HttpServletRequest req, @EntityParam Entity listEntity) {
final ID user = getRequestUser(req);
JSON config = DataListManager.instance.getFieldsLayout(listEntity.getName(), user);
JSON config = DataListManager.instance.getListFields(listEntity.getName(), user);
return RespBody.ok(config);
}
}

View file

@ -141,6 +141,7 @@ public class ApprovalController extends BaseController {
JSONArray aform = new FormBuilder(recordId, user).build(editableFields);
if (aform != null && !aform.isEmpty()) {
data.put("aform", aform);
data.put("aentity", MetadataHelper.getEntityName(recordId));
}
}

View file

@ -10,6 +10,7 @@ package com.rebuild.web.robot.trigger;
import cn.devezhao.persist4j.Entity;
import cn.devezhao.persist4j.engine.ID;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rebuild.core.Application;
import com.rebuild.core.metadata.MetadataHelper;
import com.rebuild.core.metadata.MetadataSorter;
@ -20,6 +21,7 @@ import com.rebuild.core.service.trigger.TriggerAction;
import com.rebuild.core.support.CommonsLock;
import com.rebuild.core.support.License;
import com.rebuild.core.support.i18n.Language;
import com.rebuild.utils.JSONUtils;
import com.rebuild.web.BaseController;
import com.rebuild.web.admin.ConfigCommons;
import org.apache.commons.lang.StringUtils;
@ -113,7 +115,7 @@ public class TriggerAdminController extends BaseController {
public Object[][] triggerList(HttpServletRequest request) {
String belongEntity = getParameter(request, "entity");
String q = getParameter(request, "q");
String sql = "select configId,belongEntity,belongEntity,name,isDisabled,modifiedOn,when,actionType,configId,priority from RobotTriggerConfig" +
String sql = "select configId,belongEntity,belongEntity,name,isDisabled,modifiedOn,when,actionType,configId,priority,actionContent from RobotTriggerConfig" +
" where (1=1) and (2=2)" +
" order by modifiedOn desc, name";
@ -121,7 +123,27 @@ public class TriggerAdminController extends BaseController {
for (Object[] o : array) {
o[7] = Language.L(ActionType.valueOf((String) o[7]));
o[8] = CommonsLock.getLockedUserFormat((ID) o[8]);
// 目标实体
o[10] = parseTargetEntity((String) o[10], (String) o[1]);
}
return array;
}
private String parseTargetEntity(String config, String sourceEntity) {
if (!JSONUtils.wellFormat(config)) return null;
JSONObject configJson = JSON.parseObject(config);
String targetEntity = configJson.getString("targetEntity");
if (StringUtils.isBlank(targetEntity)) return null;
if (targetEntity.startsWith(TriggerAction.SOURCE_SELF)) targetEntity = sourceEntity;
else if (targetEntity.contains(".")) targetEntity = targetEntity.split("\\.")[1];
if (MetadataHelper.containsEntity(targetEntity)) {
return EasyMetaFactory.getLabel(targetEntity);
} else {
return String.format("[%s]", targetEntity.toUpperCase());
}
}
}

View file

@ -5,6 +5,11 @@
<meta name="page-help" content="https://getrebuild.com/docs/admin/approval" />
<link rel="stylesheet" type="text/css" th:href="@{/assets/css/approvals.css}" />
<title>[[${bundle.L('审批流程')}]]</title>
<style>
tr.ui-sortable-handle.ui-sortable-helper td:first-child {
width: 339px;
}
</style>
</head>
<body>
<div class="rb-wrapper rb-fixed-sidebar rb-collapsible-sidebar rb-collapsible-sidebar-hide-logo rb-color-header" th:classappend="${sideCollapsedClazz}">

View file

@ -47,8 +47,8 @@
<tr>
<th>[[${bundle.L('名称')}]]</th>
<th>[[${bundle.L('源实体')}]]</th>
<th>[[${bundle.L('触发类型')}]]</th>
<th width="26%" class="no-sort">[[${bundle.L('触发动作')}]]</th>
<th width="20%">[[${bundle.L('触发类型')}]]</th>
<th width="20%" class="no-sort">[[${bundle.L('触发动作')}]]</th>
<th width="80" class="int-sort">[[${bundle.L('优先级')}]]</th>
<th width="80" class="no-sort">[[${bundle.L('启用')}]]</th>
<th width="120" class="no-sort">[[${bundle.L('修改时间')}]]</th>

View file

@ -449,44 +449,3 @@ See LICENSE and COMMERCIAL in the project root for license information.
.chart-list > div .chart-icon > i.SUNBURST {
background-position: -1372px -1464px;
}
.grid-stack-item.fullscreen {
position: absolute;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
z-index: 99;
}
.grid-stack-item.fullscreen .ui-resizable-handle {
display: none !important;
}
/* Colors */
.grid-stack-item.bgcolor .grid-stack-item-content,
.grid-stack-item.bgcolor .chart.index > .data-item strong {
color: #fff;
}
.grid-stack-item.bgcolor .rb-loading:after {
background-color: transparent;
}
.grid-stack-item.bgcolor .rb-spinner svg {
stroke: #fff;
}
body.fullscreen {
background: #0a1b3b url(../img/datav-bg.png) no-repeat 0 0;
background-size: 100% 100%;
}
body.fullscreen .grid-stack .grid-stack-item .grid-stack-item-content {
background-color: rgba(6, 30, 93, 0.5);
}
body.fullscreen .grid-stack .grid-stack-item.fullscreen .grid-stack-item-content {
background-color: #0a1b3b;
}

View file

@ -174,3 +174,66 @@ body.fullscreen .J_dash-fullscreen .zmdi-fullscreen::before {
.chart-grid.uneditable .chart-box .chart-head .chart-title {
cursor: default;
}
.dash-right .J_darkmode {
display: none;
}
body.fullscreen .dash-right .J_darkmode {
display: inline-block;
}
body.fullscreen.darkmode {
background: #0a1b3b url(../img/datav-bg.png) no-repeat 0 0;
background-size: 100% 100%;
}
body.fullscreen.darkmode .grid-stack .grid-stack-item .grid-stack-item-content {
background-color: rgba(6, 30, 93, 0.5);
}
body.fullscreen.darkmode .grid-stack .grid-stack-item.fullscreen .grid-stack-item-content {
background-color: #0a1b3b;
}
.grid-stack-item.fullscreen {
position: absolute;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
z-index: 99;
}
.grid-stack-item.fullscreen .ui-resizable-handle {
display: none !important;
}
body.fullscreen.darkmode .chart-title {
color: rgba(255, 255, 255, 0.68);
}
body.fullscreen.darkmode .chart.index > .data-item p,
body.fullscreen.darkmode .chart.index > .data-item strong,
body.fullscreen.darkmode .tools-bar h4 {
color: #fff;
}
body.fullscreen .tools-bar h4 {
font-weight: bold;
}
/* bgcolor */
.grid-stack-item.bgcolor .grid-stack-item-content,
.grid-stack-item.bgcolor .chart.index > .data-item strong {
color: #fff;
}
.grid-stack-item.bgcolor .rb-loading::after {
background-color: transparent;
}
.grid-stack-item.bgcolor .rb-spinner svg {
stroke: #fff;
}

View file

@ -3296,6 +3296,12 @@ form {
height: auto;
}
.preview-modal .container.fp-content.fullwidth {
max-width: 100%;
padding-left: 0;
padding-right: 0;
}
.file-share {
padding: 10px 40px;
}

View file

@ -140,6 +140,7 @@ $(document).ready(() => {
}
const option = wpc.chartConfig.option || {}
if (typeof option['mergeCell'] === undefined) option.mergeCell = true // fix: 3.1.3
for (let k in option) {
const opt = $(`.chart-option input[data-name=${k}]`)
if (opt.length > 0) {

View file

@ -132,6 +132,8 @@ $(document).ready(function () {
// eslint-disable-next-line no-undef
BaseChart.currentFullscreen && BaseChart.currentFullscreen.toggleFullscreen(true)
})
$('.J_darkmode button').on('click', () => $(document.body).toggleClass('darkmode'))
})
// 全屏工具
@ -234,7 +236,9 @@ const add_widget = function (item) {
const chart_add = $('#chart-add')
if (chart_add.length > 0) gridstack.removeWidget(chart_add.parent())
const gsi = `<div class="grid-stack-item ${item.bgcolor && 'bgcolor'}"><div id="${chid}" class="grid-stack-item-content" ${item.bgcolor ? `style="background-color:${item.bgcolor}` : ''}"></div></div>`
const gsi = `<div class="grid-stack-item ${item.bgcolor && 'bgcolor'}"><div id="${chid}" class="grid-stack-item-content" ${
item.bgcolor ? `style="background-color:${item.bgcolor}` : ''
}"></div></div>`
// Use gridstar
if (item.size_x || item.size_y) {
gridstack.addWidget(gsi, (item.col || 1) - 1, (item.row || 1) - 1, item.size_x || 2, item.size_y || 2, true, 2, 12, 2, 24)

View file

@ -120,7 +120,7 @@ class RbPreview extends React.Component {
renderDoc() {
return (
<div className="container fp-content">
<div className={`container fp-content ${this.props.fullwidth && 'fullwidth'}`}>
<div className="iframe">
{!this.state.docRendered && (
<div className="must-center">
@ -135,10 +135,10 @@ class RbPreview extends React.Component {
renderText() {
return (
<div className="container fp-content">
<div className={`container fp-content ${this.props.fullwidth && 'fullwidth'}`}>
<div className="iframe text">
{this.state.previewText || this.state.previewText === '' ? (
<pre>{this.state.previewText || <i className="text-muted">{$L('无')}</i>}</pre>
<pre className="mb-0">{this.state.previewText || <i className="text-muted">{$L('无')}</i>}</pre>
) : (
<div className="must-center">
<RbSpinner fully={true} />

View file

@ -70,7 +70,7 @@ class GridList extends React.Component {
setTimeout(() => location.reload(), 500)
} else {
this.disabled(false)
RbHighbar.error(res.error_msg)
RbHighbar.error(WrapHtml(res.error_msg))
}
})
},

View file

@ -99,8 +99,7 @@ function modeSave(newOption, next) {
})
}
// const CATE_TYPES = ['PICKLIST', 'MULTISELECT', 'CLASSIFICATION', 'DATE', 'DATETIME', 'REFERENCE', 'N2NREFERENCE']
const CATE_TYPES = ['PICKLIST', 'MULTISELECT', 'CLASSIFICATION', 'REFERENCE', 'N2NREFERENCE']
const CATE_TYPES = ['PICKLIST', 'MULTISELECT', 'CLASSIFICATION', 'DATE', 'DATETIME', 'REFERENCE', 'N2NREFERENCE']
// 模式选项
class DlgMode1Option extends RbFormHandler {
@ -146,7 +145,7 @@ class DlgMode1Option extends RbFormHandler {
</div>
<div className={`col-4 pl-0 ${this.state.advListShowCategoryFormats ? '' : 'hide'}`}>
<label className="mb-1">{$L('字段格式')}</label>
<select className="form-control form-control-sm" disabled>
<select className="form-control form-control-sm">
{this.state.advListShowCategoryFormats &&
this.state.advListShowCategoryFormats.map((item) => {
return (
@ -213,11 +212,11 @@ class DlgMode1Option extends RbFormHandler {
$catFields = $('.advListShowCategory-set select:eq(0)')
$catFormats = $('.advListShowCategory-set select:eq(1)')
$.get(`/commons/metadata/fields?entity=${wpc.entityName}&deep=2`, (res) => {
$.get(`/commons/metadata/fields?entity=${wpc.entityName}`, (res) => {
const _data = []
res.data &&
res.data.forEach((item) => {
if (CATE_TYPES.includes(item.type) && !(item.name.includes('owningDept.') || item.name.includes('owningUser.'))) {
if (CATE_TYPES.includes(item.type)) {
_data.push(item)
}
})
@ -240,10 +239,10 @@ class DlgMode1Option extends RbFormHandler {
let formats
if (found && found.type === 'CLASSIFICATION') {
formats = [
[0, $L('%d 级分类', 1)],
[1, $L('%d 级分类', 2)],
[2, $L('%d 级分类', 3)],
[3, $L('%d 级分类', 4)],
// [0, $L('%d 级分类', 1)],
// [1, $L('%d 级分类', 2)],
// [2, $L('%d 级分类', 3)],
// [3, $L('%d 级分类', 4)],
]
} else if (found && (found.type === 'DATE' || found.type === 'DATETIME')) {
formats = [

View file

@ -5,7 +5,7 @@ rebuild is dual-licensed under commercial and open source licenses (GPLv3).
See LICENSE and COMMERCIAL in the project root for license information.
*/
$(document).ready(function () {
$(document).ready(() => {
const entity = $urlp('entity')
const settingsUrl = `/admin/entity/${entity}/list-stats`
@ -87,6 +87,9 @@ const render_set = function (item) {
return
}
// 唯一
if (!item.key2) item.key2 = $random('stat-')
const $to = $('.set-items')
const calc = item.calc || 'SUM'
@ -108,10 +111,10 @@ const render_set = function (item) {
$(`<li class="dropdown-item" data-calc='_LABEL'>${$L('显示样式')}</li>`).appendTo($ul)
$ul.find('.dropdown-item').on('click', function () {
const calc = $(this).data('calc')
if (calc === '_LABEL') {
if (ShowStyles_Comps[item.name]) {
ShowStyles_Comps[item.name].show()
const c = $(this).data('calc')
if (c === '_LABEL') {
if (ShowStyles_Comps[item.key2]) {
ShowStyles_Comps[item.key2].show()
} else {
renderRbcomp(
// eslint-disable-next-line react/jsx-no-undef
@ -126,12 +129,12 @@ const render_set = function (item) {
/>,
null,
function () {
ShowStyles_Comps[item.name] = this
ShowStyles_Comps[item.key2] = this
}
)
}
} else {
$item.attr('data-calc', calc).find('.item > span').text(`${item.label} (${CALC_TYPES[calc]})`)
$item.attr('data-calc', c).find('.item > span').text(`${item.label} (${CALC_TYPES[c]})`)
}
})
}

View file

@ -70,7 +70,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
$.ajaxSetup({
headers: {
'Content-Type': 'text/plain;charset=utf-8',
'X-Client': 'RB/WEB-2.9',
'X-Client': 'RB/WEB',
'X-CsrfToken': rb.csrfToken || '',
'X-AuthToken': rb.authToken || '',
},

View file

@ -442,12 +442,12 @@ class RbForm extends React.Component {
if (this.isNew) {
this.props.children.map((child) => {
let val = child.props.value
if (val && child.props.readonly !== true) {
if (val && (child.props.readonly !== true || child.props.readonlyw === true)) {
if (typeof val === 'object') {
if ($.isArray(val)) {
// [file1, file2, image1]
// eg. [file1, file2, image1]
} else {
// {id:xxx, text:xxx}
// eg. {id:xxx, text:xxx}
val = val.id
}
}
@ -1048,9 +1048,10 @@ class RbFormNumber extends RbFormText {
// 移除千分为位
_removeComma(n) {
if (n === null || n === undefined) return ''
if (n) return (n + '').replace(/,/g, '')
else if (isNaN(n)) return ''
else return n // `0`
if (isNaN(n)) return ''
return n // `0`
}
}

View file

@ -178,11 +178,12 @@ $(function () {
})
})
if (rb.commercial === 11) {
$('a[target="_blank"]').each(function () {
if (($(this).attr('href') || '').indexOf('getrebuild.com') > -1) $(this).removeAttr('href')
})
}
// if (rb.commercial === 11) {
// $('a[target="_blank"]').each(function () {
// if (($(this).attr('href') || '').indexOf('getrebuild.com') > -1) $(this).removeAttr('href')
// })
// $('.commercial11').addClass('hide')
// }
})
var $addResizeHandler__calls = []

View file

@ -68,7 +68,14 @@ class TriggerList extends ConfigList {
<a href={`trigger/${item[0]}`}>{item[3] || item[2] + ' · ' + item[7]}</a>
</td>
<td>{item[2] || item[1]}</td>
<td>{item[7]}</td>
<td>
{item[7]}
{item[10] && (
<span title={$L('目标实体')} className="ml-1">
({item[10]})
</span>
)}
</td>
<td className="text-wrap">{item[6] > 0 ? $L(' %s ', formatWhen(item[6])) : <span className="text-warning">({$L('无触发动作')})</span>}</td>
<td>
<span className="badge badge-light">{item[9]}</span>

View file

@ -34,7 +34,7 @@
</script>
<script type="text/babel">
$(document).ready(() => {
renderRbcomp(<RbPreview urls={[window.__PageConfig.publicUrl]} unclose />)
renderRbcomp(<RbPreview urls={[window.__PageConfig.publicUrl]} unclose fullwidth />)
})
</script>
</body>

View file

@ -98,7 +98,7 @@
<span class="custom-control-label">[[${bundle.L('显示汇总')}]]</span>
</label>
<label class="custom-control custom-control-sm custom-checkbox">
<input class="custom-control-input" type="checkbox" data-name="mergeCell" checked />
<input class="custom-control-input" type="checkbox" data-name="mergeCell" />
<span class="custom-control-label">[[${bundle.L('自动合并单元格')}]]</span>
</label>
</div>

View file

@ -30,6 +30,11 @@
</div>
</div>
<div class="col-sm-6 text-right d-none d-md-block dash-right">
<div class="btn-group J_darkmode">
<button type="button" class="btn btn-link pr-0 text-right">
<i class="mdi mdi-weather-sunny icon up-1" style="font-size: 1.45rem"></i>
</button>
</div>
<div class="btn-group">
<button type="button" class="btn btn-link pr-0 text-right J_dash-fullscreen" th:title="${bundle.L('全屏')}">
<i class="zmdi zmdi-fullscreen icon" style="font-size: 1.45rem"></i>

View file

@ -43,7 +43,7 @@ public class SeriesGeneratorTest extends TestSupport {
void testIncrementVarNThreads() {
final IncreasingVar var = new IncreasingVar("0000", getSeriesField(), "Y");
final Set<String> set = Collections.synchronizedSet(new HashSet<>());
final int N = 200;
final int N = 1000;
for (int i = 0; i < N; i++) {
new Thread(() -> {
String s = var.generate();
@ -51,7 +51,7 @@ public class SeriesGeneratorTest extends TestSupport {
System.out.println(s + " << " + Thread.currentThread().getName());
}).start();
}
ThreadPool.waitFor(2000);
ThreadPool.waitFor(1200);
Assertions.assertEquals(set.size(), N);
}

View file

@ -54,7 +54,7 @@ public class DataListBuilderTest extends TestSupport {
@Test
public void testColumnLayout() {
JSON layout = DataListManager.instance.getFieldsLayout(Account, SIMPLE_USER);
JSON layout = DataListManager.instance.getListFields(Account, SIMPLE_USER);
System.out.println(layout);
}
}