mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 07:25:54 +08:00
Ann read state 85 (#705)
* be: 通过地址定位 * be: Classificatio * be: style * dash darkmode * fix: OverDateOperator * bump lib * feat: feed read status; sync H5 * Update @rbv * be: style * v3.6: 目标值为多引用时保持 `ID[]` * Update @rbv * Update trigger-design.js * fix: class import state * be: style --------- Co-authored-by: devezhao <zhaofang123@gmail.com>
This commit is contained in:
parent
64f2d1168b
commit
d47dfb007f
2
@rbv
2
@rbv
|
@ -1 +1 @@
|
|||
Subproject commit 94c40c80ab261ef0c5f3255421992543f2c0cc80
|
||||
Subproject commit aee43fb969653ab507afca2c12896f8e7bc5989a
|
6
pom.xml
6
pom.xml
|
@ -326,7 +326,7 @@
|
|||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>8.1.0</version>
|
||||
<version>8.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.ehcache</groupId>
|
||||
|
@ -381,7 +381,7 @@
|
|||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.11.0</version>
|
||||
<version>4.12.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-fileupload</groupId>
|
||||
|
@ -473,7 +473,7 @@
|
|||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>5.8.21</version>
|
||||
<version>5.8.22</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
|
|
|
@ -323,6 +323,7 @@ public class EntityHelper {
|
|||
public static final int FeedsComment = 41;
|
||||
public static final int FeedsLike = 42;
|
||||
public static final int FeedsMention = 43;
|
||||
public static final int FeedsStatus = 44;
|
||||
|
||||
// 项目
|
||||
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/*!
|
||||
Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
|
||||
|
||||
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
||||
See LICENSE and COMMERCIAL in the project root for license information.
|
||||
*/
|
||||
|
||||
package com.rebuild.core.service.trigger.aviator;
|
||||
|
||||
import com.googlecode.aviator.runtime.type.AviatorObject;
|
||||
import com.googlecode.aviator.runtime.type.AviatorType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Wrap {@link java.util.Collection} or Array
|
||||
*
|
||||
* @author devezhao
|
||||
* @since 2021/4/12
|
||||
*/
|
||||
public class AviatorArray extends AviatorObject {
|
||||
private static final long serialVersionUID = 5226071138800445209L;
|
||||
|
||||
final private Object arrayValue;
|
||||
|
||||
public AviatorArray(Collection<?> value) {
|
||||
super();
|
||||
this.arrayValue = value;
|
||||
}
|
||||
|
||||
public AviatorArray(Object[] value) {
|
||||
super();
|
||||
this.arrayValue = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int innerCompare(AviatorObject other, Map<String, Object> env) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AviatorType getAviatorType() {
|
||||
return AviatorType.JavaType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(Map<String, Object> env) {
|
||||
return this.arrayValue;
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ package com.rebuild.core.service.trigger.aviator;
|
|||
|
||||
import com.googlecode.aviator.runtime.type.AviatorObject;
|
||||
import com.googlecode.aviator.runtime.type.AviatorType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
@ -19,6 +20,7 @@ import java.util.Map;
|
|||
* @author devezhao
|
||||
* @since 2021/4/12
|
||||
*/
|
||||
@Slf4j
|
||||
public class AviatorDate extends AviatorObject {
|
||||
private static final long serialVersionUID = 2930549924386648595L;
|
||||
|
||||
|
@ -28,6 +30,7 @@ public class AviatorDate extends AviatorObject {
|
|||
public static final String DU_HOUR = "H";
|
||||
public static final String DU_MINUTE = "I";
|
||||
public static final String DU_SECOND = "S";
|
||||
public static final String DU_WEEKDAY = "W";
|
||||
|
||||
final private Date dateValue;
|
||||
|
||||
|
@ -38,7 +41,13 @@ public class AviatorDate extends AviatorObject {
|
|||
|
||||
@Override
|
||||
public int innerCompare(AviatorObject other, Map<String, Object> env) {
|
||||
return 0;
|
||||
Object $date = other.getValue(env);
|
||||
if ($date instanceof Date) {
|
||||
return dateValue.compareTo((Date) $date);
|
||||
}
|
||||
|
||||
log.warn("Could not compare " + desc(env) + " with " + other.desc(env));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -10,6 +10,7 @@ package com.rebuild.core.service.trigger.aviator;
|
|||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.googlecode.aviator.runtime.type.AviatorObject;
|
||||
import com.googlecode.aviator.runtime.type.AviatorType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -19,6 +20,7 @@ import java.util.Map;
|
|||
* @author devezhao
|
||||
* @since 2023/4/16
|
||||
*/
|
||||
@Slf4j
|
||||
public class AviatorId extends AviatorObject {
|
||||
private static final long serialVersionUID = 7227725706972057446L;
|
||||
|
||||
|
@ -31,7 +33,13 @@ public class AviatorId extends AviatorObject {
|
|||
|
||||
@Override
|
||||
public int innerCompare(AviatorObject other, Map<String, Object> env) {
|
||||
return 0;
|
||||
Object $id = other.getValue(env);
|
||||
if (idValue.equals($id)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
log.warn("Could not compare " + desc(env) + " with " + other.desc(env));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -44,15 +44,17 @@ public class AviatorUtils {
|
|||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
AVIATOR.addOpFunction(OperatorType.ADD, new OverOperatorType.DateAdd());
|
||||
AVIATOR.addOpFunction(OperatorType.SUB, new OverOperatorType.DateSub());
|
||||
AVIATOR.addOpFunction(OperatorType.LE, new OverOperatorType.DateCompareLE());
|
||||
AVIATOR.addOpFunction(OperatorType.LT, new OverOperatorType.DateCompareLT());
|
||||
AVIATOR.addOpFunction(OperatorType.GE, new OverOperatorType.DateCompareGE());
|
||||
AVIATOR.addOpFunction(OperatorType.GT, new OverOperatorType.DateCompareGT());
|
||||
AVIATOR.addOpFunction(OperatorType.EQ, new OverOperatorType.DateCompareEQ());
|
||||
AVIATOR.addOpFunction(OperatorType.NEQ, new OverOperatorType.DateCompareNEQ());
|
||||
// 重载操作符
|
||||
AVIATOR.addOpFunction(OperatorType.ADD, new OverDateOperator.DateAdd());
|
||||
AVIATOR.addOpFunction(OperatorType.SUB, new OverDateOperator.DateSub());
|
||||
AVIATOR.addOpFunction(OperatorType.LE, new OverDateOperator.DateCompareLE());
|
||||
AVIATOR.addOpFunction(OperatorType.LT, new OverDateOperator.DateCompareLT());
|
||||
AVIATOR.addOpFunction(OperatorType.GE, new OverDateOperator.DateCompareGE());
|
||||
AVIATOR.addOpFunction(OperatorType.GT, new OverDateOperator.DateCompareGT());
|
||||
AVIATOR.addOpFunction(OperatorType.EQ, new OverDateOperator.DateCompareEQ());
|
||||
AVIATOR.addOpFunction(OperatorType.NEQ, new OverDateOperator.DateCompareNEQ());
|
||||
|
||||
// 自定义函数
|
||||
addCustomFunction(new DateDiffFunction());
|
||||
addCustomFunction(new DateAddFunction());
|
||||
addCustomFunction(new DateSubFunction());
|
||||
|
|
|
@ -10,7 +10,7 @@ package com.rebuild.core.service.trigger.aviator;
|
|||
import cn.devezhao.commons.CalendarUtils;
|
||||
import com.googlecode.aviator.lexer.token.OperatorType;
|
||||
import com.googlecode.aviator.runtime.function.AbstractFunction;
|
||||
import com.googlecode.aviator.runtime.type.AviatorBoolean;
|
||||
import com.googlecode.aviator.runtime.function.FunctionUtils;
|
||||
import com.googlecode.aviator.runtime.type.AviatorLong;
|
||||
import com.googlecode.aviator.runtime.type.AviatorObject;
|
||||
|
||||
|
@ -18,14 +18,14 @@ import java.util.Date;
|
|||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 操作符重载
|
||||
* 日期相关操作符重载
|
||||
*
|
||||
* @author RB
|
||||
* @since 2023/12/6
|
||||
*/
|
||||
public class OverOperatorType {
|
||||
public class OverDateOperator {
|
||||
|
||||
private OverOperatorType() {}
|
||||
private OverDateOperator() {}
|
||||
|
||||
// -- 计算
|
||||
|
||||
|
@ -49,7 +49,7 @@ public class OverOperatorType {
|
|||
} else if ($argv2 instanceof Date && $argv1 instanceof Number) {
|
||||
return opDate((Date) $argv2, ((Number) $argv1).intValue());
|
||||
} else {
|
||||
return arg1.add(arg2, env); // Use default
|
||||
return arg1.add(arg2, env);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ public class OverOperatorType {
|
|||
int diff = CalendarUtils.getDayLeft((Date) $argv1, (Date) $argv2);
|
||||
return AviatorLong.valueOf(diff);
|
||||
} else {
|
||||
return arg1.add(arg2, env); // Use default
|
||||
return arg1.sub(arg2, env);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,10 +90,15 @@ public class OverOperatorType {
|
|||
// -- 比较
|
||||
|
||||
/**
|
||||
* 日期比较
|
||||
* 日期比较: LE `<=`
|
||||
*/
|
||||
static abstract class DateCompare extends AbstractFunction {
|
||||
private static final long serialVersionUID = -4160230503581309424L;
|
||||
static class DateCompareLE extends AbstractFunction {
|
||||
private static final long serialVersionUID = 1321662048697121893L;
|
||||
@Override
|
||||
public String getName() {
|
||||
return OperatorType.LE.getToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
|
||||
Object $argv1 = arg1.getValue(env);
|
||||
|
@ -102,86 +107,141 @@ public class OverOperatorType {
|
|||
if ($argv1 instanceof Date && $argv2 instanceof Date) {
|
||||
long v1 = ((Date) $argv1).getTime();
|
||||
long v2 = ((Date) $argv2).getTime();
|
||||
boolean b = compare(v1, v2);
|
||||
return AviatorBoolean.valueOf(b);
|
||||
return FunctionUtils.wrapReturn(v1 <= v2);
|
||||
} else {
|
||||
return arg1.add(arg2, env); // Use default
|
||||
int s = arg1.compare(arg2, env);
|
||||
return FunctionUtils.wrapReturn(s <= 0);
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected boolean compare(long v1, long v2);
|
||||
}
|
||||
|
||||
// LE `<=`
|
||||
static class DateCompareLE extends DateCompare {
|
||||
private static final long serialVersionUID = 1321662048697121893L;
|
||||
@Override
|
||||
public String getName() {
|
||||
return OperatorType.LE.getToken();
|
||||
}
|
||||
@Override
|
||||
protected boolean compare(long v1, long v2) {
|
||||
return v1 <= v2;
|
||||
}
|
||||
}
|
||||
// LT `<`
|
||||
static class DateCompareLT extends DateCompare {
|
||||
/**
|
||||
* 日期比较: LT `<`
|
||||
*/
|
||||
static class DateCompareLT extends AbstractFunction {
|
||||
private static final long serialVersionUID = 8197857653882782806L;
|
||||
@Override
|
||||
public String getName() {
|
||||
return OperatorType.LT.getToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean compare(long v1, long v2) {
|
||||
return v1 < v2;
|
||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
|
||||
Object $argv1 = arg1.getValue(env);
|
||||
Object $argv2 = arg2.getValue(env);
|
||||
|
||||
if ($argv1 instanceof Date && $argv2 instanceof Date) {
|
||||
long v1 = ((Date) $argv1).getTime();
|
||||
long v2 = ((Date) $argv2).getTime();
|
||||
return FunctionUtils.wrapReturn(v1 < v2);
|
||||
} else {
|
||||
int s = arg1.compare(arg2, env);
|
||||
return FunctionUtils.wrapReturn(s < 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
// GE `>=`
|
||||
static class DateCompareGE extends DateCompare {
|
||||
|
||||
/**
|
||||
* 日期比较: GE `>=`
|
||||
*/
|
||||
static class DateCompareGE extends AbstractFunction {
|
||||
private static final long serialVersionUID = -7966630104916265372L;
|
||||
@Override
|
||||
public String getName() {
|
||||
return OperatorType.GE.getToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean compare(long v1, long v2) {
|
||||
return v1 >= v2;
|
||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
|
||||
Object $argv1 = arg1.getValue(env);
|
||||
Object $argv2 = arg2.getValue(env);
|
||||
|
||||
if ($argv1 instanceof Date && $argv2 instanceof Date) {
|
||||
long v1 = ((Date) $argv1).getTime();
|
||||
long v2 = ((Date) $argv2).getTime();
|
||||
return FunctionUtils.wrapReturn(v1 >= v2);
|
||||
} else {
|
||||
int s = arg1.compare(arg2, env);
|
||||
return FunctionUtils.wrapReturn(s >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
// GT `>`
|
||||
static class DateCompareGT extends DateCompare {
|
||||
|
||||
/**
|
||||
* 日期比较: GT `>`
|
||||
*/
|
||||
static class DateCompareGT extends AbstractFunction {
|
||||
private static final long serialVersionUID = 5214573679573440753L;
|
||||
@Override
|
||||
public String getName() {
|
||||
return OperatorType.GT.getToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean compare(long v1, long v2) {
|
||||
return v1 > v2;
|
||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
|
||||
Object $argv1 = arg1.getValue(env);
|
||||
Object $argv2 = arg2.getValue(env);
|
||||
|
||||
if ($argv1 instanceof Date && $argv2 instanceof Date) {
|
||||
long v1 = ((Date) $argv1).getTime();
|
||||
long v2 = ((Date) $argv2).getTime();
|
||||
return FunctionUtils.wrapReturn(v1 > v2);
|
||||
} else {
|
||||
int s = arg1.compare(arg2, env);
|
||||
return FunctionUtils.wrapReturn(s > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
// EQ `==`
|
||||
static class DateCompareEQ extends DateCompare {
|
||||
|
||||
/**
|
||||
* 日期比较: EQ `==`
|
||||
*/
|
||||
static class DateCompareEQ extends AbstractFunction {
|
||||
private static final long serialVersionUID = -6142749075506832977L;
|
||||
@Override
|
||||
public String getName() {
|
||||
return OperatorType.EQ.getToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean compare(long v1, long v2) {
|
||||
return v1 == v2;
|
||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
|
||||
Object $argv1 = arg1.getValue(env);
|
||||
Object $argv2 = arg2.getValue(env);
|
||||
|
||||
if ($argv1 instanceof Date && $argv2 instanceof Date) {
|
||||
long v1 = ((Date) $argv1).getTime();
|
||||
long v2 = ((Date) $argv2).getTime();
|
||||
return FunctionUtils.wrapReturn(v1 == v2);
|
||||
} else {
|
||||
int s = arg1.compare(arg2, env);
|
||||
return FunctionUtils.wrapReturn(s == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
// NEQ `!=`
|
||||
static class DateCompareNEQ extends DateCompare {
|
||||
|
||||
/**
|
||||
* 日期比较: NEQ `!=`
|
||||
*/
|
||||
static class DateCompareNEQ extends AbstractFunction {
|
||||
private static final long serialVersionUID = -838391653977975466L;
|
||||
@Override
|
||||
public String getName() {
|
||||
return OperatorType.NEQ.getToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean compare(long v1, long v2) {
|
||||
return v1 != v2;
|
||||
public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
|
||||
Object $argv1 = arg1.getValue(env);
|
||||
Object $argv2 = arg2.getValue(env);
|
||||
|
||||
if ($argv1 instanceof Date && $argv2 instanceof Date) {
|
||||
long v1 = ((Date) $argv1).getTime();
|
||||
long v2 = ((Date) $argv2).getTime();
|
||||
return FunctionUtils.wrapReturn(v1 != v2);
|
||||
} else {
|
||||
int s = arg1.compare(arg2, env);
|
||||
return FunctionUtils.wrapReturn(s != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import com.rebuild.core.metadata.easymeta.DisplayType;
|
|||
import com.rebuild.core.metadata.easymeta.EasyDateTime;
|
||||
import com.rebuild.core.metadata.easymeta.EasyField;
|
||||
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
||||
import com.rebuild.core.metadata.easymeta.MultiValue;
|
||||
import com.rebuild.core.privileges.PrivilegesGuardContextHolder;
|
||||
import com.rebuild.core.privileges.UserService;
|
||||
import com.rebuild.core.privileges.bizz.InternalPermission;
|
||||
|
@ -52,6 +53,7 @@ import java.util.Collections;
|
|||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
@ -458,9 +460,15 @@ public class FieldWriteback extends FieldAggregation {
|
|||
value = StringUtils.EMPTY;
|
||||
}
|
||||
} else if (isMultiField) {
|
||||
// force `TEXT`
|
||||
EasyField fakeTextField = EasyMetaFactory.valueOf(MetadataHelper.getField("User", "fullName"));
|
||||
value = easyVarField.convertCompatibleValue(value, fakeTextField);
|
||||
// v3.6: 目标值为多引用时保持 `ID[]`
|
||||
if (easyVarField.getDisplayType() == DisplayType.N2NREFERENCE
|
||||
&& targetFieldEasy.getDisplayType() == DisplayType.N2NREFERENCE) {
|
||||
value = StringUtils.join((ID[]) value, MultiValue.MV_SPLIT);
|
||||
} else {
|
||||
// force `TEXT`
|
||||
EasyField fakeTextField = EasyMetaFactory.valueOf(MetadataHelper.getField("User", "fullName"));
|
||||
value = easyVarField.convertCompatibleValue(value, fakeTextField);
|
||||
}
|
||||
} else if (value instanceof ID || forceUseQuote) {
|
||||
value = value.toString();
|
||||
}
|
||||
|
@ -530,11 +538,13 @@ public class FieldWriteback extends FieldAggregation {
|
|||
} else if (dt == DisplayType.N2NREFERENCE) {
|
||||
|
||||
String[] ids = value.toString().split(",");
|
||||
List<String> idsList = new ArrayList<>();
|
||||
Set<ID> idsSet = new LinkedHashSet<>();
|
||||
for (String id : ids) {
|
||||
if (ID.isId(id)) idsList.add(id);
|
||||
id = id.trim();
|
||||
if (ID.isId(id)) idsSet.add(ID.valueOf(id));
|
||||
}
|
||||
if (ids.length == idsList.size()) newValue = value.toString();
|
||||
// v3.6: 目标值为多引用时保持 `ID[]`
|
||||
newValue = idsSet.toArray(new ID[0]);
|
||||
|
||||
} else if (dt == DisplayType.BOOL) {
|
||||
|
||||
|
|
|
@ -229,6 +229,7 @@ public class QiniuCloud {
|
|||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public long stats() {
|
||||
// https://developer.qiniu.com/kodo/3906/statistic-interface
|
||||
String time = CalendarUtils.getPlainDateFormat().format(CalendarUtils.now());
|
||||
String url = String.format(
|
||||
"%s/v6/space?bucket=%s&begin=%s000000&end=%s235959&g=day",
|
||||
|
|
|
@ -8,18 +8,22 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
package com.rebuild.web.feeds;
|
||||
|
||||
import cn.devezhao.commons.CalendarUtils;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rebuild.api.RespBody;
|
||||
import com.rebuild.core.Application;
|
||||
import com.rebuild.core.metadata.EntityHelper;
|
||||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.core.service.feeds.FeedsHelper;
|
||||
import com.rebuild.core.service.feeds.FeedsScope;
|
||||
import com.rebuild.utils.AppUtils;
|
||||
import com.rebuild.web.BaseController;
|
||||
import com.rebuild.web.IdParam;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -37,7 +41,7 @@ import java.util.List;
|
|||
public class AnnouncementController extends BaseController {
|
||||
|
||||
@GetMapping("/commons/announcements")
|
||||
public RespBody list(HttpServletRequest request) {
|
||||
public RespBody announcementList(HttpServletRequest request) {
|
||||
final ID user = AppUtils.getRequestUser(request);
|
||||
int fromWhere = getIntParameter(request, "from", 0);
|
||||
|
||||
|
@ -67,22 +71,19 @@ public class AnnouncementController extends BaseController {
|
|||
// 不在指定位置
|
||||
|
||||
int whereMask = options.getIntValue("showWhere");
|
||||
if ((fromWhere & whereMask) == 0) {
|
||||
continue;
|
||||
}
|
||||
if ((fromWhere & whereMask) == 0) continue;
|
||||
|
||||
// 不在展示时间
|
||||
|
||||
Date timeStart = parseTime(options.getString("timeStart"));
|
||||
if (timeStart != null && timeNow < timeStart.getTime()) {
|
||||
continue;
|
||||
}
|
||||
if (timeStart != null && timeNow < timeStart.getTime()) continue;
|
||||
|
||||
Date timeEnd = parseTime(options.getString("timeEnd"));
|
||||
if (timeEnd != null && timeNow > timeEnd.getTime()) {
|
||||
continue;
|
||||
}
|
||||
if (timeEnd != null && timeNow > timeEnd.getTime()) continue;
|
||||
|
||||
// 不可见
|
||||
boolean allow = false;
|
||||
|
||||
String scope = (String) o[2];
|
||||
if (FeedsScope.ALL.name().equalsIgnoreCase(scope)) {
|
||||
allow = true;
|
||||
|
@ -101,6 +102,17 @@ public class AnnouncementController extends BaseController {
|
|||
a.put("publishOn", CalendarUtils.getUTCDateTimeFormat().format(o[4]).substring(0, 16));
|
||||
a.put("publishBy", UserHelper.getName((ID) o[3]));
|
||||
a.put("id", o[5]);
|
||||
// v3.6 已读
|
||||
if (options.getBooleanValue("reqRead")) a.put("readState", 1);
|
||||
|
||||
if (user != null) {
|
||||
Object[] status = Application.createQueryNoFilter(
|
||||
"select createdOn from FeedsStatus where createdBy = ? and feedsId = ?")
|
||||
.setParameter(1, user)
|
||||
.setParameter(2, o[5])
|
||||
.unique();
|
||||
if (status != null) a.put("readState", status[0]);
|
||||
}
|
||||
as.add(a);
|
||||
}
|
||||
}
|
||||
|
@ -108,6 +120,15 @@ public class AnnouncementController extends BaseController {
|
|||
return RespBody.ok(as);
|
||||
}
|
||||
|
||||
@PostMapping("/commons/announcements/make-read")
|
||||
public RespBody announcementMakeRead(@IdParam ID aid, HttpServletRequest request) {
|
||||
final ID user = getRequestUser(request);
|
||||
Record status = EntityHelper.forNew(EntityHelper.FeedsStatus, user);
|
||||
status.setID("feedsId", aid);
|
||||
Application.getCommonsService().create(status);
|
||||
return RespBody.ok();
|
||||
}
|
||||
|
||||
private Date parseTime(String time) {
|
||||
if (StringUtils.isBlank(time)) return null;
|
||||
return CalendarUtils.parse(time + (time.length() == 16 ? ":00" : ""));
|
||||
|
|
|
@ -242,7 +242,21 @@ public class FeedsListController extends BaseController {
|
|||
|
||||
// 更多内容
|
||||
if (o[10] != null) {
|
||||
item.put("contentMore", JSON.parse((String) o[10]));
|
||||
JSON more = (JSON) JSON.parse((String) o[10]);
|
||||
item.put("contentMore", more);
|
||||
|
||||
// 公告已读
|
||||
if ((int) o[8] == FeedsType.ANNOUNCEMENT.getMask() && ((JSONObject) more).getBooleanValue("reqRead")) {
|
||||
Object[][] status = Application.createQueryNoFilter(
|
||||
"select createdBy,createdOn from FeedsStatus where feedsId = ? order by createdOn")
|
||||
.setParameter(1, o[0])
|
||||
.array();
|
||||
List<Object> statusList = new ArrayList<>();
|
||||
for (Object[] s : status) {
|
||||
statusList.add(new Object[] { s[0], UserHelper.getName((ID) s[0]), s[1] });
|
||||
}
|
||||
item.put("readStatus", statusList);
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
|
|
|
@ -197,8 +197,8 @@ public class ReferenceSearchController extends EntityController {
|
|||
|
||||
int openLevel = ClassificationManager.instance.getOpenLevel(fieldMeta);
|
||||
String sqlWhere = String.format(
|
||||
"dataId = '%s' and level = %d and (fullName like '%%%s%%' or quickCode like '%%%s%%') order by fullName",
|
||||
useClassification.toLiteral(), openLevel, q, q);
|
||||
"dataId = '%s' and level = %d and (fullName like '%%%s%%' or quickCode like '%%%s%%' or code like '%s%%') order by code,fullName",
|
||||
useClassification.toLiteral(), openLevel, q, q, q);
|
||||
|
||||
List<Object> result = resultSearch(
|
||||
sqlWhere, MetadataHelper.getEntity(EntityHelper.ClassificationData), 10);
|
||||
|
|
|
@ -463,6 +463,15 @@
|
|||
<index field-list="user,feedsId,commentId"/>
|
||||
</entity>
|
||||
|
||||
<entity name="FeedsStatus" type-code="044" description="动态扩展" queryable="false" parent="false">
|
||||
<field name="statusId" type="primary"/>
|
||||
<field name="feedsId" type="reference" ref-entity="Feeds" nullable="false" updatable="false" description="哪个动态" cascade="delete"/>
|
||||
<field name="content" type="text" description="扩展内容"/>
|
||||
<field name="createdBy" type="reference" ref-entity="User" nullable="false" creatable="false" updatable="false" description="创建人"/>
|
||||
<field name="createdOn" type="timestamp" nullable="false" creatable="false" updatable="false" description="创建时间"/>
|
||||
<index field-list="feedsId,createdBy,createdOn"/>
|
||||
</entity>
|
||||
|
||||
<entity name="ProjectConfig" type-code="050" description="项目" queryable="false">
|
||||
<field name="configId" type="primary"/>
|
||||
<field name="projectName" type="string" max-length="100" nullable="false" description="项目名称"/>
|
||||
|
|
|
@ -665,6 +665,17 @@ create table if not exists `feeds_mention` (
|
|||
index IX0_feeds_mention (`USER`, `FEEDS_ID`, `COMMENT_ID`)
|
||||
)Engine=InnoDB;
|
||||
|
||||
-- ************ Entity [FeedsStatus] DDL ************
|
||||
create table if not exists `feeds_status` (
|
||||
`STATUS_ID` char(20) not null,
|
||||
`FEEDS_ID` char(20) not null comment '哪个动态',
|
||||
`CONTENT` text(65535) comment '扩展内容',
|
||||
`CREATED_BY` char(20) not null comment '创建人',
|
||||
`CREATED_ON` timestamp not null default current_timestamp comment '创建时间',
|
||||
primary key (`STATUS_ID`),
|
||||
index IX0_feeds_status (`FEEDS_ID`, `CREATED_BY`, `CREATED_ON`)
|
||||
)Engine=InnoDB;
|
||||
|
||||
-- ************ Entity [ProjectConfig] DDL ************
|
||||
create table if not exists `project_config` (
|
||||
`CONFIG_ID` char(20) not null,
|
||||
|
@ -886,4 +897,4 @@ insert into `project_plan_config` (`CONFIG_ID`, `PROJECT_ID`, `PLAN_NAME`, `SEQ`
|
|||
|
||||
-- DB Version (see `db-upgrade.sql`)
|
||||
insert into `system_config` (`CONFIG_ID`, `ITEM`, `VALUE`)
|
||||
values ('021-9000000000000001', 'DBVer', 54);
|
||||
values ('021-9000000000000001', 'DBVer', 55);
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
-- Database upgrade scripts for rebuild 1.x and 2.x
|
||||
-- Each upgraded starts with `-- #VERSION`
|
||||
|
||||
-- #55 (v3.6)
|
||||
create table if not exists `feeds_status` (
|
||||
`STATUS_ID` char(20) not null,
|
||||
`FEEDS_ID` char(20) not null comment '哪个动态',
|
||||
`CONTENT` text(65535) comment '扩展内容',
|
||||
`CREATED_BY` char(20) not null comment '创建人',
|
||||
`CREATED_ON` timestamp not null default current_timestamp comment '创建时间',
|
||||
primary key (`STATUS_ID`),
|
||||
index IX0_feeds_status (`FEEDS_ID`, `CREATED_BY`, `CREATED_ON`)
|
||||
)Engine=InnoDB;
|
||||
|
||||
-- #54 (v3.5)
|
||||
alter table `feeds`
|
||||
add column `AUTO_LOCATION` varchar(100) comment '发布位置';
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<div class="col-12 col-md-6">
|
||||
<div class="dataTables_filter">
|
||||
<div class="input-group input-search">
|
||||
<input class="form-control" type="text" th:placeholder="${bundle.L('查询')}" maxlength="40" data-quickFields="&user,ipAddr" />
|
||||
<input class="form-control" type="text" th:placeholder="${bundle.L('快速查询')}" maxlength="40" data-quickFields="&user,ipAddr" />
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-secondary" type="button"><i class="icon zmdi zmdi-search"></i></button>
|
||||
</span>
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="input-group input-search">
|
||||
<input class="form-control" type="text" th:placeholder="${bundle.L('查询')}" maxlength="40" data-quickFields="recordId,recordName" />
|
||||
<input class="form-control" type="text" th:placeholder="${bundle.L('快速查询')}" maxlength="40" data-quickFields="recordId,recordName" />
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-secondary" type="button"><i class="icon zmdi zmdi-search"></i></button>
|
||||
</span>
|
||||
|
|
|
@ -14,22 +14,36 @@
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.table tr.group > td {
|
||||
.table.group-table > thead > tr > th {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
.table tr.group-title > td {
|
||||
padding: 0;
|
||||
}
|
||||
.table tr.group > td > h5 {
|
||||
padding: 8px 10px;
|
||||
.table tr.group-title > td > h5 {
|
||||
padding: 10px;
|
||||
padding-left: 28px;
|
||||
margin: 0;
|
||||
background-color: #dee2e6;
|
||||
background-color: #eee;
|
||||
border-radius: 0;
|
||||
position: relative;
|
||||
}
|
||||
.table tr.group > td > h5,
|
||||
.table tr.group > td > h5 * {
|
||||
color: #444;
|
||||
.table tr.group-title > td > h5,
|
||||
.table tr.group-title > td > h5 * {
|
||||
color: #777;
|
||||
font-weight: normal;
|
||||
line-height: 1;
|
||||
}
|
||||
.table.group > thead > tr > th {
|
||||
border-bottom-width: 0;
|
||||
.table tr.group-title > td > h5::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 9px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #dee2e6;
|
||||
border-radius: 50px;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
|
|
@ -102,9 +102,3 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
padding: 7px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.modal-body .tab-container {
|
||||
margin-left: -5px;
|
||||
margin-right: -5px;
|
||||
margin-top: -15px;
|
||||
}
|
||||
|
|
|
@ -184,17 +184,8 @@ body.fullscreen .dash-right .J_darkmode {
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
body.fullscreen.darkmode {
|
||||
background: #0a1b3b url(../img/datav-bg.png) no-repeat 0 0;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
body.fullscreen.darkmode .grid-stack .grid-stack-item .grid-stack-item-content {
|
||||
background-color: rgba(6, 30, 93, 0.5);
|
||||
}
|
||||
|
||||
body.fullscreen.darkmode .grid-stack .grid-stack-item.fullscreen .grid-stack-item-content {
|
||||
background-color: #0a1b3b;
|
||||
body.fullscreen .announcement-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.grid-stack-item.fullscreen {
|
||||
|
@ -238,3 +229,46 @@ body.fullscreen .tools-bar h4 {
|
|||
.grid-stack-item.bgcolor .rb-spinner svg {
|
||||
stroke: #fff;
|
||||
}
|
||||
|
||||
/* darkmode */
|
||||
|
||||
body.fullscreen.darkmode {
|
||||
background: #191a2c;
|
||||
}
|
||||
|
||||
body.fullscreen.darkmode .grid-stack .grid-stack-item .grid-stack-item-content,
|
||||
body.fullscreen.darkmode .chart-box.ApprovalList .progress-wrap,
|
||||
body.fullscreen.darkmode .table-striped tbody tr:nth-of-type(odd) {
|
||||
background-color: #24273e;
|
||||
}
|
||||
|
||||
body.fullscreen.darkmode .chart .table,
|
||||
body.fullscreen.darkmode .chart .table td > a {
|
||||
background-color: transparent;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
body.fullscreen.darkmode .chart .table th {
|
||||
background-color: #1e2033;
|
||||
}
|
||||
|
||||
body.fullscreen.darkmode .chart .table thead,
|
||||
body.fullscreen.darkmode .chart .table tbody,
|
||||
body.fullscreen.darkmode .chart .table th,
|
||||
body.fullscreen.darkmode .chart .table td,
|
||||
body.fullscreen.darkmode .chart .table {
|
||||
border-color: #191a2c;
|
||||
}
|
||||
|
||||
body.fullscreen.darkmode .chart .table tr:hover,
|
||||
body.fullscreen.darkmode .chart .table th:hover,
|
||||
body.fullscreen.darkmode .chart .table tr.highlight,
|
||||
body.fullscreen.darkmode .tcs-selected {
|
||||
background: unset;
|
||||
}
|
||||
|
||||
body.fullscreen.darkmode .ApprovalList thead th,
|
||||
body.fullscreen.darkmode .ProjectTasks thead th,
|
||||
body.fullscreen.darkmode .FeedsSchedule thead th {
|
||||
padding-top: 7px;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
-webkit-appearance: none;
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
height: 55px;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.rich-editor .action-btns {
|
||||
|
@ -694,13 +694,13 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
}
|
||||
|
||||
#collapseUser .aside-tree li > a {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
#collapseUser .aside-tree li > a .avatar {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
margin-right: 6px;
|
||||
font-size: 0;
|
||||
|
@ -714,3 +714,14 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
min-height: 36px;
|
||||
padding-top: 9px;
|
||||
}
|
||||
|
||||
.read-status .user-show {
|
||||
margin: 0;
|
||||
margin-right: 3px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.read-status .user-show .avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
display: inline-block;
|
||||
width: auto;
|
||||
line-height: 1;
|
||||
padding: 9px 20px;
|
||||
padding: 9px 18px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
border-radius: 6px !important;
|
||||
|
|
|
@ -1172,10 +1172,10 @@ a.link.link-icon::after {
|
|||
cursor: default;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
text-align: center;
|
||||
line-height: 18px;
|
||||
display: none;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
height: 19px;
|
||||
line-height: 19px;
|
||||
}
|
||||
|
||||
.img-field.avatar label.img-upload b {
|
||||
|
@ -3006,7 +3006,7 @@ form {
|
|||
}
|
||||
|
||||
.user-show + .user-show {
|
||||
margin-left: 10px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.user-show:hover {
|
||||
|
@ -4016,8 +4016,8 @@ a.icon-link > .zmdi {
|
|||
line-height: 1.7;
|
||||
font-size: 1.1rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.announcement-contents img.emoji {
|
||||
|
|
|
@ -158,14 +158,6 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
width: auto;
|
||||
}
|
||||
|
||||
.file-field.attachments > .img-thumbnail b {
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: unset;
|
||||
width: 28px;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
#task-comment .comments,
|
||||
#task-comment .comments .comment-reply {
|
||||
margin: 0;
|
||||
|
|
|
@ -186,7 +186,7 @@ class HistoryViewport extends React.Component {
|
|||
}
|
||||
|
||||
return (
|
||||
<table className="table table-fixed group">
|
||||
<table className="table table-fixed group-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="25%">{$L('字段')}</th>
|
||||
|
@ -198,7 +198,7 @@ class HistoryViewport extends React.Component {
|
|||
{this.state.dataList.map((item, idx) => {
|
||||
return (
|
||||
<RF key={idx}>
|
||||
<tr className="group">
|
||||
<tr className="group-title">
|
||||
<td colSpan="3">
|
||||
<h5>{WrapHtml($L('**%s** 由 %s %s', item[2].split(' UTC')[0], item[3], RevTypes[item[1]]))}</h5>
|
||||
</td>
|
||||
|
|
|
@ -572,7 +572,7 @@ class ChartTreemap extends BaseChart {
|
|||
series: [
|
||||
{
|
||||
data: data.data,
|
||||
type: 'treemap',
|
||||
type: 'treemap', // sunburst
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
top: window.render_preview_chart ? 0 : 15, // In preview
|
||||
|
|
|
@ -140,8 +140,12 @@ $(document).ready(() => {
|
|||
|
||||
$('.J_dash-fullscreen').on('click', () => {
|
||||
const $body = $(document.body)
|
||||
if ($body.hasClass('fullscreen')) $fullscreen.exit()
|
||||
else $fullscreen.open()
|
||||
if ($body.hasClass('fullscreen')) {
|
||||
$fullscreen.exit()
|
||||
} else {
|
||||
$fullscreen.open()
|
||||
$body.addClass('darkmode')
|
||||
}
|
||||
$body.toggleClass('fullscreen')
|
||||
})
|
||||
|
||||
|
|
|
@ -10,9 +10,12 @@ class AnnouncementModal extends React.Component {
|
|||
state = { ...this.props }
|
||||
|
||||
render() {
|
||||
const contentHtml = $converEmoji(this.props.content.replace(/\n/g, '<br/>'))
|
||||
const props = this.props
|
||||
const state = this.state
|
||||
const contentHtml = $converEmoji(props.content.replace(/\n/g, '<br/>'))
|
||||
|
||||
return (
|
||||
<div className="modal" tabIndex={this.state.tabIndex || -1} ref={(c) => (this._dlg = c)}>
|
||||
<div className="modal" tabIndex={state.tabIndex || -1} ref={(c) => (this._dlg = c)}>
|
||||
<div className="modal-dialog modal-dialog-centered">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header pb-0">
|
||||
|
@ -23,10 +26,25 @@ class AnnouncementModal extends React.Component {
|
|||
<div className="modal-body">
|
||||
<div className="text-break announcement-contents" dangerouslySetInnerHTML={{ __html: contentHtml }} />
|
||||
<div>
|
||||
<span className="float-left text-muted">{$L('由 %s 发布于 %s', this.props.publishBy, this.props.publishOn)}</span>
|
||||
<span className="float-left text-muted">{$L('由 %s 发布于 %s', props.publishBy, props.publishOn)}</span>
|
||||
<span className="float-right">
|
||||
<a href={`${rb.baseUrl}/app/redirect?id=${this.props.id}`}>{$L('前往动态查看')}</a>
|
||||
<a href={`${rb.baseUrl}/app/redirect?id=${props.id}`}>{$L('前往动态查看')}</a>
|
||||
</span>
|
||||
{state.readState && (
|
||||
<span className="float-right mr-2">
|
||||
{state.readState === 1 && (
|
||||
<a className="text-primary" onClick={() => this._makeRead()}>
|
||||
<i className="icon zmdi zmdi-check text-bold" /> {$L('标记已读')}
|
||||
</a>
|
||||
)}
|
||||
{state.readState !== 1 && (
|
||||
<a className="text-muted">
|
||||
<i className="icon zmdi zmdi-check text-bold" /> {$L('已读')}
|
||||
</a>
|
||||
)}
|
||||
<span className="text-muted text-bold ml-2">·</span>
|
||||
</span>
|
||||
)}
|
||||
<span className="clearfix" />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -46,6 +64,21 @@ class AnnouncementModal extends React.Component {
|
|||
}
|
||||
|
||||
hide = () => $(this._dlg).modal('hide')
|
||||
|
||||
_makeRead() {
|
||||
if (!rb.currentUser) {
|
||||
location.href = `${rb.baseUrl}/app/redirect?id=${this.props.id}`
|
||||
return
|
||||
}
|
||||
|
||||
$.post(`/commons/announcements/make-read?id=${this.props.id}`, (res) => {
|
||||
if (res.error_code === 0) {
|
||||
this.setState({ readState: 2 })
|
||||
} else {
|
||||
RbHighbar.error(res.error_msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const $showAnnouncement = function () {
|
||||
|
|
|
@ -211,7 +211,8 @@ class FeedsList extends React.Component {
|
|||
const firstFetch = !s.data
|
||||
// s.focusFeed 首次加载有效
|
||||
|
||||
$.post(`/feeds/feeds-list?pageNo=${s.pageNo}&sort=${s.sort || ''}&type=${s.tabType}&foucs=${firstFetch ? s.focusFeed : ''}`, JSON.stringify(filter), (res) => {
|
||||
const url = `/feeds/feeds-list?pageNo=${s.pageNo}&sort=${s.sort || ''}&type=${s.tabType}&foucs=${firstFetch && s.focusFeed ? s.focusFeed : ''}`
|
||||
$.post(url, JSON.stringify(filter), (res) => {
|
||||
const _data = res.data || { data: [], total: 0 }
|
||||
this.state.pageNo === 1 && this._Pagination.setState({ rowsTotal: _data.total, pageNo: 1 })
|
||||
this.setState({ data: _data.data, focusFeed: firstFetch ? s.focusFeed : null }, () => {
|
||||
|
@ -588,6 +589,13 @@ function __renderRichContent(e) {
|
|||
}
|
||||
}
|
||||
|
||||
let showTimeTip
|
||||
if (e.type === 3 && contentMore.timeEnd) {
|
||||
showTimeTip = <span className="badge badge-success ml-1">{$L('公示中')}</span>
|
||||
if ($expired(contentMore.timeEnd)) showTimeTip = null
|
||||
else if (contentMore.timeStart && moment().isBefore($moment(contentMore.timeStart))) showTimeTip = null
|
||||
}
|
||||
|
||||
let AL = e.autoLocation ? e.autoLocation.split('$$$$') : false
|
||||
if (AL) {
|
||||
AL = {
|
||||
|
@ -650,6 +658,18 @@ function __renderRichContent(e) {
|
|||
{(contentMore.timeStart || contentMore.timeEnd) && (
|
||||
<div>
|
||||
<span>{$L('公示时间')} : </span> {contentMore.timeStart || ''} {$L('至')} {contentMore.timeEnd}
|
||||
{showTimeTip}
|
||||
</div>
|
||||
)}
|
||||
{e.readStatus && (
|
||||
<div>
|
||||
<span>{$L('已读用户')} : </span>
|
||||
{e.readStatus.length > 0 ? <RF>{$L('%d 人已读', e.readStatus.length)}</RF> : <span className="text-muted">{$L('无')}</span>}
|
||||
<div className="read-status fs-0">
|
||||
{e.readStatus.map((item) => {
|
||||
return <UserShow key={item[0]} id={item[0]} name={item[1]} noLink />
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -508,7 +508,7 @@ class AnnouncementOptions extends React.Component {
|
|||
</dd>
|
||||
</dl>
|
||||
<dl className="row">
|
||||
<dt className="col-12 col-lg-3 pt-2"> {$L('公示时间')}</dt>
|
||||
<dt className="col-12 col-lg-3 pt-2">{$L('公示时间')}</dt>
|
||||
<dd className="col-12 col-lg-9" ref={(c) => (this._$showTime = c)}>
|
||||
<div className="input-group">
|
||||
<input type="text" className="form-control form-control-sm" placeholder={$L('现在')} />
|
||||
|
@ -519,6 +519,15 @@ class AnnouncementOptions extends React.Component {
|
|||
</div>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl className="row mb-1">
|
||||
<dt className="col-12 col-lg-3">{$L('要求已读')}</dt>
|
||||
<dd className="col-12 col-lg-9 mb-0" ref={(c) => (this._$reqRead = c)}>
|
||||
<label className="custom-control custom-checkbox custom-control-inline">
|
||||
<input className="custom-control-input" name="reqRead" type="checkbox" disabled={this.props.readonly} />
|
||||
<span className="custom-control-label">{$L('是')}</span>
|
||||
</label>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -540,6 +549,9 @@ class AnnouncementOptions extends React.Component {
|
|||
.each(function () {
|
||||
if ((~~$(this).val() & initValue.showWhere) !== 0) $(this).prop('checked', true)
|
||||
})
|
||||
$(this._$reqRead)
|
||||
.find('input')
|
||||
.prop('checked', initValue.reqRead === true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -566,12 +578,14 @@ class AnnouncementOptions extends React.Component {
|
|||
timeStart: timeStart || null,
|
||||
timeEnd: timeEnd,
|
||||
showWhere: where,
|
||||
reqRead: $(this._$reqRead).find('input')[0].checked,
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
$(this._$showTime).find('.form-control').val('')
|
||||
$(this._$showWhere).find('input').prop('checked', false)
|
||||
$(this._$reqRead).find('input').prop('checked', false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ class GroupList extends React.Component {
|
|||
<li key={`item-${item.id}`} data-id={item.id} className={this.state.active === item.id ? 'active' : ''}>
|
||||
<a className="text-truncate" onClick={() => this._handleActive(item.id)}>
|
||||
{this._showAvatar && <img src={`${rb.baseUrl}/account/user-avatar/${item.id}`} className="avatar" alt="Avatar" />}
|
||||
{item.name}
|
||||
<span className="down-1 d-inline-block">{item.name}</span>
|
||||
</a>
|
||||
<i className={`zmdi zmdi-star-outline ${item.star && 'star'}`} onClick={() => this._handleStar(item)} title={$L('星标')} />
|
||||
</li>
|
||||
|
|
|
@ -316,12 +316,20 @@ class BaiduMap extends React.Component {
|
|||
that._map = map
|
||||
|
||||
// 初始位置
|
||||
if (that.props.lnglat && that.props.lnglat.lng && that.props.lnglat.lat) {
|
||||
that.center(that.props.lnglat)
|
||||
const _lnglat = that.props.lnglat
|
||||
if (_lnglat) {
|
||||
if (_lnglat.lng && _lnglat.lat) {
|
||||
that.center(_lnglat)
|
||||
} else if (_lnglat.text) {
|
||||
const geoc = new _BMapGL.Geocoder()
|
||||
geoc.getPoint(_lnglat.text, function (point) {
|
||||
that.center(point)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const geo = new _BMapGL.Geolocation()
|
||||
geo.enableSDKLocation()
|
||||
geo.getCurrentPosition(function (e) {
|
||||
const geol = new _BMapGL.Geolocation()
|
||||
geol.enableSDKLocation()
|
||||
geol.getCurrentPosition(function (e) {
|
||||
if (this.getStatus() === window.BMAP_STATUS_SUCCESS) {
|
||||
map.centerAndZoom(e.point, 14)
|
||||
} else {
|
||||
|
|
|
@ -341,7 +341,7 @@ class DlgImports extends RbModalHandler {
|
|||
render() {
|
||||
return (
|
||||
<RbModal title={$L('导入分类数据')} ref={(c) => (this._dlg = c)}>
|
||||
<div className="tab-container">
|
||||
<div className="tab-container" style={{ marginTop: -10 }}>
|
||||
<ul className="nav nav-tabs">
|
||||
<li className="nav-item">
|
||||
<a className="nav-link active" href="#FILE" data-toggle="tab">
|
||||
|
@ -481,8 +481,8 @@ class DlgImports extends RbModalHandler {
|
|||
|
||||
_checkState(taskid) {
|
||||
if (!this.__mp) {
|
||||
const mp_parent = $(this._dlg._element).find('.modal-body').attr('id')
|
||||
this.__mp = new Mprogress({ template: 1, start: true, parent: `#${mp_parent}` })
|
||||
const mp_parent = $(this._dlg._element).find('.modal-body').attr('id', $random('node-'))
|
||||
this.__mp = new Mprogress({ template: 1, start: true, parent: `#` + $(mp_parent).attr('id') })
|
||||
}
|
||||
|
||||
$.get(`/commons/task/state?taskid=${taskid}`, (res) => {
|
||||
|
@ -517,12 +517,13 @@ class DlgEditItem extends RbAlert {
|
|||
return (
|
||||
<form className="rbalert-form-sm">
|
||||
<div className="form-group">
|
||||
<label className="text-bold">{$L('分类项名称')}</label>
|
||||
<label className="text-bold">{$L('名称')}</label>
|
||||
<input type="text" className="form-control form-control-sm" name="name" value={this.state.name || ''} onChange={this.handleChange} maxLength="50" />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label className="text-bold">{$L('编码')}</label>
|
||||
<input type="text" className="form-control form-control-sm" name="code" value={this.state.code || ''} onChange={this.handleChange} maxLength="50" placeholder={$L('无')} />
|
||||
<p className="form-text">{$L('编码可用于排序和搜素')}</p>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label className="custom-control custom-control-sm custom-checkbox custom-control-inline mt-0 mb-0">
|
||||
|
@ -553,6 +554,11 @@ class DlgEditItem extends RbAlert {
|
|||
itemCode: this.state.code,
|
||||
itemHide: this.state.hide,
|
||||
}
|
||||
if (!_data.itemName) {
|
||||
RbHighbar.create($L('请输入名称'))
|
||||
return
|
||||
}
|
||||
|
||||
typeof this.props.onConfirm === 'function' && this.props.onConfirm(_data)
|
||||
this.hide()
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ class EntityNew2 extends RbModalHandler {
|
|||
render() {
|
||||
return (
|
||||
<RbModal ref={(c) => (this._dlg = c)} title={$L('添加实体')} className="entity-new2">
|
||||
<div className="tab-container" ref={(c) => (this._$container = c)}>
|
||||
<div className="tab-container" style={{ marginTop: -10 }} ref={(c) => (this._$container = c)}>
|
||||
<ul className="nav nav-tabs">
|
||||
<li className="nav-item">
|
||||
<a className="nav-link active" href="#MANUAL" data-toggle="tab">
|
||||
|
@ -166,13 +166,13 @@ class EntityNew2 extends RbModalHandler {
|
|||
<div className="tab-pane" id="EXCEL">
|
||||
<div className="form">
|
||||
<div className="form-group row">
|
||||
<label className="col-sm-3 col-form-label text-sm-right">{$L('上传数据文件')}</label>
|
||||
<label className="col-sm-3 col-form-label text-sm-right">{$L('上传文件')}</label>
|
||||
<div className="col-sm-7">
|
||||
<div className="file-select">
|
||||
<input type="file" className="inputfile" accept=".xlsx,.xls,.csv" data-local="temp" ref={(c) => (this._$uploadfile = c)} />
|
||||
<label htmlFor="upload-input" className="btn-secondary mb-0" ref={(c) => (this._$uploadbtn = c)}>
|
||||
<i className="zmdi zmdi-upload"></i>
|
||||
<span>{$L('上传文件')}</span>
|
||||
<span>{$L('选择文件')}</span>
|
||||
</label>
|
||||
</div>
|
||||
{this.state.excelfile && (
|
||||
|
|
|
@ -94,7 +94,11 @@ class MessageList extends React.Component {
|
|||
{$L('查看')}
|
||||
</a>
|
||||
)}
|
||||
{item[3] && <a className="read-mark text-muted">{$L('标记已读')}</a>}
|
||||
{item[3] && (
|
||||
<a className="read-mark text-muted">
|
||||
<i className="icon zmdi zmdi-check text-bold" /> {$L('标记已读')}
|
||||
</a>
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
)
|
||||
|
|
|
@ -104,7 +104,7 @@ class DlgEdit extends RbFormHandler {
|
|||
</div>
|
||||
</div>
|
||||
{!this.props.id && (
|
||||
<React.Fragment>
|
||||
<RF>
|
||||
<div className="form-group row">
|
||||
<label className="col-sm-3 col-form-label text-sm-right">{$L('项目 ID')}</label>
|
||||
<div className="col-sm-7">
|
||||
|
@ -121,7 +121,7 @@ class DlgEdit extends RbFormHandler {
|
|||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
</RF>
|
||||
)}
|
||||
<div className="form-group row footer">
|
||||
<div className="col-sm-7 offset-sm-3" ref={(c) => (this._btns = c)}>
|
||||
|
|
|
@ -660,7 +660,7 @@ class TaskViewModal extends React.Component {
|
|||
location.hash = '#!/View/'
|
||||
})
|
||||
|
||||
$dlg.modal({ show: true })
|
||||
$dlg.modal({ show: true, backdrop: !false })
|
||||
// fix: 打开视图卡顿
|
||||
setTimeout(() => this.setState({ _taskUrl: `${rb.baseUrl}/project/task/${this.state.taskid}` }), 200)
|
||||
}
|
||||
|
@ -676,6 +676,7 @@ class TaskViewModal extends React.Component {
|
|||
|
||||
refreshTask(planChanged) {
|
||||
const ref = __TaskRefs[this.state.taskid]
|
||||
if (!ref) return
|
||||
if (planChanged) {
|
||||
ref.props.$$$parent.refreshTasks()
|
||||
__PlanRefs[planChanged] && __PlanRefs[planChanged].refreshTasks()
|
||||
|
|
|
@ -771,7 +771,7 @@ class UserSelector extends React.Component {
|
|||
|
||||
// ~~ 用户显示
|
||||
const UserShow = function (props) {
|
||||
const viewUrl = props.id ? `#!/View/User/${props.id}` : null
|
||||
const viewUrl = props.id && props.noLink !== true ? `#!/View/User/${props.id}` : null
|
||||
const avatarUrl = `${rb.baseUrl}/account/user-avatar/${props.id}`
|
||||
|
||||
return (
|
||||
|
|
|
@ -61,7 +61,7 @@ $(document).ready(() => {
|
|||
message={
|
||||
<div>
|
||||
<span className="mr-1">{$L('预计执行时间 (最多显示近 9 次)')} : </span>
|
||||
<code>{res.data.slice(0, 10).join(', ')}</code>
|
||||
<code>{res.data.slice(0, 9).join(', ')}</code>
|
||||
</div>
|
||||
}
|
||||
/>,
|
||||
|
|
|
@ -33,7 +33,8 @@
|
|||
<script>
|
||||
$(document).ready(() => {
|
||||
const $frame = $('.frame-wrap iframe')
|
||||
$frame.attr('src', $decode($urlp('url')))
|
||||
const url = $urlp('url') || $urlp('src')
|
||||
$frame.attr('src', $decode(url))
|
||||
|
||||
$addResizeHandler(() => {
|
||||
$frame.css({ minHeight: $(window).height() - 61 })
|
||||
|
|
|
@ -8,12 +8,12 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
package com.rebuild.core.service.trigger.aviator;
|
||||
|
||||
import cn.devezhao.commons.CalendarUtils;
|
||||
import com.googlecode.aviator.AviatorEvaluator;
|
||||
import com.googlecode.aviator.exception.ExpressionRuntimeException;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -82,11 +82,13 @@ class AviatorUtilsTest {
|
|||
|
||||
@Test
|
||||
void testDateOp() {
|
||||
Map<String, Object> env = new HashMap<>();
|
||||
env.put("date1", CalendarUtils.now());
|
||||
Map<String, Object> env = AviatorEvaluator.newEnv("date1", CalendarUtils.now());
|
||||
|
||||
AviatorUtils.eval("p(date1 + 8)", env, true);
|
||||
AviatorUtils.eval("p(date1 - 8)", env, true);
|
||||
AviatorUtils.eval("p(date1 - date1)", env, true);
|
||||
AviatorUtils.eval("p(1 + 1)", env, true);
|
||||
AviatorUtils.eval("p(1 - 1)", env, true);
|
||||
|
||||
// BAD
|
||||
Assertions.assertThrows(ExpressionRuntimeException.class,
|
||||
|
@ -95,14 +97,26 @@ class AviatorUtilsTest {
|
|||
|
||||
@Test
|
||||
void testDateCompare() {
|
||||
Map<String, Object> env = new HashMap<>();
|
||||
env.put("date1", CalendarUtils.now());
|
||||
env.put("date2", CalendarUtils.addDay(1));
|
||||
AviatorUtils.eval("p(date1 == date1)", env, true);
|
||||
AviatorUtils.eval("p(date1 != date2)", env, true);
|
||||
AviatorUtils.eval("p(date1 > date2)", env, true);
|
||||
AviatorUtils.eval("p(date1 < date2)", env, true);
|
||||
AviatorUtils.eval("p(date1 <= date1)", env, true);
|
||||
AviatorUtils.eval("p(date2 <= date2)", env, true);
|
||||
Map<String, Object> env = AviatorEvaluator.newEnv(
|
||||
"date1", CalendarUtils.now(),
|
||||
"date2", CalendarUtils.addDay(1));
|
||||
|
||||
Assertions.assertTrue((Boolean) AviatorUtils.eval("date1 == date1", env));
|
||||
Assertions.assertTrue((Boolean) AviatorUtils.eval("date1 != date2", env));
|
||||
Assertions.assertFalse((Boolean) AviatorUtils.eval("date1 > date1", env));
|
||||
Assertions.assertTrue((Boolean) AviatorUtils.eval("date1 >= date1", env));
|
||||
Assertions.assertFalse((Boolean) AviatorUtils.eval("date1 < date1", env));
|
||||
Assertions.assertTrue((Boolean) AviatorUtils.eval("date1 <= date1", env));
|
||||
Assertions.assertTrue((Boolean) AviatorUtils.eval("date1 == date1", env));
|
||||
Assertions.assertTrue((Boolean) AviatorUtils.eval("date1 != date2", env));
|
||||
|
||||
Assertions.assertTrue((Boolean) AviatorUtils.eval("1.0 == 1", env));
|
||||
Assertions.assertTrue((Boolean) AviatorUtils.eval("1 != 2", env));
|
||||
Assertions.assertFalse((Boolean) AviatorUtils.eval("1 > 2", env));
|
||||
Assertions.assertTrue((Boolean) AviatorUtils.eval("2 >= 2", env));
|
||||
Assertions.assertFalse((Boolean) AviatorUtils.eval("3.1 < 2", env));
|
||||
Assertions.assertTrue((Boolean) AviatorUtils.eval("4.56 <= 4.56", env));
|
||||
Assertions.assertTrue((Boolean) AviatorUtils.eval("12.34560 == 12.3456", env));
|
||||
Assertions.assertTrue((Boolean) AviatorUtils.eval("1 != 2", env));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue