feat(xpack): 血缘分析

This commit is contained in:
dataeaseShu 2024-08-16 15:55:18 +08:00
parent f2ec5a2c0d
commit f07f00aa56
5 changed files with 277 additions and 26 deletions

View File

@ -120,6 +120,12 @@ export const delDatasetTree = async (id): Promise<IResponse> => {
}) })
} }
export const perDelete = async (id): Promise<boolean> => {
return request.post({ url: `/datasetTree/perDelete/${id}`, data: {} }).then(res => {
return res?.data
})
}
export const getDatasourceList = async (): Promise<IResponse> => { export const getDatasourceList = async (): Promise<IResponse> => {
return request.post({ url: '/datasource/tree', data: { busiFlag: 'datasource' } }).then(res => { return request.post({ url: '/datasource/tree', data: { busiFlag: 'datasource' } }).then(res => {
return res?.data return res?.data

View File

@ -91,6 +91,12 @@ export const save = async (data = {}): Promise<Dataset> => {
}) })
} }
export const perDeleteDatasource = async (id): Promise<boolean> => {
return request.post({ url: `/datasource//perDelete/${id}`, data: {} }).then(res => {
return res?.data
})
}
export const update = async (data = {}): Promise<Dataset> => { export const update = async (data = {}): Promise<Dataset> => {
return request.post({ url: '/datasource/update', data }).then(res => { return request.post({ url: '/datasource/update', data }).then(res => {
return res?.data return res?.data

View File

@ -0,0 +1,127 @@
<script lang="ts" setup>
import { ref, reactive, nextTick } from 'vue'
import { XpackComponent } from '@/components/plugin'
import { cloneDeep } from 'lodash-es'
import {
getDatasourceRelationship as getDatasourceRelation,
getDatasetRelationship as getDatasetRelation
} from '@/api/relation/index'
const relationDrawer = ref(false)
const chartSize = reactive({
height: 0,
width: 0
})
const getChartSize = () => {
const dom = document.querySelector('.relation-drawer_content')
if (!dom) return
Object.assign(chartSize, {
height: dom.offsetHeight + 'px',
width: dom.offsetWidth + 'px'
})
}
const consanguinity = ref()
let resRef = null
const getDatasourceRelationship = id => {
getDatasourceRelation(id)
.then(res => {
resRef = cloneDeep(res || {})
})
.finally(() => {
tableLoading.value = false
nextTick(() => {
consanguinity.value.invokeMethod({
methodName: 'getChartData',
args: {
info: current,
res: resRef
}
})
})
})
}
const getDatasetRelationship = id => {
getDatasetRelation(id)
.then(res => {
resRef = cloneDeep(res || {})
})
.finally(() => {
tableLoading.value = false
nextTick(() => {
consanguinity.value.invokeMethod({
methodName: 'getChartData',
args: {
info: current,
res: resRef
}
})
})
})
}
const current = {
queryType: '',
num: '',
label: ''
}
const tableLoading = ref(false)
const getChartData = obj => {
Object.assign(current, obj || {})
const { queryType, num } = current
tableLoading.value = true
relationDrawer.value = true
nextTick(() => {
getChartSize()
switch (queryType) {
case 'datasource':
getDatasourceRelationship(num)
break
case 'dataset':
getDatasetRelationship(num)
break
default:
break
}
})
}
defineExpose({
getChartData
})
</script>
<template>
<el-drawer
title="血缘关系图"
v-model="relationDrawer"
custom-class="de-relation-drawer"
size="1200px"
direction="rtl"
>
<div v-loading="tableLoading" class="relation-drawer_content">
<XpackComponent
ref="consanguinity"
:chart-size="chartSize"
:current="current"
detailDisabled
jsname="L21lbnUvc3lzdGVtL2Fzc29jaWF0aW9uL0NoYXJ0"
/>
</div>
</el-drawer>
</template>
<style lang="less">
.de-relation-drawer {
.ed-drawer__body {
padding-bottom: 24px;
}
.relation-drawer_content {
border: 1px solid #dee0e3;
width: 100%;
height: 100%;
background: #f5f6f7;
border-radius: 4px;
position: relative;
}
}
</style>

View File

@ -1,11 +1,13 @@
<script lang="tsx" setup> <script lang="tsx" setup>
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { ref, reactive, shallowRef, computed, watch, onBeforeMount, nextTick, unref } from 'vue' import { ref, reactive, shallowRef, computed, watch, onBeforeMount, nextTick, unref, h } from 'vue'
import ArrowSide from '@/views/common/DeResourceArrow.vue' import ArrowSide from '@/views/common/DeResourceArrow.vue'
import { useEmbedded } from '@/store/modules/embedded' import { useEmbedded } from '@/store/modules/embedded'
import { useEmitt } from '@/hooks/web/useEmitt' import { useEmitt } from '@/hooks/web/useEmitt'
import relationChart from '@/components/relation-chart/index.vue'
import { import {
ElIcon, ElIcon,
ElButton,
ElMessageBox, ElMessageBox,
ElMessage, ElMessage,
type ElMessageBoxOptions, type ElMessageBoxOptions,
@ -18,7 +20,7 @@ import { useMoveLine } from '@/hooks/web/useMoveLine'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import CreatDsGroup from './form/CreatDsGroup.vue' import CreatDsGroup from './form/CreatDsGroup.vue'
import type { BusiTreeNode, BusiTreeRequest } from '@/models/tree/TreeNode' import type { BusiTreeNode, BusiTreeRequest } from '@/models/tree/TreeNode'
import { delDatasetTree, getDatasetPreview, barInfoApi } from '@/api/dataset' import { delDatasetTree, getDatasetPreview, barInfoApi, perDelete } from '@/api/dataset'
import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue' import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue'
import DeResourceGroupOpt from '@/views/common/DeResourceGroupOpt.vue' import DeResourceGroupOpt from '@/views/common/DeResourceGroupOpt.vue'
import DatasetDetail from './DatasetDetail.vue' import DatasetDetail from './DatasetDetail.vue'
@ -352,7 +354,7 @@ const handleClick = (tabName: TabPaneName) => {
break break
} }
} }
const relationChartRef = ref()
const operation = (cmd: string, data: BusiTreeNode, nodeType: string) => { const operation = (cmd: string, data: BusiTreeNode, nodeType: string) => {
if (cmd === 'copy') { if (cmd === 'copy') {
if (isDataEaseBi.value) { if (isDataEaseBi.value) {
@ -384,10 +386,46 @@ const operation = (cmd: string, data: BusiTreeNode, nodeType: string) => {
delete options.tip delete options.tip
} }
if (nodeType !== 'folder') {
perDelete(data.id).then(res => {
if (res === true) {
const onClick = () => {
relationChartRef.value.getChartData({
queryType: 'dataset',
num: data.id,
label: data.name
})
}
ElMessageBox.confirm('', {
confirmButtonType: 'danger',
type: 'warning',
autofocus: false,
confirmButtonText: '确定',
showClose: false,
dangerouslyUseHTMLString: true,
message: h('div', null, [
h('p', { style: 'margin-bottom: 8px;' }, '确定删除该数据集吗?'),
h(
'p',
{ class: 'tip' },
'该数据集存在如下血缘关系,删除会造成相关仪表板的视图失效,确定删除?'
),
h(
ElButton,
{ text: true, onClick: onClick, style: 'margin-left: -4px;' },
'查看血缘关系'
)
])
}).then(() => {
delDatasetTree(data.id).then(() => {
getData()
ElMessage.success(t('dataset.delete_success'))
})
})
} else {
ElMessageBox.confirm( ElMessageBox.confirm(
nodeType === 'folder' t('datasource.delete_this_dataset'),
? t('data_set.delete_this_folder')
: t('datasource.delete_this_dataset'),
options as ElMessageBoxOptions options as ElMessageBoxOptions
).then(() => { ).then(() => {
delDatasetTree(data.id).then(() => { delDatasetTree(data.id).then(() => {
@ -395,6 +433,18 @@ const operation = (cmd: string, data: BusiTreeNode, nodeType: string) => {
ElMessage.success(t('dataset.delete_success')) ElMessage.success(t('dataset.delete_success'))
}) })
}) })
}
})
} else {
ElMessageBox.confirm(t('data_set.delete_this_folder'), options as ElMessageBoxOptions).then(
() => {
delDatasetTree(data.id).then(() => {
getData()
ElMessage.success(t('dataset.delete_success'))
})
}
)
}
} else { } else {
creatDsFolder.value.createInit(nodeType, data, cmd) creatDsFolder.value.createInit(nodeType, data, cmd)
} }
@ -846,6 +896,7 @@ const getMenuList = (val: boolean) => {
<empty-background :description="t('deDataset.on_the_left')" img-type="select" /> <empty-background :description="t('deDataset.on_the_left')" img-type="select" />
</template> </template>
</div> </div>
<relationChart ref="relationChartRef"></relationChart>
<de-resource-group-opt <de-resource-group-opt
:cur-canvas-type="curCanvasType" :cur-canvas-type="curCanvasType"
@finish="resourceOptFinish" @finish="resourceOptFinish"

View File

@ -1,15 +1,23 @@
<script lang="tsx" setup> <script lang="tsx" setup>
import { computed, unref, reactive, ref, shallowRef, nextTick, watch, onMounted } from 'vue' import { computed, h, unref, reactive, ref, shallowRef, nextTick, watch, onMounted } from 'vue'
import { dsTypes } from '@/views/visualized/data/datasource/form/option' import { dsTypes } from '@/views/visualized/data/datasource/form/option'
import type { TabPaneName, ElMessageBoxOptions } from 'element-plus-secondary' import type { TabPaneName, ElMessageBoxOptions } from 'element-plus-secondary'
import { ElIcon, ElMessageBox, ElMessage, ElScrollbar, ElAside } from 'element-plus-secondary' import {
ElIcon,
ElButton,
ElMessageBox,
ElMessage,
ElScrollbar,
ElAside
} from 'element-plus-secondary'
import GridTable from '@/components/grid-table/src/GridTable.vue' import GridTable from '@/components/grid-table/src/GridTable.vue'
import ArrowSide from '@/views/common/DeResourceArrow.vue' import ArrowSide from '@/views/common/DeResourceArrow.vue'
import relationChart from '@/components/relation-chart/index.vue'
import { HandleMore } from '@/components/handle-more' import { HandleMore } from '@/components/handle-more'
import { Icon } from '@/components/icon-custom' import { Icon } from '@/components/icon-custom'
import { fieldType } from '@/utils/attr' import { fieldType } from '@/utils/attr'
import { useEmitt } from '@/hooks/web/useEmitt' import { useEmitt } from '@/hooks/web/useEmitt'
import { getHidePwById, listSyncRecord, uploadFile } from '@/api/datasource' import { getHidePwById, listSyncRecord, uploadFile, perDeleteDatasource } from '@/api/datasource'
import CreatDsGroup from './form/CreatDsGroup.vue' import CreatDsGroup from './form/CreatDsGroup.vue'
import type { Tree } from '../dataset/form/CreatDsGroup.vue' import type { Tree } from '../dataset/form/CreatDsGroup.vue'
import { previewData, getById } from '@/api/datasource' import { previewData, getById } from '@/api/datasource'
@ -738,6 +746,7 @@ const handleDatasourceTree = (cmd: string, data?: Tree) => {
creatDsFolder.value.createInit(cmd, data || {}) creatDsFolder.value.createInit(cmd, data || {})
} }
} }
const relationChartRef = ref()
const operation = (cmd: string, data: Tree, nodeType: string) => { const operation = (cmd: string, data: Tree, nodeType: string) => {
if (cmd === 'copy') { if (cmd === 'copy') {
handleCopy(data) handleCopy(data)
@ -758,8 +767,46 @@ const operation = (cmd: string, data: Tree, nodeType: string) => {
} else { } else {
delete options.tip delete options.tip
} }
if (nodeType !== 'folder') {
perDeleteDatasource(data.id).then(res => {
if (res === true) {
const onClick = () => {
relationChartRef.value.getChartData({
queryType: 'datasource',
num: data.id,
label: data.name
})
}
ElMessageBox.confirm('', {
confirmButtonType: 'danger',
type: 'warning',
autofocus: false,
confirmButtonText: '确定',
showClose: false,
dangerouslyUseHTMLString: true,
message: h('div', null, [
h('p', { style: 'margin-bottom: 8px;' }, '确定删除该数据源吗?'),
h('p', { class: 'tip' }, '有数据集正在使用此数据源,删除后数据集不可用,确认删除?'),
h(
ElButton,
{ text: true, onClick: onClick, style: 'margin-left: -4px;' },
'查看血缘关系'
)
])
}).then(() => {
deleteById(data.id as number).then(() => {
if (data.id === nodeInfo.id) {
Object.assign(nodeInfo, cloneDeep(defaultInfo))
}
listDs()
ElMessage.success(t('dataset.delete_success'))
})
})
} else {
ElMessageBox.confirm( ElMessageBox.confirm(
nodeType === 'folder' ? '确定删除该文件夹吗' : t('datasource.this_data_source'), t('datasource.this_data_source'),
options as ElMessageBoxOptions options as ElMessageBoxOptions
).then(() => { ).then(() => {
deleteById(data.id as number).then(() => { deleteById(data.id as number).then(() => {
@ -770,6 +817,19 @@ const operation = (cmd: string, data: Tree, nodeType: string) => {
ElMessage.success(t('dataset.delete_success')) ElMessage.success(t('dataset.delete_success'))
}) })
}) })
}
})
} else {
ElMessageBox.confirm('确定删除该文件夹吗', options as ElMessageBoxOptions).then(() => {
deleteById(data.id as number).then(() => {
if (data.id === nodeInfo.id) {
Object.assign(nodeInfo, cloneDeep(defaultInfo))
}
listDs()
ElMessage.success(t('dataset.delete_success'))
})
})
}
} else { } else {
creatDsFolder.value.createInit(nodeType, data, cmd) creatDsFolder.value.createInit(nodeType, data, cmd)
} }
@ -1661,6 +1721,7 @@ const getMenuList = (val: boolean) => {
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
<relationChart ref="relationChartRef"></relationChart>
<XpackComponent <XpackComponent
jsname="L2NvbXBvbmVudC9wbHVnaW5zLWhhbmRsZXIvRHNDYXRlZ29yeUhhbmRsZXI=" jsname="L2NvbXBvbmVudC9wbHVnaW5zLWhhbmRsZXIvRHNDYXRlZ29yeUhhbmRsZXI="