Form area and break 52 (#554)

* form line collapsed and breaked

* better AdvControl

* view approval form

* view form readonly

* tag init
This commit is contained in:
RB 2022-12-12 17:19:53 +08:00 committed by GitHub
parent c95de134c6
commit 7e53852ea9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 232 additions and 96 deletions

View file

@ -112,6 +112,7 @@ public class FormsBuilder extends FormsManager {
final Entity hasMainEntity = entityMeta.getMainEntity(); final Entity hasMainEntity = entityMeta.getMainEntity();
// 审批流程状态 // 审批流程状态
ApprovalState approvalState; ApprovalState approvalState;
String readonlyMessage = null;
// 判断表单权限 // 判断表单权限
@ -123,11 +124,10 @@ public class FormsBuilder extends FormsManager {
approvalState = EntityHelper.isUnsavedId(mainid) ? null : getHadApproval(hasMainEntity, mainid); approvalState = EntityHelper.isUnsavedId(mainid) ? null : getHadApproval(hasMainEntity, mainid);
if ((approvalState == ApprovalState.PROCESSING || approvalState == ApprovalState.APPROVED)) { if ((approvalState == ApprovalState.PROCESSING || approvalState == ApprovalState.APPROVED)) {
return formatModelError(approvalState == ApprovalState.APPROVED readonlyMessage = approvalState == ApprovalState.APPROVED
? Language.L("主记录已完成审批,不能添加明细") ? Language.L("主记录已完成审批,不能添加明细")
: Language.L("主记录正在审批中,不能添加明细")); : Language.L("主记录正在审批中,不能添加明细");
} }
// 明细无需审批 // 明细无需审批
approvalState = null; approvalState = null;
@ -161,9 +161,9 @@ public class FormsBuilder extends FormsManager {
if (approvalState != null) { if (approvalState != null) {
String recordType = hasMainEntity == null ? Language.L("记录") : Language.L("主记录"); String recordType = hasMainEntity == null ? Language.L("记录") : Language.L("主记录");
if (approvalState == ApprovalState.APPROVED) { if (approvalState == ApprovalState.APPROVED) {
return formatModelError(Language.L("%s已完成审批禁止编辑", recordType)); readonlyMessage = Language.L("%s已完成审批禁止编辑", recordType);
} else if (approvalState == ApprovalState.PROCESSING) { } else if (approvalState == ApprovalState.PROCESSING) {
return formatModelError(Language.L("%s正在审批中禁止编辑", recordType)); readonlyMessage = Language.L("%s正在审批中禁止编辑", recordType);
} }
} }
} }
@ -187,7 +187,7 @@ public class FormsBuilder extends FormsManager {
Set<String> roAutosWithout = record == null ? null : Collections.emptySet(); Set<String> roAutosWithout = record == null ? null : Collections.emptySet();
for (Object o : elements) { for (Object o : elements) {
JSONObject field = (JSONObject) o; JSONObject field = (JSONObject) o;
if (roAutos.contains(field.getString("field"))) { if (roAutos.contains(field.getString("field")) || readonlyMessage != null) {
field.put("readonly", true); field.put("readonly", true);
// 前端可收集值 // 前端可收集值
@ -213,8 +213,7 @@ public class FormsBuilder extends FormsManager {
// v3.1 // v3.1
if (!entityMeta.getExtraAttrs().getBooleanValue(EasyEntityConfigProps.NOT_COEDITING)) { if (!entityMeta.getExtraAttrs().getBooleanValue(EasyEntityConfigProps.NOT_COEDITING)) {
model.set("detailMeta", EasyMetaFactory.toJSON(entityMeta.getDetailEntity())); model.set("detailMeta", EasyMetaFactory.toJSON(entityMeta.getDetailEntity()));
model.set("detailsNotEmpty", model.set("detailsNotEmpty", entityMeta.getExtraAttrs().getBooleanValue(EasyEntityConfigProps.DETAILS_NOTEMPTY));
entityMeta.getExtraAttrs().getBooleanValue(EasyEntityConfigProps.DETAILS_NOTEMPTY));
} }
} }
@ -222,9 +221,8 @@ public class FormsBuilder extends FormsManager {
model.set("lastModified", recordData.getDate(EntityHelper.ModifiedOn).getTime()); model.set("lastModified", recordData.getDate(EntityHelper.ModifiedOn).getTime());
} }
if (approvalState != null) { if (approvalState != null) model.set("hadApproval", approvalState.getState());
model.set("hadApproval", approvalState.getState()); if (readonlyMessage != null) model.set("readonlyMessage", readonlyMessage);
}
model.set("id", null); // Clean form's ID of config model.set("id", null); // Clean form's ID of config
return model.toJSON(); return model.toJSON();

View file

@ -41,6 +41,7 @@ public enum DisplayType {
N2NREFERENCE(EasyN2NReference.class, "多引用", FieldType.REFERENCE_LIST, -1, null), N2NREFERENCE(EasyN2NReference.class, "多引用", FieldType.REFERENCE_LIST, -1, null),
LOCATION(EasyLocation.class, "位置", FieldType.STRING, 100, null), LOCATION(EasyLocation.class, "位置", FieldType.STRING, 100, null),
SIGN(EasySign.class, "签名", FieldType.TEXT, 32767, null, false, true), SIGN(EasySign.class, "签名", FieldType.TEXT, 32767, null, false, true),
TAG(EasyTag.class, "标签", FieldType.REFERENCE_LIST, -1, null),
// 内部 // 内部

View file

@ -0,0 +1,34 @@
/*!
Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
See LICENSE and COMMERCIAL in the project root for license information.
*/
package com.rebuild.core.metadata.easymeta;
import cn.devezhao.persist4j.Field;
import com.rebuild.core.support.general.N2NReferenceSupport;
/**
* @author Zixin
* @since 2022/12/12
* @see N2NReferenceSupport
*/
public class EasyTag extends EasyField {
private static final long serialVersionUID = -5827184319679918289L;
protected EasyTag(Field field, DisplayType displayType) {
super(field, displayType);
}
@Override
public Object convertCompatibleValue(Object value, EasyField targetField) {
return super.convertCompatibleValue(value, targetField);
}
@Override
public Object exprDefaultValue() {
return super.exprDefaultValue();
}
}

View file

@ -11,6 +11,10 @@ See LICENSE and COMMERCIAL in the project root for license information.
.rb-right-sidebar.field-aside { .rb-right-sidebar.field-aside {
width: 280px; width: 280px;
}
.page-aside,
.field-aside {
z-index: 1; z-index: 1;
} }

View file

@ -923,7 +923,8 @@ select.form-control:not([disabled]) {
position: relative; position: relative;
} }
.form-layout .form-line { .form-layout .form-line,
.form-layout .form-line-breaked {
width: 100%; width: 100%;
margin-left: 15px; margin-left: 15px;
margin-right: 15px; margin-right: 15px;
@ -946,9 +947,10 @@ select.form-control:not([disabled]) {
.rbform-alert { .rbform-alert {
margin: -20px -20px 20px; margin: -20px -20px 20px;
text-align: center; text-align: center;
padding: 0.6rem; padding: 0.4rem;
border-radius: 0; border-radius: 0;
color: #fff; color: #fff;
color: #836203;
font-weight: normal; font-weight: normal;
} }
@ -2007,7 +2009,7 @@ th.column-fixed {
left: 10px; left: 10px;
margin: -11px 0 0; margin: -11px 0 0;
padding: 3px 5px 3px 5px; padding: 3px 5px 3px 5px;
color: #777; color: #666;
} }
.form-line.hover fieldset legend { .form-line.hover fieldset legend {
@ -2018,6 +2020,12 @@ th.column-fixed {
color: #4285f4; color: #4285f4;
} }
.form-line-breaked {
height: 0;
font-size: 0;
overflow: hidden;
}
.btn-primary.btn-outline, .btn-primary.btn-outline,
.btn-primary.btn-outline[disabled] { .btn-primary.btn-outline[disabled] {
background-color: #fff; background-color: #fff;
@ -4342,7 +4350,7 @@ html.external-auth .auth-body.must-center .login {
box-shadow: none; box-shadow: none;
} }
.btn.btn-light:hover { .btn.btn-light:not(:disabled):hover {
background-color: #e6e6e6; background-color: #e6e6e6;
} }

View file

@ -280,7 +280,10 @@ const _handlePicklist = function (dt) {
}) })
if (res.data.length > 5) $('#picklist-items').parent().removeClass('autoh') if (res.data.length > 5) $('#picklist-items').parent().removeClass('autoh')
}) })
$('.J_picklist-edit').on('click', () => RbModal.create(`/p/admin/metadata/picklist-editor?entity=${wpc.entityName}&field=${wpc.fieldName}&multi=${dt === 'MULTISELECT'}`, $L('选项配置')))
$('.J_picklist-edit').on('click', () => {
RbModal.create(`/p/admin/metadata/picklist-editor?entity=${wpc.entityName}&field=${wpc.fieldName}&multi=${dt === 'MULTISELECT'}`, $L('配置选项'))
})
} }
const _handleSeries = function () { const _handleSeries = function () {

View file

@ -22,10 +22,11 @@ const FIELD_TYPES = {
'PICKLIST': [$L('下拉列表'), 'mdi-form-select'], 'PICKLIST': [$L('下拉列表'), 'mdi-form-select'],
'CLASSIFICATION': [$L('分类'), 'mdi-form-dropdown'], 'CLASSIFICATION': [$L('分类'), 'mdi-form-dropdown'],
'MULTISELECT': [$L('多选'), 'mdi-format-list-checks'], 'MULTISELECT': [$L('多选'), 'mdi-format-list-checks'],
'TAG': [$L('标签'), 'mdi-tag-outline'],
'REFERENCE': [$L('引用'), 'mdi-feature-search-outline'], 'REFERENCE': [$L('引用'), 'mdi-feature-search-outline'],
'N2NREFERENCE': [$L('多引用'), 'mdi-text-box-search-outline'], 'N2NREFERENCE': [$L('多引用'), 'mdi-text-box-search-outline'],
'FILE': [$L('附件'), 'mdi-attachment'], 'FILE': [$L('附件'), 'mdi-attachment'],
'IMAGE': [$L('图片'), 'mdi-image'], 'IMAGE': [$L('图片'), 'mdi-image-outline'],
'AVATAR': [$L('头像'), 'mdi-account-box-outline'], 'AVATAR': [$L('头像'), 'mdi-account-box-outline'],
'BARCODE': [$L('二维码'), 'mdi-qrcode'], 'BARCODE': [$L('二维码'), 'mdi-qrcode'],
'LOCATION': [$L('位置'), 'mdi-map-marker'], 'LOCATION': [$L('位置'), 'mdi-map-marker'],

View file

@ -16,7 +16,7 @@ const COLSPANS = {
9: 'w-33', 9: 'w-33',
} }
$(document).ready(function () { $(document).ready(() => {
$.get(`../list-field?entity=${wpc.entityName}`, function (res) { $.get(`../list-field?entity=${wpc.entityName}`, function (res) {
const validFields = {}, const validFields = {},
configFields = [] configFields = []
@ -24,26 +24,17 @@ $(document).ready(function () {
configFields.push(this.field) configFields.push(this.field)
}) })
const $advControls = $('#adv-control tbody')
const template = $advControls.find('tr').html()
$advControls.find('tr').remove()
$(res.data).each(function () { $(res.data).each(function () {
validFields[this.fieldName] = this validFields[this.fieldName] = this
if (configFields.includes(this.fieldName) === false) render_unset(this) if (!configFields.includes(this.fieldName)) render_unset(this)
// Adv control
const $control = $(`<tr data-field="${this.fieldName}">${template}</tr>`).appendTo($advControls)
$control.find('td:eq(0)').text(this.fieldLabel)
const $req = $control.find('td:eq(2)')
if (this.builtin) $req.empty()
else if (!this.nullable) $req.find('input').attr({ disabled: true, checked: true })
}) })
AdvControl.init()
$(wpc.formConfig.elements).each(function () { $(wpc.formConfig.elements).each(function () {
const field = validFields[this.field] const field = validFields[this.field]
if (this.field === DIVIDER_LINE) { if (this.field === DIVIDER_LINE) {
render_item({ fieldName: this.field, fieldLabel: this.label || '', colspan: 4 }) render_item({ fieldName: this.field, fieldLabel: this.label || '', colspan: 4, collapsed: this.collapsed, breaked: this.breaked })
} else if (!field) { } else if (!field) {
const $item = $(`<div class="dd-item"><div class="dd-handle J_field J_missed"><span class="text-danger">[${this.field.toUpperCase()}] ${$L('字段已删除')}</span></div></div>`).appendTo( const $item = $(`<div class="dd-item"><div class="dd-handle J_field J_missed"><span class="text-danger">[${this.field.toUpperCase()}] ${$L('字段已删除')}</span></div></div>`).appendTo(
'.form-preview' '.form-preview'
@ -53,8 +44,7 @@ $(document).ready(function () {
$item.remove() $item.remove()
}) })
} else { } else {
render_item({ ...field, isFull: this.isFull || false, colspan: this.colspan, tip: this.tip || null, height: this.height || null }) render_item({ ...field, ...this })
AdvControl.set(this)
} }
}) })
@ -67,7 +57,7 @@ $(document).ready(function () {
.disableSelection() .disableSelection()
}) })
$('.J_add-divider').on('click', function () { $('.J_add-divider').on('click', () => {
$('.nav-tabs-classic a[href="#form-design"]').tab('show') $('.nav-tabs-classic a[href="#form-design"]').tab('show')
render_item({ fieldName: DIVIDER_LINE, fieldLabel: '', colspan: 4 }) render_item({ fieldName: DIVIDER_LINE, fieldLabel: '', colspan: 4 })
}) })
@ -112,6 +102,8 @@ $(document).ready(function () {
if (item.field === DIVIDER_LINE) { if (item.field === DIVIDER_LINE) {
item.colspan = 4 item.colspan = 4
item.label = $this.find('span').text() || '' item.label = $this.find('span').text() || ''
item.collapsed = $isTrue($this.attr('data-collapsed'))
item.breaked = $isTrue($this.attr('data-breaked'))
} else { } else {
item.colspan = 2 // default item.colspan = 2 // default
if ($this.parent().hasClass('w-100')) item.colspan = 4 if ($this.parent().hasClass('w-100')) item.colspan = 4
@ -126,7 +118,7 @@ $(document).ready(function () {
const height = $this.attr('data-height') const height = $this.attr('data-height')
if (height) item.height = height if (height) item.height = height
AdvControl.append(item) AdvControl.cfgAppend(item)
} }
formElements.push(item) formElements.push(item)
}) })
@ -184,15 +176,17 @@ const render_item = function (data) {
`<div class="dd-handle J_field" data-field="${data.fieldName}" data-label="${data.fieldLabel}"><span _title="${isDivider ? $L('分栏') : 'FIELD'}">${data.fieldLabel}</span></div>` `<div class="dd-handle J_field" data-field="${data.fieldName}" data-label="${data.fieldLabel}"><span _title="${isDivider ? $L('分栏') : 'FIELD'}">${data.fieldLabel}</span></div>`
).appendTo($item) ).appendTo($item)
if (data.creatable === false) $handle.addClass('readonly')
else if (data.nullable === false) $handle.addClass('not-nullable')
// 填写提示
if (data.tip) $('<i class="J_tip zmdi zmdi-info-outline"></i>').appendTo($handle.find('span')).attr('title', data.tip)
// 高度
if (data.height) $handle.attr('data-height', data.height)
const $action = $('<div class="dd-action"></div>').appendTo($handle) const $action = $('<div class="dd-action"></div>').appendTo($handle)
// 字段
if (data.displayType) { if (data.displayType) {
if (data.creatable === false) $handle.addClass('readonly')
else if (data.nullable === false) $handle.addClass('not-nullable')
// 填写提示
if (data.tip) $('<i class="J_tip zmdi zmdi-info-outline"></i>').appendTo($handle.find('span')).attr('title', data.tip)
// 长文本高度
if (data.height) $handle.attr('data-height', data.height)
$(`<span class="ft">${data.displayType}</span>`).appendTo($item) $(`<span class="ft">${data.displayType}</span>`).appendTo($item)
$(`<a class="mr-1 colspan" title="${$L('宽度')}" data-toggle="dropdown"><i class="zmdi zmdi-view-column"></i></a>`).appendTo($action) $(`<a class="mr-1 colspan" title="${$L('宽度')}" data-toggle="dropdown"><i class="zmdi zmdi-view-column"></i></a>`).appendTo($action)
@ -246,26 +240,37 @@ const render_item = function (data) {
render_unset(data) render_unset(data)
$item.remove() $item.remove()
}) })
AdvControl.set({ ...data })
} }
if (isDivider) { if (isDivider) {
$item.addClass('divider') $item.addClass('divider')
const $handle = $item.find('.dd-handle').attr({
'data-collapsed': data.collapsed,
'data-breaked': data.breaked,
})
$(`<a title="${$L('修改')}"><i class="zmdi zmdi-edit"></i></a>`) $(`<a title="${$L('修改')}"><i class="zmdi zmdi-edit"></i></a>`)
.appendTo($action) .appendTo($action)
.on('click', function () { .on('click', () => {
const _onConfirm = function (nv) { const _onConfirm = function (nv) {
$item.find('.dd-handle span').text(nv.dividerName || '') $item.find('.dd-handle span').text(nv.dividerName || '')
$handle.attr('data-collapsed', $isTrue(nv.collapsed))
$handle.attr('data-breaked', $isTrue(nv.breaked))
} }
const ov = $item.find('.dd-handle span').text() const ov = {
renderRbcomp(<DlgEditDivider onConfirm={_onConfirm} dividerName={ov || ''} />) dividerName: $item.find('.dd-handle span').text() || '',
collapsed: $handle.attr('data-collapsed'),
breaked: $handle.attr('data-breaked'),
}
renderRbcomp(<DlgEditDivider onConfirm={_onConfirm} {...ov} />)
}) })
$(`<a title="${$L('移除')}"><i class="zmdi zmdi-close"></i></a>`) $(`<a title="${$L('移除')}"><i class="zmdi zmdi-close"></i></a>`)
.appendTo($action) .appendTo($action)
.on('click', function () { .on('click', () => $item.remove())
$item.remove()
})
} }
} }
@ -344,8 +349,8 @@ class DlgEditField extends RbAlert {
} }
handleChange = (e) => { handleChange = (e) => {
let target = e.target const target = e.target
let s = {} const s = {}
s[target.name] = target.type === 'checkbox' ? target.checked : target.value s[target.name] = target.type === 'checkbox' ? target.checked : target.value
this.setState(s) this.setState(s)
} }
@ -369,6 +374,16 @@ class DlgEditDivider extends DlgEditField {
<label>{$L('分栏名称')}</label> <label>{$L('分栏名称')}</label>
<input type="text" className="form-control form-control-sm" name="dividerName" value={this.state.dividerName || ''} onChange={this.handleChange} placeholder={$L('输入分栏名称')} /> <input type="text" className="form-control form-control-sm" name="dividerName" value={this.state.dividerName || ''} onChange={this.handleChange} placeholder={$L('输入分栏名称')} />
</div> </div>
<div className="form-group">
<label className="custom-control custom-control-sm custom-checkbox custom-control-inline mt-0 mb-0">
<input className="custom-control-input" type="checkbox" defaultChecked={$isTrue(this.props.collapsed)} name="collapsed" onChange={this.handleChange} />
<span className="custom-control-label">{$L('默认收起')}</span>
</label>
<label className="custom-control custom-control-sm custom-checkbox custom-control-inline mt-0 mb-0 bosskey-show">
<input className="custom-control-input" type="checkbox" defaultChecked={$isTrue(this.props.breaked)} name="breaked" onChange={this.handleChange} />
<span className="custom-control-label">{$L('仅用于断行')}</span>
</label>
</div>
<div className="form-group mb-2"> <div className="form-group mb-2">
<button type="button" className="btn btn-primary" onClick={this._onConfirm}> <button type="button" className="btn btn-primary" onClick={this._onConfirm}>
{$L('确定')} {$L('确定')}
@ -396,22 +411,33 @@ const add2Layout = function (fieldName) {
// 高级控制 // 高级控制
const AdvControl = { const AdvControl = {
$controls: $('#adv-control tbody'), $tbody: $('#adv-control tbody'),
append: function (item) { init() {
this.$controls.find(`tr[data-field="${item.field}"] input`).each(function () { this._template = this.$tbody.find('tr').html()
this.$tbody.find('tr').remove()
},
set: function (field) {
const $c = $(`<tr data-field="${field.fieldName}">${this._template}</tr>`).appendTo(this.$tbody)
$c.find('td:eq(0)').text(field.fieldLabel)
const $req = $c.find('td:eq(2)')
if (field.builtin) $req.empty()
else if (!field.nullable) $req.find('input').attr({ disabled: true, checked: true })
this.$tbody.find(`tr[data-field="${field.fieldName}"] input`).each(function () {
const $this = $(this)
if ($this.prop('disabled')) return
const v = field[$this.attr('name')]
if (v === true || v === false) $this.attr('checked', v)
})
},
cfgAppend: function (item) {
this.$tbody.find(`tr[data-field="${item.field}"] input`).each(function () {
const $this = $(this) const $this = $(this)
if ($this.prop('disabled')) return if ($this.prop('disabled')) return
item[$this.attr('name')] = $this.prop('checked') item[$this.attr('name')] = $this.prop('checked')
}) })
}, },
set: function (item) {
this.$controls.find(`tr[data-field="${item.field}"] input`).each(function () {
const $this = $(this)
if ($this.prop('disabled')) return
const v = item[$this.attr('name')]
if (v === true || v === false) $this.attr('checked', v)
})
},
} }

View file

@ -115,14 +115,14 @@ class RbFormModal extends React.Component {
const formModel = res.data const formModel = res.data
const FORM = ( const FORM = (
<RbForm entity={entity} id={id} rawModel={formModel} $$$parent={this}> <RbForm entity={entity} id={id} rawModel={formModel} $$$parent={this} readonly={!!formModel.readonlyMessage}>
{formModel.elements.map((item) => { {formModel.elements.map((item) => {
return detectElement(item, entity) return detectElement(item, entity)
})} })}
</RbForm> </RbForm>
) )
this.setState({ formComponent: FORM }, () => { this.setState({ formComponent: FORM, alertMessage: formModel.readonlyMessage || null }, () => {
this.setState({ inLoad: false }) this.setState({ inLoad: false })
if (window.FrontJS) { if (window.FrontJS) {
window.FrontJS.Form._trigger('open', [res.data]) window.FrontJS.Form._trigger('open', [res.data])
@ -244,8 +244,11 @@ class RbForm extends React.Component {
this.isNew = !props.id this.isNew = !props.id
this._postBefore = props.postBefore || props.$$$parent.props.postBefore const $$$props = props.$$$parent && props.$$$parent.props ? props.$$$parent.props : {}
this._postAfter = props.postAfter || props.$$$parent.props.postAfter this._postBefore = props.postBefore || $$$props.postBefore
this._postAfter = props.postAfter || $$$props.postAfter
this._dividerRefs = []
} }
render() { render() {
@ -253,9 +256,13 @@ class RbForm extends React.Component {
<div className="rbform form-layout"> <div className="rbform form-layout">
<div className="form row" ref={(c) => (this._form = c)}> <div className="form row" ref={(c) => (this._form = c)}>
{this.props.children.map((fieldComp) => { {this.props.children.map((fieldComp) => {
const refid = fieldComp.props.field === TYPE_DIVIDER ? null : `fieldcomp-${fieldComp.props.field}` const ref = fieldComp.props.field === TYPE_DIVIDER ? $random('divider-') : `fieldcomp-${fieldComp.props.field}`
return React.cloneElement(fieldComp, { $$$parent: this, ref: refid }) if (fieldComp.props.field === TYPE_DIVIDER && fieldComp.props.collapsed) {
this._dividerRefs.push(ref)
}
return React.cloneElement(fieldComp, { $$$parent: this, ref: ref })
})} })}
{this.renderCustomizedFormArea()} {this.renderCustomizedFormArea()}
</div> </div>
@ -265,6 +272,12 @@ class RbForm extends React.Component {
) )
} }
renderCustomizedFormArea() {
let _FormArea
if (window._CustomizedForms) _FormArea = window._CustomizedForms.useFormArea(this.props.entity, this)
return _FormArea || null
}
renderDetailForm() { renderDetailForm() {
const detailMeta = this.props.rawModel.detailMeta const detailMeta = this.props.rawModel.detailMeta
if (!detailMeta || !window.ProTable) return null if (!detailMeta || !window.ProTable) return null
@ -330,7 +343,10 @@ class RbForm extends React.Component {
// 记录转换:预览模式 // 记录转换:预览模式
const previewid = this.props.$$$parent ? this.props.$$$parent.state.previewid : null const previewid = this.props.$$$parent ? this.props.$$$parent.state.previewid : null
const NADD = [5, 10, 20] if (!_ProTable) {
_ProTable = <ProTable entity={detailMeta} mainid={this.state.id} previewid={previewid} ref={(c) => (this._ProTable = c)} $$$main={this} />
}
return ( return (
<div className="detail-form-table"> <div className="detail-form-table">
<div className="row"> <div className="row">
@ -340,6 +356,7 @@ class RbForm extends React.Component {
{detailMeta.entityLabel} {detailMeta.entityLabel}
</h5> </h5>
</div> </div>
<div className="col text-right"> <div className="col text-right">
{detailImports && detailImports.length > 0 && ( {detailImports && detailImports.length > 0 && (
<div className="btn-group mr-2"> <div className="btn-group mr-2">
@ -365,15 +382,15 @@ class RbForm extends React.Component {
)} )}
<div className="btn-group"> <div className="btn-group">
<button className="btn btn-secondary" type="button" onClick={() => _addNew()}> <button className="btn btn-secondary" type="button" onClick={() => _addNew()} disabled={this.props.readonly}>
<i className="icon x14 zmdi zmdi-playlist-plus mr-1" /> <i className="icon x14 zmdi zmdi-playlist-plus mr-1" />
{$L('添加明细')} {$L('添加明细')}
</button> </button>
<button className="btn btn-secondary dropdown-toggle w-auto" type="button" data-toggle="dropdown"> <button className="btn btn-secondary dropdown-toggle w-auto" type="button" data-toggle="dropdown" disabled={this.props.readonly}>
<i className="icon zmdi zmdi-chevron-down" /> <i className="icon zmdi zmdi-chevron-down" />
</button> </button>
<div className="dropdown-menu dropdown-menu-right"> <div className="dropdown-menu dropdown-menu-right">
{NADD.map((n) => { {[5, 10, 20].map((n) => {
return ( return (
<a className="dropdown-item" onClick={() => _addNew(n)} key={`n-${n}`}> <a className="dropdown-item" onClick={() => _addNew(n)} key={`n-${n}`}>
{$L('添加 %d ', n)} {$L('添加 %d ', n)}
@ -385,17 +402,11 @@ class RbForm extends React.Component {
</div> </div>
</div> </div>
<div className="mt-2">{_ProTable ? _ProTable : <ProTable entity={detailMeta} mainid={this.state.id} previewid={previewid} ref={(c) => (this._ProTable = c)} $$$main={this} />}</div> <div className="mt-2">{_ProTable}</div>
</div> </div>
) )
} }
renderCustomizedFormArea() {
let _FormArea
if (window._CustomizedForms) _FormArea = window._CustomizedForms.useFormArea(this.props.entity, this)
return _FormArea || null
}
renderFormAction() { renderFormAction() {
let moreActions = [] let moreActions = []
// 添加明细 // 添加明细
@ -423,22 +434,24 @@ class RbForm extends React.Component {
return ( return (
<div className="dialog-footer" ref={(c) => (this._$formAction = c)}> <div className="dialog-footer" ref={(c) => (this._$formAction = c)}>
<button className="btn btn-secondary btn-space mr-2" type="button" onClick={() => this.props.$$$parent.hide()}> <button className="btn btn-secondary btn-space" type="button" onClick={() => this.props.$$$parent.hide()}>
{$L('取消')} {$L('取消')}
</button> </button>
<div className="btn-group dropup btn-space"> {!this.props.readonly && (
<button className="btn btn-primary" type="button" onClick={() => this.post()}> <div className="btn-group dropup btn-space ml-1">
{$L('保存')} <button className="btn btn-primary" type="button" onClick={() => this.post()}>
</button> {$L('保存')}
{moreActions.length > 0 && ( </button>
<React.Fragment> {moreActions.length > 0 && (
<button className="btn btn-primary dropdown-toggle w-auto" type="button" data-toggle="dropdown"> <React.Fragment>
<i className="icon zmdi zmdi-chevron-up" /> <button className="btn btn-primary dropdown-toggle w-auto" type="button" data-toggle="dropdown">
</button> <i className="icon zmdi zmdi-chevron-up" />
<div className="dropdown-menu dropdown-menu-primary dropdown-menu-right">{moreActions}</div> </button>
</React.Fragment> <div className="dropdown-menu dropdown-menu-primary dropdown-menu-right">{moreActions}</div>
)} </React.Fragment>
</div> )}
</div>
)}
</div> </div>
) )
} }
@ -463,6 +476,12 @@ class RbForm extends React.Component {
}) })
} }
// v3.2 默认收起
this._dividerRefs.forEach((d) => {
// eslint-disable-next-line react/no-string-refs
this.refs[d].toggle()
})
setTimeout(() => RbForm.renderAfter(this), 0) setTimeout(() => RbForm.renderAfter(this), 0)
} }
@ -2415,11 +2434,14 @@ class RbFormDivider extends React.Component {
} }
render() { render() {
if (this.props.breaked === true) {
return <div className="form-line-breaked"></div>
}
return ( return (
<div className="form-line hover" ref={(c) => (this._$formLine = c)}> <div className="form-line hover" ref={(c) => (this._$formLine = c)}>
<fieldset> <fieldset>
{this.props.label && ( {this.props.label && (
<legend onClick={() => this.toggle()} className="text-bold"> <legend onClick={() => this.toggle()} className="text-bold" title={$L('展开/收起')}>
{this.props.label} {this.props.label}
</legend> </legend>
)} )}
@ -2540,3 +2562,40 @@ const __addRecentlyUse = function (id) {
$.post(`/commons/search/recently-add?id=${id}`) $.post(`/commons/search/recently-add?id=${id}`)
} }
} }
// -- Lite
// eslint-disable-next-line no-unused-vars
class LiteForm extends RbForm {
renderCustomizedFormArea() {
return null
}
renderDetailForm() {
return null
}
renderFormAction() {
return null
}
componentDidMount() {
super.componentDidMount()
// TODO init...
}
buildFormData() {
const s = {}
const data = this.__FormData || {}
for (let k in data) {
const error = data[k].error
if (error) {
RbHighbar.create(error)
return false
}
s[k] = data[k].value
}
s.metadata = { id: this.props.id || '' }
return s
}
}

View file

@ -16,6 +16,8 @@ class ProTable extends React.Component {
constructor(props) { constructor(props) {
super(props) super(props)
this.state = {} this.state = {}
this._readonly = props.$$$main.props.readonly
} }
render() { render() {
@ -69,10 +71,10 @@ class ProTable extends React.Component {
{FORM} {FORM}
<td className={`col-action ${fixed && 'column-fixed'}`}> <td className={`col-action ${fixed && 'column-fixed'}`}>
<button className="btn btn-light hide" title={$L('复制')} onClick={() => this.copyLine(key)}> <button className="btn btn-light hide" title={$L('复制')} onClick={() => this.copyLine(key)} disabled={this._readonly}>
<i className="icon zmdi zmdi-copy fs-14" /> <i className="icon zmdi zmdi-copy fs-14" />
</button> </button>
<button className="btn btn-light" title={$L('移除')} onClick={() => this.removeLine(key)}> <button className="btn btn-light" title={$L('移除')} onClick={() => this.removeLine(key)} disabled={this._readonly}>
<i className="icon zmdi zmdi-close fs-16 text-bold" /> <i className="icon zmdi zmdi-close fs-16 text-bold" />
</button> </button>
</td> </td>