Chart support for thousands

This commit is contained in:
oahzeved 2020-04-27 16:54:07 +08:00
parent 723a6f2396
commit 901c1ce275
8 changed files with 78 additions and 59 deletions

View file

@ -127,7 +127,7 @@
<dependency>
<groupId>com.github.devezhao</groupId>
<artifactId>persist4j</artifactId>
<version>3c386162b5</version>
<version>1307de500f</version>
</dependency>
<dependency>
<groupId>junit</groupId>

View file

@ -43,7 +43,7 @@ import java.util.Set;
* @since 12/14/2018
*/
public abstract class ChartData extends SetUser<ChartData> implements ChartSpec {
protected JSONObject config;
private boolean fromPreview = false;
@ -265,21 +265,35 @@ public abstract class ChartData extends SetUser<ChartData> implements ChartSpec
/**
* 格式化数值
*
* @param axis
* @param numerical
* @param value
* @return
*/
protected String wrapAxisValue(Numerical axis, Object value) {
protected String wrapAxisValue(Numerical numerical, Object value) {
return wrapAxisValue(numerical, value, Boolean.FALSE);
}
/**
* @param numerical
* @param value
* @param thousands 是否千分位
* @return
*/
protected String wrapAxisValue(Numerical numerical, Object value, boolean thousands) {
if (value == null) {
return "0";
}
String format = "###";
if (axis.getScale() > 0) {
if (numerical.getScale() > 0) {
format = "##0.";
format = StringUtils.rightPad(format, format.length() + axis.getScale(), "0");
format = StringUtils.rightPad(format, format.length() + numerical.getScale(), "0");
}
if (thousands) {
format = "#," + format;
}
if (ID.isId(value)) {
value = 1;
}
@ -289,16 +303,16 @@ public abstract class ChartData extends SetUser<ChartData> implements ChartSpec
/**
* 获取纬度标签
*
* @param axis
* @param dimension
* @param value
* @return
*/
protected String wrapAxisValue(Dimension axis, Object value) {
protected String wrapAxisValue(Dimension dimension, Object value) {
if (value == null) {
return "";
}
EasyMeta axisField = EasyMeta.valueOf(axis.getField());
EasyMeta axisField = EasyMeta.valueOf(dimension.getField());
DisplayType axisType = axisField.getDisplayType();
String label;
@ -310,25 +324,6 @@ public abstract class ChartData extends SetUser<ChartData> implements ChartSpec
}
return label;
}
/**
* 格式化汇总数值
*
* @param sumOfAxis
* @param value
* @return
*/
protected String wrapSumValue(Axis sumOfAxis, Object value) {
if (value == null) {
return "0";
}
if (sumOfAxis instanceof Numerical) {
return wrapAxisValue((Numerical) sumOfAxis, value);
} else {
return value.toString();
}
}
/**
* 构建数据

View file

@ -41,7 +41,7 @@ public class Dimension extends Axis {
case Y:
return String.format("DATE_FORMAT(%s,'%s')", super.getSqlName(), "%Y");
case Q:
return MessageFormat.format("CONCAT(YEAR({0}),''Q'',QUARTER({0}))", super.getSqlName());
return MessageFormat.format("CONCAT(YEAR({0}),'' Q'',QUARTER({0}))", super.getSqlName());
case M:
return String.format("DATE_FORMAT(%s,'%s')", super.getSqlName(), "%Y-%m");
case H:

View file

@ -34,7 +34,7 @@ public class IndexChart extends ChartData {
JSONObject index = JSONUtils.toJSONObject(
new String[] { "data", "label" },
new Object[] { wrapAxisValue(axis, dataRaw[0]), axis.getLabel() });
new Object[] { wrapAxisValue(axis, dataRaw[0], true), axis.getLabel() });
return JSONUtils.toJSONObject("index", index);
}

View file

@ -70,13 +70,18 @@ public class TableBuilder {
for (int i = 0; i < row.length; i++) {
Axis axis = axes.get(i);
TD td = null;
TD td;
if (axis == LN_REF) {
td = new TD(row[i] + "", "th");
} else {
String text = isLast == 0
? chart.wrapSumValue(axis, row[i])
: chart.wrapAxisValue(axis, row[i]);
String text;
if (isLast == 0) {
text = chart.wrapSumValue(axis, row[i]);
} else if (axis instanceof Numerical) {
text = chart.wrapAxisValue((Numerical) axis, row[i], true);
} else {
text = chart.wrapAxisValue(axis, row[i]);
}
td = new TD(text);
}
tds.addChild(td);
@ -103,9 +108,8 @@ public class TableBuilder {
}
String tClazz = (chart.isShowLineNumber() ? "line-number " : "") + (chart.isShowSums() ? "sums" : "");
String table = String.format("<table class=\"table table-bordered %s\">%s%s</table>",
return String.format("<table class=\"table table-bordered %s\">%s%s</table>",
tClazz, thead.toString(), tbody.toString());
return table;
}
// --
@ -113,17 +117,22 @@ public class TableBuilder {
// <tbody> or <thead>
private static class TBODY {
private String tag = "tbody";
private List<TR> children = new ArrayList<>();
private TBODY() {
}
private TBODY(String tag) {
this.tag = tag;
}
private TBODY addChild(TR c) {
children.add(c);
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@ -136,11 +145,14 @@ public class TableBuilder {
// <tr>
private static class TR {
private List<TD> children = new ArrayList<>();
private TR addChild(TD c) {
children.add(c);
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@ -153,16 +165,19 @@ public class TableBuilder {
// <td> or <th>
private static class TD {
private String tag = null;
private String tag;
private String content;
private int rowspan = 1;
private TD(String content) {
this(content, null);
}
private TD(String content, String tag) {
this.content = StringUtils.defaultIfBlank(content, "");
this.tag = StringUtils.defaultIfBlank(tag, "td");
}
@Override
public String toString() {
if (rowspan == 0) {

View file

@ -85,10 +85,7 @@ public class TableChart extends ChartData {
}
String tableHtml = new TableBuilder(this, dataRaw).toHTML();
return JSONUtils.toJSONObject(
new String[] { "html" },
new Object[] { tableHtml });
return JSONUtils.toJSONObject("html", tableHtml);
}
protected boolean isShowLineNumber() {
@ -99,6 +96,18 @@ public class TableChart extends ChartData {
return showSums;
}
protected String wrapSumValue(Axis sumAxis, Object value) {
if (value == null) {
return "0";
}
if (sumAxis instanceof Numerical) {
return wrapAxisValue((Numerical) sumAxis, value, true);
} else {
return value.toString();
}
}
private String buildSql(Dimension[] dims, Numerical[] nums) {
List<String> dimSqlItems = new ArrayList<>();
for (Dimension dim : dims) {

View file

@ -202,10 +202,9 @@ const add_axis = ((target, axis) => {
isNumAxis: isNumAxis,
label: $dropdown.attr('data-label'),
scale: $dropdown.attr('data-scale'),
thousands: $dropdown.attr('data-thousands') === 'true'
}
state.callback = (s) => {
$dropdown.attr({ 'data-label': s.label, 'data-scale': s.scale, 'data-thousands': s.thousands })
$dropdown.attr({ 'data-label': s.label, 'data-scale': s.scale })
render_preview()
}
console.log(JSON.stringify(state))
@ -362,12 +361,6 @@ class DlgAxisProps extends RbFormHandler {
<option value="5">5</option>
<option value="6">6</option>
</select>
<div>
<label className="custom-control custom-control-sm custom-checkbox custom-control-inline mt-3 mb-0">
<input className="custom-control-input" type="checkbox" checked={this.state.thousands || false} data-id="thousands" onChange={this.handleChange} />
<span className="custom-control-label">显示千分位</span>
</label>
</div>
</div>
</div>
}

View file

@ -194,7 +194,7 @@ const ECHART_TOOLTIP_FORMATTER = function (i) {
if (!Array.isArray(i)) i = [i] // Object > Array
const tooltip = [`<b>${i[0].name}</b>`]
i.forEach((item) => {
tooltip.push(`${item.marker} ${item.seriesName} : ${item.value}`)
tooltip.push(`${item.marker} ${item.seriesName} : ${formatThousands(item.value)}`)
})
return tooltip.join('<br>')
}
@ -208,6 +208,13 @@ const shortNumber = function (num) {
else return num
}
const formatThousands = function (num) {
if (~~num < 1000) return num
const nums = (num + '').split('.')
nums[0] = nums[0].replace(/\d{1,3}(?=(\d{3})+$)/g, '$&,')
return nums.join('.')
}
const cloneOption = function (opt) {
opt = JSON.stringify(opt)
return JSON.parse(opt)
@ -389,8 +396,8 @@ class ChartFunnel extends BaseChart {
}
opt.tooltip.trigger = 'item'
opt.tooltip.formatter = function (i) {
if (data.xLabel) return `<b>${i.name}</b> <br/> ${i.marker} ${data.xLabel} : ${i.value}`
else return `<b>${i.name}</b> <br/> ${i.marker} ${i.value}`
if (data.xLabel) return `<b>${i.name}</b> <br/> ${i.marker} ${data.xLabel} : ${formatThousands(i.value)}`
else return `<b>${i.name}</b> <br/> ${i.marker} ${formatThousands(i.value)}`
}
const c = echarts.init(document.getElementById(elid), 'light', ECHART_RENDER_OPT)
@ -443,7 +450,7 @@ class ChartTreemap extends BaseChart {
opt.tooltip.trigger = 'item'
opt.tooltip.formatter = function (i) {
const p = i.value > 0 ? (i.value * 100 / data.xAmount).toFixed(2) : 0
return `<b>${i.name.split('--------').join('<br/>')}</b> <br/> ${i.marker} ${data.xLabel} : ${i.value} (${p}%)`
return `<b>${i.name.split('--------').join('<br/>')}</b> <br/> ${i.marker} ${data.xLabel} : ${formatThousands(i.value)} (${p}%)`
}
opt.label = {
formatter: function (i) {
@ -709,9 +716,9 @@ class ChartRadar extends BaseChart {
opt.tooltip.formatter = function (a) {
const tooltip = [`<b>${a.name}</b>`]
a.value.forEach((item, idx) => {
tooltip.push(`${data.indicator[idx].name} : ${item}`)
tooltip.push(`${data.indicator[idx].name} : ${formatThousands(item)}`)
})
return tooltip.join('<br>')
return tooltip.join('<br/>')
}
const c = echarts.init(document.getElementById(elid), 'light', ECHART_RENDER_OPT)
@ -778,8 +785,8 @@ class ChartScatter extends BaseChart {
if (a.value.length === 3) {
tooltip.push(`<b>${a.value[2]}</b>`)
}
tooltip.push(`${data.dataLabel[1]} : ${a.value[1]}`)
tooltip.push(`${data.dataLabel[0]} : ${a.value[0]}`)
tooltip.push(`${data.dataLabel[1]} : ${formatThousands(a.value[1])}`)
tooltip.push(`${data.dataLabel[0]} : ${formatThousands(a.value[0])}`)
return tooltip.join('<br>')
}