Merge pull request #9590 from ulleo/dev-v2

feat(图表): 新增区间条形图
This commit is contained in:
ulleo
2024-05-10 16:11:17 +08:00
committed by GitHub
24 changed files with 1230 additions and 127 deletions

View File

@@ -5,11 +5,11 @@ import java.io.Serializable;
/**
* <p>
*
* 组件图表表
* </p>
*
* @author fit2cloud
* @since 2023-08-20
* @since 2024-05-07
*/
@TableName("core_chart_view")
public class CoreChartView implements Serializable {
@@ -191,10 +191,21 @@ public class CoreChartView implements Serializable {
*/
private Boolean jumpActive;
/**
* 复制来源
*/
private Long copyFrom;
/**
* 复制ID
*/
private Long copyId;
/**
* 区间条形图开启时间纬度开启聚合
*/
private Boolean aggregate;
public Long getId() {
return id;
}
@@ -491,6 +502,14 @@ public class CoreChartView implements Serializable {
this.copyId = copyId;
}
public Boolean getAggregate() {
return aggregate;
}
public void setAggregate(Boolean aggregate) {
this.aggregate = aggregate;
}
@Override
public String toString() {
return "CoreChartView{" +
@@ -531,6 +550,7 @@ public class CoreChartView implements Serializable {
", jumpActive = " + jumpActive +
", copyFrom = " + copyFrom +
", copyId = " + copyId +
", aggregate = " + aggregate +
"}";
}
}

View File

@@ -6,11 +6,11 @@ import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* Mapper 接口
* 组件图表表 Mapper 接口
* </p>
*
* @author fit2cloud
* @since 2023-08-20
* @since 2024-05-07
*/
@Mapper
public interface CoreChartViewMapper extends BaseMapper<CoreChartView> {

View File

@@ -34,6 +34,7 @@ import io.dataease.utils.JsonUtil;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
@@ -127,6 +128,35 @@ public class ChartDataManage {
List<ChartViewFieldDTO> yAxisExt = new ArrayList<>(view.getYAxisExt());
yAxis.addAll(yAxisExt);
}
boolean skipBarRange = false;
boolean barRangeDate = false;
if (StringUtils.equalsIgnoreCase(view.getType(), "bar-range")) { //针对区间条形图进行处理
yAxis.clear();
if (CollectionUtils.isNotEmpty(view.getYAxis()) && CollectionUtils.isNotEmpty(view.getYAxisExt())) {
ChartViewFieldDTO axis1 = view.getYAxis().get(0);
ChartViewFieldDTO axis2 = view.getYAxisExt().get(0);
if (StringUtils.equalsIgnoreCase(axis1.getGroupType(), "q") && StringUtils.equalsIgnoreCase(axis2.getGroupType(), "q")) {
yAxis.add(axis1);
yAxis.add(axis2);
} else if (StringUtils.equalsIgnoreCase(axis1.getGroupType(), "d") && axis1.getDeType() == 1 && StringUtils.equalsIgnoreCase(axis2.getGroupType(), "d") && axis2.getDeType() == 1) {
barRangeDate = true;
if (BooleanUtils.isTrue(view.getAggregate())) {
axis1.setSummary("min");
axis2.setSummary("max");
yAxis.add(axis1);
yAxis.add(axis2);
} else {
xAxis.add(axis1);
xAxis.add(axis2);
}
} else {
skipBarRange = true;
}
}
}
List<ChartViewFieldDTO> extStack = new ArrayList<>(view.getExtStack());
List<ChartViewFieldDTO> extBubble = new ArrayList<>(view.getExtBubble());
if (ObjectUtils.isNotEmpty(view.getExtLabel()) && enableExtData(view.getType())) {
@@ -753,6 +783,8 @@ public class ChartDataManage {
mapChart = ChartDataBuild.transLabelChartData(xAxis, yAxis, view, data, isDrill);
} else if (StringUtils.containsIgnoreCase(view.getType(), "quadrant")) {
mapChart = ChartDataBuild.transQuadrantDataAntV(xAxis, yAxis, view, data, extBubble, isDrill);
} else if (StringUtils.equalsIgnoreCase(view.getType(), "bar-range")) {
mapChart = ChartDataBuild.transTimeBarDataAntV(skipBarRange, barRangeDate, xAxisBase, xAxis, yAxis, view, data, isDrill);
} else {
mapChart = ChartDataBuild.transChartDataAntV(xAxis, yAxis, view, data, isDrill);
}

View File

@@ -1,13 +1,17 @@
package io.dataease.chart.utils;
import io.dataease.api.chart.dto.*;
import io.dataease.i18n.Lang;
import io.dataease.i18n.Translator;
import io.dataease.utils.IDUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
@@ -1323,4 +1327,268 @@ public class ChartDataBuild {
return map;
}
public static Map<String, Object> transTimeBarDataAntV(boolean skipBarRange, boolean isDate, List<ChartViewFieldDTO> xAxisBase, List<ChartViewFieldDTO> xAxis, List<ChartViewFieldDTO> yAxis, ChartViewDTO view, List<String[]> data, boolean isDrill) {
Map<String, Object> map = new HashMap<>();
if (skipBarRange) {
map.put("data", new ArrayList<>());
return map;
}
List<Date> dates = new ArrayList<>();
List<BigDecimal> numbers = new ArrayList<>();
ChartViewFieldDTO dateAxis1 = null;
SimpleDateFormat sdf = null;
if (isDate) {
if (BooleanUtils.isTrue(view.getAggregate())) {
dateAxis1 = yAxis.get(0);
} else {
dateAxis1 = xAxis.get(xAxisBase.size());
}
sdf = new SimpleDateFormat(getDateFormat(dateAxis1.getDateStyle(), dateAxis1.getDatePattern()));
}
List<Object> dataList = new ArrayList<>();
for (int i1 = 0; i1 < data.size(); i1++) {
String[] row = data.get(i1);
StringBuilder xField = new StringBuilder();
if (isDrill) {
xField.append(row[xAxis.size() - 1]);
} else {
for (int i = 0; i < xAxisBase.size(); i++) {
if (i == xAxisBase.size() - 1) {
xField.append(row[i]);
} else {
xField.append(row[i]).append("\n");
}
}
}
Map<String, Object> obj = new HashMap<>();
obj.put("field", xField.toString());
obj.put("category", xField.toString());
List<ChartDimensionDTO> dimensionList = new ArrayList<>();
for (int i = 0; i < xAxisBase.size(); i++) {
ChartDimensionDTO chartDimensionDTO = new ChartDimensionDTO();
chartDimensionDTO.setId(xAxis.get(i).getId());
chartDimensionDTO.setValue(row[i]);
dimensionList.add(chartDimensionDTO);
}
if (isDrill) {
int index = xAxis.size() - 1;
ChartDimensionDTO chartDimensionDTO = new ChartDimensionDTO();
chartDimensionDTO.setId(xAxis.get(index).getId());
chartDimensionDTO.setValue(row[index]);
dimensionList.add(chartDimensionDTO);
}
obj.put("dimensionList", dimensionList);
List<Object> values = new ArrayList<>();
if (row[xAxisBase.size()] == null || row[xAxisBase.size() + 1] == null) {
continue;
}
if (isDate) {
int index;
if (BooleanUtils.isTrue(view.getAggregate())) {
index = xAxis.size();
} else {
index = xAxisBase.size();
}
values.add(row[index]);
values.add(row[index + 1]);
obj.put("values", values);
Date date1 = null, date2 = null;
try {
date1 = sdf.parse(row[index]);
if (date1 != null) {
dates.add(date1);
}
} catch (Exception ignore) {
}
try {
date2 = sdf.parse(row[index + 1]);
if (date2 != null) {
dates.add(date2);
}
} catch (Exception ignore) {
}
//间隔时间
obj.put("gap", getTimeGap(date1, date2, dateAxis1.getDateStyle()));
} else {
values.add(new BigDecimal(row[xAxis.size()]));
values.add(new BigDecimal(row[xAxis.size() + 1]));
obj.put("values", values);
numbers.add(new BigDecimal(row[xAxis.size()]));
numbers.add(new BigDecimal(row[xAxis.size() + 1]));
//间隔差
obj.put("gap", new BigDecimal(row[xAxis.size() + 1]).subtract(new BigDecimal(row[xAxis.size()])));
}
dataList.add(obj);
}
if (isDate) {
Date minDate = dates.stream().min(Date::compareTo).orElse(null);
if (minDate != null) {
map.put("minTime", sdf.format(minDate));
}
Date maxDate = dates.stream().max(Date::compareTo).orElse(null);
if (maxDate != null) {
map.put("maxTime", sdf.format(maxDate));
}
} else {
map.put("min", numbers.stream().min(BigDecimal::compareTo).orElse(null));
map.put("max", numbers.stream().max(BigDecimal::compareTo).orElse(null));
}
map.put("isDate", isDate);
map.put("data", dataList);
return map;
}
private static String getDateFormat(String dateStyle, String datePattern) {
String split;
if (StringUtils.equalsIgnoreCase(datePattern, "date_split")) {
split = "/";
} else {
split = "-";
}
switch (dateStyle) {
case "y":
return "yyyy";
case "y_M":
return "yyyy" + split + "MM";
case "y_M_d":
return "yyyy" + split + "MM" + split + "dd";
case "H_m_s":
return "HH:mm:ss";
case "y_M_d_H":
return "yyyy" + split + "MM" + split + "dd" + " HH";
case "y_M_d_H_m":
return "yyyy" + split + "MM" + split + "dd" + " HH:mm";
case "y_M_d_H_m_s":
return "yyyy" + split + "MM" + split + "dd" + " HH:mm:ss";
default:
return "yyyy-MM-dd HH:mm:ss";
}
}
private static String getTimeGap(Date from, Date to, String dateStyle) {
if (from == null || to == null) {
return "";
}
Calendar fromCalender = Calendar.getInstance();
fromCalender.setTime(from);
Calendar toCalender = Calendar.getInstance();
toCalender.setTime(to);
long yearGap = 0;
long monthGap = 0;
long dayGap = (toCalender.getTimeInMillis() - fromCalender.getTimeInMillis()) / (1000 * 3600 * 24);
long hourGap = ((toCalender.getTimeInMillis() - fromCalender.getTimeInMillis()) / (1000 * 3600)) % 24;
long minuteGap = ((toCalender.getTimeInMillis() - fromCalender.getTimeInMillis()) / (1000 * 60)) % 60;
long secondGap = ((toCalender.getTimeInMillis() - fromCalender.getTimeInMillis()) / 1000) % 60;
String language = "zh-CN"; //国际化
Lang lang = Lang.getLangWithoutDefault(language);
boolean isEnUs = Lang.en_US.equals(lang);
String splitter = isEnUs ? " " : "";
String yearGapStr = "";
String monthGapStr = "";
String dayGapStr = "";
if (dayGap != 0) {
dayGapStr = dayGap + splitter + Translator.get("i18n_day") + (isEnUs && dayGap != 1 ? "s" : "");
}
String hourGapStr = "";
if (hourGap != 0) {
hourGapStr = hourGap + splitter + Translator.get("i18n_hour") + (isEnUs && hourGap != 1 ? "s" : "");
}
String minuteGapStr = "";
if (minuteGap != 0) {
minuteGapStr = minuteGap + splitter + Translator.get("i18n_minute") + (isEnUs && minuteGap != 1 ? "s" : "");
}
String secondGapStr = "";
if (secondGap != 0) {
secondGapStr = secondGap + splitter + Translator.get("i18n_second") + (isEnUs && secondGap != 1 ? "s" : "");
}
List<String> list = new ArrayList<>();
switch (dateStyle) {
case "y":
yearGap = toCalender.get(Calendar.YEAR) - fromCalender.get(Calendar.YEAR);
yearGapStr = yearGap == 0 ? "" : (yearGap + splitter + Translator.get("i18n_year") + (isEnUs && yearGap != 1 ? "s" : ""));
return yearGapStr;
case "y_M":
yearGap = ((toCalender.get(Calendar.YEAR) - fromCalender.get(Calendar.YEAR)) * 12L + (toCalender.get(Calendar.MONTH) - fromCalender.get(Calendar.MONTH))) / 12;
monthGap = ((toCalender.get(Calendar.YEAR) - fromCalender.get(Calendar.YEAR)) * 12L + (toCalender.get(Calendar.MONTH) - fromCalender.get(Calendar.MONTH))) % 12;
yearGapStr = yearGap == 0 ? "" : (yearGap + splitter + Translator.get("i18n_year") + (isEnUs && yearGap != 1 ? "s" : ""));
monthGapStr = monthGap == 0 ? "" : (monthGap + splitter + Translator.get("i18n_month") + (isEnUs && monthGap != 1 ? "s" : ""));
if (!yearGapStr.isEmpty()) {
list.add(yearGapStr);
}
if (!monthGapStr.isEmpty()) {
list.add(monthGapStr);
}
return StringUtils.join(list, splitter);
case "y_M_d":
return dayGapStr;
case "y_M_d_H":
if (!dayGapStr.isEmpty()) {
list.add(dayGapStr);
}
if (!hourGapStr.isEmpty()) {
list.add(hourGapStr);
}
return StringUtils.join(list, splitter);
case "y_M_d_H_m":
if (!dayGapStr.isEmpty()) {
list.add(dayGapStr);
}
if (!hourGapStr.isEmpty()) {
list.add(hourGapStr);
}
if (!minuteGapStr.isEmpty()) {
list.add(minuteGapStr);
}
return StringUtils.join(list, splitter);
case "H_m_s":
case "y_M_d_H_m_s":
if (!dayGapStr.isEmpty()) {
list.add(dayGapStr);
}
if (!hourGapStr.isEmpty()) {
list.add(hourGapStr);
}
if (!minuteGapStr.isEmpty()) {
list.add(minuteGapStr);
}
if (!secondGapStr.isEmpty()) {
list.add(secondGapStr);
}
return StringUtils.join(list, splitter);
default:
return "";
}
}
}

View File

@@ -51,3 +51,10 @@ i18n_error_login_type=error login type
i18n_schema_is_empty=Schema is empty!
i18n_table_name_repeat=Has duplicate name:
i18n_sql_not_empty=SQL cannot be empty!
i18n_year=Year
i18n_month=Month
i18n_day=Day
i18n_hour=Hour
i18n_minute=Minute
i18n_second=Second

View File

@@ -64,3 +64,10 @@ i18n_sql_not_empty=sql \u4E0D\u80FD\u4E3A\u7A7A
i18n_menu.parameter=\u7CFB\u7EDF\u53C2\u6570
i18n_user_old_pwd_error=\u539F\u59CB\u5BC6\u7801\u9519\u8BEF
i18n_menu.toolbox-log=\u64CD\u4F5C\u65E5\u5FD7
i18n_year=\u5E74
i18n_month=\u6708
i18n_day=\u5929
i18n_hour=\u5C0F\u65F6
i18n_minute=\u5206\u949F
i18n_second=\u79D2

View File

@@ -31,7 +31,7 @@ i18n_union_field_can_not_empty=\u95DC\u806F\u5B57\u6BB5\u4E0D\u80FD\u70BA\u7A7A
i18n_table_duplicate=\u76F8\u540C\u7BC0\u9EDE\u9700\u91CD\u65B0\u62D6\u5165\u624D\u80FD\u7E7C\u7E8C\u65B0\u5EFA\u6578\u64DA\u96C6
i18n_no_column_permission=\u6C92\u6709\u5217\u6B0A\u9650
i18n_fetch_error=SQL\u57F7\u884C\u5931\u6557\uFF0C\u8ACB\u6AA2\u67E5\u8868\u3001\u5B57\u6BB5\u3001\u95DC\u806F\u95DC\u7CFB\u7B49\u4FE1\u606F\u662F\u5426\u6B63\u78BA\u4E26\u91CD\u65B0\u7DE8\u8F2F\u3002
i18n_no_datasource_permission=\u65e0\u6570\u636e\u6e90\u8bbf\u95ee\u6743\u9650
i18n_no_datasource_permission=\u65E0\u6570\u636E\u6E90\u8BBF\u95EE\u6743\u9650
i18n_field_circular_ref=\u5B57\u6BB5\u5B58\u5728\u5FAA\u74B0\u5F15\u7528
@@ -51,4 +51,11 @@ i18n_login_name_pwd_err=\u7528\u6236\u540D\u6216\u5BC6\u78BC\u932F\u8AA4
i18n_error_login_type=\u767B\u9304\u985E\u578B\u932F\u8AA4
i18n_schema_is_empty=schema\u70BA\u7A7A\uFF01
i18n_table_name_repeat=\u540D\u7A31\u91CD\u8907:
i18n_sql_not_empty=sql\u4e0d\u80fd\u70ba\u7a7a
i18n_sql_not_empty=sql\u4E0D\u80FD\u70BA\u7A7A
i18n_year=\u5E74
i18n_month=\u6708
i18n_day=\u5929
i18n_hour=\u5C0F\u6642
i18n_minute=\u5206\u9418
i18n_second=\u79D2