feat(数据大屏): 数据大屏支持复用组件功能 #9545

This commit is contained in:
wangjiahao 2024-06-13 16:20:07 +08:00
parent 1c957aea16
commit d466332a87
5 changed files with 586 additions and 8 deletions

View File

@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1682495427436" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3689" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M672 288H352c-9.6 0-16 3.2-22.4 9.6-6.4 6.4-9.6 12.8-9.6 22.4v64c0 19.2 12.8 32 32 32s32-16 32-32v-32h96v320h-32c-16 0-32 12.8-32 32s12.8 32 32 32h128c16 0 32-12.8 32-32s-12.8-32-32-32h-32V352h96v32c0 19.2 12.8 32 32 32s32-16 32-32v-64c0-9.6-3.2-16-9.6-22.4-6.4-6.4-12.8-9.6-22.4-9.6z" fill="#F2F2F2" p-id="3690" data-spm-anchor-id="a313x.7781069.0.i4" class=""></path><path d="M896 128H128c-35.2 0-64 28.8-64 64v640c0 35.2 28.8 64 64 64h768c35.2 0 64-28.8 64-64V192c0-35.2-28.8-64-64-64z m0 672c0 16-12.8 32-32 32H160c-19.2 0-32-16-32-32V224c0-16 12.8-32 32-32h704c19.2 0 32 16 32 32v576z" fill="#F2F2F2" p-id="3691" data-spm-anchor-id="a313x.7781069.0.i1" class=""></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1682495427436" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3689" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M672 288H352c-9.6 0-16 3.2-22.4 9.6-6.4 6.4-9.6 12.8-9.6 22.4v64c0 19.2 12.8 32 32 32s32-16 32-32v-32h96v320h-32c-16 0-32 12.8-32 32s12.8 32 32 32h128c16 0 32-12.8 32-32s-12.8-32-32-32h-32V352h96v32c0 19.2 12.8 32 32 32s32-16 32-32v-64c0-9.6-3.2-16-9.6-22.4-6.4-6.4-12.8-9.6-22.4-9.6z" p-id="3690" data-spm-anchor-id="a313x.7781069.0.i4" class=""></path><path d="M896 128H128c-35.2 0-64 28.8-64 64v640c0 35.2 28.8 64 64 64h768c35.2 0 64-28.8 64-64V192c0-35.2-28.8-64-64-64z m0 672c0 16-12.8 32-32 32H160c-19.2 0-32-16-32-32V224c0-16 12.8-32 32-32h704c19.2 0 32 16 32 32v576z" p-id="3691" data-spm-anchor-id="a313x.7781069.0.i1" class=""></path></svg>

Before

Width:  |  Height:  |  Size: 1013 B

After

Width:  |  Height:  |  Size: 984 B

View File

