Merge pull request #205 from getrebuild/date-with-gmt

Use date with zone for client
This commit is contained in:
RB 2020-08-03 17:12:55 +08:00 committed by GitHub
commit c3eb3d0297
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 277 additions and 221 deletions

View file

@ -71,6 +71,7 @@
"AdvFilter": true,
"UserSelector": true,
"UserShow": true,
"DateShow": true,
"DeleteConfirm": true,
"ApprovalProcessor": true,
"RbFormModal": true,
@ -93,6 +94,7 @@
"$empty": true,
"$mp": true,
"converEmoji": true,
"$converEmoji": true,
"$throttle": true,
"$timechunk": true,
"moment": true,

View file

@ -149,7 +149,7 @@
<dependency>
<groupId>com.github.devezhao</groupId>
<artifactId>commons</artifactId>
<version>868cac675e</version>
<version>29b747ad0d</version>
<exclusions>
<exclusion>
<artifactId>httpclient</artifactId>

View file

@ -8,6 +8,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
package com.rebuild.api;
import cn.devezhao.commons.CalendarUtils;
import cn.devezhao.commons.DateFormatUtils;
import com.alibaba.fastjson.JSON;
import com.rebuild.utils.CommonsUtils;
import com.rebuild.utils.JSONUtils;
@ -24,7 +25,7 @@ public class SystemTime extends BaseApi {
public JSON execute(ApiContext context) {
JSON data = JSONUtils.toJSONObject(
new String[] { "time" },
new Object[] { CommonsUtils.formatUTCWithZone(CalendarUtils.now()) });
new Object[] { CommonsUtils.formatClientDate(CalendarUtils.now()) });
return formatSuccess(data);
}
}

View file

