From f94926ae2454243f47a45da28e059c7d07ca7fff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?REBUILD=20=E4=BC=81=E4=B8=9A=E7=AE=A1=E7=90=86=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F?= <42044143+getrebuild@users.noreply.github.com> Date: Sat, 2 Dec 2023 14:22:04 +0800 Subject: [PATCH] Fix v3.5 beta3 (#686) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update @rbv * be: upload * enh: 表单回填源字段支持ID * be: uc * be: word 上传判断 * fix: 文件移除 * fix: 1.Switch topnav; 2.N details recycle * fix: ID连接 * be:优化分组聚合回填执行效率 * feat: filter {@CURRENT} * enh: 记录转换源字段支持ID * v3.5-beta3 * _placeholderw * EncodeHintType.ERROR_CORRECTION: L * lang --------- Co-authored-by: devezhao --- @rbv | 2 +- pom.xml | 2 +- .../java/com/rebuild/core/Application.java | 4 +- .../general/AutoFillinManager.java | 5 ++ .../configuration/general/FormsBuilder.java | 4 +- .../rebuild/core/metadata/MetadataSorter.java | 20 ++++-- .../core/metadata/easymeta/EasyField.java | 2 +- .../general/recyclebin/RecycleBean.java | 24 ++++--- .../general/recyclebin/RecycleRestore.java | 14 +++- .../core/service/query/AdvFilterParser.java | 15 +++- .../trigger/impl/AggregationEvaluator.java | 2 +- .../trigger/impl/FieldAggregation.java | 21 +++--- .../trigger/impl/GroupAggregation.java | 1 + .../core/support/general/BarCodeSupport.java | 2 +- .../java/com/rebuild/utils/CommonsUtils.java | 38 ++++++++--- .../admin/data/ReportTemplateController.java | 9 ++- .../com/rebuild/web/commons/FileUploader.java | 2 +- .../rebuild/web/commons/MetadataGetting.java | 6 +- .../rebuild/web/general/MetaFormatter.java | 20 ++++++ .../general/ReferenceSearchController.java | 23 +++++-- .../transform/TransformConfigController.java | 2 +- src/main/resources/i18n/lang.zh_CN.json | 11 ++- .../web/admin/integration/apis-manager.html | 1 + src/main/resources/web/admin/system-cfg.html | 6 +- src/main/resources/web/assets/css/rb-page.css | 2 +- .../web/assets/js/admin/report-templates.js | 4 +- .../js/admin/{syscfg-g.js => system-cfg.js} | 7 +- .../resources/web/assets/js/file-preview.js | 2 +- .../web/assets/js/metadata/auto-fillin.js | 2 +- .../resources/web/assets/js/rb-advfilter.js | 2 +- .../web/assets/js/rb-datalist.common.js | 2 +- src/main/resources/web/assets/js/rb-forms.js | 68 ++++++++++--------- src/main/resources/web/assets/js/rb-page.js | 55 ++++++++++----- 33 files changed, 263 insertions(+), 117 deletions(-) rename src/main/resources/web/assets/js/admin/{syscfg-g.js => system-cfg.js} (98%) diff --git a/@rbv b/@rbv index 2e44d4228..2ab2c88a6 160000 --- a/@rbv +++ b/@rbv @@ -1 +1 @@ -Subproject commit 2e44d4228c89b993077d9a407f08f4a9c85af11b +Subproject commit 2ab2c88a6c5750feb36c13d3d35358a85fc78501 diff --git a/pom.xml b/pom.xml index 91f8768ea..28a94f966 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ com.rebuild rebuild - 3.5.0-beta2 + 3.5.0-beta3 rebuild Building your business-systems freely! diff --git a/src/main/java/com/rebuild/core/Application.java b/src/main/java/com/rebuild/core/Application.java index 8ee011e73..7ad79e678 100644 --- a/src/main/java/com/rebuild/core/Application.java +++ b/src/main/java/com/rebuild/core/Application.java @@ -74,11 +74,11 @@ public class Application implements ApplicationListener /** * Rebuild Version */ - public static final String VER = "3.5.0-beta2"; + public static final String VER = "3.5.0-beta3"; /** * Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2} */ - public static final int BUILD = 3050002; + public static final int BUILD = 3050003; static { // Driver for DB diff --git a/src/main/java/com/rebuild/core/configuration/general/AutoFillinManager.java b/src/main/java/com/rebuild/core/configuration/general/AutoFillinManager.java index 643f4a49c..496f91883 100644 --- a/src/main/java/com/rebuild/core/configuration/general/AutoFillinManager.java +++ b/src/main/java/com/rebuild/core/configuration/general/AutoFillinManager.java @@ -25,9 +25,12 @@ import com.rebuild.core.metadata.easymeta.DisplayType; import com.rebuild.core.metadata.easymeta.EasyField; import com.rebuild.core.metadata.easymeta.EasyFile; +import com.rebuild.core.metadata.easymeta.EasyID; import com.rebuild.core.metadata.easymeta.EasyMetaFactory; +import com.rebuild.core.metadata.easymeta.EasyN2NReference; import com.rebuild.core.metadata.easymeta.MixValue; import com.rebuild.core.metadata.impl.EasyFieldConfigProps; +import com.rebuild.core.support.general.FieldValueHelper; import com.rebuild.utils.JSONUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.map.CaseInsensitiveMap; @@ -294,6 +297,8 @@ protected Object conversionCompatibleValue(Field source, Field target, Object va if (!(newValue instanceof String) || sourceEasy instanceof EasyFile) { newValue = sourceEasy.wrapValue(newValue); } + } else if (sourceEasy instanceof EasyID && targetEasy instanceof EasyN2NReference) { + newValue = FieldValueHelper.wrapFieldValue(newValue, targetEasy); } return newValue; diff --git a/src/main/java/com/rebuild/core/configuration/general/FormsBuilder.java b/src/main/java/com/rebuild/core/configuration/general/FormsBuilder.java index 584bd7d2b..10d5ad189 100644 --- a/src/main/java/com/rebuild/core/configuration/general/FormsBuilder.java +++ b/src/main/java/com/rebuild/core/configuration/general/FormsBuilder.java @@ -474,8 +474,8 @@ protected void buildModelElements(JSONArray elements, Entity entity, Record reco // 默认值 if (el.get("value") == null) { - if (dt == DisplayType.SERIES - /*|| EntityHelper.ApprovalLastTime.equals(fieldName) || EntityHelper.ApprovalLastRemark.equals(fieldName)*/) { + if (dt == DisplayType.SERIES || EntityHelper.ApprovalLastTime.equals(fieldName) + || EntityHelper.ApprovalLastRemark.equals(fieldName) || EntityHelper.ApprovalLastUser.equals(fieldName)) { el.put("readonlyw", READONLYW_RO); } else { Object defaultValue = easyField.exprDefaultValue(); diff --git a/src/main/java/com/rebuild/core/metadata/MetadataSorter.java b/src/main/java/com/rebuild/core/metadata/MetadataSorter.java index ad1d519ac..6f537b8a1 100644 --- a/src/main/java/com/rebuild/core/metadata/MetadataSorter.java +++ b/src/main/java/com/rebuild/core/metadata/MetadataSorter.java @@ -21,8 +21,10 @@ import java.text.Collator; import java.util.ArrayList; import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; /** * 元数据辅助类,注意此类返回的数据会过滤和排序 @@ -159,14 +161,14 @@ public static Field[] sortFields(Field[] fields, DisplayType... usesTypes) { */ static Field[] sortByLevel(List fields) { List othersFields = new ArrayList<>(); - List commonsFields = new ArrayList<>(); List approvalFields = new ArrayList<>(); + Map commonsFieldsMap = new HashMap<>(); for (BaseMeta field : fields) { if (MetadataHelper.isApprovalField(field.getName())) { approvalFields.add(field); } else if (MetadataHelper.isCommonsField((Field) field)) { - commonsFields.add(field); + commonsFieldsMap.put(field.getName(), field); } else { othersFields.add(field); } @@ -175,12 +177,20 @@ static Field[] sortByLevel(List fields) { sortByLabel(othersFields); List allFields = new ArrayList<>(othersFields); - sortByLabel(commonsFields); - allFields.addAll(commonsFields); - sortByLabel(approvalFields); allFields.addAll(approvalFields); + // v35 特殊排序 + final String[] SPEC_SORTS = new String[] { + EntityHelper.CreatedBy, EntityHelper.CreatedOn, EntityHelper.ModifiedBy, EntityHelper.ModifiedOn, EntityHelper.OwningUser, EntityHelper.OwningDept + }; + List commonsFields = new ArrayList<>(); + for (String s : SPEC_SORTS) { + BaseMeta b = commonsFieldsMap.get(s); + if (b != null) commonsFields.add(b); + } + allFields.addAll(commonsFields); + return allFields.toArray(new Field[0]); } diff --git a/src/main/java/com/rebuild/core/metadata/easymeta/EasyField.java b/src/main/java/com/rebuild/core/metadata/easymeta/EasyField.java index 7e875e979..e63463e3a 100644 --- a/src/main/java/com/rebuild/core/metadata/easymeta/EasyField.java +++ b/src/main/java/com/rebuild/core/metadata/easymeta/EasyField.java @@ -140,7 +140,7 @@ public Object exprDefaultValue() { /** * 转换返回值,输出用(默认实现为原值返回) * - * @param value 原值 + * @param value 原值,必须符合值类型 * @return * @see com.rebuild.core.support.general.FieldValueHelper */ diff --git a/src/main/java/com/rebuild/core/service/general/recyclebin/RecycleBean.java b/src/main/java/com/rebuild/core/service/general/recyclebin/RecycleBean.java index c4db971ee..44c697da7 100644 --- a/src/main/java/com/rebuild/core/service/general/recyclebin/RecycleBean.java +++ b/src/main/java/com/rebuild/core/service/general/recyclebin/RecycleBean.java @@ -63,19 +63,21 @@ public JSON serialize() { JSONObject s = (JSONObject) queryed.serialize(); Entity detailEntity = entity.getDetailEntity(); - if (detailEntity == null) { - return s; - } + if (detailEntity == null) return s; - // 明细 - String detailSql = buildBaseSql(detailEntity) - .append(MetadataHelper.getDetailToMainField(detailEntity).getName()) - .append(" = ?") - .toString(); - List detailQueryed = Application.createQueryNoFilter(detailSql).setParameter(1, this.recordId).list(); + // v36 多明细 JSONArray detailList = new JSONArray(); - for (Record r : detailQueryed) { - detailList.add(r.serialize()); + for (Entity de : entity.getDetialEntities()) { + String detailSql = buildBaseSql(de) + .append(MetadataHelper.getDetailToMainField(de).getName()) + .append(" = ?") + .toString(); + List detailQueryed = Application.createQueryNoFilter(detailSql).setParameter(1, this.recordId).list(); + for (Record r : detailQueryed) { + JSONObject item = (JSONObject) r.serialize(); + item.put(RestoreRecordCreator.META_FIELD, de.getName()); + detailList.add(item); + } } s.put(NAME_DETAILLIST, detailList); diff --git a/src/main/java/com/rebuild/core/service/general/recyclebin/RecycleRestore.java b/src/main/java/com/rebuild/core/service/general/recyclebin/RecycleRestore.java index ce319ceef..dcb260edf 100644 --- a/src/main/java/com/rebuild/core/service/general/recyclebin/RecycleRestore.java +++ b/src/main/java/com/rebuild/core/service/general/recyclebin/RecycleRestore.java @@ -187,10 +187,22 @@ private List conver2Record(JSONObject content, ID recordId) { Record record = new RestoreRecordCreator(entity, content).create(true); records.add(record); + // v36 多明细 Entity detailEntity = entity.getDetailEntity(); if (detailList != null && detailEntity != null) { for (Object o : detailList) { - Record detail = new RestoreRecordCreator(detailEntity, (JSONObject) o).create(true); + JSONObject item = (JSONObject) o; + Entity de = detailEntity; + if (item.containsKey(RestoreRecordCreator.META_FIELD)) { + String _entity = (String) item.remove(RestoreRecordCreator.META_FIELD); + if (!MetadataHelper.containsEntity(_entity)) { + log.warn("Detail entity not longer exists : {}", _entity); + continue; + } + de = MetadataHelper.getEntity(_entity); + } + + Record detail = new RestoreRecordCreator(de, item).create(true); records.add(detail); } } diff --git a/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java b/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java index edeb6b274..af6dfba89 100644 --- a/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java +++ b/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java @@ -20,6 +20,7 @@ import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.rebuild.core.Application; +import com.rebuild.core.UserContextHolder; import com.rebuild.core.metadata.EntityHelper; import com.rebuild.core.metadata.MetadataHelper; import com.rebuild.core.metadata.easymeta.DisplayType; @@ -46,6 +47,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import static cn.devezhao.commons.CalendarUtils.addDay; @@ -251,8 +253,7 @@ private String parseItem(JSONObject item, JSONObject values, Entity specRootEnti DisplayType dt = EasyMetaFactory.getDisplayType(lastFieldMeta); - if (dt == DisplayType.CLASSIFICATION - || (dt == DisplayType.PICKLIST && hasNameFlag) /* 快速查询 */) { + if (dt == DisplayType.CLASSIFICATION || (dt == DisplayType.PICKLIST && hasNameFlag) /* 快速查询 */) { field = NAME_FIELD_PREFIX + field; } else if (hasNameFlag) { if (!(dt == DisplayType.REFERENCE || dt == DisplayType.N2NREFERENCE)) { @@ -717,6 +718,7 @@ private JSONArray buildQuickFilterItems(String quickFields) { private static final String CURRENT_ANY = "CURRENT"; private static final String CURRENT_DATE = "NOW"; + // 获取字段变量值 private String useValueOfVarRecord(String value, Field queryField) { if (StringUtils.isBlank(value) || !value.matches(PATT_FIELDVAR)) return value; @@ -730,6 +732,15 @@ private String useValueOfVarRecord(String value, Field queryField) { DisplayType dt = EasyMetaFactory.getDisplayType(queryField); if (dt == DisplayType.DATE || dt == DisplayType.DATETIME || dt == DisplayType.TIME) { useValue = CalendarUtils.now(); + + } else if (dt == DisplayType.REFERENCE) { + if (queryField.getReferenceEntity().getEntityCode() == EntityHelper.User) { + useValue = UserContextHolder.getUser(); + } else if (queryField.getReferenceEntity().getEntityCode() == EntityHelper.Department) { + Department dept = UserHelper.getDepartment(UserContextHolder.getUser()); + if (dept != null) useValue = dept.getIdentity(); + } + } else { log.warn("Cannot use `CURRENT` in `{}` (None date fields)", queryField); return StringUtils.EMPTY; diff --git a/src/main/java/com/rebuild/core/service/trigger/impl/AggregationEvaluator.java b/src/main/java/com/rebuild/core/service/trigger/impl/AggregationEvaluator.java index dabad1d32..22d164b4e 100644 --- a/src/main/java/com/rebuild/core/service/trigger/impl/AggregationEvaluator.java +++ b/src/main/java/com/rebuild/core/service/trigger/impl/AggregationEvaluator.java @@ -232,7 +232,7 @@ private Object evalRbJoin(int mode) { CollectionUtils.addAll(nvList, (ID[]) n); } else if (n instanceof ID) { if (field.getType() == FieldType.PRIMARY) { - nvList.add(n.toString()); // 保持主键 + nvList.add(n.toString()); // 保持主键为文本 } else { nvList.add(n); } diff --git a/src/main/java/com/rebuild/core/service/trigger/impl/FieldAggregation.java b/src/main/java/com/rebuild/core/service/trigger/impl/FieldAggregation.java index 7f03fbc7b..89ab43a9f 100644 --- a/src/main/java/com/rebuild/core/service/trigger/impl/FieldAggregation.java +++ b/src/main/java/com/rebuild/core/service/trigger/impl/FieldAggregation.java @@ -85,7 +85,7 @@ public class FieldAggregation extends TriggerAction { protected String followSourceWhere; public FieldAggregation(ActionContext context) { - this(context, Boolean.FALSE); + this(context, Boolean.TRUE); } protected FieldAggregation(ActionContext context, boolean ignoreSame) { @@ -215,7 +215,10 @@ public Object execute(OperatingContext operatingContext) throws TriggerException } else if (dt == DisplayType.N2NREFERENCE) { // 强制去重 Set idSet = new LinkedHashSet<>(); - for (Object id : oArray) idSet.add((ID) id); + for (Object id : oArray) { + if (id instanceof ID) idSet.add((ID) id); + else idSet.add(ID.valueOf((String) id)); // 主键会保持文本 + } targetRecord.setIDArray(targetField, idSet.toArray(new ID[0])); } else { @@ -274,19 +277,19 @@ public Object execute(OperatingContext operatingContext) throws TriggerException // 回填 (v3.1) // 仅分组聚合有此配置 - String fillbackField = ((JSONObject) actionContext.getActionContent()).getString("fillbackField"); if (fillbackField != null && MetadataHelper.checkAndWarnField(sourceEntity, fillbackField)) { - String sql = String.format("select %s from %s where %s", - sourceEntity.getPrimaryField().getName(), sourceEntity.getName(), filterSql); + String sql = String.format("select %s,%s from %s where %s", + sourceEntity.getPrimaryField().getName(), fillbackField, sourceEntity.getName(), filterSql); Object[][] fillbacks = Application.createQueryNoFilter(sql).array(); - for (Object[] to : fillbacks) { - Record fbRecord = EntityHelper.forUpdate((ID) to[0], UserService.SYSTEM_USER, false); - fbRecord.setID(fillbackField, targetRecordId); + for (Object[] o : fillbacks) { + if (CommonsUtils.isSame(o[1], targetRecordId)) continue; // FIXME 回填仅更新,无业务规则 - Application.getCommonsService().update(fbRecord, false); + Record r = EntityHelper.forUpdate((ID) o[0], UserService.SYSTEM_USER, false); + r.setID(fillbackField, targetRecordId); + Application.getCommonsService().update(r, false); } } diff --git a/src/main/java/com/rebuild/core/service/trigger/impl/GroupAggregation.java b/src/main/java/com/rebuild/core/service/trigger/impl/GroupAggregation.java index 43710600c..0a910070e 100644 --- a/src/main/java/com/rebuild/core/service/trigger/impl/GroupAggregation.java +++ b/src/main/java/com/rebuild/core/service/trigger/impl/GroupAggregation.java @@ -55,6 +55,7 @@ public GroupAggregation(ActionContext context) { this(context, Boolean.FALSE); } + // ignoreSame 本触发器有回填,忽略同值会导致无法回填 public GroupAggregation(ActionContext context, boolean ignoreSame) { super(context, ignoreSame); } diff --git a/src/main/java/com/rebuild/core/support/general/BarCodeSupport.java b/src/main/java/com/rebuild/core/support/general/BarCodeSupport.java index 90654b5e2..d824af460 100644 --- a/src/main/java/com/rebuild/core/support/general/BarCodeSupport.java +++ b/src/main/java/com/rebuild/core/support/general/BarCodeSupport.java @@ -146,7 +146,7 @@ public static BufferedImage createBarCode(String content, int width, int height, protected static BitMatrix createBarCodeImage(String content, BarcodeFormat format, int width, int height) { Map hints = new HashMap<>(); hints.put(EncodeHintType.CHARACTER_SET, AppUtils.UTF8); - hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L); // 高级别导致空白边框??? hints.put(EncodeHintType.MARGIN, 0); try { diff --git a/src/main/java/com/rebuild/utils/CommonsUtils.java b/src/main/java/com/rebuild/utils/CommonsUtils.java index 643b8523a..10f23cbdd 100644 --- a/src/main/java/com/rebuild/utils/CommonsUtils.java +++ b/src/main/java/com/rebuild/utils/CommonsUtils.java @@ -13,6 +13,7 @@ import com.rebuild.core.Application; import com.rebuild.core.RebuildException; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; @@ -26,6 +27,7 @@ import java.math.RoundingMode; import java.net.URI; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.Collection; import java.util.Objects; import java.util.UUID; @@ -258,17 +260,37 @@ public static boolean hasLength(Object any) { * @param b * @return */ + @SuppressWarnings("unchecked") public static boolean isSame(Object a, Object b) { - boolean e = Objects.equals(a, b); - if (!e) { - if (a instanceof Number && b instanceof Number) { - // FIXME 有精度问题 - e = ObjectUtils.toDouble(a) == ObjectUtils.toDouble(b); - } + if (a == null && b != null) return false; + if (a != null && b == null) return false; + if (Objects.equals(a, b)) return true; + + // 数字 + if (a instanceof Number && b instanceof Number) { + // FIXME 有精度问题 + return ObjectUtils.toDouble(a) == ObjectUtils.toDouble(b); } - return e; - } + // 集合/数组 + if ((a instanceof Collection || a instanceof Object[]) && (b instanceof Collection || b instanceof Object[])) { + Collection aColl; + if (a instanceof Object[]) aColl = Arrays.asList((Object[]) a); + else aColl = (Collection) a; + Collection bColl; + if (b instanceof Object[]) bColl = Arrays.asList((Object[]) b); + else bColl = (Collection) b; + + if (aColl.size() != bColl.size()) return false; + if (aColl.isEmpty()) return true; + return CollectionUtils.containsAll(aColl, bColl) && CollectionUtils.containsAll(bColl, aColl); + } + + // 其他 + // FIXME 完善不同值类型的比较 + return StringUtils.equals(a.toString(), b.toString()); + } + /** * 指定范围的随机数 * diff --git a/src/main/java/com/rebuild/web/admin/data/ReportTemplateController.java b/src/main/java/com/rebuild/web/admin/data/ReportTemplateController.java index ffcef55f3..34e4776dd 100644 --- a/src/main/java/com/rebuild/web/admin/data/ReportTemplateController.java +++ b/src/main/java/com/rebuild/web/admin/data/ReportTemplateController.java @@ -106,6 +106,13 @@ public RespBody checkTemplate(@EntityParam Entity entity, HttpServletRequest req final String file = getParameterNotNull(request, "file"); final int type = getIntParameter(request, "type", DataReportManager.TYPE_RECORD); + boolean isDocx = file.toLowerCase().endsWith(".docx"); + if (type == DataReportManager.TYPE_WORD) { + if (!isDocx) return RespBody.errorl("上传 WORD 文件请选择 WORD 模板类型"); + } else { + if (isDocx) return RespBody.errorl("上传 EXCEL 文件请选择 EXCEL 模板类型"); + } + File template = RebuildConfiguration.getFileOfData(file); Map vars = null; try { @@ -121,7 +128,7 @@ public RespBody checkTemplate(@EntityParam Entity entity, HttpServletRequest req } catch (Exception ex) { Log.error(null, ex); - return RespBody.error(Language.L("无效模板文件 (无法读取模板文件)")); + return RespBody.error(Language.L("无效模板文件 (无法读取文件内容)")); } if (vars == null || vars.isEmpty()) { diff --git a/src/main/java/com/rebuild/web/commons/FileUploader.java b/src/main/java/com/rebuild/web/commons/FileUploader.java index 8ce1d99d0..68732bab7 100644 --- a/src/main/java/com/rebuild/web/commons/FileUploader.java +++ b/src/main/java/com/rebuild/web/commons/FileUploader.java @@ -56,7 +56,7 @@ public void upload(HttpServletRequest request, HttpServletResponse response) { break; } - if (file == null || file.isEmpty()) { + if (file == null) { writeFailure(response, "No file found"); return; } diff --git a/src/main/java/com/rebuild/web/commons/MetadataGetting.java b/src/main/java/com/rebuild/web/commons/MetadataGetting.java index 1dc624e44..2d946e150 100644 --- a/src/main/java/com/rebuild/web/commons/MetadataGetting.java +++ b/src/main/java/com/rebuild/web/commons/MetadataGetting.java @@ -62,10 +62,12 @@ public List entities(HttpServletRequest request) { @GetMapping("fields") public JSON fields(HttpServletRequest request) { Entity entity = MetadataHelper.getEntity(getParameterNotNull(request, "entity")); - // 返回引用实体的字段 + // 返回引用实体的字段层级 int appendRefFields = getIntParameter(request, "deep", 0); + // 返回ID主键字段 + boolean forceWithId = getBoolParameter(request, "withid"); - return MetaFormatter.buildFieldsWithRefs(entity, appendRefFields, true, field -> { + return MetaFormatter.buildFieldsWithRefs(entity, appendRefFields, true, forceWithId, field -> { if (!field.isQueryable()) return true; if (field instanceof Field) { diff --git a/src/main/java/com/rebuild/web/general/MetaFormatter.java b/src/main/java/com/rebuild/web/general/MetaFormatter.java index 9e4bb02a5..efa183e7a 100644 --- a/src/main/java/com/rebuild/web/general/MetaFormatter.java +++ b/src/main/java/com/rebuild/web/general/MetaFormatter.java @@ -79,6 +79,7 @@ public static JSONObject buildRichField(EasyField field) { * @param deep * @param filter * @return + * @see #buildFieldsWithRefs(Entity, int, boolean, boolean, Predicate) */ public static JSONArray buildFieldsWithRefs(Entity entity, int deep, Predicate filter) { return buildFieldsWithRefs(entity, deep, false, filter); @@ -90,8 +91,23 @@ public static JSONArray buildFieldsWithRefs(Entity entity, int deep, Predicate filter) { + return buildFieldsWithRefs(entity, deep, riching, false, filter); + } + + /** + * 获取字段列表 + * + * @param entity + * @param deep 几级 + * @param riching + * @param forceWithId 带有主键ID + * @param filter + * @return + */ + public static JSONArray buildFieldsWithRefs(Entity entity, int deep, boolean riching, boolean forceWithId, Predicate filter) { JSONArray res = new JSONArray(); // 一级 @@ -101,6 +117,10 @@ public static JSONArray buildFieldsWithRefs(Entity entity, int deep, boolean ric res.add(buildField(easyField, null, riching)); } + if (forceWithId) { + res.add(buildField(EasyMetaFactory.valueOf(entity.getPrimaryField()), null, false)); + } + if (deep < 2) return res; List deep3Refs = new ArrayList<>(); diff --git a/src/main/java/com/rebuild/web/general/ReferenceSearchController.java b/src/main/java/com/rebuild/web/general/ReferenceSearchController.java index 265880a7b..fefe376a3 100644 --- a/src/main/java/com/rebuild/web/general/ReferenceSearchController.java +++ b/src/main/java/com/rebuild/web/general/ReferenceSearchController.java @@ -60,6 +60,8 @@ @RequestMapping({"/commons/search/","/app/entity/"}) public class ReferenceSearchController extends EntityController { + private static final String _SELF = "{@CURRENT}"; + // 引用字段-快速搜索 @GetMapping({"reference", "quick"}) public JSON referenceSearch(@EntityParam Entity entity, HttpServletRequest request) { @@ -153,6 +155,14 @@ private JSON buildResultSearch(Entity searchEntity, String quickFields, String q } List result = resultSearch(searchWhere, searchEntity, maxResults); + // v35 本人/本部门 + if ("self".equals(q)) { + if (sec == EntityHelper.User || sec == EntityHelper.Department) { + result.add(JSONUtils.toJSONObject( + new String[]{ "id", "text" }, new Object[] { _SELF, Language.L("本人/本部门") })); + } + } + return (JSON) JSON.toJSON(result); } @@ -240,17 +250,20 @@ public RespBody referenceLabel(HttpServletRequest request) { Map labels = new HashMap<>(); for (String id : ids.split("[|,]")) { - if (!ID.isId(id)) continue; + if (!ID.isId(id)) { + if (_SELF.equals(id)) { + labels.put(_SELF, Language.L("本人/本部门")); + } + continue; + } - ID recordId = ID.valueOf(id); + final ID recordId = ID.valueOf(id); if (checkPrivileges && !Application.getPrivilegesManager().allowRead(user, recordId)) continue; if (ignoreMiss) { try { labels.put(id, FieldValueHelper.getLabel(recordId)); - } catch (NoRecordFoundException ignored) { - } - + } catch (NoRecordFoundException ignored) {} } else { labels.put(id, FieldValueHelper.getLabelNotry(recordId)); } diff --git a/src/main/java/com/rebuild/web/robot/transform/TransformConfigController.java b/src/main/java/com/rebuild/web/robot/transform/TransformConfigController.java index 50af32a34..3b0f47b20 100644 --- a/src/main/java/com/rebuild/web/robot/transform/TransformConfigController.java +++ b/src/main/java/com/rebuild/web/robot/transform/TransformConfigController.java @@ -157,7 +157,7 @@ private JSONObject buildEntity(Entity entity, boolean sourceType) { JSONArray fields; // 源 if (sourceType) { - fields = MetaFormatter.buildFieldsWithRefs(entity, 3, true, field -> { + fields = MetaFormatter.buildFieldsWithRefs(entity, 3, true, true, field -> { if (field instanceof EasyField) { EasyField easyField = (EasyField) field; int c = easyField.getDisplayType() == DisplayType.REFERENCE diff --git a/src/main/resources/i18n/lang.zh_CN.json b/src/main/resources/i18n/lang.zh_CN.json index 05c03579b..2de5081e1 100644 --- a/src/main/resources/i18n/lang.zh_CN.json +++ b/src/main/resources/i18n/lang.zh_CN.json @@ -2832,5 +2832,14 @@ "仅更新不要新建":"仅更新不要新建", "输入内容":"输入内容", "定位中":"定位中", - "查看 PDF":"查看 PDF" + "查看 PDF":"查看 PDF", + "数据库字段违反约束":"数据库字段违反约束", + "上传 EXCEL 文件请选择 EXCEL 模板类型":"上传 EXCEL 文件请选择 EXCEL 模板类型", + "本人/本部门":"本人/本部门", + "查看原图":"查看原图", + "去重连接*N":"去重连接*N", + "无效模板文件 (无法读取文件内容)":"无效模板文件 (无法读取文件内容)", + "请正确填写目标实体/记录匹配规则":"请正确填写目标实体/记录匹配规则", + "上传 WORD 文件请选择 WORD 模板类型":"上传 WORD 文件请选择 WORD 模板类型", + "APP 安装包":"APP 安装包" } \ No newline at end of file diff --git a/src/main/resources/web/admin/integration/apis-manager.html b/src/main/resources/web/admin/integration/apis-manager.html index 85ed17781..4e40c4d36 100644 --- a/src/main/resources/web/admin/integration/apis-manager.html +++ b/src/main/resources/web/admin/integration/apis-manager.html @@ -41,6 +41,7 @@ text-shadow: 0 1px #fff; border-radius: 4px; padding: 15px; + font-family: ui-monospace, 'Cascadia Mono', 'Segoe UI Mono', 'Liberation Mono', Menlo, Monaco, Consolas, monospace !important; } .search-logs > input { appearance: none !important; diff --git a/src/main/resources/web/admin/system-cfg.html b/src/main/resources/web/admin/system-cfg.html index ef9d0660e..99712c5a4 100644 --- a/src/main/resources/web/admin/system-cfg.html +++ b/src/main/resources/web/admin/system-cfg.html @@ -338,11 +338,11 @@
[[${bundle.L('其他')}]]
REBUILD [[${bundle.L('云账号')}]]
-
+

[[${bundle.L('绑定 REBUILD 云账号,获取更多专享服务与资源,还有免费系统安全与健康检测。')}]]

-
+

[[${bundle.L('当前已绑定云账号')}]] @@ -362,7 +362,7 @@

[[${bundle.L('其他')}]]
} - + diff --git a/src/main/resources/web/assets/css/rb-page.css b/src/main/resources/web/assets/css/rb-page.css index 2cc14a3cd..89faf1cf5 100644 --- a/src/main/resources/web/assets/css/rb-page.css +++ b/src/main/resources/web/assets/css/rb-page.css @@ -1163,7 +1163,7 @@ a.link.link-icon::after { position: absolute; right: 0; top: 0; - font-size: 1.1rem; + font-size: 1.15rem; color: #fff; cursor: default; background-color: rgba(0, 0, 0, 0.3); diff --git a/src/main/resources/web/assets/js/admin/report-templates.js b/src/main/resources/web/assets/js/admin/report-templates.js index 7e057e5e4..9b417309a 100644 --- a/src/main/resources/web/assets/js/admin/report-templates.js +++ b/src/main/resources/web/assets/js/admin/report-templates.js @@ -133,7 +133,7 @@ class ReportEditor extends ConfigFormDlg { {$L('EXCEL 列表')}
diff --git a/src/main/resources/web/assets/js/metadata/auto-fillin.js b/src/main/resources/web/assets/js/metadata/auto-fillin.js index 0d8c269ac..896ad7db0 100644 --- a/src/main/resources/web/assets/js/metadata/auto-fillin.js +++ b/src/main/resources/web/assets/js/metadata/auto-fillin.js @@ -177,7 +177,7 @@ class DlgRuleEdit extends RbFormHandler { this.__select2.push($s2target) // #2 - $.get(`/commons/metadata/fields?entity=${this.props.sourceEntity}&deep=2`, (res) => { + $.get(`/commons/metadata/fields?entity=${this.props.sourceEntity}&deep=2&withid=true`, (res) => { this.__sourceFieldsCache = res.data this.setState({ sourceFields: res.data }, () => { const $s2source = $(this._sourceField) diff --git a/src/main/resources/web/assets/js/rb-advfilter.js b/src/main/resources/web/assets/js/rb-advfilter.js index 4beaff807..e932bb69a 100644 --- a/src/main/resources/web/assets/js/rb-advfilter.js +++ b/src/main/resources/web/assets/js/rb-advfilter.js @@ -469,7 +469,7 @@ class FilterItem extends React.Component { ] } else if (fieldType === 'TIME') { op = ['GT', 'LT', 'EQ', 'BW'] - } else if (fieldType === 'FILE' || fieldType === 'IMAGE' || fieldType === 'AVATAR' || fieldType === 'SIGN') { + } else if (fieldType === 'FILE' || fieldType === 'IMAGE' || fieldType === 'AVATAR' || fieldType === 'SIGN' || fieldType === 'ANYREFERENCE') { op = [] } else if (fieldType === 'PICKLIST' || fieldType === 'STATE' || fieldType === 'MULTISELECT') { op = ['IN', 'NIN'] diff --git a/src/main/resources/web/assets/js/rb-datalist.common.js b/src/main/resources/web/assets/js/rb-datalist.common.js index f491f553e..3a5c8070e 100644 --- a/src/main/resources/web/assets/js/rb-datalist.common.js +++ b/src/main/resources/web/assets/js/rb-datalist.common.js @@ -1408,7 +1408,7 @@ class RbListPagination extends React.Component { `/p/admin/metadata/list-stats?entity=${this._entity}`, {$L('配置统计列')} - + ) }> diff --git a/src/main/resources/web/assets/js/rb-forms.js b/src/main/resources/web/assets/js/rb-forms.js index 1794a6cce..6c5f033f9 100644 --- a/src/main/resources/web/assets/js/rb-forms.js +++ b/src/main/resources/web/assets/js/rb-forms.js @@ -261,7 +261,7 @@ class RbForm extends React.Component { for (let k in iv) { let val = iv[k] // array, object, simple - val = typeof val === 'object' ? val.id || val : val + val = val && typeof val === 'object' ? val.id || val : val this.__FormData[k] = { value: val, error: null } } } @@ -783,6 +783,16 @@ class RbFormElement extends React.Component { constructor(props) { super(props) this.state = { ...props } + + // v35 + this._isNew = props.$$$parent.isNew + if (props.readonly) { + if (this._isNew) { + this._placeholderw = props.readonlyw >= 2 ? $L('自动值') : null + } else if (!this.state.value) { + this._placeholderw = $L('无') + } + } } render() { @@ -855,7 +865,7 @@ class RbFormElement extends React.Component { onChange={(e) => this.handleChange(e, this.props.readonly ? false : true)} // onBlur={this.props.readonly ? null : () => this.checkValue()} readOnly={this.props.readonly} - placeholder={this.props.readonlyw > 0 ? $L('自动值') : null} + placeholder={this._placeholderw} maxLength={this.props.maxLength || 200} /> ) @@ -903,7 +913,7 @@ class RbFormElement extends React.Component { this.setState({ hasError: err || null }) const errMsg = err ? this.props.label + ' ' + err : null - if (this.isValueUnchanged() && !this.props.$$$parent.isNew) { + if (this.isValueUnchanged() && !this._isNew) { if (err) this.props.$$$parent.setFieldValue(this.props.field, this.state.value, errMsg) else this.props.$$$parent.setFieldUnchanged(this.props.field, this.state.value) } else { @@ -1135,7 +1145,7 @@ class RbFormNumber extends RbFormText { onChange={(e) => this.handleChange(e, !this.props.readonly)} // onBlur={this.props.readonly ? null : () => this.checkValue()} readOnly={this.props.readonly} - placeholder={this.props.readonlyw > 0 ? $L('自动值') : null} + placeholder={this._placeholderw} maxLength="29" /> {this.__valueFlag && {this.__valueFlag}} @@ -1287,7 +1297,7 @@ class RbFormTextarea extends RbFormElement { onChange={(e) => this.handleChange(e, !this.props.readonly)} // onBlur={this.props.readonly ? null : () => this.checkValue()} readOnly={this.props.readonly} - placeholder={this.props.readonlyw > 0 ? $L('自动值') : null} + placeholder={this._placeholderw} maxLength="6000" /> {this.props.useMdedit && !this.props.readonly && (this._fieldValue__upload = c)} />} @@ -1435,7 +1445,7 @@ class RbFormDateTime extends RbFormElement { value={this.state.value || ''} onChange={(e) => this.handleChange(e, !this.props.readonly)} // onBlur={this.props.readonly ? null : () => this.checkValue()} - placeholder={this.props.readonlyw > 0 ? $L('自动值') : null} + placeholder={this._placeholderw} maxLength="20" /> this.handleClear()} /> @@ -1566,12 +1576,7 @@ class RbFormImage extends RbFormElement { this._filePreview(value, idx)}> IMG {!this.props.readonly && ( - { - $stopEvent(e) - this.removeItem(item) - }}> + this.removeItem(item, e)}> )} @@ -1660,7 +1665,8 @@ class RbFormImage extends RbFormElement { } } - removeItem(item) { + removeItem(item, e) { + e && $stopEvent(e, true) const paths = this.state.value || [] paths.remove(item) this.handleChange({ target: { value: paths } }, true) @@ -1697,7 +1703,7 @@ class RbFormFile extends RbFormImage { {fileName} {!this.props.readonly && ( - this.removeItem(item)}> + this.removeItem(item, e)}> )} @@ -1802,12 +1808,12 @@ class RbFormPickList extends RbFormElement { } isValueUnchanged() { - if (this.props.$$$parent.isNew === true) return false + if (this._isNew === true) return false return super.isValueUnchanged() } setValue(val) { - if (typeof val === 'object') val = val.id + if (val && typeof val === 'object') val = val.id this.__select2.val(val).trigger('change') } } @@ -1866,7 +1872,7 @@ class RbFormReference extends RbFormElement { // 新建记录时触发回填 const props = this.props - if (props.$$$parent.isNew && props.value && props.value.id) { + if (this._isNew && props.value && props.value.id) { setTimeout(() => this.triggerAutoFillin(props.value.id), 500) } } @@ -1885,7 +1891,7 @@ class RbFormReference extends RbFormElement { const cascadingValue = this._getCascadingFieldValue() return cascadingValue ? { cascadingValue, ...query } : query }, - placeholder: this.props.readonlyw === 3 ? $L('自动值') : null, + placeholder: this._placeholderw, }) const val = this.state.value @@ -2115,7 +2121,7 @@ class RbFormN2NReference extends RbFormReference { handleChange(e, checkValue) { let val = e.target.value - if (typeof val === 'object') val = val.join(',') + if (val && typeof val === 'object') val = val.join(',') this.setState({ value: val }, () => checkValue === true && this.checkValue()) } @@ -2315,9 +2321,9 @@ class RbFormMultiSelect extends RbFormElement { } _getMaskValue() { - const value = this.state.value - if (!value) return 0 - return typeof value === 'object' ? value.id : value + const val = this.state.value + if (!val) return 0 + return typeof val === 'object' ? val.id : val } } @@ -2474,7 +2480,7 @@ class RbFormAvatar extends RbFormElement { class RbFormLocation extends RbFormElement { constructor(props) { super(props) - this._autoLocation = props.locationAutoLocation && props.$$$parent.isNew && !props.value + this._autoLocation = props.locationAutoLocation && this._isNew && !props.value } renderElement() { @@ -2502,7 +2508,7 @@ class RbFormLocation extends RbFormElement { value={lnglat ? lnglat.text || '' : ''} onChange={(e) => this.handleChange(e)} readOnly - placeholder={this.props.readonlyw > 0 ? $L('自动值') : null} + placeholder={this._placeholderw} onClick={() => this._showMap(lnglat)} /> @@ -2541,11 +2547,11 @@ class RbFormLocation extends RbFormElement { ) } - _parseLnglat(value) { - if (!value) return null - if (typeof value === 'object') return value + _parseLnglat(val) { + if (!val) return null + if (typeof val === 'object') return val - const vals = value.split('$$$$') + const vals = val.split('$$$$') const lnglat = vals[1] ? vals[1].split(',') : null // 无坐标 return { text: vals[0], @@ -2691,7 +2697,7 @@ class RbFormTag extends RbFormElement { let options = [...props.options] let selected = [] - if (props.$$$parent.isNew) { + if (this._isNew) { props.options.forEach((item) => { if (item.default) selected.push(item.name) }) @@ -2715,7 +2721,7 @@ class RbFormTag extends RbFormElement { this._initOptions() } else { this.__select2 = $(this._fieldValue).select2({ - placeholder: this.props.readonlyw === 3 ? $L('自动值') : $L('输入%s', this.props.label), + placeholder: this.props.readonlyw > 0 ? this._placeholderw : $L('输入%s', this.props.label), maximumSelectionLength: this.__maxSelect, tags: true, language: { @@ -2737,7 +2743,7 @@ class RbFormTag extends RbFormElement { } setValue(val) { - if (typeof val === 'object') val = val.join('$$$$') + if (val && typeof val === 'object') val = val.join('$$$$') super.setValue(val) // fix: v3.4.4 diff --git a/src/main/resources/web/assets/js/rb-page.js b/src/main/resources/web/assets/js/rb-page.js index c87586b56..39e1e209d 100644 --- a/src/main/resources/web/assets/js/rb-page.js +++ b/src/main/resources/web/assets/js/rb-page.js @@ -8,7 +8,7 @@ See LICENSE and COMMERCIAL in the project root for license information. /* !!! KEEP IT ES5 COMPATIBLE !!! */ // GA -(function () { +;(function () { var gaScript = document.createElement('script') gaScript.src = 'https://www.googletagmanager.com/gtag/js?id=G-ZCZHJPMEG7' gaScript.async = true @@ -180,21 +180,20 @@ $(function () { }) }) - if (window.sessionStorage) { - $('.navbar .navbar-collapse>.navbar-nav a').on('click', function (e) { - sessionStorage.setItem('AppHome._InTab', e.target.href.split('?')[1]) - }) - + var $topNavs = $('.navbar .navbar-collapse>.navbar-nav a') + if ($topNavs.length > 1) { document.onvisibilitychange = function () { if (document.visibilityState !== 'visible') return - var tabHome = sessionStorage.getItem('AppHome._InTab') - tabHome && - $(tabHome.split('&')).each(function () { + var active = $('.navbar .navbar-collapse>.navbar-nav li.active>a').attr('href') + if (active) { + active = active.split('?')[1].split('&') + $(active).each(function () { var nv = this.split('=') - if (nv[0] === 'n') $.cookie('AppHome.Nav', nv[1], { expires: null }) - if (nv[0] === 'd') $.cookie('AppHome.Dash', nv[1], { expires: null }) + if (nv[0] === 'n') $.cookie('AppHome.Nav', nv[1], { expires: 30 }) + if (nv[0] === 'd') $.cookie('AppHome.Dash', nv[1], { expires: 30 }) }) + } console.log('Switch on visibilityState ...', $.cookie('AppHome.Nav'), $.cookie('AppHome.Dash')) } } @@ -742,23 +741,24 @@ var $unmount = function (container, delay, keepContainer) { /** * 初始化引用字段(搜索) */ -var $initReferenceSelect2 = function (el, options) { +var $initReferenceSelect2 = function (el, option) { + console.log(option) var search_input = null var $el = $(el).select2({ - placeholder: options.placeholder || $L('选择%s', options.label), + placeholder: option.placeholder || $L('选择%s', option.label), minimumInputLength: 0, maximumSelectionLength: $(el).attr('multiple') ? 999 : 2, ajax: { - url: '/commons/search/' + (options.searchType || 'reference'), + url: '/commons/search/' + (option.searchType || 'reference'), delay: 300, data: function (params) { search_input = params.term var query = { - entity: options.entity, - field: options.name, + entity: option.entity, + field: option.name, q: params.term, } - if (options && typeof options.wrapQuery === 'function') return options.wrapQuery(query) + if (option && typeof option.wrapQuery === 'function') return option.wrapQuery(query) else return query }, processResults: function (data) { @@ -782,7 +782,7 @@ var $initReferenceSelect2 = function (el, options) { return $L('清除') }, }, - theme: 'default ' + (options.appendClass || ''), + theme: 'default ' + (option.appendClass || ''), }) return $el @@ -1119,3 +1119,22 @@ var $pages = function (tp, cp) { if (end <= tp) pages.push(tp) return pages } + +// 格式化代码 +var $formattedCode = function (c, type) { + if (typeof c === 'object') c = JSON.stringify(c) + if (!window.prettier) return c + + try { + // eslint-disable-next-line no-undef + return prettier.format(c, { + parser: type || 'json', + // eslint-disable-next-line no-undef + plugins: prettierPlugins, + printWidth: 10, + }) + } catch (err) { + console.log('Cannot format code :', err) + return c + } +}