n2nref compatible

This commit is contained in:
devezhao 2020-11-17 01:26:51 +08:00
parent 918f503f69
commit 2f11f5be2e
13 changed files with 109 additions and 60 deletions

2
@rbv

@ -1 +1 @@
Subproject commit 5077481be27b5c21e2ecc687149718f9498c19a8 Subproject commit cf372ef8fec69b191700374676b2c994d12ae8b7

View file

@ -118,6 +118,7 @@ public class EasyMeta implements BaseMeta {
* *
* @param name * @param name
* @return * @return
* @see FieldExtConfigProps
*/ */
public String getExtraAttr(String name) { public String getExtraAttr(String name) {
return getExtraAttrs().getString(name); return getExtraAttrs().getString(name);

View file

@ -19,6 +19,7 @@ import com.rebuild.core.metadata.MetadataHelper;
import com.rebuild.core.metadata.MetadataSorter; import com.rebuild.core.metadata.MetadataSorter;
import com.rebuild.core.metadata.impl.DisplayType; import com.rebuild.core.metadata.impl.DisplayType;
import com.rebuild.core.metadata.impl.EasyMeta; import com.rebuild.core.metadata.impl.EasyMeta;
import com.rebuild.core.metadata.impl.FieldExtConfigProps;
import com.rebuild.core.privileges.PrivilegesManager; import com.rebuild.core.privileges.PrivilegesManager;
import com.rebuild.core.support.state.StateHelper; import com.rebuild.core.support.state.StateHelper;
import com.rebuild.web.BaseController; import com.rebuild.web.BaseController;
@ -167,12 +168,14 @@ public class MetadataGetting extends BaseController {
map.put("updatable", field.isUpdatable()); map.put("updatable", field.isUpdatable());
DisplayType dt = EasyMeta.getDisplayType(field); DisplayType dt = EasyMeta.getDisplayType(field);
if (dt == DisplayType.REFERENCE) { if (dt == DisplayType.REFERENCE || dt == DisplayType.N2NREFERENCE) {
Entity refEntity = field.getReferenceEntity(); Entity refEntity = field.getReferenceEntity();
Field nameField = MetadataHelper.getNameField(refEntity); Field nameField = MetadataHelper.getNameField(refEntity);
map.put("ref", new String[]{refEntity.getName(), EasyMeta.getDisplayType(nameField).name()}); map.put("ref", new String[]{refEntity.getName(), EasyMeta.getDisplayType(nameField).name()});
} else if (dt == DisplayType.STATE) { } else if (dt == DisplayType.STATE) {
map.put("stateClass", StateHelper.getSatetClass(field).getName()); map.put("stateClass", StateHelper.getSatetClass(field).getName());
} else if (dt == DisplayType.CLASSIFICATION) {
map.put("classification", easyField.getExtraAttr(FieldExtConfigProps.CLASSIFICATION_USE));
} }
return map; return map;
} }

View file

@ -91,7 +91,6 @@ module.exports = {
ConfigFormDlg: true, ConfigFormDlg: true,
RbPreview: true, RbPreview: true,
$countdownButton: true, $countdownButton: true,
ChartSelect: true,
Share2: true, Share2: true,
$stopEvent: true, $stopEvent: true,
$addResizeHandler: true, $addResizeHandler: true,
@ -105,6 +104,7 @@ module.exports = {
$fromNow: true, $fromNow: true,
$expired: true, $expired: true,
$L: true, $L: true,
$isTrue: true $isTrue: true,
$fieldIsCompatible: true
}, },
} }

View file

@ -114,6 +114,7 @@
referenceEntity: '[[${referenceEntity}]]', referenceEntity: '[[${referenceEntity}]]',
} }
</script> </script>
<script th:src="@{/assets/js/metadata/field-compatible.js}" type="text/babel"></script>
<script th:src="@{/assets/js/metadata/auto-fillin.js}" type="text/babel"></script> <script th:src="@{/assets/js/metadata/auto-fillin.js}" type="text/babel"></script>
</body> </body>
</html> </html>

View file

