Table chart asix click (#499)

* reflink

* fix #500

* fix better

* Map suggest
This commit is contained in:
RB 2022-08-02 20:09:15 +08:00 committed by GitHub
parent bbc5ab5988
commit 68c9cc505f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 218 additions and 56 deletions

2
@rbv

@ -1 +1 @@
Subproject commit 610033ac8d08b0e20728066598c24b325329dd2b
Subproject commit 263dc325c7903bb3115ba30a9032cf182ee7e4f9

View file

@ -32,7 +32,7 @@ public class EasyPhone extends EasyText {
// 兼容电话手机国际区号
public static Pattern PATT_PHONE = Pattern.compile(
"((\\(\\d{1,5}\\))?(\\d{3,4}-)?\\d{7,8}(-\\d{1,6})?)|(1[356789]\\d{9})");
"((\\(\\d{1,5}\\))?(\\d{3,4}-)?\\d{7,8}(-\\d{1,6})?)|(1[3456789]\\d{9})");
/**
* @param phone

View file

@ -263,10 +263,10 @@ public abstract class ChartData extends SetUser implements ChartSpec {
/**
* @param numerical
* @param value
* @param thousands 是否千分位
* @param useThousands 使用千分位
* @return
*/
protected String wrapAxisValue(Numerical numerical, Object value, boolean thousands) {
protected String wrapAxisValue(Numerical numerical, Object value, boolean useThousands) {
if (ChartsHelper.isZero(value)) {
return ChartsHelper.VALUE_ZERO;
}
@ -277,7 +277,7 @@ public abstract class ChartData extends SetUser implements ChartSpec {
format = StringUtils.rightPad(format, format.length() + numerical.getScale(), "0");
}
if (thousands) {
if (useThousands) {
format = "#," + format;
}
@ -295,6 +295,18 @@ public abstract class ChartData extends SetUser implements ChartSpec {
* @return
*/
protected String wrapAxisValue(Dimension dimension, Object value) {
return wrapAxisValue(dimension, value, Boolean.FALSE);
}
/**
* 获取纬度标签
*
* @param dimension
* @param value
* @param useRefLink 使用链接
* @return
*/
protected String wrapAxisValue(Dimension dimension, Object value, boolean useRefLink) {
if (value == null) {
return ChartsHelper.VALUE_NONE;
}
@ -309,6 +321,11 @@ public abstract class ChartData extends SetUser implements ChartSpec {
|| axisType == DisplayType.PICKLIST
|| axisType == DisplayType.STATE) {
label = (String) FieldValueHelper.wrapFieldValue(value, axisField, true);
if (useRefLink && axisType == DisplayType.REFERENCE) {
label = String.format("<a href='/app/list-and-view?id=%s'>%s</a>", value, label);
}
} else {
label = value.toString();
}

View file

@ -81,7 +81,7 @@ public class TableBuilder {
} else if (axis instanceof Numerical) {
text = chart.wrapAxisValue((Numerical) axis, row[i], true);
} else {
text = chart.wrapAxisValue((Dimension) axis, row[i]);
text = chart.wrapAxisValue((Dimension) axis, row[i], true);
}
td = new TD(text);
}

View file

@ -0,0 +1,55 @@
/*!
Copyright (c) REBUILD <https://getrebuild.com/> and/or its owners. All rights reserved.
rebuild is dual-licensed under commercial and open source licenses (GPLv3).
See LICENSE and COMMERCIAL in the project root for license information.
*/
package com.rebuild.web.commons;
import cn.devezhao.commons.CodecUtils;
import com.alibaba.fastjson.JSON;
import com.rebuild.api.RespBody;
import com.rebuild.core.support.ConfigurationItem;
import com.rebuild.core.support.RebuildConfiguration;
import com.rebuild.utils.OkHttpUtils;
import com.rebuild.web.BaseController;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author Zixin (RB)
* @since 08/02/2022
*/
@RestController
@RequestMapping("/commons/map/")
public class MapController extends BaseController {
@GetMapping("suggest")
public RespBody suggest(HttpServletRequest req) {
String q = getParameterNotNull(req, "q");
String city = getParameter(req, "city", "中国");
// https://lbsyun.baidu.com/index.php?title=webapi/place-suggestion-api
String ak = StringUtils.defaultIfBlank(
RebuildConfiguration.get(ConfigurationItem.PortalBaiduMapAk), "YQKHNmIcOgYccKepCkxetRDy8oTC28nD");
String qUrl = String.format(
"https://api.map.baidu.com/place/v2/suggestion?q=%s&region=%s&city_limit=%s&output=json&ak=%s",
CodecUtils.urlEncode(q), CodecUtils.urlEncode(city), "false", ak);
JSON dataJson;
try {
String data = OkHttpUtils.get(qUrl);
dataJson = JSON.parseObject(data);
} catch (IOException e) {
return RespBody.error(e.getLocalizedMessage());
}
return RespBody.ok(dataJson);
}
}

View file

@ -35,7 +35,7 @@ public class ListAndViewRedirection extends BaseController {
@GetMapping({ "/app/list-and-view", "/app/redirect" })
public void redirect(@IdParam ID anyId, HttpServletResponse response) throws IOException {
String url = null;
if (MetadataHelper.containsEntity(anyId.getEntityCode())) {
Entity entity = MetadataHelper.getEntity(anyId.getEntityCode());

View file

@ -25,14 +25,12 @@ public class AutoApprovalController extends BaseController {
@RequestMapping("auto-approval-alist")
public JSON approvalList(HttpServletRequest request) {
String entity = getParameterNotNull(request, "entity");
Object[][] array = Application.createQueryNoFilter(
"select configId,name,isDisabled from RobotApprovalConfig where belongEntity = ? order by name")
.setParameter(1, entity)
"select configId,name from RobotApprovalConfig where belongEntity = ? and isDisabled = ? order by name")
.setParameter(1, getParameterNotNull(request, "entity"))
.setParameter(2, false)
.array();
return JSONUtils.toJSONObjectArray(new String[] { "id", "text", "disabled" }, array);
return JSONUtils.toJSONObjectArray(new String[] { "id", "text" }, array);
}
}

View file

@ -4806,3 +4806,14 @@ pre.unstyle {
cursor: default;
opacity: unset;
}
.map-suggestion.dropdown-menu {
max-width: unset;
width: 100%;
padding: 0;
}
.map-suggestion.dropdown-menu .dropdown-item {
padding: 8px 10px;
cursor: pointer;
}

View file

@ -48,7 +48,7 @@ class BaseChart extends React.Component {
<div className="chart-title text-truncate">{this.state.title}</div>
{opers}
</div>
<div ref={(c) => (this._$body = c)} className={'chart-body rb-loading ' + (!this.state.chartdata && 'rb-loading-active')}>
<div ref={(c) => (this._$body = c)} className={`chart-body rb-loading ${!this.state.chartdata && 'rb-loading-active'}`}>
{this.state.chartdata || <RbSpinner />}
</div>
</div>
@ -60,13 +60,13 @@ class BaseChart extends React.Component {
}
componentWillUnmount() {
if (this.__echarts) this.__echarts.dispose()
if (this._echarts) this._echarts.dispose()
}
loadChartData() {
this.setState({ chartdata: null })
$.post(this.buildDataUrl(), JSON.stringify(this.state.config || {}), (res) => {
if (this.__echarts) this.__echarts.dispose()
if (this._echarts) this._echarts.dispose()
if (res.error_code === 0) this.renderChart(res.data)
else this.renderError(res.error_msg)
@ -74,12 +74,12 @@ class BaseChart extends React.Component {
}
buildDataUrl() {
return (this.state.id ? '/dashboard/chart-data' : '/dashboard/chart-preview') + '?id=' + (this.state.id || '')
return `${this.state.id ? '/dashboard/chart-data' : '/dashboard/chart-preview'}?id=${this.state.id || ''}`
}
resize() {
if (this.__echarts) {
$setTimeout(() => this.__echarts.resize(), 400, 'resize-chart-' + (this.state.id || ''))
if (this._echarts) {
$setTimeout(() => this._echarts.resize(), 400, `resize-chart-${this.state.id || ''}`)
}
}
@ -189,15 +189,21 @@ class ChartTable extends BaseChart {
.css('height', $tb.height() - 20)
.perfectScrollbar()
const cols = $tb.find('tbody td').click(function () {
const $cols = $tb.find('tbody td').on('mousedown', function () {
if (colLast === this) {
$(this).toggleClass('active')
return
}
colLast = this
cols.removeClass('active')
$cols.removeClass('active')
$(this).addClass('active')
})
$tb.find('tbody td>a').each(function () {
const $a = $(this)
$a.attr({ href: `${rb.baseUrl}${$a.attr('href')}` })
})
this._$tb = $tb
})
}
@ -320,7 +326,7 @@ class ChartLine extends BaseChart {
return
}
const elid = 'echarts-line-' + (this.state.id || 'id')
const elid = `echarts-line-${this.state.id || 'id'}`
this.setState({ chartdata: <div className="chart line" id={elid} /> }, () => {
const showGrid = data._renderOption && data._renderOption.showGrid
const showNumerical = data._renderOption && data._renderOption.showNumerical
@ -370,7 +376,7 @@ class ChartLine extends BaseChart {
option.grid.top = 40
}
this.__echarts = renderEChart(option, elid)
this._echarts = renderEChart(option, elid)
})
}
}
@ -383,7 +389,7 @@ class ChartBar extends BaseChart {
return
}
const elid = 'echarts-bar-' + (this.state.id || 'id')
const elid = `echarts-bar-${this.state.id || 'id'}`
this.setState({ chartdata: <div className="chart bar" id={elid} /> }, () => {
const showGrid = data._renderOption && data._renderOption.showGrid
const showNumerical = data._renderOption && data._renderOption.showNumerical
@ -427,7 +433,7 @@ class ChartBar extends BaseChart {
option.grid.top = 40
}
this.__echarts = renderEChart(option, elid)
this._echarts = renderEChart(option, elid)
})
}
}
@ -440,7 +446,7 @@ class ChartPie extends BaseChart {
return
}
const elid = 'echarts-pie-' + (this.state.id || 'id')
const elid = `echarts-pie-${this.state.id || 'id'}`
this.setState({ chartdata: <div className="chart pie" id={elid} /> }, () => {
const showNumerical = data._renderOption && data._renderOption.showNumerical
const showLegend = data._renderOption && data._renderOption.showLegend
@ -464,7 +470,7 @@ class ChartPie extends BaseChart {
}
if (showLegend) option.legend = ECHART_LEGEND_VOPT
this.__echarts = renderEChart(option, elid)
this._echarts = renderEChart(option, elid)
})
}
}
@ -477,7 +483,7 @@ class ChartFunnel extends BaseChart {
return
}
const elid = 'echarts-funnel-' + (this.state.id || 'id')
const elid = `echarts-funnel-${this.state.id || 'id'}`
this.setState({ chartdata: <div className="chart funnel" id={elid} /> }, () => {
const showNumerical = data._renderOption && data._renderOption.showNumerical
const showLegend = data._renderOption && data._renderOption.showLegend
@ -510,7 +516,7 @@ class ChartFunnel extends BaseChart {
}
if (showLegend) option.legend = ECHART_LEGEND_VOPT
this.__echarts = renderEChart(option, elid)
this._echarts = renderEChart(option, elid)
})
}
}
@ -524,7 +530,7 @@ class ChartTreemap extends BaseChart {
return
}
const elid = 'echarts-treemap-' + (this.state.id || 'id')
const elid = `echarts-treemap-${this.state.id || 'id'}`
this.setState({ chartdata: <div className="chart treemap" id={elid} /> }, () => {
const showNumerical = data._renderOption && data._renderOption.showNumerical
@ -571,7 +577,7 @@ class ChartTreemap extends BaseChart {
},
}
this.__echarts = renderEChart(option, elid)
this._echarts = renderEChart(option, elid)
})
}
}
@ -719,7 +725,7 @@ class ApprovalList extends BaseChart {
}
buildDataUrl() {
return super.buildDataUrl() + '&state=' + this.state.viewState
return `${super.buildDataUrl()}&state=${this.state.viewState}`
}
}
@ -829,7 +835,7 @@ class ChartRadar extends BaseChart {
return
}
const elid = 'echarts-radar-' + (this.state.id || 'id')
const elid = `echarts-radar-${this.state.id || 'id'}`
this.setState({ chartdata: <div className="chart radar" id={elid} /> }, () => {
const showNumerical = data._renderOption && data._renderOption.showNumerical
const showLegend = data._renderOption && data._renderOption.showLegend
@ -892,7 +898,7 @@ class ChartRadar extends BaseChart {
}
if (showLegend) option.legend = ECHART_LEGEND_VOPT
this.__echarts = renderEChart(option, elid)
this._echarts = renderEChart(option, elid)
})
}
}
@ -905,7 +911,7 @@ class ChartScatter extends BaseChart {
return
}
const elid = 'echarts-scatter-' + (this.state.id || 'id')
const elid = `echarts-scatter-${this.state.id || 'id'}`
this.setState({ chartdata: <div className="chart scatter" id={elid} /> }, () => {
const showGrid = data._renderOption && data._renderOption.showGrid
const showNumerical = data._renderOption && data._renderOption.showNumerical
@ -979,7 +985,7 @@ class ChartScatter extends BaseChart {
option.grid.top = 40
}
this.__echarts = renderEChart(option, elid)
this._echarts = renderEChart(option, elid)
})
}
}
@ -1076,7 +1082,7 @@ class ProjectTasks extends BaseChart {
if (this._$tb) this._$tb.find('.ProjectTasks').css('height', this._$tb.height() - 13)
},
400,
'resize-chart-' + this.state.id
`resize-chart-${this.state.id}`
)
}

