通过构造echarts返回体EchartsData.class
,在返回体内开发功能代码,实现echarts数据的改动。
特点:
1、y轴数据的数量级自动调整,使图表变化趋势更明显;
2、支持多维度数据【可以有1-N多个维度】
// EchartsData对象构造样例代码
List<String> xAxis = Arrays.asList("鲤鱼","草鱼","三文鱼","鳕鱼","龙利鱼","鲫鱼");
List<Object> barList = Arrays.asList("12345","2345","3456","4567","67891","7891");
final EchartsData echartsData = new EchartsData("新增情况图表", xAxis).setFirstYAxis("总数", barList);
echartsData.setLineYAxis(“利用率(%)”, line1List);
// echartsData.setLineYAxis(“利用率_2”, line2List);
// ...
// echartsData.setLineYAxis(“利用率_N”, lineNList);
xxx
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import lombok.Getter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* echarts 表格数据结构体
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter // 规定了“不同图标类型的y坐标值”与y坐标系维度列表的对应关系,故不允许随意set
public class EchartsData extends HashMap<String, Object> {
/**
* 标题
*/
private final String title;
/**
* 横坐标值
*/
private final List<String> xAxis;
/**
* 多维纵坐标的维度名称列表
* 按索引号,分别对应坐标值:barList、line1List、line2List
* 若对应的坐标值列表List为空,则表示无数据
*/
private List<String> yAxisDimensionNameList;
/**
* 柱状图-纵坐标值
* 元素可能为简单数据类型,
* 也可能为DataWithItemStyleVO.class类型,用于标识该值的显示样式
*/
private List<Object> barList; // 柱状图
// private List<String> line{0}List; // 折线图
public EchartsData(String title, List<String> xAxis) {
this.title = title;
this.xAxis = xAxis;
this.yAxisDimensionNameList = new ArrayList<>();
// map对象,使用MapSerializer解析时,不会序列自身的属性
put("title", this.title);
put("xAxis", this.xAxis);
put("yAxisDimensionNameList", this.yAxisDimensionNameList);
}
public EchartsData setFirstYAxis(String dimension, List<?> barList) {
ParamException.isTrue(StringUtils.isBlank(dimension), "inputParam[dimension] should not be blank");
if (CollectionUtils.isEmpty(barList)) {
return this;
}
YAxis yAxis = balanceNum(dimension, barList);
yAxisDimensionNameList.add(yAxis.getDimension());
this.barList = yAxis.getDataList();
put("barList", this.barList);
return this;
}
public EchartsData setLineYAxis(String dimension, List<String> lineList) {
ParamException.isTrue(StringUtils.isBlank(dimension), "inputParam[dimension] should not be blank");
if (CollectionUtils.isEmpty(lineList)) {
return this;
}
YAxis yAxis = balanceNum(dimension, lineList); // 不指定索引号add,避免覆盖原有元素
yAxisDimensionNameList.add(yAxis.getDimension());
int index = yAxisDimensionNameList.size() - 1;
final String key = MessageFormat.format("line{0}List", index);
put(key, yAxis.getDataList());
return this;
}
private YAxis balanceNum(String sourceDimension, List<?> sourceList) {
List<Object> targetList;
String targetDimension;
List<BigDecimal> tempNumList = new ArrayList<>();
sourceList.forEach(data -> {
String value;
if (data instanceof DataWithItemStyleVO) {
value = ((DataWithItemStyleVO) data).getValue();
} else {
value = String.valueOf(data);
}
// 非数字会报错
final BigDecimal dNum = NumberUtil.parseStrToBigDecimal(value);
tempNumList.add(dNum);
});
tempNumList.sort(BigDecimal::compareTo);
final BigDecimal min = tempNumList.get(0);
final int minLength = String.valueOf(min.intValue()).length();
final BigDecimal max = tempNumList.get(tempNumList.size() - 1);
final int maxLength = String.valueOf(max.intValue()).length();
// 是否进行数值大小转换
if (!sourceDimension.contains("率") // 为了避免调整百分数,双保险(部分字段的百分数值,没有带百分号。若数值本身的带百分号的minLength=1不会转换)。若是转化,单位会很奇怪,又是百分号、又是数量单位。
&& maxLength >= minLength
// 不可自动调整的数据如(为了避免调整百分数):88 vs 10; 987 vs 10; 101 vs 99
&& (minLength > 2 || (minLength > 1 && (maxLength - minLength) > 1)) // 如:880 vs 100; 8800 vs 10
) {
targetList = new ArrayList<>();
// 将最小值,处理成个位数。缩短无用y轴刻度,使y轴数值差异明显化
// 若存在负数、小数:存在小数-因为整数位数为0故不会进行转化;存在负数-要求最小值即负数的位数比正数最大值位数短即可(否则转换后正数会小于0不符合所有值绝对值都大于零的预期)。
int powerOfTen = minLength - 1;
BigDecimal divisor = BigDecimal.valueOf(10).pow(powerOfTen); // 10的n次方。使得最小值为个位数
for (int i = 0; i < sourceList.size(); i++) { // 不打乱原排序
Object data = sourceList.get(i);
DataWithItemStyleVO dataWithItemStyleVO = null;
String value;
if (data instanceof DataWithItemStyleVO) {
dataWithItemStyleVO = (DataWithItemStyleVO) data;
value = dataWithItemStyleVO.getValue();
} else {
value = String.valueOf(data);
}
final BigDecimal dNum = NumberUtil.parseStrToBigDecimal(value);
final BigDecimal divide = dNum.divide(divisor);
if (Objects.isNull(dataWithItemStyleVO)) {
targetList.add(divide.toString());
} else {
targetList.add(new DataWithItemStyleVO(divide.toString(), dataWithItemStyleVO.getItemStyle()));
}
}
// 追加转换后的单位
NumCountUnitEnum unitEnum = NumCountUnitEnum.valueOf(powerOfTen);
targetDimension = MessageFormat.format("{0}({1})", sourceDimension, unitEnum.getCountUnitName()); // 中文括号前端无法识别
} else {
// 数值小于1000,不做处理
targetList = sourceList.stream().map(data -> (Object) data).collect(Collectors.toList());
targetDimension = sourceDimension;
}
YAxis yaxis = new YAxis();
yaxis.setDataList(targetList);
yaxis.setDimension(targetDimension);
return yaxis;
}
enum NumCountUnitEnum {
ONE(0, "个"),
TWO(1, "十"),
THREE(2, "百"),
FOUR(3, "千"),
FIVE(4, "万"),
SIX(5, "十万"),
SEVEN(6, "百万"),
EIGHT(7, "千万"),
NINE(8, "亿"),
;
/**
* 10的幂次
*/
private Integer powerOfTen;
/**
* 计数单位中文名
*/
private String countUnitName;
NumCountUnitEnum(Integer powerOfTen, String countUnitName) {
this.powerOfTen = powerOfTen;
this.countUnitName = countUnitName;
}
public static NumCountUnitEnum valueOf(Integer powerOfTen) {
ParamException.isTrue(Objects.isNull(powerOfTen), "invalid null powerOfTen");
for (NumCountUnitEnum elem : values()) {
if (powerOfTen.equals(elem.powerOfTen)) {
return elem;
}
}
throw new ParamException(String.format("invalid powerOfTen: [%s]", powerOfTen));
}
public Integer getPowerOfTen() {
return powerOfTen;
}
public String getCountUnitName() {
return countUnitName;
}
}
@Data
class YAxis {
private String dimension;
private List<Object> dataList;
}
}