Merge pull request #10873 from dataease/pr@dev-v2@feat_app-export

feat(数据大屏、仪表板): 支持应用导出
This commit is contained in:
王嘉豪 2024-07-10 10:45:53 +08:00 committed by GitHub
commit ce97990761
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 344 additions and 6 deletions

View File

@ -2,7 +2,13 @@ package io.dataease.visualization.server;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.dataease.api.visualization.vo.VisualizationReportFilterVO;
import io.dataease.api.visualization.vo.*;
import io.dataease.dataset.manage.DatasetDataManage;
import io.dataease.dataset.manage.DatasetGroupManage;
import io.dataease.dataset.manage.DatasetTableManage;
import io.dataease.extensions.datasource.dto.DatasetTableDTO;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceDTO;
import io.dataease.extensions.view.dto.ChartViewDTO;
import io.dataease.api.template.dto.TemplateManageFileDTO;
import io.dataease.api.template.dto.VisualizationTemplateExtendDataDTO;
@ -10,9 +16,6 @@ import io.dataease.api.visualization.DataVisualizationApi;
import io.dataease.api.visualization.dto.VisualizationViewTableDTO;
import io.dataease.api.visualization.request.DataVisualizationBaseRequest;
import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest;
import io.dataease.api.visualization.vo.DataVisualizationVO;
import io.dataease.api.visualization.vo.VisualizationResourceVO;
import io.dataease.api.visualization.vo.VisualizationWatermarkVO;
import io.dataease.chart.dao.auto.entity.CoreChartView;
import io.dataease.chart.dao.auto.mapper.CoreChartViewMapper;
import io.dataease.chart.manage.ChartDataManage;
@ -22,6 +25,7 @@ import io.dataease.commons.constants.OptConstants;
import io.dataease.constant.CommonConstants;
import io.dataease.constant.LogOT;
import io.dataease.exception.DEException;
import io.dataease.i18n.Translator;
import io.dataease.license.config.XpackInteract;
import io.dataease.log.DeLog;
import io.dataease.model.BusiNodeRequest;
@ -41,6 +45,7 @@ import io.dataease.visualization.dao.ext.mapper.ExtDataVisualizationMapper;
import io.dataease.visualization.manage.CoreVisualizationManage;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody;
@ -91,6 +96,12 @@ public class DataVisualizationServer implements DataVisualizationApi {
@Resource
private VisualizationWatermarkMapper watermarkMapper;
@Resource
private DatasetGroupManage datasetGroupManage;
@Resource
private DatasetDataManage datasetDataManage;
@Override
public DataVisualizationVO findCopyResource(Long dvId, String busiFlag) {
DataVisualizationVO result = findById(new DataVisualizationBaseRequest(dvId, busiFlag));
@ -390,6 +401,41 @@ public class DataVisualizationServer implements DataVisualizationApi {
}
}
@Override
public VisualizationExport2AppVO export2AppCheck(Long dvId) {
//1.获取所有视图信息
List<ChartViewDTO> chartViewsInfo = chartViewManege.listBySceneId(dvId);
//2.获取视图扩展字段信息 获取所有数据集信息
List<Long> allTableIds = chartViewsInfo.stream().map(ChartViewDTO::getTableId).collect(Collectors.toList());
List<DatasetTableDTO> datasetTablesInfo = datasetGroupManage.getDetail(allTableIds);
// dataset check
if (CollectionUtils.isEmpty(datasetTablesInfo)) {
return new VisualizationExport2AppVO(Translator.get("I18N_APP_NO_DATASET_ERROR"));
}
//4.获取所有数据集字段信息
List<DatasetTableFieldDTO> datasetTableFieldsInfo = new ArrayList<>();
datasetTablesInfo.stream().forEach(datasetTable ->{
try {
List<DatasetTableFieldDTO> result = datasetDataManage.getTableFields(datasetTable);
if(!CollectionUtils.isEmpty(result)){
datasetTableFieldsInfo.addAll(result);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
//校验标准 1.存在视图且所有视图的数据来源必须是dataset 2.存在数据集且没有excel数据集 3.存在数据源且是单数据源
//1.view check
if (CollectionUtils.isEmpty(chartViewsInfo)) {
return new VisualizationExport2AppVO(Translator.get("I18N_APP_NO_VIEW_ERROR"));
} else if (chartViewsInfo.stream().filter(chartView -> chartView.getDataFrom().equals("template")).collect(Collectors.toList()).size() > 0) {
return new VisualizationExport2AppVO(Translator.get("I18N_APP_TEMPLATE_VIEW_ERROR"));
}
return new VisualizationExport2AppVO(chartViewsInfo, null, datasetTablesInfo, datasetTableFieldsInfo,
null, null, null, null, null);
}
@Override
public void nameCheck(DataVisualizationBaseRequest request) {

View File

@ -94,3 +94,11 @@ export const getComponentInfo = dvId => {
loading: false
})
}
export const export2AppCheck = dvId => {
return request.get({
url: '/dataVisualization/export2AppCheck/' + dvId,
method: 'get',
loading: true
})
}

View File

@ -0,0 +1,147 @@
<template>
<el-drawer
:title="'应用导出'"
v-model:visible="state.applyDownloadDrawer"
custom-class="de-user-drawer"
size="600px"
direction="rtl"
>
<div class="app-export">
<el-form
ref="applyDownloadForm"
:model="state.form"
:rules="state.rule"
size="small"
class="de-form-item"
label-width="180px"
label-position="right"
>
<el-form-item :label="'应用名称'" prop="appName">
<el-input v-model="form.appName" autocomplete="off" :placeholder="'请输入名称'" />
</el-form-item>
<el-form-item :label="'应用版本号'" prop="version">
<el-input v-model="state.form.version" autocomplete="off" />
</el-form-item>
<el-form-item :label="'DataEase最低版本号'" prop="required">
<el-input v-model="state.form.required" autocomplete="off" />
</el-form-item>
<el-form-item :label="'作者'" prop="creator">
<el-input v-model="state.form.creator" autocomplete="off" />
</el-form-item>
<el-form-item :label="'描述'" prop="description">
<el-input
:placeholder="'请输入内容'"
show-word-limit
:value="state.form.description"
type="textarea"
/>
</el-form-item>
</el-form>
</div>
<div class="app-export-bottom">
<div class="apply" style="width: 100%">
<el-button secondary @click="close">{{ $t('commons.cancel') }} </el-button>
<el-button type="primary" @click="downloadApp">{{ $t('app_template.export') }} </el-button>
</div>
</div>
</el-drawer>
</template>
<script lang="ts" setup>
import { ElButton, ElDrawer, ElForm, ElFormItem, ElInput } from 'element-plus-secondary'
import { reactive, ref } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
const { t } = useI18n()
const emits = defineEmits(['closeDraw', 'downLoadApp'])
const applyDownloadForm = ref(null)
const state = reactive({
applyDownloadDrawer: false,
form: {
appName: null,
icon: null,
version: null,
creator: null,
required: '1.16.0',
description: null
},
rule: {
appName: [
{
required: true,
min: 2,
max: 25,
message: t('datasource.input_limit_2_25', [2, 25]),
trigger: 'blur'
}
],
creator: [
{
required: true,
min: 2,
max: 25,
message: t('datasource.input_limit_2_25', [2, 25]),
trigger: 'blur'
}
],
required: [
{
required: true,
min: 2,
max: 25,
message: t('datasource.input_limit_2_25', [2, 25]),
trigger: 'blur'
}
],
version: [
{
required: true,
min: 2,
max: 25,
message: t('datasource.input_limit_2_25', [2, 25]),
trigger: 'blur'
}
]
}
})
const init = params => {
state.applyDownloadDrawer = true
state.form = params
}
const close = () => {
emits('closeDraw')
state.applyDownloadDrawer = false
}
const downloadApp = () => {
applyDownloadForm.value?.validate(valid => {
if (valid) {
emits('downLoadApp', state.form)
state.applyDownloadDrawer = false
} else {
return false
}
})
}
defineExpose({
init
})
</script>
<style lang="less" scoped>
.app-export {
width: 100%;
height: calc(100% - 56px);
}
.app-export-bottom {
width: 100%;
height: 56px;
text-align: right;
}
:deep(.ed-drawer__body) {
padding-bottom: 0 !important;
}
</style>

View File

@ -15,9 +15,12 @@ import { Icon } from '@/components/icon-custom'
import { download2AppTemplate, downloadCanvas2 } from '@/utils/imgUtils'
import MultiplexPreviewShow from '@/views/data-visualization/MultiplexPreviewShow.vue'
import DvPreview from '@/views/data-visualization/DvPreview.vue'
import AppExportForm from "@/components/de-app/AppExportForm.vue";
import {personInfoApi} from "@/api/user";
import {ElMessage} from "element-plus-secondary";
const dvMainStore = dvMainStoreWithOut()
const { dvInfo } = storeToRefs(dvMainStore)
const { dvInfo, canvasViewDataInfo } = storeToRefs(dvMainStore)
const previewCanvasContainer = ref(null)
const dvPreviewRef = ref(null)
const slideShow = ref(true)
@ -26,6 +29,7 @@ const permissionStore = usePermissionStoreWithOut()
const dataInitState = ref(true)
const downloadStatus = ref(false)
const { width, node } = useMoveLine('DASHBOARD')
const appExportFormRef = ref(null)
const props = defineProps({
showPosition: {
required: false,
@ -110,6 +114,46 @@ const downloadAsAppTemplate = downloadType => {
})
}
const downLoadToAppPre =()=>{
const result = checkTemplate()
if (result && result.length > 0) {
ElMessage.warning(`当前仪表板中[${result}]属于模版视图,无法导出,请先设置数据集!`,)
} else {
appExportFormRef.value.init({
appName: dvInfo.value.name,
icon: null,
version: '2.0',
creator: state.userLoginInfo?.nickName,
required: '2.9.0',
description: null
})
}
}
const checkTemplate =()=> {
let templateViewNames = ','
Object.keys(canvasViewDataInfo.value).forEach(key => {
const viewInfo = canvasViewDataInfo.value[key]
if (viewInfo.dataFrom === 'template') {
templateViewNames = templateViewNames + viewInfo.title + ','
}
})
return templateViewNames.slice(1)
}
const downLoadToApp = (appAttachInfo) =>{
this.dataLoading = true
export2AppCheck(this.$store.state.panel.panelInfo.id).then(rsp => {
if (rsp.data.checkStatus) {
this.saveAppFile(rsp.data, appAttachInfo)
} else {
this.dataLoading = false
this.$message({
message: rsp.data.checkMes,
type: 'error'
})
}
})
},
const slideOpenChange = () => {
slideShow.value = !slideShow.value
}
@ -127,7 +171,8 @@ const state = reactive({
canvasStylePreview: null,
canvasViewInfoPreview: null,
dvInfo: null,
curPreviewGap: 0
curPreviewGap: 0,
userLoginInfo: {}
})
const sideTreeStatus = ref(true)
@ -147,6 +192,16 @@ const getPreviewStateInfo = () => {
return state
}
const downLoadApp = (appAttachInfo) =>{
downLoadToApp(appAttachInfo)
}
const findUserData = callback => {
personInfoApi().then(rsp => {
callback(rsp)
})
}
defineExpose({
getPreviewStateInfo
})
@ -155,6 +210,9 @@ onBeforeMount(() => {
if (props.showPosition === 'preview') {
dvMainStore.canvasDataInit()
}
findUserData(res => {
state.userLoginInfo = res.data
})
})
</script>
@ -244,6 +302,8 @@ onBeforeMount(() => {
</template>
</el-container>
</div>
<app-export-form ref="appExportFormRef"
@downLoadApp="downLoadApp"></app-export-form>
</template>
<style lang="less">

View File

@ -7,6 +7,7 @@ import io.dataease.api.visualization.dto.VisualizationViewTableDTO;
import io.dataease.api.visualization.request.DataVisualizationBaseRequest;
import io.dataease.api.visualization.request.VisualizationWorkbranchQueryRequest;
import io.dataease.api.visualization.vo.DataVisualizationVO;
import io.dataease.api.visualization.vo.VisualizationExport2AppVO;
import io.dataease.api.visualization.vo.VisualizationResourceVO;
import io.dataease.auth.DeApiPath;
import io.dataease.auth.DePermit;
@ -110,4 +111,8 @@ public interface DataVisualizationApi {
@GetMapping("/viewDetailList/{dvId}")
@Operation(summary = "仪表板视图明细数据")
List<VisualizationViewTableDTO> detailList(@PathVariable("dvId") Long dvId);
@GetMapping("/export2AppCheck/{dvId}")
@Operation(summary = "仪表板视图明细数据")
VisualizationExport2AppVO export2AppCheck(@PathVariable("dvId") Long dvId);
}

View File

@ -0,0 +1,72 @@
package io.dataease.api.visualization.vo;
import com.google.gson.Gson;
import io.dataease.extensions.datasource.dto.DatasetTableDTO;
import io.dataease.extensions.datasource.dto.DatasetTableFieldDTO;
import io.dataease.extensions.datasource.dto.DatasourceDTO;
import io.dataease.extensions.view.dto.ChartViewDTO;
import io.dataease.extensions.view.dto.ChartViewFieldDTO;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class VisualizationExport2AppVO {
private Boolean checkStatus = false;
private String checkMes;
private String visualizationInfo;
private String visualizationViewsInfo;
private String chartViewsInfo;
private String chartViewFieldsInfo;
private String datasetTablesInfo;
private String datasetTableFieldsInfo;
private String datasetTasksInfo;
private String datasourceInfo;
private String linkJumps;
private String linkJumpInfos;
private String linkages;
private String linkageFields;
public VisualizationExport2AppVO() {
}
public VisualizationExport2AppVO(String checkMes) {
this.checkMes = checkMes;
}
public VisualizationExport2AppVO(List<ChartViewDTO> chartViewsInfo, List<ChartViewFieldDTO> chartViewFieldsInfo,
List<DatasetTableDTO> datasetTablesInfo, List<DatasetTableFieldDTO> datasetTableFieldsInfo, List<DatasourceDTO> datasourceInfo, List<VisualizationLinkJumpVO> linkJumps, List<VisualizationLinkJumpInfoVO> linkJumpInfos,
List<VisualizationLinkageVO> linkages, List<VisualizationLinkageFieldVO> linkageFields) {
List empty = new ArrayList();
Gson gson = new Gson();
this.checkStatus = true;
this.checkMes = "success";
this.chartViewsInfo = gson.toJson(chartViewsInfo != null ? chartViewsInfo : empty);
this.chartViewFieldsInfo = gson.toJson(chartViewFieldsInfo != null ? chartViewFieldsInfo : empty);
this.datasetTablesInfo = gson.toJson(datasetTablesInfo != null ? datasetTablesInfo : empty);
this.datasetTableFieldsInfo = gson.toJson(datasetTableFieldsInfo != null ? datasetTableFieldsInfo : empty);
this.datasetTasksInfo = gson.toJson(datasetTasksInfo != null ? datasetTasksInfo : empty);
this.datasourceInfo = gson.toJson(datasourceInfo != null ? datasourceInfo : empty);
this.visualizationViewsInfo = gson.toJson(visualizationViewsInfo != null ? visualizationViewsInfo : empty);
this.linkJumps = gson.toJson(linkJumps != null ? linkJumps : empty);
this.linkJumpInfos = gson.toJson(linkJumpInfos != null ? linkJumpInfos : empty);
this.linkages = gson.toJson(linkages != null ? linkages : empty);
this.linkageFields = gson.toJson(linkJumpInfos != null ? linkageFields : empty);
}
}