Feat approve editable (#947)

* Refactor batch update editor and improve NTEXT handling

* Enable batch approval and update approval UI

* Update table and treemap chart rendering options

* Add editable record mode to approval flow nodes

* Enable editable approval records for approvers

* Allow editing details during approval process

* Refactor form modal creation and extra button logic
This commit is contained in:
REBUILD 企业管理系统 2025-08-16 16:57:06 +08:00 committed by GitHub
parent 31730c4464
commit 0c43b36419
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 399 additions and 171 deletions

View file

@ -159,5 +159,6 @@ module.exports = {
marked: true, marked: true,
$focus2End: true, $focus2End: true,
RecordSelectorModal: true, RecordSelectorModal: true,
$fetchMetaInfo: true,
}, },
} }

2
@rbv

@ -1 +1 @@
Subproject commit ffe18eeaa8ae3a8acd3de09e1dbed1dbf994e9b4 Subproject commit 34e48060b157be1bfbfb69d05506816ccccfbd48

View file

@ -34,6 +34,7 @@ import com.rebuild.core.privileges.UserService;
import com.rebuild.core.privileges.bizz.Department; import com.rebuild.core.privileges.bizz.Department;
import com.rebuild.core.privileges.bizz.User; import com.rebuild.core.privileges.bizz.User;
import com.rebuild.core.service.NoRecordFoundException; import com.rebuild.core.service.NoRecordFoundException;
import com.rebuild.core.service.approval.ApprovalHelper;
import com.rebuild.core.service.approval.ApprovalState; import com.rebuild.core.service.approval.ApprovalState;
import com.rebuild.core.service.approval.RobotApprovalManager; import com.rebuild.core.service.approval.RobotApprovalManager;
import com.rebuild.core.service.general.GeneralEntityService; import com.rebuild.core.service.general.GeneralEntityService;
@ -143,6 +144,7 @@ public class FormsBuilder extends FormsManager {
ApprovalState approvalState; ApprovalState approvalState;
// 提示 // 提示
String readonlyMessage = null; String readonlyMessage = null;
String readonlywMessage = null; // 可强制编辑
// 判断表单权限 // 判断表单权限
@ -153,10 +155,15 @@ public class FormsBuilder extends FormsManager {
Assert.notNull(mainid, "CALL `FormBuilderContextHolder#setMainIdOfDetail` FIRST!"); Assert.notNull(mainid, "CALL `FormBuilderContextHolder#setMainIdOfDetail` FIRST!");
approvalState = EntityHelper.isUnsavedId(mainid) ? null : getHadApproval(hasMainEntity, mainid); approvalState = EntityHelper.isUnsavedId(mainid) ? null : getHadApproval(hasMainEntity, mainid);
if ((approvalState == ApprovalState.PROCESSING || approvalState == ApprovalState.APPROVED)) { if (approvalState == ApprovalState.APPROVED) {
readonlyMessage = approvalState == ApprovalState.APPROVED readonlyMessage = Language.L("主记录已审批完成,不能添加明细");
? Language.L("主记录已审批完成,不能添加明细") } else if (approvalState == ApprovalState.PROCESSING) {
: Language.L("主记录正在审批中,不能添加明细"); boolean allow42 = ApprovalHelper.isAllowEditableRecord(mainid, user);
if (allow42) {
readonlywMessage = Language.L("主记录正在审批中,审批人允许编辑");
} else {
readonlyMessage = Language.L("主记录正在审批中,不能添加明细");
}
} }
// 明细无需审批 // 明细无需审批
approvalState = null; approvalState = null;
@ -193,7 +200,13 @@ public class FormsBuilder extends FormsManager {
if (approvalState == ApprovalState.APPROVED) { if (approvalState == ApprovalState.APPROVED) {
readonlyMessage = Language.L("%s已审批完成不能编辑", recordType); readonlyMessage = Language.L("%s已审批完成不能编辑", recordType);
} else if (approvalState == ApprovalState.PROCESSING) { } else if (approvalState == ApprovalState.PROCESSING) {
readonlyMessage = Language.L("%s正在审批中不能编辑", recordType); // v4.2
boolean allow42 = ApprovalHelper.isAllowEditableRecord(recordId, user);
if (allow42) {
readonlywMessage = Language.L("%s正在审批中审批人允许编辑", recordType);
} else {
readonlyMessage = Language.L("%s正在审批中不能编辑", recordType);
}
} }
} }
} }
@ -311,7 +324,8 @@ public class FormsBuilder extends FormsManager {
} }
} }
if (readonlyMessage != null) model.set("readonlyMessage", readonlyMessage); if (readonlywMessage != null) model.set("readonlywMessage", readonlywMessage);
else if (readonlyMessage != null) model.set("readonlyMessage", readonlyMessage);
// v3.4 // v3.4
String disabledViewEditable = EasyMetaFactory.valueOf(entityMeta) String disabledViewEditable = EasyMetaFactory.valueOf(entityMeta)

View file

