Fix 4.1.1 (#937)

* fix: theme-color

* Improve readonly state handling for select2 fields


* Update @rbv

* 4.1.1

* fix: 引用查询

* rebuild-market

* Improve role privileges display and logic

* be
This commit is contained in:
REBUILD 企业管理系统 2025-07-27 14:49:32 +08:00 committed by GitHub
parent 1727235e03
commit 14eb472100
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 110 additions and 93 deletions

2
@rbv

@ -1 +1 @@
Subproject commit 892e704988432da5d4335a1c0e4672ca5ef0ee28
Subproject commit d74f4da73de7de84db6b13934f6d417d51f894a0

View file

@ -17,7 +17,9 @@
相较于传统软件系统REBUILD 提供了绝佳的灵活性与可控性,可以完全按照企业需求进行量身打造。同时,当系统投入使用一段时间后会遇到业务变化或需求变更,通过 REBUILD 提供的高度可配置化能力,可快速完成需求变更而无需额外投入。
### REBUILD 最适合哪类用户
### REBUILD 适合哪类用户
REBUILD 适合需要灵活搭建业务系统的企业 IT 团队、缺乏专职开发资源的中小企业、特定业务领域的管理者、从传统系统迁移或升级的企业,或是开发者或技术合作伙伴。
#### 企业 IT 团队
@ -43,10 +45,10 @@
#### 开发者
- 扩展Java/Spring 二次开发,提供 OpenAPI 集成外部系统
- 部署:支持 Docker/私有云/本地
- 扩展Java/SpringBoot 二次开发,提供 OpenAPI 集成外部系统
- 部署:支持 Docker 私有云或本地部署
更多详情介绍 [https://getrebuild.com/](https://getrebuild.com/)
更多详情介绍 [https://getrebuild.com/learn/declaration](https://getrebuild.com/learn/declaration)
## V4.1 新特性
@ -62,7 +64,7 @@
8. [优化] 30+ 细节/BUG/安全性更新
9. ...
更多更新详情请参见 [更新日志](https://getrebuild.com/docs/dev/changelog?v=4.0)
更多更新详情请参见 [更新日志](https://getrebuild.com/docs/dev/changelog?v=4.1)
## 在线体验
@ -82,7 +84,7 @@ _生产环境强烈推荐使用此方式 !!!_
下载后解压(集成安装包),通过 `start-rebuild.bat``start-rebuild.sh` 启动,然后打开浏览器输入 [http://localhost:18080/](http://localhost:18080/) 开始体验。
更多详情请参见 [安装文档](https://getrebuild.com/learn/install)
或者您也可以 [使用 Docker 安装](https://getrebuild.com/learn/install-use-docker)。更多详情请参见 [安装文档](https://getrebuild.com/learn/install)
### 2. 通过源码编译

View file

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

View file

@ -76,11 +76,11 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
/**
* Rebuild Version
*/
public static final String VER = "4.1.0";
public static final String VER = "4.1.1";
/**
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
*/
public static final int BUILD = 4010006;
public static final int BUILD = 4010106;
static {
// Driver for DB

View file

@ -129,12 +129,12 @@ public class CombinedRole extends Role {
if (p instanceof ZeroPrivileges) continue;
System.out.println();
System.out.println("Combined Privileges : " + p.getIdentity());
System.out.println("M " + roleMain.getPrivileges(p.getIdentity()));
System.out.println("MAIN. " + roleMain.getPrivileges(p.getIdentity()));
for (Role ra : roleAppends) {
System.out.println("A " + ra.getPrivileges(p.getIdentity()));
System.out.println("APPE. " + ra.getPrivileges(p.getIdentity()));
}
System.out.println("--");
System.out.println("T " + getPrivileges(p.getIdentity()));
System.out.println("MERG. " + getPrivileges(p.getIdentity()));
}
System.out.println();
}
@ -190,11 +190,12 @@ public class CombinedRole extends Role {
}
// 字段权限
Map<String, Object> useFpDefinition;
Map<String, Object> aFpDefinition = ((CustomEntityPrivileges) a).getFpDefinition();
Map<String, Object> bFpDefinition = ((CustomEntityPrivileges) b).getFpDefinition();
if (aFpDefinition == null || bFpDefinition == null) useFpDefinition = null;
else useFpDefinition = aFpDefinition;
else useFpDefinition = aFpDefinition; // 无法比较字段权限高低因此随便选一个
String definition = StringUtils.join(defs.iterator(), ",");
return new CustomEntityPrivileges(((EntityPrivileges) a).getEntity(), definition, useCustomFilters, useFpDefinition);

View file

@ -106,7 +106,7 @@ public class TransactionManual {
}
// 非事物中
log.warn("Transaction synchronization is not active, start directly : {}", c);
log.debug("Transaction synchronization is not active, start directly : {}", c);
new Thread(c).start();
}
}

View file

@ -100,6 +100,10 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
// v4.1 theme
String theme = (String) ServletUtils.getSessionAttribute(request, LoginController.SK_USER_THEME);
if (theme != null) {
if (requestEntry.getRequestUri().contains("/admin/")
|| requestEntry.getRequestUri().contains("/admin-")) {
theme = "default";
}
theme = THEMES_COLORS.get(theme);
if (theme != null) request.setAttribute(WebConstants.THEME_COLOR, theme);
}

View file

@ -53,7 +53,7 @@ public class UseThemeController extends BaseController {
};
public static final Map<String, String> THEMES_COLORS = new HashMap<>();
static {
THEMES_COLORS.put("dark", "#2d333b");
THEMES_COLORS.put("dark", "#1c2128");
THEMES_COLORS.put("red", "#f7615e");
THEMES_COLORS.put("green", "#16a88f");
THEMES_COLORS.put("blue", "#4873c0");

View file

@ -85,8 +85,17 @@ public class ReferenceSearchController extends EntityController {
String varRecord = getParameter(request, "varRecord");
ProtocolFilterParser fp = new ProtocolFilterParser();
if (StringUtils.isNotBlank(varRecord)) {
varRecord = CodecUtils.urlDecode(varRecord);
if (JSONUtils.wellFormat(varRecord)) fp.setVarRecord(JSON.parseObject(varRecord));
if (JSONUtils.wellFormat(varRecord)) {
fp.setVarRecord(JSON.parseObject(varRecord));
} else {
// 兼容处理
try {
varRecord = CodecUtils.urlDecode(varRecord);
} catch (Exception ignored) {}
if (JSONUtils.wellFormat(varRecord)) {
fp.setVarRecord(JSON.parseObject(varRecord));
}
}
}
String protocolFilter = fp.parseRef(referenceField.getName() + "." + entity.getName(), cascadingValue);

View file

@ -58,10 +58,4 @@ public class UCenterController extends BaseController {
res.put("canBind", UserHelper.isSuperAdmin(getRequestUser(request)));
return RespBody.ok(res);
}
@GetMapping("market-query")
public RespBody marketQuery41() {
JSONObject res = License.siteApi("api/ucenter/market-query");
return RespBody.ok(res);
}
}

View file

@ -3508,5 +3508,6 @@
"确定要清空配置吗?":"确定要清空配置吗?",
"启用列表页单字段编辑":"启用列表页单字段编辑",
"无 (不限)":"无 (不限)",
"引用记录不存在":"引用记录不存在"
"引用记录不存在":"引用记录不存在",
"首次使用强烈建议阅读5分钟快速上手":"首次使用强烈建议阅读5分钟快速上手"
}

View file

@ -214,7 +214,7 @@
<td class="name">
<a th:data-entity="${entity[1]}" th:data-name="${entity[0]}" th:title="${bundle.L('批量选择')}">[[${entity[2]}]]</a>
<span>
<a th:data-entity="${entity[1]}" th:title="${bundle.L('字段权限')}"><i class="mdi mdi-form-textbox-lock mdi-sort-variant-lock--"></i></a>
<a th:data-entity="${entity[1]}" th:title="${bundle.L('字段权限')}"><i class="mdi mdi-form-textbox-lock"></i></a>
</span>
</td>
<td><i data-action="C" class="priv R0"></i><a></a></td>

View file

@ -80,25 +80,6 @@
border-radius: 2px;
padding: 10px;
}
.market-banner {
width: 100%;
margin: 0 auto;
border-radius: 6px;
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.04);
background: linear-gradient(79deg, #1366ec 0%, #8e13ec 60%, #ec138a 100%);
margin-bottom: 15px;
display: none;
}
.market-banner > a {
display: block;
padding: 1.5rem;
font-size: 1rem;
color: #fff !important;
text-align: center;
}
.market-banner.show {
display: block;
}
</style>
</head>
<body>
@ -352,12 +333,11 @@
</div>
</div>
<div class="col-lg-3 col-12 col-right-compact">
<div class="market-banner"></div>
<div class="card">
<div class="card J_systsms">
<div class="card-header card-header-divider">[[${bundle.L('版本与授权')}]]</div>
<div class="card-body">
<p class="mb-1">[[${bundle.L('系统版本')}]]<a class="link ml-1" target="_blank" th:href="|https://getrebuild.com/download?v=${Version}|">[[${Version}]]</a></p>
<p class="mb-1">[[${bundle.L('授权类型')}]]<a class="link ml-1" target="_blank" th:href="|https://getrebuild.com/authority?sn=${SN}|">[[${LicenseType}]]</a></p>
<p class="mb-1">[[${bundle.L('授权类型')}]]<a class="link ml-1" target="_blank" th:href="|https://getrebuild.com/authority?sn=${SN}|" th:data-sn="${SN}">[[${LicenseType}]]</a></p>
<p th:if="${VN != null}" class="mb-2">
[[${bundle.L('支持服务码')}]]<span class="ml-1">[[${VN}]]</span>
<a th:if="${VNExpires}" class="badge badge-danger up-2 badge-pill" th:href="'https://getrebuild.com/buy-license?type=10fw&vn=' + ${VN}" target="_blank">

View file

@ -4286,7 +4286,7 @@ h3.modal-title > .rbv {
}
.use-theme ul li a.theme-dark {
background-color: #2d333b !important;
background-color: #1c2128 !important;
}
.use-theme ul li a.theme-blue {
@ -4648,10 +4648,6 @@ pre.unstyle {
font-size: 1.2rem;
}
.bg-market {
background-image: linear-gradient(45deg, rgb(0, 84, 70), rgb(57, 0, 63), rgb(89, 0, 0)), linear-gradient(45deg, rgb(0, 122, 101), rgb(127, 14, 127), rgb(255, 137, 131));
}
#gritter-notice-wrapper {
position: fixed;
right: 20px;

View file

@ -50,13 +50,16 @@ $(document).ready(() => {
})
}
})
UCenter.market((res) => {
if (res.banner) {
$('.market-banner').html(res.banner).addClass('show')
}
$getScript('https://getrebuild.com/js/_market/rebuild-market.min.js?v=1.0', () => {
$('<link/>', {
rel: 'stylesheet',
type: 'text/css',
href: 'https://getrebuild.com/js/_market/rebuild-market.min.css?v=1.0',
}).appendTo('head')
typeof window.evalMarket === 'function' && window.evalMarket($('a[data-sn]').data('sn'))
})
// v34
// v3.4
const $mm = $('.J_maintenanceMode')
$.get('/admin/systems/maintenance-mode', (res) => {
const _data = res.data

View file

@ -283,8 +283,10 @@ const loadPrivileges = function () {
$tr.find(`a[data-action="${k}"]`).addClass('active')
}
} else if (k === 'FP') {
fieldpSettings[entity] = defs[k]
$name.parent().find('span>a').addClass('active')
if (defs[k] && Object.keys(defs[k]).length) {
fieldpSettings[entity] = defs[k]
$name.parent().find('span>a').addClass('active')
}
} else {
$tr.find(`i.priv[data-action="${k}"]`).removeClass('R0 R1 R2 R3 R4').addClass(`R${defs[k]}`)
}

View file

@ -1182,8 +1182,12 @@ class RbFormElement extends React.Component {
// 部分字段有效且如字段属性为只读即使填写值也无效
setReadonly(readonly) {
this.setState({ readonly: readonly === true }, () => {
// fix 4.0.6 只读变为非只读富附件需初始化
this.onEditModeChanged(readonly === true, true)
// fix 4.0.6 只读变为非只读富组件仍需初始化
try {
this.onEditModeChanged(readonly === true, true)
} catch (err) {
console.error(err)
}
})
}
// TIP 仅表单有效
@ -1243,9 +1247,12 @@ class RbFormText extends RbFormElement {
)
// fix:4.1-b5 禁用时不触发
$(this._fieldValue).on('click', (e) => {
$(this._fieldValue).on('click', function (e) {
const $t = e.target || {}
if ($t.disabled || $t.readOnly) $stopEvent(e, true)
if ($t.disabled || $t.readOnly) {
$stopEvent(e, true)
return false
}
})
}
}
@ -2118,11 +2125,13 @@ class RbFormPickList extends RbFormElement {
onEditModeChanged(destroy, fromReadonly41) {
if (destroy) {
if (fromReadonly41) {
this.__select2 && $(this._fieldValue).attr('disabled', true)
if (fromReadonly41 && this.__select2) {
$(this._fieldValue).attr('disabled', true)
} else {
super.onEditModeChanged(destroy)
}
} else if (fromReadonly41 && this.__select2) {
$(this._fieldValue).attr('disabled', false)
} else {
if (this._isShowRadio39) {
// Nothings
@ -2230,6 +2239,7 @@ class RbFormReference extends RbFormElement {
// fix: 4.0.4 #IC63LN 记录转换时无需回填
_disableAutoFillin() {
if (this.props._disableAutoFillin) return true
try {
// Form
let $parent = this.props.$$$parent
@ -2245,11 +2255,13 @@ class RbFormReference extends RbFormElement {
onEditModeChanged(destroy, fromReadonly41) {
if (destroy) {
if (fromReadonly41) {
this.__select2 && $(this._fieldValue).attr('disabled', true)
if (fromReadonly41 && this.__select2) {
$(this._fieldValue).attr('disabled', true)
} else {
super.onEditModeChanged(destroy)
}
} else if (fromReadonly41 && this.__select2) {
$(this._fieldValue).attr('disabled', false)
} else {
this.__select2 = $initReferenceSelect2(this._fieldValue, {
name: this.props.field,
@ -2269,7 +2281,7 @@ class RbFormReference extends RbFormElement {
}
}
varRecord['metadata.entity'] = $$$parent.props.entity
query.varRecord = $encode(JSON.stringify(varRecord))
query.varRecord = JSON.stringify(varRecord)
}
}
@ -2563,8 +2575,8 @@ class RbFormN2NReference extends RbFormReference {
super.handleChange({ target: { value: val } }, checkValue)
}
onEditModeChanged(destroy) {
super.onEditModeChanged(destroy)
onEditModeChanged(destroy, fromReadonly41) {
super.onEditModeChanged(destroy, fromReadonly41)
if (!destroy && this.__select2) {
this.__select2.on('select2:select', (e) => __addRecentlyUse(e.params.data.id))
@ -2622,10 +2634,12 @@ class RbFormAnyReference extends RbFormReference {
}
renderElement() {
const _readonly41 = this.state.readonly
return (
<div className="row">
<div className="col-4 pr-0">
<select className="form-control form-control-sm" ref={(c) => (this._$entity = c)}>
<select className="form-control form-control-sm" ref={(c) => (this._$entity = c)} disabled={_readonly41}>
{(this.state.entities || []).map((item) => {
return (
<option key={item.name} value={item.name}>
@ -2637,7 +2651,7 @@ class RbFormAnyReference extends RbFormReference {
</div>
<div className="col-8 pl-2">
<div className="input-group has-append">
<select className="form-control form-control-sm" ref={(c) => (this._fieldValue = c)} />
<select className="form-control form-control-sm" ref={(c) => (this._fieldValue = c)} disabled={_readonly41} />
{!this.state.readonly && (
<div className="input-group-append">
<button className="btn btn-secondary" type="button" onClick={() => this.showSearcher()}>
@ -2651,9 +2665,15 @@ class RbFormAnyReference extends RbFormReference {
)
}
onEditModeChanged(destroy) {
onEditModeChanged(destroy, fromReadonly41) {
if (destroy) {
super.onEditModeChanged(destroy)
if (fromReadonly41 && this.__select2) {
$([this._$entity, this._fieldValue]).attr('disabled', true)
} else {
super.onEditModeChanged(destroy)
}
} else if (fromReadonly41 && this.__select2) {
$([this._$entity, this._fieldValue]).attr('disabled', false)
} else {
const iv = this.state.value
$.get('/commons/metadata/entities?detail=true', (res) => {
@ -2723,10 +2743,7 @@ class RbFormAnyReference extends RbFormReference {
})
})
if (this.state.readonly) {
$(this._$entity).attr('disabled', true)
$(this._fieldValue).attr('disabled', true)
}
this.state.readonly && $([this._$entity, this._fieldValue]).attr('disabled', true)
}
}
@ -2735,6 +2752,7 @@ class RbFormAnyReference extends RbFormReference {
if (this.__select2Entity) {
this.__select2Entity.select2('destroy')
this.__select2Entity = null
}
}
@ -2786,8 +2804,8 @@ class RbFormClassification extends RbFormElement {
onEditModeChanged(destroy, fromReadonly41) {
if (destroy) {
if (fromReadonly41) {
this.__select2 && $(this._fieldValue).attr('disabled', true)
if (fromReadonly41 && this.__select2) {
$(this._fieldValue).attr('disabled', true)
} else {
super.onEditModeChanged(destroy)
this.__cached = null
@ -2796,6 +2814,8 @@ class RbFormClassification extends RbFormElement {
this.__selector = null
}
}
} else if (fromReadonly41 && this.__select2) {
$(this._fieldValue).attr('disabled', false)
} else {
this.__select2 = $initReferenceSelect2(this._fieldValue, {
name: this.props.field,
@ -2924,11 +2944,13 @@ class RbFormMultiSelect extends RbFormElement {
onEditModeChanged(destroy, fromReadonly41) {
if (this._isShowSelect41) {
if (destroy) {
if (fromReadonly41) {
this.__select2 && $(this._fieldValue).attr('disabled', true)
if (fromReadonly41 && this.__select2) {
$(this._fieldValue).attr('disabled', true)
} else {
super.onEditModeChanged(destroy)
}
} else if (fromReadonly41 && this.__select2) {
$(this._fieldValue).attr('disabled', false)
} else {
this.__select2 = $(this._fieldValue).select2({
placeholder: $L('选择%s', this.props.label),
@ -3384,12 +3406,14 @@ class RbFormTag extends RbFormElement {
onEditModeChanged(destroy, fromReadonly41) {
if (destroy) {
if (fromReadonly41) {
this.__select2 && $(this._fieldValue).attr('disabled', true)
if (fromReadonly41 && this.__select2) {
$(this._fieldValue).attr('disabled', true)
} else {
super.onEditModeChanged(destroy)
this._initOptions()
}
} else if (fromReadonly41 && this.__select2) {
$(this._fieldValue).attr('disabled', false)
} else {
this.__select2 = $(this._fieldValue).select2({
placeholder: this.props.readonlyw > 0 ? this._placeholderw : $L('输入%s', this.props.label),

View file

@ -1221,6 +1221,11 @@ var $getScript = function (url, callback) {
success: callback,
dataType: 'script',
cache: true,
complete: function (xhr) {
if (!(xhr.status === 200 || xhr.status === 0)) {
console.error('Failed to load script:', url, xhr)
}
},
})
}

View file

@ -16,12 +16,6 @@ const UCenter = {
bind: function () {
renderRbcomp(<UCenterBind />)
},
market: function (c) {
$.get('/settings/ucenter/market-query', (res) => {
typeof c === 'function' && res.data && c(res.data)
})
},
}
class UCenterBind extends RbFormHandler {

View file

@ -11,14 +11,16 @@
<title>REBUILD</title>
<style>
.zmdi.err400,
.zmdi.err404 {
color: #4285f4 !important;
}
.zmdi.err401,
.zmdi.err403,
.zmdi.err404 {
.zmdi.err600 {
color: #fbbc05 !important;
}
.zmdi.err600::before,
.zmdi.err602::before {
color: #ea4335;
font-size: 6rem;
content: '\f17a';
}