mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-11-10 17:04:33 +08:00
parent
673e07baee
commit
c6c404c382
24 changed files with 299 additions and 341 deletions
2
@rbv
2
@rbv
|
@ -1 +1 @@
|
|||
Subproject commit ebcc6549551569bebdb6f7afeaef09d8d967366b
|
||||
Subproject commit d8bfac898caa63ba19f870450e8002467e7c0b77
|
|
@ -17,9 +17,11 @@ import com.rebuild.core.Application;
|
|||
import com.rebuild.core.configuration.ConfigurationException;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.metadata.MetadataHelper;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.privileges.PrivilegesGuardContextHolder;
|
||||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.core.service.general.EntityService;
|
||||
import com.rebuild.core.service.notification.MessageBuilder;
|
||||
import com.rebuild.core.support.SetUser;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
|
@ -147,7 +149,7 @@ public class ApprovalProcessor extends SetUser {
|
|||
.unique();
|
||||
if (stepApprover == null || (Integer) stepApprover[1] != ApprovalState.DRAFT.getState()) {
|
||||
throw new ApprovalException(Language.L(stepApprover == null
|
||||
? Language.L("当前流程已经被他人审批") : Language.L("你已经审批过当前流程")));
|
||||
? Language.L("当前流程已经被其他人审批") : Language.L("你已经审批过当前流程")));
|
||||
}
|
||||
|
||||
Record approvedStep = EntityHelper.forUpdate((ID) stepApprover[0], approver);
|
||||
|
@ -211,6 +213,34 @@ public class ApprovalProcessor extends SetUser {
|
|||
this.record, status.getApprovalId(), getCurrentNodeId(status), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 3.1.催审
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean urge() {
|
||||
if (this.approval == null) {
|
||||
Object[] o = Application.getQueryFactory().unique(this.record, EntityHelper.ApprovalId);
|
||||
this.approval = (ID) o[0];
|
||||
}
|
||||
|
||||
int sent = 0;
|
||||
String entityLabel = EasyMetaFactory.getLabel(MetadataHelper.getEntity(this.record.getEntityCode()));
|
||||
|
||||
JSONArray step = getCurrentStep(null);
|
||||
for (Object o : step) {
|
||||
JSONObject s = (JSONObject) o;
|
||||
if (s.getIntValue("state") != 1) continue;
|
||||
|
||||
ID approver = ID.valueOf(s.getString("approver"));
|
||||
String urgeMsg = Language.L("有一条 %s 记录正在等待你审批,请尽快审批", entityLabel);
|
||||
Application.getNotifications().send(MessageBuilder.createApproval(approver, urgeMsg, this.record));
|
||||
sent++;
|
||||
}
|
||||
|
||||
return sent > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 3.撤销(管理员)
|
||||
*
|
||||
|
|
|
@ -132,8 +132,8 @@ public class RecordDifference {
|
|||
|| EntityHelper.CreatedOn.equalsIgnoreCase(fieldName)
|
||||
|| EntityHelper.CreatedBy.equalsIgnoreCase(fieldName)
|
||||
|| EntityHelper.QuickCode.equalsIgnoreCase((fieldName))
|
||||
|| (MetadataHelper.isApprovalField(fieldName) && !EntityHelper.ApprovalState.equalsIgnoreCase(fieldName))
|
||||
|| field.getType() == FieldType.PRIMARY;
|
||||
|| field.getType() == FieldType.PRIMARY
|
||||
|| MetadataHelper.isApprovalField(fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,6 +22,7 @@ import com.rebuild.core.service.trigger.RobotTriggerObserver;
|
|||
import com.rebuild.core.service.trigger.TriggerSource;
|
||||
import com.rebuild.utils.JSONUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* 记录变更历史
|
||||
|
@ -59,8 +60,11 @@ public class RevisionHistoryObserver extends OperatingObserver {
|
|||
@Override
|
||||
public void onUpdate(OperatingContext context) {
|
||||
Record revision = newRevision(context, true);
|
||||
// v3.1 无变更不记录
|
||||
if (StringUtils.length(revision.getString("revisionContent")) > 2 /* [] */) {
|
||||
Application.getCommonsService().create(revision);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDelete(OperatingContext context) {
|
||||
|
|
|
@ -181,32 +181,38 @@ public class ParseHelper {
|
|||
* 字段是否可用于快速查询
|
||||
*
|
||||
* @param field
|
||||
* @param fieldPath
|
||||
* @return
|
||||
*/
|
||||
public static String useQuickField(Field field) {
|
||||
protected static String useQuickField(Field field, String fieldPath) {
|
||||
DisplayType dt = EasyMetaFactory.getDisplayType(field);
|
||||
|
||||
// 引用字段要保证其兼容 LIKE 条件的语法要求
|
||||
if (dt == DisplayType.REFERENCE) {
|
||||
Field nameField = field.getReferenceEntity().getNameField();
|
||||
if (nameField.getType() == FieldType.REFERENCE) {
|
||||
log.warn("Quick field cannot be circular reference : " + nameField);
|
||||
log.warn("Quick field cannot be circular-reference : " + nameField);
|
||||
return null;
|
||||
}
|
||||
|
||||
String can = useQuickField(nameField);
|
||||
return can == null ? null : (QueryCompiler.NAME_FIELD_PREFIX + field.getName());
|
||||
String can = useQuickField(nameField, fieldPath);
|
||||
if (can == null) return null;
|
||||
else if (can.startsWith("&")) return can;
|
||||
else return QueryCompiler.NAME_FIELD_PREFIX + can;
|
||||
|
||||
} else if (dt == DisplayType.PICKLIST
|
||||
|| dt == DisplayType.CLASSIFICATION) {
|
||||
return QueryCompiler.NAME_FIELD_PREFIX + field.getName();
|
||||
String can = StringUtils.defaultIfEmpty(fieldPath, field.getName());
|
||||
if (can.startsWith("&")) return can;
|
||||
else return QueryCompiler.NAME_FIELD_PREFIX + can;
|
||||
|
||||
} else if (dt == DisplayType.TEXT
|
||||
|| dt == DisplayType.EMAIL
|
||||
|| dt == DisplayType.URL
|
||||
|| dt == DisplayType.PHONE
|
||||
|| dt == DisplayType.SERIES) {
|
||||
return field.getName();
|
||||
|| dt == DisplayType.SERIES
|
||||
|| dt == DisplayType.LOCATION) {
|
||||
return StringUtils.defaultIfEmpty(fieldPath, field.getName());
|
||||
|
||||
} else {
|
||||
return null;
|
||||
|
@ -233,7 +239,7 @@ public class ParseHelper {
|
|||
for (String field : quickFields.split(",")) {
|
||||
Field validField = MetadataHelper.getLastJoinField(entity, field);
|
||||
if (validField != null) {
|
||||
String can = useQuickField(validField);
|
||||
String can = useQuickField(validField, field);
|
||||
if (can != null) {
|
||||
usesFields.add(can);
|
||||
}
|
||||
|
@ -247,7 +253,7 @@ public class ParseHelper {
|
|||
if (usesFields.isEmpty()) {
|
||||
// 名称字段
|
||||
Field nameField = entity.getNameField();
|
||||
String can = useQuickField(nameField);
|
||||
String can = useQuickField(nameField, null);
|
||||
if (can != null) {
|
||||
usesFields.add(can);
|
||||
} else {
|
||||
|
|
|
@ -39,7 +39,6 @@ import java.util.Map;
|
|||
* @since 2020/6/5
|
||||
*/
|
||||
@Slf4j
|
||||
@SuppressWarnings({"unused", "UnnecessaryLocalVariable"})
|
||||
public class BarCodeSupport {
|
||||
|
||||
// 二维码(默认)
|
||||
|
@ -147,7 +146,7 @@ public class BarCodeSupport {
|
|||
}
|
||||
|
||||
/**
|
||||
* 保存
|
||||
* 保存文件
|
||||
*
|
||||
* @param content
|
||||
* @param format
|
||||
|
@ -159,6 +158,7 @@ public class BarCodeSupport {
|
|||
|
||||
String fileName = String.format("BarCode-%d.png", System.currentTimeMillis());
|
||||
File dest = RebuildConfiguration.getFileOfTemp(fileName);
|
||||
|
||||
try {
|
||||
MatrixToImageWriter.writeToPath(bitMatrix, "png", dest.toPath());
|
||||
return dest;
|
||||
|
|
|
@ -59,9 +59,8 @@ public class OnlineSessionStore implements HttpSessionListener {
|
|||
if (log.isDebugEnabled()) log.info("Destroyed session : {}", event.getSession().getId());
|
||||
|
||||
HttpSession s = event.getSession();
|
||||
if (ONLINE_SESSIONS.contains(s)) {
|
||||
ONLINE_SESSIONS.remove(s);
|
||||
} else {
|
||||
|
||||
for (Map.Entry<ID, HttpSession> e : ONLINE_USERS.entrySet()) {
|
||||
if (s.equals(e.getValue())) {
|
||||
ONLINE_USERS.remove(e.getKey());
|
||||
|
@ -69,7 +68,6 @@ public class OnlineSessionStore implements HttpSessionListener {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有会话
|
||||
|
@ -113,7 +111,7 @@ public class OnlineSessionStore implements HttpSessionListener {
|
|||
if (!RebuildConfiguration.getBool(ConfigurationItem.MultipleSessions)) {
|
||||
HttpSession previous = getSession((ID) loginUser);
|
||||
if (previous != null) {
|
||||
log.warn("Kill previous session : {} < {}", loginUser, previous.getId());
|
||||
log.warn("Kill previous session : {} ({})", previous.getId(), loginUser);
|
||||
|
||||
try {
|
||||
previous.invalidate();
|
||||
|
@ -122,7 +120,6 @@ public class OnlineSessionStore implements HttpSessionListener {
|
|||
}
|
||||
}
|
||||
|
||||
ONLINE_SESSIONS.remove(s);
|
||||
ONLINE_USERS.put((ID) loginUser, s);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,8 +64,8 @@ public class LoginLogController extends EntityController {
|
|||
}
|
||||
|
||||
JSONObject item = JSONUtils.toJSONObject(
|
||||
new String[] { "user", "fullName", "activeTime", "activeUrl", "activeIp" },
|
||||
new Object[] { user, UserHelper.getName(user), active[0], active[1], active[2] });
|
||||
new String[] { "user", "fullName", "activeTime", "activeUrl", "activeIp", "sid" },
|
||||
new Object[] { user, UserHelper.getName(user), active[0], active[1], active[2], s.getId() });
|
||||
users.add(item);
|
||||
}
|
||||
return users;
|
||||
|
@ -73,15 +73,17 @@ public class LoginLogController extends EntityController {
|
|||
|
||||
@RequestMapping("/admin/audit/kill-session")
|
||||
public RespBody killSession(HttpServletRequest request) {
|
||||
final ID user = getIdParameterNotNull(request, "user");
|
||||
String sessionId = getParameterNotNull(request, "user");
|
||||
|
||||
HttpSession s = Application.getSessionStore().getSession(user);
|
||||
if (s != null) {
|
||||
log.warn("Admin kill session : {} > {} ", s.getId(), user);
|
||||
for (HttpSession s : Application.getSessionStore().getAllSession()) {
|
||||
if (s.getId().equals(sessionId)) {
|
||||
log.warn("Admin kill session : {} ({})", sessionId, s.getAttribute(WebUtils.CURRENT_USER));
|
||||
try {
|
||||
s.invalidate();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return RespBody.ok();
|
||||
}
|
||||
|
|
|
@ -53,16 +53,18 @@ public class BarCodeGeneratorController extends BaseController {
|
|||
String content = getParameterNotNull(request, "t");
|
||||
int w = getIntParameter(request, "w", 0);
|
||||
|
||||
// 4小时缓存
|
||||
ServletUtils.addCacheHead(response, 240);
|
||||
|
||||
BufferedImage bi;
|
||||
if (request.getRequestURI().endsWith("render-qr")) {
|
||||
writeTo(BarCodeSupport.createQRCode(content, w), response);
|
||||
bi = BarCodeSupport.createQRCode(content, w);
|
||||
} else {
|
||||
// 条形码文字
|
||||
boolean showText = getBoolParameter(request, "b", true);
|
||||
writeTo(BarCodeSupport.createBarCode(content, w, showText), response);
|
||||
bi = BarCodeSupport.createBarCode(content, w, showText);
|
||||
}
|
||||
|
||||
// 4小时缓存
|
||||
ServletUtils.addCacheHead(response, 240);
|
||||
writeTo(bi, response);
|
||||
}
|
||||
|
||||
private void writeTo(BufferedImage image, HttpServletResponse response) throws IOException {
|
||||
|
|
|
@ -93,7 +93,7 @@ public class ApprovalController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
// 审批中提交人可撤回
|
||||
// 审批中提交人可撤回、催审
|
||||
if (stateVal == ApprovalState.PROCESSING.getState()
|
||||
&& user.equals(ApprovalHelper.getSubmitter(recordId, useApproval))) {
|
||||
data.put("canCancel", true);
|
||||
|
@ -220,6 +220,12 @@ public class ApprovalController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
@RequestMapping("urge")
|
||||
public RespBody doUrge(@IdParam(name = "record") ID recordId) {
|
||||
boolean s = new ApprovalProcessor(recordId).urge();
|
||||
return s ? RespBody.ok() : RespBody.errorl("无法发送催审通知");
|
||||
}
|
||||
|
||||
@RequestMapping("revoke")
|
||||
public RespBody doRevoke(@IdParam(name = "record") ID recordId) {
|
||||
try {
|
||||
|
|
|
@ -351,7 +351,7 @@
|
|||
"你可以选择来自 [RB 仓库](https://getrebuild.com/market/go/1220-rb-store) 的业务实体使用,或在安装完成后自行添加":"你可以选择来自 [RB 仓库](https://getrebuild.com/market/go/1220-rb-store) 的业务实体使用,或在安装完成后自行添加",
|
||||
"你可在导入后进行适当调整。开始导入吗?":"你可在导入后进行适当调整。开始导入吗?",
|
||||
"你已完成所有审批":"你已完成所有审批",
|
||||
"你已审批同意,正在等待他人审批":"你已审批同意,正在等待他人审批",
|
||||
"你已审批同意,正在等待其他人审批":"你已审批同意,正在等待其他人审批",
|
||||
"你已经审批过当前流程":"你已经审批过当前流程",
|
||||
"你已驳回审批":"你已驳回审批",
|
||||
"你无权查看此任务":"你无权查看此任务",
|
||||
|
@ -783,7 +783,7 @@
|
|||
"当前数据已过滤":"当前数据已过滤",
|
||||
"当前日期":"当前日期",
|
||||
"当前时间":"当前时间",
|
||||
"当前流程已经被他人审批":"当前流程已经被他人审批",
|
||||
"当前流程已经被其他人审批":"当前流程已经被其他人审批",
|
||||
"当前用户":"当前用户",
|
||||
"当前用户处于未激活状态,因为其 %s":"当前用户处于未激活状态,因为其 %s",
|
||||
"当前记录不符合转换条件":"当前记录不符合转换条件",
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="dataTables_oper">
|
||||
<button class="btn btn-space btn-secondary J_view-online" type="button"><i class="icon mdi mdi-account-network-outline"></i> [[${bundle.L('查看在线用户')}]]</button>
|
||||
<button class="btn btn-space btn-secondary J_view-online" type="button"><i class="icon mdi mdi-account-network-outline"></i> [[${bundle.L('在线用户')}]]</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2905,25 +2905,24 @@ form {
|
|||
padding: 10px 10px 10px 0;
|
||||
}
|
||||
|
||||
.approval-pane .alert .close.btn {
|
||||
.approval-pane .alert > .close {
|
||||
top: 0;
|
||||
opacity: 1;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.approval-pane .alert > .close .btn {
|
||||
padding: 8px 0;
|
||||
font-size: 12px;
|
||||
opacity: 1;
|
||||
font-weight: normal;
|
||||
line-height: 1.1;
|
||||
background-color: #fff;
|
||||
min-height: 0;
|
||||
min-width: 78px;
|
||||
margin-top: 8px;
|
||||
box-shadow: 0 0 0 #fff;
|
||||
border: 0 none;
|
||||
}
|
||||
|
||||
.approval-pane .alert .close.btn + .btn {
|
||||
margin-right: 88px;
|
||||
}
|
||||
|
||||
.approval-pane .alert .close.btn + .btn + .btn {
|
||||
margin-right: 176px;
|
||||
.approval-pane .alert > .close > .btn + .btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.form.approval-form {
|
||||
|
@ -4698,18 +4697,35 @@ pre.unstyle {
|
|||
.quick-filter-pane .col {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
max-width: 20%;
|
||||
flex: 0 0 20%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.quick-filter-pane .col.show,
|
||||
.quick-filter-pane.extended .col {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.quick-filter-pane .col:nth-child(1),
|
||||
.quick-filter-pane .col:nth-child(2),
|
||||
.quick-filter-pane .col:nth-child(3),
|
||||
.quick-filter-pane .col:last-child {
|
||||
display: block;
|
||||
@media (max-width: 1400px) {
|
||||
.quick-filter-pane .col {
|
||||
max-width: 25%;
|
||||
flex: 0 0 25%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.quick-filter-pane .col {
|
||||
max-width: 33.3333%;
|
||||
flex: 0 0 33.3333%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.quick-filter-pane .col {
|
||||
max-width: 50%;
|
||||
flex: 0 0 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.quick-filter-pane .col > div > label {
|
||||
|
@ -4723,13 +4739,15 @@ pre.unstyle {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.quick-filter-pane .operating-btn {
|
||||
.quick-filter-pane .col.operating-btn {
|
||||
min-height: 50px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.quick-filter-pane .operating-btn > div {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.quick-filter-pane .operating-btn a {
|
||||
|
@ -4740,8 +4758,13 @@ pre.unstyle {
|
|||
line-height: 1;
|
||||
}
|
||||
|
||||
.quick-filter-pane .operating-btn .btn {
|
||||
min-height: 37px;
|
||||
}
|
||||
|
||||
.quick-filter-pane .operating-btn a .icon {
|
||||
font-size: 1.231rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.quick-filter-pane .operating-btn a:hover {
|
||||
|
@ -4749,11 +4772,11 @@ pre.unstyle {
|
|||
}
|
||||
|
||||
.quick-filter-pane .operating-btn a.admin-show {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.quick-filter-pane .operating-btn:hover a.admin-show {
|
||||
display: inline-block;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.quick-filter-pane .operating-btn .dropdown-menu {
|
||||
|
@ -4763,6 +4786,14 @@ pre.unstyle {
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.quick-filter-pane .form-control {
|
||||
max-width: 227px;
|
||||
}
|
||||
|
||||
.quick-filter-pane .form-control:focus {
|
||||
border-color: #bababa;
|
||||
}
|
||||
|
||||
/* col-sm-8 */
|
||||
.quick-filter-pane .col > div .filter-items .col-sm-5.val {
|
||||
-webkit-box-flex: 0;
|
||||
|
|
|
@ -5,10 +5,12 @@ rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
|||
See LICENSE and COMMERCIAL in the project root for license information.
|
||||
*/
|
||||
|
||||
$(document).ready(function () {
|
||||
renderRbcomp(<DataList />, 'react-list')
|
||||
$(document).ready(() => {
|
||||
renderRbcomp(<DataList />, 'react-list', function () {
|
||||
RbListPage._RbList = this._List
|
||||
})
|
||||
|
||||
$('.J_view-online').on('click', () => renderRbcomp(<OnlineUserViewer />))
|
||||
$('.J_view-online').on('click', () => renderRbcomp(<OnlineUserViewer width="681" />))
|
||||
})
|
||||
|
||||
// 列表配置
|
||||
|
@ -18,7 +20,7 @@ const ListConfig = {
|
|||
{ field: 'user', label: $L('登录用户'), type: 'REFERENCE' },
|
||||
{ field: 'loginTime', label: $L('登录时间'), type: 'DATETIME' },
|
||||
{ field: 'ipAddr', label: $L('IP 地址') },
|
||||
{ field: 'userAgent', label: $L('客户端') },
|
||||
{ field: 'userAgent', label: $L('客户端'), width: 250 },
|
||||
],
|
||||
sort: 'loginTime:desc',
|
||||
}
|
||||
|
@ -27,13 +29,6 @@ class DataList extends React.Component {
|
|||
render() {
|
||||
return <RbList ref={(c) => (this._List = c)} config={ListConfig} />
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const $btn = $('.input-search .btn'),
|
||||
$input = $('.input-search input')
|
||||
$btn.click(() => this._List.searchQuick())
|
||||
$input.keydown((e) => (e.which === 13 ? $btn.trigger('click') : true))
|
||||
}
|
||||
}
|
||||
|
||||
const _pageIps = []
|
||||
|
@ -65,22 +60,21 @@ RbList.renderAfter = function () {
|
|||
}
|
||||
|
||||
// ~ 在线用户
|
||||
class OnlineUserViewer extends RbModalHandler {
|
||||
render() {
|
||||
class OnlineUserViewer extends RbAlert {
|
||||
renderContent() {
|
||||
return (
|
||||
<RbModal ref={(c) => (this._dlg = c)} title={$L('查看在线用户')} disposeOnHide={true}>
|
||||
<table className="table table-striped table-hover table-sm dialog-table">
|
||||
<table className="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ minWidth: 150 }}>{$L('用户')}</th>
|
||||
<th style={{ minWidth: 150 }}>{$L('最近活跃')}</th>
|
||||
<th width="30%">{$L('用户')}</th>
|
||||
<th>{$L('最近活跃')}</th>
|
||||
<th width="90" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(this.state.users || []).map((item) => {
|
||||
return (
|
||||
<tr key={`user-${item.user}`}>
|
||||
<tr key={item.sid}>
|
||||
<td className="user-avatar cell-detail user-info">
|
||||
<img src={`${rb.baseUrl}/account/user-avatar/${item.user}`} alt="Avatar" />
|
||||
<span className="pt-1">{item.fullName}</span>
|
||||
|
@ -93,7 +87,7 @@ class OnlineUserViewer extends RbModalHandler {
|
|||
</span>
|
||||
</td>
|
||||
<td className="actions text-right">
|
||||
<button className="btn btn-danger btn-sm btn-outline" type="button" onClick={() => this._killSession(item.user)}>
|
||||
<button className="btn btn-danger btn-sm btn-outline" type="button" onClick={() => this._killSession(item.sid)}>
|
||||
{$L('强退')}
|
||||
</button>
|
||||
</td>
|
||||
|
@ -102,11 +96,14 @@ class OnlineUserViewer extends RbModalHandler {
|
|||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</RbModal>
|
||||
)
|
||||
}
|
||||
|
||||
componentDidMount = () => this._load()
|
||||
componentDidMount() {
|
||||
super.componentDidMount()
|
||||
this._load()
|
||||
}
|
||||
|
||||
_load() {
|
||||
$.get('/admin/audit/online-users', (res) => {
|
||||
if (res.error_code === 0) this.setState({ users: res.data })
|
||||
|
|
|
@ -17,7 +17,9 @@ $(document).ready(() => {
|
|||
_ENTITIES[this.name] = this.label
|
||||
})
|
||||
|
||||
renderRbcomp(<DataList />, 'react-list')
|
||||
renderRbcomp(<DataList />, 'react-list', function () {
|
||||
RbListPage._RbList = this._List
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -52,13 +54,13 @@ class DataList extends React.Component {
|
|||
|
||||
const $btn = $('.input-search .btn'),
|
||||
$input = $('.input-search input')
|
||||
$btn.click(() => this.queryList())
|
||||
$input.keydown((e) => (e.which === 13 ? $btn.trigger('click') : true))
|
||||
$btn.off('click').on('click', () => this.queryList())
|
||||
$input.off('keydown').on('keydown', (e) => (e.which === 13 ? $btn.trigger('click') : true))
|
||||
|
||||
this._$belongEntity = $s2
|
||||
this._$recordName = $input
|
||||
|
||||
$('.J_restore').click(() => this.restore())
|
||||
$('.J_restore').on('click', () => this.restore())
|
||||
}
|
||||
|
||||
queryList() {
|
||||
|
|
|
@ -20,7 +20,9 @@ $(document).ready(() => {
|
|||
$(`<option value="${name}">${_ENTITIES[name]}</option>`).appendTo('#belongEntity')
|
||||
}
|
||||
|
||||
renderRbcomp(<DataList />, 'react-list')
|
||||
renderRbcomp(<DataList />, 'react-list', function () {
|
||||
RbListPage._RbList = this._List
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -47,7 +49,7 @@ const RevTypes = {
|
|||
32: $L('共享'),
|
||||
64: $L('取消共享'),
|
||||
991: $L('审批通过'),
|
||||
992: $L('审批撤销')
|
||||
992: $L('审批撤销'),
|
||||
}
|
||||
|
||||
class DataList extends React.Component {
|
||||
|
@ -65,17 +67,18 @@ class DataList extends React.Component {
|
|||
})
|
||||
.val('$ALL$')
|
||||
.trigger('change')
|
||||
|
||||
$s2.on('change', () => this.queryList())
|
||||
|
||||
const $btn = $('.input-search .btn'),
|
||||
$input = $('.input-search input')
|
||||
$btn.click(() => this.queryList())
|
||||
$input.keydown((e) => (e.which === 13 ? $btn.trigger('click') : true))
|
||||
$btn.off('click').on('click', () => this.queryList())
|
||||
$input.off('keydown').on('keydown', (e) => (e.which === 13 ? $btn.trigger('click') : true))
|
||||
|
||||
this._$belongEntity = $s2
|
||||
this._$recordName = $input
|
||||
|
||||
$('.J_details').click(() => this.showDetails())
|
||||
$('.J_details').on('click', () => this.showDetails())
|
||||
}
|
||||
|
||||
queryList() {
|
||||
|
@ -141,7 +144,7 @@ class DlgDetails extends RbAlert {
|
|||
<table className="table table-fixed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="22%">{$L('字段')}</th>
|
||||
<th width="25%">{$L('字段')}</th>
|
||||
<th>{$L('变更前')}</th>
|
||||
<th>{$L('变更后')}</th>
|
||||
</tr>
|
||||
|
|
|
@ -85,6 +85,7 @@ $(document).ready(function () {
|
|||
item.type === 'URL' ||
|
||||
item.type === 'PHONE' ||
|
||||
item.type === 'SERIES' ||
|
||||
item.type === 'LOCATION' ||
|
||||
item.type === 'PICKLIST' ||
|
||||
item.type === 'CLASSIFICATION' ||
|
||||
item.type === 'DATE' ||
|
||||
|
@ -117,6 +118,7 @@ $(document).ready(function () {
|
|||
item.type === 'URL' ||
|
||||
item.type === 'PHONE' ||
|
||||
item.type === 'SERIES' ||
|
||||
item.type === 'LOCATION' ||
|
||||
item.type === 'PICKLIST' ||
|
||||
item.type === 'CLASSIFICATION' ||
|
||||
// item.type === 'DATE' ||
|
||||
|
|
|
@ -659,7 +659,9 @@ class FilterItem extends React.Component {
|
|||
renderPickListAfter() {
|
||||
const that = this
|
||||
const $s2val = $(this._filterVal)
|
||||
.select2({})
|
||||
.select2({
|
||||
width: this.props.select2Width,
|
||||
})
|
||||
.on('change.select2', function () {
|
||||
that.setState({ value: $s2val.val().join('|') })
|
||||
})
|
||||
|
@ -693,6 +695,7 @@ class FilterItem extends React.Component {
|
|||
const that = this
|
||||
const $s2val = $(this._filterVal)
|
||||
.select2({
|
||||
width: this.props.select2Width,
|
||||
minimumInputLength: 1,
|
||||
ajax: {
|
||||
url: '/commons/search/search',
|
||||
|
@ -791,6 +794,7 @@ class FilterItem extends React.Component {
|
|||
const that = this
|
||||
const $s2val = $(this._filterVal)
|
||||
.select2({
|
||||
width: this.props.select2Width,
|
||||
allowClear: this.props.allowClear === true,
|
||||
placeholder: this.props.allowClear === true ? $L('全部') : null,
|
||||
})
|
||||
|
|
|
@ -29,9 +29,12 @@ class ApprovalProcessor extends React.Component {
|
|||
renderStateDraft() {
|
||||
return (
|
||||
<div className="alert alert-warning shadow-sm">
|
||||
<button className="close btn btn-secondary" onClick={this.submit}>
|
||||
<span className="close">
|
||||
<button className="btn btn-secondary" onClick={this.submit}>
|
||||
{$L('提交')}
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<div className="icon">
|
||||
<span className="zmdi zmdi-info-outline" />
|
||||
</div>
|
||||
|
@ -46,25 +49,33 @@ class ApprovalProcessor extends React.Component {
|
|||
let aMsg = $L('当前记录正在审批中')
|
||||
if (this.state.imApprover) {
|
||||
if (this.state.imApproveSatate === 1) aMsg = $L('当前记录正在等待你审批')
|
||||
else if (this.state.imApproveSatate === 10) aMsg = $L('你已审批同意,正在等待他人审批')
|
||||
else if (this.state.imApproveSatate === 10) aMsg = $L('你已审批同意,正在等待其他人审批')
|
||||
else if (this.state.imApproveSatate === 11) aMsg = $L('你已驳回审批')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="alert alert-warning shadow-sm">
|
||||
<button className="close btn btn-secondary" onClick={this.viewSteps}>
|
||||
{$L('详情')}
|
||||
</button>
|
||||
{this.state.canCancel && (
|
||||
<button className="close btn btn-secondary" onClick={this.cancel}>
|
||||
{$L('撤回')}
|
||||
</button>
|
||||
)}
|
||||
<span className="close">
|
||||
{this.state.imApprover && this.state.imApproveSatate === 1 && (
|
||||
<button className="close btn btn-secondary" onClick={this.approve}>
|
||||
<button className="btn btn-secondary" onClick={this.approve}>
|
||||
{$L('审批')}
|
||||
</button>
|
||||
)}
|
||||
{this.state.canCancel && (
|
||||
<RF>
|
||||
<button className="btn btn-secondary" onClick={this.urge}>
|
||||
{$L('催审')}
|
||||
</button>
|
||||
<button className="btn btn-secondary" onClick={this.cancel}>
|
||||
{$L('撤回')}
|
||||
</button>
|
||||
</RF>
|
||||
)}
|
||||
<button className="btn btn-secondary" onClick={this.viewSteps}>
|
||||
{$L('详情')}
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<div className="icon">
|
||||
<span className="zmdi zmdi-hourglass-alt" />
|
||||
</div>
|
||||
|
@ -78,14 +89,17 @@ class ApprovalProcessor extends React.Component {
|
|||
|
||||
return (
|
||||
<div className="alert alert-success shadow-sm">
|
||||
<button className="close btn btn-secondary" onClick={this.viewSteps}>
|
||||
{$L('详情')}
|
||||
</button>
|
||||
<span className="close">
|
||||
{rb.isAdminUser && (
|
||||
<button className="close btn btn-secondary" onClick={this.revoke}>
|
||||
<button className="btn btn-secondary" onClick={this.revoke}>
|
||||
{$L('撤销')}
|
||||
</button>
|
||||
)}
|
||||
<button className="btn btn-secondary" onClick={this.viewSteps}>
|
||||
{$L('详情')}
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<div className="icon">
|
||||
<span className="zmdi zmdi-check" />
|
||||
</div>
|
||||
|
@ -97,12 +111,15 @@ class ApprovalProcessor extends React.Component {
|
|||
renderStateRejected() {
|
||||
return (
|
||||
<div className="alert alert-danger shadow-sm">
|
||||
<button className="close btn btn-secondary" onClick={this.viewSteps}>
|
||||
{$L('详情')}
|
||||
</button>
|
||||
<button className="close btn btn-secondary" onClick={this.submit}>
|
||||
<span className="close">
|
||||
<button className="btn btn-secondary" onClick={this.submit}>
|
||||
{$L('再次提交')}
|
||||
</button>
|
||||
<button className="btn btn-secondary" onClick={this.viewSteps}>
|
||||
{$L('详情')}
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<div className="icon">
|
||||
<span className="zmdi zmdi-close-circle-o" />
|
||||
</div>
|
||||
|
@ -114,12 +131,15 @@ class ApprovalProcessor extends React.Component {
|
|||
renderStateCanceled() {
|
||||
return (
|
||||
<div className="alert alert-warning shadow-sm">
|
||||
<button className="close btn btn-secondary" onClick={this.viewSteps}>
|
||||
{$L('详情')}
|
||||
</button>
|
||||
<button className="close btn btn-secondary" onClick={this.submit}>
|
||||
<span className="close">
|
||||
<button className="btn btn-secondary" onClick={this.submit}>
|
||||
{$L('再次提交')}
|
||||
</button>
|
||||
<button className="btn btn-secondary" onClick={this.viewSteps}>
|
||||
{$L('详情')}
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<div className="icon">
|
||||
<span className="zmdi zmdi-rotate-left" />
|
||||
</div>
|
||||
|
@ -131,12 +151,15 @@ class ApprovalProcessor extends React.Component {
|
|||
renderStateRevoked() {
|
||||
return (
|
||||
<div className="alert alert-warning shadow-sm">
|
||||
<button className="close btn btn-secondary" onClick={this.viewSteps}>
|
||||
{$L('详情')}
|
||||
</button>
|
||||
<button className="close btn btn-secondary" onClick={this.submit}>
|
||||
<span className="close">
|
||||
<button className="btn btn-secondary" onClick={this.submit}>
|
||||
{$L('再次提交')}
|
||||
</button>
|
||||
<button className="btn btn-secondary" onClick={this.viewSteps}>
|
||||
{$L('详情')}
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<div className="icon">
|
||||
<span className="zmdi zmdi-rotate-left" />
|
||||
</div>
|
||||
|
@ -179,7 +202,7 @@ class ApprovalProcessor extends React.Component {
|
|||
|
||||
cancel = () => {
|
||||
const that = this
|
||||
RbAlert.create($L('确认撤回当前审批?'), {
|
||||
RbAlert.create($L('将要撤回已提交审批。是否继续?'), {
|
||||
confirm: function () {
|
||||
this.disabled(true)
|
||||
$.post(`/app/entity/approval/cancel?record=${that.props.id}`, (res) => {
|
||||
|
@ -191,9 +214,27 @@ class ApprovalProcessor extends React.Component {
|
|||
})
|
||||
}
|
||||
|
||||
urge = () => {
|
||||
const that = this
|
||||
RbAlert.create($L('将向当前审批人发送催审通知。是否继续?'), {
|
||||
confirm: function () {
|
||||
this.disabled(true)
|
||||
$.post(`/app/entity/approval/urge?record=${that.props.id}`, (res) => {
|
||||
if (res.error_code > 0) {
|
||||
RbHighbar.error(res.error_msg)
|
||||
this.disabled()
|
||||
} else {
|
||||
RbHighbar.success($L('通知已发送'))
|
||||
this.hide()
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
revoke = () => {
|
||||
const that = this
|
||||
RbAlert.create($L('将要撤销已通过审批。确认吗?'), {
|
||||
RbAlert.create($L('将要撤销已通过审批。是否继续?'), {
|
||||
type: 'danger',
|
||||
confirm: function () {
|
||||
this.disabled(true)
|
||||
|
|
|
@ -699,6 +699,7 @@ const RbListCommon = {
|
|||
}
|
||||
|
||||
const entity = wpc.entity
|
||||
if (!entity) return
|
||||
|
||||
RbListPage.init(wpc.listConfig, entity, wpc.privileges)
|
||||
if (wpc.advFilter !== false) AdvFilters.init('.adv-search', entity[0])
|
||||
|
@ -907,7 +908,7 @@ class RbList extends React.Component {
|
|||
if (wpc.advFilter !== true) this.fetchList(this._buildQuick())
|
||||
|
||||
// 按键操作
|
||||
if (wpc.type === 'RecordList') $(document).on('keydown', (e) => this._keyEvent(e))
|
||||
if (wpc.type === 'RecordList' || wpc.type === 'DetailList') $(document).on('keydown', (e) => this._keyEvent(e))
|
||||
}
|
||||
|
||||
fetchList(filter) {
|
||||
|
@ -1092,6 +1093,7 @@ class RbList extends React.Component {
|
|||
}
|
||||
|
||||
_openView($tr) {
|
||||
if (!wpc.type) return
|
||||
const id = $($tr).data('id')
|
||||
if (!wpc.forceSubView) {
|
||||
location.hash = `!/View/${this._entity}/${id}`
|
||||
|
|
|
@ -93,7 +93,7 @@ const RbListPage = {
|
|||
}
|
||||
|
||||
// Filter Pane
|
||||
if ($('.quick-filter-pane').length > 0) {
|
||||
if ($('.quick-filter-pane')[0]) {
|
||||
// eslint-disable-next-line react/jsx-no-undef
|
||||
renderRbcomp(<AdvFilterPane entity={entity[0]} fields={wpc.paneFields} onSearch={(s) => RbListPage._RbList.search(s)} />, $('.quick-filter-pane')[0])
|
||||
}
|
||||
|
|
|
@ -1,171 +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.
|
||||
*/
|
||||
// deps: rb-advfilter.js
|
||||
|
||||
const REFENTITY_CACHE = window.REFENTITY_CACHE || {}
|
||||
const IS_N2NREF = window.IS_N2NREF || {}
|
||||
const BIZZ_ENTITIES = window.BIZZ_ENTITIES || []
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
class AdvFilterPane extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {}
|
||||
this._itemsRef = []
|
||||
}
|
||||
|
||||
onRef = (c) => this._itemsRef.push(c)
|
||||
|
||||
componentDidMount() {
|
||||
const items = (this.props.fields || []).map((item) => {
|
||||
if (item.type === 'REFERENCE' || item.type === 'N2NREFERENCE') {
|
||||
REFENTITY_CACHE[`${this.props.entity}.${item.name}`] = item.ref
|
||||
if (item.type === 'N2NREFERENCE') IS_N2NREF.push(item.name)
|
||||
|
||||
// NOTE: Use `NameField` field-type
|
||||
if (!BIZZ_ENTITIES.includes(item.ref[0])) {
|
||||
item.type = item.ref[1]
|
||||
}
|
||||
}
|
||||
return item
|
||||
})
|
||||
|
||||
this.setState({ items }, () => {
|
||||
setTimeout(() => this.clearFilter(), 200)
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.state.items) return null
|
||||
|
||||
return (
|
||||
<form className="row" onSubmit={(e) => this.searchNow(e)}>
|
||||
{this.state.items.map((item, i) => {
|
||||
return (
|
||||
<div className="col col-6 col-lg-4 col-xl-3" key={i}>
|
||||
<div>
|
||||
<label>{item.label}</label>
|
||||
<div className="adv-filter">
|
||||
<div className="filter-items">
|
||||
<FilterItemExt onRef={this.onRef} $$$parent={this} fields={[item]} allowClear />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
<div className="col col-6 col-lg-4 col-xl-3 operating-btn">
|
||||
<div>
|
||||
<div className="btn-group">
|
||||
<button className="btn btn-secondary" type="submit">
|
||||
<i className="icon zmdi zmdi-search"></i> {$L('查询')}
|
||||
</button>
|
||||
<button className="btn btn-secondary dropdown-toggle w-auto" type="button" data-toggle="dropdown">
|
||||
<i className="icon zmdi zmdi-chevron-down" />
|
||||
</button>
|
||||
<div className="dropdown-menu dropdown-menu-right">
|
||||
<label className="custom-control custom-control-sm custom-radio custom-control-inline mb-0 mr-2">
|
||||
<input className="custom-control-input" type="radio" name="useEquation" value="OR" defaultChecked />
|
||||
<span className="custom-control-label">{$L('符合任一')}</span>
|
||||
</label>
|
||||
<label className="custom-control custom-control-sm custom-radio custom-control-inline mb-0 mr-0">
|
||||
<input className="custom-control-input" type="radio" name="useEquation" value="AND" ref={(c) => (this._$useEquationAnd = c)} />
|
||||
<span className="custom-control-label">{$L('符合全部')}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<a className="ml-3 down-1" onClick={() => this.clearFilter(true)}>
|
||||
<i className="icon zmdi zmdi-replay down-1" />
|
||||
</a>
|
||||
{(this.props.fields || []).length > 4 && (
|
||||
<a className="ml-2 down-1" onClick={() => this.toggleExtended()}>
|
||||
<i className={`icon down-1 zmdi ${this.state.extended ? 'zmdi-unfold-less text-bold' : 'zmdi-unfold-more'}`} title={$L('展开/收起')} />
|
||||
</a>
|
||||
)}
|
||||
{rb.isAdminUser && (
|
||||
<a
|
||||
className="ml-2 down-2 admin-show"
|
||||
title={$L('配置查询面板字段')}
|
||||
onClick={() => {
|
||||
RbModal.create(
|
||||
`/p/admin/metadata/list-filterpane?entity=${this.props.entity}`,
|
||||
<RF>
|
||||
{$L('配置查询面板字段')}
|
||||
<sup className="rbv" title={$L('增值功能')} />
|
||||
</RF>
|
||||
)
|
||||
}}>
|
||||
<i className="icon zmdi zmdi-settings" />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
searchNow(e) {
|
||||
$stopEvent(e, true)
|
||||
|
||||
const filters = []
|
||||
for (let i = 0; i < this._itemsRef.length; i++) {
|
||||
const item = this._itemsRef[i].getFilterJson()
|
||||
if (item) filters.push(item)
|
||||
}
|
||||
|
||||
const s = {
|
||||
entity: this.props.entity,
|
||||
equation: this._$useEquationAnd.checked ? 'AND' : 'OR',
|
||||
items: filters,
|
||||
}
|
||||
|
||||
if (rb.env === 'dev') console.log(JSON.stringify(s))
|
||||
typeof this.props.onSearch === 'function' && this.props.onSearch(s)
|
||||
}
|
||||
|
||||
clearFilter(searchNow) {
|
||||
this._itemsRef.forEach((i) => i.clear())
|
||||
searchNow === true && setTimeout(() => this.searchNow(), 200)
|
||||
}
|
||||
|
||||
toggleExtended() {
|
||||
this.setState({ extended: !this.state.extended }, () => {
|
||||
if (this.state.extended) $('.quick-filter-pane').addClass('extended')
|
||||
else $('.quick-filter-pane').removeClass('extended')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
class FilterItemExt extends FilterItem {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
super.componentDidMount()
|
||||
|
||||
const $s2op = this.__select2[1]
|
||||
|
||||
setTimeout(() => {
|
||||
if (this.state.type === 'DATE' || this.state.type === 'DATETIME' || this.state.type === 'TIME') {
|
||||
$s2op.val('EQ').trigger('change')
|
||||
}
|
||||
}, 200)
|
||||
}
|
||||
|
||||
// @e = el or event
|
||||
valueCheck(e) {
|
||||
const v = e.target ? e.target.value : e.val()
|
||||
if (!v) return
|
||||
|
||||
super.valueCheck(e)
|
||||
// $el.removeClass('is-invalid')
|
||||
}
|
||||
}
|
|
@ -132,7 +132,7 @@
|
|||
<script th:src="@{/assets/js/rb-forms.protable.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/rb-advfilter.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/rb-filterpane.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/rb-assignshare.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/rb-assignshare.js}" type="text/babel" th:if="${commercial > 1}"></script>
|
||||
<script th:src="@{/assets/js/rb-approval.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/settings-share2.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/rb-datalist.common.js}" type="text/babel"></script>
|
||||
|
|
|
@ -135,7 +135,7 @@
|
|||
<script th:src="@{/assets/js/rb-forms.append.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/rb-forms.protable.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/rb-advfilter.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/rb-filterpane.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/rb-filterpane.js}" type="text/babel" th:if="${commercial > 1}"></script>
|
||||
<script th:src="@{/assets/js/rb-assignshare.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/rb-approval.js}" type="text/babel"></script>
|
||||
<script th:src="@{/assets/js/settings-share2.js}" type="text/babel"></script>
|
||||
|
|
Loading…
Reference in a new issue