@ -45,7 +45,7 @@ public class FlowParser {
final private JSON flowDefinition;
private Map<String, FlowNode> nodeMap = new HashMap<>();
private final Map<String, FlowNode> nodeMap = new HashMap<>();
/**
* @param flowDefinition
@ -128,7 +128,7 @@ public class FlowParser {
next.sort((o1, o2) -> {
int p1 = ((FlowBranch) o1).getPriority();
int p2 = ((FlowBranch) o2).getPriority();
return p1 > p2 ? 1 : (p1 == p2 ? 0 : -1);
return Integer.compare(p1, p2);
});
return next;
}
@ -143,6 +143,18 @@ public class FlowParser {
}
throw new ApprovalException("无效节点 : " + nodeId);
}
/**
* 是否有审批人节点没有审批人节点的无效
*
* @return
*/
public boolean hasApproverNode() {
for (FlowNode node : nodeMap.values()) {
if (node.getType().equals(FlowNode.TYPE_APPROVER)) return true;
}
return false;
}
/**
* @return

View file

@ -18,6 +18,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
package com.rebuild.server.configuration;
import com.alibaba.fastjson.JSONObject;
import com.rebuild.server.business.approval.FlowParser;
/**
@ -28,6 +29,8 @@ import com.rebuild.server.business.approval.FlowParser;
*/
public class FlowDefinition extends ConfigEntry {
private static final long serialVersionUID = 9146239943240893998L;
transient private FlowParser flowParser;
/**
* @return
@ -40,6 +43,17 @@ public class FlowDefinition extends ConfigEntry {
* @return
*/
public FlowParser createFlowParser() {
return new FlowParser(getJSON("flowDefinition"));
if (flowParser == null) {
flowParser = new FlowParser(getJSON("flowDefinition"));
}
return flowParser;
}
/**
* @return
*/
public boolean isWorkable() {
JSONObject def = (JSONObject) getJSON("flowDefinition");
return def != null && createFlowParser().hasApproverNode();
}
}

View file

@ -21,6 +21,7 @@ import com.rebuild.server.metadata.MetadataHelper;
import com.rebuild.server.service.bizz.UserHelper;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
@ -33,6 +34,8 @@ public class RobotApprovalManager implements ConfigManager {
public static final RobotApprovalManager instance = new RobotApprovalManager();
private RobotApprovalManager() {}
private static final String CKEY_PREFIX = "RobotApprovalManager2-";
/**
* 获取实体/记录流程状态
@ -95,10 +98,10 @@ public class RobotApprovalManager implements ConfigManager {
// 过滤可用的
List<FlowDefinition> workable = new ArrayList<>();
for (FlowDefinition def : defs) {
if (def.isDisabled() || def.getJSON("flowDefinition") == null) {
if (def.isDisabled() || !def.isWorkable()) {
continue;
}
FlowParser flowParser = def.createFlowParser();
FlowNode root = flowParser.getNode("ROOT"); // 发起人节点
@ -122,24 +125,24 @@ public class RobotApprovalManager implements ConfigManager {
* @return
*/
public FlowDefinition[] getFlowDefinitions(Entity entity) {
final String cKey = "RobotApprovalManager-" + entity.getName();
final String cKey = CKEY_PREFIX + entity.getName();
FlowDefinition[] defs = (FlowDefinition[]) Application.getCommonCache().getx(cKey);
if (defs != null) {
return defs;
}
Object[][] array = Application.createQueryNoFilter(
"select flowDefinition,isDisabled,name,configId from RobotApprovalConfig where belongEntity = ?")
"select flowDefinition,isDisabled,name,configId,modifiedOn from RobotApprovalConfig where belongEntity = ?")
.setParameter(1, entity.getName())
.array();
List<FlowDefinition> list = new ArrayList<>();
for (Object[] o : array) {
FlowDefinition def = new FlowDefinition();
def.set("flowDefinition", JSON.parseObject((String) o[0]));
def.set("disabled", o[1]);
def.set("name", o[2]);
def.set("id", o[3]);
FlowDefinition def = (FlowDefinition) new FlowDefinition()
.set("flowDefinition", JSON.parseObject((String) o[0]))
.set("disabled", o[1])
.set("name", o[2])
.set("id", o[3]);
list.add(def);
}
@ -150,7 +153,7 @@ public class RobotApprovalManager implements ConfigManager {
@Override
public void clean(Object entity) {
final String cKey = "RobotApprovalManager-" + ((Entity) entity).getName();
final String cKey = CKEY_PREFIX + ((Entity) entity).getName();
Application.getCommonCache().evict(cKey);
}
}

View file

@ -11,7 +11,10 @@ import cn.devezhao.commons.CalendarUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import java.util.*;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -119,14 +122,12 @@ public class CommonsUtils {
}
/**
* 客户端所需的日期时间格式带时区偏移
* @param date
* @return
*/
public static String formatUTCWithZone(Date date) {
public static String formatClientDate(Date date) {
if (date == null) return null;
String datetime = CalendarUtils.getUTCDateTimeFormat().format(date);
int offset = CalendarUtils.getInstance().get(Calendar.ZONE_OFFSET);
offset = offset / 1000 / 60 / 60; // in hours
return datetime + " UTC" + (offset >= 0 ? "+" : "") + offset;
return CalendarUtils.getUTCWithZoneDateTimeFormat().format(date);
}
}

View file

@ -25,12 +25,7 @@ import cn.devezhao.persist4j.engine.ID;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.rebuild.server.Application;
import com.rebuild.server.business.approval.ApprovalException;
import com.rebuild.server.business.approval.ApprovalHelper;
import com.rebuild.server.business.approval.ApprovalProcessor;
import com.rebuild.server.business.approval.ApprovalState;
import com.rebuild.server.business.approval.FlowNodeGroup;
import com.rebuild.server.business.approval.FormBuilder;
import com.rebuild.server.business.approval.*;
import com.rebuild.server.configuration.FlowDefinition;
import com.rebuild.server.configuration.RobotApprovalManager;
import com.rebuild.server.metadata.EntityHelper;
@ -46,7 +41,6 @@ import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@ -60,9 +54,9 @@ public class ApprovalControll extends BasePageControll {
@RequestMapping("workable")
public void getWorkable(HttpServletRequest request, HttpServletResponse response) {
ID recordId = getIdParameterNotNull(request, "record");
ID user = getRequestUser(request);
final ID user = getRequestUser(request);
final ID recordId = getIdParameterNotNull(request, "record");
FlowDefinition[] defs = RobotApprovalManager.instance.getFlowDefinitions(recordId, user);
JSONArray data = new JSONArray();
for (FlowDefinition d : defs) {
@ -73,11 +67,10 @@ public class ApprovalControll extends BasePageControll {
@RequestMapping("state")
public void getApprovalState(HttpServletRequest request, HttpServletResponse response) {
ID recordId = getIdParameterNotNull(request, "record");
ID user = getRequestUser(request);
Object[] state = Application.getQueryFactory().unique(recordId,
EntityHelper.ApprovalId, EntityHelper.ApprovalState);
final ID user = getRequestUser(request);
final ID recordId = getIdParameterNotNull(request, "record");
Object[] state = ApprovalHelper.getApprovalState(recordId);
if (state == null) {
writeFailure(response, "无效记录");
return;
@ -85,7 +78,7 @@ public class ApprovalControll extends BasePageControll {
Map<String, Object> data = new HashMap<>();
int stateVal = ObjectUtils.toInt(state[1], ApprovalState.DRAFT.getState());
int stateVal = ObjectUtils.toInt(state[2], ApprovalState.DRAFT.getState());
data.put("state", stateVal);
ID useApproval = (ID) state[0];
if (useApproval != null) {
@ -118,10 +111,10 @@ public class ApprovalControll extends BasePageControll {
@RequestMapping("fetch-nextstep")
public void fetchNextStep(HttpServletRequest request, HttpServletResponse response) {
ID recordId = getIdParameterNotNull(request, "record");
ID approvalId = getIdParameterNotNull(request, "approval");
ID user = getRequestUser(request);
final ID user = getRequestUser(request);
final ID recordId = getIdParameterNotNull(request, "record");
final ID approvalId = getIdParameterNotNull(request, "approval");
ApprovalProcessor approvalProcessor = new ApprovalProcessor(recordId, approvalId);
FlowNodeGroup nextNodes = approvalProcessor.getNextNodes();
@ -162,9 +155,10 @@ public class ApprovalControll extends BasePageControll {
}
@RequestMapping("submit")
public void doSubmit(HttpServletRequest request, HttpServletResponse response) throws IOException {
ID recordId = getIdParameterNotNull(request, "record");
ID approvalId = getIdParameterNotNull(request, "approval");
public void doSubmit(HttpServletRequest request, HttpServletResponse response) {
final ID recordId = getIdParameterNotNull(request, "record");
final ID approvalId = getIdParameterNotNull(request, "approval");
JSONObject selectUsers = (JSONObject) ServletUtils.getRequestJson(request);
try {
@ -180,10 +174,10 @@ public class ApprovalControll extends BasePageControll {
}
@RequestMapping("approve")
public void doApprove(HttpServletRequest request, HttpServletResponse response) throws IOException {
ID approver = getRequestUser(request);
ID recordId = getIdParameterNotNull(request, "record");
int state = getIntParameter(request, "state", ApprovalState.REJECTED.getState());
public void doApprove(HttpServletRequest request, HttpServletResponse response) {
final ID approver = getRequestUser(request);
final ID recordId = getIdParameterNotNull(request, "record");
final int state = getIntParameter(request, "state", ApprovalState.REJECTED.getState());
JSONObject post = (JSONObject) ServletUtils.getRequestJson(request);
JSONObject selectUsers = post.getJSONObject("selectUsers");
@ -212,7 +206,7 @@ public class ApprovalControll extends BasePageControll {
}
@RequestMapping("cancel")
public void doCancel(HttpServletRequest request, HttpServletResponse response) throws IOException {
public void doCancel(HttpServletRequest request, HttpServletResponse response) {
ID recordId = getIdParameterNotNull(request, "record");
try {
new ApprovalProcessor(recordId).cancel();
@ -223,7 +217,7 @@ public class ApprovalControll extends BasePageControll {
}
@RequestMapping("revoke")
public void doRevoke(HttpServletRequest request, HttpServletResponse response) throws IOException {
public void doRevoke(HttpServletRequest request, HttpServletResponse response) {
ID recordId = getIdParameterNotNull(request, "record");
try {
new ApprovalProcessor(recordId).revoke();
@ -235,7 +229,8 @@ public class ApprovalControll extends BasePageControll {
@RequestMapping("flow-definition")
public void getFlowDefinition(HttpServletRequest request, HttpServletResponse response) {
ID approvalId = getIdParameterNotNull(request, "id");
final ID approvalId = getIdParameterNotNull(request, "id");
Object[] belongEntity = Application.createQueryNoFilter(
"select belongEntity from RobotApprovalConfig where configId = ?")
.setParameter(1, approvalId)

View file

@ -11,7 +11,6 @@ import cn.devezhao.bizz.privileges.impl.BizzPermission;
import cn.devezhao.bizz.security.AccessDeniedException;
import cn.devezhao.commons.CalendarUtils;
import cn.devezhao.commons.web.ServletUtils;
import cn.devezhao.momentjava.Moment;
import cn.devezhao.persist4j.Entity;
import cn.devezhao.persist4j.Field;
import cn.devezhao.persist4j.Record;
@ -33,6 +32,7 @@ import com.rebuild.server.service.ServiceSpec;
import com.rebuild.server.service.base.BulkContext;
import com.rebuild.server.service.bizz.UserHelper;
import com.rebuild.server.service.bizz.privileges.User;
import com.rebuild.utils.CommonsUtils;
import com.rebuild.utils.JSONUtils;
import com.rebuild.web.BaseControll;
import com.rebuild.web.IllegalParameterException;
@ -353,8 +353,8 @@ public class GeneralOperatingControll extends BaseControll {
return;
}
recordMeta[0] = Moment.moment((Date) recordMeta[0]).fromNow();
recordMeta[1] = Moment.moment((Date) recordMeta[1]).fromNow();
recordMeta[0] = CommonsUtils.formatClientDate((Date) recordMeta[0]);
recordMeta[1] = CommonsUtils.formatClientDate((Date) recordMeta[1]);
String[] owning = null;
List<String[]> sharingList = null;

View file

@ -19,6 +19,7 @@ import com.rebuild.server.business.feeds.FeedsType;
import com.rebuild.server.helper.fieldvalue.FieldValueWrapper;
import com.rebuild.server.metadata.EntityHelper;
import com.rebuild.server.metadata.MetadataHelper;
import com.rebuild.utils.CommonsUtils;
import com.rebuild.utils.JSONUtils;
import com.rebuild.web.BaseControll;
import org.apache.commons.lang.ArrayUtils;
@ -67,7 +68,7 @@ public class RelatedListControll extends BaseControll {
nameValue = FieldValueWrapper.NO_LABEL_PREFIX + o[0].toString().toUpperCase();
}
o[1] = nameValue;
o[2] = Moment.moment((Date) o[2]).fromNow();
o[2] = CommonsUtils.formatClientDate((Date) o[2]);
}
JSON ret = JSONUtils.toJSONObject(

View file

@ -26,6 +26,7 @@ import com.rebuild.server.metadata.entity.EasyMeta;
import com.rebuild.server.service.bizz.RoleService;
import com.rebuild.server.service.bizz.UserHelper;
import com.rebuild.server.service.configuration.DashboardConfigService;
import com.rebuild.utils.CommonsUtils;
import com.rebuild.utils.JSONUtils;
import com.rebuild.web.BasePageControll;
import org.apache.commons.lang.ArrayUtils;
@ -149,7 +150,7 @@ public class DashboardControll extends BasePageControll {
charts = Application.createQueryNoFilter(sql).setParameter(1, useBizz).array();
for (Object[] o : charts) {
o[3] = Moment.moment((Date) o[3]).fromNow();
o[3] = CommonsUtils.formatClientDate((Date) o[3]);
o[4] = EasyMeta.getLabel(MetadataHelper.getEntity((String) o[4]));
}
}

View file

@ -8,9 +8,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
package com.rebuild.web.feeds;
import cn.devezhao.bizz.security.member.Team;
import cn.devezhao.commons.CalendarUtils;
import cn.devezhao.commons.web.ServletUtils;
import cn.devezhao.momentjava.Moment;
import cn.devezhao.persist4j.engine.ID;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
@ -23,6 +21,7 @@ import com.rebuild.server.metadata.entity.EasyMeta;
import com.rebuild.server.service.bizz.UserHelper;
import com.rebuild.server.service.bizz.privileges.User;
import com.rebuild.server.service.query.AdvFilterParser;
import com.rebuild.utils.CommonsUtils;
import com.rebuild.utils.JSONUtils;
import com.rebuild.web.BasePageControll;
import org.apache.commons.lang.StringUtils;
@ -33,7 +32,6 @@ import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@ -52,7 +50,7 @@ public class FeedsListControll extends BasePageControll {
* @see com.rebuild.server.business.feeds.FeedsType
*/
@RequestMapping("/feeds/{type}")
public ModelAndView pageIndex(@PathVariable String type, HttpServletRequest request) throws IOException {
public ModelAndView pageIndex(@PathVariable String type, HttpServletRequest request) {
ModelAndView mv = createModelAndView("/feeds/home.jsp");
mv.getModel().put("feedsType", type);
@ -64,7 +62,7 @@ public class FeedsListControll extends BasePageControll {
}
@RequestMapping("/feeds/feeds-list")
public void fetchFeeds(HttpServletRequest request, HttpServletResponse response) throws IOException {
public void fetchFeeds(HttpServletRequest request, HttpServletResponse response) {
final ID user = getRequestUser(request);
JSON filter = ServletUtils.getRequestJson(request);
@ -183,7 +181,7 @@ public class FeedsListControll extends BasePageControll {
}
@RequestMapping("/feeds/comments-list")
public void fetchComments(HttpServletRequest request, HttpServletResponse response) throws IOException {
public void fetchComments(HttpServletRequest request, HttpServletResponse response) {
final ID user = getRequestUser(request);
ID feeds = getIdParameterNotNull(request, "feeds");
@ -227,9 +225,8 @@ public class FeedsListControll extends BasePageControll {
item.put("id", o[0]);
item.put("self", o[1].equals(user));
item.put("createdBy", new Object[] { o[1], UserHelper.getName((ID) o[1]) });
item.put("createdOn", CalendarUtils.getUTCDateTimeFormat().format(o[2]));
item.put("createdOnFN", Moment.moment((Date) o[2]).fromNow());
item.put("modifedOn", CalendarUtils.getUTCDateTimeFormat().format(o[3]));
item.put("createdOn", CommonsUtils.formatClientDate((Date) o[2]));
item.put("modifedOn", CommonsUtils.formatClientDate((Date) o[3]));
item.put("content", FeedsHelper.formatContent((String) o[4]));
if (o[5] != null) {
item.put("images", JSON.parse((String) o[5]));

View file

@ -1,19 +1,8 @@
/*
rebuild - Building your business-systems freely.
Copyright (C) 2018-2019 devezhao <zhaofang123@gmail.com>
Copyright (c) REBUILD <https://getrebuild.com/> and its owners. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
See LICENSE and COMMERCIAL in the project root for license information.
*/
package com.rebuild.web.files;
@ -34,6 +23,7 @@ import com.rebuild.server.metadata.MetadataSorter;
import com.rebuild.server.metadata.entity.DisplayType;
import com.rebuild.server.metadata.entity.EasyMeta;
import com.rebuild.server.service.bizz.UserHelper;
import com.rebuild.utils.CommonsUtils;
import com.rebuild.utils.JSONUtils;
import com.rebuild.web.BasePageControll;
import org.apache.commons.io.FileUtils;
@ -164,7 +154,7 @@ public class FileListControll extends BasePageControll {
item.put("fileType", o[2]);
item.put("fileSize", FileUtils.byteCountToDisplaySize(ObjectUtils.toLong(o[3])));
item.put("uploadBy", new Object[] { o[4], UserHelper.getName((ID) o[4]) });
item.put("uploadOn", Moment.moment((Date) o[5]).fromNow());
item.put("uploadOn", CommonsUtils.formatClientDate((Date) o[5]));
item.put("inFolder", o[6]);
ID relatedRecord = (ID) o[7];

View file

@ -7,7 +7,6 @@ See LICENSE and COMMERCIAL in the project root for license information.
package com.rebuild.web.notification;
import cn.devezhao.momentjava.Moment;
import cn.devezhao.persist4j.Record;
import cn.devezhao.persist4j.engine.ID;
import com.rebuild.server.Application;
@ -15,6 +14,7 @@ import com.rebuild.server.business.approval.ApprovalState;
import com.rebuild.server.metadata.EntityHelper;
import com.rebuild.server.service.bizz.UserHelper;
import com.rebuild.server.service.notification.MessageBuilder;
import com.rebuild.utils.CommonsUtils;
import com.rebuild.utils.JSONUtils;
import com.rebuild.web.BasePageControll;
import org.springframework.stereotype.Controller;
@ -23,7 +23,6 @@ import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
/**
@ -38,23 +37,23 @@ import java.util.Date;
public class NotificationControll extends BasePageControll {
@RequestMapping("/notifications")
public ModelAndView pageIndex(HttpServletRequest request) throws IOException {
public ModelAndView pageIndex() {
return createModelAndView("/notification/messages.jsp");
}
@RequestMapping("/notifications/todo")
public ModelAndView pageTodo(HttpServletRequest request) throws IOException {
public ModelAndView pageTodo() {
return createModelAndView("/notification/todo.jsp");
}
@RequestMapping("/notification/check-state")
public void checkMessage(HttpServletRequest request, HttpServletResponse response) throws IOException {
public void checkMessage(HttpServletRequest request, HttpServletResponse response) {
int unread = Application.getNotifications().getUnreadMessage(getRequestUser(request));
writeSuccess(response, JSONUtils.toJSONObject("unread", unread));
}
@RequestMapping("/notification/make-read")
public void toggleUnread(HttpServletRequest request, HttpServletResponse response) throws IOException {
public void toggleUnread(HttpServletRequest request, HttpServletResponse response) {
ID user = getRequestUser(request);
String ids = getParameter(request, "id");
@ -70,9 +69,7 @@ public class NotificationControll extends BasePageControll {
}
for (String id : ids.split(",")) {
if (!ID.isId(id)) {
continue;
}
if (!ID.isId(id)) continue;
Record record = EntityHelper.forUpdate(ID.valueOf(id), user);
record.setBoolean("unread", false);
@ -82,7 +79,7 @@ public class NotificationControll extends BasePageControll {
}
@RequestMapping("/notification/messages")
public void listMessage(HttpServletRequest request, HttpServletResponse response) throws IOException {
public void listMessage(HttpServletRequest request, HttpServletResponse response) {
ID user = getRequestUser(request);
int pn = getIntParameter(request, "pageNo", 1);
int ps = getIntParameter(request, "pageSize", 40);
@ -107,14 +104,14 @@ public class NotificationControll extends BasePageControll {
Object[] m = array[i];
m[0] = new Object[] { m[0], UserHelper.getName((ID) m[0]) };
m[1] = MessageBuilder.formatMessage((String) m[1], !preview, true);
m[2] = Moment.moment((Date) m[2]).fromNow();
m[2] = CommonsUtils.formatClientDate((Date) m[2]);
array[i] = m;
}
writeSuccess(response, array);
}
@RequestMapping("/notification/approvals")
public void listApprovals(HttpServletRequest request, HttpServletResponse response) throws IOException {
public void listApprovals(HttpServletRequest request, HttpServletResponse response) {
ID user = getRequestUser(request);
int pn = getIntParameter(request, "pageNo", 1);
int ps = getIntParameter(request, "pageSize", 40);
@ -130,7 +127,7 @@ public class NotificationControll extends BasePageControll {
Object[] m = array[i];
m[0] = new Object[] { m[0], UserHelper.getName((ID) m[0]) };
m[1] = MessageBuilder.formatMessage((String) m[1]);
m[2] = Moment.moment((Date) m[2]).fromNow();
m[2] = CommonsUtils.formatClientDate((Date) m[2]);
// 审批状态
ID approvalStep = (ID) m[3];

View file

@ -7,6 +7,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
package com.rebuild.web.project;
import cn.devezhao.commons.DateFormatUtils;
import cn.devezhao.persist4j.engine.ID;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
@ -49,7 +50,7 @@ public class ProjectCommentControll extends BaseControll {
JSONArray ret = new JSONArray();
for (Object[] o : array) {
if (o[2] != null) o[2] = JSON.parse((String) o[2]);
o[3] = CommonsUtils.formatUTCWithZone((Date) o[3]);
o[3] = CommonsUtils.formatClientDate((Date) o[3]);
o[4] = new Object[]{ o[4], UserHelper.getName((ID) o[4]) };
o[5] = user.equals(o[5]);

View file

@ -7,6 +7,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
package com.rebuild.web.project;
import cn.devezhao.commons.DateFormatUtils;
import cn.devezhao.commons.web.ServletUtils;
import cn.devezhao.persist4j.engine.ID;
import com.alibaba.fastjson.JSON;
@ -148,9 +149,9 @@ public class ProjectTaskControll extends BasePageControll {
String taskNumber = o[1].toString();
if (StringUtils.isNotBlank((String) o[0])) taskNumber = o[0] + "-" + taskNumber;
String createdOn = CommonsUtils.formatUTCWithZone((Date) o[4]);
String deadline = CommonsUtils.formatUTCWithZone((Date) o[5]);
String endTime = CommonsUtils.formatUTCWithZone((Date) o[10]);
String createdOn = CommonsUtils.formatClientDate((Date) o[4]);
String deadline = CommonsUtils.formatClientDate((Date) o[5]);
String endTime = CommonsUtils.formatClientDate((Date) o[10]);
Object[] executor = o[6] == null ? null : new Object[]{ o[6], UserHelper.getName((ID) o[6]) };

View file

@ -4,6 +4,7 @@
<script src="${baseUrl}/assets/lib/bootstrap.min.js"></script>
<script src="${baseUrl}/assets/lib/widget/perfect-scrollbar.jquery.min.js"></script>
<script src="${baseUrl}/assets/lib/widget/mprogress.min.js"></script>
<script src="${baseUrl}/assets/lib/moment-with-locales.min.js?v=2.27.0"></script>
<script src="${baseUrl}/assets/lib/widget/bootstrap-datetimepicker.min.js?v=2.4.4"></script>
<script src="${baseUrl}/assets/lib/jquery.html5uploader.js"></script>
<script src="${baseUrl}/assets/lib/qiniu.min.js"></script>

View file

@ -978,7 +978,7 @@ class ChartSelect extends RbModalHandler {
<span className="float-left chart-icon"><i className={item[2]}></i></span>
<span className="float-left title">
<strong>{item[1]}</strong>
<p className="text-muted fs-12">{item[4] && <span>{item[4]}</span>}<span>{item[3]}</span></p>
<p className="text-muted fs-12">{item[4] && <span>{item[4]}</span>} <DateShow date={item[3]} /></p>
</span>
<span className="float-right">
{this.state.appended.includes(item[0])

View file

@ -96,7 +96,7 @@ class NodeSpec extends React.Component {
serialize() {
//
if (this.nodeType === 'approver' || this.nodeType === 'cc') {
let users = this.state.data ? this.state.data.users : ['']
const users = this.state.data ? this.state.data.users : ['']
if (users[0] === 'SPEC') {
RbHighbar.create(NTs[this.nodeType][2])
this.setState({ hasError: true })
@ -117,8 +117,8 @@ class NodeGroupSpec extends React.Component {
}
renderNodes() {
let nodes = (this.state.nodes || []).map((item) => {
let props = { ...item, key: 'kn-' + item.nodeId, $$$parent: this }
const nodes = (this.state.nodes || []).map((item) => {
const props = { ...item, key: 'kn-' + item.nodeId, $$$parent: this }
if (item.type === 'condition') return <ConditionNode {...props} />
else return <SimpleNode {...props} />
})
@ -126,13 +126,13 @@ class NodeGroupSpec extends React.Component {
}
onRef = (nodeRef, remove) => {
let nodeId = nodeRef.props.nodeId
const nodeId = nodeRef.props.nodeId
this.__nodeRefs[nodeId] = (remove ? null : nodeRef)
}
addNode = (type, depsNodeId, call) => {
let n = { type: type, nodeId: $random(type === 'condition' ? 'COND' : 'NODE') }
let nodes = []
const n = { type: type, nodeId: $random(type === 'condition' ? 'COND' : 'NODE') }
const nodes = []
if (depsNodeId) {
if (depsNodeId === 'ROOT' || depsNodeId === 'COND') nodes.push(n)
this.state.nodes.forEach((item) => {
@ -157,7 +157,7 @@ class NodeGroupSpec extends React.Component {
}
removeNode = (nodeId) => {
let nodes = []
const nodes = []
this.state.nodes.forEach((item) => {
if (nodeId !== item.nodeId) nodes.push(item)
})
@ -165,10 +165,10 @@ class NodeGroupSpec extends React.Component {
}
serialize() {
let ns = []
const ns = []
for (let i = 0; i < this.state.nodes.length; i++) {
let nodeRef = this.__nodeRefs[this.state.nodes[i].nodeId]
let s = nodeRef.serialize()
const nodeRef = this.__nodeRefs[this.state.nodes[i].nodeId]
const s = nodeRef.serialize()
if (!s) return false
ns.push(s)
}
@ -235,12 +235,12 @@ class ConditionNode extends NodeSpec {
}
onRef = (branchRef, remove) => {
let nodeId = branchRef.props.nodeId
const nodeId = branchRef.props.nodeId
this.__branchRefs[nodeId] = (remove ? null : branchRef)
}
addBranch = () => {
let bs = this.state.branches
const bs = this.state.branches
bs.push({ nodeId: $random('COND') })
this.setState({ branches: bs })
}
@ -250,7 +250,7 @@ class ConditionNode extends NodeSpec {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
}
let bs = []
const bs = []
this.state.branches.forEach((item) => {
if (nodeId !== item.nodeId) bs.push(item)
})
@ -263,7 +263,7 @@ class ConditionNode extends NodeSpec {
serialize() {
let holdANode = null
let bs = []
const bs = []
for (let i = 0; i < this.state.branches.length; i++) {
let branchRef = this.__branchRefs[this.state.branches[i].nodeId]
if (!holdANode) holdANode = branchRef
@ -350,7 +350,7 @@ class ConditionBranch extends NodeGroupSpec {
}
serialize() {
let s = super.serialize()
const s = super.serialize()
if (!s || s.nodes.length === 0) {
this.setState({ hasError: true })
if (s !== false) RbHighbar.create('请为分支添加审批人或抄送人')
@ -483,7 +483,7 @@ class StartNodeConfig extends RbFormHandler {
}
save = () => {
let d = {
const d = {
nodeName: this.state.nodeName,
users: this.state.users === 'SPEC' ? this._UserSelector.getSelected() : [this.state.users]
}
@ -625,7 +625,7 @@ class ApproverNodeConfig extends StartNodeConfig {
}
removeEditableField(field) {
let fs = []
const fs = []
this.state.editableFields.forEach((item) => {
if (item.field !== field) fs.push(item)
})
@ -698,7 +698,7 @@ class ConditionBranchConfig extends StartNodeConfig {
<div className="header">
<input type="text" placeholder="分支条件" data-id="nodeName" value={this.state.nodeName || ''} onChange={this.handleChange} maxLength="20" />
</div>
{this.state.isLast && <div className="alert alert-warning">条件分支将作为最终分支匹配其他条件</div>}
{this.state.isLast && <div className="alert alert-warning">分支将作为最终分支匹配其他条件</div>}
<AdvFilter filter={this.state.filter} entity={this.props.entity} confirm={this.save} cancel={this.cancel} canNoFilters={true} />
</div>
}
@ -750,16 +750,21 @@ class RbFlowCanvas extends NodeGroupSpec {
$('.box-scale').draggable({ cursor: 'move', axis: 'x', scroll: false })
$('#rbflow').removeClass('rb-loading-active')
const btns = $('.J_save').click(() => {
const $btns = $('.J_save').click(() => {
const s = this.serialize()
if (!s) return
let _data = { flowDefinition: s }
_data.metadata = { entity: 'RobotApprovalConfig', id: wpc.configId }
btns.button('loading')
$.post('/app/entity/record-save', JSON.stringify(_data), (res) => {
let data = {
flowDefinition: s,
metadata: { id: wpc.configId }
}
data = JSON.stringify(data)
const noApproverNode = !data.includes('"approver"')
$btns.button('loading')
$.post('/app/entity/record-save', data, (res) => {
if (res.error_code === 0) {
RbAlert.create('保存并发布成功', {
RbAlert.create(`保存并发布成功${noApproverNode ? '(未添加审批节点,暂不可用)' : ''}`, {
type: 'primary',
cancelText: '返回列表',
cancel: () => location.replace('../approvals'),
@ -767,7 +772,7 @@ class RbFlowCanvas extends NodeGroupSpec {
confirm: () => location.reload()
})
} else RbHighbar.error(res.error_msg)
btns.button('reset')
$btns.button('reset')
})
})
@ -789,6 +794,7 @@ class RbFlowCanvas extends NodeGroupSpec {
}
}
// ~
class DlgFields extends RbModalHandler {
constructor(props) {
@ -820,7 +826,7 @@ class DlgFields extends RbModalHandler {
}
confirm = () => {
let selected = []
const selected = []
$(this._fields).find('input:checked').each(function () {
selected.push(this.value)
})
@ -830,6 +836,7 @@ class DlgFields extends RbModalHandler {
}
}
// ~~
class DlgCopy extends ConfigFormDlg {
constructor(props) {

View file

@ -71,7 +71,8 @@ class FeedsList extends React.Component {
<span className="float-right badge">{FeedsTypes[item.type] || '动态'}</span>
<a>{item.createdBy[1]}</a>
<p className="text-muted fs-12 m-0">
<span title={item.createdOn}>{item.createdOnFN}{item.createdOn !== item.modifedOn && <span className="text-danger" title={`编辑于 ${item.modifedOn}`}> (已编辑)</span>}</span>
<DateShow date={item.createdOn} />
{item.createdOn !== item.modifedOn && <span className="text-danger" title={`编辑于 ${item.modifedOn}`}> (已编辑)</span>}
&nbsp;&nbsp;·&nbsp;&nbsp;
{typeof item.scope === 'string' ? item.scope : <span>{item.scope[1]} <i title="团队成员可见" className="zmdi zmdi-accounts fs-14 down-1"></i></span>}
</p>
@ -236,7 +237,7 @@ class FeedsComments extends React.Component {
{__renderRichContent(item)}
<div className="actions">
<div className="float-left text-muted fs-12 time">
<span title={item.createdOn}>{item.createdOnFN}</span>
<DateShow date={item.createdOn} />
</div>
<ul className="list-unstyled m-0">
{item.self && <li className="list-inline-item mr-2">

View file

@ -16,37 +16,39 @@ class FilesList extends React.Component {
render() {
const hasFiles = (this.state.files || []).length > 0
return <div className="file-list">
{(this.state.files || []).map((item) => {
let checked = this.state.currentActive === item.id
return <div key={`file-${item.id}`} className={`file-list-item ${checked ? 'active' : ''}`} onClick={() => this._handleClick(item.id)}>
<div className="check">
<div className="custom-control custom-checkbox m-0">
<input className="custom-control-input" type="checkbox" checked={checked} onChange={() => this._handleClick(item.id)} />
<label className="custom-control-label"></label>
return (
<div className="file-list">
{(this.state.files || []).map((item) => {
const checked = this.state.currentActive === item.id
return <div key={`file-${item.id}`} className={`file-list-item ${checked ? 'active' : ''}`} onClick={() => this._handleClick(item.id)}>
<div className="check">
<div className="custom-control custom-checkbox m-0">
<input className="custom-control-input" type="checkbox" checked={checked} onChange={() => this._handleClick(item.id)} />
<label className="custom-control-label"></label>
</div>
</div>
<div className="type"><i className="file-icon" data-type={item.fileType}></i></div>
<div className="detail">
<a onClick={(e) => previewFile(e, item.filePath, item.relatedRecord ? item.relatedRecord[0] : null)}>{$fileCutName(item.filePath)}</a>
<div className="extras">{this.renderExtras(item)}</div>
</div>
<div className="info"><DateShow date={item.uploadOn} /></div>
<div className="info">{item.uploadBy[1]}</div>
</div>
<div className="type"><i className="file-icon" data-type={item.fileType}></i></div>
<div className="detail">
<a onClick={(e) => previewFile(e, item.filePath, item.relatedRecord ? item.relatedRecord[0] : null)}>{$fileCutName(item.filePath)}</a>
<div className="extras">{this.renderExtras(item)}</div>
})}
{this.state.currentLen >= PAGE_SIZE &&
<div className="text-center mt-3 mb-3">
<a href="#mores" onClick={(e) => { this.loadData(null, this.__pageNo + 1); e.preventDefault() }}>显示更多</a>
</div>
<div className="info">{item.uploadOn}</div>
<div className="info">{item.uploadBy[1]}</div>
</div>
})}
{this.state.currentLen >= PAGE_SIZE &&
<div className="text-center mt-3 mb-3">
<a href="#mores" onClick={(e) => { this.loadData(null, this.__pageNo + 1); e.preventDefault() }}>显示更多</a>
</div>
}
{this.__pageNo > 1 && this.state.currentLen > 0 && this.state.currentLen < PAGE_SIZE &&
<div className="text-center mt-3 mb-3 text-muted">没有更多了</div>
}
{this.__pageNo === 1 && !hasFiles &&
<div className="list-nodata pt-8 pb-8"><i className="zmdi zmdi-folder-outline"></i><p>暂无相关文件</p></div>
}
</div>
}
{this.__pageNo > 1 && this.state.currentLen > 0 && this.state.currentLen < PAGE_SIZE &&
<div className="text-center mt-3 mb-3 text-muted">没有更多了</div>
}
{this.__pageNo === 1 && !hasFiles &&
<div className="list-nodata pt-8 pb-8"><i className="zmdi zmdi-folder-outline"></i><p>暂无相关文件</p></div>
}
</div>
)
}
_handleClick(id) {
@ -58,7 +60,9 @@ class FilesList extends React.Component {
renderExtras(item) {
return <React.Fragment >
<span>{item.fileSize}</span>
{item.relatedRecord && <span><a title="点击查看相关记录" onClick={(e) => $stopEvent(e)} href={`${rb.baseUrl}/app/list-and-view?id=${item.relatedRecord[0]}`}>{item.relatedRecord[1]}</a></span>}
{item.relatedRecord &&
<span><a title="点击查看相关记录" onClick={(e) => $stopEvent(e)} href={`${rb.baseUrl}/app/list-and-view?id=${item.relatedRecord[0]}`}>{item.relatedRecord[1]}</a></span>
}
</React.Fragment>
}

View file

@ -67,7 +67,7 @@ class MessageList extends React.Component {
<div className="image"><img src={`${rb.baseUrl}/account/user-avatar/${item[0][0]}`} title={item[0][1]} alt="Avatar" /></div>
<div className="notification-info">
<div className="text" dangerouslySetInnerHTML={{ __html: item[1] }}></div>
<div className="date">{item[2]}</div>
<div className="date"><DateShow date={item[2]} /></div>
</div>
{append
&& <a title="查看相关记录" className="badge link" href={`${rb.baseUrl}/app/list-and-view?id=${item[5]}`}>查看</a>}
@ -146,7 +146,7 @@ class ApprovalList extends MessageList {
<div className="image"><img src={`${rb.baseUrl}/account/user-avatar/${item[0][0]}`} title={item[0][1]} alt="Avatar" /></div>
<div className="notification-info">
<div className="text" dangerouslySetInnerHTML={{ __html: item[1] }}></div>
<div className="date">{item[2]}</div>
<div className="date"><DateShow date={item[2]} /></div>
{(item[3] && item[3][0] === 1) && <span className="badge badge-warning">{item[3][1]}</span>}
{(item[3] && item[3][0] === 2) && <span className="badge badge-secondary">{item[3][1]}</span>}
{(item[3] && item[3][0] === 10) && <span className="badge badge-success">{item[3][1]}</span>}

View file

@ -344,14 +344,13 @@ class Task extends React.Component {
</div>
<div className="task-content">
<div className="task-title text-wrap">{this.state.taskName}</div>
{this.state.endTime
&& <div className="task-time">完成于 <span title={this.state.endTime}>{$fromNow(this.state.endTime)}</span></div>}
<div className="task-time">创建于 <span title={this.state.createdOn}>{$fromNow(this.state.createdOn)}</span></div>
{this.state.endTime && <div className="task-time">完成于 <DateShow date={this.state.endTime} /></div>}
<div className="task-time">创建于 <DateShow date={this.state.createdOn} /></div>
{(!this.state.endTime && this.state.deadline)
&& (
<div className="task-time">
<span className={`badge badge-${this._outDeadline(this.state.deadline) ? 'danger' : 'primary'}`}>
截止时间 <span title={this.state.deadline}>{$fromNow(this.state.deadline)}</span>
截止时间 <DateShow date={this.state.deadline} />
</span>
</div>
)}

View file

@ -494,7 +494,7 @@ class TaskCommentsList extends React.Component {
{TextEditor.renderRichContent(item)}
<div className="actions">
<div className="float-left text-muted fs-12 time">
<span title={item.createdOn}>{$fromNow(item.createdOn)}</span>
<DateShow date={item.createdOn} />
</div>
<ul className="list-unstyled m-0">
{item.self && <li className="list-inline-item mr-2">

View file

@ -95,14 +95,14 @@ class ApprovalProcessor extends React.Component {
submit = () => {
const that = this
if (this._submitForm) this._submitForm.show()
else renderRbcomp(<ApprovalSubmitForm id={this.props.id} />, null, function () { that._submitForm = this })
if (this._SubmitForm) this._SubmitForm.show(null, () => that._SubmitForm.reload())
else renderRbcomp(<ApprovalSubmitForm id={this.props.id} />, null, function () { that._SubmitForm = this })
}
approve = () => {
const that = this
if (this._approveForm) this._approveForm.show()
else renderRbcomp(<ApprovalApproveForm id={this.props.id} approval={this.state.approvalId} entity={this.props.entity} />, null, function () { that._approveForm = this })
if (this._ApproveForm) this._ApproveForm.show()
else renderRbcomp(<ApprovalApproveForm id={this.props.id} approval={this.state.approvalId} entity={this.props.entity} />, null, function () { that._ApproveForm = this })
}
cancel = () => {
@ -184,7 +184,7 @@ class ApprovalUsersForm extends RbFormHandler {
selectCcs: this.state.ccSelfSelecting ? this._ccSelector.getSelected() : []
}
if (this.state.isLastStep !== true) {
if (!this.state.isLastStep) {
if ((this.state.nextApprovers || []).length === 0 && selectUsers.selectApprovers.length === 0) {
RbHighbar.create('请选择审批人')
return false
@ -235,7 +235,9 @@ class ApprovalSubmitForm extends ApprovalUsersForm {
</RbModal>
}
componentDidMount() {
componentDidMount = () => this.reload()
reload() {
$.get(`/app/entity/approval/workable?record=${this.props.id}`, (res) => {
if (res.data && res.data.length > 0) {
this.setState({ approvals: res.data, useApproval: res.data[0].id }, () => {
@ -252,10 +254,7 @@ class ApprovalSubmitForm extends ApprovalUsersForm {
}
post() {
if (!this.state.useApproval) {
RbHighbar.create('请选择审批流程')
return
}
if (!this.state.useApproval) return RbHighbar.create('请选择审批流程')
const selectUsers = this.getSelectUsers()
if (!selectUsers) return
@ -293,10 +292,6 @@ class ApprovalApproveForm extends ApprovalUsersForm {
</RbModal>
}
componentDidMount() {
this.getNextStep()
}
_renderEditableForm() {
const fake = {
state: { id: this.props.id, __formModel: {} }
@ -312,8 +307,10 @@ class ApprovalApproveForm extends ApprovalUsersForm {
</div>
}
componentDidMount = () => this.getNextStep()
post(state) {
let aformData = {}
const aformData = {}
if (this.state.aform && state === 10) {
const fd = this._rbform.__FormData
for (let k in fd) {
@ -330,7 +327,11 @@ class ApprovalApproveForm extends ApprovalUsersForm {
if (!selectUsers) return
}
const data = { remark: this.state.remark || '', selectUsers: selectUsers, aformData: aformData }
const data = {
remark: this.state.remark || '',
selectUsers: selectUsers,
aformData: aformData
}
this.disabled(true)
$.post(`/app/entity/approval/approve?record=${this.props.id}&state=${state}`, JSON.stringify(data), (res) => {
@ -404,7 +405,7 @@ class ApprovalStepViewer extends React.Component {
renderApprovers(s, idx, lastState) {
const kp = 'step-' + idx + '-'
let sss = []
const sss = []
let nodeState = 0
if (s[0].signMode === 'OR') {
s.forEach(item => {
@ -413,7 +414,7 @@ class ApprovalStepViewer extends React.Component {
}
s.forEach(item => {
let approverName = item.approver === rb.currentUser ? '你' : item.approverName
const approverName = item.approver === rb.currentUser ? '你' : item.approverName
let aMsg = `等待 ${approverName} 审批`
if (item.state >= 10) aMsg = `${approverName} ${STATE_NAMES[item.state]}`
if ((nodeState >= 10 || lastState >= 10) && item.state < 10) aMsg = `${approverName} 未进行审批`
@ -463,9 +464,10 @@ class ApprovalStepViewer extends React.Component {
}
//
const _reload = function (a, msg) {
const _reload = function (dlg, msg) {
dlg && dlg.hide()
msg && RbHighbar.success(msg)
a && a.hide()
setTimeout(() => {
if (window.RbViewPage) window.RbViewPage.reload()
if (window.RbListPage) window.RbListPage.reload()

View file

@ -135,7 +135,7 @@ class RbModalHandler extends React.Component {
}
}
// ~~ Modal of Form
// ~~ FormModal
class RbFormHandler extends RbModalHandler {
constructor(props) {
@ -153,7 +153,10 @@ class RbFormHandler extends RbModalHandler {
this.setState(s, call)
this.handleChangeAfter(id, val)
}
handleChangeAfter(name, value) {/* NOOP */ }
handleChangeAfter(name, value) {
// NOOP
}
componentWillUnmount() {
// destroy select2
@ -593,6 +596,11 @@ const UserShow = function (props) {
)
}
// ~~
const DateShow = function (props) {
return props.date ? <span title={props.date}>{$fromNow(props.date)}</span> : null
}
/**
* JSX 组件渲染
* @param {*} jsx

View file

@ -108,7 +108,7 @@ class RbFormModal extends React.Component {
if (res.error_code === 0) {
if (res.data.lastModified !== this.__lastModified) {
// this.setState({ alertMessage: <p><a onClick={() => this.__refresh()}></a></p> })
this.__refresh()
this._refresh()
}
} else if (res.error_msg === 'NO_EXISTS') {
this.setState({ alertMessage: '记录已经不存在,可能已被其他用户删除' })
@ -116,7 +116,7 @@ class RbFormModal extends React.Component {
})
}
__refresh() {
_refresh() {
const hold = { id: this.state.id, entity: this.state.entity }
this.setState({ id: null, alertMessage: null }, () => { this.show(hold) })
}
@ -245,12 +245,12 @@ class RbForm extends React.Component {
/**
* @next {Number}
*/
post(next) { setTimeout(() => this._post(next), 30) }
post = (next) => setTimeout(() => this._post(next), 30)
_post(next) {
const _data = {}
for (let k in this.__FormData) {
const err = this.__FormData[k].error
if (err) { RbHighbar.create(err); return }
if (err) return RbHighbar.create(err)
else _data[k] = this.__FormData[k].value
}
_data.metadata = { entity: this.state.entity, id: this.state.id }

View file

@ -254,7 +254,7 @@ var __loadMessages = function () {
$('<div class="image"><img src="' + rb.baseUrl + '/account/user-avatar/' + item[0][0] + '" alt="Avatar"></div>').appendTo(o)
o = $('<div class="notification-info"></div>').appendTo(o)
$('<div class="text text-truncate">' + item[1] + '</div>').appendTo(o)
$('<span class="date">' + item[2] + '</span>').appendTo(o)
$('<span class="date">' + $fromNow(item[2]) + '</span>').appendTo(o)
})
__loadMessages_state = true
if (res.data.length === 0) $('<li class="text-center mt-4 mb-4 text-muted">暂无消息</li>').appendTo(dest)
@ -559,9 +559,10 @@ var converEmoji = function (text) {
})
return text
}
var $converEmoji = converEmoji
// Use momentjs
var $fromNow = function (date) {
if (!date || !window.moment) return
if (!date || !window.moment) return null
return moment(date.split('UTC')[0].trim()).fromNow()
}

View file

@ -53,7 +53,7 @@ class RbViewForm extends React.Component {
}
renderViewError(message) {
this.setState({ formComponent: __renderError(message) }, () => this.hideLoading())
this.setState({ formComponent: _renderError(message) }, () => this.hideLoading())
$('.view-operating .view-action').empty()
}
@ -96,28 +96,32 @@ class RbViewForm extends React.Component {
saveSingleFieldValue(fieldComp) { setTimeout(() => this._saveSingleFieldValue(fieldComp), 30) }
_saveSingleFieldValue(fieldComp) {
const fieldName = fieldComp.props.field
const val = this.__FormData[fieldName]
const fieldValue = this.__FormData[fieldName]
// Unchanged
if (!val) {
if (!fieldValue) {
fieldComp.toggleEditMode(false)
return
}
if (val.error) {
RbHighbar.create(val.error)
return
if (fieldValue.error) return RbHighbar.create(fieldValue.error)
const data = {
metadata: { entity: this.props.entity, id: this.props.id },
[fieldName]: fieldValue.value
}
const _data = { metadata: { entity: this.props.entity, id: this.props.id } }
_data[fieldName] = val.value
const btns = $(fieldComp._fieldText).find('.edit-oper .btn').button('loading')
$.post('/app/entity/record-save?single=true', JSON.stringify(_data), (res) => {
btns.button('reset')
const $btns = $(fieldComp._fieldText).find('.edit-oper .btn').button('loading')
$.post('/app/entity/record-save?single=true', JSON.stringify(data), (res) => {
$btns.button('reset')
if (res.error_code === 0) {
this.setFieldUnchanged(fieldName)
fieldComp.toggleEditMode(false, res.data[fieldName])
//
parent && parent.RbListPage && parent.RbListPage.reload()
}
else if (res.error_code === 499) {
//
renderRbcomp(<RepeatedViewer entity={this.props.entity} data={res.data} />)
}
else if (res.error_code === 499) renderRbcomp(<RepeatedViewer entity={this.props.entity} data={res.data} />)
else RbHighbar.error(res.error_msg)
})
}
@ -131,7 +135,7 @@ const detectViewElement = function (item) {
return <div className={`col-12 col-sm-${item.isFull ? 12 : 6}`} key={item.key}>{window.detectElement(item)}</div>
}
const __renderError = (message) => {
const _renderError = (message) => {
return (
<div className="alert alert-danger alert-icon mt-5 w-75" style={{ margin: '0 auto' }}>
<div className="icon"><i className="zmdi zmdi-alert-triangle"></i></div>
@ -143,6 +147,7 @@ const __renderError = (message) => {
//
class SelectReport extends React.Component {
state = { ...this.props }
render() {
return (
<div className="modal select-list" ref={(c) => this._dlg = c} tabIndex="-1">
@ -192,11 +197,8 @@ class SelectReport extends React.Component {
// ~
class RelatedList extends React.Component {
state = { ...this.props, viewOpens: {}, viewComponents: {} }
constructor(props) {
super(props)
this.state = { ...props, viewOpens: {}, viewComponents: {} }
}
render() {
return (
<div className={`related-list ${!this.state.list ? 'rb-loading rb-loading-active' : ''}`}>
@ -209,7 +211,7 @@ class RelatedList extends React.Component {
<a href={`#!/View/${this.props.entity.split('.')[0]}/${item[0]}`} onClick={(e) => this._handleView(e)} title="打开">{item[1]}</a>
</div>
<div className="col-2 text-right">
<span className="fs-12 text-muted" title="修改时间">{item[2]}</span>
<span className="fs-12 text-muted" title={`修改时间 ${item[2]}`}>{$fromNow(item[2])}</span>
</div>
</div>
<div className="rbview-form inside">
@ -219,17 +221,18 @@ class RelatedList extends React.Component {
})}
{this.state.showMores && (
<div className="text-center load-mores">
<div><button type="button" className="btn btn-secondary" onClick={() => this.loadList(1)}>加载更多</button></div>
<div><button type="button" className="btn btn-secondary" onClick={() => this.fetchList(1)}>加载更多</button></div>
</div>
)}
</div>
)
}
componentDidMount = () => this.loadList()
loadList(plus) {
componentDidMount = () => this.fetchList()
fetchList(append) {
this.__pageNo = this.__pageNo || 1
if (plus) this.__pageNo += plus
if (append) this.__pageNo += append
const pageSize = 20
$.get(`/app/entity/related-list?masterId=${this.props.master}&related=${this.props.entity}&pageNo=${this.__pageNo}&pageSize=${pageSize}`, (res) => {
const _data = res.data.data || []
@ -255,7 +258,7 @@ class RelatedList extends React.Component {
$.get(`/app/${this.props.entity.split('.')[0]}/view-model?id=${id}`, (res) => {
if (res.error_code > 0 || !!res.data.error) {
const err = res.data.error || res.error_msg
viewComponents[id] = __renderError(err)
viewComponents[id] = _renderError(err)
}
else {
viewComponents[id] = <div className="row">
@ -276,6 +279,7 @@ const FeedsList = window.FeedsList || React.Component
// eslint-disable-next-line no-undef
class ReducedFeedsList extends FeedsList {
state = { ...this.props }
render() {
return (
<div className={`related-list ${!this.state.data ? 'rb-loading rb-loading-active' : ''}`}>
@ -292,13 +296,13 @@ class ReducedFeedsList extends FeedsList {
)
}
fetchFeeds(plus) {
fetchFeeds(append) {
const filter = { entity: 'Feeds', equation: 'AND', items: [] }
filter.items.push({ field: 'type', op: 'EQ', value: 2 })
filter.items.push({ field: 'relatedRecord', op: 'EQ', value: wpc.recordId })
this.__pageNo = this.__pageNo || 1
if (plus) this.__pageNo += plus
if (append) this.__pageNo += append
const pageSize = 20
$.post(`/feeds/feeds-list?pageNo=${this.__pageNo}&sort=&type=&foucs=&pageSize=${pageSize}`, JSON.stringify(filter), (res) => {
@ -376,18 +380,23 @@ const RbViewPage = {
}
},
//
//
initRecordMeta() {
$.get(`/app/entity/record-meta?id=${this.__id}`, (res) => {
if (res.error_code !== 0) {
$('.view-operating').empty()
return
}
for (let k in res.data) {
const v = res.data[k]
if (!v || v === undefined) return
const $el = $('.J_' + k)
if ($el.length === 0) return
if (k === 'owningUser') {
renderRbcomp(<UserShow id={v[0]} name={v[1]} showName={true} deptName={v[2]} onClick={() => this.clickViewUser(v[0])} />, $('.J_owningUser')[0])
renderRbcomp(<UserShow id={v[0]} name={v[1]} showName={true} deptName={v[2]} onClick={() => this.clickViewUser(v[0])} />, $el[0])
} else if (k === 'sharingList') {
const list = $('<ul class="list-unstyled list-inline mb-0"></ul>').appendTo('.J_sharingList')
const _this = this
@ -407,8 +416,12 @@ const RbViewPage = {
} else {
$('.J_sharingList').parent().remove()
}
} else if (k === 'createdOn' || k === 'modifiedOn') {
renderRbcomp(<DateShow date={v} />, $el[0])
} else {
$('<span>' + v + '</span>').appendTo('.J_' + k)
$('<span>' + v + '</span>').appendTo($el)
}
}

View file

@ -65,7 +65,6 @@
</div>
</div>
</div>
<script src="${baseUrl}/assets/lib/moment-with-locales.min.js?v=2.27.0"></script>
<%@ include file="/_include/Foot.jsp"%>
<script>
window.__PageConfig = {

View file

@ -21,7 +21,6 @@
<div id="task-contents"></div>
</div>
<div id="task-comment"></div>
<script src="${baseUrl}/assets/lib/moment-with-locales.min.js?v=2.27.0"></script>
<%@ include file="/_include/Foot.jsp"%>
<script>
window.__PageConfig = {

View file

@ -7,6 +7,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
package com.rebuild.utils;
import cn.devezhao.commons.CalendarUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
@ -95,4 +96,9 @@ public class CommonsUtilsTest {
dest.put("code", code);
dest.put("name", name);
}
@Test
public void formatClientDate() {
System.out.println(CommonsUtils.formatClientDate(CalendarUtils.now()));
}
}