Fix 3.9.1 (#855)

* fix: 表单回填ID无效

* feat: check-state st

* fix: tel/mailto pdf for dd/wx

* fix: 刷新导致标题变化

* 3.9.1

* be:变量匹配 {}

* fix: AnyRecordSelector

* style:_backup

* enh: filter date EQ

* OSC

* Update rb-forms.js
This commit is contained in:
REBUILD 企业管理系统 2025-01-09 13:22:12 +08:00 committed by GitHub
parent 8c9e831d26
commit 0a9fbcdde1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 157 additions and 58 deletions

View file

@ -151,5 +151,6 @@ module.exports = {
$clearSelection: true,
DlgTransform: true,
$select2OpenTemplateResult: true,
$env: true,
},
}

2
@rbv

@ -1 +1 @@
Subproject commit 55386d909e5ed6afe1fe7a0793c460451b491296
Subproject commit 9a6c91abe293077d0ef39b7b22834ba9bd22add8

View file

@ -10,7 +10,7 @@
</parent>
<groupId>com.rebuild</groupId>
<artifactId>rebuild</artifactId>
<version>3.9.0</version>
<version>3.9.1</version>
<name>rebuild</name>
<description>Building your business-systems freely!</description>
<url>https://getrebuild.com/</url>

View file

@ -74,11 +74,11 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
/**
* Rebuild Version
*/
public static final String VER = "3.9.0";
public static final String VER = "3.9.1";
/**
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
*/
public static final int BUILD = 3090005;
public static final int BUILD = 3090106;
static {
// Driver for DB

View file

@ -24,10 +24,7 @@ import com.rebuild.core.configuration.ConfigManager;
import com.rebuild.core.metadata.MetadataHelper;
import com.rebuild.core.metadata.easymeta.DisplayType;
import com.rebuild.core.metadata.easymeta.EasyField;
import com.rebuild.core.metadata.easymeta.EasyFile;
import com.rebuild.core.metadata.easymeta.EasyID;
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
import com.rebuild.core.metadata.easymeta.EasyN2NReference;
import com.rebuild.core.metadata.easymeta.MixValue;
import com.rebuild.core.metadata.impl.EasyFieldConfigProps;
import com.rebuild.core.support.general.CalcFormulaSupport;
@ -313,17 +310,20 @@ public class AutoFillinManager implements ConfigManager {
// 转换成前端可接受的值
if (sourceEasy.getDisplayType() == targetEasy.getDisplayType()
&& sourceEasy.getDisplayType() == DisplayType.MULTISELECT) {
DisplayType sourceEasyType = sourceEasy.getDisplayType();
DisplayType targetEasyType = targetEasy.getDisplayType();
if (sourceEasyType == targetEasyType && sourceEasyType == DisplayType.MULTISELECT) {
return newValue; // Long
}
if (sourceEasy instanceof EasyID && targetEasy instanceof EasyN2NReference) {
if (sourceEasyType == DisplayType.ID
&& (targetEasyType == DisplayType.REFERENCE || targetEasyType == DisplayType.N2NREFERENCE || targetEasyType == DisplayType.ANYREFERENCE)) {
newValue = FieldValueHelper.wrapFieldValue(newValue, targetEasy);
} else if (targetEasy instanceof EasyN2NReference) {
} else if (targetEasyType == DisplayType.N2NREFERENCE) {
newValue = targetEasy.wrapValue(newValue);
} else if (sourceEasy instanceof MixValue) {
if (!(newValue instanceof String) || sourceEasy instanceof EasyFile) {
if (!(newValue instanceof String) || sourceEasyType == DisplayType.FILE) {
newValue = sourceEasy.wrapValue(newValue);
}
}

View file

@ -524,6 +524,29 @@ public class AdvFilterParser extends SetUser {
op = ParseHelper.BW;
}
// v3.9.1 日期查询年月日 0000-00-00
if (ParseHelper.EQ.equals(op) && (value.startsWith("0000-") || value.contains("-00-") || value.endsWith("-00"))) {
final String[] ymd = value.split("-");
String format;
if (ymd[0].equals("0000") && ymd[1].equals("00")) { // 0000-00-XX
format = "%d";
value = ymd[2];
} else if (ymd[0].equals("0000") && ymd[2].equals("00")) { // 0000-XX-00
format = "%m";
value = ymd[1];
} else if (ymd[1].equals("00") && ymd[2].equals("00")) { // XXXX-00-00
format = "%Y";
value = ymd[0];
} else if (ymd[1].equals("00")) { // XXXX-00-XX
format = "%Y%d";
value = ymd[0] + ymd[2];
} else { // 0000-XX-XX
format = "%m%d";
value = ymd[1] + ymd[2];
}
field = String.format("DATE_FORMAT(%s, '%s')", field, format);
}
} else if (dt == DisplayType.TIME) {
// 前端输入 HH:mm
if (value != null && value.length() == 5) {
@ -662,13 +685,13 @@ public class AdvFilterParser extends SetUser {
.append(" )");
}
// 当前审批人
if (VF_ACU.equals(field)) {
return String.format(
"(exists (select recordId from RobotApprovalStep where ^%s = recordId and state = 1 and isCanceled = 'F' and %s) and approvalState = 2)",
specRootEntity.getPrimaryField().getName(), sb.toString().replace(VF_ACU, "approver"));
} else {
return sb.toString();
}
return sb.toString();
}
/**

View file

@ -75,7 +75,7 @@ public final class License {
if ((error = auth.getString("error")) != null) {
auth = JSONUtils.toJSONObject(
new String[] { "sn", "authType", "authObject", "authExpires" },
new String[] { SN(), "开源社区版", "GitHub", "" });
new String[] { SN(), "开源社区版", "OSC", "" });
}
if ("BLOCKED".equals(error)) System.exit(110);
return auth;

View file

@ -41,7 +41,7 @@ public class ContentWithFieldVars {
/**
* 通过 `{}` 包裹的变量或字段
*/
public static final Pattern PATT_VAR = Pattern.compile("\\{([0-9a-zA-Z._$]+)}");
public static final Pattern PATT_VAR = Pattern.compile("\\{([0-9a-zA-Z._$]{4,})}");
/**
* 替换文本中的字段变量
@ -82,9 +82,7 @@ public class ContentWithFieldVars {
* @return
*/
public static String replaceWithRecord(String content, Record record) {
if (StringUtils.isBlank(content) || record == null) {
return content;
}
if (StringUtils.isBlank(content) || record == null) return content;
// 主键占位符
content = content.replace("{ID}",
@ -147,25 +145,19 @@ public class ContentWithFieldVars {
}
/**
* 提取内容中的变量 {xxx}
* 提取内容中的变量 {xxxx}
*
* @param content
* @return
*/
public static Set<String> matchsVars(String content) {
if (StringUtils.isBlank(content)) {
return Collections.emptySet();
}
if (StringUtils.isBlank(content)) return Collections.emptySet();
Set<String> vars = new HashSet<>();
Matcher m = PATT_VAR.matcher(content);
while (m.find()) {
String varName = m.group(1);
if (StringUtils.isBlank(varName)) {
log.warn("Blank `\\{\\}` found in `{}`", content);
} else {
vars.add(varName);
}
vars.add(varName);
}
return vars;
}

View file

@ -60,6 +60,7 @@ public class NotificationController extends BaseController {
JSON mm = buildMM();
if (mm != null) state.put("mm", mm);
state.put("st", CalendarUtils.now());
// v3.8 手机版利用通知检查做最近活跃信息存储
String h5referer = getParameter(request, "h5referer");

View file

@ -3345,5 +3345,6 @@
"未备份":"未备份",
"选择要备份哪些数据":"选择要备份哪些数据",
"分配与共享":"分配与共享",
"最多显示最近 500 条记录":"最多显示最近 500 条记录"
"最多显示最近 500 条记录":"最多显示最近 500 条记录",
"失败时的提示内容。内容支持字段变量,如 `{createdOn}` (其中 createdOn 为源实体的字段内部标识)":"失败时的提示内容。内容支持字段变量,如 `{createdOn}` (其中 createdOn 为源实体的字段内部标识)"
}

View file

@ -33,3 +33,6 @@
<script th:src="@{/assets/js/rebuild-tour.js}" type="text/babel"></script>
</th:block>
</th:block>
<!--
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
-->

View file

@ -542,11 +542,11 @@
/>
<div class="dropdown-menu common-texts J_advPattern">
<h5>[[${bundle.L('常用')}]]</h5>
<a class="badge" data-patt="^([0-9A-Z]{15}|[0-9A-Z]{17}|[0-9A-Z]{18}|[0-9A-Z]{20})$">[[${bundle.L('税号')}]]</a>
<a class="badge" data-patt="^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$">[[${bundle.L('身份证')}]]</a>
<a class="badge" data-patt="^1[3456789]\d{9}$">[[${bundle.L('手机号')}]]</a>
<a class="badge" data-patt="^[1-9][0-9]{4,10}$">[[${bundle.L('QQ号')}]]</a>
<a class="badge" data-patt="^[1-9][0-9]{5}$">[[${bundle.L('邮编')}]]</a>
<a class="badge" data-patt="^([0-9A-Z]{15}|[0-9A-Z]{17}|[0-9A-Z]{18}|[0-9A-Z]{20})$">[[${bundle.L('税号')}]]</a>
<a class="badge" data-patt="^[\u4e00-\u9fa5]{0,}$">[[${bundle.L('仅中文')}]]</a>
<a class="badge" data-patt="^[A-Za-z]{0,}$">[[${bundle.L('仅英文')}]]</a>
<a class="badge" data-patt="^[0-9]{0,}$">[[${bundle.L('仅数字')}]]</a>

View file

@ -74,6 +74,12 @@
button.J_MobileAppPath + button.J_MobileAppPath-del {
margin-left: 70px;
}
.backup-box {
background-color: #eee;
margin-bottom: 10px;
border-radius: 2px;
padding: 10px;
}
</style>
</head>
<body>

View file

@ -339,6 +339,7 @@ class DlgBackup extends RbAlert {
state = { ...this.props }
renderContent() {
const _backup = this.state.db || this.state.file
return (
<form className="rbalert-form-sm">
<div className="form-group mb-0">
@ -355,9 +356,26 @@ class DlgBackup extends RbAlert {
</div>
</div>
<div className="form-group mb-1">
<div className="text-warning mb-1" ref={(c) => (this._$tips = c)}>
<i className="mdi-alert-outline mdi" /> {$L('请勿在业务高峰时段执行备份')}
</div>
{_backup ? (
<div className="backup-box">
<table>
<tbody>
<tr>
<td className="text-ellipsis pr-1">{$L('数据库')}</td>
<td>{this.state.db ? <code>{this.state.db}</code> : <span className="text-muted">{$L('未备份')}</span>}</td>
</tr>
<tr>
<td className="text-ellipsis pr-1">{$L('数据目录文件')}</td>
<td>{this.state.file ? <code>{this.state.file}</code> : <span className="text-muted">{$L('未备份')}</span>}</td>
</tr>
</tbody>
</table>
</div>
) : (
<div className="text-warning mb-1" ref={(c) => (this._$tips = c)}>
<i className="mdi-alert-outline mdi" /> {$L('请勿在业务高峰时段执行备份')}
</div>
)}
<button type="button" className="btn btn-space btn-primary" onClick={this.confirm} ref={(c) => (this._$btn = c)} data-spinner>
{$L('开始备份')}
</button>
@ -367,19 +385,15 @@ class DlgBackup extends RbAlert {
}
confirm = () => {
let type = ($(this._$bkType).find('input:eq(0)').prop('checked') ? 1 : 0) + ($(this._$bkType).find('input:eq(1)').prop('checked') ? 2 : 0)
const type = ($(this._$bkType).find('input:eq(0)').prop('checked') ? 1 : 0) + ($(this._$bkType).find('input:eq(1)').prop('checked') ? 2 : 0)
if (type === 0) return
this.disabled(true, true)
const $btn = $(this._$btn).button('loading')
$.post(`systems/backup?type=${type}`, (res) => {
if (res.error_code === 0) {
const data = res.data || {}
const tips = [$L('数据库'), ' <code>', data.db || $L('未备份'), '</code><br>', $L('数据目录文件'), ' <code>', data.file || $L('未备份'), '</code>']
$(this._$tips).html(tips.join(''))
} else {
RbHighbar.error(res.error_msg)
}
if (res.error_code === 0) this.setState({ ...res.data })
else RbHighbar.error(res.error_msg)
this.disabled(false, false)
$btn.button('reset')
})

View file

@ -1391,7 +1391,7 @@ class DataList extends BaseChart {
}
const extconfig = this.state.config.extconfig
extconfig && this.setState({ title: extconfig.title || $L('数据列表') })
// extconfig && this.setState({ title: extconfig.title || $L('数据列表') })
const listFields = data.fields
const listData = data.data

View file

@ -210,6 +210,13 @@ class RbPreview extends React.Component {
previewUrl = `${rb.baseUrl}/filex/` + url.split('/filex/')[1]
}
}
// fix:3.9.1 PC钉钉预览PDF
if ($env.isDingTalk()) {
window.open(previewUrl)
that.hide()
return
}
}
that.setState({ previewUrl: previewUrl, errorMsg: null })
}

View file

@ -1739,9 +1739,13 @@ CellRenders.addRender('EMAIL', (v, s, k) => {
return (
<td key={k}>
<div style={s} title={v}>
<a href={`mailto:${v}`} className="column-url" onClick={(e) => $stopEvent(e)}>
{v}
</a>
{$env.isDingTalk() ? (
<a>{v}</a>
) : (
<a href={`mailto:${v}`} className="column-url" onClick={(e) => $stopEvent(e)}>
{v}
</a>
)}
</div>
</td>
)
@ -1750,9 +1754,13 @@ CellRenders.addRender('PHONE', (v, s, k) => {
return (
<td key={k}>
<div style={s} title={v}>
<a href={`tel:${v}`} className="column-url" onClick={(e) => $stopEvent(e)}>
{v}
</a>
{$env.isDingTalk() || $env.isWxWork() ? (
<a>{v}</a>
) : (
<a href={`tel:${v}`} className="column-url" onClick={(e) => $stopEvent(e)}>
{v}
</a>
)}
</div>
</td>
)

View file

@ -712,7 +712,7 @@ class RbForm extends React.Component {
const fieldComp = _refs[key]
let v = fieldComp.getValue()
if (v && typeof v === 'object') v = v.id
if (v && typeof v === 'object') v = v.id || v // array
if (v) data[fieldComp.props.field] = v
}
return data
@ -1232,9 +1232,13 @@ class RbFormEMail extends RbFormText {
return (
<div className="form-control-plaintext">
<a title={$L('发送邮件')} href={`mailto:${this.state.value}`} className="link">
{this.state.value}
</a>
{$env.isDingTalk() ? (
<a>{this.state.value}</a>
) : (
<a title={$L('发送邮件')} href={`mailto:${this.state.value}`} className="link">
{this.state.value}
</a>
)}
</div>
)
}
@ -1252,9 +1256,13 @@ class RbFormPhone extends RbFormText {
return (
<div className="form-control-plaintext">
<a title={$L('拨打电话')} href={`tel:${this.state.value}`} className="link">
{this.state.value}
</a>
{$env.isDingTalk() || $env.isWxWork() ? (
<a>{this.state.value}</a>
) : (
<a title={$L('拨打电话')} href={`tel:${this.state.value}`} className="link">
{this.state.value}
</a>
)}
</div>
)
}

View file

@ -990,8 +990,10 @@ class AnyRecordSelector extends RecordSelector {
const iv = this.props.initValue
if (iv) {
$(this._$entity).val(iv.entity).trigger('change')
const option = new Option(iv.text, iv.id, true, true)
$(this._$select).append(option)
setTimeout(() => {
const o = new Option(iv.text, iv.id, true, true)
$(this._$select).append(o)
}, 200)
}
})
})

View file

@ -209,6 +209,8 @@ $(window).on('load', () => {
if (($(this).attr('href') || '').indexOf('getrebuild.com') > -1) $(this).removeAttr('href')
})
}
// vConsole
if (window.VConsole) new window.VConsole()
})
// 取消管理中心访问
@ -387,6 +389,7 @@ var _checkMessage = function () {
}
_showStateMM(res.data.mm)
_showStateST(res.data.st)
setTimeout(_checkMessage, rb.env === 'dev' ? 9000 : 2000)
})
}
@ -446,7 +449,6 @@ var _showNotification = function (state) {
}
var _showStateMM = function (mm) {
if ($.cookie('mm_gritter_cancel')) return
if (mm) {
var $mm = $('#mm_gritter')
if ($mm[0]) {
@ -471,6 +473,25 @@ var _showStateMM = function (mm) {
RbGritter.remove('mm_gritter')
}
}
var _showStateST = function (st) {
if ($.cookie('st_gritter_cancel')) return
st = Math.abs(moment().diff(st, 'seconds'))
if (st > 60) {
if (!$('#gritter-item-st_gritter')[0]) {
RbGritter.create($L('本地计算机与服务器时间存在加大差异建议立即校正'), {
timeout: 60 * 1000,
type: 'danger',
icon: 'mdi-clock-remove-outline',
onCancel: function () {
$.cookie('st_gritter_cancel', st, { expires: 1 })
},
id: 'st_gritter',
})
}
} else {
RbGritter.remove('st_gritter')
}
}
// 全局搜索
var _initGlobalSearch = function () {
$('.global-search2>a').on('click', function () {
@ -1336,3 +1357,15 @@ function $select2OpenTemplateResult(res) {
}
return $span
}
// 环境 @see LoginChannel.java
var $env = {
// 钉钉
isDingTalk: function () {
return navigator.userAgent.match(/(DINGTALK)/i) || true
},
// 企微
isWxWork: function () {
return navigator.userAgent.match(/(WXWORK)/i)
},
}