forked from github/dataease
Merge pull request #1423 from dataease/pr@dev@feat_compare
feat(视图): 同比环比
This commit is contained in:
commit
a78248e2fc
@ -0,0 +1,21 @@
|
||||
package io.dataease.dto.chart;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author gin
|
||||
* @Date 2021/12/9 2:48 下午
|
||||
*/
|
||||
@Data
|
||||
public class ChartFieldCompareCustomDTO {
|
||||
private String field;
|
||||
private String calcType;
|
||||
private String timeType;
|
||||
private String currentTime;
|
||||
private String compareTime;
|
||||
private List<String> currentTimeRange;
|
||||
private List<String> compareTimeRange;
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package io.dataease.dto.chart;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author gin
|
||||
* @Date 2021/12/9 2:48 下午
|
||||
*/
|
||||
@Data
|
||||
public class ChartFieldCompareDTO {
|
||||
private String type;
|
||||
private String resultData;
|
||||
private String field;
|
||||
private ChartFieldCompareCustomDTO custom;
|
||||
}
|
@ -46,4 +46,6 @@ public class ChartViewFieldDTO implements Serializable {
|
||||
private Integer extField;
|
||||
|
||||
private String chartType;
|
||||
|
||||
private ChartFieldCompareDTO compareCalc;
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
package io.dataease.service.chart;
|
||||
|
||||
/**
|
||||
* @Author gin
|
||||
* @Date 2021/12/9 3:58 下午
|
||||
*/
|
||||
public class ChartConstants {
|
||||
public static final String YEAR_MOM = "year_mom";
|
||||
public static final String MONTH_MOM = "month_mom";
|
||||
public static final String YEAR_YOY = "year_yoy";
|
||||
public static final String DAY_MOM = "day_mom";
|
||||
public static final String MONTH_YOY = "month_yoy";
|
||||
}
|
@ -36,6 +36,7 @@ import org.springframework.stereotype.Service;
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Collectors;
|
||||
@ -433,6 +434,87 @@ public class ChartViewService {
|
||||
}
|
||||
}
|
||||
|
||||
// 同比/环比计算,通过对比类型和数据设置,计算出对应指标的结果,然后替换结果data数组中的对应元素
|
||||
// 如果因维度变化(如时间字段缺失,时间字段的展示格式变化)导致无法计算结果的,则结果data数组中的对应元素全置为null
|
||||
// 根据不同图表类型,获得需要替换的指标index array
|
||||
for (int i = 0; i < yAxis.size(); i++) {
|
||||
ChartViewFieldDTO chartViewFieldDTO = yAxis.get(i);
|
||||
ChartFieldCompareDTO compareCalc = chartViewFieldDTO.getCompareCalc();
|
||||
if (ObjectUtils.isEmpty(compareCalc)) {
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.isNotEmpty(compareCalc.getType())
|
||||
&& !StringUtils.equalsIgnoreCase(compareCalc.getType(), "none")) {
|
||||
String compareFieldId = compareCalc.getField();// 选中字段
|
||||
String resultData = compareCalc.getResultData();// 数据设置
|
||||
// 获取选中字段以及下标
|
||||
List<ChartViewFieldDTO> checkedField = new ArrayList<>(xAxis);
|
||||
if (StringUtils.containsIgnoreCase(view.getType(), "stack")) {
|
||||
checkedField.addAll(extStack);
|
||||
}
|
||||
int timeIndex = 0;// 时间字段下标
|
||||
ChartViewFieldDTO timeField = null;
|
||||
for (int j = 0; j < checkedField.size(); j++) {
|
||||
if (StringUtils.equalsIgnoreCase(checkedField.get(j).getId(), compareFieldId)) {
|
||||
timeIndex = j;
|
||||
timeField = checkedField.get(j);
|
||||
}
|
||||
}
|
||||
// 计算指标对应的下标
|
||||
int dataIndex = 0;// 数据字段下标
|
||||
if (StringUtils.containsIgnoreCase(view.getType(), "stack")) {
|
||||
dataIndex = xAxis.size() + extStack.size() + i;
|
||||
} else {
|
||||
dataIndex = xAxis.size() + i;
|
||||
}
|
||||
// 无选中字段,或者选中字段已经不在维度list中,或者选中字段日期格式不符合对比类型的,直接将对应数据置为null
|
||||
if (ObjectUtils.isEmpty(timeField) || !checkCalcType(timeField.getDateStyle(), compareCalc.getType())) {
|
||||
// set null
|
||||
for (String[] item : data) {
|
||||
item[dataIndex] = null;
|
||||
}
|
||||
} else {
|
||||
// 计算 同比/环比
|
||||
// 1,处理当期数据;2,根据type计算上一期数据;3,根据resultData计算结果
|
||||
Map<String, String> currentMap = new LinkedHashMap<>();
|
||||
for (String[] item : data) {
|
||||
currentMap.put(item[timeIndex], item[dataIndex]);
|
||||
}
|
||||
|
||||
Iterator<Map.Entry<String, String>> iterator = currentMap.entrySet().iterator();
|
||||
int index = 0;
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, String> next = iterator.next();
|
||||
String cTime = next.getKey();
|
||||
String cValue = next.getValue();
|
||||
|
||||
String lastTime = calcLastTime(cTime, compareCalc.getType(), timeField.getDateStyle());
|
||||
String lastValue = currentMap.get(lastTime);
|
||||
if (StringUtils.isEmpty(cValue) || StringUtils.isEmpty(lastValue)) {
|
||||
data.get(index)[dataIndex] = null;
|
||||
} else {
|
||||
if (StringUtils.equalsIgnoreCase(resultData, "sub")) {
|
||||
data.get(index)[dataIndex] = new BigDecimal(cValue).subtract(new BigDecimal(lastValue)).toString();
|
||||
} else if (StringUtils.equalsIgnoreCase(resultData, "percent")) {
|
||||
if (StringUtils.isEmpty(lastValue)) {
|
||||
data.get(index)[dataIndex] = null;
|
||||
} else {
|
||||
data.get(index)[dataIndex] = new BigDecimal(cValue)
|
||||
.divide(new BigDecimal(lastValue), 2, RoundingMode.HALF_UP)
|
||||
.subtract(new BigDecimal(1))
|
||||
.setScale(2, RoundingMode.HALF_UP)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 构建结果
|
||||
Map<String, Object> map = new TreeMap<>();
|
||||
// 图表组件可再扩展
|
||||
Map<String, Object> mapChart = new HashMap<>();
|
||||
@ -491,6 +573,68 @@ public class ChartViewService {
|
||||
return dto;
|
||||
}
|
||||
|
||||
private boolean checkCalcType(String dateStyle, String calcType) {
|
||||
switch (dateStyle) {
|
||||
case "y":
|
||||
return StringUtils.equalsIgnoreCase(calcType, "year_mom");
|
||||
case "y_M":
|
||||
return StringUtils.equalsIgnoreCase(calcType, "month_mom")
|
||||
|| StringUtils.equalsIgnoreCase(calcType, "year_yoy");
|
||||
case "y_M_d":
|
||||
return StringUtils.equalsIgnoreCase(calcType, "day_mom")
|
||||
|| StringUtils.equalsIgnoreCase(calcType, "month_yoy")
|
||||
|| StringUtils.equalsIgnoreCase(calcType, "year_yoy");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String calcLastTime(String cTime, String type, String dateStyle) throws Exception {
|
||||
String lastTime = null;
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
if (StringUtils.equalsIgnoreCase(type, ChartConstants.YEAR_MOM)) {
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy");
|
||||
Date date = simpleDateFormat.parse(cTime);
|
||||
calendar.setTime(date);
|
||||
calendar.add(Calendar.YEAR, -1);
|
||||
lastTime = simpleDateFormat.format(calendar.getTime());
|
||||
} else if (StringUtils.equalsIgnoreCase(type, ChartConstants.MONTH_MOM)) {
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM");
|
||||
Date date = simpleDateFormat.parse(cTime);
|
||||
calendar.setTime(date);
|
||||
calendar.add(Calendar.MONTH, -1);
|
||||
lastTime = simpleDateFormat.format(calendar.getTime());
|
||||
} else if (StringUtils.equalsIgnoreCase(type, ChartConstants.YEAR_YOY)) {
|
||||
SimpleDateFormat simpleDateFormat = null;
|
||||
if (StringUtils.equalsIgnoreCase(dateStyle, "y_M")) {
|
||||
simpleDateFormat = new SimpleDateFormat("yyyy-MM");
|
||||
} else if (StringUtils.equalsIgnoreCase(dateStyle, "y_M_d")) {
|
||||
simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
}
|
||||
Date date = simpleDateFormat.parse(cTime);
|
||||
calendar.setTime(date);
|
||||
calendar.add(Calendar.YEAR, -1);
|
||||
lastTime = simpleDateFormat.format(calendar.getTime());
|
||||
} else if (StringUtils.equalsIgnoreCase(type, ChartConstants.DAY_MOM)) {
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Date date = simpleDateFormat.parse(cTime);
|
||||
calendar.setTime(date);
|
||||
calendar.add(Calendar.DAY_OF_MONTH, -1);
|
||||
lastTime = simpleDateFormat.format(calendar.getTime());
|
||||
} else if (StringUtils.equalsIgnoreCase(type, ChartConstants.MONTH_YOY)) {
|
||||
SimpleDateFormat simpleDateFormat = null;
|
||||
if (StringUtils.equalsIgnoreCase(dateStyle, "y_M")) {
|
||||
simpleDateFormat = new SimpleDateFormat("yyyy-MM");
|
||||
} else if (StringUtils.equalsIgnoreCase(dateStyle, "y_M_d")) {
|
||||
simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
}
|
||||
Date date = simpleDateFormat.parse(cTime);
|
||||
calendar.setTime(date);
|
||||
calendar.add(Calendar.MONTH, -1);
|
||||
lastTime = simpleDateFormat.format(calendar.getTime());
|
||||
}
|
||||
return lastTime;
|
||||
}
|
||||
|
||||
private boolean checkDrillExist(List<ChartViewFieldDTO> xAxis, List<ChartViewFieldDTO> extStack, ChartViewFieldDTO dto, ChartViewWithBLOBs view) {
|
||||
if (CollectionUtils.isNotEmpty(xAxis)) {
|
||||
for (ChartViewFieldDTO x : xAxis) {
|
||||
|
@ -11,6 +11,9 @@
|
||||
</span>
|
||||
<span class="item-span-style" :title="item.name">{{ item.name }}</span>
|
||||
<span v-if="item.summary" class="summary-span">{{ $t('chart.'+item.summary) }}</span>
|
||||
<span v-if="item.deType === 1" class="summary-span">
|
||||
{{ $t('chart.' + item.dateStyle) }}
|
||||
</span>
|
||||
</el-tag>
|
||||
<el-dropdown v-else trigger="click" size="mini" @command="clickItem">
|
||||
<span class="el-dropdown-link">
|
||||
@ -25,6 +28,9 @@
|
||||
</span>
|
||||
<span class="item-span-style" :title="item.name">{{ item.name }}</span>
|
||||
<span v-if="item.summary" class="summary-span">{{ $t('chart.'+item.summary) }}</span>
|
||||
<span v-if="item.deType === 1" class="summary-span">
|
||||
{{ $t('chart.' + item.dateStyle) }}
|
||||
</span>
|
||||
<i class="el-icon-arrow-down el-icon--right" style="position: absolute;top: 6px;right: 10px;" />
|
||||
</el-tag>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
@ -239,7 +245,7 @@ export default {
|
||||
|
||||
.item-span-style{
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
width: 70px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
@ -254,6 +260,6 @@ export default {
|
||||
margin-left: 4px;
|
||||
color: #878d9f;
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
right: 25px;
|
||||
}
|
||||
</style>
|
||||
|
@ -10,6 +10,9 @@
|
||||
<svg-icon v-if="item.sort === 'desc'" icon-class="sort-desc" class-name="field-icon-sort" />
|
||||
</span>
|
||||
<span class="item-span-style" :title="item.name">{{ item.name }}</span>
|
||||
<span v-if="item.deType === 1" class="summary-span">
|
||||
{{ $t('chart.' + item.dateStyle) }}
|
||||
</span>
|
||||
</el-tag>
|
||||
<el-dropdown v-else trigger="click" size="mini" @command="clickItem">
|
||||
<span class="el-dropdown-link">
|
||||
@ -23,6 +26,9 @@
|
||||
<svg-icon v-if="item.sort === 'desc'" icon-class="sort-desc" class-name="field-icon-sort" />
|
||||
</span>
|
||||
<span class="item-span-style" :title="item.name">{{ item.name }}</span>
|
||||
<span v-if="item.deType === 1" class="summary-span">
|
||||
{{ $t('chart.' + item.dateStyle) }}
|
||||
</span>
|
||||
<i class="el-icon-arrow-down el-icon--right" style="position: absolute;top: 6px;right: 10px;" />
|
||||
</el-tag>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
@ -32,7 +38,7 @@
|
||||
<span>
|
||||
<i class="el-icon-sort" />
|
||||
<span>{{ $t('chart.sort') }}</span>
|
||||
<span class="summary-span">({{ $t('chart.'+item.sort) }})</span>
|
||||
<span class="summary-span-item">({{ $t('chart.'+item.sort) }})</span>
|
||||
</span>
|
||||
<i class="el-icon-arrow-right el-icon--right" />
|
||||
</span>
|
||||
@ -53,7 +59,7 @@
|
||||
<span>
|
||||
<i class="el-icon-c-scale-to-original" />
|
||||
<span>{{ $t('chart.dateStyle') }}</span>
|
||||
<span class="summary-span">({{ $t('chart.'+item.dateStyle) }})</span>
|
||||
<span class="summary-span-item">({{ $t('chart.'+item.dateStyle) }})</span>
|
||||
</span>
|
||||
<i class="el-icon-arrow-right el-icon--right" />
|
||||
</span>
|
||||
@ -73,7 +79,7 @@
|
||||
<span>
|
||||
<i class="el-icon-timer" />
|
||||
<span>{{ $t('chart.datePattern') }}</span>
|
||||
<span class="summary-span">({{ $t('chart.'+item.datePattern) }})</span>
|
||||
<span class="summary-span-item">({{ $t('chart.'+item.datePattern) }})</span>
|
||||
</span>
|
||||
<i class="el-icon-arrow-right el-icon--right" />
|
||||
</span>
|
||||
@ -215,7 +221,9 @@ export default {
|
||||
|
||||
.summary-span{
|
||||
margin-left: 4px;
|
||||
color: #878d9f;;
|
||||
color: #878d9f;
|
||||
position: absolute;
|
||||
right: 25px;
|
||||
}
|
||||
|
||||
.inner-dropdown-menu{
|
||||
@ -227,9 +235,14 @@ export default {
|
||||
|
||||
.item-span-style{
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
width: 70px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.summary-span-item{
|
||||
margin-left: 4px;
|
||||
color: #878d9f;
|
||||
}
|
||||
</style>
|
||||
|
@ -195,7 +195,7 @@ export default {
|
||||
const t2 = extStack.filter(ele => {
|
||||
return ele.deType === 1
|
||||
})
|
||||
if ((t1.length > 0 || t2.length > 0) && this.chart.type !== 'text' && this.chart.type !== 'gauge' && this.chart.type !== 'liquid') {
|
||||
if ((t1.length + t2.length === 1) && this.chart.type !== 'text' && this.chart.type !== 'gauge' && this.chart.type !== 'liquid') {
|
||||
this.disableEditCompare = false
|
||||
} else {
|
||||
this.disableEditCompare = true
|
||||
|
Loading…
Reference in New Issue
Block a user