Quick query area (#445)

* form col-4

* form layout col-4

* chart: TIME

* quickFieldsLabel

* record-list2

* Update @rbv

* view style

* fix: reference-search

* style multi-values

* Update reference-search.html

* style search-input-gs

* quick-filter-pane

* Update Application.java
This commit is contained in:
RB 2022-03-28 13:02:01 +08:00 committed by GitHub
parent eb77a77f41
commit 1b165b1e4c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 384 additions and 118 deletions

2
@rbv

@ -1 +1 @@
Subproject commit 23e80f0d07e09466c45460b62e7966f4ecf33e0d
Subproject commit 18ed152e76bc042c6d07b81158dc807f7dae1f6e

View file

@ -45,6 +45,7 @@ import com.rebuild.utils.codec.RbRecordCodec;
import com.rebuild.web.OnlineSessionStore;
import com.rebuild.web.RebuildWebConfigurer;
import lombok.extern.slf4j.Slf4j;
import net.sf.ehcache.CacheManager;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
@ -92,6 +93,9 @@ public class Application implements ApplicationListener<ApplicationStartedEvent>
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteMapNullValue.getMask();
// for ehcache
System.setProperty(CacheManager.ENABLE_SHUTDOWN_HOOK_PROPERTY, "true");
}
// 系统状态

View file