View file

@ -283,7 +283,7 @@ var $regex = {
_Mail: /^[a-z0-9._%-]+@[a-z0-9.-]+\.[a-z]{2,4}$/i,
_Number: /^[-+]?[0-9]+$/, // 数字
_Decimal: /^[-+]?\d*\.?\d+$/, // 包括小数点的数字
_Mobile: /^(1[356789])\d{9}$/, // CN Mobile
_Mobile: /^(1[3456789])\d{9}$/, // CN Mobile
_Tel: /^(\(\d{1,5}\))?(\d{3,4}-)?\d{7,8}(-\d{1,6})?$/, // (国际码)区号-号码-分机
_Text: /^[a-z\d\u4E00-\u9FA5]+$/i, // 不含特殊字符和标点
isDate: function (val) {

View file

@ -409,6 +409,8 @@ class BaiduMapModal extends RbModal {
}
render() {
const ss = this.state.suggestion || []
return this.state.destroy === true ? null : (
<div className="modal" ref={(c) => (this._rbmodal = c)}>
<div className="modal-dialog modal-xl">
@ -424,21 +426,44 @@ class BaiduMapModal extends RbModal {
<div className="map-pin">
<div className="row">
<div className="col-6">
<div className="input-group w-100">
<input
type="text"
ref={(c) => (this._searchValue = c)}
className="form-control form-control-sm"
placeholder={$L('查找位置')}
onKeyDown={(e) => {
e.which === 13 && this._search()
}}
defaultValue={this.props.lnglat ? this.props.lnglat.text || '' : ''}
/>
<div className="input-group-append">
<button className="btn btn-secondary" type="button" onClick={() => this._search()}>
<i className="icon zmdi zmdi-search" />
</button>
<div className="dropdown">
<div className="input-group w-100">
<input
type="text"
ref={(c) => (this._$searchValue = c)}
className="form-control form-control-sm dropdown-toggle"
placeholder={$L('查找位置')}
defaultValue={this.props.lnglat ? this.props.lnglat.text || '' : ''}
onKeyDown={(e) => {
if (e.which === 38 || e.which === 40) this._updown(e.which)
else if (e.which === 13) this._search()
else this._suggest()
}}
onFocus={() => this._suggest()}
/>
<div className="input-group-append">
<button className="btn btn-secondary" type="button" onClick={() => this._search()}>
<i className="icon zmdi zmdi-search" />
</button>
</div>
</div>
<div className={`dropdown-menu map-suggestion ${ss.length > 0 && 'show'}`}>
{ss.map((item) => {
return (
<a
key={$random()}
className="dropdown-item"
title={item.address}
onClick={(e) => {
$stopEvent(e, true)
$(this._$searchValue).val(item.address)
this._BaiduMap.center(item.location)
this.setState({ suggestion: [] })
}}>
{item.address}
</a>
)
})}
</div>
</div>
</div>
@ -456,9 +481,9 @@ class BaiduMapModal extends RbModal {
lnglat={this.props.lnglat}
canPin={this.props.canPin}
onPin={(latlng) => {
if (this._searchValue) {
if (this._$searchValue) {
this._latlngValue = latlng
$(this._searchValue).val(latlng.text)
$(this._$searchValue).val(latlng.text)
}
}}
/>
@ -470,6 +495,17 @@ class BaiduMapModal extends RbModal {
)
}
componentDidMount() {
super.componentDidMount()
$(this._rbmodal).on('click', (e) => {
if (e.target && e.target.tagName === 'INPUT') return
setTimeout(() => {
this.setState({ suggestion: [] })
}, 100)
})
}
// show(lnglat) {
// $(this._$modal).modal('show')
// if (lnglat) {
@ -482,7 +518,45 @@ class BaiduMapModal extends RbModal {
}
_search() {
this._BaiduMap.search($val(this._searchValue))
this._BaiduMap.search($val(this._$searchValue))
this.setState({ suggestion: [] })
}
_suggest() {
if (this._sugTimer) {
clearTimeout(this._sugTimer)
this._sugTimer = null
}
let q = $(this._$searchValue).val()
q = $.trim(q)
this._sugTimer = setTimeout(() => {
if (!q || q.length < 3) {
this.setState({ suggestion: [] })
return
}
this._sugCached = this._sugCached || {}
if (this._sugCached[q]) {
this.setState({ suggestion: this._sugCached[q] })
return
}
$.get(`/commons/map/suggest?q=${$encode(q)}`, (res) => {
const result = res.data ? res.data.result || [] : []
const ss = []
result.forEach((item) => {
item.address && ss.push({ address: item.address.replaceAll('-', ''), location: item.location })
})
this.setState({ suggestion: ss })
this._sugCached[q] = ss
})
}, 600)
}
_updown(key) {
// TODO
}
_onConfirm() {
@ -491,7 +565,7 @@ class BaiduMapModal extends RbModal {
return
}
const val = { ...this._latlngValue, text: $val(this._searchValue) }
const val = { ...this._latlngValue, text: $val(this._$searchValue) }
typeof this.props.onConfirm === 'function' && this.props.onConfirm(val)
this.hide()
}

View file

@ -27,6 +27,7 @@ class ContentAutoApproval extends ActionContentSpec {
)
})}
</select>
<p className="form-text">{WrapHtml($L('需要先添加 [审批流程](../approvals) 才能在此处选择'))}</p>
</div>
</div>
<div className="form-group row">