mirror of
https://github.com/getrebuild/rebuild.git
synced 2024-09-20 07:25:54 +08:00
Be v3.5 (#680)
* frontjs * be: 公式日期值 * IMP: 明细使用自有 Service * 公开注册 * be: this._defaultBackPath * style: dataTables_oper.invisible2 * varRecord * fix: 明细未配置或出错 * lib * feat: autoLocation
This commit is contained in:
parent
c9b81f91aa
commit
db7f32d1e5
2
@rbv
2
@rbv
|
@ -1 +1 @@
|
|||
Subproject commit 416bec1eb0ad62f482dab2975690bada8dcc5eea
|
||||
Subproject commit d46fd910bcf5dc37aed718d5ccb409c886a3dc82
|
4
pom.xml
4
pom.xml
|
@ -270,7 +270,7 @@
|
|||
<dependency>
|
||||
<groupId>com.github.devezhao</groupId>
|
||||
<artifactId>commons</artifactId>
|
||||
<version>6234f298ef</version>
|
||||
<version>1.3.13</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>httpclient</artifactId>
|
||||
|
@ -290,7 +290,7 @@
|
|||
<dependency>
|
||||
<groupId>com.github.devezhao</groupId>
|
||||
<artifactId>persist4j</artifactId>
|
||||
<version>0b293fa74b</version>
|
||||
<version>1.7.5</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.alibaba</groupId>
|
||||
|
|
|
@ -49,11 +49,11 @@ public class AuthTokenManager {
|
|||
* 生成 Token
|
||||
*
|
||||
* @param user
|
||||
* @param expires
|
||||
* @param seconds
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
protected static String generateToken(ID user, int expires, String type) {
|
||||
protected static String generateToken(ID user, int seconds, String type) {
|
||||
// Type,User,Time,Version
|
||||
String desc = String.format("%s,%s,%d,v2",
|
||||
ObjectUtils.defaultIfNull(type, TYPE_ACCESS_TOKEN),
|
||||
|
@ -61,7 +61,7 @@ public class AuthTokenManager {
|
|||
System.nanoTime());
|
||||
String token = EncryptUtils.toSHA1Hex(desc);
|
||||
|
||||
Application.getCommonsCache().put(TOKEN_PREFIX + token, desc, expires);
|
||||
Application.getCommonsCache().put(TOKEN_PREFIX + token, desc, seconds);
|
||||
return token;
|
||||
}
|
||||
|
||||
|
@ -84,12 +84,12 @@ public class AuthTokenManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param expires
|
||||
* @param seconds
|
||||
* @return
|
||||
* @see #TYPE_CSRF_TOKEN
|
||||
*/
|
||||
public static String generateCsrfToken(int expires) {
|
||||
return generateToken(null, expires, TYPE_CSRF_TOKEN);
|
||||
public static String generateCsrfToken(int seconds) {
|
||||
return generateToken(null, seconds, TYPE_CSRF_TOKEN);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -111,7 +111,8 @@ public class MetadataSorter {
|
|||
|
||||
List<BaseMeta> entities = new ArrayList<>();
|
||||
CollectionUtils.addAll(entities, mainEntity.getDetialEntities());
|
||||
sortByLabel(entities);
|
||||
// SORT: 名称。默认是返回按CODE大小
|
||||
if (entities.size() > 1) sortByLabel(entities);
|
||||
return entities.toArray(new Entity[0]);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
|||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.TransactionAspectSupport;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* 手动事物管理。默认事务管理见 `application-bean.xml`
|
||||
|
@ -27,6 +28,8 @@ public class TransactionManual {
|
|||
* 开启一个事物
|
||||
*
|
||||
* @return
|
||||
* @see #commit(TransactionStatus)
|
||||
* @see #rollback(TransactionStatus)
|
||||
*/
|
||||
public static TransactionStatus newTransaction() {
|
||||
DefaultTransactionAttribute attr = new DefaultTransactionAttribute();
|
||||
|
@ -34,15 +37,6 @@ public class TransactionManual {
|
|||
return getTxManager().getTransaction(attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shadow for TransactionAspectSupport#currentTransactionStatus
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static TransactionStatus currentTransaction() {
|
||||
return TransactionAspectSupport.currentTransactionStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交
|
||||
*
|
||||
|
@ -72,4 +66,21 @@ public class TransactionManual {
|
|||
return Application.getBean(DataSourceTransactionManager.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shadow for <tt>TransactionAspectSupport#currentTransactionStatus</tt>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static TransactionStatus currentTransactionStatus() {
|
||||
return TransactionAspectSupport.currentTransactionStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shadow for <tt>TransactionSynchronizationManager.getCurrentTransactionName</tt>
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String currentTransactionName() {
|
||||
return TransactionSynchronizationManager.getCurrentTransactionName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ public interface EntityService extends ServiceSpec {
|
|||
* 检查并获取(如有)重复记录
|
||||
*
|
||||
* @param checkRecord
|
||||
* @param limit
|
||||
* @param limit 最大查重返回数量
|
||||
* @return
|
||||
*/
|
||||
List<Record> getAndCheckRepeated(Record checkRecord, int limit);
|
||||
|
|
|
@ -170,19 +170,24 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
record = record.getPrimary() == null ? create(record) : update(record);
|
||||
if (!hasDetails) return record;
|
||||
|
||||
// 主记录+明细记录处理
|
||||
// 明细记录处理
|
||||
|
||||
final String dtfField = MetadataHelper.getDetailToMainField(record.getEntity().getDetailEntity()).getName();
|
||||
final Entity detailEntity = record.getEntity().getDetailEntity();
|
||||
final String dtfField = MetadataHelper.getDetailToMainField(detailEntity).getName();
|
||||
final ID mainid = record.getPrimary();
|
||||
|
||||
final boolean checkDetailsRepeated = rcm == GeneralEntityServiceContextHolder.RCM_CHECK_DETAILS
|
||||
|| rcm == GeneralEntityServiceContextHolder.RCM_CHECK_ALL;
|
||||
|
||||
// 明细可能有自己的 Service
|
||||
EntityService des = Application.getEntityService(detailEntity.getEntityCode());
|
||||
if (des.getEntityCode() == 0) des = this;
|
||||
|
||||
// 先删除
|
||||
for (int i = 0; i < details.size(); i++) {
|
||||
Record d = details.get(i);
|
||||
if (d instanceof DeleteRecord) {
|
||||
delete(d.getPrimary());
|
||||
des.delete(d.getPrimary());
|
||||
detaileds.put(i, d.getPrimary());
|
||||
}
|
||||
}
|
||||
|
@ -195,7 +200,7 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
if (checkDetailsRepeated) {
|
||||
d.setID(dtfField, mainid); // for check
|
||||
|
||||
List<Record> repeated = getAndCheckRepeated(d, 20);
|
||||
List<Record> repeated = des.getAndCheckRepeated(d, 20);
|
||||
if (!repeated.isEmpty()) {
|
||||
throw new RepeatedRecordsException(repeated);
|
||||
}
|
||||
|
@ -203,9 +208,9 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
|
||||
if (d.getPrimary() == null) {
|
||||
d.setID(dtfField, mainid);
|
||||
create(d);
|
||||
des.create(d);
|
||||
} else {
|
||||
update(d);
|
||||
des.update(d);
|
||||
}
|
||||
detaileds.put(i, d.getPrimary());
|
||||
}
|
||||
|
@ -850,4 +855,9 @@ public class GeneralEntityService extends ObservableService implements EntitySer
|
|||
}
|
||||
return specTriggers.toArray(new TriggerAction[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getEntityCode() + "#" + super.toString();
|
||||
}
|
||||
}
|
|
@ -73,26 +73,28 @@ public class RecordDifference {
|
|||
if (record != null) {
|
||||
JSONObject recordSerialize = (JSONObject) record.serialize();
|
||||
for (Map.Entry<String, Object> e : recordSerialize.entrySet()) {
|
||||
String field = e.getKey();
|
||||
if (!diffCommons && isIgnoreField(entity.getField(field))) continue;
|
||||
String fieldName = e.getKey();
|
||||
if (!entity.containsField(fieldName)) continue;
|
||||
if (!diffCommons && isIgnoreField(entity.getField(fieldName))) continue;
|
||||
|
||||
Object beforeVal = e.getValue();
|
||||
if (NullValue.is(beforeVal)) beforeVal = null;
|
||||
|
||||
merged.put(field, new Object[]{beforeVal, null});
|
||||
merged.put(fieldName, new Object[]{beforeVal, null});
|
||||
}
|
||||
}
|
||||
|
||||
if (after != null) {
|
||||
JSONObject afterSerialize = (JSONObject) after.serialize();
|
||||
for (Map.Entry<String, Object> e : afterSerialize.entrySet()) {
|
||||
String field = e.getKey();
|
||||
if (!diffCommons && isIgnoreField(entity.getField(field))) continue;
|
||||
String fieldName = e.getKey();
|
||||
if (!entity.containsField(fieldName)) continue;
|
||||
if (!diffCommons && isIgnoreField(entity.getField(fieldName))) continue;
|
||||
|
||||
Object afterVal = e.getValue();
|
||||
if (NullValue.is(afterVal)) continue;
|
||||
|
||||
Object[] mergedValue = merged.computeIfAbsent(field, k -> new Object[]{null, null});
|
||||
Object[] mergedValue = merged.computeIfAbsent(fieldName, k -> new Object[]{null, null});
|
||||
mergedValue[1] = afterVal;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
|
|||
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
|
||||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.core.privileges.bizz.Department;
|
||||
import com.rebuild.core.support.License;
|
||||
import com.rebuild.core.support.SetUser;
|
||||
import com.rebuild.core.support.i18n.Language;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
|
@ -108,12 +109,12 @@ public class AdvFilterParser extends SetUser {
|
|||
|
||||
/**
|
||||
* @param filterExpr
|
||||
* @param varRecord 条件中包含字段变量,将从此记录中提取实际值。注意如果包括字段变量但是字段无值,则过滤项会被忽略,应在配置条件时考虑此问题(设置不允许为空)
|
||||
* @param varRecord 条件中包含字段变量,将从该记录中提取实际值替换
|
||||
*/
|
||||
public AdvFilterParser(JSONObject filterExpr, ID varRecord) {
|
||||
this.filterExpr = filterExpr;
|
||||
this.rootEntity = MetadataHelper.getEntity(varRecord.getEntityCode());
|
||||
this.varRecord = varRecord;
|
||||
this.varRecord = License.isRbvAttached() ? varRecord : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -162,7 +162,7 @@ public class FieldAggregation extends TriggerAction {
|
|||
JSONObject dataFilter = ((JSONObject) actionContext.getActionContent()).getJSONObject("dataFilter");
|
||||
String dataFilterSql = null;
|
||||
if (dataFilter != null && !dataFilter.isEmpty()) {
|
||||
dataFilterSql = new AdvFilterParser(dataFilter).toSqlWhere();
|
||||
dataFilterSql = new AdvFilterParser(dataFilter, operatingContext.getFixedRecordId()).toSqlWhere();
|
||||
}
|
||||
|
||||
String filterSql = followSourceWhere;
|
||||
|
|
|
@ -468,7 +468,13 @@ public class FieldWriteback extends FieldAggregation {
|
|||
} else if (dt == DisplayType.DECIMAL) {
|
||||
targetRecord.setDouble(targetField, ObjectUtils.toDouble(newValue));
|
||||
} else if (dt == DisplayType.DATE || dt == DisplayType.DATETIME) {
|
||||
targetRecord.setDate(targetField, (Date) newValue);
|
||||
if (newValue instanceof Date) {
|
||||
targetRecord.setDate(targetField, (Date) newValue);
|
||||
} else {
|
||||
Date newValueCast = CalendarUtils.parse(newValue.toString());
|
||||
if (newValueCast == null) log.warn("Cannot cast string to date : {}", newValue);
|
||||
else targetRecord.setDate(targetField, newValueCast);
|
||||
}
|
||||
} else {
|
||||
newValue = checkoutFieldValue(newValue, targetFieldEasy);
|
||||
if (newValue != null) {
|
||||
|
|
|
@ -339,7 +339,7 @@ public class FileDownloader extends BaseController {
|
|||
|
||||
// 火狐 Safari 中文名乱码问题
|
||||
String UA = StringUtils.defaultIfBlank(request.getHeader("user-agent"), "").toUpperCase();
|
||||
if (UA.contains("FIREFOX") || UA.contains("SAFARI")) {
|
||||
if (UA.contains("FIREFOX") || UA.contains("SAFARI") || UA.contains("APPLEWEBKIT")) {
|
||||
attname = CodecUtils.urlDecode(attname);
|
||||
attname = new String(attname.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
|
|
@ -210,7 +210,7 @@ public class FeedsListController extends BaseController {
|
|||
}
|
||||
|
||||
private static final String ITEM_SQL = "select" +
|
||||
" feedsId,createdBy,createdOn,modifiedOn,content,images,attachments,scope,type,relatedRecord,contentMore" +
|
||||
" feedsId,createdBy,createdOn,modifiedOn,content,images,attachments,scope,type,relatedRecord,contentMore,autoLocation" +
|
||||
" from Feeds where ";
|
||||
|
||||
private JSONObject buildItem(Object[] o, ID user) {
|
||||
|
@ -226,6 +226,8 @@ public class FeedsListController extends BaseController {
|
|||
}
|
||||
item.put("type", o[8]);
|
||||
item.put("numComments", FeedsHelper.getNumOfComment((ID) o[0]));
|
||||
// v35
|
||||
item.put("autoLocation", o[11]);
|
||||
|
||||
// 相关记录
|
||||
ID related = (ID) o[9];
|
||||
|
|
|
@ -66,7 +66,10 @@ public class CommonOperatingController extends BaseController {
|
|||
public RespBody get(@IdParam ID recordId, HttpServletRequest request) {
|
||||
final String fields = getParameter(request, "fields");
|
||||
Record record = Application.getQueryFactory()
|
||||
.record(recordId, StringUtils.isBlank(fields) ? new String[0] : fields.split(","));
|
||||
.record(recordId, StringUtils.isBlank(fields) ? new String[0] : fields.split("[,;]"));
|
||||
if (record == null) {
|
||||
return RespBody.error("无权读取此记录或记录已被删除");
|
||||
}
|
||||
return RespBody.ok(record);
|
||||
}
|
||||
|
||||
|
|
|
@ -130,8 +130,8 @@ public class ReportsController extends BaseController {
|
|||
|
||||
if (AppUtils.isMobile(request)) {
|
||||
String fileUrl = String.format(
|
||||
"/filex/download/%s?temp=yes&_onceToken=%s&attname=%s",
|
||||
CodecUtils.urlEncode(output.getName()), AuthTokenManager.generateOnceToken(null), fileName);
|
||||
"/filex/download/%s?temp=yes&_csrfToken=%s&attname=%s",
|
||||
CodecUtils.urlEncode(output.getName()), AuthTokenManager.generateCsrfToken(90), CodecUtils.urlEncode(fileName));
|
||||
data.put("fileUrl", fileUrl);
|
||||
}
|
||||
writeSuccess(response, data);
|
||||
|
@ -139,12 +139,11 @@ public class ReportsController extends BaseController {
|
|||
} else if ("preview".equalsIgnoreCase(typeOutput)) {
|
||||
String fileUrl = String.format(
|
||||
"/filex/download/%s?temp=yes&_onceToken=%s&attname=%s",
|
||||
CodecUtils.urlEncode(output.getName()), AuthTokenManager.generateOnceToken(null), fileName);
|
||||
CodecUtils.urlEncode(output.getName()), AuthTokenManager.generateOnceToken(null), CodecUtils.urlEncode(fileName));
|
||||
fileUrl = RebuildConfiguration.getHomeUrl(fileUrl);
|
||||
|
||||
String previewUrl = StringUtils.defaultIfBlank(
|
||||
RebuildConfiguration.get(ConfigurationItem.PortalOfficePreviewUrl),
|
||||
"https://view.officeapps.live.com/op/embed.aspx?src=");
|
||||
RebuildConfiguration.get(ConfigurationItem.PortalOfficePreviewUrl), "https://view.officeapps.live.com/op/embed.aspx?src=");
|
||||
|
||||
previewUrl += CodecUtils.urlEncode(fileUrl);
|
||||
response.sendRedirect(previewUrl);
|
||||
|
|
|
@ -8,6 +8,7 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
package com.rebuild.web.robot.approval;
|
||||
|
||||
import cn.devezhao.commons.web.ServletUtils;
|
||||
import cn.devezhao.persist4j.Entity;
|
||||
import cn.devezhao.persist4j.Record;
|
||||
import cn.devezhao.persist4j.engine.ID;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
@ -79,7 +80,8 @@ public class ApprovalController extends BaseController {
|
|||
|
||||
@GetMapping("state")
|
||||
public RespBody getApprovalState(HttpServletRequest request, @IdParam(name = "record") ID recordId) {
|
||||
if (!MetadataHelper.hasApprovalField(MetadataHelper.getEntity(recordId.getEntityCode()))) {
|
||||
final Entity approvalEntity = MetadataHelper.getEntity(recordId.getEntityCode());
|
||||
if (!MetadataHelper.hasApprovalField(approvalEntity)) {
|
||||
return RespBody.error("NOT AN APPROVAL ENTITY");
|
||||
}
|
||||
|
||||
|
@ -87,6 +89,7 @@ public class ApprovalController extends BaseController {
|
|||
final ApprovalStatus status = ApprovalHelper.getApprovalStatus(recordId);
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("entityName", approvalEntity.getName());
|
||||
|
||||
int stateVal = status.getCurrentState().getState();
|
||||
data.put("state", stateVal);
|
||||
|
|
|
@ -113,6 +113,10 @@ public class SignUpController extends BaseController {
|
|||
|
||||
@PostMapping("signup-confirm")
|
||||
public RespBody signupConfirm(HttpServletRequest request) {
|
||||
if (!RebuildConfiguration.getBool(ConfigurationItem.OpenSignUp)) {
|
||||
return RespBody.errorl("管理员未开放公开注册");
|
||||
}
|
||||
|
||||
JSONObject data = (JSONObject) ServletUtils.getRequestJson(request);
|
||||
|
||||
String email = data.getString("email");
|
||||
|
|
|
@ -2725,5 +2725,112 @@
|
|||
"系统即将开始维护,暂时禁止登录":"系统即将开始维护,暂时禁止登录",
|
||||
"本月..":"本月..",
|
||||
"指定..天":"指定..天",
|
||||
"本周..":"本周.."
|
||||
"本周..":"本周..",
|
||||
"永久":"永久",
|
||||
"同步更新":"同步更新",
|
||||
"查看位置":"查看位置",
|
||||
"内容粗体":"内容粗体",
|
||||
"执行内容/结果":"执行内容/结果",
|
||||
"批量审批完成。成功 %d 条,失败 %d 条":"批量审批完成。成功 %d 条,失败 %d 条",
|
||||
"无法获取位置":"无法获取位置",
|
||||
"客户端不支持":"客户端不支持",
|
||||
"同步删除":"同步删除",
|
||||
"点击文件名保存":"点击文件名保存",
|
||||
"谁能使用这个报表":"谁能使用这个报表",
|
||||
"输入手机或邮箱,多个请使用逗号分开":"输入手机或邮箱,多个请使用逗号分开",
|
||||
"预计执行时间 (最多显示近 9 次)":"预计执行时间 (最多显示近 9 次)",
|
||||
"在线预览":"在线预览",
|
||||
"执行顺序":"执行顺序",
|
||||
"发布位置":"发布位置",
|
||||
"批量审批":"批量审批",
|
||||
"数据库语句执行超时":"数据库语句执行超时",
|
||||
"通过规则匹配":"通过规则匹配",
|
||||
"转换条件":"转换条件",
|
||||
"已允许获取位置":"已允许获取位置",
|
||||
"获取位置":"获取位置",
|
||||
"未被允许推送通知":"未被允许推送通知",
|
||||
"表单引用":"表单引用",
|
||||
"全部粗体":"全部粗体",
|
||||
"请选择%s":"请选择%s",
|
||||
"手机版菜单样式":"手机版菜单样式",
|
||||
"免费版不支持引用表单功能 [(查看详情)](https://getrebuild.com/docs/rbv-features)":"免费版不支持引用表单功能 [(查看详情)](https://getrebuild.com/docs/rbv-features)",
|
||||
"技术细节":"技术细节",
|
||||
"符合聚合数据条件的记录才会被聚合":"符合聚合数据条件的记录才会被聚合",
|
||||
"选择内部接收用户":"选择内部接收用户",
|
||||
"在线模板编辑器":"在线模板编辑器",
|
||||
"默认粗体":"默认粗体",
|
||||
"请求地址":"请求地址",
|
||||
"启用后会推送全部字段/数据(包括明细实体),否则将仅推送修改的字段/数据":"启用后会推送全部字段/数据(包括明细实体),否则将仅推送修改的字段/数据",
|
||||
"在顶部显示":"在顶部显示",
|
||||
"请再次确认审批数据范围和审批结果。开始审批吗?":"请再次确认审批数据范围和审批结果。开始审批吗?",
|
||||
"去重连接":"去重连接",
|
||||
"导出格式":"导出格式",
|
||||
"允许批量":"允许批量",
|
||||
"暂无成员":"暂无成员",
|
||||
"永久有效":"永久有效",
|
||||
"详情模式选项":"详情模式选项",
|
||||
"已做审批":"已做审批",
|
||||
"允许批量审批":"允许批量审批",
|
||||
"已允许推送通知":"已允许推送通知",
|
||||
"包括获取您的位置信息,以及通知推送":"包括获取您的位置信息,以及通知推送",
|
||||
"明细加载失败,请稍后重试":"明细加载失败,请稍后重试",
|
||||
"不允许批量审批":"不允许批量审批",
|
||||
"手动输入":"手动输入",
|
||||
"暂无使用用户":"暂无使用用户",
|
||||
"推送通知":"推送通知",
|
||||
"客户端权限":"客户端权限",
|
||||
"目标实体/记录匹配规则":"目标实体/记录匹配规则",
|
||||
"明细显示在下方":"明细显示在下方",
|
||||
"主角色":"主角色",
|
||||
"留空表示所有用户均可使用":"留空表示所有用户均可使用",
|
||||
"EXCEL 列表":"EXCEL 列表",
|
||||
"批量":"批量",
|
||||
"页面布局":"页面布局",
|
||||
"无效审批状态":"无效审批状态",
|
||||
"请设置过滤条件":"请设置过滤条件",
|
||||
"请设置高级表达式":"请设置高级表达式",
|
||||
"成功":"成功",
|
||||
"适合页面":"适合页面",
|
||||
"仅处于待你审批,且允许批量审批的记录才能审批成功":"仅处于待你审批,且允许批量审批的记录才能审批成功",
|
||||
"上传文件。需要 %s 个":"上传文件。需要 %s 个",
|
||||
"定位失败":"定位失败",
|
||||
"所有用户":"所有用户",
|
||||
"副字段显示":"副字段显示",
|
||||
"清除缓存":"清除缓存",
|
||||
"仍旧导入 (新建)":"仍旧导入 (新建)",
|
||||
"转换后同步更新":"转换后同步更新",
|
||||
"请选择审批结果":"请选择审批结果",
|
||||
"表达式返回了非布尔值,这会导致触发器执行错误。是否继续?":"表达式返回了非布尔值,这会导致触发器执行错误。是否继续?",
|
||||
"编号":"编号",
|
||||
"使用用户":"使用用户",
|
||||
"未激活":"未激活",
|
||||
"%s个维度 %s个数值":"%s个维度 %s个数值",
|
||||
"转换后同步删除":"转换后同步删除",
|
||||
"无效模板文件 (无法读取模板文件)":"无效模板文件 (无法读取模板文件)",
|
||||
"请输入%s":"请输入%s",
|
||||
"没有任何符合批量审批条件的记录":"没有任何符合批量审批条件的记录",
|
||||
"引用字段":"引用字段",
|
||||
"符合转换条件的记录才允许被转换":"符合转换条件的记录才允许被转换",
|
||||
"在线模板":"在线模板",
|
||||
"编辑数据列表":"编辑数据列表",
|
||||
"在侧栏显示":"在侧栏显示",
|
||||
"启用后调用返回结果必须为 `SUCCESS`,否则将视为调用失败":"启用后调用返回结果必须为 `SUCCESS`,否则将视为调用失败",
|
||||
"查询面板":"查询面板",
|
||||
"保存并返回":"保存并返回",
|
||||
"查看 HTML":"查看 HTML",
|
||||
"默认字体":"默认字体",
|
||||
"你提交的 %s 已审批通过":"你提交的 %s 已审批通过",
|
||||
"悼念模式":"悼念模式",
|
||||
"免费版不支持 WORD 模板功能 [(查看详情)](https://getrebuild.com/docs/rbv-features)":"免费版不支持 WORD 模板功能 [(查看详情)](https://getrebuild.com/docs/rbv-features)",
|
||||
"表达式可能存在错误,这会导致触发器执行错误。是否继续?":"表达式可能存在错误,这会导致触发器执行错误。是否继续?",
|
||||
"90天":"90天",
|
||||
"触发过程":"触发过程",
|
||||
"免费版不支持批量审批功能 [(查看详情)](https://getrebuild.com/docs/rbv-features)":"免费版不支持批量审批功能 [(查看详情)](https://getrebuild.com/docs/rbv-features)",
|
||||
"加载更多":"加载更多",
|
||||
"调用量":"调用量",
|
||||
"非审核人":"非审核人",
|
||||
"仅更新不要新建":"仅更新不要新建",
|
||||
"输入内容":"输入内容",
|
||||
"定位中":"定位中",
|
||||
"查看 PDF":"查看 PDF"
|
||||
}
|
|
@ -431,6 +431,7 @@
|
|||
<field name="relatedRecord" type="any-reference" description="相关记录" cascade="ignore"/>
|
||||
<field name="scheduleTime" type="timestamp" description="日程时间"/>
|
||||
<field name="scope" type="string" max-length="20" default-value="ALL" description="可见范围 (ALL/SELF/$TeamID)" queryable="false"/>
|
||||
<field name="autoLocation" type="string" max-length="100" description="发布位置" extra-attrs="{displayType:'LOCATION'}"/>
|
||||
<index field-list="createdOn,scope,type,createdBy"/>
|
||||
<index field-list="relatedRecord"/>
|
||||
<index field-list="type,scheduleTime,createdBy"/>
|
||||
|
|
|
@ -618,6 +618,7 @@ create table if not exists `feeds` (
|
|||
`RELATED_RECORD` char(20) comment '相关记录',
|
||||
`SCHEDULE_TIME` timestamp null default null comment '日程时间',
|
||||
`SCOPE` varchar(20) default 'ALL' comment '可见范围 (ALL/SELF/$TeamID)',
|
||||
`AUTO_LOCATION` varchar(100) comment '发布位置',
|
||||
`CREATED_BY` char(20) not null comment '创建人',
|
||||
`CREATED_ON` timestamp not null default current_timestamp comment '创建时间',
|
||||
`MODIFIED_ON` timestamp not null default current_timestamp comment '修改时间',
|
||||
|
@ -885,4 +886,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', 53);
|
||||
values ('021-9000000000000001', 'DBVer', 54);
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
-- Database upgrade scripts for rebuild 1.x and 2.x
|
||||
-- Each upgraded starts with `-- #VERSION`
|
||||
|
||||
-- #54 (v3.5)
|
||||
alter table `feeds`
|
||||
add column `AUTO_LOCATION` varchar(100) comment '发布位置';
|
||||
|
||||
-- #53 (v3.5)
|
||||
alter table `revision_history`
|
||||
add column `AUTO_ID` bigint(20) not null auto_increment comment '保证顺序',
|
||||
|
|
|
@ -64,9 +64,6 @@ button[disabled] {
|
|||
|
||||
code {
|
||||
color: #e83e8c;
|
||||
}
|
||||
|
||||
.code-view {
|
||||
font-family: ui-monospace, 'Cascadia Mono', 'Segoe UI Mono', 'Liberation Mono', Menlo, Monaco, Consolas, monospace;
|
||||
}
|
||||
|
||||
|
@ -5008,6 +5005,20 @@ pre.unstyle {
|
|||
.tablesort thead th.sorted {
|
||||
cursor: pointer;
|
||||
background-color: #dee2e6;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tablesort thead th.sorted.ascending::after,
|
||||
.tablesort thead th.sorted.descending::after {
|
||||
font: normal normal normal 18px/1 "Material Design Icons";
|
||||
content: '\F0140';
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 9.5px;
|
||||
}
|
||||
|
||||
.tablesort thead th.sorted.descending::after {
|
||||
content: '\F0143';
|
||||
}
|
||||
|
||||
.map-suggestion.dropdown-menu {
|
||||
|
@ -5354,3 +5365,12 @@ div.dataTables_wrapper.compact div.dataTables_oper .btn-space {
|
|||
.table.table-btm-line tbody {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.dataTables_oper.invisible2 > .btn,
|
||||
.dataTables_oper.invisible2 > .btn-group {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dataTables_oper.invisible2 > .btn.J_view {
|
||||
display: inline-block;
|
||||
}
|
|
@ -272,7 +272,7 @@ class ApprovalReferral extends RbModalHandler {
|
|||
$mp.end()
|
||||
|
||||
if (res.error_code === 0) {
|
||||
RbHighbar.success(res.data > 0 ? $L('已转审 %d 条审批记录') : $L('批量转审完成'))
|
||||
RbHighbar.success(res.data > 0 ? $L('已转审 %d 条审批记录', res.data) : $L('批量转审完成'))
|
||||
setTimeout(() => that.hide(), 1500)
|
||||
} else {
|
||||
RbHighbar.error(res.error_msg)
|
||||
|
|
|
@ -37,10 +37,10 @@ class ReportList extends ConfigList {
|
|||
) : (
|
||||
item[3]
|
||||
)}
|
||||
{item[6] === 1 && <span className="badge badge-info badge-arrow3 badge-sm ml-1 excel">{$L('EXCEL')}</span>}
|
||||
{item[6] === 1 && <span className="badge badge-info badge-arrow3 badge-sm ml-1 excel">EXCEL</span>}
|
||||
{item[6] === 2 && <span className="badge badge-info badge-arrow3 badge-sm ml-1 excel">{$L('EXCEL 列表')}</span>}
|
||||
{isHtml5 && <span className="badge badge-info badge-arrow3 badge-sm ml-1">{$L('在线模板')}</span>}
|
||||
{item[6] === 4 && <span className="badge badge-info badge-arrow3 badge-sm ml-1 word">{$L('WORD')}</span>}
|
||||
{item[6] === 4 && <span className="badge badge-info badge-arrow3 badge-sm ml-1 word">WORD</span>}
|
||||
|
||||
{outputType.includes('pdf') && <span className="badge badge-secondary badge-sm ml-1">PDF</span>}
|
||||
{outputType.includes('html') && <span className="badge badge-secondary badge-sm ml-1">HTML</span>}
|
||||
|
|
|
@ -680,7 +680,7 @@ class ApprovalList extends BaseChart {
|
|||
<tbody>
|
||||
{data.data.map((item, idx) => {
|
||||
return (
|
||||
<tr key={'approval-' + idx}>
|
||||
<tr key={`approval-${idx}`}>
|
||||
<td className="user-avatar cell-detail user-info">
|
||||
<img src={`${rb.baseUrl}/account/user-avatar/${item[0]}`} alt="Avatar" />
|
||||
<span>{item[1]}</span>
|
||||
|
@ -689,7 +689,9 @@ class ApprovalList extends BaseChart {
|
|||
</span>
|
||||
</td>
|
||||
<td className="cell-detail">
|
||||
<a href={`${rb.baseUrl}/app/redirect?id=${item[3]}`}>{item[4]}</a>
|
||||
<a href={`${rb.baseUrl}/app/redirect?id=${item[3]}&type=newtab`} target="_blank">
|
||||
{item[4]}
|
||||
</a>
|
||||
<span className="cell-detail-description">{item[6]}</span>
|
||||
</td>
|
||||
<td className="actions text-right text-nowrap">
|
||||
|
@ -795,7 +797,7 @@ class FeedsSchedule extends BaseChart {
|
|||
}
|
||||
|
||||
return (
|
||||
<tr key={'schedule-' + idx}>
|
||||
<tr key={`schedule-${idx}`}>
|
||||
<td>
|
||||
<a href={`${rb.baseUrl}/app/redirect?id=${item.id}`} className="content text-break" dangerouslySetInnerHTML={{ __html: item.content }} />
|
||||
</td>
|
||||
|
@ -1118,10 +1120,7 @@ class ProjectTasks extends BaseChart {
|
|||
|
||||
$.post('/app/entity/common-save', JSON.stringify(data), (res) => {
|
||||
if (res.error_code > 0) return RbHighbar.error(res.error_msg)
|
||||
$target
|
||||
.parents('tr')
|
||||
.removeClass('status-0 status-1')
|
||||
.addClass('status-' + data.status)
|
||||
$target.parents('tr').removeClass('status-0 status-1').addClass(`status-${data.status}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1231,7 +1230,7 @@ class DataList extends BaseChart {
|
|||
data-id={lastCell.id}
|
||||
onDoubleClick={(e) => {
|
||||
$stopEvent(e, true)
|
||||
window.open(`${rb.baseUrl}/app/redirect?id=${lastCell.id}`)
|
||||
window.open(`${rb.baseUrl}/app/redirect?id=${lastCell.id}&type=newtab`)
|
||||
}}>
|
||||
{row.map((c, idx) => {
|
||||
if (idx === lastIndex) return null // Last is ID
|
||||
|
|
|
@ -588,10 +588,29 @@ function __renderRichContent(e) {
|
|||
}
|
||||
}
|
||||
|
||||
let AL = e.autoLocation ? e.autoLocation.split('$$$$') : false
|
||||
if (AL) {
|
||||
AL = {
|
||||
lng: AL[1].split(',')[0],
|
||||
lat: AL[1].split(',')[1],
|
||||
text: AL[0],
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rich-content">
|
||||
<div className="texts text-break" dangerouslySetInnerHTML={{ __html: contentHtml }} />
|
||||
<div className="appends">
|
||||
{AL && (
|
||||
<div>
|
||||
<span>
|
||||
<i className="icon mdi mdi-cellphone-marker mr-1" />
|
||||
</span>
|
||||
<a title={$L('查看位置')} onClick={() => BaiduMapModal.view(AL)}>
|
||||
{AL.text}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
{e.type === 4 && (
|
||||
<div>
|
||||
<div>
|
||||
|
@ -616,7 +635,7 @@ function __renderRichContent(e) {
|
|||
<i className={`icon zmdi zmdi-${e.relatedRecord.icon}`} />
|
||||
{` ${e.relatedRecord.entityLabel} : `}
|
||||
</span>
|
||||
<a href={`${rb.baseUrl}/app/redirect?id=${e.relatedRecord.id}`} title={$L('查看记录')}>
|
||||
<a href={`${rb.baseUrl}/app/redirect?id=${e.relatedRecord.id}&type=newtab`} target="_blank" title={$L('查看记录')}>
|
||||
{e.relatedRecord.text}
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -119,7 +119,7 @@ $(document).ready(() => {
|
|||
$item.addClass('hide')
|
||||
}
|
||||
})
|
||||
})
|
||||
}, 200)
|
||||
})
|
||||
|
||||
setTimeout(() => $input[0].focus(), 20)
|
||||
|
|
|
@ -7,6 +7,9 @@ See LICENSE and COMMERCIAL in the project root for license information.
|
|||
/* global InitModels */
|
||||
|
||||
window.__LAB_SHOWNDETAIL = false
|
||||
window.bosskeyTrigger = function () {
|
||||
window.__LAB_SHOWNDETAIL = true
|
||||
}
|
||||
|
||||
$(document).ready(() => {
|
||||
const $bnew = $('.btn-primary.new').on('click', () => {
|
||||
|
|
|
@ -91,7 +91,7 @@ const RbListPage = {
|
|||
if (ep.A !== true) $('.J_assign').remove()
|
||||
if (ep.S !== true) $('.J_share, .J_unshare').remove()
|
||||
$cleanMenu('.J_action')
|
||||
$('.dataTables_oper.invisible').removeClass('invisible')
|
||||
$('.dataTables_oper.invisible2').removeClass('invisible2')
|
||||
}
|
||||
|
||||
// Filter Pane
|
||||
|
|
|
@ -220,7 +220,7 @@ class RbFormModal extends React.Component {
|
|||
}
|
||||
|
||||
// 获取当前表单对象
|
||||
getCurrentForm() {
|
||||
getFormComp() {
|
||||
return this._formComponentRef
|
||||
}
|
||||
|
||||
|
@ -230,17 +230,20 @@ class RbFormModal extends React.Component {
|
|||
* @param {*} forceNew
|
||||
*/
|
||||
static create(props, forceNew) {
|
||||
const that = this
|
||||
if (forceNew === true) {
|
||||
renderRbcomp(<RbFormModal {...props} disposeOnHide />)
|
||||
renderRbcomp(<RbFormModal {...props} disposeOnHide />, function () {
|
||||
that.__CURRENT35 = this
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (this.__HOLDER) {
|
||||
this.__HOLDER.show(props)
|
||||
} else {
|
||||
const that = this
|
||||
renderRbcomp(<RbFormModal {...props} />, null, function () {
|
||||
that.__HOLDER = this
|
||||
that.__CURRENT35 = this
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -648,6 +651,9 @@ class RbForm extends React.Component {
|
|||
const keys = Object.keys(this._ProTables)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const _ProTable = this._ProTables[keys[i]]
|
||||
// 明细未配置或出错
|
||||
if (!_ProTable._initModel) continue
|
||||
|
||||
const details = _ProTable.buildFormData()
|
||||
if (!details) return
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ class ProTable extends React.Component {
|
|||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
$('.detail-form-table .btn-group .btn').attr('disabled', true)
|
||||
// $('.detail-form-table .btn-group .btn').attr('disabled', true)
|
||||
return <RbAlertBox message={this.state.hasError} />
|
||||
}
|
||||
|
||||
|
@ -143,6 +143,12 @@ class ProTable extends React.Component {
|
|||
}
|
||||
|
||||
addLine(model) {
|
||||
// 明细未配置或出错
|
||||
if (!model) {
|
||||
if (this.state.hasError) RbHighbar.create(this.state.hasError)
|
||||
return
|
||||
}
|
||||
|
||||
const lineKey = `${this.props.entity.entity}-${model.id ? model.id : $random()}`
|
||||
const ref = React.createRef()
|
||||
const FORM = (
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="dataTables_oper invisible">
|
||||
<div class="dataTables_oper invisible2">
|
||||
<button class="btn btn-space btn-secondary J_view" type="button" disabled="disabled"><i class="icon zmdi zmdi-folder"></i> [[${bundle.L('打开')}]]</button>
|
||||
<button class="btn btn-space btn-secondary J_edit" type="button" disabled="disabled"><i class="icon zmdi zmdi-edit"></i> [[${bundle.L('编辑')}]]</button>
|
||||
<div class="btn-group btn-space J_action">
|
||||
|
|
|
@ -36,6 +36,9 @@
|
|||
outline: 0 none;
|
||||
background-color: transparent;
|
||||
}
|
||||
.sortable-box-title .search-input > input:focus {
|
||||
border-color: #4285f4;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="dialog">
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="dataTables_oper invisible">
|
||||
<div class="dataTables_oper invisible2">
|
||||
<button class="btn btn-space btn-secondary J_view" type="button" disabled="disabled"><i class="icon zmdi zmdi-folder"></i> [[${bundle.L('打开')}]]</button>
|
||||
<button class="btn btn-space btn-secondary J_edit" type="button" disabled="disabled"><i class="icon zmdi zmdi-edit"></i> [[${bundle.L('编辑')}]]</button>
|
||||
<button class="btn btn-space btn-primary J_new" type="button" disabled="disabled"><i class="icon zmdi zmdi-plus"></i> [[${bundle.L('新建')}]]</button>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
// 建议将上方匹配路径设置为 `/` 以便观察效果
|
||||
|
||||
const demoEntityName = 'Account' // TODO 修改为你要测试的实体
|
||||
const demoFieldName = 'AccountName' // TODO 修改为你要测试的实体字段
|
||||
const demoFieldName = 'AccountName' // TODO 修改为你要测试的实体字段(文本字段)
|
||||
|
||||
let _List, _Form, _View
|
||||
|
||||
|
@ -78,6 +78,7 @@ function demoForList() {
|
|||
// 指定字段加粗加红显示
|
||||
const fieldKey = `${demoEntityName}.${demoFieldName}`
|
||||
_List.regCellRender(fieldKey, function (v) {
|
||||
// 如果返回 false 则按照默认样式显示
|
||||
return <strong className="text-danger">{JSON.stringify(v)}</strong>
|
||||
})
|
||||
|
||||
|
@ -104,7 +105,31 @@ function demoForForm() {
|
|||
const lookFieldKey = `${demoEntityName}.${demoFieldName}`
|
||||
_Form.onFieldValueChange(function (fieldKey, fieldValue, recordId) {
|
||||
if (lookFieldKey === fieldKey) {
|
||||
RbHighbar.create(`记录: ${recordId || ''} 的新值 : ${fieldValue}`)
|
||||
RbHighbar.create(`记录: ${recordId || ''} 的新值 : ${fieldValue || ''}`)
|
||||
}
|
||||
})
|
||||
|
||||
_Form.onOpen(() => {
|
||||
// 获取字段组件,便便进行相关操作
|
||||
const fieldComp = _Form.getFieldComp(demoFieldName)
|
||||
|
||||
// 设为必填
|
||||
fieldComp.setNullable(false)
|
||||
// 设置 Tip
|
||||
fieldComp.setTip('危险品')
|
||||
fieldComp.setTip(<b className="text-danger">危险品</b>)
|
||||
|
||||
// 显示隐藏
|
||||
_Form.onFieldValueChange(function (fieldKey, fieldValue) {
|
||||
if (lookFieldKey === fieldKey) {
|
||||
// 输入 hide 隐藏
|
||||
if (fieldValue === 'hide') {
|
||||
fieldComp.setHidden(true)
|
||||
|
||||
// 3 秒后重新显示
|
||||
setTimeout(() => fieldComp.setHidden(false), 3000)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue