Merge pull request #11009 from dataease/pr@dev-v2@feat_app-import

feat(仪表板、大屏): 支持应用导出、应用导入相关优化
This commit is contained in:
王嘉豪 2024-07-17 23:27:04 +08:00 committed by GitHub
commit e6d8e141d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 381 additions and 73 deletions

View File

@ -80,6 +80,7 @@ public class DatasetGroupManage {
private Lock lock = new ReentrantLock();
@Transactional
public DatasetGroupInfoDTO save(DatasetGroupInfoDTO datasetGroupInfoDTO, boolean rename) throws Exception {
lock.lock();
try {

View File

@ -9,7 +9,7 @@ import java.io.Serializable;
* </p>
*
* @author fit2cloud
* @since 2024-07-16
* @since 2024-07-17
*/
@TableName("visualization_template")
public class VisualizationTemplate implements Serializable {

View File

@ -10,7 +10,7 @@ import org.apache.ibatis.annotations.Mapper;
* </p>
*
* @author fit2cloud
* @since 2024-07-16
* @since 2024-07-17
*/
@Mapper
public interface VisualizationTemplateMapper extends BaseMapper<VisualizationTemplate> {

View File

@ -2,8 +2,16 @@ package io.dataease.visualization.server;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.gson.Gson;
import io.dataease.api.dataset.union.DatasetGroupInfoDTO;
import io.dataease.api.visualization.request.VisualizationAppExportRequest;
import io.dataease.api.visualization.vo.*;
import io.dataease.dataset.dao.auto.entity.CoreDatasetGroup;
import io.dataease.dataset.dao.auto.entity.CoreDatasetTable;
import io.dataease.dataset.dao.auto.entity.CoreDatasetTableField;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetGroupMapper;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetTableFieldMapper;
import io.dataease.dataset.dao.auto.mapper.CoreDatasetTableMapper;
import io.dataease.dataset.manage.DatasetDataManage;
import io.dataease.dataset.manage.DatasetGroupManage;
import io.dataease.extensions.datasource.dto.DatasetTableDTO;
@ -37,6 +45,7 @@ import io.dataease.template.dao.auto.mapper.VisualizationTemplateMapper;
import io.dataease.template.dao.ext.ExtVisualizationTemplateMapper;
import io.dataease.template.manage.TemplateCenterManage;
import io.dataease.utils.*;
import io.dataease.visualization.dao.auto.entity.CoreStore;
import io.dataease.visualization.dao.auto.entity.DataVisualizationInfo;
import io.dataease.visualization.dao.auto.entity.VisualizationWatermark;
import io.dataease.visualization.dao.auto.mapper.DataVisualizationInfoMapper;
@ -45,6 +54,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.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestBody;
@ -104,6 +114,15 @@ public class DataVisualizationServer implements DataVisualizationApi {
@Resource
private ExtVisualizationTemplateMapper appTemplateMapper;
@Resource
private CoreDatasetGroupMapper coreDatasetGroupMapper;
@Resource
private CoreDatasetTableMapper coreDatasetTableMapper;
@Resource
private CoreDatasetTableFieldMapper coreDatasetTableFieldMapper;
@Override
public DataVisualizationVO findCopyResource(Long dvId, String busiFlag) {
DataVisualizationVO result = findById(new DataVisualizationBaseRequest(dvId, busiFlag));
@ -152,7 +171,84 @@ public class DataVisualizationServer implements DataVisualizationApi {
@DeLog(id = "#p0.id", pid = "#p0.pid", ot = LogOT.CREATE, stExp = "#p0.type")
@Override
@Transactional
public String saveCanvas(DataVisualizationBaseRequest request) {
public String saveCanvas(DataVisualizationBaseRequest request) throws Exception{
Long time = System.currentTimeMillis();
// 如果是应用 则新进行应用校验 数据集名称和 数据源名称校验
VisualizationExport2AppVO appData = request.getAppData();
Map<Long,Long> dsGroupIdMap = new HashMap<>();
Map<Long,Long> dsTableIdMap = new HashMap<>();
Map<Long,Long> dsTableFieldsIdMap = new HashMap<>();
if(appData != null){
try {
Map<Long,Long> datasourceIdMap = appData.getDatasourceInfo().stream()
.collect(Collectors.toMap(AppCoreDatasourceVO::getId, AppCoreDatasourceVO::getSystemDatasourceId));
Long datasetFolderPid = request.getDatasetFolderPid();
String datasetFolderName = request.getDatasetFolderName();
QueryWrapper<CoreDatasetGroup> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("name", datasetFolderName);
queryWrapper.eq("pid", datasetFolderPid);
if (coreDatasetGroupMapper.exists(queryWrapper)) {
DEException.throwException("当前数据集分组名称已存在");
}
//新建数据集分组
DatasetGroupInfoDTO datasetFolderNewRequest = new DatasetGroupInfoDTO();
datasetFolderNewRequest.setName(datasetFolderName);
datasetFolderNewRequest.setNodeType("folder");
datasetFolderNewRequest.setPid(datasetFolderPid);
DatasetGroupInfoDTO datasetFolderNew = datasetGroupManage.save(datasetFolderNewRequest, false);
Long datasetFolderNewId = datasetFolderNew.getId();
//新建数据集
appData.getDatasetGroupsInfo().forEach(appDatasetGroup -> {
if ("dataset".equals(appDatasetGroup.getNodeType())) {
Long oldId = appDatasetGroup.getId();
Long newId = IDUtils.snowID();
DatasetGroupInfoDTO datasetNewRequest = new DatasetGroupInfoDTO();
BeanUtils.copyBean(datasetNewRequest, appDatasetGroup);
datasetNewRequest.setId(newId);
datasetNewRequest.setCreateBy(AuthUtils.getUser().getUserId() + "");
datasetNewRequest.setUpdateBy(AuthUtils.getUser().getUserId() + "");
datasetNewRequest.setCreateTime(time);
datasetNewRequest.setLastUpdateTime(time);
datasetNewRequest.setPid(datasetFolderNewId);
try {
datasetGroupManage.innerSave(datasetNewRequest);
dsGroupIdMap.put(oldId,newId);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
// 新建数据集表
appData.getDatasetTablesInfo().forEach(appCoreDatasetTableVO -> {
Long oldId = appCoreDatasetTableVO.getId();
Long newId = IDUtils.snowID();
CoreDatasetTable datasetTable = new CoreDatasetTable();
BeanUtils.copyBean(datasetTable,appCoreDatasetTableVO);
datasetTable.setDatasetGroupId(dsGroupIdMap.get(datasetTable.getDatasetGroupId()));
datasetTable.setId(newId);
datasetTable.setDatasourceId(datasourceIdMap.get(datasetTable.getDatasourceId()));
coreDatasetTableMapper.insert(datasetTable);
dsTableIdMap.put(oldId,newId);
});
// 新建数据字段
appData.getDatasetTableFieldsInfo().forEach( appDsTableFields ->{
Long oldId = appDsTableFields.getId();
Long newId = IDUtils.snowID();
CoreDatasetTableField dsDsField = new CoreDatasetTableField();
BeanUtils.copyBean(dsDsField,appDsTableFields);
dsDsField.setDatasetGroupId(dsGroupIdMap.get(dsDsField.getDatasetGroupId()));
dsDsField.setDatasetTableId(dsTableIdMap.get(dsDsField.getDatasetTableId()));
dsDsField.setDatasourceId(datasourceIdMap.get(dsDsField.getDatasourceId()));
dsDsField.setId(newId);
coreDatasetTableFieldMapper.insert(dsDsField);
dsTableFieldsIdMap.put(oldId,newId);
});
}catch (Exception e){
DEException.throwException("应用创建失败");
}
}
DataVisualizationInfo visualizationInfo = new DataVisualizationInfo();
BeanUtils.copyBean(visualizationInfo, request);
visualizationInfo.setNodeType(request.getNodeType() == null ? DataVisualizationConstants.NODE_TYPE.LEAF : request.getNodeType());
@ -168,6 +264,7 @@ public class DataVisualizationServer implements DataVisualizationApi {
}
Long newDvId = coreVisualizationManage.innerSave(visualizationInfo);
request.setId(newDvId);
// TODO 还原ID信息
//保存图表信息
chartDataManage.saveChartViewFromVisualization(request.getComponentData(), newDvId, request.getCanvasViewInfo());
return newDvId.toString();
@ -378,6 +475,11 @@ public class DataVisualizationServer implements DataVisualizationApi {
VisualizationTemplateExtendDataDTO extendDataDTO = new VisualizationTemplateExtendDataDTO(newDvId, newViewId, originViewData);
extendDataInfo.put(newViewId, extendDataDTO);
templateData = templateData.replaceAll(originViewId, newViewId.toString());
if(appData != null){
Map appDataFormat = JsonUtil.parse(appData,Map.class);
String sourceDvId = (String) appDataFormat.get("id");
appData = appData.replaceAll(originViewId, newViewId.toString()).replaceAll(sourceDvId, newDvId.toString());
}
canvasViewInfo.put(chartView.getId(), chartView);
//插入模板数据 此处预先插入减少数据交互量
VisualizationTemplateExtendData extendData = new VisualizationTemplateExtendData();

View File

@ -1,7 +1,7 @@
<script setup lang="ts">
import { ElMessage, ElMessageBox } from 'element-plus-secondary'
import eventBus from '@/utils/eventBus'
import { ref, nextTick, computed } from 'vue'
import { ref, nextTick, computed, toRefs } from 'vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
import { useAppStoreWithOut } from '@/store/modules/app'
@ -32,6 +32,7 @@ const dvMainStore = dvMainStoreWithOut()
const snapshotStore = snapshotStoreWithOut()
const { styleChangeTimes, snapshotIndex } = storeToRefs(snapshotStore)
const resourceGroupOpt = ref(null)
const resourceAppOpt = ref(null)
const dvToolbarMain = ref(null)
const { componentData, canvasStyleData, canvasViewInfo, dvInfo, editMode } =
storeToRefs(dvMainStore)
@ -41,6 +42,15 @@ const dvModel = 'dataV'
const outerParamsSetRef = ref(null)
const fullScreeRef = ref(null)
const props = defineProps({
createType: {
type: String,
default: 'create'
}
})
const { createType } = toRefs(props)
const closeEditCanvasName = () => {
nameEdit.value = false
if (!inputName.value || !inputName.value.trim()) {
@ -89,11 +99,33 @@ const resourceOptFinish = param => {
saveCanvasWithCheck()
}
}
const appOptFinish = param => {
if (param && param.opt === 'newLeaf') {
dvInfo.value.dataState = 'ready'
dvInfo.value.pid = param.pid
dvInfo.value.name = param.name
}
}
const saveCanvasWithCheck = () => {
const appData = dvMainStore.getAppDataInfo()
if (dvInfo.value.dataState === 'prepare') {
const params = { name: dvInfo.value.name, leaf: true, id: dvInfo.value.pid }
resourceGroupOpt.value.optInit('leaf', params, 'newLeaf', true)
if (appData) {
//
const params = {
base: {
pid: '',
name: dvInfo.value.name,
datasetFolderPid: null,
datasetFolderName: dvInfo.value.name
},
appData: appData
}
resourceAppOpt.value.init(params)
} else {
const params = { name: dvInfo.value.name, leaf: true, id: dvInfo.value.pid }
resourceGroupOpt.value.optInit('leaf', params, 'newLeaf', true)
}
return
}
saveResource()
@ -345,10 +377,12 @@ const fullScreenPreview = () => {
ref="resourceGroupOpt"
/>
<de-app-apply
ref="resourceGroupOpt"
ref="resourceAppOpt"
:component-data="componentData"
:dv-info="dvInfo"
:canvas-view-info="canvasViewInfo"
cur-canvas-type="dataV"
@saveApp="appOptFinish"
></de-app-apply>
</div>
<de-fullscreen ref="fullScreeRef" show-position="dvEdit"></de-fullscreen>

View File

@ -45,6 +45,7 @@ export const dvMainStore = defineStore('dataVisualization', {
inMobile: false,
firstLoadMap: [],
canvasStyleData: { ...deepCopy(DEFAULT_CANVAS_STYLE_DATA_DARK), backgroundColor: null },
appData: {}, //应用信息
// 当前展示画布缓存数据
componentDataCache: null,
// PC布局画布组件数据
@ -245,7 +246,12 @@ export const dvMainStore = defineStore('dataVisualization', {
setCanvasViewInfo(canvasViewInfo) {
this.canvasViewInfo = canvasViewInfo
},
getAppDataInfo() {
return this.appData
},
setAppDataInfo(appDataInfo) {
this.appData = appDataInfo
},
setCurComponent({ component, index }) {
if (!component && this.curComponent) {
this.curComponent['editing'] = false
@ -1205,13 +1211,14 @@ export const dvMainStore = defineStore('dataVisualization', {
this.canvasState[key] = value
}
},
createInit(dvType, resourceId?, pid?, watermarkInfo?) {
createInit(dvType, resourceId?, pid?, watermarkInfo?, preName) {
const optName = dvType === 'dashboard' ? '新建仪表板' : '新建数据大屏'
const name = preName ? preName : optName
this.dvInfo = {
dataState: 'prepare',
optType: null,
id: resourceId,
name: optName,
name: name,
pid: pid,
type: dvType,
status: 1,

View File

@ -25,7 +25,7 @@ import {
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
import { deepCopy } from '@/utils/utils'
const dvMainStore = dvMainStoreWithOut()
const { curBatchOptComponents, dvInfo, canvasStyleData, componentData, canvasViewInfo } =
const { curBatchOptComponents, dvInfo, canvasStyleData, componentData, canvasViewInfo, appData } =
storeToRefs(dvMainStore)
const snapshotStore = snapshotStoreWithOut()
@ -349,6 +349,7 @@ export async function canvasSave(callBack) {
canvasStyleData: JSON.stringify(canvasStyleData.value),
componentData: JSON.stringify(componentDataToSave),
canvasViewInfo: canvasViewInfo.value,
appData: appData.value,
...dvInfo.value,
watermarkInfo: null
}
@ -521,6 +522,10 @@ export async function decompressionPre(params, callBack) {
.then(response => {
const deTemplateDataTemp = response.data
const sourceComponentData = JSON.parse(deTemplateDataTemp['componentData'])
let appData
if (deTemplateDataTemp['appData']) {
appData = JSON.parse(deTemplateDataTemp['appData'])
}
sourceComponentData.forEach(componentItem => {
// 2 为基础版本 此处需要增加仪表板矩阵密度
if (
@ -538,7 +543,10 @@ export async function decompressionPre(params, callBack) {
canvasStyleData: sourceCanvasStyle,
componentData: sourceComponentData,
canvasViewInfo: deTemplateDataTemp['canvasViewInfo'],
appData: deTemplateDataTemp['appData']
appData: appData,
baseInfo: {
preName: deTemplateDataTemp.name
}
}
})
.catch(e => {

View File

@ -5,8 +5,9 @@ import { Plus, Search } from '@element-plus/icons-vue'
import { useI18n } from '@/hooks/web/useI18n'
import { useAppStoreWithOut } from '@/store/modules/app'
import _ from 'lodash'
import { getDatasetTree } from '@/api/dataset'
import { getDatasetTree, getDatasourceList } from '@/api/dataset'
import { ElFormItem, FormInstance } from 'element-plus-secondary'
import type { DataSource } from '@/views/visualized/data/dataset/form/util'
const props = withDefaults(
defineProps<{
@ -14,10 +15,12 @@ const props = withDefaults(
modelValue?: string | number
stateObj: any
viewId: string
sourceType: string
}>(),
{
datasetTree: () => [],
themes: 'dark'
themes: 'dark',
sourceType: 'dataset'
}
)
@ -29,9 +32,13 @@ const datasetTree = ref<Tree[]>([])
const toolTip = computed(() => {
return props.themes === 'dark' ? 'ndark' : 'dark'
})
const sourceName = computed(() => (props.sourceType === 'datasource' ? '数据源' : '数据集'))
const initDataset = () => {
loadingDatasetTree.value = true
getDatasetTree({})
const method = props.sourceType === 'datasource' ? getDatasourceList : getDatasetTree
method({})
.then(res => {
datasetTree.value = (res as unknown as Tree[]) || []
})
@ -110,7 +117,7 @@ const exist = computed(() => {
const selectedNodeName = computed(() => {
if (!exist.value) {
return '数据集不存在'
return sourceName.value + '不存在'
}
return selectedNode.value?.name
})
@ -212,7 +219,7 @@ onMounted(() => {
v-model="selectedNodeName"
readonly
class="data-set-dark"
placeholder="请选择数据集"
:placeholder="'请选择' + sourceName"
>
<template #suffix>
<el-icon class="input-arrow-icon" :class="{ reverse: _popoverShow }">
@ -227,7 +234,7 @@ onMounted(() => {
<el-container :class="themes">
<el-header>
<div class="m-title" :class="{ dark: themes === 'dark' }">
<div>{{ t('dataset.datalist') }}</div>
<div>{{ sourceName }}</div>
<el-button type="primary" link class="refresh-btn" @click="refresh">
{{ t('commons.refresh') }}
</el-button>
@ -244,7 +251,7 @@ onMounted(() => {
<el-main :class="{ dark: themes === 'dark' }">
<el-scrollbar max-height="252px" always>
<div class="m-loading" v-if="loadingDatasetTree" v-loading="loadingDatasetTree"></div>
<div class="empty-info" v-if="showEmptyInfo">暂无数据集</div>
<div class="empty-info" v-if="showEmptyInfo">暂无{{ sourceName }}</div>
<!-- <div class="empty-info" v-if="showEmptySearchInfo">暂无相关数据</div>-->
<el-tree
:class="{ dark: themes === 'dark' }"
@ -294,7 +301,7 @@ onMounted(() => {
<el-footer v-if="!isDataEaseBi">
<div class="footer-container">
<el-button type="primary" :icon="Plus" link class="add-btn" @click="addDataset">
新建数据集
新建{{ sourceName }}
</el-button>
</div>
</el-footer>

View File

@ -3,7 +3,7 @@
:title="'保存应用'"
v-model="state.appApplyDrawer"
custom-class="de-user-drawer"
size="600px"
size="500px"
direction="rtl"
>
<div class="app-export">
@ -12,16 +12,17 @@
:model="state.form"
:rules="state.rule"
class="de-form-item"
size="middle"
label-width="180px"
label-position="top"
>
<div class="de-row-rules" style="margin: 0 0 16px">
<span>基本信息</span>
</div>
<el-form-item :label="dvPreName + '名称'" prop="appName">
<el-form-item :label="dvPreName + '名称'" prop="name">
<el-input v-model="state.form.name" autocomplete="off" :placeholder="'请输入名称'" />
</el-form-item>
<el-form-item :label="dvPreName + '所在位置'" prop="version">
<el-form-item :label="dvPreName + '所在位置'" prop="pid">
<el-tree-select
style="width: 100%"
@keydown.stop
@ -44,23 +45,23 @@
</template>
</el-tree-select>
</el-form-item>
<el-form-item :label="'数据集分组名称'" prop="appName">
<el-form-item :label="'数据集分组名称'" prop="datasetFolderName">
<el-input
v-model="state.form.datasetFolderName"
autocomplete="off"
:placeholder="'请输入名称'"
/>
</el-form-item>
<el-form-item label="数据集分组位置" prop="version">
<el-form-item label="数据集分组位置" prop="datasetFolderPid">
<el-tree-select
style="width: 100%"
@keydown.stop
@keyup.stop
v-model="state.form.datasetFolderPid"
:data="state.dvTree"
:data="state.dsTree"
:props="state.propsTree"
@node-click="dvTreeSelect"
:filter-method="dvTreeFilterMethod"
@node-click="dsTreeSelect"
:filter-method="dsTreeFilterMethod"
:render-after-expand="false"
filterable
>
@ -77,13 +78,47 @@
<div class="de-row-rules" style="margin: 0 0 16px">
<span>数据源信息</span>
</div>
<div>数据源信息配置</div>
<el-row class="datasource-link">
<el-row class="head">
<el-col :span="11">应用数据源</el-col><el-col :span="2"></el-col
><el-col :span="11">系统数据源</el-col>
</el-row>
<el-row
:key="index"
class="content"
v-for="(appDatasource, index) in state.appData.datasourceInfo"
>
<el-col :span="11">
<el-select style="width: 100%" v-model="appDatasource.name" disabled>
<el-option
:key="appDatasource.name"
:label="appDatasource.name"
:value="appDatasource.name"
>
</el-option>
</el-select> </el-col
><el-col :span="2" class="icon-center">
<Icon style="width: 20px; height: 20px" name="dv-link-target" /></el-col
><el-col :span="11">
<dataset-select
ref="datasetSelector"
v-model="appDatasource.systemDatasourceId"
style="flex: 1"
:state-obj="state"
themes="light"
source-type="datasource"
@add-ds-window="addDsWindow"
view-id="0"
/>
</el-col>
</el-row>
</el-row>
</el-form>
</div>
<template #footer>
<div class="apply" style="width: 100%">
<el-button secondary @click="close">{{ $t('commons.cancel') }} </el-button>
<el-button type="primary" @click="downloadApp">保存</el-button>
<el-button type="primary" @click="saveApp">保存</el-button>
</div>
</template>
</el-drawer>
@ -98,11 +133,20 @@ import {
ElInput,
ElTreeSelect
} from 'element-plus-secondary'
import { computed, reactive, ref, toRefs } from 'vue'
import { computed, PropType, reactive, ref, toRefs } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { queryTreeApi } from '@/api/visualization/dataVisualization'
import { BusiTreeNode, BusiTreeRequest } from '@/models/tree/TreeNode'
import { getDatasetTree } from '@/api/dataset'
import DatasetSelect from '@/views/chart/components/editor/dataset-select/DatasetSelect.vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import { deepCopy } from '@/utils/utils'
const { t } = useI18n()
const emits = defineEmits(['closeDraw', 'downLoadApp'])
const emits = defineEmits(['closeDraw', 'saveApp'])
const appSaveForm = ref(null)
const dvMainStore = dvMainStoreWithOut()
const { dvInfo, appData } = storeToRefs(dvMainStore)
const props = defineProps({
componentData: {
@ -113,34 +157,42 @@ const props = defineProps({
type: Object,
required: true
},
dvInfo: {
type: Object,
curCanvasType: {
type: String,
required: true
},
dvType: {
type: String,
default: 'dashboard'
themes: {
type: String as PropType<EditorTheme>,
default: 'dark'
}
})
const { componentData, canvasViewInfo, dvInfo, dvType } = toRefs(props)
const { componentData, canvasViewInfo, curCanvasType, themes } = toRefs(props)
const dvPreName = computed(() => (dvType.value === 'dashboard' ? '仪表板' : '数据大屏'))
const dvPreName = computed(() => (curCanvasType.value === 'dashboard' ? '仪表板' : '数据大屏'))
const addDsWindow = () => {
// do addDsWindow
const url = '#/data/datasource?opt=create'
window.open(url, '_blank')
}
const state = reactive({
appApplyDrawer: false,
dvTree: [],
dsTree: [],
propsTree: {
label: 'name',
children: 'children',
isLeaf: node => !node.children?.length
},
appData: {
datasourceInfo: []
},
form: {
pid: '',
name: '新建',
datasetFolderPid: null,
datasetFolderName: null,
datasourceMap: {} // ID
datasetFolderName: null
},
rule: {
name: [
@ -173,8 +225,6 @@ const state = reactive({
datasetFolderPid: [
{
required: true,
min: 2,
max: 25,
message: '请选择数据集分组所属文件夹',
trigger: 'blur'
}
@ -182,20 +232,59 @@ const state = reactive({
}
})
const initData = () => {
const request = { busiFlag: curCanvasType.value, leaf: false, weight: 7 }
queryTreeApi(request).then(res => {
const resultTree = res || []
dfs(resultTree as unknown as BusiTreeNode[])
state.dvTree = (resultTree as unknown as BusiTreeNode[]) || []
if (state.dvTree.length && state.dvTree[0].name === 'root' && state.dvTree[0].id === '0') {
state.dvTree[0].name = curCanvasType.value === 'dataV' ? '数据大屏' : '仪表板'
}
})
const requestDs = { leaf: false, weight: 7 } as BusiTreeRequest
getDatasetTree(requestDs).then(res => {
dfs(res as unknown as BusiTreeNode[])
state.dsTree = (res as unknown as BusiTreeNode[]) || []
if (state.dsTree.length && state.dsTree[0].name === 'root' && state.dsTree[0].id === '0') {
state.dsTree[0].name = '数据集'
}
})
}
const dfs = (arr: BusiTreeNode[]) => {
arr.forEach(ele => {
ele['value'] = ele.id
if (ele.children?.length) {
dfs(ele.children)
}
})
}
const init = params => {
console.log('init==')
state.appApplyDrawer = true
state.form = params
state.form = params.base
state.appData.datasourceInfo = deepCopy(appData.value?.datasourceInfo)
initData()
}
const dvTreeFilterMethod = value => {
state.dvTree = [...state.dvTree].filter(item => item.name.includes(value))
}
const dsTreeFilterMethod = value => {
state.dsTree = [...state.dsTree].filter(item => item.name.includes(value))
}
const dvTreeSelect = element => {
state.form.pid = element.id
}
const dsTreeSelect = element => {
state.form.datasetFolderPid = element.id
}
const close = () => {
emits('closeDraw')
state.appApplyDrawer = false
@ -204,8 +293,14 @@ const close = () => {
const saveApp = () => {
appSaveForm.value?.validate(valid => {
if (valid) {
const viewIds = []
const dsIds = []
// datasource
appData.value['datasourceInfo'] = state.appData.datasourceInfo
dvInfo.value['pid'] = state.form.pid
dvInfo.value['name'] = state.form.name
dvInfo.value['datasetFolderPid'] = state.form.datasetFolderPid
dvInfo.value['datasetFolderName'] = state.form.datasetFolderName
dvInfo.value['dataState'] = 'ready'
emits('saveApp')
} else {
return false
}
@ -241,6 +336,7 @@ defineExpose({
line-height: 22px;
padding-left: 10px;
margin: 24px 0 16px 0;
color: var(--ed-text-color-regular);
&::before {
content: '';
@ -253,4 +349,37 @@ defineExpose({
background: #3370ff;
}
}
.custom-tree-node {
display: flex;
align-items: center;
span {
margin-left: 8.75px;
width: 120px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
.datasource-link {
color: var(--ed-text-color-regular);
font-size: 12px;
font-weight: 500;
width: 100%;
.head {
width: 100%;
}
.content {
width: 100%;
margin-top: 8px;
}
}
.icon-center {
padding: 0 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
</style>

View File

@ -326,13 +326,15 @@ onMounted(async () => {
console.error('can not find watermark info')
}
let deTemplateData
let preName
if (createType === 'template') {
const templateParamsApply = JSON.parse(Base64.decode(decodeURIComponent(templateParams + '')))
await decompressionPre(templateParamsApply, result => {
deTemplateData = result
preName = deTemplateData.baseInfo?.preName
})
}
dvMainStore.createInit('dataV', null, pid, watermarkBaseInfo)
dvMainStore.createInit('dataV', null, pid, watermarkBaseInfo, preName)
nextTick(() => {
state.canvasInitStatus = true
dvMainStore.setDataPrepareState(true)
@ -342,6 +344,7 @@ onMounted(async () => {
dvMainStore.setComponentData(deTemplateData['componentData'])
dvMainStore.setCanvasStyle(deTemplateData['canvasStyleData'])
dvMainStore.setCanvasViewInfo(deTemplateData['canvasViewInfo'])
dvMainStore.setAppDataInfo(deTemplateData['appData'])
setTimeout(() => {
snapshotStore.recordSnapshotCache()
}, 1500)

View File

@ -46,7 +46,7 @@ public interface DataVisualizationApi {
@PostMapping("/saveCanvas")
@DePermit(value = {"#p0.pid + ':manage'"}, busiFlag = "#p0.type")
@Operation(summary = "画布保存")
String saveCanvas(@RequestBody DataVisualizationBaseRequest request);
String saveCanvas(@RequestBody DataVisualizationBaseRequest request) throws Exception;
@PostMapping("/updateCanvas")
@DePermit(value = {"#p0.id + ':manage'"}, busiFlag = "#p0.type")

View File

@ -1,6 +1,9 @@
package io.dataease.api.visualization.request;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.dataease.api.visualization.vo.DataVisualizationVO;
import io.dataease.api.visualization.vo.VisualizationExport2AppVO;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -32,11 +35,22 @@ public class DataVisualizationBaseRequest extends DataVisualizationVO {
private String source;
// 定时报告id
@JsonSerialize(using = ToStringSerializer.class)
private Long reportId;
// 定时报告任务id
@JsonSerialize(using = ToStringSerializer.class)
private Long taskId;
private VisualizationExport2AppVO appData;
@JsonSerialize(using = ToStringSerializer.class)
// 数据集分组PID
private Long datasetFolderPid;
// 数据集分组名称
private String datasetFolderName;
public DataVisualizationBaseRequest(Long id,String busiFlag) {
this.busiFlag = busiFlag;

View File

@ -79,4 +79,9 @@ public class AppCoreDatasourceVO implements Serializable {
*/
private String taskStatus;
/**
* 映射系统数据源ID
*/
private Long systemDatasourceId;
}

View File

@ -17,27 +17,27 @@ public class VisualizationExport2AppVO {
private String visualizationViewsInfo;
private String chartViewsInfo;
List<AppCoreChartViewVO> chartViewsInfo;
private String datasetGroupsInfo;
List<AppCoreDatasetGroupVO> datasetGroupsInfo;
private String datasetTablesInfo;
List<AppCoreDatasetTableVO> datasetTablesInfo;
private String datasetTableFieldsInfo;
List<AppCoreDatasetTableFieldVO> datasetTableFieldsInfo;
private String datasourceInfo;
List<AppCoreDatasourceVO> datasourceInfo;
private String datasourceTaskInfo;
List<AppCoreDatasourceTaskVO> datasourceTaskInfo;
private String linkJumps;
List<VisualizationLinkJumpVO> linkJumps;
private String linkJumpInfos;
List<VisualizationLinkJumpInfoVO> linkJumpInfos;
private String linkJumpTargetInfos;
List<VisualizationLinkJumpTargetViewInfoVO> linkJumpTargetInfos;
private String linkages;
List<VisualizationLinkageVO> linkages;
private String linkageFields;
List<VisualizationLinkageFieldVO> linkageFields;
public VisualizationExport2AppVO() {
@ -58,20 +58,18 @@ public class VisualizationExport2AppVO {
List<VisualizationLinkJumpTargetViewInfoVO> linkJumpTargetViewVOInfo,
List<VisualizationLinkageVO> linkagesVOInfo,
List<VisualizationLinkageFieldVO> linkageFieldVOInfo) {
List<Object> empty = new ArrayList<>();
Gson gson = new Gson();
this.checkStatus = true;
this.checkMes = "success";
this.chartViewsInfo = gson.toJson(chartViewVOInfo != null ? chartViewVOInfo : empty);
this.datasetGroupsInfo = gson.toJson(datasetGroupVOInfo != null ? datasetGroupVOInfo : empty);
this.datasetTablesInfo = gson.toJson(datasetTableVOInfo != null ? datasetTableVOInfo : empty);
this.datasetTableFieldsInfo = gson.toJson(datasetTableFieldVOInfo != null ? datasetTableFieldVOInfo : empty);
this.datasourceTaskInfo = gson.toJson(datasourceTaskVOInfo != null ? datasourceTaskVOInfo : empty);
this.datasourceInfo = gson.toJson(datasourceVOInfo != null ? datasourceVOInfo : empty);
this.linkJumps = gson.toJson(linkJumpVOInfo != null ? linkJumpVOInfo : empty);
this.linkJumpInfos = gson.toJson(linkJumpInfoVOInfo != null ? linkJumpInfoVOInfo : empty);
this.linkJumpTargetInfos = gson.toJson(linkJumpTargetViewVOInfo != null ? linkJumpTargetViewVOInfo : empty);
this.linkages = gson.toJson(linkagesVOInfo != null ? linkagesVOInfo : empty);
this.linkageFields = gson.toJson(linkageFieldVOInfo != null ? linkageFieldVOInfo : empty);
this.chartViewsInfo = chartViewVOInfo != null ? chartViewVOInfo : new ArrayList<>();
this.datasetGroupsInfo = datasetGroupVOInfo != null ? datasetGroupVOInfo : new ArrayList<>();
this.datasetTablesInfo = datasetTableVOInfo != null ? datasetTableVOInfo : new ArrayList<>();
this.datasetTableFieldsInfo = datasetTableFieldVOInfo != null ? datasetTableFieldVOInfo : new ArrayList<>();
this.datasourceTaskInfo = datasourceTaskVOInfo != null ? datasourceTaskVOInfo : new ArrayList<>();
this.datasourceInfo = datasourceVOInfo != null ? datasourceVOInfo : new ArrayList<>();
this.linkJumps = linkJumpVOInfo != null ? linkJumpVOInfo : new ArrayList<>();
this.linkJumpInfos = linkJumpInfoVOInfo != null ? linkJumpInfoVOInfo : new ArrayList<>();
this.linkJumpTargetInfos = linkJumpTargetViewVOInfo != null ? linkJumpTargetViewVOInfo : new ArrayList<>();
this.linkages = linkagesVOInfo != null ? linkagesVOInfo : new ArrayList<>();
this.linkageFields = linkageFieldVOInfo != null ? linkageFieldVOInfo : new ArrayList<>();
}
}