Merge pull request #6658 from dataease/pr@dev@refactor_template-market

refactor: 模版市场优化
This commit is contained in:
王嘉豪 2023-11-13 15:03:42 +08:00 committed by GitHub
commit b9e38d3717
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 432 additions and 9 deletions

View File

@ -6,10 +6,12 @@ import io.dataease.api.chart.dto.ChartViewDTO;
import io.dataease.api.chart.dto.ViewDetailField;
import io.dataease.api.chart.request.ChartExcelRequest;
import io.dataease.chart.manage.ChartDataManage;
import io.dataease.constant.CommonConstants;
import io.dataease.engine.constant.DeTypeConstants;
import io.dataease.exception.DEException;
import io.dataease.result.ResultCode;
import io.dataease.utils.LogUtil;
import io.dataease.visualization.manage.VisualizationTemplateExtendDataManage;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils;
@ -41,10 +43,18 @@ public class ChartDataServer implements ChartDataApi {
@Resource
private ChartDataManage chartDataManage;
@Resource
private VisualizationTemplateExtendDataManage extendDataManage;
@Override
public ChartViewDTO getData(ChartViewDTO chartViewDTO) throws Exception {
try {
return chartDataManage.calcData(chartViewDTO);
// 从模版数据获取
if(CommonConstants.VIEW_DATA_FROM.TEMPLATE.equalsIgnoreCase(chartViewDTO.getDataFrom())){
return extendDataManage.getChartDataInfo(chartViewDTO.getId(),chartViewDTO);
}else{
return chartDataManage.calcData(chartViewDTO);
}
} catch (Exception e) {
DEException.throwException(ResultCode.DATA_IS_WRONG.code(), e.getMessage());
}

View File

@ -39,7 +39,7 @@ public class MenuManage {
return convertTree(treeNodes);
}
@Cacheable(cacheNames = CORE_MENU_CACHE, key = "'-dataease-'")
// @Cacheable(cacheNames = CORE_MENU_CACHE, key = "'-dataease-'")
public List<CoreMenu> coreMenus() {
QueryWrapper<CoreMenu> wrapper = new QueryWrapper<>();
wrapper.orderByAsc("menu_sort");

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.dataease.api.chart.dto.ChartViewDTO;
import io.dataease.api.template.dto.TemplateManageFileDTO;
import io.dataease.api.template.dto.VisualizationTemplateExtendDataDTO;
import io.dataease.api.visualization.DataVisualizationApi;
import io.dataease.api.visualization.request.DataVisualizationBaseRequest;
import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest;
@ -20,6 +21,8 @@ import io.dataease.license.config.XpackInteract;
import io.dataease.model.BusiNodeRequest;
import io.dataease.model.BusiNodeVO;
import io.dataease.template.dao.auto.entity.VisualizationTemplate;
import io.dataease.template.dao.auto.entity.VisualizationTemplateExtendData;
import io.dataease.template.dao.auto.mapper.VisualizationTemplateExtendDataMapper;
import io.dataease.template.dao.auto.mapper.VisualizationTemplateMapper;
import io.dataease.template.manage.TemplateMarketManage;
import io.dataease.utils.AuthUtils;
@ -73,6 +76,9 @@ public class DataVisualizationServer implements DataVisualizationApi {
@Resource
private StaticResourceServer staticResourceServer;
@Resource
private VisualizationTemplateExtendDataMapper templateExtendDataMapper;
@Override
@XpackInteract(value = "dataVisualizationServer", original = true)
@ -256,7 +262,7 @@ public class DataVisualizationServer implements DataVisualizationApi {
Map<String, String> dynamicDataMap = JsonUtil.parseObject(dynamicData, Map.class);
List<ChartViewDTO> chartViews = new ArrayList<>();
Map<Long,ChartViewDTO> canvasViewInfo = new HashMap<>();
// List<PanelGroupExtendDataDTO> viewsData = new ArrayList<>();
Map<Long,VisualizationTemplateExtendDataDTO> extendDataInfo = new HashMap<>();
for (Map.Entry<String, String> entry : dynamicDataMap.entrySet()) {
String originViewId = entry.getKey();
String originViewData = entry.getValue();
@ -265,17 +271,21 @@ public class DataVisualizationServer implements DataVisualizationApi {
chartView.setId(newViewId);
chartView.setSceneId(newDvId);
chartView.setDataFrom(CommonConstants.VIEW_DATA_FROM.TEMPLATE);
// 数据处理 1.替换viewId 2.加入panelView 数据(数据来源为template) 3.加入模板view data数据
// viewsData.add(new PanelGroupExtendDataDTO(newPanelId, newViewId, originViewData));
// 数据处理 1.替换viewId 2.加入模板view data数据
VisualizationTemplateExtendDataDTO extendDataDTO = new VisualizationTemplateExtendDataDTO(newViewId, newDvId,originViewData);
extendDataInfo.put(newViewId, extendDataDTO);
templateData = templateData.replaceAll(originViewId, newViewId.toString());
chartViewManege.save(chartView);
canvasViewInfo.put(chartView.getId(),chartView);
//插入模版数据 此处预先插入减少数据交互量
VisualizationTemplateExtendData extendData = new VisualizationTemplateExtendData();
templateExtendDataMapper.insert(BeanUtils.copyBean(extendData,extendDataDTO));
}
request.setComponentData(templateData);
request.setCanvasStyleData(templateStyle);
//Store static resource into the server
staticResourceServer.saveFilesToServe(staticResource);
return new DataVisualizationVO(newDvId,name,dvType,templateStyle,templateData,canvasViewInfo);
return new DataVisualizationVO(newDvId,name,dvType,templateStyle,templateData,canvasViewInfo,null);
}
@Override

View File

@ -67,3 +67,5 @@ export const storeApi = (data): Promise<IResponse> => {
export const storeStatusApi = (id: string): Promise<IResponse> => {
return request.get({ url: `/store/favorited/${id}` })
}
export const decompression = data => request.post({ url: '/dataVisualization/decompression', data })

View File

@ -0,0 +1,239 @@
<template>
<el-dialog
class="create-dialog"
title="从模版新建"
v-model="state.dialogShow"
width="600"
:before-close="close"
@submit.prevent
>
<el-row v-loading="state.loading">
<el-row>
<el-col :span="18" style="height: 40px">
<el-radio v-model="state.inputType" label="new_outer_template"
>{{ t('visualization.import_template') }}
</el-radio>
<el-radio v-model="state.inputType" label="new_inner_template" @click="getTree"
>{{ t('visualization.copy_template') }}
</el-radio>
</el-col>
<el-col v-if="state.inputType === 'new_outer_template'" :span="6">
<el-button class="el-icon-upload" size="small" type="primary" @click="goFile"
>{{ t('visualization.upload_template') }}
</el-button>
<input
id="input"
ref="files"
type="file"
accept=".DET"
hidden
@change="handleFileChange"
/>
</el-col>
</el-row>
<el-row style="margin-top: 5px">
<el-col :span="4">{{ state.titleSuf }}{{ t('commons.name') }}</el-col>
<el-col :span="20">
<el-input v-model="state.dvCreateInfo.name" clearable size="mini" />
</el-col>
</el-row>
<el-row v-if="state.inputType === 'new_inner_template'" class="preview">
<el-col :span="8" style="height: 100%; overflow-y: auto">
<template-all-list
:template-list="state.templateList"
@showCurrentTemplateInfo="showCurrentTemplateInfo"
/>
</el-col>
<el-col :span="16" :style="classBackground" class="preview-show" />
</el-row>
<el-row
v-if="state.inputType === 'new_outer_template'"
class="preview"
:style="classBackground"
/>
<el-row class="root-class">
<el-button size="mini" @click="cancel()">{{ t('commons.cancel') }} </el-button>
<el-button type="primary" size="mini" :disabled="!saveStatus" @click="save()"
>{{ t('commons.confirm') }}
</el-button>
</el-row>
</el-row>
</el-dialog>
</template>
<script setup lang="ts">
import { showTemplateList } from '@/api/template'
import { useI18n } from '@/hooks/web/useI18n'
import { computed, reactive, ref } from 'vue'
import { imgUrlTrans } from '@/utils/imgUtils'
import { watch } from 'vue/dist/vue'
import { ElMessage } from 'element-plus-secondary'
import { decompression } from '@/api/visualization/dataVisualization'
const { t } = useI18n()
const emits = defineEmits(['closeEditPanelDialog'])
const files = ref(null)
const props = defineProps({
editPanelOut: {
type: Object,
required: true
}
})
const state = reactive({
dialogShow: false,
loading: false,
inputType: 'new_outer_template',
fieldName: 'name',
tableRadio: null,
keyWordSearch: '',
columnLabel: t('visualization.belong_to_category'),
templateList: [],
importTemplateInfo: {
snapshot: ''
},
dvCreateInfo: {
name:null,
canvasStyleData: null,
componentData: null,
templateId: null,
dynamicData: null,
staticResource: null
},
templateSelected: false
})
const saveStatus = computed(() => {
return state.dvCreateInfo.name && state.templateSelected
})
const classBackground = computed(() => {
if (state.importTemplateInfo.snapshot) {
return {
background: `url(${imgUrlTrans(state.importTemplateInfo.snapshot)}) no-repeat`
}
} else {
return {}
}
})
watch(
() => state.inputType,
val => {
state.templateSelected = false
state.dvCreateInfo.name = null
state.dvCreateInfo.canvasStyleData = null
state.dvCreateInfo.componentData = null
state.importTemplateInfo.snapshot = null
state.dvCreateInfo.templateId = null
}
)
const showCurrentTemplateInfo = data => {
state.dvCreateInfo.templateId = data.id
if (data.nodeType === 'folder') {
state.dvCreateInfo.name = null
state.importTemplateInfo.snapshot = null
state.templateSelected = false
} else {
state.dvCreateInfo.name = data.name
state.importTemplateInfo.snapshot = data.snapshot
state.templateSelected = true
}
}
const getTree = () => {
const request = {
level: '-1',
withChildren: true
}
state.loading = true
showTemplateList(request).then(res => {
state.templateList = res.data
state.loading = false
})
}
const cancel = () => {
emits('closeEditPanelDialog')
}
const save = () => {
if (!state.dvCreateInfo.name) {
ElMessage.warning(t('common.save_success'))
return false
}
if (state.dvCreateInfo.name.length > 50) {
ElMessage.warning(t('common.char_can_not_more_50'))
return false
}
if (
!state.dvCreateInfo.templateId &&
state.inputType === 'new_inner_template'
) {
ElMessage.warning('chart.template_can_not_empty')
return false
}
state.dvCreateInfo['newFrom'] = state.inputType
state.loading = true
decompression(state.dvCreateInfo)
.then(response => {
state.loading = false
emits('closeEditPanelDialog', response.data)
})
.catch(() => {
state.loading = false
})
}
const handleFileChange = e => {
const file = e.target.files[0]
const reader = new FileReader()
reader.onload = res => {
state.templateSelected = true
const result = res.target.result
state.importTemplateInfo = JSON.parse(result)
state.dvCreateInfo.name = state.importTemplateInfo['name'].name
state.dvCreateInfo.canvasStyleData = state.importTemplateInfo['canvasStyleData']
state.dvCreateInfo.componentData = state.importTemplateInfo['componentData']
state.dvCreateInfo.dynamicData = state.importTemplateInfo['dynamicData']
state.dvCreateInfo.staticResource = state.importTemplateInfo['staticResource']
}
reader.readAsText(file)
}
const goFile = () => {
files.value.files.click()
}
const close = () ={
// do close
}
const optInit = () =>{
}
defineExpose({
optInit
})
</script>
<style scoped>
.root-class {
margin: 15px 0px 5px;
text-align: center;
}
.preview {
margin-top: 5px;
border: 1px solid #e6e6e6;
height: 250px !important;
overflow: hidden;
background-size: 100% 100% !important;
}
.preview-show {
border-left: 1px solid #e6e6e6;
height: 250px;
background-size: 100% 100% !important;
}
</style>

View File

@ -102,6 +102,11 @@ state.resourceTypeList = [
svgName: curCanvasType.value === 'dashboard' ? 'dv-dashboard-spine' : 'dv-screen-spine',
command: 'newLeaf'
},
{
label: '从模版新建',
svgName: curCanvasType.value === 'dashboard' ? 'dv-dashboard-spine' : 'dv-screen-spine',
command: 'newFromTemplate'
},
{
label: '新建文件夹',
divided: true,
@ -236,6 +241,9 @@ const addOperation = (
} else {
window.open(baseUrl, '_blank')
}
} else if(cmd === 'newFromTemplate') {
// newFromTemplate
} else {
resourceGroupOpt.value.optInit(nodeType, data || {}, cmd, parentSelect)
}

View File

@ -0,0 +1,95 @@
<template>
<el-col>
<el-row style="margin-top: 5px">
<el-row>
<el-input
v-model="state.templateFilterText"
:placeholder="t('visualization.filter_keywords')"
size="mini"
clearable
prefix-icon="el-icon-search"
/>
</el-row>
<el-row style="margin-top: 5px">
<el-tree
ref="templateTree"
:default-expanded-keys="state.defaultExpandedKeys"
:data="templateList"
node-key="id"
:expand-on-click-node="true"
:filter-node-method="filterNode"
:highlight-current="true"
@node-click="nodeClick"
>
<template #default="{ data }">
<span class="custom-tree-node">
<span style="display: flex; flex: 1 1 0%; width: 0px">
<span v-if="data.nodeType === 'template'">
<svg-icon icon-class="panel" class="ds-icon-scene" />
</span>
<span v-if="data.nodeType === 'folder'">
<i class="el-icon-folder" />
</span>
<span
:title="data.name"
style="
margin-left: 6px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
"
>{{ data.name }}</span
>
</span>
</span>
</template>
</el-tree>
</el-row>
</el-row>
</el-col>
</template>
<script setup lang="ts">
import { findOne } from '@/api/template'
import { useI18n } from '@/hooks/web/useI18n'
import { reactive } from 'vue'
const { t } = useI18n()
const emits = defineEmits(['showCurrentTemplateInfo'])
const props = defineProps({
templateList: {
type: Array,
default: function () {
return []
}
}
})
const state = reactive({
templateFilterText: '',
defaultExpandedKeys: [],
currentTemplateShowList: []
})
const filterNode = (value, data) => {
if (!value) return true
return data.label.indexOf(value) !== -1
}
const nodeClick = (data, node) => {
findOne(data.id).then(res => {
emits('showCurrentTemplateInfo', res.data)
})
}
</script>
<style scoped>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
</style>

View File

@ -0,0 +1,26 @@
package io.dataease.api.template.dto;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.dataease.api.template.vo.VisualizationTemplateExtendDataVO;
import io.dataease.utils.IDUtils;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author : WangJiaHao
* @date : 2023/11/13 10:25
*/
@Data
@NoArgsConstructor
public class VisualizationTemplateExtendDataDTO extends VisualizationTemplateExtendDataVO {
public VisualizationTemplateExtendDataDTO(Long dvId, Long viewId, String viewDetails) {
super();
super.setId(IDUtils.snowID());
super.setDvId(dvId);
super.setViewId(viewId);
super.setViewDetails(viewDetails);
}
}

View File

@ -0,0 +1,26 @@
package io.dataease.api.template.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
/**
* @author : WangJiaHao
* @date : 2023/11/13 10:25
*/
@Data
public class VisualizationTemplateExtendDataVO {
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@JsonSerialize(using = ToStringSerializer.class)
private Long dvId;
@JsonSerialize(using = ToStringSerializer.class)
private Long viewId;
private String viewDetails;
private String copyFrom;
private String copyId;
}

View File

@ -3,6 +3,7 @@ package io.dataease.api.visualization.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.dataease.api.chart.dto.ChartViewDTO;
import io.dataease.api.template.dto.VisualizationTemplateExtendDataDTO;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -13,7 +14,7 @@ import java.util.Map;
@Data
@NoArgsConstructor
public class DataVisualizationVO implements Serializable {
public class DataVisualizationVO implements Serializable {
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@ -121,14 +122,20 @@ public class DataVisualizationVO implements Serializable {
/**
* 视图基本信息
*/
private Map<Long,ChartViewDTO> canvasViewInfo = new HashMap<>();
private Map<Long, ChartViewDTO> canvasViewInfo = new HashMap<>();
public DataVisualizationVO(Long id, String name, String type, String canvasStyleData, String componentData, Map<Long,ChartViewDTO> canvasViewInfo) {
/**
* 视图模版数据
*/
private Map<Long, VisualizationTemplateExtendDataDTO> extendDataInfo = new HashMap<>();
public DataVisualizationVO(Long id, String name, String type, String canvasStyleData, String componentData, Map<Long, ChartViewDTO> canvasViewInfo, Map<Long, VisualizationTemplateExtendDataDTO> extendDataInfo) {
this.id = id;
this.name = name;
this.type = type;
this.canvasStyleData = canvasStyleData;
this.componentData = componentData;
this.canvasViewInfo = canvasViewInfo;
this.extendDataInfo = extendDataInfo;
}
}