diff --git a/.eslintrc.json b/.eslintrc.json index d82692aa0..14079c468 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -88,7 +88,8 @@ "$pgt": true, "$stopEvent": true, "$addResizeHandler": true, - "$lang": true + "$lang": true, + "$empty": true }, "rules": { "strict": 0, diff --git a/src/main/webapp/assets/css/rb-base.css b/src/main/webapp/assets/css/rb-base.css index 508580ae2..da091c503 100644 --- a/src/main/webapp/assets/css/rb-base.css +++ b/src/main/webapp/assets/css/rb-base.css @@ -23952,7 +23952,8 @@ fieldset[disabled] .full-calendar .fc-button:hover { .select2-container--default .select2-selection--multiple .select2-selection__rendered .select2-selection__clear { right: 20px; font-size: 1.35rem; - top: -1px; + top: -2px; + font-weight: normal; } .select2-container--default .select2-selection--multiple .select2-selection__rendered .select2-selection__clear { diff --git a/src/main/webapp/assets/css/view-page.css b/src/main/webapp/assets/css/view-page.css index a9d1ffa12..b355506d9 100644 --- a/src/main/webapp/assets/css/view-page.css +++ b/src/main/webapp/assets/css/view-page.css @@ -299,7 +299,7 @@ body { color: #aaa; width: 20px; height: 20px; - margin-top: 8px; + margin-top: 7px; text-align: center; border-radius: 2px; background-color: #eee; @@ -329,14 +329,43 @@ body { text-align: right; } -.rbview-form .form-group.row.editable>div .edit-oper>.btn { - width: 26px; - height: 26px; +.rbview-form .form-group.row.editable>div .edit-oper .btn { + width: 28px; + height: 28px; overflow: hidden; line-height: 1.1; padding: 0; min-width: 0; min-height: 0; font-weight: bold; - margin-left: -1px; +} + +.type-AVATAR .edit-oper, +.type-BOOL .edit-oper, +.type-FILE .edit-oper, +.type-MULTISELECT .edit-oper, +.type-IMAGE .edit-oper { + border-top: 1px solid #dbdbdb; +} + +.type-AVATAR .edit-oper { + margin-top: 7px; +} + +.type-IMAGE .edit-oper { + margin-top: -2px; +} + +.type-BOOL .edit-oper { + margin-top: -8px; +} + +.type-MULTISELECT .edit-oper { + margin-top: -1px; +} + +.rbview-form.readonly .form-group.row.editable a.edit { + display: none !important; + width: 0 !important; + height: 0 !important; } \ No newline at end of file diff --git a/src/main/webapp/assets/js/rb-approval.jsx b/src/main/webapp/assets/js/rb-approval.jsx index 34d912aeb..a0e59f287 100644 --- a/src/main/webapp/assets/js/rb-approval.jsx +++ b/src/main/webapp/assets/js/rb-approval.jsx @@ -27,7 +27,7 @@ class ApprovalProcessor extends React.Component { } renderStateProcessing() { - $('.J_edit,.J_delete,.J_add-slave').attr('disabled', true) + window.RbViewPage && window.RbViewPage.setReadonly(true) let aMsg = '当前记录正在审批中' if (this.state.imApprover) { if (this.state.imApproveSatate === 1) aMsg = '当前记录正在等待你审批' @@ -44,9 +44,7 @@ class ApprovalProcessor extends React.Component { } renderStateApproved() { - $('.J_edit,.J_delete,.J_add-slave').remove() - window.RbViewPage && window.RbViewPage.cleanViewActionButton() - + window.RbViewPage && window.RbViewPage.setReadonly(true) return (
@@ -73,9 +71,7 @@ class ApprovalProcessor extends React.Component { } componentDidMount() { - $.get(`${rb.baseUrl}/app/entity/approval/state?record=${this.props.id}`, (res) => { - this.setState(res.data) - }) + $.get(`${rb.baseUrl}/app/entity/approval/state?record=${this.props.id}`, (res) => this.setState(res.data)) } submit = () => { diff --git a/src/main/webapp/assets/js/rb-datalist.jsx b/src/main/webapp/assets/js/rb-datalist.jsx index 843e82546..3e34f4e1d 100644 --- a/src/main/webapp/assets/js/rb-datalist.jsx +++ b/src/main/webapp/assets/js/rb-datalist.jsx @@ -288,7 +288,6 @@ class RbList extends React.Component { if ($this.prop('checked')) selected.push($this.parents('tr').data('id')) }) if (selected.length === 0 && noWarn !== true) RbHighbar.create('未选中任何记录') - console.log(selected) return selected } diff --git a/src/main/webapp/assets/js/rb-forms.exts.jsx b/src/main/webapp/assets/js/rb-forms.exts.jsx index abf27245b..a5a0ec8af 100644 --- a/src/main/webapp/assets/js/rb-forms.exts.jsx +++ b/src/main/webapp/assets/js/rb-forms.exts.jsx @@ -9,7 +9,9 @@ class RbFormAvatar extends RbFormElement { let aUrl = rb.baseUrl + (this.state.value ? `/filex/img/${this.state.value}?imageView2/2/w/100/interlace/1/q/100` : '/assets/img/avatar.png') return
- {!this.props.readonly && this._fieldValue__input = c} type="file" className="inputfile" id={`${this.props.field}-input`} accept="image/png,image/jpeg,image/gif" />} + {!this.props.readonly && + this._fieldValue__input = c} type="file" className="inputfile" id={`${this.props.field}-input`} accept="image/png,image/jpeg,image/gif" /> + }
@@ -20,18 +22,19 @@ class RbFormAvatar extends RbFormElement { return
} - componentDidMount() { - super.componentDidMount() - if (this.state.viewMode === true || this.props.readonly) return - - let mp - $createUploader(this._fieldValue__input, (res) => { - if (!mp) mp = new Mprogress({ template: 1, start: true }) - mp.set(res.percent / 100) // 0.x - }, (res) => { - mp.end() - this.handleChange({ target: { value: res.key } }, true) - }) + onEditModeChanged(destroy) { + if (destroy) { + // NOOP + } else { + let mp + $createUploader(this._fieldValue__input, (res) => { + if (!mp) mp = new Mprogress({ template: 1, start: true }) + mp.set(res.percent / 100) // 0.x + }, (res) => { + mp.end() + this.handleChange({ target: { value: res.key } }, true) + }) + } } // Not implemented diff --git a/src/main/webapp/assets/js/rb-forms.jsx b/src/main/webapp/assets/js/rb-forms.jsx index a31e29843..ab79e8684 100644 --- a/src/main/webapp/assets/js/rb-forms.jsx +++ b/src/main/webapp/assets/js/rb-forms.jsx @@ -146,7 +146,7 @@ class RbForm extends React.Component { return
this._form = c}> {this.props.children.map((child) => { - return React.cloneElement(child, { $$$parent: this, ref: 'reffield-' + child.props.field }) + return React.cloneElement(child, { $$$parent: this, ref: 'field-' + child.props.field }) // Has error in strict-mode //child.$$$parent = that; return child })} @@ -198,10 +198,10 @@ class RbForm extends React.Component { if (!data || data.length === 0) return data.forEach((item) => { // eslint-disable-next-line react/no-string-refs - let ref = this.refs['reffield-' + item.field] - if (ref) { - if (item.fillinForce !== true && !!ref.getValue()) return - if ((this.isNew === true && item.whenCreate === true) || (this.isNew !== true && item.whenUpdate === true)) ref.setValue(item.value) + let reff = this.refs['field-' + item.field] + if (reff) { + if (item.fillinForce !== true && !!reff.getValue()) return + if ((this.isNew && item.whenCreate) || (!this.isNew && item.whenUpdate)) reff.setValue(item.value) } }) } @@ -280,6 +280,9 @@ class RbForm extends React.Component { } } +// 开启视图编辑 +const EDIT_ON_VIEW = rb.env === 'dev' + // 表单元素基础类 class RbFormElement extends React.Component { constructor(props) { @@ -298,16 +301,18 @@ class RbFormElement extends React.Component { colWidths[0] = 4 if (props.isFull === true) colWidths = [2, 10] } - const editable = props.onView && !props.readonly + const editable = EDIT_ON_VIEW && props.onView && !props.readonly return
-
- {this.state.viewMode ? this.renderViewElement() : this.renderElement()} +
this._fieldText = c} className={'col-12 col-sm-' + colWidths[1]}> + {(!props.onView || (editable && this.state.editMode)) ? this.renderElement() : this.renderViewElement()} {!props.onView && props.tip &&

{props.tip}

} - {editable && this.state.viewMode && this.toggleEdit(false)} />} - {editable && !this.state.viewMode &&
@@ -321,9 +326,9 @@ class RbFormElement extends React.Component { } // 渲染视图 - renderViewElement(forceText) { - let text = forceText === undefined ? this.state.value : forceText - if ($.type(text) === 'array' && text.length === 0) text = null + renderViewElement() { + let text = arguments.length > 0 ? arguments[0] : this.state.value // use `forceText` + if (text && $empty(text)) text = null return
{text || ()}
@@ -331,11 +336,25 @@ class RbFormElement extends React.Component { componentDidMount() { const props = this.props - // 必填字段 - if (props.nullable === false && props.readonly === false && props.onView !== true) { - if (!props.value) props.$$$parent.setFieldValue(props.field, null, props.label + '不能为空') + if (!props.onView) { + // 必填字段 + if (props.nullable === false && props.readonly === false) { + $empty(props.value) && props.$$$parent.setFieldValue(props.field, null, props.label + '不能为空') + } + props.tip && $(this._fieldLabel).find('i.zmdi').tooltip({ placement: 'right' }) + } + if (!props.onView && !this.props.readonly) this.onEditModeChanged() + } + componentWillUnmount() { this.onEditModeChanged(true) } + + onEditModeChanged(destroy) { + if (destroy) { + if (this.__select2) { + if ($.type(this.__select2) === 'array') $(this.__select2).each(function () { this.select2('destroy') }) + else this.__select2.select2('destroy') + this.__select2 = null + } } - if (!props.onView && props.tip) $(this._fieldLabel).find('i.zmdi').tooltip({ placement: 'right' }) } // 修改值(表单组件(字段)值变化应调用此方法) @@ -349,18 +368,19 @@ class RbFormElement extends React.Component { this.setState({ value: '' }, () => this.checkValue()) } - // 编辑值 - toggleEdit(viewMode, newValue) { - this.setState({ viewMode: viewMode }, () => { - if (this.state.viewMode) { - if (newValue === undefined) { - newValue = this.state.newValue === undefined ? this.props.value : this.state.newValue - } - this.setState({ value: newValue, newValue: newValue }) + // 编辑单个字段值 + toggleEditMode(mode) { + this.setState({ editMode: mode }, () => { + if (this.state.editMode) { + this.onEditModeChanged() + } else { + let newValue = arguments.length > 1 ? arguments[1] // use `newValue` + : (this.state.newValue === undefined ? this.props.value : this.state.newValue) + this.setState({ value: newValue, newValue: newValue || null }, () => this.onEditModeChanged(true)) } }) } - handleEdit = () => { + handleEditConfirm = () => { this.props.$$$parent.saveSingleFieldValue && this.props.$$$parent.saveSingleFieldValue(this) } @@ -388,19 +408,13 @@ class RbFormElement extends React.Component { // 未修改 isValueUnchanged() { - debugger let propValue = this.state.newValue === undefined ? this.props.value : this.state.newValue return $same(propValue || '', this.state.value || '') } // Getter / Setter - - setValue(val) { - this.handleChange({ target: { value: val } }, true) - } - getValue() { - return this.state.value - } + setValue(val) { this.handleChange({ target: { value: val } }, true) } + getValue() { return this.state.value } } class RbFormText extends RbFormElement { @@ -434,7 +448,7 @@ class RbFormEMail extends RbFormText { renderViewElement() { if (!this.state.value) return super.renderViewElement() - return
+ return } isValueError() { @@ -449,11 +463,15 @@ class RbFormPhone extends RbFormText { super(props) } + renderViewElement() { + if (!this.state.value) return super.renderViewElement() + return + } + isValueError() { let err = super.isValueError() if (err) return err - if (!!this.state.value && $regex.isTel(this.state.value) === false) '电话/手机格式不正确' - return null + return (!!this.state.value && $regex.isTel(this.state.value) === false) ? '电话/手机格式不正确' : null } } @@ -466,7 +484,8 @@ class RbFormNumber extends RbFormText { let err = super.isValueError() if (err) return err if (!!this.state.value && $regex.isNumber(this.state.value) === false) return '整数格式不正确' - if (!!this.state.value && this.props.notNegative === 'true' && parseFloat(this.state.value) < 0) return '不允许为负数' + // eslint-disable-next-line eqeqeq + if (!!this.state.value && this.props.notNegative === true && parseFloat(this.state.value) < 0) return '不允许为负数' return null } } @@ -480,7 +499,8 @@ class RbFormDecimal extends RbFormText { let err = super.isValueError() if (err) return err if (!!this.state.value && $regex.isDecimal(this.state.value) === false) return '货币格式不正确' - if (!!this.state.value && this.props.notNegative === 'true' && parseFloat(this.state.value) < 0) return '不允许为负数' + // eslint-disable-next-line eqeqeq + if (!!this.state.value && this.props.notNegative == true && parseFloat(this.state.value) < 0) return '不允许为负数' return null } } @@ -497,10 +517,21 @@ class RbFormTextarea extends RbFormElement { renderViewElement() { if (!this.state.value) return super.renderViewElement() - return
+ return
this._textarea = c}> {this.state.value.split('\n').map((line, idx) => { return

{line}

})}
} + + componentDidMount() { + super.componentDidMount() + this.unmountFieldComp() + } + unmountFieldComp() { + if (this._textarea) $(this._textarea).perfectScrollbar() + } + mountFieldComp() { + if (this._textarea) $(this._textarea).perfectScrollbar('destroy') + } } class RbFormDateTime extends RbFormElement { @@ -519,40 +550,37 @@ class RbFormDateTime extends RbFormElement {
} - componentDidMount() { - super.componentDidMount() - if (this.state.viewMode || this.props.readonly) return + onEditModeChanged(destroy) { + if (destroy) { + if (this.__datetimepicker) { + this.__datetimepicker.datetimepicker('remove') + this.__datetimepicker = null + } + } else { + const format = (this.props.datetimeFormat || this.props.dateFormat).replace('mm', 'ii').toLowerCase() + let minView = 0 + if (format.length === 7) minView = 'year' + else if (format.length === 10) minView = 'month' - const format = (this.props.datetimeFormat || this.props.dateFormat).replace('mm', 'ii').toLowerCase() - let minView = 0 - if (format.length === 7) minView = 'year' - else if (format.length === 10) minView = 'month' - - const that = this - this.__datetimepicker = $(this._fieldValue).datetimepicker({ - componentIcon: 'zmdi zmdi-calendar', - navIcons: { rightIcon: 'zmdi zmdi-chevron-right', leftIcon: 'zmdi zmdi-chevron-left' }, - format: format || 'yyyy-mm-dd hh:ii:ss', - minView: minView, - startView: minView === 'year' ? 'year' : 'month', - weekStart: 1, - autoclose: true, - language: 'zh', - todayHighlight: true, - showMeridian: false, - keyboardNavigation: false, - minuteStep: 5, - }).on('changeDate', function () { - let val = $(this).val() - that.handleChange({ target: { value: val } }, true) - }) - $(this._fieldValue__icon).click(() => this.__datetimepicker.datetimepicker('show')) - } - - componentWillUnmount() { - if (this.__datetimepicker) { - this.__datetimepicker.datetimepicker('remove') - this.__datetimepicker = null + const that = this + this.__datetimepicker = $(this._fieldValue).datetimepicker({ + componentIcon: 'zmdi zmdi-calendar', + navIcons: { rightIcon: 'zmdi zmdi-chevron-right', leftIcon: 'zmdi zmdi-chevron-left' }, + format: format || 'yyyy-mm-dd hh:ii:ss', + minView: minView, + startView: minView === 'year' ? 'year' : 'month', + weekStart: 1, + autoclose: true, + language: 'zh', + todayHighlight: true, + showMeridian: false, + keyboardNavigation: false, + minuteStep: 5, + }).on('changeDate', function () { + let val = $(this).val() + that.handleChange({ target: { value: val } }, true) + }) + $(this._fieldValue__icon).click(() => this.__datetimepicker.datetimepicker('show')) } } } @@ -594,8 +622,7 @@ class RbFormImage extends RbFormElement { renderViewElement() { const value = this.state.value - if (!value || value.length === 0) return super.renderViewElement() - + if ($empty(value)) return super.renderViewElement() return
{value.map((item, idx) => { return @@ -607,20 +634,21 @@ class RbFormImage extends RbFormElement {
} - componentDidMount() { - super.componentDidMount() - if (this.state.viewMode || this.props.readonly) return - - let mp - $createUploader(this._fieldValue__input, (res) => { - if (!mp) mp = new Mprogress({ template: 1, start: true }) - mp.set(res.percent / 100) // 0.x - }, (res) => { - mp.end() - let paths = this.state.value || [] - paths.push(res.key) - this.handleChange({ target: { value: paths } }, true) - }) + onEditModeChanged(destroy) { + if (destroy) { + // NOOP + } else { + let mp + $createUploader(this._fieldValue__input, (res) => { + if (!mp) mp = new Mprogress({ template: 1, start: true }) + mp.set(res.percent / 100) // 0.x + }, (res) => { + mp.end() + let paths = this.state.value || [] + paths.push(res.key) + this.handleChange({ target: { value: paths } }, true) + }) + } } removeItem(item) { @@ -636,11 +664,6 @@ class RbFormImage extends RbFormElement { if (this.__minUpload > 0 && ups < this.__minUpload) return `至少需要上传 ${this.__minUpload} 个` if (this.__maxUpload < ups) return `最多允许上传 ${this.__maxUpload} 个` } - - setValue(val) { - val = JSON.parse(val || '[]') - this.handleChange({ target: { value: val } }, true) - } } class RbFormFile extends RbFormImage { @@ -673,8 +696,7 @@ class RbFormFile extends RbFormImage { renderViewElement() { const value = this.state.value - if (!value || value.length === 0) return super.renderViewElement() - + if ($empty(value)) return super.renderViewElement() return
{value.map((item) => { let fileName = $fileCutName(item) @@ -719,33 +741,23 @@ class RbFormPickList extends RbFormElement { } renderViewElement() { - return super.renderViewElement(__findOptionText(this.state.options, this.props.value)) + return super.renderViewElement(__findOptionText(this.state.options, this.state.value)) } - componentDidMount() { - super.componentDidMount() - if (this.state.viewMode || this.props.readonly) return + onEditModeChanged(destroy) { + if (destroy) { + super.onEditModeChanged(destroy) + } else { + this.__select2 = $(this._fieldValue).select2({ + placeholder: '选择' + this.props.label + }) + if (!this.state.value) this.__select2.val(null) - this.__select2 = $(this._fieldValue).select2({ - placeholder: '选择' + this.props.label - }) - - $setTimeout(() => { - // No value - if (!this.props.$$$parent.isNew && !this.props.value) this.__select2.val(null) - - let that = this + const that = this this.__select2.on('change', function (e) { let val = e.target.value that.handleChange({ target: { value: val } }, true) }).trigger('change') - }, 100) - } - - componentWillUnmount() { - if (this.__select2) { - this.__select2.select2('destroy') - this.__select2 = null } } @@ -771,7 +783,7 @@ class RbFormReference extends RbFormElement { renderViewElement() { const value = this.state.value - if (!value) return super.renderViewElement() + if ($empty(value)) return super.renderViewElement() if (typeof value === 'string') return
{value}
if (!value.id) return
{value.text}
@@ -781,45 +793,38 @@ class RbFormReference extends RbFormElement { } _clickView = (e) => window.RbViewPage && window.RbViewPage.clickView(e.target) - componentDidMount() { - super.componentDidMount() - if (this.state.viewMode || this.props.readonly) return - - const entity = this.props.$$$parent.props.entity - const field = this.props.field - - this.__select2 = $initReferenceSelect2(this._fieldValue, { - name: field, - label: this.props.label, - entity: entity - }) - - $setTimeout(() => { - const val = this.props.value - if (val) { - let o = new Option(val.text, val.id, true, true) - this.__select2.append(o).trigger('change') - } - - let that = this - this.__select2.on('change', function (e) { - let v = e.target.value - if (v) { - $.post(`${rb.baseUrl}/commons/search/recently-add?id=${v}`) - // 字段回填 - $.post(`${rb.baseUrl}/app/entity/extras/fillin-value?entity=${entity}&field=${that.props.field}&source=${v}`, (res) => { - res.error_code === 0 && res.data.length > 0 && that.props.$$$parent.setAutoFillin(res.data) - }) - } - that.handleChange({ target: { value: v } }, true) + onEditModeChanged(destroy) { + if (destroy) { + super.onEditModeChanged(destroy) + } else { + const entity = this.props.$$$parent.props.entity + const field = this.props.field + this.__select2 = $initReferenceSelect2(this._fieldValue, { + name: field, + label: this.props.label, + entity: entity }) - }, 100) - } - componentWillUnmount() { - if (this.__select2) { - this.__select2.select2('destroy') - this.__select2 = null + $setTimeout(() => { + const val = this.props.value + if (val) { + let o = new Option(val.text, val.id, true, true) + this.__select2.append(o).trigger('change') + } + + const that = this + this.__select2.on('change', function (e) { + let v = e.target.value + if (v) { + $.post(`${rb.baseUrl}/commons/search/recently-add?id=${v}`) + // 字段回填 + $.post(`${rb.baseUrl}/app/entity/extras/fillin-value?entity=${entity}&field=${that.props.field}&source=${v}`, (res) => { + res.error_code === 0 && res.data.length > 0 && that.props.$$$parent.setAutoFillin(res.data) + }) + } + that.handleChange({ target: { value: v } }, true) + }) + }, 100) } } @@ -847,43 +852,39 @@ class RbFormClassification extends RbFormElement { return