@ -21,6 +21,8 @@ import { useCache } from '@/hooks/web/useCache'
import QueryGroup from '@/custom-component/component-group/QueryGroup.vue'
import ComponentButton from '@/components/visualization/ComponentButton.vue'
import OuterParamsSet from '@/components/visualization/OuterParamsSet.vue'
import MultiplexingCanvas from '@/views/common/MultiplexingCanvas.vue'
import ComponentButtonLabel from '@/components/visualization/ComponentButtonLabel.vue'
let nameEdit = ref(false)
let inputName = ref('')
let nameInput = ref(null)
@ -166,6 +168,7 @@ const getFullScale = () => {
}
const appStore = useAppStoreWithOut()
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
const multiplexingRef = ref(null)
eventBus.on('preview', preview)
eventBus.on('save', saveCanvasWithCheck)
@ -182,6 +185,10 @@ const openOuterParamsSet = () => {
}
outerParamsSetRef.value.optInit()
}
const multiplexingCanvasOpen = () => {
multiplexingRef.value.dialogInit('dataV')
}
</script>
<template>
@ -260,9 +267,21 @@ const openOuterParamsSet = () => {
<component-group is-label :base-width="215" icon-name="dv-more-com" title="更多">
<more-com-group></more-com-group>
</component-group>
<component-group is-label :base-width="410" icon-name="dv-material" title="素材">
<component-group
is-label
:base-width="410"
icon-name="dv-material"
:show-split-line="true"
title="素材"
>
<common-group></common-group>
</component-group>
<component-button-label
icon-name="icon_copy_filled"
title="复用"
is-label
@customClick="multiplexingCanvasOpen"
></component-button-label>
</div>
</template>
<div class="right-area">
@ -316,6 +335,7 @@ const openOuterParamsSet = () => {
ref="resourceGroupOpt"
/>
</div>
<multiplexing-canvas ref="multiplexingRef"></multiplexing-canvas>
<outer-params-set ref="outerParamsSetRef"> </outer-params-set>
<XpackComponent ref="openHandler" jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvT3BlbkhhbmRsZXI=" />
</template>

View File

@ -8,12 +8,19 @@
custom-class="custom-drawer"
>
<dashboard-preview-show
v-if="dialogShow"
v-if="dialogShow && curDvType === 'dashboard'"
ref="multiplexingPreviewShowRef"
class="multiplexing-area"
no-close
show-position="multiplexing"
></dashboard-preview-show>
<preview-show
v-if="dialogShow && curDvType === 'dataV'"
ref="multiplexingPreviewShowRef"
class="multiplexing-area"
no-close
show-position="multiplexing"
></preview-show>
<template #footer>
<el-row class="multiplexing-footer">
<el-col class="adapt-count">
@ -55,6 +62,7 @@ import { copyStoreWithOut } from '@/store/modules/data-visualization/copy'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
import PreviewShow from '@/views/data-visualization/PreviewShow.vue'
const dvMainStore = dvMainStoreWithOut()
const snapshotStore = snapshotStoreWithOut()
const dialogShow = ref(false)
@ -67,10 +75,12 @@ const state = reactive({
{ label: '保持源样式', value: false }
]
})
const curDvType = ref('dashboard')
const selectComponentCount = computed(() => Object.keys(curMultiplexingComponents.value).length)
const dialogInit = () => {
const dialogInit = (dvType = 'dashboard') => {
curDvType.value = dvType
dialogShow.value = true
dvMainStore.initCurMultiplexingComponents()
}

View File

@ -0,0 +1,527 @@
<script lang="ts" setup>
import { nextTick, onBeforeMount, reactive, ref, toRefs, watch } from 'vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
const dvMainStore = dvMainStoreWithOut()
const viewShow = ref(true)
const props = defineProps({
canvasStyleData: {
type: Object,
required: true
},
componentData: {
type: Object,
required: true
},
canvasViewInfo: {
type: Object,
required: true
},
dvInfo: {
type: Object,
required: true
}
})
const { canvasStyleData, componentData, canvasViewInfo, dvInfo } = toRefs(props)
const filterNodeMethod = (value, data) => {
return !value || data.multiplexActive
}
const nodeClick = data => {
const components = componentData.value.filter(ele => ele.id === data.id)
state.multiplexInfo = components[0]
viewShow.value = false
nextTick(() => {
viewShow.value = true
})
}
const multiplexInfoTree = ref(null)
const state = reactive({
sourceMultiplexInfo: {},
showSelected: false,
curMultiplexViewInfo: {},
curDatasetInfo: {},
initState: false,
viewId: null,
treeProp: {
id: 'id',
label: 'label',
name: 'name'
},
multiplexInfo: null
})
const curMultiplexTargetComponentsInfo = ref([])
const targetViewCheckedChange = (val, data) => {
nextTick(() => {
multiplexingCheck(val, data)
multiplexInfoTree.value.setCurrentKey(data.id)
nodeClick(data)
})
}
const getIconName = item => {
if (item.component === 'UserView') {
const viewInfo = canvasViewInfo.value[item.id]
return `${viewInfo.type}`
} else {
return item.icon
}
}
watch(
() => dvInfo.value,
() => {
if (dvInfo.value) {
init()
}
}
)
const init = () => {
dvMainStore.initCurMultiplexingComponents()
curMultiplexTargetComponentsInfo.value = []
componentData.value.forEach(item => {
curMultiplexTargetComponentsInfo.value.push({
id: item.id,
label: item.label,
icon: item.icon,
multiplexActive: false,
component: item.component
})
})
}
const multiplexingCheck = (val, data) => {
if (val) {
const components = componentData.value.filter(ele => ele.id === data.id)
// push
dvMainStore.addCurMultiplexingComponent({
component: components[0],
componentId: data.id
})
} else {
// remove
dvMainStore.removeCurMultiplexingComponentWithId(data)
}
}
onBeforeMount(() => {
init()
})
</script>
<template>
<el-row class="preview">
<el-col :span="6" style="height: 100%; overflow-y: auto">
<el-row class="tree-head">
<span class="head-text">选择组件</span>
<span class="head-filter"
>仅看已选 <el-switch size="small" v-model="state.showSelected" />
</span>
</el-row>
<el-tree
class="custom-tree"
menu
ref="multiplexInfoTree"
:empty-text="'暂无可用图表'"
:filter-node-method="filterNodeMethod"
:data="curMultiplexTargetComponentsInfo"
node-key="targetViewId"
highlight-current
@node-click="nodeClick"
>
<template #default="{ data }">
<span class="custom-tree-node">
<span>
<div @click.stop>
<span class="auth-span">
<el-checkbox
v-model="data.multiplexActive"
@change="targetViewCheckedChange($event, data)"
/>
</span>
</div>
</span>
<span>
<span class="tree-select-field">
<Icon
class-name="view-type-icon"
style="margin-right: 4px"
:name="getIconName(data)"
/>
{{ data.label }}
</span>
</span>
</span>
</template>
</el-tree>
</el-col>
<el-col :span="18" class="preview-show">
<div class="view-show-content">
<ComponentWrapper
v-if="viewShow && state.multiplexInfo && state.multiplexInfo.id"
class="wrapper-content"
:view-info="canvasViewInfo[state.multiplexInfo.id]"
:config="state.multiplexInfo"
:canvas-style-data="canvasStyleData"
:dv-info="dvInfo"
:canvas-view-info="canvasViewInfo"
/>
</div>
</el-col>
</el-row>
</template>
<style scoped lang="less">
.root-class {
margin: 15px 0px 5px;
justify-content: right;
}
.preview {
border-radius: 4px;
width: 100%;
height: 100% !important;
overflow: hidden;
background-size: 100% 100% !important;
}
.preview-show {
height: 100%;
padding-left: 12px;
background-size: 100% 100% !important;
}
.slot-class {
color: white;
}
.bottom {
margin-top: 10px;
justify-content: center;
}
.ellip {
margin-left: 10px;
margin-right: 10px;
overflow: hidden; /*超出部分隐藏*/
white-space: nowrap; /*不换行*/
text-overflow: ellipsis; /*超出部分文字以...显示*/
text-align: center;
background-color: #f7f8fa;
color: #3d4d66;
font-size: 12px;
line-height: 24px;
height: 24px;
border-radius: 3px;
}
.select-filed {
overflow: hidden; /*超出部分隐藏*/
white-space: nowrap; /*不换行*/
text-overflow: ellipsis; /*超出部分文字以...显示*/
color: #3d4d66;
font-size: 12px;
border-radius: 3px;
}
.custom-position {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
flex-flow: row nowrap;
color: #9ea6b2;
flex-direction: column;
span {
line-height: 22px;
color: #646a73;
}
}
.tree-style {
padding: 10px 15px;
height: 100%;
overflow-y: auto;
}
.custom-tree-node {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
}
.auth-span {
float: left;
width: 30px;
margin-left: -8px;
}
.tree-head {
height: 40px;
line-height: 40px;
font-size: 12px;
color: #3d4d66;
background-color: white;
.head-text {
margin-left: 16px;
font-weight: 500;
font-size: 14px;
color: #1f2329;
}
.head-filter {
flex: 1;
display: flex;
align-items: center;
justify-content: end;
margin-right: 16px;
font-weight: 400;
font-size: 12px;
color: #646a73;
.ed-switch {
margin-left: 8px;
}
}
}
.padding-lr {
padding: 0 4px;
}
.field-height {
height: calc(100% - 25px);
margin-top: 12px;
}
.drag-list {
height: calc(100% - 26px);
overflow: auto;
}
.item-dimension {
display: flex !important;
align-items: center;
padding: 2px 10px;
margin: 2px 2px 0 2px;
border: solid 1px #eee;
text-align: left;
color: #606266;
font-size: 14px;
background-color: white;
display: block;
word-break: break-all;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.item-dimension + .item-dimension {
margin-top: 2px;
}
.item-dimension:hover {
color: #1890ff;
background: #e8f4ff;
border-color: #a3d3ff;
cursor: pointer;
}
.item-quota {
padding: 2px 10px;
margin: 2px 2px 0 2px;
border: solid 1px #eee;
text-align: left;
color: #606266;
background-color: white;
display: block;
word-break: break-all;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.item-quota + .item-quota {
margin-top: 2px;
}
.item-quota:hover {
color: #67c23a;
background: #f0f9eb;
border-color: #b2d3a3;
cursor: pointer;
}
.blackTheme .item-quota:hover {
}
span {
font-size: 12px;
}
.set-name-area {
font-weight: 600;
margin-right: 20px;
}
:deep(.ed-row) {
width: 100%;
}
.dv-selector {
width: 100%;
}
.top-area {
float: left;
line-height: 33px;
display: flex;
flex-direction: row;
align-items: center;
}
.top-area-text {
font-weight: 400;
font-size: 14px;
color: #646a73;
margin-left: 24px;
}
.top-area-value {
font-weight: 400;
font-size: 14px;
color: #1f2329;
display: flex;
flex-direction: row;
align-items: center;
}
.view-type-icon {
color: var(--ed-color-primary);
width: 22px;
height: 16px;
}
.content-head {
height: 22px;
margin-top: 10px;
margin-left: 16px;
font-weight: 500;
font-size: 14px;
color: #1f2329;
line-height: 32px;
margin-right: 16px;
}
.link-icon-join {
font-size: 20px;
margin-top: 7px;
margin-left: 8px;
margin-right: 8px;
}
.inner-content {
width: 100%;
padding: 16px 16px 8px 16px;
font-size: 14px !important;
}
.outer-content {
height: 340px;
border-radius: 4px;
}
.padding-lr {
height: 500px;
border: 1px solid var(--deCardStrokeColor, #dee0e3);
border-radius: 4px;
padding: 12px;
box-sizing: border-box;
margin-left: 12px;
width: 214px;
overflow-y: hidden;
}
.mb8 {
margin-bottom: 8px;
display: inline-flex;
align-items: center;
i {
margin-left: 4.67px;
}
}
.field-height {
height: calc(50% - 41px);
margin-top: 4px;
overflow-y: auto;
}
.class-na {
margin-top: 8px;
text-align: center;
font-size: 14px;
color: var(--deTextDisable);
}
.outer-content-mirror {
border: 1px solid #bbbfc4;
border-radius: 4px;
height: 100%;
overflow: hidden;
}
.url-text {
width: 100%;
line-height: 14px;
margin-bottom: 8px;
}
.tree-select-field {
font-size: 14px;
display: flex;
align-items: center;
}
.custom-tree {
height: 100%;
overflow-y: auto;
}
.m-del-icon-btn {
color: #646a73;
margin-top: 4px;
margin-left: 4px;
&:hover {
background: rgba(31, 35, 41, 0.1) !important;
}
&:focus {
background: rgba(31, 35, 41, 0.1) !important;
}
&:active {
background: rgba(31, 35, 41, 0.2) !important;
}
}
.custom-option {
font-size: 14px;
display: flex;
align-items: center;
}
.enlarge-inner {
position: relative;
width: 100%;
height: 100%;
}
.view-show-content {
position: relative;
width: 100%;
height: 100%;
background: #ffffff;
.wrapper-content {
width: 100%;
height: 100%;
}
}
</style>

View File

@ -13,7 +13,8 @@ import { useRequestStoreWithOut } from '@/store/modules/request'
import { usePermissionStoreWithOut } from '@/store/modules/permission'
import { useMoveLine } from '@/hooks/web/useMoveLine'
import { Icon } from '@/components/icon-custom'
import { download2AppTemplate, downloadCanvas, downloadCanvas2 } from '@/utils/imgUtils'
import { download2AppTemplate, downloadCanvas2 } from '@/utils/imgUtils'
import MultiplexPreviewShow from '@/views/data-visualization/MultiplexPreviewShow.vue'
const dvMainStore = dvMainStoreWithOut()
const { dvInfo } = storeToRefs(dvMainStore)
@ -31,6 +32,11 @@ defineProps({
required: false,
type: String,
default: 'preview'
},
noClose: {
required: false,
type: Boolean,
default: false
}
})
@ -76,7 +82,7 @@ const loadCanvasData = (dvId, weight?) => {
dvMainStore.updateCurDvInfo(dvInfo)
dataInitState.value = true
nextTick(() => {
dvPreview.value.restore()
dvPreview.value?.restore()
})
}
)
@ -143,6 +149,7 @@ onBeforeMount(() => {
<template>
<div class="dv-preview">
<ArrowSide
v-if="!noClose"
:style="{ left: (sideTreeStatus ? width - 12 : 0) + 'px' }"
@change-side-tree-status="changeSideTreeStatus"
:isInside="!sideTreeStatus"
@ -156,6 +163,7 @@ onBeforeMount(() => {
:style="{ width: width + 'px' }"
>
<ArrowSide
v-if="!noClose"
:isInside="!sideTreeStatus"
:style="{ left: (sideTreeStatus ? width - 12 : 0) + 'px' }"
@change-side-tree-status="changeSideTreeStatus"
@ -179,12 +187,20 @@ onBeforeMount(() => {
</div>
<template v-if="dvInfo.name">
<preview-head
v-show="showPosition === 'preview'"
v-if="showPosition === 'preview'"
@reload="reload"
@download="download"
@downloadAsAppTemplate="downloadAsAppTemplate"
/>
<div ref="previewCanvasContainer" class="content">
<div v-if="showPosition === 'multiplexing'" class="content multiplexing-content">
<multiplex-preview-show
:component-data="state.canvasDataPreview"
:canvas-style-data="state.canvasStylePreview"
:canvas-view-info="state.canvasViewInfoPreview"
:dv-info="state.dvInfo"
></multiplex-preview-show>
</div>
<div v-if="showPosition === 'preview'" ref="previewCanvasContainer" class="content">
<div class="content-outer">
<div class="content-inner">
<de-preview
@ -300,4 +316,9 @@ onBeforeMount(() => {
border-right: 1px solid #d7d7d7;
border-bottom: 1px solid #d7d7d7;
}
.multiplexing-content {
padding: 12px;
background-color: rgb(245, 246, 247);
}
</style>