mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 15:35:55 +08:00
form: n2n supports
This commit is contained in:
parent
797cf6839d
commit
3f3e09315d
2
pom.xml
2
pom.xml
|
@ -257,7 +257,7 @@
|
|||
<dependency>
|
||||
<groupId>com.github.devezhao</groupId>
|
||||
<artifactId>persist4j</artifactId>
|
||||
<version>3854adcc15</version>
|
||||
<version>6957ebec45</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cglib</groupId>
|
||||
|
|
|
@ -372,6 +372,7 @@ public class FormsBuilder extends FormsManager {
|
|||
if (el.get("value") == null) {
|
||||
if (dt == DisplayType.SERIES) {
|
||||
el.put("value", autoValue);
|
||||
|
||||
} else {
|
||||
Object defVal = FieldDefaultValueHelper.exprDefaultValue(fieldMeta);
|
||||
if (defVal != null) {
|
||||
|
@ -389,6 +390,10 @@ public class FormsBuilder extends FormsManager {
|
|||
defVal = null;
|
||||
}
|
||||
}
|
||||
// 多引用
|
||||
else if (dt == DisplayType.N2NREFERENCE) {
|
||||
defVal = FieldValueWrapper.instance.wrapN2NReference(defVal, EasyMeta.valueOf(fieldMeta));
|
||||
}
|
||||
|
||||
el.put("value", defVal);
|
||||
}
|
||||
|
|
|
@ -61,7 +61,8 @@ public class RecentlyUsedHelper {
|
|||
List<ID> data = new ArrayList<>();
|
||||
for (int i = 0; i < limit && i < cached.size(); i++) {
|
||||
final ID raw = cached.get(i);
|
||||
if (!(raw.getEntityCode() == EntityHelper.ClassificationData || Application.getPrivilegesManager().allowRead(user, raw))) {
|
||||
if (!(raw.getEntityCode() == EntityHelper.ClassificationData
|
||||
|| Application.getPrivilegesManager().allowRead(user, raw))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,8 +24,10 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -34,6 +36,7 @@ import java.util.regex.Pattern;
|
|||
*
|
||||
* @author devezhao
|
||||
* @since 2019/8/20
|
||||
* @see FieldValueWrapper
|
||||
*/
|
||||
public class FieldDefaultValueHelper {
|
||||
|
||||
|
@ -115,6 +118,15 @@ public class FieldDefaultValueHelper {
|
|||
} else if (dt == DisplayType.REFERENCE || dt == DisplayType.CLASSIFICATION) {
|
||||
return ID.valueOf(valueExpr);
|
||||
|
||||
} else if (dt == DisplayType.N2NREFERENCE) {
|
||||
String[] ids = valueExpr.split(",");
|
||||
|
||||
List<ID> idArray = new ArrayList<>();
|
||||
for (String id : ids) {
|
||||
if (ID.isId(id)) idArray.add(ID.valueOf(id));
|
||||
}
|
||||
return idArray.toArray(new ID[0]);
|
||||
|
||||
} else if (dt == DisplayType.BOOL) {
|
||||
return BooleanUtils.toBoolean(valueExpr);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import cn.devezhao.persist4j.Field;
|
|||
import cn.devezhao.persist4j.engine.ID;
|
||||
import cn.devezhao.persist4j.metadata.MetadataException;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.configuration.general.ClassificationManager;
|
||||
|
@ -118,6 +119,8 @@ public class FieldValueWrapper {
|
|||
return wrapDecimal(value, field);
|
||||
} else if (dt == DisplayType.REFERENCE) {
|
||||
return wrapReference(value, field);
|
||||
} else if (dt == DisplayType.N2NREFERENCE) {
|
||||
return wrapN2NReference(value, field);
|
||||
} else if (dt == DisplayType.BOOL) {
|
||||
return wrapBool(value, field);
|
||||
} else if (dt == DisplayType.PICKLIST) {
|
||||
|
@ -189,7 +192,7 @@ public class FieldValueWrapper {
|
|||
* @return
|
||||
* @see #wrapMixValue(ID, String)
|
||||
*/
|
||||
public JSON wrapReference(Object value, EasyMeta field) {
|
||||
public JSONObject wrapReference(Object value, EasyMeta field) {
|
||||
Object text = ((ID) value).getLabelRaw();
|
||||
if (text == null) {
|
||||
text = getLabelNotry((ID) value);
|
||||
|
@ -202,6 +205,22 @@ public class FieldValueWrapper {
|
|||
return wrapMixValue((ID) value, text == null ? null : text.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value
|
||||
* @param field
|
||||
* @return
|
||||
* @see #wrapReference(Object, EasyMeta)
|
||||
*/
|
||||
public JSONArray wrapN2NReference(Object value, EasyMeta field) {
|
||||
ID[] ids = (ID[]) value;
|
||||
|
||||
JSONArray idArray = new JSONArray();
|
||||
for (ID id : ids) {
|
||||
idArray.add(wrapReference(id, field));
|
||||
}
|
||||
return idArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value
|
||||
* @param field
|
||||
|
|
|
@ -8,20 +8,21 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
package com.rebuild.web.general;
|
||||
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.api.RespBody;
|
||||
import com.rebuild.core.service.general.RecentlyUsedHelper;
|
||||
import com.rebuild.core.support.general.FieldValueWrapper;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import com.rebuild.web.BaseController;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 最近搜索(针对引用字段)。
|
||||
|
@ -30,36 +31,39 @@ import javax.servlet.http.HttpServletResponse;
|
|||
* @author devezhao zhaofang123@gmail.com
|
||||
* @since 2019/04/25
|
||||
*/
|
||||
@Controller
|
||||
@RestController
|
||||
@RequestMapping("/commons/search/")
|
||||
public class RecentlyUsedSearchController extends BaseController {
|
||||
|
||||
@GetMapping("recently")
|
||||
public void fetchRecently(HttpServletRequest request, HttpServletResponse response) {
|
||||
public JSON fetchRecently(HttpServletRequest request) {
|
||||
String entity = getParameterNotNull(request, "entity");
|
||||
String type = getParameter(request, "type");
|
||||
|
||||
ID[] recently = RecentlyUsedHelper.gets(getRequestUser(request), entity, type);
|
||||
writeSuccess(response, formatSelect2(recently, "最近使用"));
|
||||
return formatSelect2(recently, getLang(request, "RecentlyUsed"));
|
||||
}
|
||||
|
||||
@PostMapping("recently-add")
|
||||
public void addRecently(HttpServletRequest request, HttpServletResponse response) {
|
||||
public RespBody addRecently(HttpServletRequest request) {
|
||||
ID id = getIdParameterNotNull(request, "id");
|
||||
String type = getParameter(request, "type");
|
||||
|
||||
RecentlyUsedHelper.add(getRequestUser(request), id, type);
|
||||
writeSuccess(response);
|
||||
return RespBody.ok();
|
||||
}
|
||||
|
||||
@PostMapping("recently-clean")
|
||||
public void cleanRecently(HttpServletRequest request, HttpServletResponse response) {
|
||||
public RespBody cleanRecently(HttpServletRequest request) {
|
||||
String entity = getParameterNotNull(request, "entity");
|
||||
String type = getParameter(request, "type");
|
||||
|
||||
RecentlyUsedHelper.clean(getRequestUser(request), entity, type);
|
||||
writeSuccess(response);
|
||||
return RespBody.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化成前端 select2 组件数据格式
|
||||
* 格式化成前端 `select2` 组件数据格式
|
||||
*
|
||||
* @param idLabels
|
||||
* @param groupName select2 分组 null 表示无分组
|
||||
|
@ -74,8 +78,8 @@ public class RecentlyUsedSearchController extends BaseController {
|
|||
}
|
||||
|
||||
data.add(JSONUtils.toJSONObject(
|
||||
new String[]{"id", "text"},
|
||||
new String[]{id.toLiteral(), label}));
|
||||
new String[] { "id", "text" },
|
||||
new String[] { id.toLiteral(), label }));
|
||||
}
|
||||
|
||||
if (groupName != null) {
|
||||
|
|
|
@ -12,6 +12,7 @@ import cn.devezhao.persist4j.Field;
|
|||
import cn.devezhao.persist4j.dialect.FieldType;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.rebuild.api.RespBody;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.configuration.general.ClassificationManager;
|
||||
import com.rebuild.core.configuration.general.DataListManager;
|
||||
|
@ -21,18 +22,19 @@ import com.rebuild.core.metadata.impl.DisplayType;
|
|||
import com.rebuild.core.metadata.impl.EasyMeta;
|
||||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.core.service.NoRecordFoundException;
|
||||
import com.rebuild.core.service.general.RecentlyUsedHelper;
|
||||
import com.rebuild.core.service.query.ParseHelper;
|
||||
import com.rebuild.core.support.general.FieldValueWrapper;
|
||||
import com.rebuild.core.support.general.ProtocolFilterParser;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import com.rebuild.web.EntityController;
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import com.rebuild.web.EntityParam;
|
||||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -48,22 +50,19 @@ import java.util.*;
|
|||
* @see RecentlyUsedSearchController
|
||||
* @since 08/24/2018
|
||||
*/
|
||||
@Controller
|
||||
@RestController
|
||||
@RequestMapping("/commons/search/")
|
||||
public class ReferenceSearchController extends EntityController {
|
||||
|
||||
// 快速搜索引用字段
|
||||
@GetMapping({"reference", "quick"})
|
||||
public void referenceSearch(HttpServletRequest request, HttpServletResponse response) {
|
||||
public JSON referenceSearch(@EntityParam Entity entity, HttpServletRequest request) {
|
||||
final ID user = getRequestUser(request);
|
||||
final String entity = getParameterNotNull(request, "entity");
|
||||
final String field = getParameterNotNull(request, "field");
|
||||
|
||||
Entity metaEntity = MetadataHelper.getEntity(entity);
|
||||
Field referenceField = metaEntity.getField(field);
|
||||
if (referenceField.getType() != FieldType.REFERENCE) {
|
||||
writeSuccess(response, JSONUtils.EMPTY_ARRAY);
|
||||
return;
|
||||
Field referenceField = entity.getField(field);
|
||||
if (!(referenceField.getType() == FieldType.REFERENCE || referenceField.getType() == FieldType.REFERENCE_LIST)) {
|
||||
return JSONUtils.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
// 查询引用字段的实体
|
||||
|
@ -83,59 +82,49 @@ public class ReferenceSearchController extends EntityController {
|
|||
}
|
||||
|
||||
if (recently == null || recently.length == 0) {
|
||||
writeSuccess(response, JSONUtils.EMPTY_ARRAY);
|
||||
return JSONUtils.EMPTY_ARRAY;
|
||||
} else {
|
||||
writeSuccess(response, RecentlyUsedSearchController.formatSelect2(recently, null));
|
||||
return RecentlyUsedSearchController.formatSelect2(recently, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询字段
|
||||
Set<String> searchFields = ParseHelper.buildQuickFields(searchEntity, getParameter(request, "quickFields"));
|
||||
if (searchFields.isEmpty()) {
|
||||
LOG.warn("No fields of search found : " + searchEntity);
|
||||
writeSuccess(response, JSONUtils.EMPTY_ARRAY);
|
||||
return;
|
||||
}
|
||||
|
||||
q = StringEscapeUtils.escapeSql(q);
|
||||
String like = " like '%" + q + "%'";
|
||||
String searchWhere = StringUtils.join(searchFields.iterator(), like + " or ") + like;
|
||||
if (protocolFilter != null) {
|
||||
searchWhere = "(" + searchWhere + ") and (" + protocolFilter + ')';
|
||||
}
|
||||
|
||||
List<Object> result = resultSearch(searchWhere, searchEntity, true);
|
||||
writeSuccess(response, result);
|
||||
return buildResultSearch(searchEntity, getParameter(request, "quickFields"), q);
|
||||
}
|
||||
|
||||
// 搜索指定实体的指定字段
|
||||
@GetMapping("search")
|
||||
public void search(HttpServletRequest request, HttpServletResponse response) {
|
||||
public JSON search(@EntityParam Entity searchEntity, HttpServletRequest request) {
|
||||
final ID user = getRequestUser(request);
|
||||
final String entity = getParameterNotNull(request, "entity");
|
||||
|
||||
String q = getParameter(request, "q");
|
||||
// 为空则加载最近使用的
|
||||
if (StringUtils.isBlank(q)) {
|
||||
String type = getParameter(request, "type");
|
||||
ID[] recently = RecentlyUsedHelper.gets(user, entity, type);
|
||||
ID[] recently = RecentlyUsedHelper.gets(user, searchEntity.getName(), type);
|
||||
if (recently.length == 0) {
|
||||
writeSuccess(response, JSONUtils.EMPTY_ARRAY);
|
||||
return JSONUtils.EMPTY_ARRAY;
|
||||
} else {
|
||||
writeSuccess(response, RecentlyUsedSearchController.formatSelect2(recently, null));
|
||||
return RecentlyUsedSearchController.formatSelect2(recently, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final Entity searchEntity = MetadataHelper.getEntity(entity);
|
||||
return buildResultSearch(searchEntity, getParameter(request, "quickFields"), q);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建查询
|
||||
*
|
||||
* @param searchEntity
|
||||
* @param quickFields
|
||||
* @param q
|
||||
* @return
|
||||
*/
|
||||
private JSON buildResultSearch(Entity searchEntity, String quickFields, String q) {
|
||||
// 查询字段
|
||||
Set<String> searchFields = ParseHelper.buildQuickFields(searchEntity, getParameter(request, "quickFields"));
|
||||
Set<String> searchFields = ParseHelper.buildQuickFields(searchEntity, quickFields);
|
||||
if (searchFields.isEmpty()) {
|
||||
LOG.warn("No fields of search found : " + searchEntity);
|
||||
writeSuccess(response, ArrayUtils.EMPTY_STRING_ARRAY);
|
||||
return;
|
||||
return JSONUtils.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
q = StringEscapeUtils.escapeSql(q);
|
||||
|
@ -143,17 +132,16 @@ public class ReferenceSearchController extends EntityController {
|
|||
String searchWhere = StringUtils.join(searchFields.iterator(), like + " or ") + like;
|
||||
|
||||
List<Object> result = resultSearch(searchWhere, searchEntity, true);
|
||||
writeSuccess(response, result);
|
||||
return (JSON) JSON.toJSON(result);
|
||||
}
|
||||
|
||||
// 搜索分类字段
|
||||
@GetMapping("classification")
|
||||
public void searchClassification(HttpServletRequest request, HttpServletResponse response) {
|
||||
public JSON searchClassification(@EntityParam Entity entity, HttpServletRequest request) {
|
||||
final ID user = getRequestUser(request);
|
||||
final String entity = getParameterNotNull(request, "entity");
|
||||
final String field = getParameterNotNull(request, "field");
|
||||
|
||||
Field fieldMeta = MetadataHelper.getField(entity, field);
|
||||
Field fieldMeta = entity.getField(field);
|
||||
ID useClassification = ClassificationManager.instance.getUseClassification(fieldMeta, false);
|
||||
|
||||
String q = getParameter(request, "q");
|
||||
|
@ -162,12 +150,12 @@ public class ReferenceSearchController extends EntityController {
|
|||
String type = "d" + useClassification;
|
||||
ID[] recently = RecentlyUsedHelper.gets(user, "ClassificationData", type);
|
||||
if (recently.length == 0) {
|
||||
writeSuccess(response, JSONUtils.EMPTY_ARRAY);
|
||||
return JSONUtils.EMPTY_ARRAY;
|
||||
} else {
|
||||
writeSuccess(response, RecentlyUsedSearchController.formatSelect2(recently, null));
|
||||
return RecentlyUsedSearchController.formatSelect2(recently, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
q = StringEscapeUtils.escapeSql(q);
|
||||
|
||||
int openLevel = ClassificationManager.instance.getOpenLevel(fieldMeta);
|
||||
|
@ -176,7 +164,7 @@ public class ReferenceSearchController extends EntityController {
|
|||
useClassification.toLiteral(), openLevel, q, q);
|
||||
|
||||
List<Object> result = resultSearch(sqlWhere, MetadataHelper.getEntity(EntityHelper.ClassificationData), false);
|
||||
writeSuccess(response, result);
|
||||
return (JSON) JSON.toJSON(result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -224,22 +212,34 @@ public class ReferenceSearchController extends EntityController {
|
|||
|
||||
// 获取记录的名称字段值
|
||||
@GetMapping("read-labels")
|
||||
public void referenceLabel(HttpServletRequest request, HttpServletResponse response) {
|
||||
public RespBody referenceLabel(HttpServletRequest request) {
|
||||
String ids = getParameter(request, "ids", null);
|
||||
if (ids == null) {
|
||||
writeSuccess(response);
|
||||
return;
|
||||
if (StringUtils.isBlank(ids)) {
|
||||
return RespBody.ok();
|
||||
}
|
||||
|
||||
// 不存在的记录不返回
|
||||
boolean ignoreMiss = getBoolParameter(request, "ignoreMiss", false);
|
||||
|
||||
Map<String, String> labels = new HashMap<>();
|
||||
for (String id : ids.split("\\|")) {
|
||||
if (!ID.isId(id)) {
|
||||
continue;
|
||||
for (String id : ids.split("[|,]")) {
|
||||
if (!ID.isId(id)) continue;
|
||||
|
||||
String label;
|
||||
if (ignoreMiss) {
|
||||
try {
|
||||
label = FieldValueWrapper.getLabel(ID.valueOf(id));
|
||||
labels.put(id, label);
|
||||
} catch (NoRecordFoundException ignored) {
|
||||
}
|
||||
|
||||
} else {
|
||||
label = FieldValueWrapper.getLabelNotry(ID.valueOf(id));
|
||||
labels.put(id, label);
|
||||
}
|
||||
String label = FieldValueWrapper.getLabelNotry(ID.valueOf(id));
|
||||
labels.put(id, label);
|
||||
}
|
||||
writeSuccess(response, labels);
|
||||
|
||||
return RespBody.ok(labels);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1211,6 +1211,10 @@
|
|||
"NoInitEntityTips": "暂无可用业务实体。此安装步骤不是必须的,你仍可以继续安装",
|
||||
"SelectInitEntityTips": "你可以选择需要的业务实体使用,或在安装完成后再进行添加",
|
||||
"CantSelectInitEntityTips": "由于使用了已存在的 RB 数据库,因此此步骤不可用,你仍可以继续安装",
|
||||
"DialPhone": "拨打电话",
|
||||
"UnConf": "未配置",
|
||||
"SomeUnConf": "{0}未配置",
|
||||
"RecentlyUsed": "Recently",
|
||||
|
||||
"s.__": "状态",
|
||||
"s.ApprovalState.DRAFT": "草稿",
|
||||
|
|
|
@ -298,7 +298,7 @@
|
|||
<input
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
maxlength="200"
|
||||
maxlength="420"
|
||||
class="form-control form-control-sm J_defaultValue"
|
||||
th:data-o="${fieldDefaultValue}"
|
||||
th:value="${fieldDefaultValue}"
|
||||
|
|
|
@ -700,6 +700,17 @@ body.view-body {
|
|||
color: #4285f4;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--multiple::after {
|
||||
content: '\f2f9';
|
||||
font-family: 'Material-Design-Iconic-Font';
|
||||
font-size: 1.6rem;
|
||||
font-weight: 400;
|
||||
line-height: 35px;
|
||||
color: #404040;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
textarea.row2x {
|
||||
height: 52px !important;
|
||||
resize: none;
|
||||
|
@ -998,11 +1009,11 @@ a {
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.datetime-field .input-group-append button {
|
||||
.input-group.has-append .input-group-append button {
|
||||
min-width: 38px;
|
||||
}
|
||||
|
||||
.datetime-field .clean {
|
||||
.input-group.has-append .clean {
|
||||
position: absolute;
|
||||
right: 45px;
|
||||
top: 0;
|
||||
|
|
|
@ -9,7 +9,7 @@ const wpc = window.__PageConfig
|
|||
const __gExtConfig = {}
|
||||
|
||||
const SHOW_REPEATABLE = ['TEXT', 'DATE', 'DATETIME', 'EMAIL', 'URL', 'PHONE', 'REFERENCE', 'CLASSIFICATION']
|
||||
const SHOW_DEFAULTVALUE = ['TEXT', 'NTEXT', 'EMAIL', 'PHONE', 'URL', 'NUMBER', 'DECIMAL', 'DATE', 'DATETIME', 'BOOL', 'CLASSIFICATION', 'REFERENCE']
|
||||
const SHOW_DEFAULTVALUE = ['TEXT', 'NTEXT', 'EMAIL', 'PHONE', 'URL', 'NUMBER', 'DECIMAL', 'DATE', 'DATETIME', 'BOOL', 'CLASSIFICATION', 'REFERENCE', 'N2NREFERENCE']
|
||||
|
||||
$(document).ready(function () {
|
||||
const dt = wpc.fieldType
|
||||
|
@ -28,7 +28,14 @@ $(document).ready(function () {
|
|||
}
|
||||
if (data.fieldLabel === '') return RbHighbar.create($L('PlsInputSome,FieldName'))
|
||||
|
||||
const dv = dt === 'CLASSIFICATION' || dt === 'REFERENCE' ? $('.J_defaultValue').attr('data-value-id') : $val('.J_defaultValue')
|
||||
// 默认值
|
||||
let dv = $val('.J_defaultValue')
|
||||
if (dt === 'CLASSIFICATION' || dt === 'REFERENCE' || dt === 'N2NREFERENCE') {
|
||||
dv = $('.J_defaultValue').attr('data-value-id') || ''
|
||||
const odv = $('.J_defaultValue').attr('data-o') || ''
|
||||
if (dv === odv) dv = null
|
||||
}
|
||||
|
||||
if (dv) {
|
||||
if (checkDefaultValue(dv, dt) === false) return
|
||||
else data.defaultValue = dv
|
||||
|
@ -114,6 +121,8 @@ $(document).ready(function () {
|
|||
_handleClassification(extConfig)
|
||||
} else if (dt === 'REFERENCE') {
|
||||
_handleReference()
|
||||
} else if (dt === 'N2NREFERENCE') {
|
||||
_handleReference(true)
|
||||
} else if (dt === 'BOOL') {
|
||||
const $dv = $('.J_defaultValue')
|
||||
if ($dv.data('o')) $dv.val($dv.data('o'))
|
||||
|
@ -253,65 +262,6 @@ class AdvDateDefaultValue extends RbAlert {
|
|||
}
|
||||
}
|
||||
|
||||
const _handleReference = function () {
|
||||
const referenceEntity = $('.J_referenceEntity').data('refentity')
|
||||
|
||||
let dataFilter = (wpc.extConfig || {}).referenceDataFilter
|
||||
const saveFilter = function (res) {
|
||||
if (res && res.items && res.items.length > 0) {
|
||||
$('#referenceDataFilter').text(`${$L('AdvFiletrSeted')} (${res.items.length})`)
|
||||
dataFilter = res
|
||||
} else {
|
||||
$('#referenceDataFilter').text($L('ClickSet'))
|
||||
dataFilter = null
|
||||
}
|
||||
__gExtConfig.referenceDataFilter = dataFilter
|
||||
}
|
||||
dataFilter && saveFilter(dataFilter)
|
||||
|
||||
let advFilter
|
||||
$('#referenceDataFilter').click(() => {
|
||||
if (advFilter) {
|
||||
advFilter.show()
|
||||
} else {
|
||||
renderRbcomp(<AdvFilter title={$L('SetAdvFiletr')} inModal={true} canNoFilters={true} entity={referenceEntity} filter={dataFilter} confirm={saveFilter} />, null, function () {
|
||||
advFilter = this
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 默认值
|
||||
const $dv = $('.J_defaultValue')
|
||||
const $dvClear = $('.J_defaultValue-clear')
|
||||
|
||||
let _ReferenceSearcher
|
||||
function _showSearcher() {
|
||||
if (_ReferenceSearcher) {
|
||||
_ReferenceSearcher.show()
|
||||
} else {
|
||||
const searchUrl = `${rb.baseUrl}/commons/search/reference-search?field=${wpc.fieldName}.${wpc.entityName}`
|
||||
// eslint-disable-next-line react/jsx-no-undef
|
||||
renderRbcomp(<ReferenceSearcher url={searchUrl} title={$L('SelectSome,DefaultValue')} />, function () {
|
||||
_ReferenceSearcher = this
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const $append = $(`<button class="btn btn-secondary mw-auto" type="button" title="${$L('SelectSome,DefaultValue')}"><i class="icon zmdi zmdi-search"></i></button>`).appendTo(
|
||||
'.J_defaultValue-append'
|
||||
)
|
||||
$append.click(() => _showSearcher())
|
||||
|
||||
window.referenceSearch__call = function (s) {
|
||||
s = s[0]
|
||||
$dv.attr('data-value-id', s).val(s)
|
||||
_loadRefLabel($dv, $dvClear)
|
||||
_ReferenceSearcher.hide()
|
||||
}
|
||||
|
||||
_loadRefLabel($dv, $dvClear)
|
||||
}
|
||||
|
||||
const _handlePicklist = function (dt) {
|
||||
$.get(`/admin/field/picklist-gets?entity=${wpc.entityName}&field=${wpc.fieldName}&isAll=false`, function (res) {
|
||||
if (res.data.length === 0) {
|
||||
|
@ -382,7 +332,10 @@ const _handleFile = function (extConfig) {
|
|||
|
||||
const _handleClassification = function (extConfig) {
|
||||
const $dv = $('.J_defaultValue')
|
||||
const $dvClear = $('.J_defaultValue-clear')
|
||||
const $dvClear = $('.J_defaultValue-clear').click(() => {
|
||||
$dv.attr('data-value-id', '').val('')
|
||||
$dvClear.addClass('hide')
|
||||
})
|
||||
|
||||
let _ClassificationSelector
|
||||
function _showSelector(data) {
|
||||
|
@ -422,19 +375,103 @@ const _handleClassification = function (extConfig) {
|
|||
$append.click(() => _showSelector(res.data))
|
||||
})
|
||||
|
||||
_loadRefLabel($dv, $dvClear)
|
||||
_loadRefsLabel($dv, $dvClear)
|
||||
}
|
||||
|
||||
const _loadRefLabel = function ($dv, $dvClear) {
|
||||
const _handleReference = function (isN2N) {
|
||||
const referenceEntity = $('.J_referenceEntity').data('refentity')
|
||||
|
||||
// 数据过滤
|
||||
let dataFilter = (wpc.extConfig || {}).referenceDataFilter
|
||||
const saveFilter = function (res) {
|
||||
if (res && res.items && res.items.length > 0) {
|
||||
$('#referenceDataFilter').text(`${$L('AdvFiletrSeted')} (${res.items.length})`)
|
||||
dataFilter = res
|
||||
} else {
|
||||
$('#referenceDataFilter').text($L('ClickSet'))
|
||||
dataFilter = null
|
||||
}
|
||||
__gExtConfig.referenceDataFilter = dataFilter
|
||||
}
|
||||
dataFilter && saveFilter(dataFilter)
|
||||
|
||||
let advFilter
|
||||
$('#referenceDataFilter').click(() => {
|
||||
if (advFilter) {
|
||||
advFilter.show()
|
||||
} else {
|
||||
renderRbcomp(<AdvFilter title={$L('SetAdvFiletr')} inModal={true} canNoFilters={true} entity={referenceEntity} filter={dataFilter} confirm={saveFilter} />, null, function () {
|
||||
advFilter = this
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 默认值
|
||||
const $dv = $('.J_defaultValue')
|
||||
const $dvClear = $('.J_defaultValue-clear').click(() => {
|
||||
$dv.attr('data-value-id', '').val('')
|
||||
$dvClear.addClass('hide')
|
||||
})
|
||||
|
||||
let _ReferenceSearcher
|
||||
function _showSearcher() {
|
||||
if (_ReferenceSearcher) {
|
||||
_ReferenceSearcher.show()
|
||||
} else {
|
||||
const searchUrl = `${rb.baseUrl}/commons/search/reference-search?field=${wpc.fieldName}.${wpc.entityName}`
|
||||
// eslint-disable-next-line react/jsx-no-undef
|
||||
renderRbcomp(<ReferenceSearcher url={searchUrl} title={$L('SelectSome,DefaultValue')} />, function () {
|
||||
_ReferenceSearcher = this
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const $append = $(`<button class="btn btn-secondary mw-auto" type="button" title="${$L('SelectSome,DefaultValue')}"><i class="icon zmdi zmdi-search"></i></button>`).appendTo(
|
||||
'.J_defaultValue-append'
|
||||
)
|
||||
$dv.attr('readonly', true)
|
||||
$append.click(() => _showSearcher())
|
||||
|
||||
window.referenceSearch__call = function (selected) {
|
||||
let val
|
||||
if (isN2N) {
|
||||
let keepVal = $dv.attr('data-value-id')
|
||||
if (keepVal) keepVal = keepVal.split(',')
|
||||
else keepVal = []
|
||||
|
||||
selected.forEach((s) => {
|
||||
if (!keepVal.contains(s)) keepVal.push(s)
|
||||
})
|
||||
val = keepVal.slice(0, 20).join(',')
|
||||
} else {
|
||||
val = selected[0]
|
||||
}
|
||||
|
||||
$dv.attr('data-value-id', val).val(val)
|
||||
_loadRefsLabel($dv, $dvClear)
|
||||
_ReferenceSearcher.hide()
|
||||
}
|
||||
|
||||
_loadRefsLabel($dv, $dvClear)
|
||||
}
|
||||
|
||||
const _loadRefsLabel = function ($dv, $dvClear) {
|
||||
const dvid = $dv.val()
|
||||
if (dvid) {
|
||||
$.get(`/commons/search/read-labels?ids=${dvid}`, (res) => {
|
||||
if (res.data && res.data[dvid]) $dv.val(res.data[dvid])
|
||||
$.get(`/commons/search/read-labels?ids=${dvid}&ignoreMiss=true`, (res) => {
|
||||
if (res.data) {
|
||||
const ids = []
|
||||
const labels = []
|
||||
for (let k in res.data) {
|
||||
ids.push(k)
|
||||
labels.push(res.data[k])
|
||||
}
|
||||
|
||||
$dv.attr('data-value-id', ids.join(','))
|
||||
$dv.val(labels.join(', '))
|
||||
}
|
||||
})
|
||||
|
||||
$dvClear.removeClass('hide').click(() => {
|
||||
$dv.attr('data-value-id', '').val('')
|
||||
$dvClear.addClass('hide')
|
||||
})
|
||||
$dvClear && $dvClear.removeClass('hide')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -193,3 +193,105 @@ class ReferenceSearcher extends RbModal {
|
|||
window.referenceSearch__dlg = this
|
||||
}
|
||||
}
|
||||
|
||||
// 删除确认
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
class DeleteConfirm extends RbAlert {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = { enableCascades: false }
|
||||
}
|
||||
|
||||
render() {
|
||||
let message = this.props.message
|
||||
if (!message) message = this.props.ids ? $L('DeleteSelectedSomeConfirm').replace('%d', this.props.ids.length) : $L('DeleteRecordConfirm')
|
||||
|
||||
return (
|
||||
<div className="modal rbalert" ref={(c) => (this._dlg = c)} tabIndex="-1">
|
||||
<div className="modal-dialog modal-dialog-centered">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header pb-0">
|
||||
<button className="close" type="button" onClick={() => this.hide()}>
|
||||
<span className="zmdi zmdi-close" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="text-center ml-6 mr-6">
|
||||
<div className="text-danger">
|
||||
<span className="modal-main-icon zmdi zmdi-alert-triangle" />
|
||||
</div>
|
||||
<div className="mt-3 text-bold">{message}</div>
|
||||
{!this.props.entity ? null : (
|
||||
<div className="mt-2">
|
||||
<label className="custom-control custom-control-sm custom-checkbox custom-control-inline mb-2">
|
||||
<input className="custom-control-input" type="checkbox" checked={this.state.enableCascade === true} onChange={() => this.enableCascade()} />
|
||||
<span className="custom-control-label"> {$L('DeleteCasTips')}</span>
|
||||
</label>
|
||||
<div className={' ' + (this.state.enableCascade ? '' : 'hide')}>
|
||||
<select className="form-control form-control-sm" ref={(c) => (this._cascades = c)} multiple>
|
||||
{(this.state.cascadesEntity || []).map((item) => {
|
||||
return (
|
||||
<option key={`opt-${item[0]}`} value={item[0]}>
|
||||
{item[1]}
|
||||
</option>
|
||||
)
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-4 mb-3" ref={(c) => (this._btns = c)}>
|
||||
<button className="btn btn-space btn-secondary" type="button" onClick={() => this.hide()}>
|
||||
{$L('Cancel')}
|
||||
</button>
|
||||
<button className="btn btn-space btn-danger" type="button" onClick={() => this.handleDelete()}>
|
||||
{$L('Delete')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
enableCascade() {
|
||||
this.setState({ enableCascade: !this.state.enableCascade })
|
||||
if (!this.state.cascadesEntity) {
|
||||
$.get(`/commons/metadata/references?entity=${this.props.entity}&permission=D`, (res) => {
|
||||
this.setState({ cascadesEntity: res.data }, () => {
|
||||
this.__select2 = $(this._cascades)
|
||||
.select2({
|
||||
placeholder: $L('SelectCasEntity'),
|
||||
width: '88%',
|
||||
})
|
||||
.val(null)
|
||||
.trigger('change')
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleDelete() {
|
||||
let ids = this.props.ids || this.props.id
|
||||
if (!ids || ids.length === 0) return
|
||||
if (typeof ids === 'object') ids = ids.join(',')
|
||||
const cascades = this.__select2 ? this.__select2.val().join(',') : ''
|
||||
|
||||
const $btns = $(this._btns).find('.btn').button('loading')
|
||||
$.post(`/app/entity/record-delete?id=${ids}&cascades=${cascades}`, (res) => {
|
||||
if (res.error_code === 0) {
|
||||
if (res.data.deleted === res.data.requests) RbHighbar.success($L('SomeSuccess', 'Delete'))
|
||||
else if (res.data.deleted === 0) RbHighbar.error($L('NotDeleteTips'))
|
||||
else RbHighbar.success($L('SuccessDeletedXItems').replace('%d', res.data.deleted))
|
||||
|
||||
this.hide()
|
||||
typeof this.props.deleteAfter === 'function' && this.props.deleteAfter()
|
||||
} else {
|
||||
RbHighbar.error(res.error_msg)
|
||||
$btns.button('reset')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,6 +156,7 @@ class RbFormModal extends React.Component {
|
|||
// -- Usage
|
||||
/**
|
||||
* @param {*} props
|
||||
* @param {*} newDlg
|
||||
*/
|
||||
static create(props, newDlg) {
|
||||
if (newDlg === true) {
|
||||
|
@ -418,7 +419,7 @@ class RbFormElement extends React.Component {
|
|||
{editable && this.state.editMode && (
|
||||
<div className="edit-oper">
|
||||
<div className="btn-group shadow-sm">
|
||||
<button type="button" className="btn btn-secondary" onClick={this.handleEditConfirm}>
|
||||
<button type="button" className="btn btn-secondary" onClick={() => this.handleEditConfirm()}>
|
||||
<i className="icon zmdi zmdi-check"></i>
|
||||
</button>
|
||||
<button type="button" className="btn btn-secondary" onClick={() => this.toggleEditMode(false)}>
|
||||
|
@ -432,35 +433,6 @@ class RbFormElement extends React.Component {
|
|||
)
|
||||
}
|
||||
|
||||
// 渲染表单
|
||||
renderElement() {
|
||||
const value = arguments.length > 0 ? arguments[0] : this.state.value
|
||||
return (
|
||||
<input
|
||||
ref={(c) => (this._fieldValue = c)}
|
||||
className={`form-control form-control-sm ${this.state.hasError ? 'is-invalid' : ''}`}
|
||||
title={this.state.hasError}
|
||||
type="text"
|
||||
value={value || ''}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.props.readonly ? null : this.checkValue}
|
||||
readOnly={this.props.readonly}
|
||||
maxLength={this.props.maxLength || 200}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// 渲染视图
|
||||
renderViewElement() {
|
||||
let text = arguments.length > 0 ? arguments[0] : this.state.value
|
||||
if (text && $empty(text)) text = null
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="form-control-plaintext">{text || <span className="text-muted">无</span>}</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const props = this.props
|
||||
if (!props.onView) {
|
||||
|
@ -475,6 +447,97 @@ class RbFormElement extends React.Component {
|
|||
this.onEditModeChanged(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染表单
|
||||
*/
|
||||
renderElement() {
|
||||
const value = arguments.length > 0 ? arguments[0] : this.state.value
|
||||
return (
|
||||
<input
|
||||
ref={(c) => (this._fieldValue = c)}
|
||||
className={`form-control form-control-sm ${this.state.hasError ? 'is-invalid' : ''}`}
|
||||
title={this.state.hasError}
|
||||
type="text"
|
||||
value={value || ''}
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.props.readonly ? null : this.checkValue}
|
||||
readOnly={this.props.readonly}
|
||||
maxLength={this.props.maxLength || 200}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染视图
|
||||
*/
|
||||
renderViewElement() {
|
||||
let text = arguments.length > 0 ? arguments[0] : this.state.value
|
||||
if (text && $empty(text)) text = null
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="form-control-plaintext">{text || <span className="text-muted">无</span>}</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改值(表单组件(字段)值变化应调用此方法)
|
||||
*
|
||||
* @param {*} e
|
||||
* @param {*} checkValue
|
||||
*/
|
||||
handleChange(e, checkValue) {
|
||||
const val = e.target.value
|
||||
this.setState({ value: val }, () => checkValue === true && this.checkValue())
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空值
|
||||
*/
|
||||
handleClear() {
|
||||
this.setState({ value: '' }, () => this.checkValue())
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查值
|
||||
*/
|
||||
checkValue() {
|
||||
const err = this.isValueError()
|
||||
const errMsg = err ? this.props.label + err : null
|
||||
|
||||
if (this.isValueUnchanged() && !this.props.$$$parent.isNew) {
|
||||
if (err) this.props.$$$parent.setFieldValue(this.props.field, this.state.value, errMsg)
|
||||
else this.props.$$$parent.setFieldUnchanged(this.props.field)
|
||||
} else {
|
||||
this.setState({ hasError: err })
|
||||
this.props.$$$parent.setFieldValue(this.props.field, this.state.value, errMsg)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 无效值检查
|
||||
*/
|
||||
isValueError() {
|
||||
if (this.props.nullable === false) {
|
||||
const v = this.state.value
|
||||
if (v && $.type(v) === 'array') return v.length === 0 ? $L('SomeNotEmpty').replace('{0}', '') : null
|
||||
else return !v ? $L('SomeNotEmpty').replace('{0}', '') : null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否未修改
|
||||
*/
|
||||
isValueUnchanged() {
|
||||
const oldv = this.state.newValue === undefined ? this.props.value : this.state.newValue
|
||||
return $same(oldv, this.state.value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 视图编辑-编辑状态改变
|
||||
*
|
||||
* @param {*} destroy
|
||||
*/
|
||||
onEditModeChanged(destroy) {
|
||||
if (destroy) {
|
||||
if (this.__select2) {
|
||||
|
@ -490,18 +553,11 @@ class RbFormElement extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
// 修改值(表单组件(字段)值变化应调用此方法)
|
||||
handleChange(e, checkValue) {
|
||||
const val = e.target.value
|
||||
this.setState({ value: val }, () => checkValue === true && this.checkValue())
|
||||
}
|
||||
|
||||
// 清空值
|
||||
handleClear() {
|
||||
this.setState({ value: '' }, () => this.checkValue())
|
||||
}
|
||||
|
||||
// 编辑单个字段值
|
||||
/**
|
||||
* 视图编辑-编辑模式
|
||||
*
|
||||
* @param {*} mode
|
||||
*/
|
||||
toggleEditMode(mode) {
|
||||
this.setState({ editMode: mode }, () => {
|
||||
if (this.state.editMode) {
|
||||
|
@ -514,44 +570,18 @@ class RbFormElement extends React.Component {
|
|||
})
|
||||
}
|
||||
|
||||
handleEditConfirm = () => {
|
||||
/**
|
||||
* 视图编辑-确认
|
||||
*/
|
||||
handleEditConfirm() {
|
||||
this.props.$$$parent.saveSingleFieldValue && this.props.$$$parent.saveSingleFieldValue(this)
|
||||
}
|
||||
|
||||
// 检查值
|
||||
checkValue() {
|
||||
const err = this.isValueError()
|
||||
const errMsg = err ? this.props.label + err : null
|
||||
|
||||
if (this.isValueUnchanged() && !this.props.$$$parent.isNew) {
|
||||
if (err) this.props.$$$parent.setFieldValue(this.props.field, this.state.value, errMsg)
|
||||
else this.props.$$$parent.setFieldUnchanged(this.props.field)
|
||||
} else {
|
||||
this.setState({ hasError: err })
|
||||
this.props.$$$parent.setFieldValue(this.props.field, this.state.value, errMsg)
|
||||
}
|
||||
}
|
||||
|
||||
// 无效值检查
|
||||
isValueError() {
|
||||
if (this.props.nullable === false) {
|
||||
const v = this.state.value
|
||||
if (v && $.type(v) === 'array') return v.length === 0 ? $L('SomeNotEmpty').replace('{0}', '') : null
|
||||
else return !v ? $L('SomeNotEmpty').replace('{0}', '') : null
|
||||
}
|
||||
}
|
||||
|
||||
// 未修改
|
||||
isValueUnchanged() {
|
||||
const oldv = this.state.newValue === undefined ? this.props.value : this.state.newValue
|
||||
return $same(oldv, this.state.value)
|
||||
}
|
||||
|
||||
// Getter / Setter
|
||||
// Getter
|
||||
setValue(val) {
|
||||
this.handleChange({ target: { value: val } }, true)
|
||||
}
|
||||
|
||||
// Setter
|
||||
getValue() {
|
||||
return this.state.value
|
||||
}
|
||||
|
@ -570,7 +600,8 @@ class RbFormUrl extends RbFormText {
|
|||
|
||||
renderViewElement() {
|
||||
if (!this.state.value) return super.renderViewElement()
|
||||
const clickUrl = rb.baseUrl + '/commons/url-safe?url=' + encodeURIComponent(this.state.value)
|
||||
|
||||
const clickUrl = `${rb.baseUrl}/commons/url-safe?url=${encodeURIComponent(this.state.value)}`
|
||||
return (
|
||||
<div className="form-control-plaintext">
|
||||
<a href={clickUrl} className="link" target="_blank" rel="noopener noreferrer">
|
||||
|
@ -594,9 +625,10 @@ class RbFormEMail extends RbFormText {
|
|||
|
||||
renderViewElement() {
|
||||
if (!this.state.value) return super.renderViewElement()
|
||||
|
||||
return (
|
||||
<div className="form-control-plaintext">
|
||||
<a title={$L('SendEmail')} href={'mailto:' + this.state.value} className="link">
|
||||
<a title={$L('SendEmail')} href={`mailto:${this.state.value}`} className="link">
|
||||
{this.state.value}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -617,9 +649,10 @@ class RbFormPhone extends RbFormText {
|
|||
|
||||
renderViewElement() {
|
||||
if (!this.state.value) return super.renderViewElement()
|
||||
|
||||
return (
|
||||
<div className="form-control-plaintext">
|
||||
<a title="拨打电话" href={'tel:' + this.state.value} className="link">
|
||||
<a title={$L('DialPhone')} href={`tel:${this.state.value}`} className="link">
|
||||
{this.state.value}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -646,7 +679,6 @@ class RbFormNumber extends RbFormText {
|
|||
if (!!this.state.value && $isTrue(this.props.notNegative) && parseFloat(this.state.value) < 0) return $L('SomeNotNegative').replace('{0}', '')
|
||||
return null
|
||||
}
|
||||
|
||||
_isValueError() {
|
||||
return super.isValueError()
|
||||
}
|
||||
|
@ -707,6 +739,7 @@ class RbFormTextarea extends RbFormElement {
|
|||
|
||||
renderViewElement() {
|
||||
if (!this.state.value) return super.renderViewElement()
|
||||
|
||||
return (
|
||||
<div className="form-control-plaintext" ref={(c) => (this._textarea = c)}>
|
||||
{this.state.value.split('\n').map((line, idx) => {
|
||||
|
@ -736,8 +769,9 @@ class RbFormDateTime extends RbFormElement {
|
|||
|
||||
renderElement() {
|
||||
if (this.props.readonly) return super.renderElement()
|
||||
|
||||
return (
|
||||
<div className="input-group datetime-field">
|
||||
<div className="input-group has-append">
|
||||
<input
|
||||
ref={(c) => (this._fieldValue = c)}
|
||||
className={'form-control form-control-sm ' + (this.state.hasError ? 'is-invalid' : '')}
|
||||
|
@ -783,6 +817,7 @@ class RbFormDateTime extends RbFormElement {
|
|||
const val = $(this).val()
|
||||
that.handleChange({ target: { value: val } }, true)
|
||||
})
|
||||
|
||||
$(this._fieldValue__icon).click(() => this.__datetimepicker.datetimepicker('show'))
|
||||
}
|
||||
}
|
||||
|
@ -812,7 +847,7 @@ class RbFormImage extends RbFormElement {
|
|||
return (
|
||||
<span key={'img-' + item}>
|
||||
<a title={$fileCutName(item)} className="img-thumbnail img-upload">
|
||||
<img src={`${rb.baseUrl}/filex/img/${item}?imageView2/2/w/100/interlace/1/q/100`} />
|
||||
<img src={`${rb.baseUrl}/filex/img/${item}?imageView2/2/w/100/interlace/1/q/100`} alt="IMG" />
|
||||
{!this.props.readonly && (
|
||||
<b title={$L('Remove')} onClick={() => this.removeItem(item)}>
|
||||
<span className="zmdi zmdi-close"></span>
|
||||
|
@ -845,7 +880,7 @@ class RbFormImage extends RbFormElement {
|
|||
return (
|
||||
<span key={'img-' + item}>
|
||||
<a title={$fileCutName(item)} onClick={() => (parent || window).RbPreview.create(value, idx)} className="img-thumbnail img-upload zoom-in">
|
||||
<img src={`${rb.baseUrl}/filex/img/${item}?imageView2/2/w/100/interlace/1/q/100`} />
|
||||
<img src={`${rb.baseUrl}/filex/img/${item}?imageView2/2/w/100/interlace/1/q/100`} alt="IMG" />
|
||||
</a>
|
||||
</span>
|
||||
)
|
||||
|
@ -980,13 +1015,13 @@ class RbFormPickList extends RbFormElement {
|
|||
renderElement() {
|
||||
if (this.props.readonly) return super.renderElement(__findOptionText(this.state.options, this.props.value))
|
||||
|
||||
const name = `${this.state.field}-opt-`
|
||||
const keyName = `${this.state.field}-opt-`
|
||||
return (
|
||||
<select ref={(c) => (this._fieldValue = c)} className="form-control form-control-sm" value={this.state.value || ''} onChange={this.handleChange}>
|
||||
<option value=""></option>
|
||||
{this.state.options.map((item) => {
|
||||
return (
|
||||
<option key={`${name}${item.id}`} value={item.id}>
|
||||
<option key={`${keyName}${item.id}`} value={item.id}>
|
||||
{item.text}
|
||||
</option>
|
||||
)
|
||||
|
@ -1037,10 +1072,10 @@ class RbFormReference extends RbFormElement {
|
|||
|
||||
const hasDataFilter = this.props.referenceDataFilter && (this.props.referenceDataFilter.items || []).length > 0
|
||||
return (
|
||||
<div className="input-group datetime-field">
|
||||
<select ref={(c) => (this._fieldValue = c)} className="form-control form-control-sm" title={hasDataFilter ? $L('FieldUseDataFilter') : null} />
|
||||
<div className="input-group has-append">
|
||||
<select ref={(c) => (this._fieldValue = c)} className="form-control form-control-sm" title={hasDataFilter ? $L('FieldUseDataFilter') : null} multiple={this._multiple === true} />
|
||||
<div className="input-group-append">
|
||||
<button className="btn btn-secondary" type="button" onClick={this.showSearcher}>
|
||||
<button className="btn btn-secondary" type="button" onClick={() => this.showSearcher()}>
|
||||
<i className="icon zmdi zmdi-search" />
|
||||
</button>
|
||||
</div>
|
||||
|
@ -1054,6 +1089,7 @@ class RbFormReference extends RbFormElement {
|
|||
|
||||
if (typeof value === 'string') return <div className="form-control-plaintext">{value}</div>
|
||||
if (!value.id) return <div className="form-control-plaintext">{value.text}</div>
|
||||
|
||||
return (
|
||||
<div className="form-control-plaintext">
|
||||
<a href={`#!/View/${value.entity}/${value.id}`} onClick={this._clickView}>
|
||||
|
@ -1062,6 +1098,9 @@ class RbFormReference extends RbFormElement {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
_renderViewElement() {
|
||||
super.renderViewElement()
|
||||
}
|
||||
|
||||
_clickView = (e) => window.RbViewPage && window.RbViewPage.clickView(e.target)
|
||||
|
||||
|
@ -1089,14 +1128,15 @@ class RbFormReference extends RbFormElement {
|
|||
|
||||
const val = this.state.value
|
||||
if (val) {
|
||||
const o = new Option(val.text, val.id, true, true)
|
||||
this.__select2.append(o).trigger('change')
|
||||
this.setValue(val)
|
||||
// const o = new Option(val.text, val.id, true, true)
|
||||
// this.__select2.append(o).trigger('change')
|
||||
}
|
||||
|
||||
const that = this
|
||||
this.__select2.on('change', function (e) {
|
||||
const v = e.target.value
|
||||
if (v) {
|
||||
const v = $(e.target).val()
|
||||
if (v && typeof v === 'string') {
|
||||
$.post(`/commons/search/recently-add?id=${v}`)
|
||||
that.triggerAutoFillin(v)
|
||||
}
|
||||
|
@ -1125,21 +1165,15 @@ class RbFormReference extends RbFormElement {
|
|||
const o = new Option(val.text, val.id, true, true)
|
||||
this.__select2.append(o)
|
||||
this.handleChange({ target: { value: val.id } }, true)
|
||||
} else this.__select2.val(null).trigger('change')
|
||||
} else {
|
||||
this.__select2.val(null).trigger('change')
|
||||
}
|
||||
}
|
||||
|
||||
showSearcher = () => {
|
||||
showSearcher() {
|
||||
const that = this
|
||||
window.referenceSearch__call = function (selected) {
|
||||
selected = selected[0]
|
||||
if ($(that._fieldValue).find(`option[value="${selected}"]`).length > 0) {
|
||||
that.__select2.val(selected).trigger('change')
|
||||
} else {
|
||||
$.get(`/commons/search/read-labels?ids=${selected}`, (res) => {
|
||||
const o = new Option(res.data[selected], selected, true, true)
|
||||
that.__select2.append(o).trigger('change')
|
||||
})
|
||||
}
|
||||
that.showSearcher_call(selected, that)
|
||||
that.__searcher.hide()
|
||||
}
|
||||
|
||||
|
@ -1153,6 +1187,77 @@ class RbFormReference extends RbFormElement {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
showSearcher_call(selected, that) {
|
||||
const first = selected[0]
|
||||
if ($(that._fieldValue).find(`option[value="${first}"]`).length > 0) {
|
||||
that.__select2.val(first).trigger('change')
|
||||
} else {
|
||||
$.get(`/commons/search/read-labels?ids=${first}`, (res) => {
|
||||
const o = new Option(res.data[first], first, true, true)
|
||||
that.__select2.append(o).trigger('change')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RbFormN2NReference extends RbFormReference {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this._multiple = true
|
||||
}
|
||||
|
||||
renderViewElement() {
|
||||
const value = this.state.value
|
||||
if ($empty(value)) return super._renderViewElement()
|
||||
|
||||
if (typeof value === 'string') return <div className="form-control-plaintext">{value}</div>
|
||||
if (!value.id) return <div className="form-control-plaintext">{value.text}</div>
|
||||
return (
|
||||
<div className="form-control-plaintext">
|
||||
<a href={`#!/View/${value.entity}/${value.id}`} onClick={this._clickView}>
|
||||
{value.text}
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
isValueUnchanged() {
|
||||
let oldvArray = this.state.newValue || this.props.value || []
|
||||
let oldv = []
|
||||
oldvArray.forEach((s) => oldv.push(s.id))
|
||||
return $same(oldv.join(','), this.state.value)
|
||||
}
|
||||
|
||||
handleChange(e, checkValue) {
|
||||
let val = e.target.value
|
||||
if (typeof val === 'object') val = val.join(',')
|
||||
this.setState({ value: val }, () => checkValue === true && this.checkValue())
|
||||
}
|
||||
|
||||
setValue(val) {
|
||||
if (val && val.length > 0) {
|
||||
const ids = []
|
||||
val.forEach((item) => {
|
||||
const o = new Option(item.text, item.id, true, true)
|
||||
this.__select2.append(o)
|
||||
ids.push(item.id)
|
||||
})
|
||||
this.handleChange({ target: { value: ids.join(',') } }, true)
|
||||
} else {
|
||||
this.__select2.val(null).trigger('change')
|
||||
}
|
||||
}
|
||||
|
||||
showSearcher_call(selected, that) {
|
||||
$.get(`/commons/search/read-labels?ids=${selected.join(',')}`, (res) => {
|
||||
const val = []
|
||||
for (let k in res.data) {
|
||||
val.push({ id: k, text: res.data[k] })
|
||||
}
|
||||
that.setValue(val)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class RbFormClassification extends RbFormElement {
|
||||
|
@ -1162,8 +1267,9 @@ class RbFormClassification extends RbFormElement {
|
|||
|
||||
renderElement() {
|
||||
if (this.props.readonly) return super.renderElement(this.props.value ? this.props.value.text : null)
|
||||
|
||||
return (
|
||||
<div className="input-group datetime-field">
|
||||
<div className="input-group has-append">
|
||||
<select ref={(c) => (this._fieldValue = c)} className="form-control form-control-sm" />
|
||||
<div className="input-group-append">
|
||||
<button className="btn btn-secondary" type="button" onClick={this.showSelector}>
|
||||
|
@ -1233,7 +1339,9 @@ class RbFormClassification extends RbFormElement {
|
|||
|
||||
_setClassificationValue(s) {
|
||||
if (!s.id) return
|
||||
|
||||
const data = this.__cached || {}
|
||||
|
||||
if (data[s.id]) {
|
||||
this.__select2.val(s.id).trigger('change')
|
||||
} else if (this._fieldValue) {
|
||||
|
@ -1251,16 +1359,16 @@ class RbFormMultiSelect extends RbFormElement {
|
|||
}
|
||||
|
||||
renderElement() {
|
||||
const name = `checkbox-${this.props.field}`
|
||||
const keyName = `checkbox-${this.props.field}-`
|
||||
return (
|
||||
<div className="mt-1" ref={(c) => (this._fieldValue__wrap = c)}>
|
||||
{(this.props.options || []).length === 0 && <div className="text-danger">{$L('NoData')}</div>}
|
||||
{(this.props.options || []).length === 0 && <div className="text-danger">{$L('UnConf')}</div>}
|
||||
{(this.props.options || []).map((item) => {
|
||||
return (
|
||||
<label key={name + item.mask} className="custom-control custom-checkbox custom-control-inline">
|
||||
<label key={keyName + item.mask} className="custom-control custom-checkbox custom-control-inline">
|
||||
<input
|
||||
className="custom-control-input"
|
||||
name={name}
|
||||
name={keyName}
|
||||
type="checkbox"
|
||||
checked={(this.state.value & item.mask) !== 0}
|
||||
value={item.mask}
|
||||
|
@ -1278,6 +1386,7 @@ class RbFormMultiSelect extends RbFormElement {
|
|||
renderViewElement() {
|
||||
const value = this.state.value
|
||||
if (!value) return super.renderViewElement()
|
||||
|
||||
return (
|
||||
<div className="form-control-plaintext">
|
||||
{__findMultiTexts(this.props.options, value).map((item) => {
|
||||
|
@ -1298,15 +1407,17 @@ class RbFormMultiSelect extends RbFormElement {
|
|||
.each(function () {
|
||||
maskValue += ~~$(this).val()
|
||||
})
|
||||
|
||||
this.handleChange({ target: { value: maskValue === 0 ? null : maskValue } }, true)
|
||||
}
|
||||
}
|
||||
|
||||
const BoolOptions = {
|
||||
T: $L('True'),
|
||||
F: $L('False'),
|
||||
}
|
||||
class RbFormBool extends RbFormElement {
|
||||
_Options = {
|
||||
T: $L('True'),
|
||||
F: $L('False'),
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
@ -1324,7 +1435,7 @@ class RbFormBool extends RbFormElement {
|
|||
onChange={this.changeValue}
|
||||
disabled={this.props.readonly}
|
||||
/>
|
||||
<span className="custom-control-label">{$L('True')}</span>
|
||||
<span className="custom-control-label">{this._Options['T']}</span>
|
||||
</label>
|
||||
<label className="custom-control custom-radio custom-control-inline">
|
||||
<input
|
||||
|
@ -1336,14 +1447,14 @@ class RbFormBool extends RbFormElement {
|
|||
onChange={this.changeValue}
|
||||
disabled={this.props.readonly}
|
||||
/>
|
||||
<span className="custom-control-label">{$L('False')}</span>
|
||||
<span className="custom-control-label">{this._Options['F']}</span>
|
||||
</label>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
renderViewElement() {
|
||||
return super.renderViewElement(this.state.value ? BoolOptions[this.state.value] : null)
|
||||
return super.renderViewElement(this.state.value ? this._Options[this.state.value] : null)
|
||||
}
|
||||
|
||||
changeValue = (e) => {
|
||||
|
@ -1370,6 +1481,7 @@ class RbFormBarcode extends RbFormElement {
|
|||
|
||||
renderViewElement() {
|
||||
if (!this.state.value) return super.renderViewElement()
|
||||
|
||||
const codeUrl = `${rb.baseUrl}/commons/barcode/render${this.props.barcodeType === 'QRCODE' ? '-qr' : ''}?t=${$encode(this.state.value)}`
|
||||
return (
|
||||
<div className="img-field barcode">
|
||||
|
@ -1407,7 +1519,7 @@ class RbFormAvatar extends RbFormElement {
|
|||
return (
|
||||
<div className="img-field avatar">
|
||||
<a className="img-thumbnail img-upload">
|
||||
<img src={aUrl} />
|
||||
<img src={aUrl} alt="Avatar" />
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
|
@ -1462,12 +1574,12 @@ class RbFormDivider extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<div className="form-line hover" ref={(c) => (this._element = c)}>
|
||||
<fieldset>{this.props.label && <legend onClick={this.toggle}>{this.props.label}</legend>}</fieldset>
|
||||
<fieldset>{this.props.label && <legend onClick={() => this.toggle()}>{this.props.label}</legend>}</fieldset>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
toggle = () => {
|
||||
toggle() {
|
||||
if (this.props.onView) {
|
||||
let $next = $(this._element).parent()
|
||||
while (($next = $next.next()).length > 0) {
|
||||
|
@ -1514,6 +1626,8 @@ var detectElement = function (item) {
|
|||
return <RbFormPickList {...item} />
|
||||
} else if (item.type === 'REFERENCE') {
|
||||
return <RbFormReference {...item} />
|
||||
} else if (item.type === 'N2NREFERENCE') {
|
||||
return <RbFormN2NReference {...item} />
|
||||
} else if (item.type === 'CLASSIFICATION') {
|
||||
return <RbFormClassification {...item} />
|
||||
} else if (item.type === 'MULTISELECT') {
|
||||
|
@ -1552,108 +1666,6 @@ const __findMultiTexts = function (options, maskValue) {
|
|||
return texts
|
||||
}
|
||||
|
||||
// 删除确认
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
class DeleteConfirm extends RbAlert {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = { enableCascades: false }
|
||||
}
|
||||
|
||||
render() {
|
||||
let message = this.props.message
|
||||
if (!message) message = this.props.ids ? $L('DeleteSelectedSomeConfirm').replace('%d', this.props.ids.length) : $L('DeleteRecordConfirm')
|
||||
|
||||
return (
|
||||
<div className="modal rbalert" ref={(c) => (this._dlg = c)} tabIndex="-1">
|
||||
<div className="modal-dialog modal-dialog-centered">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header pb-0">
|
||||
<button className="close" type="button" onClick={() => this.hide()}>
|
||||
<span className="zmdi zmdi-close" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="text-center ml-6 mr-6">
|
||||
<div className="text-danger">
|
||||
<span className="modal-main-icon zmdi zmdi-alert-triangle" />
|
||||
</div>
|
||||
<div className="mt-3 text-bold">{message}</div>
|
||||
{!this.props.entity ? null : (
|
||||
<div className="mt-2">
|
||||
<label className="custom-control custom-control-sm custom-checkbox custom-control-inline mb-2">
|
||||
<input className="custom-control-input" type="checkbox" checked={this.state.enableCascade === true} onChange={() => this.enableCascade()} />
|
||||
<span className="custom-control-label"> {$L('DeleteCasTips')}</span>
|
||||
</label>
|
||||
<div className={' ' + (this.state.enableCascade ? '' : 'hide')}>
|
||||
<select className="form-control form-control-sm" ref={(c) => (this._cascades = c)} multiple>
|
||||
{(this.state.cascadesEntity || []).map((item) => {
|
||||
return (
|
||||
<option key={'option-' + item[0]} value={item[0]}>
|
||||
{item[1]}
|
||||
</option>
|
||||
)
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-4 mb-3" ref={(c) => (this._btns = c)}>
|
||||
<button className="btn btn-space btn-secondary" type="button" onClick={() => this.hide()}>
|
||||
{$L('Cancel')}
|
||||
</button>
|
||||
<button className="btn btn-space btn-danger" type="button" onClick={() => this.handleDelete()}>
|
||||
{$L('Delete')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
enableCascade() {
|
||||
this.setState({ enableCascade: !this.state.enableCascade })
|
||||
if (!this.state.cascadesEntity) {
|
||||
$.get(`/commons/metadata/references?entity=${this.props.entity}&permission=D`, (res) => {
|
||||
this.setState({ cascadesEntity: res.data }, () => {
|
||||
this.__select2 = $(this._cascades)
|
||||
.select2({
|
||||
placeholder: $L('SelectCasEntity'),
|
||||
width: '88%',
|
||||
})
|
||||
.val(null)
|
||||
.trigger('change')
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleDelete() {
|
||||
let ids = this.props.ids || this.props.id
|
||||
if (!ids || ids.length === 0) return
|
||||
if (typeof ids === 'object') ids = ids.join(',')
|
||||
const cascades = this.__select2 ? this.__select2.val().join(',') : ''
|
||||
|
||||
const btns = $(this._btns).find('.btn').button('loading')
|
||||
$.post(`/app/entity/record-delete?id=${ids}&cascades=${cascades}`, (res) => {
|
||||
if (res.error_code === 0) {
|
||||
if (res.data.deleted === res.data.requests) RbHighbar.success($L('SomeSuccess', 'Delete'))
|
||||
else if (res.data.deleted === 0) RbHighbar.error($L('NotDeleteTips'))
|
||||
else RbHighbar.success($L('SuccessDeletedXItems').replace('%d', res.data.deleted))
|
||||
|
||||
this.hide()
|
||||
typeof this.props.deleteAfter === 'function' && this.props.deleteAfter()
|
||||
} else {
|
||||
RbHighbar.error(res.error_msg)
|
||||
btns.button('reset')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ~ 重复记录查看
|
||||
class RepeatedViewer extends RbModalHandler {
|
||||
constructor(props) {
|
||||
|
@ -1677,7 +1689,7 @@ class RepeatedViewer extends RbModalHandler {
|
|||
<tbody>
|
||||
{data.map((item, idx) => {
|
||||
if (idx === 0) return null
|
||||
return this.renderLine(item, idx)
|
||||
return this.renderRow(item, idx)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -1685,7 +1697,7 @@ class RepeatedViewer extends RbModalHandler {
|
|||
)
|
||||
}
|
||||
|
||||
renderLine(item, idx) {
|
||||
renderRow(item, idx) {
|
||||
return (
|
||||
<tr key={`row-${idx}`}>
|
||||
{item.map((o, i) => {
|
||||
|
|
|
@ -492,7 +492,7 @@ var $initReferenceSelect2 = function (el, field) {
|
|||
return $(el).select2({
|
||||
placeholder: $L('SelectSome').replace('{0}', field.label),
|
||||
minimumInputLength: 0,
|
||||
maximumSelectionLength: 2,
|
||||
maximumSelectionLength: $(el).attr('multiple') ? 999 : 2,
|
||||
ajax: {
|
||||
url: '/commons/search/' + (field.searchType || 'reference'),
|
||||
delay: 300,
|
||||
|
|
Loading…
Reference in a new issue