mirror of
https://github.com/getrebuild/rebuild.git
synced 2025-10-06 13:40:00 +08:00
feat: Type cast (#702)
* bcc * feat: field-type-cast * feat: aviator DateCompare * tf.nullable
This commit is contained in:
parent
30ddd5528a
commit
b6530c7938
15 changed files with 305 additions and 26 deletions
2
@rbv
2
@rbv
|
@ -1 +1 @@
|
||||||
Subproject commit 791d6f665c9e937b4503459cc68958f4c266b4cd
|
Subproject commit 031039036ba44f47b61a2b81bcaa70922ef3656a
|
|
@ -411,4 +411,54 @@ public class Field2Schema extends SetUser {
|
||||||
|
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型转换
|
||||||
|
*
|
||||||
|
* @param field
|
||||||
|
* @param toType
|
||||||
|
* @param force
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean castType(Field field, DisplayType toType, boolean force) {
|
||||||
|
EasyField easyMeta = EasyMetaFactory.valueOf(field);
|
||||||
|
ID metaRecordId = easyMeta.getMetaId();
|
||||||
|
if (easyMeta.isBuiltin() || metaRecordId == null) {
|
||||||
|
throw new MetadataModificationException(Language.L("系统内置,不允许转换"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!force) {
|
||||||
|
long count;
|
||||||
|
if ((count = checkRecordCount(field.getOwnEntity())) > 100000) {
|
||||||
|
throw new MetadataModificationException(Language.L("实体记录过多 (%d),转换字段可能导致表损坏", count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Record meta = EntityHelper.forUpdate(metaRecordId, getUser(), false);
|
||||||
|
meta.setString("displayType", toType.name());
|
||||||
|
Application.getCommonsService().update(meta, false);
|
||||||
|
|
||||||
|
Dialect dialect = Application.getPersistManagerFactory().getDialect();
|
||||||
|
final Table table = new Table(field.getOwnEntity(), dialect);
|
||||||
|
StringBuilder ddl = new StringBuilder();
|
||||||
|
table.generateFieldDDL(field, ddl);
|
||||||
|
|
||||||
|
String alterSql = String.format("alter table `%s` change column `%s` ",
|
||||||
|
field.getOwnEntity().getPhysicalName(), field.getPhysicalName());
|
||||||
|
alterSql += ddl.toString().trim();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Application.getSqlExecutor().executeBatch(new String[]{alterSql}, DDL_TIMEOUT);
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
// 还原
|
||||||
|
meta.setString("displayType", EasyMetaFactory.getDisplayType(field).name());
|
||||||
|
Application.getCommonsService().update(meta, false);
|
||||||
|
|
||||||
|
log.error("DDL ERROR : \n" + alterSql, ex);
|
||||||
|
throw new MetadataModificationException(ThrowableUtils.getRootCause(ex).getLocalizedMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
MetadataHelper.getMetadataFactory().refresh();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,12 @@ public class AviatorUtils {
|
||||||
|
|
||||||
AVIATOR.addOpFunction(OperatorType.ADD, new OverOperatorType.DateAdd());
|
AVIATOR.addOpFunction(OperatorType.ADD, new OverOperatorType.DateAdd());
|
||||||
AVIATOR.addOpFunction(OperatorType.SUB, new OverOperatorType.DateSub());
|
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());
|
||||||
|
|
||||||
addCustomFunction(new DateDiffFunction());
|
addCustomFunction(new DateDiffFunction());
|
||||||
addCustomFunction(new DateAddFunction());
|
addCustomFunction(new DateAddFunction());
|
||||||
|
|
|
@ -10,6 +10,7 @@ package com.rebuild.core.service.trigger.aviator;
|
||||||
import cn.devezhao.commons.CalendarUtils;
|
import cn.devezhao.commons.CalendarUtils;
|
||||||
import com.googlecode.aviator.lexer.token.OperatorType;
|
import com.googlecode.aviator.lexer.token.OperatorType;
|
||||||
import com.googlecode.aviator.runtime.function.AbstractFunction;
|
import com.googlecode.aviator.runtime.function.AbstractFunction;
|
||||||
|
import com.googlecode.aviator.runtime.type.AviatorBoolean;
|
||||||
import com.googlecode.aviator.runtime.type.AviatorLong;
|
import com.googlecode.aviator.runtime.type.AviatorLong;
|
||||||
import com.googlecode.aviator.runtime.type.AviatorObject;
|
import com.googlecode.aviator.runtime.type.AviatorObject;
|
||||||
|
|
||||||
|
@ -26,6 +27,8 @@ public class OverOperatorType {
|
||||||
|
|
||||||
private OverOperatorType() {}
|
private OverOperatorType() {}
|
||||||
|
|
||||||
|
// -- 计算
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 日期加
|
* 日期加
|
||||||
*/
|
*/
|
||||||
|
@ -83,4 +86,102 @@ public class OverOperatorType {
|
||||||
Date d = CalendarUtils.addDay(date, num);
|
Date d = CalendarUtils.addDay(date, num);
|
||||||
return new AviatorDate(d);
|
return new AviatorDate(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -- 比较
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期比较
|
||||||
|
*/
|
||||||
|
static abstract class DateCompare extends AbstractFunction {
|
||||||
|
private static final long serialVersionUID = -4160230503581309424L;
|
||||||
|
@Override
|
||||||
|
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();
|
||||||
|
boolean b = compare(v1, v2);
|
||||||
|
return AviatorBoolean.valueOf(b);
|
||||||
|
} else {
|
||||||
|
return arg1.add(arg2, env); // Use default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// GE `>=`
|
||||||
|
static class DateCompareGE extends DateCompare {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// GT `>`
|
||||||
|
static class DateCompareGT extends DateCompare {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// EQ `==`
|
||||||
|
static class DateCompareEQ extends DateCompare {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// NEQ `!=`
|
||||||
|
static class DateCompareNEQ extends DateCompare {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ public enum ConfigurationItem {
|
||||||
StorageURL, StorageApiKey, StorageApiSecret, StorageBucket,
|
StorageURL, StorageApiKey, StorageApiSecret, StorageBucket,
|
||||||
|
|
||||||
// 邮件
|
// 邮件
|
||||||
MailUser, MailPassword, MailAddr, MailName(AppName), MailCc,
|
MailUser, MailPassword, MailAddr, MailName(AppName), MailCc, MailBcc,
|
||||||
MailSmtpServer,
|
MailSmtpServer,
|
||||||
|
|
||||||
// 短信
|
// 短信
|
||||||
|
|
|
@ -115,7 +115,7 @@ public class RebuildConfiguration extends KVStorage {
|
||||||
/**
|
/**
|
||||||
* 邮件账号
|
* 邮件账号
|
||||||
*
|
*
|
||||||
* @return returns [MailUser, MailPassword, MailAddr, MailName, MailCc, MailSmtpServer]
|
* @return returns [MailUser, MailPassword, MailAddr, MailName, MailCc, MailBcc, MailSmtpServer]
|
||||||
*/
|
*/
|
||||||
public static String[] getMailAccount() {
|
public static String[] getMailAccount() {
|
||||||
String[] set = getsNoUnset(false,
|
String[] set = getsNoUnset(false,
|
||||||
|
@ -123,11 +123,13 @@ public class RebuildConfiguration extends KVStorage {
|
||||||
if (set == null) return null;
|
if (set == null) return null;
|
||||||
|
|
||||||
String cc = get(ConfigurationItem.MailCc);
|
String cc = get(ConfigurationItem.MailCc);
|
||||||
|
String bcc = get(ConfigurationItem.MailBcc);
|
||||||
String smtpServer = get(ConfigurationItem.MailSmtpServer);
|
String smtpServer = get(ConfigurationItem.MailSmtpServer);
|
||||||
|
|
||||||
return new String[] {
|
return new String[] {
|
||||||
set[0], set[1], set[2], set[3],
|
set[0], set[1], set[2], set[3],
|
||||||
StringUtils.defaultIfBlank(cc, null),
|
StringUtils.defaultIfBlank(cc, null),
|
||||||
|
StringUtils.defaultIfBlank(bcc, null),
|
||||||
StringUtils.defaultIfBlank(smtpServer, null)
|
StringUtils.defaultIfBlank(smtpServer, null)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,16 +89,16 @@ public class SMSender {
|
||||||
* @throws ConfigurationException If mail-account unset
|
* @throws ConfigurationException If mail-account unset
|
||||||
*/
|
*/
|
||||||
public static String sendMail(String to, String subject, String content, boolean useTemplate, String[] specAccount) throws ConfigurationException {
|
public static String sendMail(String to, String subject, String content, boolean useTemplate, String[] specAccount) throws ConfigurationException {
|
||||||
if (specAccount == null || specAccount.length < 5
|
if (specAccount == null || specAccount.length < 6
|
||||||
|| StringUtils.isBlank(specAccount[0]) || StringUtils.isBlank(specAccount[1])
|
|| StringUtils.isBlank(specAccount[0]) || StringUtils.isBlank(specAccount[1])
|
||||||
|| StringUtils.isBlank(specAccount[2]) || StringUtils.isBlank(specAccount[3])) {
|
|| StringUtils.isBlank(specAccount[2]) || StringUtils.isBlank(specAccount[3])) {
|
||||||
throw new ConfigurationException(Language.L("邮件账户未配置或配置错误"));
|
throw new ConfigurationException(Language.L("邮件账户未配置或配置错误"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Application.devMode()) {
|
// if (Application.devMode()) {
|
||||||
log.info("[dev] Fake send email to : {} / {} / {}", to, subject, content);
|
// log.info("[dev] Fake send email to : {} / {} / {}", to, subject, content);
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// 使用邮件模板
|
// 使用邮件模板
|
||||||
if (useTemplate) {
|
if (useTemplate) {
|
||||||
|
@ -133,7 +133,7 @@ public class SMSender {
|
||||||
final String logContent = "【" + subject + "】" + content;
|
final String logContent = "【" + subject + "】" + content;
|
||||||
|
|
||||||
// Use SMTP
|
// Use SMTP
|
||||||
if (specAccount.length >= 6 && StringUtils.isNotBlank(specAccount[5])) {
|
if (specAccount.length >= 7 && StringUtils.isNotBlank(specAccount[6])) {
|
||||||
try {
|
try {
|
||||||
String emailId = sendMailViaSmtp(to, subject, content, specAccount);
|
String emailId = sendMailViaSmtp(to, subject, content, specAccount);
|
||||||
createLog(to, logContent, TYPE_EMAIL, emailId, null);
|
createLog(to, logContent, TYPE_EMAIL, emailId, null);
|
||||||
|
@ -150,6 +150,7 @@ public class SMSender {
|
||||||
params.put("signature", specAccount[1]);
|
params.put("signature", specAccount[1]);
|
||||||
params.put("to", to);
|
params.put("to", to);
|
||||||
if (StringUtils.isNotBlank(specAccount[4])) params.put("cc", specAccount[4]);
|
if (StringUtils.isNotBlank(specAccount[4])) params.put("cc", specAccount[4]);
|
||||||
|
if (StringUtils.isNotBlank(specAccount[5])) params.put("bcc", specAccount[5]);
|
||||||
params.put("from", specAccount[2]);
|
params.put("from", specAccount[2]);
|
||||||
params.put("from_name", specAccount[3]);
|
params.put("from_name", specAccount[3]);
|
||||||
params.put("subject", subject);
|
params.put("subject", subject);
|
||||||
|
@ -196,6 +197,7 @@ public class SMSender {
|
||||||
HtmlEmail email = new HtmlEmail();
|
HtmlEmail email = new HtmlEmail();
|
||||||
email.addTo(to);
|
email.addTo(to);
|
||||||
if (StringUtils.isNotBlank(specAccount[4])) email.addCc(specAccount[4]);
|
if (StringUtils.isNotBlank(specAccount[4])) email.addCc(specAccount[4]);
|
||||||
|
if (StringUtils.isNotBlank(specAccount[5])) email.addBcc(specAccount[5]);
|
||||||
email.setSubject(subject);
|
email.setSubject(subject);
|
||||||
email.setHtmlMsg(htmlContent);
|
email.setHtmlMsg(htmlContent);
|
||||||
|
|
||||||
|
@ -203,7 +205,7 @@ public class SMSender {
|
||||||
email.setAuthentication(specAccount[0], specAccount[1]);
|
email.setAuthentication(specAccount[0], specAccount[1]);
|
||||||
|
|
||||||
// HOST[:PORT:SSL|TLS]
|
// HOST[:PORT:SSL|TLS]
|
||||||
String[] hostPortSsl = specAccount[5].split(":");
|
String[] hostPortSsl = specAccount[6].split(":");
|
||||||
email.setHostName(hostPortSsl[0]);
|
email.setHostName(hostPortSsl[0]);
|
||||||
if (hostPortSsl.length > 1) email.setSmtpPort(Integer.parseInt(hostPortSsl[1]));
|
if (hostPortSsl.length > 1) email.setSmtpPort(Integer.parseInt(hostPortSsl[1]));
|
||||||
if (hostPortSsl.length > 2) {
|
if (hostPortSsl.length > 2) {
|
||||||
|
|
|
@ -245,6 +245,7 @@ public class ConfigurationController extends BaseController {
|
||||||
data.getString("MailUser"), data.getString("MailPassword"),
|
data.getString("MailUser"), data.getString("MailPassword"),
|
||||||
data.getString("MailAddr"), data.getString("MailName"),
|
data.getString("MailAddr"), data.getString("MailName"),
|
||||||
data.getString("MailCc"),
|
data.getString("MailCc"),
|
||||||
|
data.getString("MailBcc"),
|
||||||
data.getString("MailSmtpServer")
|
data.getString("MailSmtpServer")
|
||||||
};
|
};
|
||||||
if (specAccount[1].contains("*")) {
|
if (specAccount[1].contains("*")) {
|
||||||
|
@ -277,7 +278,7 @@ public class ConfigurationController extends BaseController {
|
||||||
|
|
||||||
Object[] smsCount = Application.createQueryNoFilter(sqlCount)
|
Object[] smsCount = Application.createQueryNoFilter(sqlCount)
|
||||||
.setParameter(1, 1)
|
.setParameter(1, 1)
|
||||||
.setParameter(1, xday)
|
.setParameter(2, xday)
|
||||||
.unique();
|
.unique();
|
||||||
|
|
||||||
Object[][] email = Application.createQueryNoFilter(sql)
|
Object[][] email = Application.createQueryNoFilter(sql)
|
||||||
|
|
|
@ -43,8 +43,8 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="smtp-show">
|
<tr class="smtp-show">
|
||||||
<td width="40%">[[${bundle.L('SMTP 服务器地址')}]]</td>
|
<td width="40%">[[${bundle.L('SMTP 服务器地址')}]]</td>
|
||||||
<td data-id="MailSmtpServer" data-ignore="true" th:data-value="${mailAccount == null ? '' : mailAccount[5]}">
|
<td data-id="MailSmtpServer" data-ignore="true" th:data-value="${mailAccount == null ? '' : mailAccount[6]}">
|
||||||
[[${mailAccount == null ? bundle.L('未设置') : mailAccount[5]}]]
|
[[${mailAccount == null ? bundle.L('未设置') : mailAccount[6]}]]
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -78,6 +78,12 @@
|
||||||
[[${mailAccount == null || mailAccount[4] == null ? bundle.L('无') : mailAccount[4]}]]
|
[[${mailAccount == null || mailAccount[4] == null ? bundle.L('无') : mailAccount[4]}]]
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>[[${bundle.L('密送地址')}]]</td>
|
||||||
|
<td data-id="MailBcc" th:data-value="${mailAccount == null || mailAccount[5] == null ? '' : mailAccount[5]}" data-optional="true">
|
||||||
|
[[${mailAccount == null || mailAccount[5] == null ? bundle.L('无') : mailAccount[5]}]]
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr class="show-on-edit">
|
<tr class="show-on-edit">
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
@ -66,7 +66,12 @@
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right">[[${bundle.L('类型')}]]</label>
|
<label class="col-md-12 col-xl-3 col-lg-4 col-form-label text-lg-right">[[${bundle.L('类型')}]]</label>
|
||||||
<div class="col-md-12 col-xl-6 col-lg-8">
|
<div class="col-md-12 col-xl-6 col-lg-8">
|
||||||
|
<div class="input-group">
|
||||||
<input class="form-control form-control-sm" type="text" readonly th:value="${fieldTypeLabel}" />
|
<input class="form-control form-control-sm" type="text" readonly th:value="${fieldTypeLabel}" />
|
||||||
|
<div class="input-group-append hide">
|
||||||
|
<button type="button" class="btn btn-secondary J_cast-type" th:title="${bundle.L('转换字段类型')}"><i class="zmdi zmdi-swap icon"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<th:block th:if="${fieldType == 'DECIMAL'}">
|
<th:block th:if="${fieldType == 'DECIMAL'}">
|
||||||
|
@ -531,6 +536,7 @@
|
||||||
<script th:src="@{/assets/js/general/rb-forms.append.js}" type="text/babel"></script>
|
<script th:src="@{/assets/js/general/rb-forms.append.js}" type="text/babel"></script>
|
||||||
<script th:src="@{/assets/js/metadata/field-formula.js}" type="text/babel"></script>
|
<script th:src="@{/assets/js/metadata/field-formula.js}" type="text/babel"></script>
|
||||||
<script th:src="@{/assets/js/metadata/field-edit.js}" type="text/babel"></script>
|
<script th:src="@{/assets/js/metadata/field-edit.js}" type="text/babel"></script>
|
||||||
|
<script th:src="@{/assets/js/metadata/field-type.js}" type="text/babel"></script>
|
||||||
<script th:src="@{/assets/js/metadata/entity-switch.js}" type="text/babel"></script>
|
<script th:src="@{/assets/js/metadata/entity-switch.js}" type="text/babel"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -321,6 +321,9 @@ class DlgAddChart extends RbFormHandler {
|
||||||
<button className="btn btn-primary" type="button" onClick={() => this.next()}>
|
<button className="btn btn-primary" type="button" onClick={() => this.next()}>
|
||||||
{$L('下一步')}
|
{$L('下一步')}
|
||||||
</button>
|
</button>
|
||||||
|
<button className="btn btn-link" type="button" onClick={() => this.hide()}>
|
||||||
|
{$L('取消')}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -340,12 +343,6 @@ class DlgAddChart extends RbFormHandler {
|
||||||
this.__select2 = $(this._$entity).select2({
|
this.__select2 = $(this._$entity).select2({
|
||||||
allowClear: false,
|
allowClear: false,
|
||||||
placeholder: $L('选择数据来源'),
|
placeholder: $L('选择数据来源'),
|
||||||
// templateResult: function (res) {
|
|
||||||
// const $span = $('<span class="icon-append"></span>').attr('title', res.text).text(res.text)
|
|
||||||
// const found = _data.find((x) => x.entity === res.id)
|
|
||||||
// if (found) $(`<i class="icon zmdi zmdi-${found.icon}"></i>`).appendTo($span)
|
|
||||||
// return $span
|
|
||||||
// },
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights re
|
||||||
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
|
||||||
See LICENSE and COMMERCIAL in the project root for license information.
|
See LICENSE and COMMERCIAL in the project root for license information.
|
||||||
*/
|
*/
|
||||||
/* global FormulaDate, FormulaCalc */
|
/* global FormulaDate, FormulaCalc, FIELD_TYPES */
|
||||||
|
|
||||||
const wpc = window.__PageConfig
|
const wpc = window.__PageConfig
|
||||||
const __gExtConfig = {}
|
const __gExtConfig = {}
|
||||||
|
@ -25,6 +25,8 @@ $(document).ready(function () {
|
||||||
if (wpc.fieldBuildin) {
|
if (wpc.fieldBuildin) {
|
||||||
$('.J_fieldAttrs, .J_advOpt, .J_for-STATE').remove()
|
$('.J_fieldAttrs, .J_advOpt, .J_for-STATE').remove()
|
||||||
$('#referenceCascadingField, #referenceQuickNew, #referenceDataFilter').parents('.form-group').remove()
|
$('#referenceCascadingField, #referenceQuickNew, #referenceDataFilter').parents('.form-group').remove()
|
||||||
|
} else {
|
||||||
|
$('.J_cast-type').parent().removeClass('hide')
|
||||||
}
|
}
|
||||||
// 显示重复值选项
|
// 显示重复值选项
|
||||||
if (SHOW_REPEATABLE.includes(dt) && wpc.fieldName !== 'approvalId') {
|
if (SHOW_REPEATABLE.includes(dt) && wpc.fieldName !== 'approvalId') {
|
||||||
|
@ -299,6 +301,14 @@ $(document).ready(function () {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$('.J_cast-type').on('click', () => {
|
||||||
|
if (rb.commercial < 10) {
|
||||||
|
RbHighbar.error(WrapHtml($L('免费版不支持此功能 [(查看详情)](https://getrebuild.com/docs/rbv-features)')))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
renderRbcomp(<FieldTypeCast entity={wpc.entityName} field={wpc.fieldName} fromType={wpc.fieldType} />)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Check incorrect?
|
// Check incorrect?
|
||||||
|
@ -732,3 +742,87 @@ const _handleAnyReference = function (es) {
|
||||||
if (es) $s2.val(es.split(',')).trigger('change')
|
if (es) $s2.val(es.split(',')).trigger('change')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 字段类型转换
|
||||||
|
const __TYPE2TYPE = {
|
||||||
|
'NUMBER': ['DECIMAL'],
|
||||||
|
'DECIMAL': ['NUMBER'],
|
||||||
|
'DATE': ['DATETIME'],
|
||||||
|
'DATETIME': ['DATE'],
|
||||||
|
'TEXT': ['NTEXT', 'PHONE', 'EMAIL', 'URL'],
|
||||||
|
'PHONE': ['TEXT'],
|
||||||
|
'EMAIL': ['TEXT'],
|
||||||
|
'URL': ['TEXT'],
|
||||||
|
'NTEXT': ['TEXT'],
|
||||||
|
'IMAGE': ['FILE'],
|
||||||
|
'FILE': ['IMAGE'],
|
||||||
|
}
|
||||||
|
class FieldTypeCast extends RbFormHandler {
|
||||||
|
render() {
|
||||||
|
const toTypes = __TYPE2TYPE[this.props.fromType] || []
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RbModal title={$L('转换字段类型')} ref={(c) => (this._dlg = c)} disposeOnHide>
|
||||||
|
<div className="form">
|
||||||
|
<div className="form-group row">
|
||||||
|
<label className="col-sm-3 col-form-label text-sm-right">{$L('当前字段类型')}</label>
|
||||||
|
<div className="col-sm-7">
|
||||||
|
<div className="form-control-plaintext text-bold">{FIELD_TYPES[this.props.fromType][0]}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-group row">
|
||||||
|
<label className="col-sm-3 col-form-label text-sm-right">{$L('新字段类型')}</label>
|
||||||
|
<div className="col-sm-7">
|
||||||
|
<select className="form-control form-control-sm" ref={(c) => (this._$toType = c)}>
|
||||||
|
{toTypes.map((t) => {
|
||||||
|
return (
|
||||||
|
<option value={t} key={t}>
|
||||||
|
{(FIELD_TYPES[t] || [t])[0]}
|
||||||
|
</option>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</select>
|
||||||
|
<p className="form-text">{$L('转换可能导致一定的精度损失,请谨慎进行')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="form-group row footer" ref={(c) => (this._$btns = c)}>
|
||||||
|
<div className="col-sm-7 offset-sm-3">
|
||||||
|
<button className="btn btn-primary" type="button" onClick={() => this.post()}>
|
||||||
|
{$L('确定')}
|
||||||
|
</button>
|
||||||
|
<button className="btn btn-link" type="button" onClick={() => this.hide()}>
|
||||||
|
{$L('取消')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</RbModal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
$(this._$toType).select2({
|
||||||
|
placeholder: $L('不可转换'),
|
||||||
|
templateResult: function (res) {
|
||||||
|
const $span = $('<span class="icon-append"></span>').attr('title', res.text).text(res.text)
|
||||||
|
$(`<i class="icon mdi ${(FIELD_TYPES[res.id] || [])[1]}"></i>`).appendTo($span)
|
||||||
|
return $span
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
post() {
|
||||||
|
const toType = $(this._$toType).val()
|
||||||
|
if (!toType) return RbHighbar.create($L('不可转换'))
|
||||||
|
|
||||||
|
const $btn = $(this._$btns).find('.btn').button('loading')
|
||||||
|
$.post(`/admin/entity/field-type-cast?entity=${this.props.entity}&field=${this.props.field}&toType=${toType}`, (res) => {
|
||||||
|
if (res.error_code === 0) {
|
||||||
|
location.reload()
|
||||||
|
} else {
|
||||||
|
$btn.button('reset')
|
||||||
|
RbHighbar.error(res.error_msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -181,12 +181,12 @@ class RbModalHandler extends React.Component {
|
||||||
this.state = { ...props }
|
this.state = { ...props }
|
||||||
}
|
}
|
||||||
|
|
||||||
show = (state, call) => {
|
show = (state, cb) => {
|
||||||
const callback = () => {
|
const callback = () => {
|
||||||
// eslint-disable-next-line react/no-string-refs
|
// eslint-disable-next-line react/no-string-refs
|
||||||
const dlg = this._dlg || this.refs['dlg']
|
const dlg = this._dlg || this.refs['dlg']
|
||||||
if (dlg) dlg.show()
|
if (dlg) dlg.show()
|
||||||
typeof call === 'function' && call(this)
|
typeof cb === 'function' && cb(this)
|
||||||
}
|
}
|
||||||
if (state && $.type(state) === 'object') this.setState(state, callback)
|
if (state && $.type(state) === 'object') this.setState(state, callback)
|
||||||
else callback()
|
else callback()
|
||||||
|
@ -1243,7 +1243,7 @@ const renderRbcomp = function (jsx, target, callback) {
|
||||||
target = target[0]
|
target = target[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReactDOM.render(<React.StrictMode>{jsx}</React.StrictMode>, target, call)
|
// ReactDOM.render(<React.StrictMode>{jsx}</React.StrictMode>, target, callback)
|
||||||
ReactDOM.render(jsx, target, callback)
|
ReactDOM.render(jsx, target, callback)
|
||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
|
|
|
@ -339,8 +339,9 @@ class ContentFieldWriteback extends ActionContentSpec {
|
||||||
sourceField = this._$sourceValue.val()
|
sourceField = this._$sourceValue.val()
|
||||||
if (!sourceField) return
|
if (!sourceField) return
|
||||||
} else if (mode === 'VNULL') {
|
} else if (mode === 'VNULL') {
|
||||||
const tf = this.state.targetFields.find((x) => x.name === targetField)
|
// v3.6 不校验
|
||||||
if (!tf.nullable) return RbHighbar.create($L('目标字段 %s 不能为空', `[ ${tf.label} ]`))
|
// const tf = this.state.targetFields.find((x) => x.name === targetField)
|
||||||
|
// if (!tf.nullable) return RbHighbar.create($L('目标字段 %s 不能为空', tf.label))
|
||||||
}
|
}
|
||||||
|
|
||||||
const items = this.state.items || []
|
const items = this.state.items || []
|
||||||
|
|
|
@ -92,4 +92,17 @@ class AviatorUtilsTest {
|
||||||
Assertions.assertThrows(ExpressionRuntimeException.class,
|
Assertions.assertThrows(ExpressionRuntimeException.class,
|
||||||
() -> AviatorUtils.eval("date1 + date1", env, false));
|
() -> AviatorUtils.eval("date1 + date1", env, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue