forked from github/dataease
feat: 视图明细导出支持附带明细截图
This commit is contained in:
parent
0b09cb1d6f
commit
b7437f0413
@ -10,6 +10,7 @@ import io.dataease.commons.constants.DePermissionType;
|
||||
import io.dataease.commons.constants.ResourceAuthLevel;
|
||||
import io.dataease.controller.handler.annotation.I18n;
|
||||
import io.dataease.controller.request.panel.PanelGroupRequest;
|
||||
import io.dataease.controller.request.panel.PanelViewDetailsRequest;
|
||||
import io.dataease.dto.PermissionProxy;
|
||||
import io.dataease.dto.authModel.VAuthModelDTO;
|
||||
import io.dataease.dto.panel.PanelGroupDTO;
|
||||
@ -22,6 +23,8 @@ import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -100,4 +103,12 @@ public class PanelGroupController {
|
||||
return panelGroupService.queryPanelComponents(id);
|
||||
}
|
||||
|
||||
@ApiOperation("导出仪表板视图明细")
|
||||
@PostMapping("/exportDetails")
|
||||
@I18n
|
||||
public void exportDetails(@RequestBody PanelViewDetailsRequest request, HttpServletResponse response) throws IOException {
|
||||
panelGroupService.exportPanelViewDetails(request,response);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package io.dataease.controller.request.panel;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Author: wangjiahao
|
||||
* Date: 2022/4/8
|
||||
* Description:
|
||||
*/
|
||||
@Data
|
||||
public class PanelViewDetailsRequest {
|
||||
|
||||
private String viewName;
|
||||
|
||||
private String[] header;
|
||||
|
||||
private List<String[]> details;
|
||||
|
||||
private String snapshot;
|
||||
|
||||
private int snapshotWidth;
|
||||
|
||||
private int snapshotHeight;
|
||||
|
||||
|
||||
}
|
@ -13,6 +13,7 @@ import io.dataease.commons.utils.TreeUtils;
|
||||
import io.dataease.controller.request.authModel.VAuthModelRequest;
|
||||
import io.dataease.controller.request.dataset.DataSetTableRequest;
|
||||
import io.dataease.controller.request.panel.PanelGroupRequest;
|
||||
import io.dataease.controller.request.panel.PanelViewDetailsRequest;
|
||||
import io.dataease.dto.PanelGroupExtendDataDTO;
|
||||
import io.dataease.dto.authModel.VAuthModelDTO;
|
||||
import io.dataease.dto.chart.ChartViewDTO;
|
||||
@ -20,6 +21,7 @@ import io.dataease.dto.dataset.DataSetTableDTO;
|
||||
import io.dataease.dto.panel.PanelGroupDTO;
|
||||
import io.dataease.dto.panel.linkJump.PanelLinkJumpBaseRequest;
|
||||
import io.dataease.dto.panel.po.PanelViewInsertDTO;
|
||||
import io.dataease.excel.utils.EasyExcelExporter;
|
||||
import io.dataease.exception.DataEaseException;
|
||||
import io.dataease.i18n.Translator;
|
||||
import io.dataease.listener.util.CacheUtils;
|
||||
@ -30,14 +32,24 @@ import io.swagger.annotations.ApiModelProperty;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.poi.hssf.usermodel.*;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.pentaho.di.core.util.UUIDUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -51,6 +63,7 @@ public class PanelGroupService {
|
||||
|
||||
private final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
private final static String DATA_URL_TITLE = "data:image/jpeg;base64,";
|
||||
@Resource
|
||||
private PanelGroupMapper panelGroupMapper;
|
||||
@Resource
|
||||
@ -110,7 +123,7 @@ public class PanelGroupService {
|
||||
public PanelGroup saveOrUpdate(PanelGroupRequest request) {
|
||||
String userName = AuthUtils.getUser().getUsername();
|
||||
String panelId = request.getId();
|
||||
if(StringUtils.isNotEmpty(panelId)){
|
||||
if (StringUtils.isNotEmpty(panelId)) {
|
||||
panelViewService.syncPanelViews(request);
|
||||
}
|
||||
if (StringUtils.isEmpty(panelId)) { // 新建
|
||||
@ -310,46 +323,46 @@ public class PanelGroupService {
|
||||
return newPanelId;
|
||||
}
|
||||
|
||||
public String newPanel(PanelGroupRequest request){
|
||||
public String newPanel(PanelGroupRequest request) {
|
||||
String newPanelId = UUIDUtil.getUUIDAsString();
|
||||
String newFrom = request.getNewFrom();
|
||||
String templateStyle = null;
|
||||
String templateData = null;
|
||||
String dynamicData = null;
|
||||
if(PanelConstants.NEW_PANEL_FROM.NEW.equals(newFrom)){
|
||||
if (PanelConstants.NEW_PANEL_FROM.NEW.equals(newFrom)) {
|
||||
|
||||
}else{
|
||||
} else {
|
||||
//内部模板新建
|
||||
if(PanelConstants.NEW_PANEL_FROM.NEW_INNER_TEMPLATE.equals(newFrom)){
|
||||
if (PanelConstants.NEW_PANEL_FROM.NEW_INNER_TEMPLATE.equals(newFrom)) {
|
||||
PanelTemplateWithBLOBs panelTemplate = templateMapper.selectByPrimaryKey(request.getTemplateId());
|
||||
templateStyle = panelTemplate.getTemplateStyle();
|
||||
templateData = panelTemplate.getTemplateData();
|
||||
dynamicData = panelTemplate.getDynamicData();
|
||||
}else if(PanelConstants.NEW_PANEL_FROM.NEW_OUTER_TEMPLATE.equals(newFrom)){
|
||||
} else if (PanelConstants.NEW_PANEL_FROM.NEW_OUTER_TEMPLATE.equals(newFrom)) {
|
||||
templateStyle = request.getPanelStyle();
|
||||
templateData = request.getPanelData();
|
||||
dynamicData = request.getDynamicData();
|
||||
}
|
||||
Map<String,String> dynamicDataMap = JSON.parseObject(dynamicData,Map.class);
|
||||
Map<String, String> dynamicDataMap = JSON.parseObject(dynamicData, Map.class);
|
||||
List<PanelViewInsertDTO> panelViews = new ArrayList<>();
|
||||
List<PanelGroupExtendDataDTO> viewsData = new ArrayList<>();
|
||||
for(Map.Entry<String, String> entry : dynamicDataMap.entrySet()){
|
||||
for (Map.Entry<String, String> entry : dynamicDataMap.entrySet()) {
|
||||
String originViewId = entry.getKey();
|
||||
String originViewData = entry.getValue();
|
||||
ChartViewDTO chartView = JSON.parseObject(originViewData,ChartViewDTO.class);
|
||||
ChartViewDTO chartView = JSON.parseObject(originViewData, ChartViewDTO.class);
|
||||
String position = chartView.getPosition();
|
||||
String newViewId = UUIDUtil.getUUIDAsString();
|
||||
chartView.setId(newViewId);
|
||||
chartView.setSceneId(newPanelId);
|
||||
chartView.setDataFrom(CommonConstants.VIEW_DATA_FROM.TEMPLATE);
|
||||
//TODO 数据处理 1.替换viewId 2.加入panelView 数据(数据来源为template) 3.加入模板view data数据
|
||||
templateData = templateData.replaceAll(originViewId,newViewId);
|
||||
panelViews.add(new PanelViewInsertDTO(newViewId,newPanelId,position));
|
||||
viewsData.add(new PanelGroupExtendDataDTO(newPanelId,newViewId,originViewData));
|
||||
templateData = templateData.replaceAll(originViewId, newViewId);
|
||||
panelViews.add(new PanelViewInsertDTO(newViewId, newPanelId, position));
|
||||
viewsData.add(new PanelGroupExtendDataDTO(newPanelId, newViewId, originViewData));
|
||||
chartViewMapper.insertSelective(chartView);
|
||||
extChartViewMapper.copyToCache(newViewId);
|
||||
}
|
||||
if(CollectionUtils.isNotEmpty(panelViews)){
|
||||
if (CollectionUtils.isNotEmpty(panelViews)) {
|
||||
extPanelViewMapper.savePanelView(panelViews);
|
||||
extPanelGroupExtendDataMapper.savePanelExtendData(viewsData);
|
||||
}
|
||||
@ -439,10 +452,77 @@ public class PanelGroupService {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private void clearPermissionCache(){
|
||||
|
||||
private void clearPermissionCache() {
|
||||
CacheUtils.removeAll(AuthConstants.USER_PANEL_NAME);
|
||||
CacheUtils.removeAll(AuthConstants.ROLE_PANEL_NAME);
|
||||
CacheUtils.removeAll(AuthConstants.DEPT_PANEL_NAME);
|
||||
}
|
||||
|
||||
|
||||
public void exportPanelViewDetails(PanelViewDetailsRequest request, HttpServletResponse response) throws IOException {
|
||||
OutputStream outputStream = response.getOutputStream();
|
||||
try {
|
||||
String snapshot = request.getSnapshot();
|
||||
List<String[]> details = request.getDetails();
|
||||
details.add(0,request.getHeader());
|
||||
HSSFWorkbook wb = new HSSFWorkbook();
|
||||
//明细sheet
|
||||
HSSFSheet detailsSheet = wb.createSheet("视图明细");
|
||||
|
||||
//给单元格设置样式
|
||||
CellStyle cellStyle = wb.createCellStyle();
|
||||
Font font = wb.createFont();
|
||||
//设置字体大小
|
||||
font.setFontHeightInPoints((short) 12);
|
||||
//设置字体加粗
|
||||
font.setBold(true);
|
||||
//给字体设置样式
|
||||
cellStyle.setFont(font);
|
||||
//设置单元格背景颜色
|
||||
cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
|
||||
//设置单元格填充样式(使用纯色背景颜色填充)
|
||||
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
|
||||
if (CollectionUtils.isNotEmpty(details)) {
|
||||
for (int i = 0; i < details.size(); i++) {
|
||||
HSSFRow row = detailsSheet.createRow(i);
|
||||
String[] rowData = details.get(i);
|
||||
if (rowData != null) {
|
||||
for (int j = 0; j < rowData.length; j++) {
|
||||
HSSFCell cell = row.createCell(j);
|
||||
cell.setCellValue(rowData[j]);
|
||||
if(i==0){// 头部
|
||||
cell.setCellStyle(cellStyle);
|
||||
//设置列的宽度
|
||||
detailsSheet.setColumnWidth(j, 255*20);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(StringUtils.isNotEmpty(snapshot)){
|
||||
//截图sheet 1px ≈ 2.33dx ≈ 0.48 dy 8*24 个单元格
|
||||
HSSFSheet snapshotSheet = wb.createSheet("截图");
|
||||
short reDefaultRowHeight = (short)Math.round(request.getSnapshotHeight()*3.5/8);
|
||||
int reDefaultColumnWidth = (int)Math.round(request.getSnapshotWidth()*0.25/24);
|
||||
snapshotSheet.setDefaultColumnWidth(reDefaultColumnWidth);
|
||||
snapshotSheet.setDefaultRowHeight(reDefaultRowHeight);
|
||||
|
||||
//画图的顶级管理器,一个sheet只能获取一个(一定要注意这点)i
|
||||
HSSFPatriarch patriarch = snapshotSheet.createDrawingPatriarch();
|
||||
HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, reDefaultColumnWidth, reDefaultColumnWidth,(short) 0, 0, (short)8, 24);
|
||||
anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_DO_RESIZE);
|
||||
patriarch.createPicture(anchor, wb.addPicture(Base64Utils.decodeFromString(snapshot.replace(DATA_URL_TITLE,"")), HSSFWorkbook.PICTURE_TYPE_JPEG));
|
||||
}
|
||||
response.setContentType("application/vnd.ms-excel");
|
||||
//文件名称
|
||||
response.setHeader("Content-disposition", "attachment;filename=" + request.getViewName() + ".xlsx");
|
||||
wb.write(outputStream);
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
} catch (Exception e) {
|
||||
DataEaseException.throwException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -188,4 +188,14 @@ export function initViewCache(panelId) {
|
||||
loading: false
|
||||
})
|
||||
}
|
||||
export function exportDetails(data) {
|
||||
// 初始化仪表板视图缓存
|
||||
return request({
|
||||
url: 'panel/group/exportDetails',
|
||||
method: 'post',
|
||||
data: data,
|
||||
loading: true,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,19 @@
|
||||
<template>
|
||||
<de-container>
|
||||
<de-aside-container v-if="chart.type !== 'table-normal' && chart.type !== 'table-info'" :style="customStyle">
|
||||
<plugin-com
|
||||
v-if="chart.isPlugin"
|
||||
|
||||
:component-name="chart.type + '-view'"
|
||||
:obj="{chart}"
|
||||
class="chart-class"
|
||||
/>
|
||||
<chart-component v-else-if="!chart.type.includes('text') && chart.type !== 'label' && !chart.type.includes('table') && renderComponent() === 'echarts'" class="chart-class" :chart="chart" />
|
||||
<chart-component-g2 v-else-if="!chart.type.includes('text') && chart.type !== 'label' && !chart.type.includes('table') && renderComponent() === 'antv'" class="chart-class" :chart="chart" />
|
||||
<chart-component-s2 v-else-if="chart.type === 'table-pivot' && renderComponent() === 'antv'" class="chart-class" :chart="chart" />
|
||||
<label-normal v-else-if="chart.type.includes('text')" :chart="chart" class="table-class" />
|
||||
<label-normal-text v-else-if="chart.type === 'label'" :chart="chart" class="table-class" />
|
||||
<de-container v-loading="$store.getters.loadingMap[$store.getters.currentPath]">
|
||||
<de-aside-container v-if="showChartCanvas">
|
||||
<div id="chartCanvas" :style="customStyle">
|
||||
<plugin-com
|
||||
v-if="chart.isPlugin"
|
||||
:component-name="chart.type + '-view'"
|
||||
:obj="{chart}"
|
||||
class="chart-class"
|
||||
/>
|
||||
<chart-component v-else-if="!chart.type.includes('text') && chart.type !== 'label' && !chart.type.includes('table') && renderComponent() === 'echarts'" class="chart-class" :chart="chart" />
|
||||
<chart-component-g2 v-else-if="!chart.type.includes('text') && chart.type !== 'label' && !chart.type.includes('table') && renderComponent() === 'antv'" class="chart-class" :chart="chart" />
|
||||
<chart-component-s2 v-else-if="chart.type === 'table-pivot' && renderComponent() === 'antv'" class="chart-class" :chart="chart" />
|
||||
<label-normal v-else-if="chart.type.includes('text')" :chart="chart" class="table-class" />
|
||||
<label-normal-text v-else-if="chart.type === 'label'" :chart="chart" class="table-class" />
|
||||
</div>
|
||||
</de-aside-container>
|
||||
<de-main-container>
|
||||
<table-normal :chart="chartTable" :show-summary="false" class="table-class" />
|
||||
@ -28,12 +29,14 @@ import LabelNormal from '@/views/chart/components/normal/LabelNormal'
|
||||
import DeMainContainer from '@/components/dataease/DeMainContainer'
|
||||
import DeContainer from '@/components/dataease/DeContainer'
|
||||
import DeAsideContainer from '@/components/dataease/DeAsideContainer'
|
||||
import { export_json_to_excel } from '@/plugins/Export2Excel'
|
||||
// import { export_json_to_excel } from '@/plugins/Export2Excel'
|
||||
import { mapState } from 'vuex'
|
||||
import ChartComponentG2 from '@/views/chart/components/ChartComponentG2'
|
||||
import PluginCom from '@/views/system/plugin/PluginCom'
|
||||
import ChartComponentS2 from '@/views/chart/components/ChartComponentS2'
|
||||
import LabelNormalText from '@/views/chart/components/normal/LabelNormalText'
|
||||
import { exportDetails } from '@/api/panel/panel'
|
||||
import html2canvas from 'html2canvasde'
|
||||
export default {
|
||||
name: 'UserView',
|
||||
components: { LabelNormalText, ChartComponentS2, ChartComponentG2, DeMainContainer, DeContainer, DeAsideContainer, ChartComponent, TableNormal, LabelNormal, PluginCom },
|
||||
@ -53,8 +56,12 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
showChartCanvas() {
|
||||
return this.chart.type !== 'table-normal' && this.chart.type !== 'table-info'
|
||||
},
|
||||
customStyle() {
|
||||
let style = {
|
||||
height: '100%'
|
||||
}
|
||||
if (this.canvasStyleData.openCommonStyle) {
|
||||
if (this.canvasStyleData.panel.backgroundType === 'image' && this.canvasStyleData.panel.imageUrl) {
|
||||
@ -83,11 +90,39 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
exportExcel() {
|
||||
const _this = this
|
||||
if (this.showChartCanvas) {
|
||||
html2canvas(document.getElementById('chartCanvas')).then(canvas => {
|
||||
const snapshot = canvas.toDataURL('image/jpeg', 1) // 是图片质量
|
||||
_this.exportExcelDownload(snapshot, canvas.width, canvas.height)
|
||||
})
|
||||
} else {
|
||||
_this.exportExcelDownload()
|
||||
}
|
||||
},
|
||||
exportExcelDownload(snapshot, width, height) {
|
||||
const excelHeader = JSON.parse(JSON.stringify(this.chart.data.fields)).map(item => item.name)
|
||||
const excelHeaderKeys = JSON.parse(JSON.stringify(this.chart.data.fields)).map(item => item.dataeaseName)
|
||||
const excelData = JSON.parse(JSON.stringify(this.chart.data.tableRow)).map(item => excelHeaderKeys.map(i => item[i]))
|
||||
const excelName = this.chart.name
|
||||
export_json_to_excel(excelHeader, excelData, excelName)
|
||||
const request = {
|
||||
viewName: excelName,
|
||||
header: excelHeader,
|
||||
details: excelData,
|
||||
snapshot: snapshot,
|
||||
snapshotWidth: width,
|
||||
snapshotHeight: height
|
||||
}
|
||||
exportDetails(request).then((res) => {
|
||||
const blob = new Blob([res], { type: 'application/vnd.ms-excel' })
|
||||
const link = document.createElement('a')
|
||||
link.style.display = 'none'
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.download = excelName // 下载的文件名
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
})
|
||||
},
|
||||
|
||||
renderComponent() {
|
||||
|
Loading…
Reference in New Issue
Block a user