<template>
|
<el-drawer
|
:title="t('visualization.save_app')"
|
v-model="state.appApplyDrawer"
|
modal-class="de-app-drawer"
|
:show-close="false"
|
:close-on-click-modal="false"
|
:close-on-press-escape="false"
|
size="500px"
|
direction="rtl"
|
:z-index="1000"
|
>
|
<div class="app-export">
|
<el-form
|
ref="appSaveForm"
|
:model="state.form"
|
:rules="isDatasourceMatch ? state.ruleDatasource : state.ruleDataset"
|
class="de-form-item app-form create-dialog"
|
label-width="180px"
|
label-position="top"
|
>
|
<div class="de-row-rules" style="margin: 0 0 16px">
|
<span>{{ t('visualization.base_info') }}</span>
|
</div>
|
<el-form-item :label="dvPreName + t('visualization.name')" prop="name">
|
<el-input
|
v-model="state.form.name"
|
autocomplete="off"
|
:placeholder="t('visualization.input_tips')"
|
/>
|
</el-form-item>
|
<el-form-item :label="dvPreName + t('visualization.position')" prop="pid">
|
<el-tree-select
|
style="width: 100%"
|
@keydown.stop
|
@keyup.stop
|
v-model="state.form.pid"
|
:data="state.dvTree"
|
:props="state.propsTree"
|
@node-click="dvTreeSelect"
|
:render-after-expand="false"
|
filterable
|
>
|
<template #default="{ data: { name } }">
|
<span class="custom-tree-node">
|
<el-icon>
|
<Icon name="dv-folder"><dvFolder class="svg-icon" /></Icon>
|
</el-icon>
|
<span :title="name">{{ name }}</span>
|
</span>
|
</template>
|
</el-tree-select>
|
</el-form-item>
|
<el-form-item :label="t('visualization.data_match_type')" prop="dataType">
|
<el-select v-model="state.form.dataType" :placeholder="t('chart.pls_select_field')">
|
<el-option key="datasource" :label="t('datasource.datasource')" value="datasource" />
|
<el-option key="dataset" :label="t('dataset.datalist')" value="dataset" />
|
</el-select>
|
</el-form-item>
|
<template v-if="isDatasourceMatch">
|
<el-form-item :label="t('visualization.ds_group_name')" prop="datasetFolderName">
|
<el-input
|
v-model="state.form.datasetFolderName"
|
autocomplete="off"
|
:placeholder="t('visualization.input_tips')"
|
/>
|
</el-form-item>
|
<el-form-item :label="t('visualization.ds_group_position')" prop="datasetFolderPid">
|
<el-tree-select
|
style="width: 100%"
|
@keydown.stop
|
@keyup.stop
|
v-model="state.form.datasetFolderPid"
|
:data="state.dsTree"
|
:props="state.propsTree"
|
@node-click="dsTreeSelect"
|
:render-after-expand="false"
|
filterable
|
>
|
<template #default="{ data: { name } }">
|
<span class="custom-tree-node">
|
<el-icon>
|
<Icon name="dv-folder"><dvFolder class="svg-icon" /></Icon>
|
</el-icon>
|
<span :title="name">{{ name }}</span>
|
</span>
|
</template>
|
</el-tree-select>
|
</el-form-item>
|
<div class="de-row-rules" style="margin: 0 0 16px">
|
<span>{{ t('visualization.datasource_info') }}</span>
|
</div>
|
<el-row class="datasource-link">
|
<el-row class="head">
|
<el-col :span="11">{{ t('visualization.app_datasource') }}</el-col
|
><el-col :span="2"></el-col
|
><el-col :span="11">{{ t('visualization.sys_datasource') }}</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 name="dv-link-target"
|
><dvLinkTarget
|
class="svg-icon"
|
style="width: 20px; height: 20px" /></Icon></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="addDatasourceWindow"
|
view-id="0"
|
/>
|
</el-col>
|
</el-row>
|
</el-row>
|
</template>
|
<template v-if="!isDatasourceMatch">
|
<div class="de-row-rules" style="margin: 0 0 16px">
|
<span>{{ t('visualization.dataset_info') }}</span>
|
</div>
|
<el-row class="datasource-link">
|
<el-row class="head">
|
<el-col :span="11">{{ t('visualization.app_dataset') }}</el-col
|
><el-col :span="2"></el-col
|
><el-col :span="11">{{ t('visualization.sys_dataset') }}</el-col>
|
</el-row>
|
<el-row
|
:key="index"
|
class="content"
|
v-for="(appDataset, index) in state.appData.datasetGroupsInfo"
|
>
|
<el-col :span="11">
|
<el-select style="width: 100%" v-model="appDataset.name" disabled>
|
<el-option
|
:key="appDataset.name"
|
:label="appDataset.name"
|
:value="appDataset.name"
|
>
|
</el-option>
|
</el-select> </el-col
|
><el-col :span="2" class="icon-center">
|
<Icon name="dv-link-target"
|
><dvLinkTarget
|
class="svg-icon"
|
style="width: 20px; height: 20px" /></Icon></el-col
|
><el-col :span="11">
|
<dataset-select
|
ref="datasetSelector"
|
v-model="appDataset.systemDatasetId"
|
style="flex: 1"
|
:state-obj="state"
|
themes="light"
|
@add-ds-window="addDatasetWindow"
|
view-id="0"
|
/>
|
</el-col>
|
</el-row>
|
</el-row>
|
</template>
|
</el-form>
|
</div>
|
<template #footer>
|
<div class="apply" style="width: 100%">
|
<el-button v-if="isDesktop() || openType === '_self'" @click="goBack">{{
|
t('visualization.back')
|
}}</el-button>
|
<el-button type="primary" @click="saveApp">{{ t('visualization.save') }}</el-button>
|
</div>
|
</template>
|
</el-drawer>
|
</template>
|
|
<script lang="ts" setup>
|
import dvFolder from '@/assets/svg/dv-folder.svg'
|
import dvLinkTarget from '@/assets/svg/dv-link-target.svg'
|
import {
|
ElButton,
|
ElDrawer,
|
ElForm,
|
ElFormItem,
|
ElInput,
|
ElMessage,
|
ElTreeSelect
|
} from 'element-plus-secondary'
|
import { computed, PropType, reactive, ref, toRefs } from 'vue'
|
import { useI18n } from '@/hooks/web/useI18n'
|
import { dvNameCheck, 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'
|
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
|
import { useCache } from '@/hooks/web/useCache'
|
import { isDesktop } from '@/utils/ModelUtil'
|
import { filterFreeFolder } from '@/utils/utils'
|
|
const { wsCache } = useCache('localStorage')
|
const { t } = useI18n()
|
const emits = defineEmits(['closeDraw', 'saveAppCanvas'])
|
const appSaveForm = ref(null)
|
const dvMainStore = dvMainStoreWithOut()
|
const { dvInfo, appData } = storeToRefs(dvMainStore)
|
const snapshotStore = snapshotStoreWithOut()
|
const props = defineProps({
|
componentData: {
|
type: Object,
|
required: true
|
},
|
canvasViewInfo: {
|
type: Object,
|
required: true
|
},
|
curCanvasType: {
|
type: String,
|
required: true
|
},
|
themes: {
|
type: String as PropType<EditorTheme>,
|
default: 'dark'
|
}
|
})
|
|
const { curCanvasType } = toRefs(props)
|
const openType = wsCache.get('open-backend') === '1' ? '_self' : '_blank'
|
|
const dvPreName = computed(() =>
|
curCanvasType.value === 'dashboard'
|
? t('work_branch.dashboard')
|
: t('work_branch.big_data_screen')
|
)
|
const isDatasourceMatch = computed(() => state.form.dataType === 'datasource')
|
const addDatasourceWindow = () => {
|
// do addDsWindow
|
const url = '#/data/datasource?opt=create'
|
window.open(url, openType)
|
}
|
|
const addDatasetWindow = () => {
|
// do addDsWindow
|
const url = '#/data/dataset?opt=create'
|
window.open(url, openType)
|
}
|
|
const state = reactive({
|
appApplyDrawer: false,
|
dvTree: [],
|
dsTree: [],
|
propsTree: {
|
label: 'name',
|
children: 'children',
|
isLeaf: node => !node.children?.length
|
},
|
appData: {
|
datasourceInfo: [],
|
datasetGroupsInfo: []
|
},
|
form: {
|
pid: '',
|
name: t('visualization.new'),
|
datasetFolderPid: null,
|
datasetFolderName: null,
|
dataType: 'datasource'
|
},
|
ruleDataset: {
|
name: [
|
{
|
required: true,
|
min: 2,
|
max: 25,
|
message: t('datasource.input_limit_2_25', [2, 25]),
|
trigger: 'blur'
|
}
|
],
|
pid: [
|
{
|
required: true,
|
message: t('visualization.select_folder'),
|
trigger: 'blur'
|
}
|
],
|
dataType: [
|
{
|
required: true
|
}
|
]
|
},
|
ruleDatasource: {
|
name: [
|
{
|
required: true,
|
min: 2,
|
max: 25,
|
message: t('datasource.input_limit_2_25', [2, 25]),
|
trigger: 'blur'
|
}
|
],
|
pid: [
|
{
|
required: true,
|
message: t('visualization.select_folder'),
|
trigger: 'blur'
|
}
|
],
|
datasetFolderName: [
|
{
|
required: true,
|
min: 2,
|
max: 25,
|
message: t('datasource.input_limit_2_25', [2, 25]),
|
trigger: 'blur'
|
}
|
],
|
datasetFolderPid: [
|
{
|
required: true,
|
message: t('visualization.select_ds_group_folder'),
|
trigger: 'blur'
|
}
|
],
|
dataType: [
|
{
|
required: true
|
}
|
]
|
}
|
})
|
|
const goBack = () => {
|
window.history.back()
|
}
|
|
const initData = () => {
|
const request = { busiFlag: curCanvasType.value, resourceTable: 'core', leaf: false, weight: 7 }
|
queryTreeApi(request).then(res => {
|
filterFreeFolder(res, curCanvasType.value)
|
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'
|
? t('work_branch.big_data_screen')
|
: t('work_branch.dashboard')
|
}
|
})
|
|
const requestDs = { leaf: false, weight: 7 } as BusiTreeRequest
|
getDatasetTree(requestDs).then(res => {
|
filterFreeFolder(res, 'dataset')
|
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 = t('visualization.dataset')
|
}
|
})
|
}
|
|
const dfs = (arr: BusiTreeNode[]) => {
|
arr.forEach(ele => {
|
ele['value'] = ele.id
|
if (ele.children?.length) {
|
dfs(ele.children)
|
}
|
})
|
}
|
|
const init = params => {
|
state.appApplyDrawer = true
|
state.form = params.base
|
state.form.dataType = 'datasource'
|
state.appData.datasourceInfo = deepCopy(appData.value?.datasourceInfo)
|
state.appData.datasetGroupsInfo = deepCopy(appData.value?.datasetGroupsInfo)
|
initData()
|
}
|
|
const dvTreeSelect = element => {
|
state.form.pid = element.id
|
}
|
|
const dsTreeSelect = element => {
|
state.form.datasetFolderPid = element.id
|
}
|
|
const close = () => {
|
emits('closeDraw')
|
snapshotStore.recordSnapshotCache('renderChart')
|
state.appApplyDrawer = false
|
}
|
|
const saveApp = () => {
|
let datasourceMatchReady = true
|
let datasetMatchReady = true
|
state.appData.datasourceInfo.forEach(datasource => {
|
if (!datasource.systemDatasourceId) {
|
datasourceMatchReady = false
|
}
|
})
|
|
state.appData.datasetGroupsInfo.forEach(dataset => {
|
if (!dataset.systemDatasetId) {
|
datasetMatchReady = false
|
}
|
})
|
if (!datasourceMatchReady && isDatasourceMatch.value) {
|
ElMessage.error(t('visualization.app_no_datasource_tips'))
|
return
|
}
|
|
if (!datasetMatchReady && !isDatasourceMatch.value) {
|
ElMessage.error(t('visualization.app_no_dataset_tips'))
|
return
|
}
|
appSaveForm.value?.validate(async valid => {
|
if (valid) {
|
// 还原datasource
|
appData.value['datasourceInfo'] = state.appData.datasourceInfo
|
appData.value['datasetGroupsInfo'] = state.appData.datasetGroupsInfo
|
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['dataType'] = state.form.dataType
|
dvInfo.value['dataState'] = 'ready'
|
await dvNameCheck({
|
opt: 'newLeaf',
|
nodeType: 'leaf',
|
name: dvInfo.value['name'],
|
type: curCanvasType.value,
|
pid: dvInfo.value['pid']
|
})
|
snapshotStore.recordSnapshotCache('renderChart')
|
|
emits('saveAppCanvas')
|
} else {
|
return false
|
}
|
})
|
}
|
|
defineExpose({
|
init,
|
close
|
})
|
</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;
|
}
|
|
.de-row-rules {
|
display: flex;
|
align-items: center;
|
position: relative;
|
font-size: 14px;
|
font-weight: 500;
|
line-height: 22px;
|
padding-left: 10px;
|
margin: 24px 0 16px 0;
|
color: var(--ed-text-color-regular);
|
|
&::before {
|
content: '';
|
position: absolute;
|
left: 0;
|
top: 50%;
|
transform: translateY(-50%);
|
height: 14px;
|
width: 2px;
|
background: var(--ed-color-primary, #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_type {
|
width: 100%;
|
margin-bottom: 16px;
|
}
|
.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;
|
}
|
.app-form {
|
padding-bottom: 95px;
|
}
|
</style>
|
|
<style lang="less">
|
.de-app-drawer {
|
z-index: 1000;
|
}
|
</style>
|