feat(图表): 拆分双轴图左右轴数据

This commit is contained in:
ulleo 2024-06-06 16:38:20 +08:00
parent dfca84679d
commit 98161b4c8d
9 changed files with 376 additions and 247 deletions

View File

@ -82,7 +82,6 @@ public class ChartDataManage {
chartExtRequest = new ChartExtRequest();
}
ChartViewDTO chartViewDTO = new ChartViewDTO();
if (ObjectUtils.isEmpty(view)) {
DEException.throwException(ResultCode.DATA_IS_WRONG.code(), Translator.get("i18n_chart_delete"));
}
@ -108,6 +107,54 @@ public class ChartDataManage {
// get all fields
List<ChartViewFieldDTO> allFields = getAllChartFields(view);
ChartViewDTO chartViewDTO = null;
if (StringUtils.equalsIgnoreCase(view.getType(), "chart-mix")) {
//左轴右轴需要分别调用一次查询
String viewJson = (String) JsonUtil.toJSONString(view);
Map<String, Object> data = new HashMap<>();
//针对左轴删除yAxisExt
ChartViewDTO view1 = JsonUtil.parseObject(viewJson, ChartViewDTO.class);
view1.setYAxisExt(new ArrayList<>());
if (view1.getSenior() != null) {
ChartSeniorAssistCfgDTO assistLineCfg1 = JsonUtil.parseObject((String) JsonUtil.toJSONString(view1.getSenior().get("assistLineCfg")), ChartSeniorAssistCfgDTO.class);
if (assistLineCfg1 != null && assistLineCfg1.isEnable()) {
List<ChartSeniorAssistDTO> assistLines = assistLineCfg1.getAssistLine();
//去除右轴辅助线
assistLineCfg1.setAssistLine(assistLines.stream().filter(d -> StringUtils.equalsIgnoreCase(d.getYAxisType(), "left")).collect(Collectors.toList()));
view1.getSenior().put("assistLineCfg", assistLineCfg1);
}
}
ChartViewDTO left = calcData(view1, chartExtRequest, allFields, viewFields);
data.put("left", left.getData());
//针对右轴删除yAxis
ChartViewDTO view2 = JsonUtil.parseObject(viewJson, ChartViewDTO.class);
view2.setYAxis(new ArrayList<>());
if (view2.getSenior() != null) {
ChartSeniorAssistCfgDTO assistLineCfg2 = JsonUtil.parseObject((String) JsonUtil.toJSONString(view2.getSenior().get("assistLineCfg")), ChartSeniorAssistCfgDTO.class);
if (assistLineCfg2 != null && assistLineCfg2.isEnable()) {
List<ChartSeniorAssistDTO> assistLines = assistLineCfg2.getAssistLine();
//去除左轴辅助线
assistLineCfg2.setAssistLine(assistLines.stream().filter(d -> StringUtils.equalsIgnoreCase(d.getYAxisType(), "right")).collect(Collectors.toList()));
view2.getSenior().put("assistLineCfg", assistLineCfg2);
}
}
ChartViewDTO right = calcData(view2, chartExtRequest, allFields, viewFields);
data.put("right", right.getData());
//重新组装
chartViewDTO = BeanUtils.copyBean(new ChartViewDTO(), left);
chartViewDTO.setYAxisExt(view.getYAxisExt());
chartViewDTO.setData(data);
chartViewDTO.setSenior(view.getSenior());
} else {
chartViewDTO = calcData(view, chartExtRequest, allFields, viewFields);
}
return chartViewDTO;
}
public ChartViewDTO calcData(ChartViewDTO view, ChartExtRequest chartExtRequest, List<ChartViewFieldDTO> allFields, List<ChartViewFieldDTO> viewFields) throws Exception {
List<ChartViewFieldDTO> xAxisBase = new ArrayList<>(view.getXAxis());
List<ChartViewFieldDTO> xAxis = new ArrayList<>(view.getXAxis());
@ -557,7 +604,7 @@ public class ChartDataManage {
} else if (StringUtils.containsIgnoreCase(view.getType(), "quadrant")) {
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, transFields(allFields), crossDs, dsMap);
yAxis.addAll(extBubble);
if(ObjectUtils.isNotEmpty(view.getExtTooltip())){
if (ObjectUtils.isNotEmpty(view.getExtTooltip())) {
yAxis.addAll(new ArrayList<>(view.getExtTooltip()));
}
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, transFields(allFields), crossDs, dsMap);
@ -803,7 +850,7 @@ public class ChartDataManage {
} else {
mapTableNormal = ChartDataBuild.transTableNormal(xAxis, yAxis, view, data, extStack, desensitizationList);
}
chartViewDTO = uniteViewResult(datasourceRequest.getQuery(), mapChart, mapTableNormal, view, isDrill, drillFilters, dynamicAssistFields, assistData);
ChartViewDTO chartViewDTO = uniteViewResult(datasourceRequest.getQuery(), mapChart, mapTableNormal, view, isDrill, drillFilters, dynamicAssistFields, assistData);
chartViewDTO.setTotalPage(totalPage);
chartViewDTO.setTotalItems(totalItems);
return chartViewDTO;

View File

@ -4,6 +4,7 @@ import io.dataease.api.chart.ChartDataApi;
import io.dataease.api.chart.dto.ChartViewDTO;
import io.dataease.api.chart.dto.ViewDetailField;
import io.dataease.api.chart.request.ChartExcelRequest;
import io.dataease.api.chart.request.ChartExcelRequestInner;
import io.dataease.chart.manage.ChartDataManage;
import io.dataease.constant.AuthConstant;
import io.dataease.constant.CommonConstants;
@ -32,7 +33,6 @@ import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author Junjun
@ -90,14 +90,8 @@ public class ChartDataServer implements ChartDataApi {
OutputStream outputStream = response.getOutputStream();
try {
findExcelData(request);
List<Object[]> details = request.getDetails();
Integer[] excelTypes = request.getExcelTypes();
details.add(0, request.getHeader());
Workbook wb = new SXSSFWorkbook();
//明细sheet
Sheet detailsSheet = wb.createSheet("数据");
//给单元格设置样式
CellStyle cellStyle = wb.createCellStyle();
@ -113,109 +107,35 @@ public class ChartDataServer implements ChartDataApi {
//设置单元格填充样式(使用纯色背景颜色填充)
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
Boolean mergeHead = false;
ViewDetailField[] detailFields = request.getDetailFields();
if (ArrayUtils.isNotEmpty(detailFields)) {
cellStyle.setBorderTop(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderLeft(BorderStyle.THIN);
String[] detailField = Arrays.stream(detailFields).map(field -> field.getName()).collect(Collectors.toList()).toArray(new String[detailFields.length]);
if (CollectionUtils.isEmpty(request.getMultiInfo())) {
List<Object[]> details = request.getDetails();
Integer[] excelTypes = request.getExcelTypes();
details.add(0, request.getHeader());
ViewDetailField[] detailFields = request.getDetailFields();
Object[] header = request.getHeader();
Row row = detailsSheet.createRow(0);
int headLen = header.length;
int detailFieldLen = detailField.length;
for (int i = 0; i < headLen; i++) {
Cell cell = row.createCell(i);
cell.setCellValue(header[i].toString());
if (i < headLen - 1) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 1, i, i);
detailsSheet.addMergedRegion(cellRangeAddress);
} else {
for (int j = i + 1; j < detailFieldLen + i; j++) {
row.createCell(j).setCellStyle(cellStyle);
}
CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 0, i, i + detailFieldLen - 1);
detailsSheet.addMergedRegion(cellRangeAddress);
}
cell.setCellStyle(cellStyle);
detailsSheet.setColumnWidth(i, 255 * 20);
}
Row detailRow = detailsSheet.createRow(1);
for (int i = 0; i < headLen - 1; i++) {
Cell cell = detailRow.createCell(i);
cell.setCellStyle(cellStyle);
}
for (int i = 0; i < detailFieldLen; i++) {
int colIndex = headLen - 1 + i;
Cell cell = detailRow.createCell(colIndex);
cell.setCellValue(detailField[i]);
cell.setCellStyle(cellStyle);
detailsSheet.setColumnWidth(colIndex, 255 * 20);
}
details.add(1, detailField);
mergeHead = true;
}
if (CollectionUtils.isNotEmpty(details) && (!mergeHead || details.size() > 2)) {
int realDetailRowIndex = 2;
for (int i = (mergeHead ? 2 : 0); i < details.size(); i++) {
Row row = detailsSheet.createRow(realDetailRowIndex > 2 ? realDetailRowIndex : i);
Object[] rowData = details.get(i);
if (rowData != null) {
for (int j = 0; j < rowData.length; j++) {
Object cellValObj = rowData[j];
if (mergeHead && j == rowData.length - 1 && (cellValObj.getClass().isArray() || cellValObj instanceof ArrayList)) {
Object[] detailRowArray = ((List<Object>) cellValObj).toArray(new Object[((List<?>) cellValObj).size()]);
int detailRowArrayLen = detailRowArray.length;
int temlJ = j;
while (detailRowArrayLen > 1 && temlJ-- > 0) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(realDetailRowIndex, realDetailRowIndex + detailRowArrayLen - 1, temlJ, temlJ);
detailsSheet.addMergedRegion(cellRangeAddress);
}
//明细sheet
Sheet detailsSheet = wb.createSheet("数据");
for (int k = 0; k < detailRowArrayLen; k++) {
List<Object> detailRows = (List<Object>) detailRowArray[k];
Row curRow = row;
if (k > 0) {
curRow = detailsSheet.createRow(realDetailRowIndex + k);
}
setExcelData(detailsSheet, cellStyle, header, details, detailFields, excelTypes);
} else {
//多个sheet
for (int i = 0; i < request.getMultiInfo().size(); i++) {
ChartExcelRequestInner requestInner = request.getMultiInfo().get(i);
for (int l = 0; l < detailRows.size(); l++) {
Object col = detailRows.get(l);
Cell cell = curRow.createCell(j + l);
cell.setCellValue(col.toString());
}
}
realDetailRowIndex += detailRowArrayLen;
break;
}
List<Object[]> details = requestInner.getDetails();
Integer[] excelTypes = requestInner.getExcelTypes();
details.add(0, requestInner.getHeader());
ViewDetailField[] detailFields = requestInner.getDetailFields();
Object[] header = requestInner.getHeader();
Cell cell = row.createCell(j);
if (i == 0) {// 头部
cell.setCellValue(cellValObj.toString());
cell.setCellStyle(cellStyle);
//设置列的宽度
detailsSheet.setColumnWidth(j, 255 * 20);
} else if (cellValObj != null) {
try {
// with DataType
if ((excelTypes[j].equals(DeTypeConstants.DE_INT) || excelTypes[j].equals(DeTypeConstants.DE_FLOAT)) && StringUtils.isNotEmpty(cellValObj.toString())) {
cell.setCellValue(Double.valueOf(cellValObj.toString()));
} else {
cell.setCellValue(cellValObj.toString());
}
} catch (Exception e) {
LogUtil.warn("export excel data transform error");
}
}
//明细sheet
Sheet detailsSheet = wb.createSheet("数据 " + (i + 1));
}
}
setExcelData(detailsSheet, cellStyle, header, details, detailFields, excelTypes);
}
}
response.setContentType("application/vnd.ms-excel");
//文件名称
response.setHeader("Content-disposition", "attachment;filename=" + request.getViewName() + ".xlsx");
@ -227,6 +147,111 @@ public class ChartDataServer implements ChartDataApi {
}
}
public static void setExcelData(Sheet detailsSheet, CellStyle cellStyle, Object[] header, List<Object[]> details, ViewDetailField[] detailFields, Integer[] excelTypes) {
boolean mergeHead = false;
if (ArrayUtils.isNotEmpty(detailFields)) {
cellStyle.setBorderTop(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderLeft(BorderStyle.THIN);
String[] detailField = Arrays.stream(detailFields).map(ViewDetailField::getName).toList().toArray(new String[detailFields.length]);
Row row = detailsSheet.createRow(0);
int headLen = header.length;
int detailFieldLen = detailField.length;
for (int i = 0; i < headLen; i++) {
Cell cell = row.createCell(i);
cell.setCellValue(header[i].toString());
if (i < headLen - 1) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 1, i, i);
detailsSheet.addMergedRegion(cellRangeAddress);
} else {
for (int j = i + 1; j < detailFieldLen + i; j++) {
row.createCell(j).setCellStyle(cellStyle);
}
CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 0, i, i + detailFieldLen - 1);
detailsSheet.addMergedRegion(cellRangeAddress);
}
cell.setCellStyle(cellStyle);
detailsSheet.setColumnWidth(i, 255 * 20);
}
Row detailRow = detailsSheet.createRow(1);
for (int i = 0; i < headLen - 1; i++) {
Cell cell = detailRow.createCell(i);
cell.setCellStyle(cellStyle);
}
for (int i = 0; i < detailFieldLen; i++) {
int colIndex = headLen - 1 + i;
Cell cell = detailRow.createCell(colIndex);
cell.setCellValue(detailField[i]);
cell.setCellStyle(cellStyle);
detailsSheet.setColumnWidth(colIndex, 255 * 20);
}
details.add(1, detailField);
mergeHead = true;
}
if (CollectionUtils.isNotEmpty(details) && (!mergeHead || details.size() > 2)) {
int realDetailRowIndex = 2;
for (int i = (mergeHead ? 2 : 0); i < details.size(); i++) {
Row row = detailsSheet.createRow(realDetailRowIndex > 2 ? realDetailRowIndex : i);
Object[] rowData = details.get(i);
if (rowData != null) {
for (int j = 0; j < rowData.length; j++) {
Object cellValObj = rowData[j];
if (mergeHead && j == rowData.length - 1 && (cellValObj.getClass().isArray() || cellValObj instanceof ArrayList)) {
Object[] detailRowArray = ((List<Object>) cellValObj).toArray(new Object[((List<?>) cellValObj).size()]);
int detailRowArrayLen = detailRowArray.length;
int temlJ = j;
while (detailRowArrayLen > 1 && temlJ-- > 0) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(realDetailRowIndex, realDetailRowIndex + detailRowArrayLen - 1, temlJ, temlJ);
detailsSheet.addMergedRegion(cellRangeAddress);
}
for (int k = 0; k < detailRowArrayLen; k++) {
List<Object> detailRows = (List<Object>) detailRowArray[k];
Row curRow = row;
if (k > 0) {
curRow = detailsSheet.createRow(realDetailRowIndex + k);
}
for (int l = 0; l < detailRows.size(); l++) {
Object col = detailRows.get(l);
Cell cell = curRow.createCell(j + l);
cell.setCellValue(col.toString());
}
}
realDetailRowIndex += detailRowArrayLen;
break;
}
Cell cell = row.createCell(j);
if (i == 0) {// 头部
cell.setCellValue(cellValObj.toString());
cell.setCellStyle(cellStyle);
//设置列的宽度
detailsSheet.setColumnWidth(j, 255 * 20);
} else if (cellValObj != null) {
try {
// with DataType
if ((excelTypes[j].equals(DeTypeConstants.DE_INT) || excelTypes[j].equals(DeTypeConstants.DE_FLOAT)) && StringUtils.isNotEmpty(cellValObj.toString())) {
cell.setCellValue(Double.valueOf(cellValObj.toString()));
} else {
cell.setCellValue(cellValObj.toString());
}
} catch (Exception e) {
LogUtil.warn("export excel data transform error");
}
}
}
}
}
}
}
@Override
public List<String> getFieldData(ChartViewDTO view, Long fieldId, String fieldType) throws Exception {
return chartDataManage.getFieldData(view, fieldId, fieldType);

View File

@ -4,11 +4,11 @@ package io.dataease.exportCenter.manage;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.dataease.api.chart.dto.ViewDetailField;
import io.dataease.api.chart.request.ChartExcelRequest;
import io.dataease.api.chart.request.ChartExcelRequestInner;
import io.dataease.api.exportCenter.vo.ExportTaskDTO;
import io.dataease.auth.bo.TokenUserBO;
import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper;
import io.dataease.chart.server.ChartDataServer;
import io.dataease.engine.constant.DeTypeConstants;
import io.dataease.exception.DEException;
import io.dataease.exportCenter.dao.auto.entity.CoreExportTask;
import io.dataease.exportCenter.dao.auto.mapper.CoreExportTaskMapper;
@ -21,10 +21,8 @@ import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@ -38,7 +36,6 @@ import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Component
@Transactional(rollbackFor = Exception.class)
@ -52,7 +49,7 @@ public class ExportCenterManage {
@Autowired
private WsService wsService;
@Resource
private SysParameterManage sysParameterManage;
private SysParameterManage sysParameterManage;
@Value("${export.dataset.limit:100000}")
private int limit;
@ -251,12 +248,9 @@ public class ExportCenterManage {
exportTask.setExportStatus("IN_PROGRESS");
exportTaskMapper.updateById(exportTask);
chartDataServer.findExcelData(request);
List<Object[]> details = request.getDetails();
Integer[] excelTypes = request.getExcelTypes();
details.add(0, request.getHeader());
Workbook wb = new SXSSFWorkbook();
//明细sheet
Sheet detailsSheet = wb.createSheet("数据");
//给单元格设置样式
CellStyle cellStyle = wb.createCellStyle();
Font font = wb.createFont();
@ -271,110 +265,36 @@ public class ExportCenterManage {
//设置单元格填充样式(使用纯色背景颜色填充)
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
Boolean mergeHead = false;
ViewDetailField[] detailFields = request.getDetailFields();
if (ArrayUtils.isNotEmpty(detailFields)) {
cellStyle.setBorderTop(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderLeft(BorderStyle.THIN);
String[] detailField = Arrays.stream(detailFields).map(field -> field.getName()).collect(Collectors.toList()).toArray(new String[detailFields.length]);
if (CollectionUtils.isEmpty(request.getMultiInfo())) {
List<Object[]> details = request.getDetails();
Integer[] excelTypes = request.getExcelTypes();
details.add(0, request.getHeader());
ViewDetailField[] detailFields = request.getDetailFields();
Object[] header = request.getHeader();
Row row = detailsSheet.createRow(0);
int headLen = header.length;
int detailFieldLen = detailField.length;
for (int i = 0; i < headLen; i++) {
Cell cell = row.createCell(i);
cell.setCellValue(header[i].toString());
if (i < headLen - 1) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 1, i, i);
detailsSheet.addMergedRegion(cellRangeAddress);
} else {
for (int j = i + 1; j < detailFieldLen + i; j++) {
row.createCell(j).setCellStyle(cellStyle);
}
CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 0, i, i + detailFieldLen - 1);
detailsSheet.addMergedRegion(cellRangeAddress);
}
cell.setCellStyle(cellStyle);
detailsSheet.setColumnWidth(i, 255 * 20);
}
Row detailRow = detailsSheet.createRow(1);
for (int i = 0; i < headLen - 1; i++) {
Cell cell = detailRow.createCell(i);
cell.setCellStyle(cellStyle);
}
for (int i = 0; i < detailFieldLen; i++) {
int colIndex = headLen - 1 + i;
Cell cell = detailRow.createCell(colIndex);
cell.setCellValue(detailField[i]);
cell.setCellStyle(cellStyle);
detailsSheet.setColumnWidth(colIndex, 255 * 20);
}
details.add(1, detailField);
mergeHead = true;
}
if (CollectionUtils.isNotEmpty(details) && (!mergeHead || details.size() > 2)) {
int realDetailRowIndex = 2;
for (int i = (mergeHead ? 2 : 0); i < details.size(); i++) {
Row row = detailsSheet.createRow(realDetailRowIndex > 2 ? realDetailRowIndex : i);
Object[] rowData = details.get(i);
if (rowData != null) {
for (int j = 0; j < rowData.length; j++) {
Object cellValObj = rowData[j];
if (mergeHead && j == rowData.length - 1 && (cellValObj.getClass().isArray() || cellValObj instanceof ArrayList)) {
Object[] detailRowArray = ((List<Object>) cellValObj).toArray(new Object[((List<?>) cellValObj).size()]);
int detailRowArrayLen = detailRowArray.length;
int temlJ = j;
while (detailRowArrayLen > 1 && temlJ-- > 0) {
CellRangeAddress cellRangeAddress = new CellRangeAddress(realDetailRowIndex, realDetailRowIndex + detailRowArrayLen - 1, temlJ, temlJ);
detailsSheet.addMergedRegion(cellRangeAddress);
}
//明细sheet
Sheet detailsSheet = wb.createSheet("数据");
for (int k = 0; k < detailRowArrayLen; k++) {
List<Object> detailRows = (List<Object>) detailRowArray[k];
Row curRow = row;
if (k > 0) {
curRow = detailsSheet.createRow(realDetailRowIndex + k);
}
ChartDataServer.setExcelData(detailsSheet, cellStyle, header, details, detailFields, excelTypes);
} else {
//多个sheet
for (int i = 0; i < request.getMultiInfo().size(); i++) {
ChartExcelRequestInner requestInner = request.getMultiInfo().get(i);
for (int l = 0; l < detailRows.size(); l++) {
Object col = detailRows.get(l);
Cell cell = curRow.createCell(j + l);
cell.setCellValue(col.toString());
}
}
realDetailRowIndex += detailRowArrayLen;
break;
}
List<Object[]> details = requestInner.getDetails();
Integer[] excelTypes = requestInner.getExcelTypes();
details.add(0, requestInner.getHeader());
ViewDetailField[] detailFields = requestInner.getDetailFields();
Object[] header = requestInner.getHeader();
Cell cell = row.createCell(j);
if (i == 0) {// 头部
cell.setCellValue(cellValObj.toString());
cell.setCellStyle(cellStyle);
//设置列的宽度
detailsSheet.setColumnWidth(j, 255 * 20);
} else if (cellValObj != null) {
try {
// with DataType
if ((excelTypes[j].equals(DeTypeConstants.DE_INT) || excelTypes[j].equals(DeTypeConstants.DE_FLOAT)) && StringUtils.isNotEmpty(cellValObj.toString())) {
cell.setCellValue(Double.valueOf(cellValObj.toString()));
} else {
cell.setCellValue(cellValObj.toString());
}
} catch (Exception e) {
LogUtil.warn("export excel data transform error");
}
}
//明细sheet
Sheet detailsSheet = wb.createSheet("数据 " + (i + 1));
}
}
ChartDataServer.setExcelData(detailsSheet, cellStyle, header, details, detailFields, excelTypes);
}
}
try (FileOutputStream outputStream = new FileOutputStream(dataPath + "/" + request.getViewName() + ".xlsx")) {
wb.write(outputStream);
outputStream.flush();

View File

@ -59,11 +59,29 @@
show-position="viewDialog"
/>
<chart-component-s2
v-if="optType === 'details'"
v-if="optType === 'details' && sourceViewType !== 'chart-mix'"
:view="viewInfo"
show-position="viewDialog"
ref="chartComponentDetails"
/>
<template v-else-if="optType === 'details' && sourceViewType === 'chart-mix'">
<el-tabs class="tab-header" v-model="activeName" @tab-change="handleClick">
<el-tab-pane :label="t('chart.drag_block_value_axis_left')" name="left"></el-tab-pane>
<el-tab-pane :label="t('chart.drag_block_value_axis_right')" name="right"></el-tab-pane>
</el-tabs>
<chart-component-s2
v-if="activeName === 'left'"
:view="viewInfo"
show-position="viewDialog"
ref="chartComponentDetails"
/>
<chart-component-s2
v-else-if="activeName === 'right'"
:view="viewInfo"
show-position="viewDialog"
ref="chartComponentDetails2"
/>
</template>
</div>
</div>
</el-dialog>
@ -93,9 +111,11 @@ const viewContainer = ref(null)
const { t } = useI18n()
const optType = ref(null)
const chartComponentDetails = ref(null)
const chartComponentDetails2 = ref(null)
const { dvInfo } = storeToRefs(dvMainStore)
const exportLoading = ref(false)
const sourceViewType = ref()
const activeName = ref('left')
const DETAIL_TABLE_ATTR: DeepPartial<ChartObj> = {
render: 'antv',
type: 'table-info',
@ -184,7 +204,23 @@ const dialogInit = (canvasStyle, view, item, opt) => {
const dataDetailsOpt = () => {
nextTick(() => {
const viewDataInfo = dvMainStore.getViewDataDetails(viewInfo.value.id)
chartComponentDetails.value.renderChartFromDialog(viewInfo.value, viewDataInfo)
if (sourceViewType.value === 'chart-mix') {
chartComponentDetails.value?.renderChartFromDialog(viewInfo.value, viewDataInfo.left)
chartComponentDetails2.value?.renderChartFromDialog(viewInfo.value, viewDataInfo.right)
} else {
chartComponentDetails.value.renderChartFromDialog(viewInfo.value, viewDataInfo)
}
})
}
const handleClick = tab => {
nextTick(() => {
const viewDataInfo = dvMainStore.getViewDataDetails(viewInfo.value.id)
if (tab === 'left') {
chartComponentDetails.value?.renderChartFromDialog(viewInfo.value, viewDataInfo.left)
} else if (tab === 'right') {
chartComponentDetails2.value?.renderChartFromDialog(viewInfo.value, viewDataInfo.right)
}
})
}
@ -319,4 +355,42 @@ defineExpose({
height: 100%;
}
}
.tab-header {
--ed-tabs-header-height: 34px;
--custom-tab-color: #646a73;
:deep(.ed-tabs__nav-wrap::after) {
background-color: unset;
}
&.dark {
--custom-tab-color: #a6a6a6;
}
:deep(.ed-tabs__item) {
font-weight: 400;
font-size: 12px;
padding: 0 8px !important;
margin-right: 12px;
color: var(--custom-tab-color);
}
:deep(.is-active) {
font-weight: 500;
color: var(--ed-color-primary, #3370ff);
}
:deep(.ed-tabs__nav-scroll) {
padding-left: 0 !important;
}
:deep(.ed-tabs__header) {
margin: 0 !important;
}
:deep(.ed-tabs__content) {
height: calc(100% - 35px);
overflow-y: auto;
overflow-x: hidden;
}
}
</style>

View File

@ -17,6 +17,21 @@ declare interface Chart {
dynamicAssistLines?: AssistLine[]
fields: ChartViewField[]
tableRow: []
//chart-mix
left: {
data: any[]
series?: any[]
dynamicAssistLines?: AssistLine[]
fields: ChartViewField[]
tableRow: []
}
right: {
data: any[]
series?: any[]
dynamicAssistLines?: AssistLine[]
fields: ChartViewField[]
tableRow: []
}
}
xAxis?: Axis[]
xAxisExt?: Axis[]

View File

@ -12,7 +12,7 @@ import {
setGradientColor
} from '../../common/common_antv'
import { flow, hexColorToRGBA, parseJson } from '@/views/chart/components/js/util'
import { cloneDeep, isEmpty, defaultTo, map, filter } from 'lodash-es'
import { cloneDeep, isEmpty, defaultTo, map, filter, union } from 'lodash-es'
import { valueFormatter } from '@/views/chart/components/js/formatter'
import {
CHART_MIX_AXIS_TYPE,
@ -55,21 +55,23 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
}
drawChart(drawOptions: G2PlotDrawOptions<DualAxes>): DualAxes {
const { chart, action, container } = drawOptions
if (!chart.data.data?.length) {
if (!chart.data.left.data?.length && !chart.data.right.data?.length) {
return
}
const data = cloneDeep(chart.data.data)
const left = cloneDeep(chart.data?.left?.data)
const right = cloneDeep(chart.data?.right?.data)
const data1Type = data[0]?.type === 'bar' ? 'column' : data[0]?.type
const data2Type = data[1]?.type === 'bar' ? 'column' : data[1]?.type
const data1Type = (left[0]?.type === 'bar' ? 'column' : left[0]?.type) ?? 'column'
const data2Type = (right[0]?.type === 'bar' ? 'column' : right[0]?.type) ?? 'column'
const data1 = defaultTo(data[0]?.data, [])
const data2 = map(defaultTo(data[1]?.data, []), d => {
const data1 = defaultTo(left[0]?.data, [])
const data2 = map(defaultTo(right[0]?.data, []), d => {
return {
...d,
valueExt: d.value
}
})
// custom color
const customAttr = parseJson(chart.customAttr)
let color = customAttr.basicStyle.colors
@ -368,14 +370,16 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
protected configLegend(chart: Chart, options: DualAxesOptions): DualAxesOptions {
const o = super.configLegend(chart, options)
if (o.legend) {
const data = chart.data.data
const left = cloneDeep(chart.data?.left?.data)
const right = cloneDeep(chart.data?.right?.data)
o.legend.itemName = {
formatter: (text: string, item: any, index: number) => {
let name = undefined
if (index === 0 && text === 'value') {
name = data[0]?.name
name = left[0]?.name
} else if (index === 1 && text === 'valueExt') {
name = data[1]?.name
name = right[0]?.name
}
if (name === undefined) {
return text
@ -389,6 +393,10 @@ export class ColumnLineMix extends G2PlotChartView<DualAxesOptions, DualAxes> {
}
protected configAnalyse(chart: Chart, options: DualAxesOptions): DualAxesOptions {
chart.data.dynamicAssistLines = union(
defaultTo(chart.data?.left?.dynamicAssistLines, []),
defaultTo(chart.data?.right?.dynamicAssistLines, [])
)
const list = getAnalyse(chart)
const annotations = {
value: filter(list, l => l.yAxisType === 'left'),

View File

@ -11,6 +11,7 @@ import { innerExportDetails } from '@/api/chart'
import { ElMessage } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n'
import { useLinkStoreWithOut } from '@/store/modules/link'
const { t } = useI18n()
// 同时支持将hex和rgb转换成rgba
export function hexColorToRGBA(hex, alpha) {
@ -420,17 +421,16 @@ export const getGeoJsonFile = async (areaId: string): Promise<FeatureCollection>
return toRaw(geoJson)
}
export const exportExcelDownload = (chart, callBack?) => {
const fields = JSON.parse(JSON.stringify(chart.data.fields))
const tableRow = JSON.parse(JSON.stringify(chart.data.tableRow))
const getExcelDownloadRequest = data => {
const fields = JSON.parse(JSON.stringify(data.fields))
const tableRow = JSON.parse(JSON.stringify(data.tableRow))
const excelHeader = fields.map(item => item.chartShowName ?? item.name)
const excelTypes = fields.map(item => item.deType)
const excelHeaderKeys = fields.map(item => item.dataeaseName)
let excelData = tableRow.map(item => excelHeaderKeys.map(i => item[i]))
const excelName = chart.title
let detailFields = []
if (chart.data.detailFields?.length) {
detailFields = chart.data.detailFields.map(item => {
if (data.detailFields?.length) {
detailFields = data.detailFields.map(item => {
return {
name: item.name,
deType: item.deType,
@ -450,21 +450,41 @@ export const exportExcelDownload = (chart, callBack?) => {
})
})
}
const request = {
proxy: null,
viewId: chart.id,
viewName: excelName,
return {
header: excelHeader,
details: excelData,
excelTypes: excelTypes,
excelHeaderKeys: excelHeaderKeys,
viewInfo: chart,
detailFields
detailFields: detailFields
}
}
export const exportExcelDownload = (chart, callBack?) => {
const excelName = chart.title
let request: any = {
proxy: null,
viewId: chart.id,
viewInfo: chart,
viewName: excelName
}
if (chart.type === 'chart-mix') {
const req1 = getExcelDownloadRequest(chart.data.left)
const req2 = getExcelDownloadRequest(chart.data.right)
request = {
...request,
multiInfo: [req1, req2]
}
} else {
const req = getExcelDownloadRequest(chart.data)
request = {
...request,
...req
}
}
const linkStore = useLinkStoreWithOut()
const method = innerExportDetails
method(request)
innerExportDetails(request)
.then(res => {
if (linkStore.getLinkToken) {
const blob = new Blob([res.data], { type: 'application/vnd.ms-excel' })

View File

@ -1,31 +1,25 @@
package io.dataease.api.chart.request;
import io.dataease.api.chart.dto.ChartViewDTO;
import io.dataease.api.chart.dto.ViewDetailField;
import lombok.Data;
import java.io.Serial;
import java.util.List;
/**
* @Author wangjiahao
*/
@Data
public class ChartExcelRequest {
public class ChartExcelRequest extends ChartExcelRequestInner {
@Serial
private static final long serialVersionUID = 3829386417457449431L;
private String viewId;
private String viewName;
private String[] header;
private Integer[] excelTypes;
private List<Object[]> details;
private ViewDetailField[] detailFields;
private ChartViewDTO viewInfo;
private List<String> excelHeaderKeys;
private List<ChartExcelRequestInner> multiInfo;
}

View File

@ -0,0 +1,26 @@
package io.dataease.api.chart.request;
import io.dataease.api.chart.dto.ViewDetailField;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
@Data
public class ChartExcelRequestInner implements Serializable {
@Serial
private static final long serialVersionUID = -8655439241248268940L;
private String[] header;
private Integer[] excelTypes;
private List<Object[]> details;
private ViewDetailField[] detailFields;
private List<String> excelHeaderKeys;
}