@ -111,6 +111,7 @@ $(document).ready(function () {
chart.w = chart.h = 4 chart.w = chart.h = 4
add_widget(chart) add_widget(chart)
} }
// eslint-disable-next-line react/jsx-no-undef
renderRbcomp(<ChartSelect key="ChartSelect" select={select} />, null, function () { renderRbcomp(<ChartSelect key="ChartSelect" select={select} />, null, function () {
dlgChartSelect = this dlgChartSelect = this
this.setState({ appended: appended }) this.setState({ appended: appended })

View file

@ -16,10 +16,10 @@ $(document).ready(() => {
}) })
const loadRules = () => { const loadRules = () => {
$.get('../auto-fillin-list?field=' + wpc.fieldName, (res) => { $.get(`../auto-fillin-list?field=${wpc.fieldName}`, (res) => {
const tbody = $('#dataList tbody').empty() const $tbody = $('#dataList tbody').empty()
$(res.data).each(function () { $(res.data).each(function () {
let tr = $('<tr></tr>').appendTo(tbody) let tr = $('<tr></tr>').appendTo($tbody)
$('<td><div>' + this.targetFieldLabel + '</div></td>').appendTo(tr) $('<td><div>' + this.targetFieldLabel + '</div></td>').appendTo(tr)
$('<td>' + this.sourceFieldLabel + '</div></td>').appendTo(tr) $('<td>' + this.sourceFieldLabel + '</div></td>').appendTo(tr)
const extc = this.extConfig const extc = this.extConfig
@ -63,7 +63,7 @@ class DlgRuleEdit extends RbFormHandler {
render() { render() {
return ( return (
<RbModal title="回填规则" ref={(c) => (this._dlg = c)} disposeOnHide={true}> <RbModal title={$L('FillbackRule')} ref={(c) => (this._dlg = c)} disposeOnHide={true}>
<div className="form"> <div className="form">
<div className="form-group row"> <div className="form-group row">
<label className="col-sm-3 col-form-label text-sm-right">{$L('SourceField')}</label> <label className="col-sm-3 col-form-label text-sm-right">{$L('SourceField')}</label>
@ -150,7 +150,7 @@ class DlgRuleEdit extends RbFormHandler {
placeholder: $L('SelectSome,Field'), placeholder: $L('SelectSome,Field'),
allowClear: false, allowClear: false,
}) })
.on('change', (e) => this.__renderTargetFields(e.target.value)) .on('change', (e) => this._renderTargetFields(e.target.value))
this.__select2.push(s2source) this.__select2.push(s2source)
if (this.props.sourceField) { if (this.props.sourceField) {
@ -168,40 +168,20 @@ class DlgRuleEdit extends RbFormHandler {
}) })
}) })
} }
__renderTargetFields(s) {
const source = this.__sourceFieldsCache.find((x) => { _renderTargetFields(s) {
return s === x.name const source = this.__sourceFieldsCache.find((x) => s === x.name)
})
let canFillinByType = CAN_FILLIN_MAPPINGS[source.type] || []
canFillinByType.push('TEXT')
canFillinByType.push('NTEXT')
// 显示兼容的目标字段 // 显示兼容的目标字段
let tFields = [] const targetFields = []
$(this.__targetFieldsCache).each(function () { $(this.__targetFieldsCache).each(function () {
if ( if (this.creatable && this.name !== wpc.fieldName && $fieldIsCompatible(source, this)) {
!this.creatable || targetFields.push(this)
this.name === wpc.fieldName ||
this.type === 'SERIES' ||
this.type === 'MULTISELECT' ||
this.type === 'PICKLIST' ||
(source.type === 'FILE' && this.type !== 'FILE') ||
(source.type === 'IMAGE' && this.type !== 'IMAGE') ||
(source.type === 'AVATAR' && this.type !== 'AVATAR')
)
return
if (source.type === this.type || canFillinByType.includes(this.type)) {
if (source.type === 'REFERENCE') {
if (source.ref && this.ref && source.ref[0] === this.ref[0]) tFields.push(this)
} else if (source.type === 'STATE') {
if (source.stateClass && source.stateClass === this.stateClass) tFields.push(this)
} else {
tFields.push(this)
}
} }
}) })
this.setState({ targetFields: tFields }) this.setState({ targetFields: targetFields }, () => {
if (targetFields.length > 0) this.__select2[0].val(targetFields[0].name)
})
} }
handleChange(e) { handleChange(e) {
@ -237,10 +217,3 @@ class DlgRuleEdit extends RbFormHandler {
}) })
} }
} }
const CAN_FILLIN_MAPPINGS = {
NUMBER: ['DECIMAL'],
DECIMAL: ['NUMBER'],
DATE: ['DATETIME'],
DATETIME: ['DATE'],
}

View file

@ -0,0 +1,53 @@
/*
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.
*/
// 字段类型兼容
const FT_COMPATIBLE = {
NUMBER: ['DECIMAL'],
DECIMAL: ['NUMBER'],
DATE: ['DATETIME'],
DATETIME: ['DATE'],
TEXT: ['*'],
NTEXT: ['*'],
}
/**
* 字段兼容判断
* see backend `FieldValueCompatibleConversion.java`
*
* @param s 源字段
* @param t 目标字段
* @returns {boolean}
*/
// eslint-disable-next-line no-unused-vars
function $fieldIsCompatible(s, t) {
// 不支持
if (t.type === 'SERIES') return false
if (t.type === 'MULTISELECT') return false
if (t.type === 'PICKLIST') return false
// 必须对应
if (s.type === 'FILE' && t.type !== 'FILE') return false
if (s.type === 'IMAGE' && t.type !== 'IMAGE') return false
if (s.type === 'AVATAR' && t.type !== 'AVATAR') return false
// 判断附加参数
if (t.type === 'REFERENCE' || t.type === 'N2NREFERENCE') {
return t.ref && s.ref && t.ref[0] === s.ref[0]
}
if (t.type === 'CLASSIFICATION') {
return t.classification && t.classification === s.classification
}
if (t.type === 'STATE') {
return t.stateClass && t.stateClass === s.stateClass
}
if (t.type === s.type) return true
const allow = FT_COMPATIBLE[t.type] || []
return allow.includes('*') || allow.includes(s.type)
}

View file

@ -4,6 +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.
*/ */
// 打印视图
const wpc = window.__PageConfig const wpc = window.__PageConfig
$(document).ready(() => { $(document).ready(() => {
@ -32,8 +33,8 @@ class PreviewTable extends React.Component {
<table className="table table-bordered table-sm table-fixed"> <table className="table table-bordered table-sm table-fixed">
<tbody> <tbody>
{rows.map((row, idx) => { {rows.map((row, idx) => {
let c1 = row[0] const c1 = row[0]
let c2 = row[1] || {} const c2 = row[1] || {}
if (row.length === 1) { if (row.length === 1) {
if (c1.field === '$DIVIDER$') { if (c1.field === '$DIVIDER$') {
return ( return (
@ -72,7 +73,7 @@ class PreviewTable extends React.Component {
return ( return (
<div className="img-field avatar"> <div className="img-field avatar">
<span className="img-thumbnail img-upload"> <span className="img-thumbnail img-upload">
<img src={`${rb.baseUrl}/assets/img/avatar.png`} /> <img src={`${rb.baseUrl}/assets/img/avatar.png`} alt="Avatar" />
</span> </span>
</div> </div>
) )
@ -84,7 +85,7 @@ class PreviewTable extends React.Component {
return ( return (
<ul className="m-0 p-0 pl-3"> <ul className="m-0 p-0 pl-3">
{item.value.map((x) => { {item.value.map((x) => {
return <li key={`file-${x}`}>{$fileCutName(x)}</li> return <li key={x}>{$fileCutName(x)}</li>
})} })}
</ul> </ul>
) )
@ -93,8 +94,8 @@ class PreviewTable extends React.Component {
<ul className="list-inline m-0"> <ul className="list-inline m-0">
{item.value.map((x) => { {item.value.map((x) => {
return ( return (
<li className="list-inline-item" key={`image-${x}`}> <li className="list-inline-item" key={x}>
<img src={`${rb.baseUrl}/filex/img/${x}?imageView2/2/w/100/interlace/1/q/100`} /> <img src={`${rb.baseUrl}/filex/img/${x}?imageView2/2/w/100/interlace/1/q/100`} alt="IMG" />
</li> </li>
) )
})} })}
@ -104,7 +105,7 @@ class PreviewTable extends React.Component {
return ( return (
<div className="img-field avatar"> <div className="img-field avatar">
<span className="img-thumbnail img-upload"> <span className="img-thumbnail img-upload">
<img src={`${rb.baseUrl}/filex/img/${item.value}?imageView2/2/w/100/interlace/1/q/100`} /> <img src={`${rb.baseUrl}/filex/img/${item.value}?imageView2/2/w/100/interlace/1/q/100`} alt="Avatar" />
</span> </span>
</div> </div>
) )
@ -132,12 +133,24 @@ class PreviewTable extends React.Component {
</span> </span>
</div> </div>
) )
} else if (item.type === 'N2NREFERENCE') {
return (
<ul className="m-0 p-0 pl-3">
{item.value.map((x) => {
return <li key={x.id}>{__formatRefText(x)}</li>
})}
</ul>
)
} else if (typeof item.value === 'object') { } else if (typeof item.value === 'object') {
let text = item.value.text return __formatRefText(item.value)
if (!text && item.value.id) text = `@${item.value.id.toUpperCase()}`
return text
} else { } else {
return item.value return item.value
} }
} }
} }
const __formatRefText = function (value) {
const text = value.text
if (!text && value.id) return `@${value.id.toUpperCase()}`
else return text
}

View file

@ -398,7 +398,10 @@ class FilterItem extends React.Component {
} }
} else if (fieldType === 'BOOL') { } else if (fieldType === 'BOOL') {
op = ['EQ'] op = ['EQ']
} else if (fieldType === 'N2NREFERENCE') {
op = []
} }
op.push('NL', 'NT') op.push('NL', 'NT')
if (this.isApprovalState()) op = ['IN', 'NIN'] if (this.isApprovalState()) op = ['IN', 'NIN']

View file

@ -277,7 +277,7 @@ class BatchUpdateEditor extends React.Component {
componentWillUnmount() { componentWillUnmount() {
this.__select2.forEach((item) => item.select2('destroy')) this.__select2.forEach((item) => item.select2('destroy'))
this.__select2 = null this.__select2 = null
this.__destroyLastValueComp() this._destroyLastValueComp()
} }
render() { render() {
@ -341,7 +341,7 @@ class BatchUpdateEditor extends React.Component {
} }
} }
__destroyLastValueComp() { _destroyLastValueComp() {
if (this.__lastSelect2) { if (this.__lastSelect2) {
this.__lastSelect2.select2('destroy') this.__lastSelect2.select2('destroy')
this.__lastSelect2 = null this.__lastSelect2 = null
@ -356,7 +356,7 @@ class BatchUpdateEditor extends React.Component {
// Unchanged // Unchanged
if (prevState.selectField === this.state.selectField && prevState.selectOp === this.state.selectOp) return if (prevState.selectField === this.state.selectField && prevState.selectOp === this.state.selectOp) return
if (this.state.selectOp === 'NULL') return if (this.state.selectOp === 'NULL') return
this.__destroyLastValueComp() this._destroyLastValueComp()
const field = this.props.fields.find((item) => { const field = this.props.fields.find((item) => {
return this.state.selectField === item.name return this.state.selectField === item.name
@ -365,7 +365,7 @@ class BatchUpdateEditor extends React.Component {
if (field.type === 'REFERENCE' || field.type === 'CLASSIFICATION') { if (field.type === 'REFERENCE' || field.type === 'CLASSIFICATION') {
this.__lastSelect2 = $initReferenceSelect2(this._value, { this.__lastSelect2 = $initReferenceSelect2(this._value, {
name: field.name, name: field.name,
label: field.label, placeholder: $L('NewValue'),
entity: this.props.entity, entity: this.props.entity,
searchType: field.type === 'CLASSIFICATION' ? 'classification' : null, searchType: field.type === 'CLASSIFICATION' ? 'classification' : null,
}) })

View file

@ -1237,6 +1237,7 @@ const ChartsWidget = {
this.__chartSelect.setState({ appended: ChartsWidget.__currentCharts() }) this.__chartSelect.setState({ appended: ChartsWidget.__currentCharts() })
return return
} }
// eslint-disable-next-line react/jsx-no-undef
renderRbcomp(<ChartSelect select={(c) => this.renderChart(c, true)} entity={wpc.entity[0]} />, null, function () { renderRbcomp(<ChartSelect select={(c) => this.renderChart(c, true)} entity={wpc.entity[0]} />, null, function () {
ChartsWidget.__chartSelect = this ChartsWidget.__chartSelect = this
this.setState({ appended: ChartsWidget.__currentCharts() }) this.setState({ appended: ChartsWidget.__currentCharts() })

View file

@ -490,7 +490,7 @@ var $unmount = function (container, delay, keepContainer) {
var $initReferenceSelect2 = function (el, field) { var $initReferenceSelect2 = function (el, field) {
var search_input = null var search_input = null
return $(el).select2({ return $(el).select2({
placeholder: $L('SelectSome').replace('{0}', field.label), placeholder: field.placeholder || $L('SelectSome').replace('{0}', field.label),
minimumInputLength: 0, minimumInputLength: 0,
maximumSelectionLength: $(el).attr('multiple') ? 999 : 2, maximumSelectionLength: $(el).attr('multiple') ? 999 : 2,
ajax: { ajax: {