@ -50,6 +50,13 @@ public class Dimension extends Axis {
return String.format("DATE_FORMAT(%s,'%s')", super.getSqlName(), "%Y-%m-%d");
}
} else if (dt == DisplayType.TIME) {
if (getFormatCalc() == FormatCalc.H) {
return String.format("DATE_FORMAT(%s,'%s')", super.getSqlName(), "%H");
} else {
return String.format("DATE_FORMAT(%s,'%s')", super.getSqlName(), "%H:%i");
}
} else if (dt == DisplayType.CLASSIFICATION
&& getFormatCalc() != null && getFormatCalc().name().startsWith("L")) {
int useLevel = ClassificationManager.instance.getOpenLevel(getField()) + 1;

View file

@ -19,9 +19,10 @@ public enum FormatCalc {
SUM("求和"), AVG("平均值"), MAX("最大数"), MIN("最小数"), COUNT("计数"),
COUNT2("去重计数"),
// 日期字段
// 日期时间字段
Y(""), M(""), D(""), H(""),
Q(""),
I("时分"),
// 分类字段
L1("一级"), L2("二级"), L3("三级"), L4("四级"),

View file

@ -138,6 +138,8 @@ public class ChartDesignController extends EntityController {
String type = "text";
if (dt == DisplayType.DATE || dt == DisplayType.DATETIME) {
type = "date";
} else if (dt == DisplayType.TIME) {
type = "time";
} else if (dt == DisplayType.NUMBER || dt == DisplayType.DECIMAL) {
type = "num";
} else if (dt == DisplayType.CLASSIFICATION) {

View file

@ -15,22 +15,29 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rebuild.core.Application;
import com.rebuild.core.configuration.general.DataListManager;
import com.rebuild.core.metadata.EntityHelper;
import com.rebuild.core.metadata.MetadataHelper;
import com.rebuild.core.metadata.easymeta.EasyEntity;
import com.rebuild.core.metadata.easymeta.EasyField;
import com.rebuild.core.metadata.easymeta.EasyMetaFactory;
import com.rebuild.core.metadata.impl.EasyEntityConfigProps;
import com.rebuild.core.privileges.bizz.ZeroEntry;
import com.rebuild.core.service.query.ParseHelper;
import com.rebuild.core.support.general.DataListBuilder;
import com.rebuild.core.support.general.DataListBuilderImpl;
import com.rebuild.core.support.i18n.Language;
import com.rebuild.web.EntityController;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* 数据列表
@ -61,7 +68,7 @@ public class GeneralListController extends EntityController {
listMode = ObjectUtils.toInt(mainEntity.getExtraAttr(EasyEntityConfigProps.ADV_LIST_MODE), 1);
}
if (listMode == 2) {
listPage = "/general/record-list-2"; // Mode2
listPage = "/general/record-list2"; // Mode2
}
ModelAndView mv = createModelAndView(listPage, entity, user);
@ -96,6 +103,9 @@ public class GeneralListController extends EntityController {
mv.getModel().put("DataListConfig", JSON.toJSONString(listConfig));
// 快速查询
mv.getModel().put("quickFieldsLabel", getQuickFieldsLabel(listEntity));
return mv;
}
@ -106,15 +116,7 @@ public class GeneralListController extends EntityController {
return builder.getJSONResult();
}
/**
* 检查实体页面
*
* @param user
* @param entity
* @param response
* @return
* @throws IOException
*/
// 检查实体页面
static Entity checkPageOfEntity(ID user, String entity, HttpServletResponse response) throws IOException {
if (!MetadataHelper.containsEntity(entity)) {
response.sendError(404);
@ -134,4 +136,18 @@ public class GeneralListController extends EntityController {
return checkEntity;
}
// 快速查询
static String getQuickFieldsLabel(Entity entity) {
Set<String> quickFields = ParseHelper.buildQuickFields(entity, null);
List<String> quickFieldsLabel = new ArrayList<>();
for (String qf : quickFields) {
if (qf.equalsIgnoreCase(EntityHelper.QuickCode)) continue;
if (qf.startsWith("&")) qf = qf.substring(1);
quickFieldsLabel.add(EasyMetaFactory.getLabel(entity, qf));
}
return StringUtils.join(quickFieldsLabel, " / ");
}
}

View file

@ -289,6 +289,9 @@ public class ReferenceSearchController extends EntityController {
mv.getModel().put("referenceFilter", StringUtils.EMPTY);
}
// 快速查询
mv.getModel().put("quickFieldsLabel", GeneralListController.getQuickFieldsLabel(searchEntity));
return mv;
}
}

View file

@ -282,7 +282,8 @@ a.ui-draggable.ui-draggable-dragging {
content: 'T';
}
.data-info .fields li.date a::before {
.data-info .fields li.date a::before,
.data-info .fields li.time a::before {
content: 'D';
}

View file

@ -88,13 +88,13 @@ See LICENSE and COMMERCIAL in the project root for license information.
width: 180px;
min-width: 180px;
text-align: center;
border-radius: 4px;
border-radius: 2px;
font-size: 0;
line-height: 1;
}
.dd-list .dd-item .dd-action a.colspan + .dropdown-menu > a {
width: 21%;
width: 17%;
display: inline-block;
background-color: #eee;
padding: 0;
@ -130,6 +130,10 @@ See LICENSE and COMMERCIAL in the project root for license information.
width: 100%;
}
.dd-list .dd-item .dd-action a.colspan + .dropdown-menu > a[data-colspan='9']::after {
width: 33.33%;
}
.form-preview .dd-item,
.form-preview .dd-placeholder {
float: left;
@ -160,6 +164,10 @@ See LICENSE and COMMERCIAL in the project root for license information.
width: 99% !important;
}
.form-preview .dd-item.w-33 {
width: 32.33% !important;
}
.form-preview .dd-item span.ft {
top: 13px;
}

View file

@ -869,11 +869,13 @@ select.form-control:not([disabled]) {
}
@media (min-width: 1064px) {
.form-layout > .row .form-group.col-sm-3 {
.form-layout > .row .form-group.col-sm-3,
.form-layout > .row .form-group.col-sm-4 {
padding-right: 0;
}
.form-layout > .row .form-group.col-sm-3 > .col-form-label {
width: 100px;
.form-layout > .row .form-group.col-sm-3 > .col-form-label,
.form-layout > .row .form-group.col-sm-4 > .col-form-label {
width: 110px;
}
}
@ -1095,6 +1097,15 @@ select.form-control:not([disabled]) {
cursor: default;
}
.img-field.barcode .img-thumbnail.w-auto {
width: auto;
height: auto;
}
.img-field.barcode .img-thumbnail.w-auto > img {
width: auto;
}
.file-field .file-select label.btn-secondary {
line-height: 35px;
}
@ -1173,6 +1184,10 @@ select.form-control:not([disabled]) {
cursor: pointer;
} */
.input-group.has-append {
max-width: 100%;
}
.input-group.has-append .input-group-append button {
min-width: 38px;
box-shadow: none !important;
@ -1323,6 +1338,18 @@ select.form-control:not([disabled]) {
z-index: 2;
}
.dataTables_wrapper .rb-datatable-header {
padding-top: 12px;
padding-bottom: 7px;
}
.dataTables_wrapper .rb-datatable-header .quick-filter-pane {
margin: -12px -25px 12px -23px;
background-color: #f5f5f5;
border-bottom: 1px solid #dee2e6;
padding: 12px 18px;
}
@media (max-width: 767px) {
.dataTables_wrapper .rb-datatable-header {
top: 0;
@ -1448,10 +1475,12 @@ i.dividing.ui-draggable-dragging {
.data-list .table td div .badge {
font-weight: normal;
font-size: 12px;
line-height: 1.5;
font-size: 13px;
padding: 1px 6px;
border-radius: 99px;
border-radius: 3px;
line-height: 17px;
border: 1px solid #ddd;
background-color: #fff;
}
.data-list .table td div .badge.badge-default {
@ -1719,9 +1748,9 @@ th.column-fixed {
}
.main-content > .nav-tabs-classic > li.nav-item a.nav-link {
padding: 11px 15px;
min-width: 101px;
max-width: 245px;
padding: 9px 15px;
min-width: 120px;
max-width: 240px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@ -3127,11 +3156,10 @@ form {
.search-container > .global-create > .dropdown > a {
display: inline-block;
width: 35px;
line-height: 35px;
height: 35px;
line-height: 37px;
height: 37px;
text-align: center;
color: #5a5a5a;
border-radius: 99px;
font-size: 1.73rem;
}
@ -3142,6 +3170,15 @@ form {
transition: background-color 0.3s;
}
.rb-top-header .search-input-gs {
background-color: #f5f5f5;
}
.rb-top-header .search-input-gs,
.search-container > .global-create > .dropdown > a {
border-radius: 4px;
}
.preview-modal {
position: fixed;
height: 100%;
@ -4637,3 +4674,47 @@ pre.unstyle {
.pointer {
cursor: pointer !important;
}
/* filter pane */
.dataTables_wrapper .rb-datatable-header .quick-filter-pane {
padding-top: 0;
}
.quick-filter-pane .ph-item {
background-color: #e6e6e6;
min-height: 59px;
border-radius: 3px;
}
.quick-filter-pane .ph-item-mt {
height: 12px;
overflow: hidden;
}
.quick-filter-pane .col > div > label {
display: block;
margin-bottom: 3px;
margin-top: 12px;
}
.quick-filter-pane .col > div .filter-items .col-sm-5.field {
display: none;
}
/* col-sm-4 */
.quick-filter-pane .col > div .filter-items .col-sm-2.op {
-webkit-box-flex: 0;
-ms-flex: 0 0 33.333333%;
flex: 0 0 33.333333%;
max-width: 33.333333%;
padding-left: 15px;
}
/* col-sm-8 */
.quick-filter-pane .col > div .filter-items .col-sm-5.val {
-webkit-box-flex: 0;
-ms-flex: 0 0 66.666667%;
flex: 0 0 66.666667%;
max-width: 66.666667%;
}

View file

@ -186,14 +186,17 @@ body {
}
.view-operating {
margin-top: 33px;
padding-top: 3px;
}
.view-operating > div {
margin-bottom: 15px !important;
.view-operating > div{
margin-top: 15px;
}
.view-operating > div.view-action {
margin-top: 30px;
}
.view-operating .row,
.view-operating .form-line {
margin-bottom: 10px;
}
@ -207,11 +210,11 @@ body {
}
.view-action .col-lg-6:nth-child(odd) {
padding-right: 0.384615rem;
padding-right: 5px;
}
.view-action .col-lg-6:nth-child(even) {
padding-left: 0.384615rem;
padding-left: 5px;
}
.view-action .col-lg-6 .btn {
@ -220,10 +223,6 @@ body {
text-overflow: ellipsis;
}
.view-action.empty {
margin-top: 4px;
}
@media (max-width: 991px) {
.view-action .col-lg-6 {
padding-left: 15px !important;
@ -231,10 +230,6 @@ body {
}
}
.view-operating > div.view-user {
margin-bottom: 5px !important;
}
.view-date .col-lg-4,
.view-user .col-lg-4 {
padding-right: 0;
@ -620,17 +615,14 @@ body {
.form-control-plaintext.multi-values > a,
.form-control-plaintext.multi-values > span {
border-radius: 99px;
border-radius: 3px;
padding: 1px 6px;
margin-right: 3px;
margin-bottom: 3px;
white-space: nowrap;
display: inline-block;
background-color: #eee;
}
.form-control-plaintext.multi-values > a {
background-color: #e6eff8;
border: 1px solid #ddd;
background-color: #fff;
line-height: 17px;
}
.file-list.inview {

View file

@ -108,12 +108,12 @@ $(document).ready(() => {
if (wpc.chartConfig && wpc.chartConfig.axis) {
$(wpc.chartConfig.axis.dimension).each((idx, item) => add_axis('.J_axis-dim', item))
$(wpc.chartConfig.axis.numerical).each((idx, item) => add_axis('.J_axis-num', item))
$('.chart-type>a[data-type="' + wpc.chartConfig.type + '"]').trigger('click')
$(`.chart-type>a[data-type="${wpc.chartConfig.type}"]`).trigger('click')
dataFilter = wpc.chartConfig.filter
const option = wpc.chartConfig.option || {}
for (let k in option) {
const opt = $('.chart-option input[data-name=' + k + ']')
const opt = $(`.chart-option input[data-name=${k}]`)
if (opt.length > 0) {
if (opt.attr('type') === 'checkbox') {
if ($isTrue(option[k])) opt.trigger('click')
@ -153,6 +153,7 @@ const CTs = {
M: $L('按月'),
D: $L('按日'),
H: $L('按时'),
I: $L('按时分'),
L1: $L('1级'),
L2: $L('2级'),
L3: $L('3级'),
@ -189,21 +190,24 @@ const add_axis = (target, axis) => {
calc = fieldType === 'num' ? 'SUM' : 'COUNT'
} else if (fieldType === 'date') {
calc = 'D'
} else if (fieldType === 'time') {
calc = 'I'
} else if (fieldType === 'clazz') {
calc = 'L1'
}
}
$dropdown.attr({ 'data-calc': calc, 'data-sort': sort })
fieldLabel = fieldLabel || '[' + fieldName.toUpperCase() + ']'
fieldLabel = fieldLabel || `[${fieldName.toUpperCase()}]`
if (isNumAxis) {
$dropdown.find('.J_date, .J_clazz').remove()
$dropdown.find('.J_date, .J_time, .J_clazz').remove()
if (fieldType === 'num') $dropdown.find('.J_text').remove()
else $dropdown.find('.J_num').remove()
} else {
$dropdown.find('.J_text, .J_num').remove()
if (fieldType !== 'date') $dropdown.find('.J_date').remove()
if (fieldType !== 'time') $dropdown.find('.J_time').remove()
if (fieldType !== 'clazz') $dropdown.find('.J_clazz').remove()
}
if ($dropdown.find('li:eq(0)').hasClass('dropdown-divider')) $dropdown.find('.dropdown-divider').remove()
@ -216,7 +220,7 @@ const add_axis = (target, axis) => {
const calc = $this.data('calc')
const sort = $this.data('sort')
if (calc) {
$dropdown.find('span').text(fieldLabel + (' (' + $this.text() + ')'))
$dropdown.find('span').text(`${fieldLabel} (${$this.text()})`)
$dropdown.attr('data-calc', calc)
aopts.each(function () {
if ($(this).data('calc')) $(this).removeClass('text-primary')
@ -379,7 +383,7 @@ const __buildAxisItem = (item, isNum) => {
if (isNum) {
x.calc = item.attr('data-calc')
x.scale = item.attr('data-scale')
} else if (item.data('type') === 'date' || item.data('type') === 'clazz') {
} else if (['date', 'time', 'clazz'].includes(item.data('type'))) {
x.calc = item.attr('data-calc')
}
return x

View file

@ -12,6 +12,7 @@ const COLSPANS = {
2: 'w-50',
3: 'w-75',
4: 'w-100',
9: 'w-33',
}
$(document).ready(function () {
@ -133,6 +134,7 @@ $(document).ready(function () {
if ($this.parent().hasClass('w-100')) item.colspan = 4
if ($this.parent().hasClass('w-75')) item.colspan = 3
if ($this.parent().hasClass('w-25')) item.colspan = 1
if ($this.parent().hasClass('w-33')) item.colspan = 9
const tip = $this.find('.J_tip').attr('title')
if (tip) item.tip = tip
@ -201,12 +203,17 @@ const render_item = function (data) {
if (data.displayType) {
$(`<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)
const $colspan = $(
'<div class="dropdown-menu dropdown-menu-right"><a data-colspan="1" title="1/4"></a><a data-colspan="2" title="2/4"></a><a data-colspan="3" title="3/4"></a><a data-colspan="4" title="4/4"></a></div>'
).appendTo($action)
const $colspan = $('<div class="dropdown-menu dropdown-menu-right"></div>').appendTo($action)
$('<a data-colspan="1" title="4"></a>').appendTo($colspan)
$('<a data-colspan="9" title="3"></a>').appendTo($colspan)
$('<a data-colspan="2" title="2"></a>').appendTo($colspan)
$('<a data-colspan="4" title="1"></a>').appendTo($colspan)
$('<a data-colspan="3" title="3/4"></a>').appendTo($colspan)
$colspan.find('a').on('click', function () {
const colspan = ~~$(this).data('colspan')
$item.removeClass('w-25 w-50 w-75 w-100').addClass(COLSPANS[colspan])
$item.removeClass('w-25 w-50 w-75 w-100 w-33').addClass(COLSPANS[colspan])
})
$(`<a title="${$L('修改')}"><i class="zmdi zmdi-edit"></i></a>`)

View file

@ -18,16 +18,20 @@ class PreviewTable extends React.Component {
let currentSpan = 0
this.props.data.elements.forEach((c) => {
let colspan = c.colspan || 2
if (c.isFull || c.colspan === 4 || c.field === '$DIVIDER$') colspan = 4
// set
let colspan = 6
if (c.isFull || c.colspan === 4 || c.field === '$DIVIDER$') colspan = 12
else if (c.colspan === 3) colspan = 9
else if (c.colspan === 2) colspan = 6
else if (c.colspan === 1) colspan = 3
else if (c.colspan === 9) colspan = 4
// correct
c.colspan = colspan
if (currentSpan + colspan > 4) {
if (currentSpan + colspan > 12) {
rows.push(currentRow)
currentRow = [c]
currentSpan = colspan
} else if (currentSpan + colspan === 4) {
} else if (currentSpan + colspan === 12) {
currentRow.push(c)
rows.push(currentRow)
currentRow = []
@ -44,14 +48,9 @@ class PreviewTable extends React.Component {
<table className="table table-bordered table-sm table-fixed">
<tbody>
<tr className="hide">
<td />
<td />
<td />
<td />
<td />
<td />
<td />
<td />
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((i) => {
return <td data-col={i} key={i} />
})}
</tr>
{rows.map((row, idx) => {
const k = `row-${idx}-`
@ -71,38 +70,27 @@ class PreviewTable extends React.Component {
if (c1.field === '$DIVIDER$') {
cells.push(
<th colSpan="8" className="divider">
<th colSpan="12" className="divider">
{c1.label}
</th>
)
return cells
}
function _colSpan(n) {
if (n === 4) return 7
else if (n === 3) return 5
else if (n === 2) return 3
else return 1
}
cells.push(<th>{c1.label}</th>)
let colSpan = _colSpan(c2 ? c1.colspan : 4)
cells.push(<td colSpan={colSpan}>{this.formatValue(c1)}</td>)
cells.push(<td colSpan={c2 ? c1.colspan - 1 : 11}>{this.formatValue(c1)}</td>)
if (!c2) return cells
cells.push(<th>{c2.label}</th>)
colSpan = _colSpan(c3 ? c2.colspan : 4 - c1.colspan)
cells.push(<td colSpan={colSpan}>{this.formatValue(c2)}</td>)
cells.push(<td colSpan={c3 ? c2.colspan - 1 : 11 - c1.colspan}>{this.formatValue(c2)}</td>)
if (!c3) return cells
cells.push(<th>{c3.label}</th>)
colSpan = _colSpan(c4 ? c3.colspan : 4 - c1.colspan - c2.colspan)
cells.push(<td colSpan={colSpan}>{this.formatValue(c3)}</td>)
cells.push(<td colSpan={c4 ? c3.colspan - 1 : 11 - c1.colspan - c2.colspan}>{this.formatValue(c3)}</td>)
if (!c4) return cells
cells.push(<th>{c4.label}</th>)
colSpan = 1
cells.push(<td colSpan={colSpan}>{this.formatValue(c4)}</td>)
cells.push(<td colSpan={2}>{this.formatValue(c4)}</td>)
return cells
}

View file

@ -64,7 +64,7 @@ class AdvFilter extends React.Component {
)}
<div className="adv-filter">
<div className="filter-items" onKeyPress={this.searchByKey}>
<div className="filter-items" onKeyPress={(e) => this.searchByKey(e)}>
{this.state.items}
<div className="item plus">
@ -270,12 +270,12 @@ class AdvFilter extends React.Component {
return adv
}
searchByKey = (e) => {
searchByKey(e) {
if (this.props.fromList !== true || e.which !== 13) return // Not [Enter]
this.searchNow()
}
searchNow = () => {
searchNow() {
const adv = this.toFilterJson(true)
if (!!adv && window.RbListPage) RbListPage._RbList.search(adv, true)
}
@ -435,8 +435,15 @@ class FilterItem extends React.Component {
if (this.state.op === 'BW') {
valComp = (
<div className="val-range">
<input className="form-control form-control-sm" ref={(c) => (this._filterVal = c)} onChange={this.valueHandle} onBlur={this.valueCheck} value={this.state.value || ''} />
<input className="form-control form-control-sm" ref={(c) => (this._filterVal2 = c)} onChange={this.valueHandle} onBlur={this.valueCheck} value={this.state.value2 || ''} data-at="2" />
<input className="form-control form-control-sm" ref={(c) => (this._filterVal = c)} onChange={(e) => this.valueHandle(e)} onBlur={(e) => this.valueCheck(e)} value={this.state.value || ''} />
<input
className="form-control form-control-sm"
ref={(c) => (this._filterVal2 = c)}
onChange={(e) => this.valueHandle(e)}
onBlur={(e) => this.valueCheck(e)}
value={this.state.value2 || ''}
data-at="2"
/>
<span>{$L('起')}</span>
<span className="end">{$L('止')}</span>
</div>
@ -464,7 +471,9 @@ class FilterItem extends React.Component {
</select>
)
} else {
valComp = <input className="form-control form-control-sm" ref={(c) => (this._filterVal = c)} onChange={this.valueHandle} onBlur={this.valueCheck} value={this.state.value || ''} />
valComp = (
<input className="form-control form-control-sm" ref={(c) => (this._filterVal = c)} onChange={(e) => this.valueHandle(e)} onBlur={(e) => this.valueCheck(e)} value={this.state.value || ''} />
)
}
return valComp
@ -584,24 +593,26 @@ class FilterItem extends React.Component {
this.removeBool()
}
valueHandle = (e) => {
valueHandle(e) {
const v = e.target.value
if (~~e.target.dataset.at === 2) this.setState({ value2: v })
else this.setState({ value: v })
}
// @e = el or event
valueCheck = (e) => {
valueCheck(e) {
const $el = e.target ? $(e.target) : e
let v = e.target ? e.target.value : e.val()
$el.removeClass('is-invalid')
const v = e.target ? e.target.value : e.val()
if (!v) {
$el.addClass('is-invalid')
} else {
if (this.isNumberValue()) {
if ($regex.isDecimal(v) === false) $el.addClass('is-invalid')
} else if (this.state.type === 'DATE' || this.state.type === 'DATETIME') {
if ($regex.isUTCDate(v) === false) $el.addClass('is-invalid')
if ($regex.isDate(v) === false) $el.addClass('is-invalid')
} else if (this.state.type === 'TIME') {
if ($regex.isTime(v) === false) $el.addClass('is-invalid')
}
}
}

View file

@ -276,9 +276,8 @@ var $cleanMap = function (map) {
* 常用正则
*/
var $regex = {
_Date:
/^((((1[6-9]|[2-9]\d)\d{2})-(0?[13578]|1[02])-(0?[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})-(0?[13456789]|1[012])-(0?[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})-0?2-(0?[1-9]|1\d|2[0-8]))|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))-0?2-29-))$/,
_UTCDate: /^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$/, // eg. 2010-01-01, 2010-1-9
_Date: /^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}$/, // eg. 2010-01-01, 2010-1-9
_Time: /^[0-9]{1,2}:[0-9]{1,2}(:[0-9]{1,2})?$/, // eg. 16:01:10, 10:1:9
// eslint-disable-next-line no-useless-escape
_Url: /^(http|https|ftp)\:\/\/[a-z0-9\-\.]+(:[0-9]*)?\/?([a-z0-9\-\._\?\,\'\/\\\+&amp;%\$#\=~!:])*$/i,
_Mail: /^[a-z0-9._%-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i,
@ -290,8 +289,8 @@ var $regex = {
isDate: function (val) {
return this._Date.test(val)
},
isUTCDate: function (val) {
return this._UTCDate.test(val)
isTime: function (val) {
return this._Time.test(val)
},
isUrl: function (val) {
return this._Url.test(val)

View file

@ -131,18 +131,21 @@ class RbList extends React.Component {
wheelSpeed: 2,
})
// enable pins
// enable pin
if ($(window).height() > 666 && $(window).width() >= 1280) {
$('.main-content').addClass('pb-0')
$('.main-content .rb-datatable-header').addClass('header-fixed')
// $('.main-content .rb-datatable-header').addClass('header-fixed')
if (supportFixedColumns) $scroller.find('.table').addClass('table-header-fixed')
$addResizeHandler(() => {
let mh = $(window).height() - 215
if ($('.main-content>.nav-tabs-classic').length > 0) mh -= 44 // Has tab
let mh = $(window).height() - 208
if ($('.main-content>.nav-tabs-classic').length > 0) mh -= 40 // Has tab
if ($('.main-content .quick-filter-pane').length > 0) mh -= 84 // Has query-pane
$scroller.css({ maxHeight: mh })
$scroller.perfectScrollbar('update')
})()
} else {
$('.main-content .rb-datatable-header').addClass('header-fixed')
}
if (supportFixedColumns) {
@ -942,6 +945,12 @@ const RbListPage = {
$cleanMenu('.J_action')
}
// Filter Pane
if ($('.quick-filter-pane').length > 0) {
// eslint-disable-next-line react/jsx-no-undef
renderRbcomp(<AdvFilterPane entity={entity[0]} />, $('.quick-filter-pane')[0])
}
typeof window.startTour === 'function' && window.startTour(1000)
},

View file

@ -0,0 +1,119 @@
/*
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.
*/
// deps: rb-advfilter.js
const REFENTITY_CACHE = window.REFENTITY_CACHE || {}
const IS_N2NREF = window.IS_N2NREF || {}
const BIZZ_ENTITIES = window.BIZZ_ENTITIES || []
// eslint-disable-next-line no-unused-vars
class AdvFilterPane extends React.Component {
constructor(props) {
super(props)
this.state = {}
this._itemsRef = []
}
onRef = (c) => this._itemsRef.push(c)
componentDidMount() {
$.get(`/commons/metadata/fields?entity=${this.props.entity}`, (res) => {
const items = res.data.map((item) => {
if (item.type === 'REFERENCE' || item.type === 'N2NREFERENCE') {
REFENTITY_CACHE[`${this.props.entity}.${item.name}`] = item.ref
if (item.type === 'N2NREFERENCE') IS_N2NREF.push(item.name)
// NOTE: Use `NameField` field-type
if (!BIZZ_ENTITIES.includes(item.ref[0])) {
item.type = item.ref[1]
}
}
return item
})
this.setState({ items })
})
// const items = [
// { name: 'UnitPrice', label: '单价', type: 'DECIMAL' },
// { name: 'UnitPrice', label: '单价', type: 'DECIMAL' },
// { name: 'UnitPrice', label: '单价', type: 'DECIMAL' },
// { name: 'UnitPrice', label: '单价', type: 'DECIMAL' },
// { name: 'UnitPrice', label: '单价', type: 'DECIMAL' },
// { name: 'UnitPrice', label: '单价', type: 'DECIMAL' },
// ]
// this.setState({ items })
}
render() {
if (!this.state.items) return null
const col = $('#react-list').width() > 1200 ? 3 : 4
return (
<div className="row" onKeyPress={(e) => this.searchByKey(e)}>
{this.state.items.map((item, i) => {
return (
<div className={`col col-${col}`} key={i}>
<div>
<label>{item.label}</label>
<div className="adv-filter">
<div className="filter-items">
<FilterItemExt onRef={this.onRef} $$$parent={this} fields={[item]} />
</div>
</div>
</div>
</div>
)
})}
<div className={`col col-${col}`}>
<div>
<label>&nbsp;</label>
<button className="btn btn-primary btn-outline" type="button" onClick={() => this.searchNow()}>
<i className="icon zmdi zmdi-search"></i> {$L('查询')}
</button>
</div>
</div>
</div>
)
}
searchByKey(e) {
e.which === 13 && this.searchNow()
}
searchNow() {
const filters = []
for (let i = 0; i < this._itemsRef.length; i++) {
const item = this._itemsRef[i].getFilterJson()
if (item) filters.push(item)
}
const adv = {
entity: this.props.entity,
items: filters,
}
if (rb.env === 'dev') console.log(JSON.stringify(adv))
}
}
// eslint-disable-next-line no-undef
class FilterItemExt extends FilterItem {
constructor(props) {
super(props)
}
// @e = el or event
valueCheck(e) {
const v = e.target ? e.target.value : e.val()
if (!v) return
else super.valueCheck(e)
// $el.removeClass('is-invalid')
}
}

View file

@ -471,6 +471,7 @@ class RbFormElement extends React.Component {
if (props.colspan === 4 || props.isFull === true) colspan = 12
else if (props.colspan === 1) colspan = 3
else if (props.colspan === 3) colspan = 9
else if (props.colspan === 9) colspan = 4
const editable = props.$$$parent.onViewEditable && props.onView && !props.readonly
@ -1735,7 +1736,7 @@ class RbFormBool extends RbFormElement {
<label className="custom-control custom-radio custom-control-inline">
<input
className="custom-control-input"
name={'radio-' + this.props.field}
name={`radio-${this.props.field}`}
type="radio"
checked={!$isTrue(this.state.value)}
data-value="F"
@ -1786,8 +1787,8 @@ class RbFormBarcode extends RbFormElement {
const codeUrl = `${rb.baseUrl}/commons/barcode/render${isbar ? '' : '-qr'}?t=${$encode(this.state.value)}`
return (
<div className="img-field barcode">
<a className="img-thumbnail" title={this.state.value}>
<img src={codeUrl} alt={this.state.value} className={isbar ? 'w-auto' : ''} />
<a className={`img-thumbnail ${isbar && 'w-auto'}`} title={this.state.value}>
<img src={codeUrl} alt={this.state.value} />
</a>
</div>
)

View file

@ -813,7 +813,7 @@ const RbViewPage = {
$('.view-action .col-lg-6').each(function () {
if ($(this).children().length === 0) $(this).remove()
})
if ($('.view-action').children().length === 0) $('.view-action').addClass('empty').empty()
if ($('.view-action').children().length === 0) $('.view-action').addClass('mt-0').empty()
},
100,
'_cleanViewActionButton'

View file

@ -160,6 +160,8 @@
<li class="dropdown-item J_date" data-calc="M">[[${bundle.L('按月')}]]</li>
<li class="dropdown-item J_date" data-calc="D">[[${bundle.L('按日')}]]</li>
<li class="dropdown-item J_date" data-calc="H">[[${bundle.L('按时')}]]</li>
<li class="dropdown-item J_time" data-calc="H">[[${bundle.L('按时')}]]</li>
<li class="dropdown-item J_time" data-calc="I">[[${bundle.L('按时分')}]]</li>
<li class="dropdown-item J_clazz" data-calc="L1">[[${bundle.L('1级')}]]</li>
<li class="dropdown-item J_clazz" data-calc="L2">[[${bundle.L('2级')}]]</li>
<li class="dropdown-item J_clazz" data-calc="L3">[[${bundle.L('3级')}]]</li>

View file

@ -50,6 +50,12 @@
<div class="card-body">
<div class="dataTables_wrapper container-fluid">
<div class="row rb-datatable-header">
<div class="col-12">
<div class="quick-filter-pane">
<div class="ph-item-mt"></div>
<div class="ph-item rb"></div>
</div>
</div>
<div class="col-12 col-md-6">
<div class="dataTables_filter">
<div class="adv-search float-left">
@ -66,7 +72,7 @@
</div>
</div>
<div class="input-group input-search float-left">
<input class="form-control" type="text" th:placeholder="${bundle.L('快速查询')}" maxlength="40" />
<input class="form-control" type="text" th:placeholder="${quickFieldsLabel ?:bundle.L('快速查询')}" th:title="${quickFieldsLabel}" maxlength="40" />
<button class="btn btn-input-clear" type="button"></button>
<span class="input-group-btn"
><button class="btn btn-secondary" type="button"><i class="icon zmdi zmdi-search"></i></button
@ -122,6 +128,7 @@
<script th:src="@{/assets/js/rb-forms.append.js}" type="text/babel"></script>
<script th:src="@{/assets/js/rb-forms.protable.js}" type="text/babel"></script>
<script th:src="@{/assets/js/rb-advfilter.js}" type="text/babel"></script>
<script th:src="@{/assets/js/rb-filter-pane.js}" type="text/babel"></script>
<script th:src="@{/assets/js/rb-assignshare.js}" type="text/babel"></script>
<script th:src="@{/assets/js/rb-approval.js}" type="text/babel"></script>
<script th:src="@{/assets/js/settings-share2.js}" type="text/babel"></script>

View file

@ -22,7 +22,9 @@
}
.table th {
background-color: #eee !important;
width: 14%;
width: auto;
min-width: 110px;
max-width: 180px;
}
.table th.divider {
width: 100%;

View file

@ -66,7 +66,7 @@
</div>
</div>
<div class="input-group input-search float-left">
<input class="form-control" type="text" th:placeholder="${bundle.L('快速查询')}" maxlength="40" />
<input class="form-control" type="text" th:placeholder="${quickFieldsLabel ?:bundle.L('快速查询')}" th:title="${quickFieldsLabel}" maxlength="40" />
<button class="btn btn-input-clear" type="button"></button>
<span class="input-group-btn"
><button class="btn btn-secondary" type="button"><i class="icon zmdi zmdi-search"></i></button

View file

@ -3,8 +3,9 @@
<head>
<th:block th:replace="~{/_include/header}" />
<title th:text="${entityLabel}"></title>
<style type="text/css">
.rb-datatable-header {
<style>
.dataTables_wrapper .rb-datatable-header,
.dataTables_wrapper .rb-datatable-header.header-fixed {
position: fixed;
top: 0;
left: 0;
@ -13,7 +14,7 @@
z-index: 10;
}
#react-list {
margin-top: 68px;
margin-top: 60px;
}
</style>
</head>
@ -26,7 +27,7 @@
<div class="col-6">
<div class="dataTables_filter">
<div class="input-group input-search">
<input class="form-control" type="text" th:placeholder="${bundle.L('快速查询')}" maxlength="40" />
<input class="form-control" type="text" th:placeholder="${quickFieldsLabel ?:bundle.L('快速查询')}" th:title="${quickFieldsLabel}" maxlength="40" />
<span class="input-group-btn">
<button class="btn btn-secondary" type="button"><i class="icon zmdi zmdi-search"></i></button>
</span>
@ -59,8 +60,9 @@
protocolFilter: '[[${referenceFilter}]]',
}
</script>
<script th:src="@{/assets/js/rb-datalist.js}" type="text/babel"></script>
<script th:src="@{/assets/js/rb-datalist.common.js}" type="text/babel"></script>
<script th:src="@{/assets/js/rb-datalist.js}" type="text/babel"></script>
<script th:src="@{/assets/js/rb-forms.append.js}" type="text/babel"></script>
<script type="text/babel">
RbList.renderAfter = function () {
parent && parent.referenceSearch__dlg && parent.referenceSearch__dlg.resize()