@ -12,6 +12,7 @@ import cn.devezhao.commons.ObjectUtils;
import cn.devezhao.persist4j.Entity; import cn.devezhao.persist4j.Entity;
import cn.devezhao.persist4j.Field; import cn.devezhao.persist4j.Field;
import cn.devezhao.persist4j.engine.ID; import cn.devezhao.persist4j.engine.ID;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.rebuild.core.Application; import com.rebuild.core.Application;
import com.rebuild.core.configuration.ConfigurationException; import com.rebuild.core.configuration.ConfigurationException;
@ -237,6 +238,38 @@ public class ApprovalHelper {
return null; return null;
} }
/**
* @param recordId
* @return
*/
public static FlowNode getCurrentFlowNode(ID recordId) {
ApprovalStatus s = getApprovalStatus(recordId);
return getFlowNode(s.getApprovalId(), s.getCurrentStepNode());
}
/**
* @param recordId
* @param user
* @return
*/
public static boolean isAllowEditableRecord(ID recordId, ID user) {
// 明细需要使用主记录判断
if (MetadataHelper.getEntity(recordId.getEntityCode()).getMainEntity() != null) {
recordId = QueryHelper.getMainIdByDetail(recordId);
}
ApprovalStatus s = getApprovalStatus(recordId);
FlowNode node = getFlowNode(s.getApprovalId(), s.getCurrentStepNode());
if (node == null || node.getEditableMode() != FlowNode.EDITABLE_MODE_RECORD) return false;
JSONArray current = new ApprovalProcessor(recordId, s.getApprovalId()).getCurrentStep(s);
for (Object o : current) {
JSONObject step = (JSONObject) o;
if (StringUtils.equalsIgnoreCase(user.toLiteral(), step.getString("approver"))) return true;
}
return false;
}
/** /**
* 获取审批超时时间 * 获取审批超时时间
* *

View file

@ -20,6 +20,7 @@ import com.rebuild.core.privileges.UserHelper;
import com.rebuild.core.privileges.bizz.Department; import com.rebuild.core.privileges.bizz.Department;
import com.rebuild.utils.JSONUtils; import com.rebuild.utils.JSONUtils;
import lombok.Getter; import lombok.Getter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import java.util.ArrayList; import java.util.ArrayList;
@ -66,6 +67,12 @@ public class FlowNode {
public static final String SIGN_OR = "OR"; // 或签 public static final String SIGN_OR = "OR"; // 或签
public static final String SIGN_ALL = "ALL"; // 逐个审批暂未用 public static final String SIGN_ALL = "ALL"; // 逐个审批暂未用
// 可修改记录
public static final int EDITABLE_MODE_NONE = 0;
public static final int EDITABLE_MODE_RECORD = 1;
public static final int EDITABLE_MODE_FIELDS = 10;
// -- // --
@Getter @Getter
@ -153,8 +160,9 @@ public class FlowNode {
* @return * @return
*/ */
public boolean allowBatch() { public boolean allowBatch() {
Boolean b = getDataMap().getBoolean("allowBatch"); // Boolean b = getDataMap().getBoolean("allowBatch");
return b != null && b; // return b != null && b;
return true; // v4.2 全面允许
} }
/** /**
@ -166,7 +174,7 @@ public class FlowNode {
*/ */
public Set<ID> getSpecUsers(ID operator, ID record) { public Set<ID> getSpecUsers(ID operator, ID record) {
JSONArray userDefs = getDataMap().getJSONArray("users"); JSONArray userDefs = getDataMap().getJSONArray("users");
if (userDefs == null || userDefs.isEmpty()) return Collections.emptySet(); if (CollectionUtils.isEmpty(userDefs)) return Collections.emptySet();
String userType = userDefs.getString(0); String userType = userDefs.getString(0);
if (USER_SELF.equalsIgnoreCase(userType)) { if (USER_SELF.equalsIgnoreCase(userType)) {
@ -247,7 +255,7 @@ public class FlowNode {
*/ */
public Set<String> getCcAccounts(ID record) { public Set<String> getCcAccounts(ID record) {
JSONArray accountFields = getDataMap().getJSONArray("accounts"); JSONArray accountFields = getDataMap().getJSONArray("accounts");
if (accountFields == null || accountFields.isEmpty()) return Collections.emptySet(); if (CollectionUtils.isEmpty(accountFields)) return Collections.emptySet();
Entity useEntity = MetadataHelper.getEntity(record.getEntityCode()); Entity useEntity = MetadataHelper.getEntity(record.getEntityCode());
List<String> useFields = new ArrayList<>(); List<String> useFields = new ArrayList<>();
@ -291,20 +299,32 @@ public class FlowNode {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == null) { if (obj == null) return false;
return false;
}
return obj instanceof FlowNode && obj.hashCode() == this.hashCode(); return obj instanceof FlowNode && obj.hashCode() == this.hashCode();
} }
/**
* 节点可修改记录
*
* @return 0=不可修改, 1=可修改, 10=可修改字段
* @see #getEditableFields()
*/
public int getEditableMode() {
if (getDataMap().containsKey("editableMode")) {
return getDataMap().getIntValue("editableMode");
}
// v4.2 兼容
return CollectionUtils.isEmpty(getEditableFields()) ? EDITABLE_MODE_NONE : EDITABLE_MODE_FIELDS;
}
/** /**
* 节点可编辑字段 * 节点可编辑字段
* *
* @return * @return
*/ */
public JSONArray getEditableFields() { public JSONArray getEditableFields() {
JSONArray editableFields = dataMap == null ? null : dataMap.getJSONArray("editableFields"); JSONArray editableFields = getDataMap().getJSONArray("editableFields");
if (editableFields == null) return null; if (CollectionUtils.isEmpty(editableFields)) return JSONUtils.EMPTY_ARRAY;
editableFields = (JSONArray) JSONUtils.clone(editableFields); editableFields = (JSONArray) JSONUtils.clone(editableFields);
for (Object o : editableFields) { for (Object o : editableFields) {
@ -321,7 +341,7 @@ public class FlowNode {
* @return * @return
*/ */
public JSONObject getExpiresAuto() { public JSONObject getExpiresAuto() {
JSONObject expiresAuto = dataMap == null ? null : dataMap.getJSONObject("expiresAuto"); JSONObject expiresAuto = getDataMap().getJSONObject("expiresAuto");
if (expiresAuto == null) return null; if (expiresAuto == null) return null;
if (expiresAuto.getIntValue("expiresAuto") <= 0) return null; if (expiresAuto.getIntValue("expiresAuto") <= 0) return null;
return expiresAuto; return expiresAuto;

View file

@ -68,16 +68,12 @@ public class RobotApprovalManager implements ConfigManager {
// 实体的 // 实体的
FlowDefinition[] defs = getFlowDefinitions(entity); FlowDefinition[] defs = getFlowDefinitions(entity);
for (FlowDefinition def : defs) { for (FlowDefinition d : defs) {
if (!def.isDisabled()) { if (!d.isDisabled()) return ApprovalState.DRAFT;
return ApprovalState.DRAFT;
}
} }
return null; return null;
} }
/** /**
* 获取实体是否有流程 * 获取实体是否有流程
* *

View file

@ -19,7 +19,6 @@ import org.apache.commons.lang3.ArrayUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
@ -113,7 +112,13 @@ public class TableChart extends ChartData {
} }
String tableHtml = new TableBuilder(this, dataRaw).toHTML(); String tableHtml = new TableBuilder(this, dataRaw).toHTML();
return JSONUtils.toJSONObject("html", tableHtml);
JSONObject renderOption = config.getJSONObject("option");
if (renderOption == null) renderOption = new JSONObject();
return JSONUtils.toJSONObject(
new String[]{"html", "_renderOption"},
new Object[]{tableHtml, renderOption});
} }
protected boolean isShowLineNumber() { protected boolean isShowLineNumber() {

View file

@ -48,7 +48,8 @@ public class TreemapChart extends ChartData {
JSONObject renderOption = config.getJSONObject("option"); JSONObject renderOption = config.getJSONObject("option");
if (renderOption == null) renderOption = new JSONObject(); if (renderOption == null) renderOption = new JSONObject();
renderOption.put("dataFlags", new String[] { getNumericalFlag(num1) }); renderOption.put("dataFlags", new String[]{getNumericalFlag(num1)});
renderOption.remove("useBgcolor");
return JSONUtils.toJSONObject( return JSONUtils.toJSONObject(
new String[]{"data", "xLabel", "xAmount", "_renderOption"}, new String[]{"data", "xLabel", "xAmount", "_renderOption"},

View file

@ -610,10 +610,13 @@ public class GeneralEntityService extends ObservableService implements EntitySer
} }
ApprovalState state = ApprovalHelper.getApprovalState(dtmFieldValue); ApprovalState state = ApprovalHelper.getApprovalState(dtmFieldValue);
if (state == ApprovalState.APPROVED || state == ApprovalState.PROCESSING) { if (state == ApprovalState.APPROVED) {
throw new DataSpecificationException(state == ApprovalState.APPROVED throw new DataSpecificationException(Language.L("主记录已审批完成,不能添加明细"));
? Language.L("主记录已审批完成,不能添加明细") } else if (state == ApprovalState.PROCESSING) {
: Language.L("主记录正在审批中,不能添加明细")); boolean allow42 = ApprovalHelper.isAllowEditableRecord(dtmFieldValue, getCurrentUser());
if (!allow42) {
throw new DataSpecificationException(Language.L("主记录正在审批中,不能添加明细"));
}
} }
} }
@ -645,6 +648,13 @@ public class GeneralEntityService extends ObservableService implements EntitySer
boolean unallow = false; boolean unallow = false;
if (action == BizzPermission.DELETE) { if (action == BizzPermission.DELETE) {
unallow = currentState == ApprovalState.APPROVED || currentState == ApprovalState.PROCESSING; unallow = currentState == ApprovalState.APPROVED || currentState == ApprovalState.PROCESSING;
// v4.2 允许修改记录
if (unallow && currentState == ApprovalState.PROCESSING) {
boolean allow42 = ApprovalHelper.isAllowEditableRecord(checkRecordId, getCurrentUser());
if (allow42) unallow = false;
}
} else if (action == BizzPermission.UPDATE) { } else if (action == BizzPermission.UPDATE) {
unallow = currentState == ApprovalState.APPROVED || currentState == ApprovalState.PROCESSING; unallow = currentState == ApprovalState.APPROVED || currentState == ApprovalState.PROCESSING;
@ -659,6 +669,15 @@ public class GeneralEntityService extends ObservableService implements EntitySer
boolean forceUpdate = GeneralEntityServiceContextHolder.isAllowForceUpdate(false); boolean forceUpdate = GeneralEntityServiceContextHolder.isAllowForceUpdate(false);
if (forceUpdate) unallow = false; if (forceUpdate) unallow = false;
} }
// v4.2 允许修改记录
if (unallow) {
boolean is = ApprovalHelper.isAllowEditableRecord(checkRecordId, getCurrentUser());
if (is) {
unallow = false;
GeneralEntityServiceContextHolder.setAllowForceUpdate(checkRecordId);
}
}
} }
if (unallow) { if (unallow) {

View file

@ -38,6 +38,7 @@ import com.rebuild.core.service.trigger.DataValidateException;
import com.rebuild.core.support.RbvFunction; import com.rebuild.core.support.RbvFunction;
import com.rebuild.utils.JSONUtils; import com.rebuild.utils.JSONUtils;
import com.rebuild.web.BaseController; import com.rebuild.web.BaseController;
import com.rebuild.web.EntityParam;
import com.rebuild.web.IdParam; import com.rebuild.web.IdParam;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
@ -85,11 +86,28 @@ public class ApprovalController extends BaseController {
return res; return res;
} }
@GetMapping("alist")
public RespBody getApprovalList(HttpServletRequest request, @EntityParam Entity entity) {
boolean valid = getBoolParameter(request, "valid");
FlowDefinition[] defs = RobotApprovalManager.instance.getFlowDefinitions(entity);
List<Object> res = new ArrayList<>();
for (FlowDefinition d : defs) {
if (d.isDisabled()) continue;
// 仅返回可用的
if (valid && !d.isWorkable()) continue;
res.add(JSONUtils.toJSONObject(new String[]{"id", "text"},
new Object[]{d.getID("id"), d.getString("name")}));
}
return RespBody.ok(res);
}
@GetMapping("state") @GetMapping("state")
public RespBody getApprovalState(HttpServletRequest request, @IdParam(name = "record") ID recordId) { public RespBody getApprovalState(HttpServletRequest request, @IdParam(name = "record") ID recordId) {
final Entity approvalEntity = MetadataHelper.getEntity(recordId.getEntityCode()); final Entity approvalEntity = MetadataHelper.getEntity(recordId.getEntityCode());
if (!MetadataHelper.hasApprovalField(approvalEntity)) { if (!MetadataHelper.hasApprovalField(approvalEntity)) {
return RespBody.error("NOT AN APPROVAL ENTITY"); return RespBody.error("NONE APPROVAL ENTITY");
} }
final ID user = getRequestUser(request); final ID user = getRequestUser(request);
@ -189,10 +207,14 @@ public class ApprovalController extends BaseController {
if (reqType < 2) data.put("remarkReq", reqType); if (reqType < 2) data.put("remarkReq", reqType);
else data.put("remarkReq", expTime == null || expTime < 0 ? 0 : 1); else data.put("remarkReq", expTime == null || expTime < 0 ? 0 : 1);
// 可修改字段 // 可修改记录
JSONArray editableFields = currentFlowNode.getEditableFields(); int editableMode = currentFlowNode.getEditableMode();
if (editableFields != null && !editableFields.isEmpty()) { data.put("editableMode", editableMode);
data.putAll(new EditableFields(editableFields).buildForms(recordId, user)); if (editableMode ==FlowNode.EDITABLE_MODE_FIELDS) {
JSONArray editableFields = currentFlowNode.getEditableFields();
if (!CollectionUtils.isEmpty(editableFields)) {
data.putAll(new EditableFields(editableFields).buildForms(recordId, user));
}
} }
return data; return data;

View file

@ -1,36 +0,0 @@
/*Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
See LICENSE and COMMERCIAL in the project root for license information.
*/
package com.rebuild.web.robot.trigger;
import com.alibaba.fastjson.JSON;
import com.rebuild.core.Application;
import com.rebuild.utils.JSONUtils;
import com.rebuild.web.BaseController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* @author devezhao
* @since 2019/05/25
*/
@RestController
@RequestMapping("/admin/robot/trigger/")
public class AutoApprovalController extends BaseController {
@RequestMapping("auto-approval-alist")
public JSON approvalList(HttpServletRequest request) {
Object[][] array = Application.createQueryNoFilter(
"select configId,name from RobotApprovalConfig where belongEntity = ? and isDisabled = ? order by name")
.setParameter(1, getParameterNotNull(request, "entity"))
.setParameter(2, false)
.array();
return JSONUtils.toJSONObjectArray(new String[] { "id", "text" }, array);
}
}

View file

@ -714,7 +714,8 @@ See LICENSE and COMMERCIAL in the project root for license information.
width: 100%; width: 100%;
} }
.expires-notify-set { .expires-notify-set,
.editable-mode-set {
background-color: #eee; background-color: #eee;
padding: 20px; padding: 20px;
padding-top: 15px; padding-top: 15px;

View file

@ -233,6 +233,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
background-color: #eceff1; background-color: #eceff1;
border-width: 1px; border-width: 1px;
font-weight: normal; font-weight: normal;
font-weight: bold;
} }
.chart.ctable .table.line-number thead th:first-child, .chart.ctable .table.line-number thead th:first-child,
@ -309,6 +310,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
padding-top: 0; padding-top: 0;
padding-bottom: 6px; padding-bottom: 6px;
font-weight: normal; font-weight: normal;
font-weight: bold;
} }
.chart.ApprovalList .table td, .chart.ApprovalList .table td,
@ -744,6 +746,14 @@ See LICENSE and COMMERCIAL in the project root for license information.
right: 0; right: 0;
} }
.chart-box.TABLE .chart.ctable .table {
background-color: transparent;
}
.chart-box.TABLE.gradient-bg .chart.ctable .table th {
background-color: rgba(255, 255, 255, 0.2);
}
/* gradient-backgrounds.css */ /* gradient-backgrounds.css */
.gradient-bg-1 { .gradient-bg-1 {

View file

@ -918,6 +918,7 @@ body.view-body {
textarea.row1x { textarea.row1x {
height: 37px !important; height: 37px !important;
resize: none; resize: none;
padding-top: 7px;
} }
textarea.row2x { textarea.row2x {

View file

@ -221,10 +221,10 @@ class SimpleNode extends NodeSpec {
if (this.nodeType === 'approver') { if (this.nodeType === 'approver') {
if (data.allowReferral) descs.push($L('允许转审')) if (data.allowReferral) descs.push($L('允许转审'))
if (data.allowCountersign) descs.push($L('允许加签')) if (data.allowCountersign) descs.push($L('允许加签'))
if (data.allowBatch) descs.push($L('允许批量')) // if (data.allowBatch) descs.push($L('允许批量'))
descs.push(data.signMode === 'AND' ? $L('会签') : data.signMode === 'ALL' ? $L('依次审批') : $L('或签')) descs.push(data.signMode === 'AND' ? $L('会签') : data.signMode === 'ALL' ? $L('依次审批') : $L('或签'))
if (data.expiresAuto && ~~data.expiresAuto.expiresAuto > 0) descs.push($L('限时审批')) if (data.expiresAuto && ~~data.expiresAuto.expiresAuto > 0) descs.push($L('限时审批'))
if (data.editableFields && data.editableFields.length > 0) descs.push($L('可修改字段')) if (~~data.editableMode > 0) descs.push($L('可修改记录'))
} else if (this.nodeType === 'start') { } else if (this.nodeType === 'start') {
if (data.unallowCancel) descs.push($L('禁止撤回')) if (data.unallowCancel) descs.push($L('禁止撤回'))
} }
@ -635,6 +635,10 @@ class ApproverNodeConfig extends StartNodeConfig {
if (!props.users || props.users.length === 0) this.state.users = 'SPEC' if (!props.users || props.users.length === 0) this.state.users = 'SPEC'
else if (props.users[0] === 'SELF') this.state.users = 'SELF' else if (props.users[0] === 'SELF') this.state.users = 'SELF'
else this.state.users = 'SPEC' else this.state.users = 'SPEC'
// v4.2 兼容
if (props.editableFields && props.editableFields.length > 0 && !props.editableMode) {
this.state.editableMode = '10'
}
} }
render() { render() {
@ -682,7 +686,7 @@ class ApproverNodeConfig extends StartNodeConfig {
</span> </span>
</label> </label>
</div> </div>
<div className="form-group mb-0"> <div className="form-group mb-0 hide disabled-on-4.2">
<label className="custom-control custom-control-sm custom-checkbox"> <label className="custom-control custom-control-sm custom-checkbox">
<input className="custom-control-input" type="checkbox" name="allowBatch" checked={this.state.allowBatch === true} onChange={this.handleChange} /> <input className="custom-control-input" type="checkbox" name="allowBatch" checked={this.state.allowBatch === true} onChange={this.handleChange} />
<span className="custom-control-label"> <span className="custom-control-label">
@ -804,43 +808,57 @@ class ApproverNodeConfig extends StartNodeConfig {
</div> </div>
</div> </div>
<div className="form-group mt-5"> <div className="form-group mt-5 pb-5">
<label className="text-bold">{$L('可修改字段')}</label> <label className="text-bold">{$L('修改记录')}</label>
<div style={{ position: 'relative' }}> <div className="row">
<table className={`table table-sm fields-table ${(this.state.editableFields || []).length === 0 && 'hide'}`}> <div className="col">
<tbody ref={(c) => (this._$editableFields = c)}> <select className="form-control form-control-sm" name="editableMode" defaultValue={this.state.editableMode || null} onChange={this.handleChange}>
{(this.state.editableFields || []).map((item) => { <option value="0">{$L('不可修改')}</option>
return ( <option value="1">{$L('可修改')}</option>
<tr key={`field-${item.field}`}> <option value="10">{$L('可修改指定字段')} </option>
<td>{this.__fieldLabel(item.field)}</td> </select>
<td width="140" data-field={item.field}> </div>
<label className="custom-control custom-control-sm custom-checkbox custom-control-inline"> <div className="col pl-0" />
<input className="custom-control-input" type="checkbox" name="notNull" defaultChecked={item.notNull === true} /> </div>
<span className="custom-control-label">{$L('必填')}</span> <div className={`editable-mode-set mt-3 ${~~this.state.editableMode !== 10 && 'hide'}`}>
</label> <label className="text-bold">{$L('指定字段')}</label>
<label className="custom-control custom-control-sm custom-checkbox custom-control-inline ml-3"> <div className="position-relative">
<input className="custom-control-input" type="checkbox" name="readOnly" defaultChecked={item.readOnly === true} /> <table className={`table table-sm fields-table ${(this.state.editableFields || []).length === 0 && 'hide'}`}>
<span className="custom-control-label">{$L('只读')}</span> <tbody ref={(c) => (this._$editableFields = c)}>
</label> {(this.state.editableFields || []).map((item) => {
</td> return (
<td width="40"> <tr key={`field-${item.field}`}>
<a className="close" title={$L('移除')} onClick={() => this.removeEditableField(item.field)}> <td width="57%">{this.__fieldLabel(item.field)}</td>
<i className="zmdi icon zmdi-close" /> <td width="35%" data-field={item.field}>
</a> <label className="custom-control custom-control-sm custom-checkbox custom-control-inline">
</td> <input className="custom-control-input" type="checkbox" name="notNull" defaultChecked={item.notNull === true} />
</tr> <span className="custom-control-label">{$L('必填')}</span>
) </label>
})} <label className="custom-control custom-control-sm custom-checkbox custom-control-inline ml-3">
</tbody> <input className="custom-control-input" type="checkbox" name="readOnly" defaultChecked={item.readOnly === true} />
</table> <span className="custom-control-label">{$L('只读')}</span>
<div className="pb-4"> </label>
<button className="btn btn-secondary btn-sm" onClick={() => renderRbcomp(<DlgFields selected={this.state.editableFields} call={(fs) => this.setEditableFields(fs)} />)}> </td>
+ {$L('选择字段')} <td width="8%">
</button> <a className="close" title={$L('移除')} onClick={() => this.removeEditableField(item.field)}>
<i className="zmdi icon zmdi-close" />
</a>
</td>
</tr>
)
})}
</tbody>
</table>
<div>
<button className="btn btn-secondary btn-sm" onClick={() => renderRbcomp(<DlgFields selected={this.state.editableFields} call={(fs) => this.setEditableFields(fs)} />)}>
+ {$L('选择字段')}
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{this.renderButton()} {this.renderButton()}
</div> </div>
) )
@ -907,14 +925,6 @@ class ApproverNodeConfig extends StartNodeConfig {
} }
save = () => { save = () => {
const editableFields = []
$(this._$editableFields)
.find('td[data-field]')
.each(function () {
const $this = $(this)
editableFields.push({ field: $this.data('field'), notNull: $this.find('input:eq(0)').prop('checked'), readOnly: $this.find('input:eq(1)').prop('checked') })
})
const expiresAuto = {} const expiresAuto = {}
$(this._$expiresAuto) $(this._$expiresAuto)
.find('input, select, textarea') .find('input, select, textarea')
@ -929,11 +939,27 @@ class ApproverNodeConfig extends StartNodeConfig {
return return
} }
const editableFields = []
if (~~this.state.editableMode === 10) {
$(this._$editableFields)
.find('td[data-field]')
.each(function () {
const $this = $(this)
editableFields.push({ field: $this.data('field'), notNull: $this.find('input:eq(0)').prop('checked'), readOnly: $this.find('input:eq(1)').prop('checked') })
})
if (editableFields.length === 0) {
RbHighbar.create($L('请指定可修改字段'))
return
}
}
const d = { const d = {
nodeName: this.state.nodeName, nodeName: this.state.nodeName,
users: this.state.users === 'SPEC' ? this._UserSelector.getSelected() : [this.state.users], users: this.state.users === 'SPEC' ? this._UserSelector.getSelected() : [this.state.users],
signMode: this.state.signMode, signMode: this.state.signMode,
selfSelecting: this.state.selfSelecting, selfSelecting: this.state.selfSelecting,
editableMode: this.state.editableMode,
editableFields: editableFields, editableFields: editableFields,
allowReferral: this.state.allowReferral, allowReferral: this.state.allowReferral,
allowCountersign: this.state.allowCountersign, allowCountersign: this.state.allowCountersign,

View file

@ -52,7 +52,9 @@ class BaseChart extends React.Component {
) )
return ( return (
<div className={`chart-box ${this.props.type} ${this.state.useBgcolor && `gradient-bg-${this.state.useBgcolor}`} ${this.props.type === 'DATALIST2' && 'DataList'}`} ref={(c) => (this._$box = c)}> <div
className={`chart-box ${this.props.type} ${this.state.useBgcolor && `gradient-bg gradient-bg-${this.state.useBgcolor}`} ${this.props.type === 'DATALIST2' && 'DataList'}`}
ref={(c) => (this._$box = c)}>
<div className="chart-head"> <div className="chart-head">
<div className="chart-title text-truncate">{this.state.title}</div> <div className="chart-title text-truncate">{this.state.title}</div>
{opActions} {opActions}

View file

@ -484,6 +484,7 @@ class ApprovalApproveForm extends ApprovalUsersForm {
)} )}
{(this.state.aform || this.state.aform_details) && this.renderLiteForm()} {(this.state.aform || this.state.aform_details) && this.renderLiteForm()}
{this.state.editableMode === 1 && this.renderEditable()}
<div className="form-group"> <div className="form-group">
<label> <label>
@ -559,6 +560,44 @@ class ApprovalApproveForm extends ApprovalUsersForm {
) )
} }
renderEditable() {
return (
<div className="form-group">
<label>{$L('信息完善')}</label>
<div>
<button
type="button"
className="btn btn-primary btn-outline"
onClick={() => {
$fetchMetaInfo(this.props.entity, (res) => {
const editProps = {
entity: res.entity,
title: $L('编辑%s', res.entityLabel),
icon: res.icon,
id: this.props.id,
postAfter: (recordId, next, formObject) => {
// 刷新列表
const rlp = window.RbListPage || parent.RbListPage
if (rlp) rlp.reload(recordId)
RbAlert.create($L('数据可能已更改是否需要刷新页面'), {
onConfirm: () => {
// 刷新视图
if (window.RbViewPage) window.RbViewPage.reload()
},
})
},
}
RbFormModal.create(editProps, true)
})
}}>
<i className="icon zmdi zmdi-edit mr-1" />
{$L('编辑')}
</button>
</div>
</div>
)
}
componentDidMount = () => this.getNextStep() componentDidMount = () => this.getNextStep()
reload = () => this.getNextStep() reload = () => this.getNextStep()
@ -632,7 +671,7 @@ class ApprovalApproveForm extends ApprovalUsersForm {
} }
// v4.0 // v4.0
if (this.state.remarkReq >= 1 && $empty(data.remark)) { if (this.state.remarkReq >= 1 && $empty(data.remark)) {
RbHighbar.createl('填写批注') RbHighbar.createl('输入批注')
return false return false
} }

View file

@ -419,7 +419,7 @@ class BatchUpdate extends BatchOperator {
<span className="badge badge-warning">{field.label}</span> <span className="badge badge-warning">{field.label}</span>
</div> </div>
<div className="col-2 pl-0 pr-0"> <div className="col-2 pl-0 pr-0">
<span className="badge badge-warning">{BUE_OPTYPES[item.op]}</span> <span className="badge badge-warning">{BU_OPS[item.op]}</span>
</div> </div>
<div className="col-6"> <div className="col-6">
{item.op !== 'NULL' && <span className="badge badge-warning text-break text-left">{FieldValueSet.formatFieldText(item.value, field)}</span>} {item.op !== 'NULL' && <span className="badge badge-warning text-break text-left">{FieldValueSet.formatFieldText(item.value, field)}</span>}
@ -433,7 +433,7 @@ class BatchUpdate extends BatchOperator {
})} })}
</div> </div>
<div className="batch-editor"> <div className="batch-editor">
{this.state.fields && <BatchUpdateEditor ref={(c) => (this._editor = c)} fields={this.state.fields} entity={this.props.entity} />} {this.state.fields && <BatchUpdateEntry ref={(c) => (this._buEntry = c)} fields={this.state.fields} entity={this.props.entity} />}
<div className="mt-1"> <div className="mt-1">
<button className="btn btn-primary btn-sm btn-outline" onClick={() => this.addItem()} type="button"> <button className="btn btn-primary btn-sm btn-outline" onClick={() => this.addItem()} type="button">
+ {$L('添加')} + {$L('添加')}
@ -446,7 +446,7 @@ class BatchUpdate extends BatchOperator {
} }
addItem() { addItem() {
const item = this._editor.buildItem() const item = this._buEntry.buildItem()
if (!item) return if (!item) return
const contents = this.state.updateContents || [] const contents = this.state.updateContents || []
@ -537,19 +537,22 @@ class BatchUpdate extends BatchOperator {
} }
} }
// ~ 批量修改编辑器 const BU_OPS = {
const BUE_OPTYPES = {
SET: $L('修改为'), SET: $L('修改为'),
NULL: $L('置空'), NULL: $L('置空'),
// TODO 支持更多修改模式 // TODO 支持更多修改模式
// 250813 也可以触发器修改
// PREFIX: $L('前添加'), // PREFIX: $L('前添加'),
// SUFFIX: $L('后添加'), // SUFFIX: $L('后添加'),
// REPLACE: $L('替换'),
// PLUS: $L('加上'), // PLUS: $L('加上'),
// MINUS: $L('减去'), // MINUS: $L('减去'),
// MULTIPLY: $L('乘以'),
// DIVIDE: $L('除以'),
} }
class BatchUpdateEditor extends React.Component { // 批量修改编辑器
class BatchUpdateEntry extends React.Component {
state = { ...this.props, selectOp: 'SET' } state = { ...this.props, selectOp: 'SET' }
componentDidMount() { componentDidMount() {
@ -598,8 +601,8 @@ class BatchUpdateEditor extends React.Component {
</div> </div>
<div className="col-2 pl-0 pr-0"> <div className="col-2 pl-0 pr-0">
<select className="form-control form-control-sm" ref={(c) => (this._$op = c)}> <select className="form-control form-control-sm" ref={(c) => (this._$op = c)}>
<option value="SET">{BUE_OPTYPES['SET']}</option> <option value="SET">{BU_OPS['SET']}</option>
<option value="NULL">{BUE_OPTYPES['NULL']}</option> <option value="NULL">{BU_OPS['NULL']}</option>
</select> </select>
<span className="text-muted">{$L('修改方式')}</span> <span className="text-muted">{$L('修改方式')}</span>
</div> </div>
@ -636,7 +639,10 @@ class BatchUpdateEditor extends React.Component {
} }
data.value = this._FieldValue.val() data.value = this._FieldValue.val()
if (!data.value) { if (data.value === false) {
// 格式不正确
return null
} else if (!data.value) {
RbHighbar.create($L('请填写新值')) RbHighbar.create($L('请填写新值'))
return null return null
} else { } else {
@ -661,10 +667,11 @@ class BatchApprove extends BatchOperator {
} }
renderOperator() { renderOperator() {
const approveState = ~~this.state.approveState
return ( return (
<div> <div>
<div className="form-group"> <div className="form-group">
<label className="text-bold">{$L('审批结果')}</label> <label className="text-bold">{$L('审批方式')}</label>
<div> <div>
<label className="custom-control custom-control-sm custom-radio custom-control-inline mb-0"> <label className="custom-control custom-control-sm custom-radio custom-control-inline mb-0">
<input className="custom-control-input" type="radio" name="approveState" value="10" onClick={this.handleChange} /> <input className="custom-control-input" type="radio" name="approveState" value="10" onClick={this.handleChange} />
@ -674,36 +681,73 @@ class BatchApprove extends BatchOperator {
<input className="custom-control-input" type="radio" name="approveState" value="11" onClick={this.handleChange} /> <input className="custom-control-input" type="radio" name="approveState" value="11" onClick={this.handleChange} />
<span className="custom-control-label">{$L('驳回')}</span> <span className="custom-control-label">{$L('驳回')}</span>
</label> </label>
<label className="custom-control custom-control-sm custom-radio custom-control-inline mb-0">
<input className="custom-control-input" type="radio" name="approveState" value="1" onClick={this.handleChange} />
<span className="custom-control-label">{$L('提交')}</span>
</label>
</div> </div>
</div> </div>
<div className="form-group">
<div className={`form-group ${approveState >= 10 ? '' : 'hide'}`}>
<label className="text-bold">{$L('批注')}</label> <label className="text-bold">{$L('批注')}</label>
<textarea className="form-control form-control-sm row2x" name="approveRemark" placeholder={$L('输入批注 (可选)')} maxLength="600" onChange={this.handleChange} /> <textarea className="form-control form-control-sm row2x" name="approveRemark" placeholder={$L('输入批注')} maxLength="600" onChange={this.handleChange} />
</div> </div>
<RbAlertBox message={$L('仅处于待你审批且允许批量审批的记录才能审批成功')} type="info" className="mb-0" /> <div className={`form-group ${approveState === 1 ? '' : 'hide'}`}>
<label className="text-bold">{$L('审批流程')}</label>
<select className="form-control form-control-sm" ref={(c) => (this._$useApproval = c)} />
</div>
<RbAlertBox message={$L('仅允许你审批或提交的记录才能审批成功')} type="info" className="mb-0" />
</div> </div>
) )
} }
componentDidMount() {
// super.componentDidMount()
$.get(`/app/entity/approval/alist?entity=${wpc.entity[0]}&valid=true`, (res) => {
$(this._$useApproval).select2({
placeholder: $L('无'),
allowClear: false,
language: {
noResults: () => $L('无适用流程'),
},
data: res.data || [],
})
})
}
handleConfirm() { handleConfirm() {
if (rb.commercial < 10) { if (rb.commercial < 10) {
RbHighbar.error(WrapHtml($L('免费版不支持批量审批功能 [(查看详情)](https://getrebuild.com/docs/rbv-features)'))) RbHighbar.error(WrapHtml($L('免费版不支持批量审批功能 [(查看详情)](https://getrebuild.com/docs/rbv-features)')))
return return
} }
if (!this.state.approveState) return RbHighbar.create($L('请选择审批结果')) if (!this.state.approveState) return RbHighbar.create($L('请选择审批方式'))
const _data = { const _data = {
queryData: this.getQueryData(), queryData: this.getQueryData(),
approveContent: { approveContent: {
state: this.state.approveState, state: this.state.approveState,
remark: this.state.approveRemark, remark: this.state.approveRemark || null,
approvalId: $val(this._$useApproval) || null,
}, },
} }
if (~~this.state.approveState === 1) {
if (!_data.approveContent.approvalId) {
RbHighbar.create($L('请选择审批流程'))
return
}
} else {
if ($empty(this.state.approveRemark)) {
RbHighbar.create($L('请输入批注'))
return
}
}
if (rb.env === 'dev') console.log(JSON.stringify(_data)) if (rb.env === 'dev') console.log(JSON.stringify(_data))
const that = this const that = this
RbAlert.create(<b>{$L('请再次确认审批数据范围和审批结果开始审批吗')}</b>, { RbAlert.create(<b>{$L('请再次确认审批数据范围和审批方式开始审批吗')}</b>, {
onConfirm: function () { onConfirm: function () {
this.hide() this.hide()
that.disabled(true, true) that.disabled(true, true)
@ -722,6 +766,7 @@ class BatchApprove extends BatchOperator {
} }
}) })
}, },
countdown: 5,
}) })
} }
@ -838,16 +883,16 @@ const RbListCommon = {
RbListPage.init(wpc.listConfig, entity, wpc.privileges) RbListPage.init(wpc.listConfig, entity, wpc.privileges)
if (wpc.advFilter !== false) AdvFilters.init('.adv-search', entity[0]) if (wpc.advFilter !== false) AdvFilters.init('.adv-search', entity[0])
const newProps = { title: $L('新建%s', entity[1]), entity: entity[0], icon: entity[2] } const newProps = { title: $L('新建%s', entity[1]), entity: entity[0], icon: entity[2], showExtraButton: true }
const $new = $('.J_new') const $new = $('.J_new')
.attr('disabled', false) .attr('disabled', false)
.on('click', () => RbFormModal.create(newProps)) .on('click', () => RbFormModal.create(newProps))
if (wpc.formsAttr) { if (wpc.formsAttr) {
$new.next().removeClass('hide') $new.next().removeClass('hide')
const $nn = $new.next().next() const $next = $new.next().next()
wpc.formsAttr.map((n) => { wpc.formsAttr.map((n) => {
$(`<a class="dropdown-item" data-id="${n.id}">${n.name || $L('默认布局')}</a>`) $(`<a class="dropdown-item" data-id="${n.id}">${n.name || $L('默认布局')}</a>`)
.appendTo($nn) .appendTo($next)
.on('click', () => RbFormModal.create({ ...newProps, specLayout: n.id }, true)) .on('click', () => RbFormModal.create({ ...newProps, specLayout: n.id }, true))
}) })
} else { } else {

View file

@ -39,10 +39,7 @@ const RbListPage = {
$('.J_edit').on('click', () => { $('.J_edit').on('click', () => {
const ids = this._RbList.getSelectedIds() const ids = this._RbList.getSelectedIds()
if (ids.length >= 1) { if (ids.length >= 1) {
const _entity = entity[0] RbFormModal.create({ id: ids[0], title: $L('编辑%s', entity[1]), entity: entity[0], icon: entity[2], showExtraButton: true }, true)
const editProps = { id: ids[0], title: $L('编辑%s', entity[1]), entity: _entity, icon: entity[2] }
if ((window.__LAB40_EDIT_PROVIDERS || {})[_entity]) window.__LAB40_EDIT_PROVIDERS[_entity](editProps)
else RbFormModal.create(editProps, true)
} }
}) })
$('.J_delete').on('click', () => { $('.J_delete').on('click', () => {
@ -111,7 +108,7 @@ class RbViewModal extends React.Component {
super(props) super(props)
this.state = { ...props, inLoad: true, isHide: true, destroy: false } this.state = { ...props, inLoad: true, isHide: true, destroy: false }
this._mcWidth = this.props.subView === true ? 1344 : 1404 this._mcWidth = props.subView === true ? 1344 : 1404
if ($(window).width() < 1464) this._mcWidth -= 184 if ($(window).width() < 1464) this._mcWidth -= 184
} }
@ -122,7 +119,14 @@ class RbViewModal extends React.Component {
<div className="modal-dialog"> <div className="modal-dialog">
<div className="modal-content" style={{ width: this._mcWidth }}> <div className="modal-content" style={{ width: this._mcWidth }}>
<div className={`modal-body iframe rb-loading ${this.state.inLoad === true && 'rb-loading-active'}`}> <div className={`modal-body iframe rb-loading ${this.state.inLoad === true && 'rb-loading-active'}`}>
<iframe ref={(c) => (this._iframe = c)} className={this.state.isHide ? 'invisible' : ''} src={this.state.showAfterUrl || 'about:blank'} frameBorder="0" scrolling="no" /> <iframe
data-subview={this.props.subView || false}
ref={(c) => (this._iframe = c)}
className={this.state.isHide ? 'invisible' : ''}
src={this.state.showAfterUrl || 'about:blank'}
frameBorder="0"
scrolling="no"
/>
<RbSpinner /> <RbSpinner />
</div> </div>
</div> </div>

View file

@ -134,7 +134,7 @@ class RbFormModal extends React.Component {
</RbForm> </RbForm>
) )
that.setState({ formComponent: FORM, alertMessage: formModel.readonlyMessage || null }, () => { that.setState({ formComponent: FORM, alertMessage: formModel.readonlywMessage || formModel.readonlyMessage || null }, () => {
that.setState({ inLoad: false }) that.setState({ inLoad: false })
if (window.FrontJS) { if (window.FrontJS) {
window.FrontJS.Form._trigger('open', [formModel]) window.FrontJS.Form._trigger('open', [formModel])
@ -254,6 +254,12 @@ class RbFormModal extends React.Component {
* @param {*} forceNew * @param {*} forceNew
*/ */
static create(props, forceNew) { static create(props, forceNew) {
// 自定义编辑
if ((window.__LAB40_EDIT_PROVIDERS || {})[props.entity]) {
window.__LAB40_EDIT_PROVIDERS[props.entity](props, forceNew)
return
}
// `__CURRENT35`, `__HOLDER` 可能已 unmount // `__CURRENT35`, `__HOLDER` 可能已 unmount
const that = this const that = this
if (forceNew === true) { if (forceNew === true) {
@ -559,7 +565,7 @@ class RbForm extends React.Component {
let moreActions = [] let moreActions = []
// 添加明细 // 添加明细
if (props.rawModel.mainMeta) { if (props.rawModel.mainMeta) {
if (parentProps._nextAddDetail) { if (parentProps.nextAddDetail) {
moreActions.push( moreActions.push(
<a key="Action101" className="dropdown-item" onClick={() => this.post(RbForm.NEXT_NEWDETAIL)}> <a key="Action101" className="dropdown-item" onClick={() => this.post(RbForm.NEXT_NEWDETAIL)}>
{$L('保存并添加')} {$L('保存并添加')}
@ -567,10 +573,8 @@ class RbForm extends React.Component {
) )
} }
} else { } else {
if (parentProps._noExtraButton) { // 保存并...
// 无扩展按钮 if (parentProps.showExtraButton) {
} else {
// 保存并...
if (props.rawModel.hadApproval && window.ApprovalSubmitForm) { if (props.rawModel.hadApproval && window.ApprovalSubmitForm) {
moreActions.push( moreActions.push(
<a key="Action103" className="dropdown-item" onClick={() => this.post(RbForm.NEXT_SUBMIT37)}> <a key="Action103" className="dropdown-item" onClick={() => this.post(RbForm.NEXT_SUBMIT37)}>
@ -896,12 +900,12 @@ class RbForm extends React.Component {
// 提交前调用 // 提交前调用
_postBeforeExec(data) { _postBeforeExec(data) {
if (typeof this._postBefore === 'function') { if (typeof this._postBefore === 'function') {
let ret = this._postBefore(data, this) const ret = this._postBefore(data, this)
if (ret === false) return false if (ret === false) return false
} }
if (window.FrontJS) { if (window.FrontJS) {
let ret = window.FrontJS.Form._trigger('saveBefore', [data, this]) const ret = window.FrontJS.Form._trigger('saveBefore', [data, this])
if (ret === false) return false if (ret === false) return false
} }

View file

@ -20,6 +20,7 @@ class RbViewForm extends React.Component {
this.onViewEditable = this.props.onViewEditable this.onViewEditable = this.props.onViewEditable
if (this.onViewEditable) this.onViewEditable = wpc.onViewEditable !== false if (this.onViewEditable) this.onViewEditable = wpc.onViewEditable !== false
if (window.__LAB_VIEWEDITABLE === false) this.onViewEditable = false if (window.__LAB_VIEWEDITABLE === false) this.onViewEditable = false
// temp for `saveSingleFieldValue` // temp for `saveSingleFieldValue`
this.__FormData = {} this.__FormData = {}
this._verticalLayout42 = window.__LAB_VERTICALLAYOUT this._verticalLayout42 = window.__LAB_VERTICALLAYOUT
@ -46,6 +47,7 @@ class RbViewForm extends React.Component {
} }
let hadApproval = res.data.hadApproval let hadApproval = res.data.hadApproval
if (hadApproval === 2 || hadApproval === 10) this.onViewEditable = false // be:4.2
let hadAlert = null let hadAlert = null
let hadSop = res.data.hadSop && rb.commercial > 1 let hadSop = res.data.hadSop && rb.commercial > 1
if (wpc.type === 'DetailView') { if (wpc.type === 'DetailView') {
@ -503,10 +505,7 @@ class EntityRelatedList extends RelatedList {
_handleEdit(e, id) { _handleEdit(e, id) {
$stopEvent(e, true) $stopEvent(e, true)
const _entity = this.__entity RbFormModal.create({ id: id, entity: this.__entity, title: $L('编辑%s', this.props.entity2[0]), icon: this.props.entity2[1] }, true)
const editProps = { id: id, entity: _entity, title: $L('编辑%s', this.props.entity2[0]), icon: this.props.entity2[1] }
if ((window.__LAB40_EDIT_PROVIDERS || {})[_entity]) window.__LAB40_EDIT_PROVIDERS[_entity](editProps)
else RbFormModal.create(editProps, true)
} }
_handleView(e) { _handleView(e) {
@ -663,10 +662,7 @@ const RbViewPage = {
}) })
$('.J_edit').on('click', () => { $('.J_edit').on('click', () => {
const _entity = entity[0] RbFormModal.create({ id: id, title: $L('编辑%s', entity[1]), entity: entity[0], icon: entity[2] }, true)
const editProps = { id: id, title: $L('编辑%s', entity[1]), entity: _entity, icon: entity[2] }
if ((window.__LAB40_EDIT_PROVIDERS || {})[_entity]) window.__LAB40_EDIT_PROVIDERS[_entity](editProps)
else RbFormModal.create(editProps, true)
}) })
$('.J_assign').on('click', () => DlgAssign.create({ entity: entity[0], ids: [id] })) $('.J_assign').on('click', () => DlgAssign.create({ entity: entity[0], ids: [id] }))
$('.J_share').on('click', () => DlgShare.create({ entity: entity[0], ids: [id] })) $('.J_share').on('click', () => DlgShare.create({ entity: entity[0], ids: [id] }))
@ -674,7 +670,7 @@ const RbViewPage = {
$('.J_add-detail-menu>a').on('click', function () { $('.J_add-detail-menu>a').on('click', function () {
const iv = { $MAINID$: id } const iv = { $MAINID$: id }
const $this = $(this) const $this = $(this)
RbFormModal.create({ title: $L('添加%s', $this.data('label')), entity: $this.data('entity'), icon: $this.data('icon'), initialValue: iv, _nextAddDetail: true }) RbFormModal.create({ title: $L('添加%s', $this.data('label')), entity: $this.data('entity'), icon: $this.data('icon'), initialValue: iv, nextAddDetail: true })
}) })
if (wpc.transformTos && wpc.transformTos.length > 0) { if (wpc.transformTos && wpc.transformTos.length > 0) {
@ -928,7 +924,13 @@ const RbViewPage = {
if (entity.length > 1) iv[entity[1]] = that.__id if (entity.length > 1) iv[entity[1]] = that.__id
else iv[`&${that.__entity[0]}`] = that.__id else iv[`&${that.__entity[0]}`] = that.__id
RbFormModal.create({ title: $L('新建%s', item._entityLabel || item.entityLabel), entity: entity[0], icon: item.icon, initialValue: iv, _noExtraButton: true }) const newProps = {
title: $L('新建%s', item._entityLabel || item.entityLabel),
entity: entity[0],
icon: item.icon,
initialValue: iv,
}
RbFormModal.create(newProps)
} }
}) })
@ -1035,13 +1037,17 @@ $(document).ready(() => {
$('.J_home').removeClass('hide') $('.J_home').removeClass('hide')
} }
// v4.2 // v4.2
if (parent && parent.RbListPage && parent.RbListPage._RbList && parent.RbListPage._RbList.jumpView) { if (window.frameElement && parent && parent.RbListPage && parent.RbListPage._RbList && parent.RbListPage._RbList.jumpView) {
$('.J_record-next') if ($(window.frameElement).data('subview')) {
.removeClass('hide') // SubView
.on('click', () => parent.RbListPage._RbList.jumpView(1)) } else {
$('.J_record-prev') $('.J_record-next')
.removeClass('hide') .removeClass('hide')
.on('click', () => parent.RbListPage._RbList.jumpView(-1)) .on('click', () => parent.RbListPage._RbList.jumpView(1))
$('.J_record-prev')
.removeClass('hide')
.on('click', () => parent.RbListPage._RbList.jumpView(-1))
}
} }
// iframe 点击穿透 // iframe 点击穿透

View file

@ -41,6 +41,10 @@ class FieldValueSet extends React.Component {
) )
} }
if (field.type === 'NTEXT') {
return <textarea className="form-control form-control-sm row1x" ref={(c) => (this._$value = c)} key={field.name}></textarea>
}
return <input className="form-control form-control-sm" placeholder={this.props.placeholder} ref={(c) => (this._$value = c)} key={field.name} maxLength="255" /> return <input className="form-control form-control-sm" placeholder={this.props.placeholder} ref={(c) => (this._$value = c)} key={field.name} maxLength="255" />
} }

View file

@ -610,7 +610,7 @@ var _initGlobalCreate = function () {
var $item = $('<a class="dropdown-item"><i class="icon zmdi zmdi-' + this.icon + '"></i>' + this.entityLabel + '</a>').appendTo($gc) var $item = $('<a class="dropdown-item"><i class="icon zmdi zmdi-' + this.icon + '"></i>' + this.entityLabel + '</a>').appendTo($gc)
var _this = this var _this = this
$item.on('click', function () { $item.on('click', function () {
RbFormModal.create({ title: $L('新建%s', _this.entityLabel), entity: _this.entity, icon: _this.icon }) RbFormModal.create({ title: $L('新建%s', _this.entityLabel), entity: _this.entity, icon: _this.icon, showExtraButton: true })
}) })
}) })
} }
@ -1479,3 +1479,14 @@ function $focus2End(el, delay) {
el.setSelectionRange(len, len) el.setSelectionRange(len, len)
}, delay || 100) }, delay || 100)
} }
// 获取实体元数据
function $fetchMetaInfo(name, cb) {
$.get('/commons/metadata/meta-info?name=' + $encode(name), function (res) {
if (res.error_code === 0) {
typeof cb === 'function' && cb(res.data || {})
} else {
RbHighbar.error(res.error_msg)
}
})
}

View file

@ -211,7 +211,7 @@
<option value="macarons">MACARONS</option> <option value="macarons">MACARONS</option>
</select> </select>
</div> </div>
<div class="hide bosskey-show-- J_opt-INDEX J_opt-LINE J_opt-BAR J_opt-BAR2 J_opt-BAR3 J_opt-PIE J_opt-FUNNEL J_opt-RADAR J_opt-SCATTER"> <div class="hide bosskey-show-- J_opt-TABLE J_opt-INDEX J_opt-LINE J_opt-BAR J_opt-BAR2 J_opt-BAR3 J_opt-PIE J_opt-FUNNEL J_opt-RADAR J_opt-SCATTER">
<label style="color: #444; margin-bottom: 8px">[[${bundle.L('背景色')}]]</label> <label style="color: #444; margin-bottom: 8px">[[${bundle.L('背景色')}]]</label>
<div id="useBgcolor" class="rbcolors"> <div id="useBgcolor" class="rbcolors">
<a class="default" th:title="${bundle.L('默认')}"></a> <a class="default" th:title="${bundle.L('默认')}"></a>