better FROM/LIST

This commit is contained in:
Zixin 2022-01-09 14:11:49 +08:00
parent c87381e5fe
commit 29cc020b63
8 changed files with 2102 additions and 172 deletions

1934
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -412,7 +412,7 @@ class ApprovalApproveForm extends ApprovalUsersForm {
_renderEditableForm() {
const fake = {
state: { id: this.props.id, __formModel: {} },
state: { id: this.props.id },
}
return (
<div className="form-group">
@ -478,7 +478,7 @@ class ApprovalApproveForm extends ApprovalUsersForm {
// eslint-disable-next-line no-undef
class EditableForm extends RbForm {
constructor(props) {
super(props)
super({ ...props, rawModel: {} })
}
renderFormAction() {

View file

@ -1058,7 +1058,7 @@ class RbGritter extends React.Component {
*
* @param {*} jsx
* @param {*} target id or object of element (or function of callback)
* @param {*} call callback
* @param {*} call callback on mounted
*/
const renderRbcomp = function (jsx, target, call) {
if (typeof target === 'function') {
@ -1079,6 +1079,8 @@ const renderRbcomp = function (jsx, target, call) {
} else if (target instanceof $) {
target = target[0]
}
// ReactDOM.render(<React.StrictMode>{jsx}</React.StrictMode>, target, call)
ReactDOM.render(jsx, target, call)
return target
}

View file

@ -7,6 +7,10 @@ See LICENSE and COMMERCIAL in the project root for license information.
/* global FieldValueSet */
// 列表公共操作
const _RbList = function () {
return RbListPage._RbList || {}
}
// ~~ 高级查询操作类
const AdvFilters = {
@ -18,18 +22,18 @@ const AdvFilters = {
this.__el = $(el)
this.__entity = entity
this.__el.find('.J_advfilter').click(() => {
this.__el.find('.J_advfilter').on('click', () => {
this.showAdvFilter(null, this.current)
this.current = null
})
const $all = $('.adv-search .dropdown-item:eq(0)')
$all.click(() => this._effectFilter($all, 'aside'))
$all.on('click', () => this._effectFilter($all, 'aside'))
this.loadFilters()
},
loadFilters() {
const lastFilter = $storage.get(RbListPage._RbList.__defaultFilterKey)
const lastFilter = $storage.get(_RbList().__defaultFilterKey)
const that = this
let $defaultFilter
@ -43,7 +47,7 @@ const AdvFilters = {
$(res.data).each(function () {
const item = this
const $item = $(`<div class="dropdown-item J_custom" data-id="${item.id}"><a class="text-truncate">${item.name}</a></div>`).appendTo($menu)
$item.click(() => that._effectFilter($item, 'aside'))
$item.on('click', () => that._effectFilter($item, 'aside'))
if (lastFilter === item.id) $defaultFilter = $item
@ -53,13 +57,13 @@ const AdvFilters = {
`<div class="action"><a title="${$L('修改')}"><i class="zmdi zmdi-edit"></i></a><a title="${$L('删除')}" class="danger-hover"><i class="zmdi zmdi-delete"></i></a></div>`
).appendTo($item)
$action.find('a:eq(0)').click(function () {
$action.find('a:eq(0)').on('click', function () {
that.showAdvFilter(item.id)
$('.adv-search .btn.dropdown-toggle').dropdown('toggle')
return false
})
$action.find('a:eq(1)').click(function () {
$action.find('a:eq(1)').on('click', function () {
RbAlert.create($L('确认删除此高级查询'), {
type: 'danger',
confirmText: $L('删除'),
@ -70,7 +74,7 @@ const AdvFilters = {
this.hide()
that.loadFilters()
if (lastFilter === item.id) {
RbListPage._RbList.setAdvFilter(null)
_RbList().setAdvFilter(null)
$('.adv-search .J_name').text($L('全部数据'))
}
} else {
@ -91,7 +95,7 @@ const AdvFilters = {
$ghost.removeAttr('style')
$ghost.removeAttr('data-ps-id')
$ghost.find('.ps-scrollbar-x-rail, .ps-scrollbar-y-rail').remove()
$ghost.find('.dropdown-item').click(function () {
$ghost.find('.dropdown-item').on('click', function () {
$ghost.find('.dropdown-item').removeClass('active')
$(this).addClass('active')
that._effectFilter($(this), 'aside')
@ -120,7 +124,7 @@ const AdvFilters = {
}
if (this.current === '$ALL$') this.current = null
RbListPage._RbList.setAdvFilter(this.current)
_RbList().setAdvFilter(this.current)
},
showAdvFilter(id, useCopyId) {
@ -165,7 +169,7 @@ const AdvFilters = {
$.post(url, JSON.stringify(filter), (res) => {
if (res.error_code === 0) {
$storage.set(RbListPage._RbList.__defaultFilterKey, res.data.id)
$storage.set(_RbList().__defaultFilterKey, res.data.id)
that.loadFilters()
} else {
RbHighbar.error(res.error_msg)
@ -564,7 +568,7 @@ const RbListCommon = {
// 快速查询
const $btn = $('.input-search .input-group-btn .btn'),
$input = $('.input-search input')
$btn.on('click', () => RbListPage._RbList.searchQuick())
$btn.on('click', () => _RbList().searchQuick())
$input.on('keydown', (e) => {
e.which === 13 && $btn.trigger('click')
})
@ -578,7 +582,7 @@ const RbListCommon = {
if (via) {
wpc.protocolFilter = `via:${via}`
const $cleanVia = $(`<div class="badge filter-badge">${$L('当前数据已过滤')}<a class="close" title="${$L('查看全部数据')}">&times;</a></div>`).appendTo('.dataTables_filter')
$cleanVia.find('a').click(() => {
$cleanVia.find('a').on('click', () => {
wpc.protocolFilter = null
RbListPage.reload()
$cleanVia.remove()
@ -593,9 +597,9 @@ const RbListCommon = {
// 新建
$('.J_new').on('click', () => RbFormModal.create({ title: $L('新建%s', entity[1]), entity: entity[0], icon: entity[2] }))
// 导出
$('.J_export').on('click', () => renderRbcomp(<DataExport listRef={RbListPage._RbList} entity={entity[0]} />))
$('.J_export').on('click', () => renderRbcomp(<DataExport listRef={_RbList()} entity={entity[0]} />))
// 批量修改
$('.J_batch').on('click', () => renderRbcomp(<BatchUpdate listRef={RbListPage._RbList} entity={entity[0]} />))
$('.J_batch').on('click', () => renderRbcomp(<BatchUpdate listRef={_RbList()} entity={entity[0]} />))
// 自动打开新建
if (location.hash === '#!/New') {

View file

@ -20,12 +20,12 @@ class RbList extends React.Component {
constructor(props) {
super(props)
this.__defaultFilterKey = `AdvFilter-${this.props.config.entity}`
this.__sortFieldKey = `SortField-${this.props.config.entity}`
this.__columnWidthKey = `ColumnWidth-${this.props.config.entity}.`
this.__defaultFilterKey = `AdvFilter-${props.config.entity}`
this.__sortFieldKey = `SortField-${props.config.entity}`
this.__columnWidthKey = `ColumnWidth-${props.config.entity}.`
const sort = ($storage.get(this.__sortFieldKey) || ':').split(':')
const fields = props.config.fields
const fields = props.config.fields || []
for (let i = 0; i < fields.length; i++) {
const cw = $storage.get(this.__columnWidthKey + fields[i].field)
if (!!cw && ~~cw >= COLUMN_MIN_WIDTH) fields[i].width = ~~cw
@ -34,7 +34,7 @@ class RbList extends React.Component {
if (['SIGN', 'N2NREFERENCE', 'MULTISELECT', 'FILE', 'IMAGE', 'AVATAR'].includes(fields[i].type)) fields[i].unsort = true
}
props.config.fields = null
delete props.config.fields
this.state = { ...props, fields: fields, rowsData: [], pageNo: 1, pageSize: 20, inLoad: true }
this.__defaultColumnWidth = $('#react-list').width() / 10
@ -955,27 +955,27 @@ const RbListPage = {
class RbViewModal extends React.Component {
constructor(props) {
super(props)
this.state = { ...props, inLoad: true, isHide: true, isDestroy: false }
this.state = { ...props, inLoad: true, isHide: true, destroy: false }
this.mcWidth = this.props.subView === true ? 1344 : 1404
if ($(window).width() < 1464) this.mcWidth -= 184
}
render() {
if (this.state.destroy) return null
return (
!this.state.isDestroy && (
<div className="modal-wrapper">
<div className="modal rbview" ref={(c) => (this._rbview = c)}>
<div className="modal-dialog">
<div className="modal-content" style={{ width: this.mcWidth }}>
<div className={'modal-body iframe rb-loading ' + (this.state.inLoad === true && 'rb-loading-active')}>
<iframe ref={(c) => (this._iframe = c)} className={this.state.isHide ? 'invisible' : ''} src={this.state.showAfterUrl || 'about:blank'} frameBorder="0" scrolling="no" />
<RbSpinner />
</div>
<div className="modal-wrapper">
<div className="modal rbview" ref={(c) => (this._rbview = c)}>
<div className="modal-dialog">
<div className="modal-content" style={{ width: this.mcWidth }}>
<div className={'modal-body iframe rb-loading ' + (this.state.inLoad === true && 'rb-loading-active')}>
<iframe ref={(c) => (this._iframe = c)} className={this.state.isHide ? 'invisible' : ''} src={this.state.showAfterUrl || 'about:blank'} frameBorder="0" scrolling="no" />
<RbSpinner />
</div>
</div>
</div>
</div>
)
</div>
)
}
@ -993,7 +993,7 @@ class RbViewModal extends React.Component {
// SubView 子视图不保持
if (that.state.disposeOnHide === true) {
$root.modal('dispose')
that.setState({ isDestroy: true }, () => {
that.setState({ destroy: true }, () => {
RbViewModal.holder(that.state.id, 'DISPOSE')
$unmount(rootWrap)
})
@ -1029,6 +1029,7 @@ class RbViewModal extends React.Component {
if (url && url === this.state.url) urlChanged = false
ext = ext || {}
url = url || this.state.url
this.__urlChanged = urlChanged
this.setState({ ...ext, url: url, inLoad: urlChanged, isHide: urlChanged }, () => {
$(this._rbview).modal({ show: true, backdrop: true, keyboard: false })

View file

@ -4,10 +4,11 @@ Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights re
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
See LICENSE and COMMERCIAL in the project root for license information.
*/
/* eslint-disable no-unused-vars */
// 表单附加操作可在其他页面独立引入
// 分类数据选择
// eslint-disable-next-line no-unused-vars
// ~~ 分类数据选择
class ClassificationSelector extends React.Component {
constructor(props) {
super(props)
@ -154,14 +155,12 @@ class ClassificationSelector extends React.Component {
}
}
// eslint-disable-next-line no-unused-vars
// ~~ 引用字段搜索
window.referenceSearch__call = function (selected) {}
// eslint-disable-next-line no-unused-vars
window.referenceSearch__dlg
// ~~ 引用字段搜索
// see `reference-search.html`
// eslint-disable-next-line no-unused-vars
class ReferenceSearcher extends RbModal {
constructor(props) {
super(props)
@ -189,7 +188,6 @@ class ReferenceSearcher extends RbModal {
componentDidMount() {
super.componentDidMount()
// eslint-disable-next-line no-unused-vars
window.referenceSearch__dlg = this
}
@ -198,8 +196,8 @@ class ReferenceSearcher extends RbModal {
}
}
// 删除确认
// eslint-disable-next-line no-unused-vars
// ~~ 删除确认
class DeleteConfirm extends RbAlert {
constructor(props) {
super(props)
@ -300,7 +298,8 @@ class DeleteConfirm extends RbAlert {
}
}
// 百度地图
// ~~ 百度地图
// https://mapopen-pub-jsapi.bj.bcebos.com/jsapi/reference/jsapi_webgl_1_0.html#a1b0
class BaiduMap extends React.Component {
render() {
@ -400,7 +399,6 @@ class BaiduMap extends React.Component {
}
}
// eslint-disable-next-line no-unused-vars
class BaiduMapModal extends RbModal {
constructor(props) {
super(props)
@ -510,8 +508,8 @@ class BaiduMapModal extends RbModal {
}
}
// 签名板
// eslint-disable-next-line no-unused-vars
// ~~ 签名板
class SignPad extends React.Component {
state = { ...this.props }
@ -585,3 +583,57 @@ class SignPad extends React.Component {
$(this._$dlg).modal('show')
}
}
// ~~ 重复记录查看
class RepeatedViewer extends RbModalHandler {
constructor(props) {
super(props)
}
render() {
const data = this.props.data
return (
<RbModal ref={(c) => (this._dlg = c)} title={$L('存在 %d 条重复记录', this.props.data.length - 1)} disposeOnHide={true} colored="warning">
<table className="table table-striped table-hover table-sm dialog-table">
<thead>
<tr>
{data[0].map((item, idx) => {
if (idx === 0) return null
return <th key={`field-${idx}`}>{item}</th>
})}
<th width="30" />
</tr>
</thead>
<tbody>
{data.map((item, idx) => {
if (idx === 0) return null
return this.renderRow(item, idx)
})}
</tbody>
</table>
</RbModal>
)
}
renderRow(item, idx) {
return (
<tr key={`row-${idx}`}>
{item.map((o, i) => {
if (i === 0) return null
return <td key={`col-${idx}-${i}`}>{o || <span className="text-muted">{$L('无')}</span>}</td>
})}
<td className="actions">
<a className="icon" onClick={() => this.openView(item[0])} title={$L('查看详情')}>
<i className="zmdi zmdi-open-in-new" />
</a>
</td>
</tr>
)
}
openView(id) {
if (window.RbViewModal) window.RbViewModal.create({ id: id, entity: this.props.entity })
else window.open(`${rb.baseUrl}/app/list-and-view?id=${id}`)
}
}

View file

@ -4,7 +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).
See LICENSE and COMMERCIAL in the project root for license information.
*/
/* global SimpleMDE */
/* global SimpleMDE, RepeatedViewer */
const TYPE_DIVIDER = '$DIVIDER$'
@ -17,36 +17,35 @@ class RbFormModal extends React.Component {
}
render() {
const maxWidth = { maxWidth: this.props.width || 1064 }
if (this.state.destroy) return null
const mw = { maxWidth: this.props.width || 1064 }
return (
this.state.isDestroy !== true && (
<div className="modal-wrapper">
<div className="modal rbmodal colored-header colored-header-primary" ref={(c) => (this._rbmodal = c)}>
<div className="modal-dialog" style={maxWidth}>
<div className="modal-content" style={maxWidth}>
<div className="modal-header modal-header-colored">
{this.state.icon && <span className={'icon zmdi zmdi-' + this.state.icon} />}
<h3 className="modal-title">{this.state.title || $L('新建')}</h3>
{rb.isAdminUser && (
<a className="close s" href={`${rb.baseUrl}/admin/entity/${this.state.entity}/form-design`} title={$L('表单设计')} target="_blank">
<span className="zmdi zmdi-settings" />
</a>
)}
<button className="close md-close" type="button" onClick={() => this.hide()}>
<span className="zmdi zmdi-close" />
</button>
</div>
<div className={'modal-body rb-loading' + (this.state.inLoad ? ' rb-loading-active' : '')}>
{this.state.alertMessage && <div className="alert alert-warning rbform-alert">{this.state.alertMessage}</div>}
{this.state.formComponent}
{this.state.inLoad && <RbSpinner />}
</div>
<div className="modal-wrapper">
<div className="modal rbmodal colored-header colored-header-primary" ref={(c) => (this._rbmodal = c)}>
<div className="modal-dialog" style={mw}>
<div className="modal-content" style={mw}>
<div className="modal-header modal-header-colored">
{this.state.icon && <span className={`icon zmdi zmdi-${this.state.icon}`} />}
<h3 className="modal-title">{this.state.title || $L('新建')}</h3>
{rb.isAdminUser && (
<a className="close s" href={`${rb.baseUrl}/admin/entity/${this.state.entity}/form-design`} title={$L('表单设计')} target="_blank">
<span className="zmdi zmdi-settings" />
</a>
)}
<button className="close md-close" type="button" onClick={() => this.hide()}>
<span className="zmdi zmdi-close" />
</button>
</div>
<div className={`modal-body rb-loading ${this.state.inLoad ? 'rb-loading-active' : ''}`}>
{this.state.alertMessage && <div className="alert alert-warning rbform-alert">{this.state.alertMessage}</div>}
{this.state.formComponent}
{this.state.inLoad && <RbSpinner />}
</div>
</div>
</div>
</div>
)
</div>
)
}
@ -73,29 +72,32 @@ class RbFormModal extends React.Component {
const id = this.state.id || ''
const initialValue = this.state.initialValue || {} // 默认值填充仅新建有效
const that = this
$.post(`/app/${entity}/form-model?id=${id}`, JSON.stringify(initialValue), function (res) {
$.post(`/app/${entity}/form-model?id=${id}`, JSON.stringify(initialValue), (res) => {
// 包含错误
if (res.error_code > 0 || !!res.data.error) {
const error = (res.data || {}).error || res.error_msg
that.renderFromError(error)
this.renderFromError(error)
return
}
const formModel = res.data
const FORM = (
<RbForm entity={entity} id={id} $$$parent={that}>
{res.data.elements.map((item) => {
<RbForm entity={entity} id={id} $$$parent={this} rawModel={formModel}>
{formModel.elements.map((item) => {
return detectElement(item)
})}
</RbForm>
)
that.setState({ formComponent: FORM, __formModel: res.data }, () => {
that.setState({ inLoad: false })
this.setState({ formComponent: FORM }, () => {
this.setState({ inLoad: false })
if (window.FrontJS) {
window.FrontJS.Form._trigger('open', [res.data])
}
})
that.__lastModified = res.data.lastModified || 0
this.__lastModified = res.data.lastModified || 0
})
}
@ -105,7 +107,7 @@ class RbFormModal extends React.Component {
<div className="icon">
<i className="zmdi zmdi-alert-triangle" />
</div>
<div className="message" dangerouslySetInnerHTML={{ __html: `<strong>${$L('抱歉!')}</strong> ` + message }} />
<div className="message" dangerouslySetInnerHTML={{ __html: `<strong>${$L('抱歉!')}</strong> ${message}` }} />
</div>
)
this.setState({ formComponent: error }, () => this.setState({ inLoad: false }))
@ -119,11 +121,11 @@ class RbFormModal extends React.Component {
const stateNew = [state.id, state.entity, state.initialValue]
const stateOld = [this.state.id, this.state.entity, this.state.initialValue]
if (this.state.isDestroy === true || JSON.stringify(stateNew) !== JSON.stringify(stateOld)) {
if (this.state.destroy === true || JSON.stringify(stateNew) !== JSON.stringify(stateOld)) {
state = { formComponent: null, initialValue: null, inLoad: true, ...state }
this.setState(state, () => this.showAfter({ isDestroy: false }, true))
this.setState(state, () => this.showAfter({ destroy: false }, true))
} else {
this.showAfter({ ...state, isDestroy: false })
this.showAfter({ ...state, destroy: false })
this.checkDrityData()
}
}
@ -137,10 +139,11 @@ class RbFormModal extends React.Component {
checkDrityData() {
if (!this.__lastModified || !this.state.id) return
$.get(`/app/entity/extras/record-last-modified?id=${this.state.id}`, (res) => {
if (res.error_code === 0) {
if (res.data.lastModified !== this.__lastModified) {
// this.setState({ alertMessage: <p>记录已由其他用户编辑过<a onClick={() => this.__refresh()}>点击此处</a>查看最新数据</p> })
// this.setState({ alertMessage: <p>记录已由其他用户编辑过<a onClick={() => this._refresh()}>点击此处</a>查看最新数据</p> })
this._refresh()
}
} else if (res.error_msg === 'NO_EXISTS') {
@ -150,15 +153,16 @@ class RbFormModal extends React.Component {
}
_refresh() {
const hold = { id: this.state.id, entity: this.state.entity }
const hs = { id: this.state.id, entity: this.state.entity }
this.setState({ id: null, alertMessage: null }, () => {
this.show(hold)
this.show(hs)
})
}
hide(destroy) {
$(this._rbmodal).modal('hide')
const state = { isDestroy: destroy === true }
const state = { destroy: destroy === true }
if (destroy === true) state.id = null
this.setState(state)
}
@ -166,10 +170,10 @@ class RbFormModal extends React.Component {
// -- Usage
/**
* @param {*} props
* @param {*} newDlg
* @param {*} forceNew
*/
static create(props, newDlg) {
if (newDlg === true) {
static create(props, forceNew) {
if (forceNew === true) {
renderRbcomp(<RbFormModal {...props} />)
return
}
@ -192,7 +196,7 @@ class RbForm extends React.Component {
this.state = { ...props }
this.__FormData = {}
const iv = props.$$$parent.state.__formModel.initialValue
const iv = props.rawModel.initialValue
if (iv) {
for (let k in iv) {
const val = iv[k]
@ -201,7 +205,7 @@ class RbForm extends React.Component {
}
this.isNew = !props.$$$parent.state.id
this.setFieldValue = this.setFieldValue.bind(this)
// this.setFieldValue = this.setFieldValue.bind(this)
}
render() {
@ -219,10 +223,8 @@ class RbForm extends React.Component {
}
renderFormAction() {
const pmodel = this.props.$$$parent.state.__formModel
const moreActions = []
if (pmodel.isDetail === true) {
if (this.props.rawModel.isDetail === true) {
moreActions.push(
<a key="Action101" className="dropdown-item" onClick={() => this.post(RbForm.__NEXT_ADDDETAIL)}>
{$L('保存并继续添加')}
@ -306,7 +308,9 @@ class RbForm extends React.Component {
/**
* @next {Number}
*/
post = (next) => setTimeout(() => this._post(next), 30)
post(next) {
setTimeout(() => this._post(next), 30)
}
_post(next) {
const data = {}
@ -336,11 +340,10 @@ class RbForm extends React.Component {
RbForm.postAfter({ ...res.data, isNew: !this.state.id }, next)
const recordId = res.data.id
const pState = this.props.$$$parent.state
if (next === RbForm.__NEXT_ADDDETAIL) {
const iv = { $MAINID$: recordId }
const dm = pState.__formModel.detailMeta
const iv = { '$MAINID$': recordId }
const dm = this.props.rawModel.detailMeta
RbFormModal.create({
title: $L('添加%s', dm.entityLabel),
entity: dm.entity,
@ -348,7 +351,7 @@ class RbForm extends React.Component {
initialValue: iv,
})
} else if (next === RbForm.__NEXT_VIEW && window.RbViewModal) {
window.RbViewModal.create({ id: recordId, entity: pState.entity })
window.RbViewModal.create({ id: recordId, entity: this.state.entity })
}
// ...
@ -456,6 +459,7 @@ class RbFormElement extends React.Component {
*/
renderElement() {
const value = arguments.length > 0 ? arguments[0] : this.state.value
return (
<input
ref={(c) => (this._fieldValue = c)}
@ -477,18 +481,15 @@ class RbFormElement extends React.Component {
renderViewElement() {
let value = arguments.length > 0 ? arguments[0] : this.state.value
if (value && $empty(value)) value = null
return (
<React.Fragment>
<div className="form-control-plaintext">{value || <span className="text-muted">{$L('无')}</span>}</div>
</React.Fragment>
)
return <div className="form-control-plaintext">{value || <span className="text-muted">{$L('无')}</span>}</div>
}
/**
* 修改值表单组件字段值变化应调用此方法
*
* @param {*} e
* @param {*} checkValue
* @param {Event} e
* @param {Boolean} checkValue
*/
handleChange(e, checkValue) {
const val = e.target.value
@ -540,7 +541,7 @@ class RbFormElement extends React.Component {
/**
* 视图编辑-编辑状态改变
*
* @param {*} destroy
* @param {Boolean} destroy
*/
onEditModeChanged(destroy) {
if (destroy) {
@ -560,7 +561,7 @@ class RbFormElement extends React.Component {
/**
* 视图编辑-编辑模式
*
* @param {*} editMode
* @param {Boolean} editMode
*/
toggleEditMode(editMode) {
// if (editMode) {
@ -2043,56 +2044,3 @@ const __addRecentlyUse = function (id) {
if (!id) return
$.post(`/commons/search/recently-add?id=${id}`)
}
// ~ 重复记录查看
class RepeatedViewer extends RbModalHandler {
constructor(props) {
super(props)
}
render() {
const data = this.props.data
return (
<RbModal ref={(c) => (this._dlg = c)} title={$L('存在 %d 条重复记录', this.props.data.length - 1)} disposeOnHide={true} colored="warning">
<table className="table table-striped table-hover table-sm dialog-table">
<thead>
<tr>
{data[0].map((item, idx) => {
if (idx === 0) return null
return <th key={`field-${idx}`}>{item}</th>
})}
<th width="30" />
</tr>
</thead>
<tbody>
{data.map((item, idx) => {
if (idx === 0) return null
return this.renderRow(item, idx)
})}
</tbody>
</table>
</RbModal>
)
}
renderRow(item, idx) {
return (
<tr key={`row-${idx}`}>
{item.map((o, i) => {
if (i === 0) return null
return <td key={`col-${idx}-${i}`}>{o || <span className="text-muted">{$L('无')}</span>}</td>
})}
<td className="actions">
<a className="icon" onClick={() => this.openView(item[0])} title={$L('查看详情')}>
<i className="zmdi zmdi-open-in-new" />
</a>
</td>
</tr>
)
}
openView(id) {
if (window.RbViewModal) window.RbViewModal.create({ id: id, entity: this.props.entity })
else window.open(`${rb.baseUrl}/app/list-and-view?id=${id}`)
}
}

View file

@ -1,9 +1,2 @@
/*!
* Beagle v1.5.0
* https://foxythemes.net
*
* Copyright (c) 2018 Foxy Themes
*/
/* perfect-scrollbar v0.6.16 */
.ps-container{-ms-touch-action:auto;touch-action:auto;overflow:hidden !important;-ms-overflow-style:none}@supports (-ms-overflow-style: none){.ps-container{overflow:auto !important}}@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none){.ps-container{overflow:auto !important}}.ps-container.ps-active-x>.ps-scrollbar-x-rail,.ps-container.ps-active-y>.ps-scrollbar-y-rail{display:block;background-color:transparent}.ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail{background-color:#eee;opacity:.9}.ps-container.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x{background-color:#999;height:11px}.ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail{background-color:#eee;opacity:.9}.ps-container.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y{background-color:#999;width:11px}.ps-container>.ps-scrollbar-x-rail{display:none;position:absolute;opacity:0;-webkit-transition:background-color .2s linear, opacity .2s linear;-o-transition:background-color .2s linear, opacity .2s linear;-moz-transition:background-color .2s linear, opacity .2s linear;transition:background-color .2s linear, opacity .2s linear;bottom:0px;height:15px}.ps-container>.ps-scrollbar-x-rail>.ps-scrollbar-x{position:absolute;background-color:#aaa;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;-o-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;-moz-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;bottom:2px;height:6px}.ps-container>.ps-scrollbar-x-rail:hover>.ps-scrollbar-x,.ps-container>.ps-scrollbar-x-rail:active>.ps-scrollbar-x{height:11px}.ps-container>.ps-scrollbar-y-rail{display:none;position:absolute;opacity:0;-webkit-transition:background-color .2s linear, opacity .2s linear;-o-transition:background-color .2s linear, opacity .2s linear;-moz-transition:background-color .2s linear, opacity .2s linear;transition:background-color .2s linear, opacity .2s linear;right:0;width:15px}.ps-container>.ps-scrollbar-y-rail>.ps-scrollbar-y{position:absolute;background-color:#aaa;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, -webkit-border-radius .2s ease-in-out;-o-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;-moz-transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out;transition:background-color .2s linear, height .2s linear, width .2s ease-in-out, border-radius .2s ease-in-out, -webkit-border-radius .2s ease-in-out, -moz-border-radius .2s ease-in-out;right:2px;width:6px}.ps-container>.ps-scrollbar-y-rail:hover>.ps-scrollbar-y,.ps-container>.ps-scrollbar-y-rail:active>.ps-scrollbar-y{width:11px}.ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail{background-color:#eee;opacity:.9}.ps-container:hover.ps-in-scrolling.ps-x>.ps-scrollbar-x-rail>.ps-scrollbar-x{background-color:#999;height:11px}.ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail{background-color:#eee;opacity:.9}.ps-container:hover.ps-in-scrolling.ps-y>.ps-scrollbar-y-rail>.ps-scrollbar-y{background-color:#999;width:11px}.ps-container:hover>.ps-scrollbar-x-rail,.ps-container:hover>.ps-scrollbar-y-rail{opacity:.6}.ps-container:hover>.ps-scrollbar-x-rail:hover{background-color:#eee;opacity:.9}.ps-container:hover>.ps-scrollbar-x-rail:hover>.ps-scrollbar-x{background-color:#999}.ps-container:hover>.ps-scrollbar-y-rail:hover{background-color:#eee;opacity:.9}.ps-container:hover>.ps-scrollbar-y-rail:hover>.ps-scrollbar-y{background-color:#999}