Merge pull request #269 from getrebuild/related-expand-612

Related expand 612
This commit is contained in:
RB 2020-12-22 23:08:37 +08:00 committed by GitHub
commit d4202951c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 113 additions and 69 deletions

View file

@ -28,9 +28,13 @@ import java.util.Set;
*/
public abstract class ShareToManager implements ConfigManager {
// 共享给全部
/**
* 共享给全部
*/
public static final String SHARE_ALL = "ALL";
// 私有
/**
* 私有
*/
public static final String SHARE_SELF = "SELF";
/**
@ -72,9 +76,7 @@ public abstract class ShareToManager implements ConfigManager {
*/
public ID detectUseConfig(ID user, String belongEntity, String applyType) {
final Object[][] alls = getAllConfig(belongEntity, applyType);
if (alls.length == 0) {
return null;
}
if (alls.length == 0) return null;
// 1.优先使用自己的
for (Object[] d : alls) {
@ -134,7 +136,8 @@ public abstract class ShareToManager implements ConfigManager {
sqlWhere.add(String.format("applyType = '%s'", applyType));
}
String ql = String.format("select %s from %s where (1=1) order by modifiedOn desc", getConfigFields(), getConfigEntity());
String ql = String.format(
"select %s from %s where (1=1) order by modifiedOn desc", getConfigFields(), getConfigEntity());
if (!sqlWhere.isEmpty()) {
ql = ql.replace("(1=1)", StringUtils.join(sqlWhere.iterator(), " and "));
}

View file

@ -20,8 +20,13 @@ import com.rebuild.core.configuration.ConfigBean;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.metadata.MetadataHelper;
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
import com.rebuild.core.support.i18n.Language;
import com.rebuild.utils.JSONUtils;
import java.util.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* 视图-相关项/新建相关
@ -43,12 +48,14 @@ public class ViewAddonsManager extends BaseLayoutManager {
public static final String EF_SPLIT = ".";
/**
* 显示项
*
* @param entity
* @param user
* @return
*/
public JSON getViewTab(String entity, ID user) {
JSON tabs = getViewAddons(entity, user, TYPE_TAB);
public JSONObject getViewTab(String entity, ID user) {
JSONObject vtabs = getViewAddons(entity, user, TYPE_TAB);
// 添加明细实体到第一个
Entity entityMeta = MetadataHelper.getEntity(entity);
@ -56,18 +63,20 @@ public class ViewAddonsManager extends BaseLayoutManager {
JSON detail = EasyMetaFactory.toJSON(entityMeta.getDetailEntity());
JSONArray tabsFluent = new JSONArray();
tabsFluent.add(detail);
tabsFluent.fluentAddAll((Collection<?>) tabs);
tabs = tabsFluent;
tabsFluent.fluentAddAll(vtabs.getJSONArray("items"));
vtabs.put("items", tabsFluent);
}
return tabs;
return vtabs;
}
/**
* 新建项
*
* @param entity
* @param user
* @return
*/
public JSON getViewAdd(String entity, ID user) {
public JSONObject getViewAdd(String entity, ID user) {
return getViewAddons(entity, user, TYPE_ADD);
}
@ -77,35 +86,35 @@ public class ViewAddonsManager extends BaseLayoutManager {
* @param applyType
* @return
*/
private JSON getViewAddons(String entity, ID user, String applyType) {
private JSONObject getViewAddons(String entity, ID user, String applyType) {
final ConfigBean config = getLayout(user, entity, applyType);
final Permission useAction = TYPE_TAB.equals(applyType) ? BizzPermission.READ : BizzPermission.CREATE;
final Entity entityMeta = MetadataHelper.getEntity(entity);
final Set<Entity> mfRefs = hasMultiFieldsReferenceTo(entityMeta);
// 未配置则使用全部相关项
// 未配置则使用全部
if (config == null) {
JSONArray refs = new JSONArray();
JSONArray useRefs = new JSONArray();
for (Field field : entityMeta.getReferenceToFields(true)) {
Entity e = field.getOwnEntity();
if (e.getMainEntity() == null &&
Application.getPrivilegesManager().allow(user, e.getEntityCode(), useAction)) {
refs.add(getEntityShow(field, mfRefs, applyType));
useRefs.add(getEntityShow(field, mfRefs, applyType));
}
}
// 动态跟进
// if (TYPE_TAB.equalsIgnoreCase(applyType)) {
// Field relatedRecordOfFeeds = MetadataHelper.getField("Feeds", "relatedRecord");
// refs.add(getEntityShow(relatedRecordOfFeeds, Collections.emptySet(), applyType));
// }
return JSONUtils.toJSONObject("items", useRefs);
}
return refs;
// fix: v2.2 兼容
JSON configJson = config.getJSON("config");
if (configJson instanceof JSONArray) {
configJson = JSONUtils.toJSONObject("items", configJson);
}
JSONArray addons = new JSONArray();
for (Object o : (JSONArray) config.getJSON("config")) {
for (Object o : ((JSONObject) configJson).getJSONArray ("items")) {
// Entity.Field (v1.9)
String[] e = ((String) o).split("\\.");
if (!MetadataHelper.containsEntity(e[0])) {
@ -125,7 +134,10 @@ public class ViewAddonsManager extends BaseLayoutManager {
}
}
}
return addons;
return JSONUtils.toJSONObject(
new String[] { "items", "autoExpand" },
new Object[] { addons, ((JSONObject) configJson).getBooleanValue("autoExpand") });
}
/**
@ -157,10 +169,6 @@ public class ViewAddonsManager extends BaseLayoutManager {
}
/**
* @param field
* @param mfRefs
* @param applyType
* @return
* @see EasyMetaFactory#toJSON(Entity)
*/
private JSONObject getEntityShow(Field field, Set<Entity> mfRefs, String applyType) {
@ -174,7 +182,7 @@ public class ViewAddonsManager extends BaseLayoutManager {
: String.format("%s (%s)", show.getString("entityLabel"), EasyMetaFactory.getLabel(field));
show.put("entityLabel", entityLabel);
} else if (fieldEntity.getEntityCode() == EntityHelper.Feeds) {
show.put("entityLabel", "跟进");
show.put("entityLabel", Language.L("e.Feeds"));
}
return show;
}

View file

@ -13,6 +13,8 @@ import cn.devezhao.persist4j.Field;
import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.engine.ID;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.rebuild.api.RespBody;
import com.rebuild.core.Application;
import com.rebuild.core.configuration.ConfigBean;
import com.rebuild.core.configuration.general.LayoutConfigService;
@ -20,16 +22,12 @@ import com.rebuild.core.configuration.general.ViewAddonsManager;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.metadata.MetadataHelper;
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
import com.rebuild.core.support.i18n.Language;
import com.rebuild.utils.JSONUtils;
import com.rebuild.web.BaseController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashSet;
import java.util.Set;
@ -39,18 +37,18 @@ import java.util.Set;
* @author devezhao
* @since 10/23/2018
*/
@Controller
@RestController
@RequestMapping("/admin/entity/")
public class ViewAddonsController extends BaseController {
@PostMapping("{entity}/view-addons")
public void sets(@PathVariable String entity,
HttpServletRequest request, HttpServletResponse response) {
ID user = getRequestUser(request);
public RespBody sets(@PathVariable String entity, HttpServletRequest request) {
final ID user = getRequestUser(request);
String applyType = getParameter(request, "type", ViewAddonsManager.TYPE_TAB);
JSON config = ServletUtils.getRequestJson(request);
ID configId = ViewAddonsManager.instance.detectUseConfig(user, entity, applyType);
Record record;
if (configId == null) {
record = EntityHelper.forNew(EntityHelper.LayoutConfig, user);
@ -63,18 +61,22 @@ public class ViewAddonsController extends BaseController {
record.setString("config", config.toJSONString());
Application.getBean(LayoutConfigService.class).createOrUpdate(record);
writeSuccess(response);
return RespBody.ok();
}
@GetMapping("{entity}/view-addons")
public void gets(@PathVariable String entity,
HttpServletRequest request, HttpServletResponse response) {
ID user = getRequestUser(request);
public JSON gets(@PathVariable String entity, HttpServletRequest request) {
final ID user = getRequestUser(request);
String applyType = getParameter(request, "type", ViewAddonsManager.TYPE_TAB);
Entity entityMeta = MetadataHelper.getEntity(entity);
ConfigBean config = ViewAddonsManager.instance.getLayout(user, entity, applyType);
// fix: v2.2 兼容
JSON configJson = config == null ? null : config.getJSON("config");
if (configJson instanceof JSONArray) {
configJson = JSONUtils.toJSONObject("items", configJson);
}
Entity entityMeta = MetadataHelper.getEntity(entity);
Set<Entity> mfRefs = ViewAddonsManager.hasMultiFieldsReferenceTo(entityMeta);
Set<String[]> refs = new HashSet<>();
@ -88,17 +90,16 @@ public class ViewAddonsController extends BaseController {
if (mfRefs.contains(e)) {
label = EasyMetaFactory.getLabel(field) + " (" + label + ")";
}
refs.add(new String[]{e.getName() + ViewAddonsManager.EF_SPLIT + field.getName(), label});
refs.add(new String[] { e.getName() + ViewAddonsManager.EF_SPLIT + field.getName(), label });
}
// 跟进动态
if (ViewAddonsManager.TYPE_TAB.equalsIgnoreCase(applyType)) {
refs.add(new String[]{"Feeds.relatedRecord", "跟进"});
refs.add(new String[] { "Feeds.relatedRecord", Language.L("e.Feeds") });
}
JSON ret = JSONUtils.toJSONObject(
new String[]{"config", "refs"},
new Object[]{config == null ? null : config.getJSON("config"), refs});
writeSuccess(response, ret);
return JSONUtils.toJSONObject(
new String[] { "config", "refs" },
new Object[] { configJson, refs });
}
}

View file

@ -60,10 +60,11 @@ public class GeneralModelController extends EntityController {
} else {
mv = createModelAndView("/general/record-view", id, user);
JSON vtab = ViewAddonsManager.instance.getViewTab(entity, user);
mv.getModel().put("ViewTabs", vtab);
JSON vadd = ViewAddonsManager.instance.getViewAdd(entity, user);
mv.getModel().put("ViewAdds", vadd);
JSONObject vtab = ViewAddonsManager.instance.getViewTab(entity, user);
mv.getModel().put("ViewTabs", vtab.getJSONArray("items"));
mv.getModel().put("ViewTabsAutoExpand", vtab.getBooleanValue("autoExpand"));
JSONObject vadd = ViewAddonsManager.instance.getViewAdd(entity, user);
mv.getModel().put("ViewAdds", vadd.getJSONArray("items"));
}
// 记录转换

View file

@ -1274,6 +1274,7 @@
"BadOrDeleteApproval": "Invalid approval, may have been deleted",
"FormCalcFormula": "Form Calculation Formula",
"FormCalcFormulaTips": "If the fields used in the formula are not layout/displayed, calculations cannot be performed. More powerful calculation rules can be achieved through [Trigger (Data Aggregation)](/admin/robot/triggers)",
"RelatedAutoExpand": "Automatically expand tab records",
"s.__": "STATE",
"s.ApprovalState.DRAFT": "Draft",

View file

@ -1274,6 +1274,7 @@
"BadOrDeleteApproval": "无效审批流程,可能已被删除",
"FormCalcFormula": "表单计算公式",
"FormCalcFormulaTips": "如公式中所用字段未布局/未显示,则无法进行计算。通过 [触发器 (数据聚合)](/admin/robot/triggers) 可以实现更加强大的计算规则",
"RelatedAutoExpand": "自动展开显示项记录",
"s.__": "状态",
"s.ApprovalState.DRAFT": "草稿",

View file

@ -1274,6 +1274,7 @@
"BadOrDeleteApproval": "無效審批流程,可能已被刪除",
"FormCalcFormula": "表單計算公式",
"FormCalcFormulaTips": "如公式中所用字段未佈局/未顯示,則無法進行計算。通過 [觸發器(數據聚合](/admin/robot/triggers) 可以實現更加強大的計算規則",
"RelatedAutoExpand": "自動展開顯示項記錄",
"s.__": "狀態",
"s.ApprovalState.DRAFT": "草稿",

View file

@ -22,6 +22,12 @@
</div>
</div>
<div class="dialog-footer">
<span th:if="${param.type[0] == 'TAB'}" class="float-left">
<label class="custom-control custom-control-sm custom-checkbox custom-control-inline">
<input class="custom-control-input" type="checkbox" id="relatedAutoExpand" />
<span class="custom-control-label">[[${bundle.L('RelatedAutoExpand')}]]</span>
</label>
</span>
<button class="btn btn-primary J_save" type="button">[[${bundle.L('Save')}]]</button>
<button class="btn btn-secondary" onclick="parent.RbModal.hide()" type="button">[[${bundle.L('Cancel')}]]</button>
</div>
@ -32,28 +38,40 @@
$(document).ready(function () {
const entity = $urlp('entity'),
type = $urlp('type')
const _url = '/admin/entity/' + entity + '/view-addons?type=' + type
const _url = `/admin/entity/${entity}/view-addons?type=${type}`
$.get(_url, function (res) {
$(res.data.refs).each(function () {
render_unset(this)
})
$(res.data.config).each(function () {
$('.unset-list li[data-key="' + this + '"]').trigger('click')
})
if (!res.data.refs || res.data.refs.length === 0) $(`<li class="dd-item nodata">${$L('NoData')}</li>`).appendTo('.unset-list')
if (res.data.config) {
$(res.data.config.items).each(function () {
$('.unset-list li[data-key="' + this + '"]').trigger('click')
})
$('#relatedAutoExpand').attr('checked', res.data.config.autoExpand === true)
}
if (!res.data.refs || res.data.refs.length === 0) {
$(`<li class="dd-item nodata">${$L('NoData')}</li>`).appendTo('.unset-list')
}
})
const $btn = $('.J_save').click(function () {
const config = []
let config = []
$('.J_config>li').each(function () {
config.push($(this).data('key'))
})
config = {
items: config,
autoExpand: $val('#relatedAutoExpand'),
}
$btn.button('loading')
$.post(_url, JSON.stringify(config), function (res) {
$btn.button('reset')
if (res.error_code === 0) parent.location.reload()
else RbHighbar.error(res.error_msg)
})
})
})

View file

@ -140,13 +140,12 @@ body {
.related-list .card {
border: 1px solid #ebebeb;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.04);
padding: 0;
margin-bottom: 9px;
}
.related-list .card:hover {
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.1);
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}
.related-list .card .header-title {

View file

@ -249,7 +249,7 @@ class RelatedList extends React.Component {
)}
{(this.state.list || []).map((item) => {
return (
<div className={`card ${this.state.viewOpens[item[0]] ? 'active' : ''}`} key={`rr-${item[0]}`}>
<div className={`card ${this.state.viewOpens[item[0]] ? 'active' : ''}`} key={item[0]} ref={`item-${item[0]}`}>
<div className="row header-title" onClick={() => this._toggleInsideView(item[0])}>
<div className="col-10">
<a href={`#!/View/${this.props.entity.split('.')[0]}/${item[0]}`} onClick={(e) => this._handleView(e)} title={$L('Open')}>
@ -284,11 +284,21 @@ class RelatedList extends React.Component {
fetchList(append) {
this.__pageNo = this.__pageNo || 1
if (append) this.__pageNo += append
const pageSize = 20
const pageSize = 5
$.get(`/app/entity/related-list?mainid=${this.props.main}&related=${this.props.entity}&pageNo=${this.__pageNo}&pageSize=${pageSize}`, (res) => {
const _data = res.data.data || []
const _list = (this.state.list || []).concat(_data)
this.setState({ list: _list, showMores: _data.length >= pageSize })
const data = res.data.data || []
const list = (this.state.list || []).concat(data)
this.setState({ list: list, showMores: data.length >= pageSize }, () => {
if (this.props.autoExpand) {
data.forEach((item) => {
// eslint-disable-next-line react/no-string-refs
const $H = $(this.refs[`item-${item[0]}`]).find('.header-title')
if ($H.length > 0) $H[0].click()
})
}
})
})
}
@ -551,7 +561,7 @@ const RbViewPage = {
const tabNav = $(`<li class="nav-item"><a class="nav-link" href="#${tabId}" data-toggle="tab" title="${this.entityLabel}">${this.entityLabel}</a></li>`).appendTo('.nav-tabs')
const tabPane = $(`<div class="tab-pane" id="${tabId}"></div>`).appendTo('.tab-content')
tabNav.find('a').click(function () {
tabPane.find('.related-list').length === 0 && renderRbcomp(<MixRelatedList entity={entity} main={that.__id} />, tabPane)
tabPane.find('.related-list').length === 0 && renderRbcomp(<MixRelatedList entity={entity} main={that.__id} autoExpand={$isTrue(wpc.viewTabsAutoExpand)} />, tabPane)
})
})
this.updateVTabs()

View file

@ -103,6 +103,7 @@
entity: ['[[${entityName}]]', '[[${entityLabel}]]', '[[${entityIcon}]]'],
privileges: _$unthy('[[${entityPrivileges}]]'),
viewTabs: _$unthy('[[${ViewTabs}]]'),
viewTabsAutoExpand: '[[${ViewTabsAutoExpand}]]',
viewAdds: _$unthy('[[${ViewAdds}]]'),
transformTos: _$unthy('[[${TransformTos}]]'),
recordId: '[[${id}]]',