添加大屏模块

This commit is contained in:
limengnan 2025-04-01 17:26:00 +08:00
parent 80bb62a774
commit 349722797e
11 changed files with 2806 additions and 11 deletions

View File

@ -51,8 +51,8 @@ function paramsResetForm() {
emit('closeClick', false) emit('closeClick', false)
} }
function saveParamsObj(){ function saveParamsObj(){
debugger
if(isSwitch.value == false){ if(isSwitch.value == true){
return return
} }
isSwitch.value = true isSwitch.value = true

View File

@ -5,6 +5,7 @@ import { ElMessage,ElMessageBox } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { publicTree } from '@/utils/validate'; import { publicTree } from '@/utils/validate';
import { moduleList,moduleAdd,moduleUpdate,moduleDel } from '@/api/application/module' import { moduleList,moduleAdd,moduleUpdate,moduleDel } from '@/api/application/module'
const emit = defineEmits(['handleNodeClick'])
const { t } = useI18n() const { t } = useI18n()
const props = defineProps({ const props = defineProps({
projectInfo: { projectInfo: {
@ -12,6 +13,28 @@ const props = defineProps({
default: '' default: ''
} }
}) })
const typeList:any = ref([{
code:'01',
name:'登录页面'
},{
code:'02',
name:'主框架页面'
},{
code:'03',
name:'GIS大屏页面'
},{
code:'04',
name:'二级弹窗页面'
},{
code:'05',
name:'数据填报页面'
},{
code:'06',
name:'文档资源页面'
},{
code:'09',
name:'自定义页面'
}])
const popupType:any = ref(0) const popupType:any = ref(0)
const isSwitch =ref(false) const isSwitch =ref(false)
const treeloading = ref(false) // const treeloading = ref(false) //
@ -23,6 +46,7 @@ const isTreeDrag =ref(false)
const dataInfo:any =ref({ const dataInfo:any =ref({
pid: '', pid: '',
level: '', level: '',
type: '',
nodeType: '', nodeType: '',
name:'', name:'',
appId:'' appId:''
@ -35,6 +59,11 @@ watch(() => props.projectInfo, val => { // 初始化数据
getInit() getInit()
}) })
onMounted(()=>{ onMounted(()=>{
const canvas_style_data= {"width":1920,"height":1080,"refreshViewEnable":false,"refreshViewLoading":false,"refreshUnit":"minute","refreshTime":5,"scale":60,"scaleWidth":100,"scaleHeight":100,"backgroundColorSelect":false,"backgroundImageEnable":true,"backgroundType":"backgroundColor","background":"/static-resource/7127292608094670848.png","openCommonStyle":true,"opacity":1,"fontSize":14,"themeId":"10001","color":"#000000","backgroundColor":"rgba(245, 246, 247, 1)","dashboard":{"gap":"yes","gapSize":5,"resultMode":"all","resultCount":1000,"themeColor":"light","mobileSetting":{"customSetting":false,"imageUrl":null,"backgroundType":"image","color":"#000"}},"component":{"chartTitle":{"show":true,"fontSize":"18","hPosition":"left","vPosition":"top","isItalic":false,"isBolder":true,"remarkShow":false,"remark":"","fontFamily":"Microsoft YaHei","letterSpace":"0","fontShadow":false,"color":"#000000","remarkBackgroundColor":"#ffffff","modifyName":"color"},"chartColor":{"basicStyle":{"colorScheme":"default","colors":["#1E90FF","#90EE90","#00CED1","#E2BD84","#7A90E0","#3BA272","#2BE7FF","#0A8ADA","#FFD700"],"alpha":100,"gradient":true,"mapStyle":"normal","areaBaseColor":"#FFFFFF","areaBorderColor":"#303133","gaugeStyle":"default","tableBorderColor":"#E6E7E4","tableScrollBarColor":"#00000024"},"misc":{"mapLineGradient":false,"mapLineSourceColor":"#146C94","mapLineTargetColor":"#576CBC","nameFontColor":"#000000","valueFontColor":"#5470c6"},"tableHeader":{"tableHeaderBgColor":"#1E90FF","tableHeaderFontColor":"#000000"},"tableCell":{"tableItemBgColor":"#FFFFFF","tableFontColor":"#000000"},"modifyName":"gradient"},"chartCommonStyle":{"backgroundColorSelect":true,"backgroundImageEnable":false,"backgroundType":"innerImage","innerImage":"board/board_1.svg","outerImage":null,"innerPadding":12,"borderRadius":8,"backgroundColor":"rgba(255, 255, 255, 1)","innerImageColor":"rgba(16, 148, 229,1)"},"filterStyle":{"layout":"horizontal","titleLayout":"left","labelColor":"#1F2329","titleColor":"#1F2329","color":"#1f2329","borderColor":"#BBBFC4","text":"#1F2329","bgColor":"#FFFFFF"},"tabStyle":{"headPosition":"left","headFontColor":"#000000","headFontActiveColor":"#000000","headBorderColor":"#ffffff","headBorderActiveColor":"#ffffff"}}}
console.log(canvas_style_data)
projectInfo.value = props.projectInfo projectInfo.value = props.projectInfo
}) })
function getInit() { // function getInit() { //
@ -50,6 +79,7 @@ function addClic(name:any,level:any,pid:any){ // 新建目录、模块
level: level, level: level,
nodeType: "", nodeType: "",
name:'', name:'',
type:'',
} }
if(name == '目录'){ if(name == '目录'){
@ -68,6 +98,12 @@ function handleDatasetName(){ // 关键字搜索
} }
const handleNodeClick = (data: any) => { // const handleNodeClick = (data: any) => { //
if(data.nodeType == '01'){
}else{
data.id = '1097641013486424064'
emit('handleNodeClick', data)
}
} }
function moreClic(event:any ,data:any){ // function moreClic(event:any ,data:any){ //
@ -88,7 +124,8 @@ function moreClic(event:any ,data:any){ // 更多操作
isTreeDrag.value = true isTreeDrag.value = true
} }
function editClic(data:any){ function editClic(data:any){
ElMessage.success('页面跳转,暂无页面') window.open('/#/dvsCanvas?dvId=' +data.id, '_blank');
// window.open('/#/dvCanvas?dvId=' + "1097641013486424064", '_blank');
} }
function addTreeChildNode(event:any,level:any,pid:any){ // // function addTreeChildNode(event:any,level:any,pid:any){ // //
let element = document.getElementById("drag_main_area") let element = document.getElementById("drag_main_area")
@ -105,6 +142,7 @@ function addTreeChildNode(event:any,level:any,pid:any){ // // 添加子节点
level: level, level: level,
nodeType: "", nodeType: "",
name:'', name:'',
type:''
} }
isTreeDrag.value = true isTreeDrag.value = true
} }
@ -130,16 +168,21 @@ function saveData(){ // 保存
if(dataInfo.value.id !=null && dataInfo.value.id !=''){ if(dataInfo.value.id !=null && dataInfo.value.id !=''){
let data = { let data = {
id:dataInfo.value.id, id:dataInfo.value.id,
name:dataInfo.value.name name:dataInfo.value.name,
type:dataInfo.value.type,
} }
moduleUpdate(data).then(() => { moduleUpdate(data).then(() => {
ElMessage.success('添加成功') ElMessage.success('修改成功')
getInit() getInit()
isDialog.value = false isDialog.value = false
}).catch(() => { }).catch(() => {
isSwitch.value = false isSwitch.value = false
}) })
}else{ }else{
// const canvas_style_data= {"width":1920,"height":1080,"refreshViewEnable":false,"refreshViewLoading":false,"refreshUnit":"minute","refreshTime":5,"scale":60,"scaleWidth":100,"scaleHeight":100,"backgroundColorSelect":false,"backgroundImageEnable":true,"backgroundType":"backgroundColor","background":"/static-resource/7127292608094670848.png","openCommonStyle":true,"opacity":1,"fontSize":14,"themeId":"10001","color":"#000000","backgroundColor":"rgba(245, 246, 247, 1)","dashboard":{"gap":"yes","gapSize":5,"resultMode":"all","resultCount":1000,"themeColor":"light","mobileSetting":{"customSetting":false,"imageUrl":null,"backgroundType":"image","color":"#000"}},"component":{"chartTitle":{"show":true,"fontSize":"18","hPosition":"left","vPosition":"top","isItalic":false,"isBolder":true,"remarkShow":false,"remark":"","fontFamily":"Microsoft YaHei","letterSpace":"0","fontShadow":false,"color":"#000000","remarkBackgroundColor":"#ffffff","modifyName":"color"},"chartColor":{"basicStyle":{"colorScheme":"default","colors":["#1E90FF","#90EE90","#00CED1","#E2BD84","#7A90E0","#3BA272","#2BE7FF","#0A8ADA","#FFD700"],"alpha":100,"gradient":true,"mapStyle":"normal","areaBaseColor":"#FFFFFF","areaBorderColor":"#303133","gaugeStyle":"default","tableBorderColor":"#E6E7E4","tableScrollBarColor":"#00000024"},"misc":{"mapLineGradient":false,"mapLineSourceColor":"#146C94","mapLineTargetColor":"#576CBC","nameFontColor":"#000000","valueFontColor":"#5470c6"},"tableHeader":{"tableHeaderBgColor":"#1E90FF","tableHeaderFontColor":"#000000"},"tableCell":{"tableItemBgColor":"#FFFFFF","tableFontColor":"#000000"},"modifyName":"gradient"},"chartCommonStyle":{"backgroundColorSelect":true,"backgroundImageEnable":false,"backgroundType":"innerImage","innerImage":"board/board_1.svg","outerImage":null,"innerPadding":12,"borderRadius":8,"backgroundColor":"rgba(255, 255, 255, 1)","innerImageColor":"rgba(16, 148, 229,1)"},"filterStyle":{"layout":"horizontal","titleLayout":"left","labelColor":"#1F2329","titleColor":"#1F2329","color":"#1f2329","borderColor":"#BBBFC4","text":"#1F2329","bgColor":"#FFFFFF"},"tabStyle":{"headPosition":"left","headFontColor":"#000000","headFontActiveColor":"#000000","headBorderColor":"#ffffff","headBorderActiveColor":"#ffffff"}}}
// const canvasStyleData = JSON.stringify(canvas_style_data)
// const component_data = []
// const componentData = JSON.stringify(component_data)
moduleAdd(dataInfo.value).then(() => { moduleAdd(dataInfo.value).then(() => {
ElMessage.success('添加成功') ElMessage.success('添加成功')
getInit() getInit()
@ -179,6 +222,7 @@ function delTreeClic(){ // 删除
}) })
} }
</script> </script>
<template> <template>
<div class="project-left-box"> <div class="project-left-box">
@ -211,13 +255,13 @@ function delTreeClic(){ // 删除
<div style=" <div style="
width: 20px; width: 20px;
height: 24px;"> height: 24px;">
<img v-if="data.nodeType == '01'" src="@/assets/newimg/icon/add.png" alt="" style="width: 14px;height: 13px;" @click="addTreeChildNode($event, Number(data.level)+1,data.id)"> <img v-if="data.nodeType == '01'" src="@/assets/newimg/icon/add.png" alt="" style="width: 14px;height: 13px;" @click.stop="addTreeChildNode($event, Number(data.level)+1,data.id)">
<img v-if="data.nodeType == '02'" src="@/assets/newimg/icon/bianji.png" alt="" style="width: 14px;height: 13px;" @click="editClic(data.id)"> <img v-if="data.nodeType == '02'" src="@/assets/newimg/icon/bianji.png" alt="" style="width: 14px;height: 13px;" @click.stop="editClic(data)">
</div> </div>
<div style=" <div style="
width: 20px; width: 20px;
height: 24px;" @click="moreClic($event,data)"> height: 24px;" @click.stop="moreClic($event,data)">
<img src="@/assets/newimg/icon/dian.png" alt="" style="width: 4px;height: 14px;margin-left: 15px;"> <img src="@/assets/newimg/icon/dian.png" alt="" style="width: 4px;height: 14px;margin-left: 15px;">
</div> </div>
@ -236,6 +280,7 @@ function delTreeClic(){ // 删除
<el-form <el-form
ref="paramsObjRef" ref="paramsObjRef"
:model="dataInfo" :model="dataInfo"
label-width="100px"
> >
<el-form-item :label="dataInfo.nodeType=='01'?'目录名称:':'模块名称:'" prop="name"> <el-form-item :label="dataInfo.nodeType=='01'?'目录名称:':'模块名称:'" prop="name">
<el-input <el-input
@ -243,6 +288,24 @@ function delTreeClic(){ // 删除
v-model="dataInfo.name" v-model="dataInfo.name"
/> />
</el-form-item> </el-form-item>
<el-form-item :label="dataInfo.nodeType=='01'?'目录类型:':'模块类型:'" prop="type">
<el-select
v-model="dataInfo.type"
placeholder=""
style="width: 100%"
:disabled="dataInfo.nodeType=='02'"
>
<el-option
v-for="item in typeList"
:key="item.code"
:label="item.name"
:value="item.code"
/>
</el-select>
</el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<el-button @click="paramsCloseForm">{{ t('dataset.cancel') }} </el-button> <el-button @click="paramsCloseForm">{{ t('dataset.cancel') }} </el-button>
@ -384,6 +447,15 @@ function delTreeClic(){ // 删除
:deep(.ed-input__wrapper:hover){ :deep(.ed-input__wrapper:hover){
box-shadow:0 0 0 1px rgba(80, 80, 80, 1) ; box-shadow:0 0 0 1px rgba(80, 80, 80, 1) ;
} }
:deep(.ed-input.is-disabled .ed-input__wrapper){
background-color: rgba(37, 38, 38, 0);
box-shadow:0 0 0 1px rgba(80, 80, 80, 1) ;
}
:deep(.ed-input.is-disabled .ed-input__inner){
background-color: rgba(37, 38, 38, 0);
}
</style> </style>
<style> <style>

View File

@ -1,10 +1,57 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, onUnmounted } from 'vue' import { ref, onMounted, onUnmounted,computed, reactive,nextTick,onBeforeMount } from 'vue'
import Header from './header.vue' import Header from './header.vue'
import Leftmenu from './Leftmenu/index.vue' import Leftmenu from './Leftmenu/index.vue'
import Rightmenu from './Rightmenu/index.vue' import Rightmenu from './Rightmenu/index.vue'
import { findApplicationById } from "@/api/application/application"; import { findApplicationById } from "@/api/application/application";
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import DvPreview from '@/viewsnew/data-visualization/DvPreview.vue'
import { initCanvasData, initCanvasDataPrepare, onInitReady } from '@/utils/canvasUtils'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
const dvMainStore = dvMainStoreWithOut()
const state = reactive({
canvasDataPreview: null,
canvasStylePreview: null,
canvasViewInfoPreview: null,
dvInfo: null,
curPreviewGap: 0
})
const dataInitState = ref(true)
const downloadStatus = ref(false)
const dvPreviewRef = ref(null)
const showPosition = ref("preview")
// const noClose = ref(false)
// const props = defineProps({
// showPosition: {
// required: false,
// type: String,
// default: 'preview'
// },
// noClose: {
// required: false,
// type: Boolean,
// default: false
// }
// })
const dataVKeepSize = computed(() => {
return state.canvasStylePreview?.screenAdaptor === 'keep'
})
const {
fullscreenFlag,
componentData,
curComponent,
isClickComponent,
canvasStyleData,
canvasViewInfo,
editMode,
dvInfo,
canvasState,
batchOptStatus
} = storeToRefs(dvMainStore)
const route = useRoute() const route = useRoute()
const projectInfo:any = ref({}) const projectInfo:any = ref({})
const applicationId:any = ref('') const applicationId:any = ref('')
@ -17,12 +64,64 @@ function getInit() {
projectInfo.value = res.data.data projectInfo.value = res.data.data
}) })
} }
const loadCanvasData = (dvId, weight?, ext?) => {
// const initMethod = props.showPosition === 'multiplexing' ? initCanvasDataPrepare : initCanvasData
const initMethod = initCanvasData
dataInitState.value = false
initMethod(
dvId,
'dataV',
function ({
canvasDataResult,
canvasStyleResult,
dvInfo,
canvasViewInfoPreview,
curPreviewGap
}) {
dvInfo['weight'] = weight
dvInfo['ext'] = ext || 0
state.canvasDataPreview = canvasDataResult
state.canvasStylePreview = canvasStyleResult
state.canvasViewInfoPreview = canvasViewInfoPreview
state.dvInfo = dvInfo
state.curPreviewGap = curPreviewGap
dataInitState.value = true
dvMainStore.updateCurDvInfo(dvInfo)
nextTick(() => {
dvPreviewRef.value?.restore()
})
nextTick(() => {
onInitReady({ resourceId: dvId })
})
}
)
}
function handleNodeClick(e){
loadCanvasData(e.id,9,0)
}
onBeforeMount(() => {
dvMainStore.canvasDataInit()
})
</script> </script>
<template> <template>
<div class="project-box"> <div class="project-box">
<Header :projectInfo="projectInfo"/> <Header :projectInfo="projectInfo"/>
<div class="project-content" v-if="applicationId != ''"> <div class="project-content" v-if="applicationId != ''">
<Leftmenu v-if="applicationId != ''" :projectInfo="projectInfo"/> <Leftmenu v-if="applicationId != ''" :projectInfo="projectInfo" @handleNodeClick="handleNodeClick"/>
<dv-preview
ref="dvPreviewRef"
v-if="state.canvasStylePreview && dataInitState"
:show-position="showPosition"
:canvas-data-preview="state.canvasDataPreview"
:canvas-style-preview="state.canvasStylePreview"
:canvas-view-info-preview="state.canvasViewInfoPreview"
:dv-info="state.dvInfo"
:cur-preview-gap="state.curPreviewGap"
:download-status="downloadStatus"
></dv-preview>
</div> </div>
</div> </div>
</template> </template>

View File

@ -0,0 +1,111 @@
<script setup lang="ts">
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { computed, ref } from 'vue'
import DePreview from '@/components/data-visualization/canvas/DePreview.vue'
import { storeToRefs } from 'pinia'
const dvMainStore = dvMainStoreWithOut()
const { fullscreenFlag } = storeToRefs(dvMainStore)
const dePreviewRef = ref(null)
const dataInitState = ref(true)
const props = defineProps({
canvasStylePreview: {
required: true,
type: Object
},
canvasDataPreview: {
required: true,
type: Object
},
canvasViewInfoPreview: {
required: true,
type: Object
},
dvInfo: {
required: true,
type: Object
},
curPreviewGap: {
required: false,
type: Number,
default: 0
},
showPosition: {
required: false,
type: String,
default: 'preview'
},
downloadStatus: {
required: false,
type: Boolean,
default: false
}
})
console.log(props)
const restore = () => {
dePreviewRef.value.restore()
}
const contentInnerClass = computed(() => {
// widthFirst=() heightFirst= full= keepSize=
if (props.canvasStylePreview.screenAdaptor === 'heightFirst') {
return 'preview-content-inner-height-first'
} else if (props.canvasStylePreview.screenAdaptor === 'full') {
return 'preview-content-inner-full'
} else if (props.canvasStylePreview.screenAdaptor === 'keep') {
return 'preview-content-inner-size-keep'
} else {
return 'preview-content-inner-width-first'
}
})
const outerStyle = computed(() => {
return {
flexDirection: props.canvasStylePreview.screenAdaptor === 'heightFirst' ? 'row' : 'column'
}
})
defineExpose({
restore
})
</script>
<template>
<div
id="de-preview-content"
:class="{ 'de-screen-full': fullscreenFlag }"
:style="outerStyle"
class="content-outer"
>
<div class="content-inner" :class="contentInnerClass">
<de-preview
ref="dePreviewRef"
v-if="canvasStylePreview && dataInitState"
:component-data="canvasDataPreview"
:canvas-style-data="canvasStylePreview"
:canvas-view-info="canvasViewInfoPreview"
:dv-info="dvInfo"
:cur-gap="curPreviewGap"
:show-position="showPosition"
:download-status="downloadStatus"
></de-preview>
</div>
</div>
</template>
<style lang="less">
.content-outer {
width: 100%;
// height: calc(100vh - 112px);
height: calc(100vh - 60px);
background: #f5f6f7;
display: flex;
overflow-y: auto;
align-items: center;
flex-direction: column;
justify-content: center; /* 上下居中 */
::-webkit-scrollbar {
display: none;
}
}
</style>

View File

@ -0,0 +1,6 @@
<template>
<link-index :error-tips="true" />
</template>
<script lang="ts" setup>
import LinkIndex from '@/views/share/link/index.vue'
</script>

View File

@ -0,0 +1,586 @@
<script lang="ts" setup>
import bar from '@/assets/svg/bar.svg'
import dbMoreWeb from '@/assets/svg/db-more-web.svg'
import dvMoreTimeClock from '@/assets/svg/dv-more-time-clock.svg'
import dvPictureReal from '@/assets/svg/dv-picture-real.svg'
import dvTab from '@/assets/svg/dv-tab.svg'
import iconStream from '@/assets/svg/icon-stream.svg'
import iconVideo from '@/assets/svg/icon-video.svg'
import icon_graphical from '@/assets/svg/icon_graphical.svg'
import icon_search from '@/assets/svg/icon_search.svg'
import other_material_board from '@/assets/svg/other_material_board.svg'
import other_material_icon from '@/assets/svg/other_material_icon.svg'
import scrollText from '@/assets/svg/scroll-text.svg'
import { computed, nextTick, onBeforeMount, reactive, ref, toRefs, watch } from 'vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { getCanvasStyle } from '@/utils/style'
import EmptyBackground from '../../components/empty-background/src/EmptyBackground.vue'
import { iconChartMap } from '@/components/icon-group/chart-list'
import { useI18n } from '@/hooks/web/useI18n'
const dvMainStore = dvMainStoreWithOut()
const viewShow = ref(true)
const { t } = useI18n()
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 canvasStyle = computed(() => getCanvasStyle(canvasStyleData.value))
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 iconMap = {
bar: bar,
'db-more-web': dbMoreWeb,
'dv-more-time-clock': dvMoreTimeClock,
'dv-picture-real': dvPictureReal,
'dv-tab': dvTab,
'icon-stream': iconStream,
'icon-video': iconVideo,
icon_graphical: icon_graphical,
icon_search: icon_search,
other_material_board: other_material_board,
other_material_icon: other_material_icon,
'scroll-text': scrollText
}
const getIconName = item => {
if (item.component === 'UserView') {
const viewInfo = canvasViewInfo.value[item.id]
return iconChartMap[`${viewInfo.type}`]
} else {
return iconMap[item.icon]
}
}
watch(
() => dvInfo.value,
() => {
if (dvInfo.value) {
init()
}
}
)
watch(
() => state.showSelected,
newValue => {
multiplexInfoTree.value?.filter(newValue)
}
)
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.id)
}
}
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">{{ t('visualization.to_select_view') }}</span>
<span class="head-filter"
>{{ t('visualization.show_selected_only') }}
<el-switch size="small" v-model="state.showSelected" />
</span>
</el-row>
<el-tree
class="custom-tree-multiplex"
menu
ref="multiplexInfoTree"
:empty-text="t('visualization.no_available_component')"
: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"
><component
class="svg-icon view-type-icon"
style="margin-right: 4px"
:is="getIconName(data)"
></component
></Icon>
{{ data.label }}
</span>
</span>
</span>
</template>
</el-tree>
</el-col>
<el-col :span="18" class="preview-show">
<div class="view-show-content-outer">
<div class="view-show-content">
<ComponentWrapper
v-if="viewShow && state.multiplexInfo && state.multiplexInfo.id"
class="wrapper-content"
:style="canvasStyle"
:view-info="canvasViewInfo[state.multiplexInfo.id]"
:config="state.multiplexInfo"
:canvas-style-data="canvasStyleData"
:dv-info="dvInfo"
:show-position="'canvas-multiplexing'"
:canvas-view-info="canvasViewInfo"
/>
<empty-background
v-else
:description="t('visualization.no_selected_component')"
img-type="select"
/>
</div>
</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-multiplex {
height: calc(100% - 40px);
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-outer {
width: 100%;
height: 100%;
padding: 12px;
background: #ffffff;
}
.view-show-content {
position: relative;
width: 100%;
height: 100%;
.wrapper-content {
width: 100%;
height: 100%;
background-size: 100% 100% !important;
}
}
</style>

View File

@ -0,0 +1,249 @@
<script setup lang="ts">
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
import DePreview from '@/components/data-visualization/canvas/DePreview.vue'
import router from '@/router'
import { useEmitt } from '@/hooks/web/useEmitt'
import { initCanvasData, onInitReady } from '@/utils/canvasUtils'
import { queryTargetVisualizationJumpInfo } from '@/api/visualization/linkJump'
import { Base64 } from 'js-base64'
import { getOuterParamsInfo } from '@/api/visualization/outerParams'
import { ElMessage } from 'element-plus-secondary'
import { useEmbedded } from '@/store/modules/embedded'
import { useI18n } from '@/hooks/web/useI18n'
import { XpackComponent } from '@/components/plugin'
import { propTypes } from '@/utils/propTypes'
import { downloadCanvas2 } from '@/utils/imgUtils'
import { isLink, setTitle } from '@/utils/utils'
import EmptyBackground from '../../components/empty-background/src/EmptyBackground.vue'
import { useRoute } from 'vue-router'
const routeWatch = useRoute()
const dvMainStore = dvMainStoreWithOut()
const { t } = useI18n()
const embeddedStore = useEmbedded()
const previewCanvasContainer = ref(null)
const downloadStatus = ref(false)
const state = reactive({
canvasDataPreview: null,
canvasStylePreview: null,
canvasViewInfoPreview: null,
dvInfo: null,
curPreviewGap: 0,
initState: true,
showPosition: null
})
const props = defineProps({
publicLinkStatus: {
type: Boolean,
required: false,
default: false
},
isSelector: {
type: Boolean,
default: false
},
ticketArgs: propTypes.string.def(null)
})
const loadCanvasDataAsync = async (dvId, dvType, ignoreParams = false) => {
const jumpInfoParam = embeddedStore.jumpInfoParam || router.currentRoute.value.query.jumpInfoParam
let jumpParam
//
if (jumpInfoParam) {
jumpParam = JSON.parse(Base64.decode(decodeURIComponent(jumpInfoParam)))
const jumpRequestParam = {
sourceDvId: jumpParam.sourceDvId,
sourceViewId: jumpParam.sourceViewId,
sourceFieldId: null,
targetDvId: dvId
}
try {
//
await queryTargetVisualizationJumpInfo(jumpRequestParam).then(rsp => {
dvMainStore.setNowTargetPanelJumpInfo(rsp.data)
})
} catch (e) {
console.error(e)
}
}
let argsObject = null
try {
argsObject = JSON.parse(props.ticketArgs)
} catch (error) {
console.error(error)
}
const hasTicketArgs = argsObject && Object.keys(argsObject)
//
let attachParam
await getOuterParamsInfo(dvId).then(rsp => {
dvMainStore.setNowPanelOuterParamsInfoV2(rsp.data, dvId)
})
// iframe iframe
const attachParamsEncode = router.currentRoute.value.query.attachParams
if (attachParamsEncode || hasTicketArgs) {
try {
if (!!attachParamsEncode) {
attachParam = JSON.parse(Base64.decode(decodeURIComponent(attachParamsEncode)))
}
if (hasTicketArgs) {
attachParam = Object.assign({}, attachParam, argsObject)
}
} catch (e) {
console.error(e)
ElMessage.error(t('visualization.outer_param_decode_error'))
}
}
const initBrowserTimer = () => {
if (state.canvasStylePreview.refreshBrowserEnable && isLink()) {
const gap = state.canvasStylePreview.refreshBrowserUnit === 'minute' ? 60 : 1
const browserRefreshTime = state.canvasStylePreview.refreshBrowserTime * gap * 1000
setTimeout(() => {
window.location.reload()
}, browserRefreshTime)
}
}
await initCanvasData(
dvId,
dvType,
function ({
canvasDataResult,
canvasStyleResult,
dvInfo,
canvasViewInfoPreview,
curPreviewGap
}) {
state.canvasDataPreview = canvasDataResult
state.canvasStylePreview = canvasStyleResult
state.canvasViewInfoPreview = canvasViewInfoPreview
state.dvInfo = dvInfo
state.curPreviewGap = curPreviewGap
if (jumpParam) {
dvMainStore.addViewTrackFilter(jumpParam)
}
if (!ignoreParams) {
state.initState = false
dvMainStore.addOuterParamsFilter(attachParam)
state.initState = true
}
if (props.publicLinkStatus) {
// title
document.title = dvInfo.name
setTitle(dvInfo.name)
}
initBrowserTimer()
nextTick(() => {
onInitReady({ resourceId: dvId })
})
}
)
}
const downloadH2 = type => {
downloadStatus.value = true
nextTick(() => {
const vueDom = previewCanvasContainer.value.querySelector('.canvas-container')
downloadCanvas2(type, vueDom, state.dvInfo.name, () => {
downloadStatus.value = false
})
})
}
//
//
watch(
() => ({ path: routeWatch.path, params: routeWatch.params }),
() => {
location.reload() //
},
{ deep: true }
)
let p = null
const XpackLoaded = () => p(true)
onMounted(async () => {
useEmitt({
name: 'canvasDownload',
callback: function (type = 'img') {
downloadH2(type)
}
})
await new Promise(r => (p = r))
const dvId = embeddedStore.dvId || router.currentRoute.value.query.dvId
//
const ignoreParams = router.currentRoute.value.query.ignoreParams === 'true'
const isPopWindow = router.currentRoute.value.query.popWindow === 'true'
const isFrameFlag = window.self !== window.top
dvMainStore.setIframeFlag(isFrameFlag)
dvMainStore.setIsPopWindow(isPopWindow)
const { dvType, callBackFlag, taskId, showWatermark } = router.currentRoute.value.query
if (!!taskId) {
dvMainStore.setCanvasAttachInfo({ taskId, showWatermark })
}
if (dvId) {
await loadCanvasDataAsync(dvId, dvType, ignoreParams)
return
}
dvMainStore.setEmbeddedCallBack(callBackFlag || 'no')
dvMainStore.setPublicLinkStatus(props.publicLinkStatus)
})
const dataVKeepSize = computed(() => {
return state.canvasStylePreview?.screenAdaptor === 'keep'
})
defineExpose({
loadCanvasDataAsync
})
</script>
<template>
<div
class="content"
v-loading="!state.initState"
:class="{ 'canvas_keep-size': dataVKeepSize }"
ref="previewCanvasContainer"
>
<de-preview
ref="dvPreview"
v-if="state.canvasStylePreview && state.initState"
:component-data="state.canvasDataPreview"
:canvas-style-data="state.canvasStylePreview"
:canvas-view-info="state.canvasViewInfoPreview"
:dv-info="state.dvInfo"
:cur-gap="state.curPreviewGap"
:is-selector="props.isSelector"
:download-status="downloadStatus"
:show-pop-bar="true"
></de-preview>
<empty-background
v-if="!state.initState"
:description="t('visualization.no_params_tips')"
img-type="noneWhite"
/>
</div>
<XpackComponent
jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvTmV3V2luZG93SGFuZGxlcg=="
@loaded="XpackLoaded"
@load-fail="XpackLoaded"
/>
</template>
<style lang="less" scoped>
::-webkit-scrollbar {
display: none;
}
.content {
background-color: #ffffff;
width: 100%;
height: 100vh;
align-items: center;
overflow-x: hidden;
overflow-y: auto;
}
</style>

View File

@ -0,0 +1,202 @@
<script setup lang="ts">
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { onMounted, reactive } from 'vue'
import DePreview from '@/components/data-visualization/canvas/DePreview.vue'
import router from '@/router/mobile'
import { initCanvasDataMobile, initCanvasData } from '@/utils/canvasUtils'
import { queryTargetVisualizationJumpInfo } from '@/api/visualization/linkJump'
import { Base64 } from 'js-base64'
import { getOuterParamsInfo } from '@/api/visualization/outerParams'
import { ElMessage } from 'element-plus-secondary'
import { useEmbedded } from '@/store/modules/embedded'
import { useI18n } from '@/hooks/web/useI18n'
import { XpackComponent } from '@/components/plugin'
import { propTypes } from '@/utils/propTypes'
import { setTitle } from '@/utils/utils'
import EmptyBackground from '../../components/empty-background/src/EmptyBackground.vue'
const dvMainStore = dvMainStoreWithOut()
const { t } = useI18n()
const embeddedStore = useEmbedded()
const state = reactive({
canvasDataPreview: null,
canvasStylePreview: null,
canvasViewInfoPreview: null,
dvInfo: null,
curPreviewGap: 0,
initState: true
})
const props = defineProps({
publicLinkStatus: {
type: Boolean,
required: false,
default: false
},
isSelector: {
type: Boolean,
default: false
},
ticketArgs: propTypes.string.def(null)
})
const loadCanvasDataAsync = async (dvId, dvType) => {
const jumpInfoParam = embeddedStore.jumpInfoParam || router.currentRoute.value.query.jumpInfoParam
let jumpParam
//
if (jumpInfoParam) {
jumpParam = JSON.parse(Base64.decode(decodeURIComponent(jumpInfoParam as string)))
const jumpRequestParam = {
sourceDvId: jumpParam.sourceDvId,
sourceViewId: jumpParam.sourceViewId,
sourceFieldId: null,
targetDvId: dvId
}
try {
//
await queryTargetVisualizationJumpInfo(jumpRequestParam).then(rsp => {
dvMainStore.setNowTargetPanelJumpInfo(rsp.data)
})
} catch (e) {
console.error(e)
}
}
let argsObject = null
try {
argsObject = JSON.parse(props.ticketArgs)
} catch (error) {
console.error(error)
}
const hasTicketArgs = argsObject && Object.keys(argsObject)
//
let attachParam
await getOuterParamsInfo(dvId).then(rsp => {
dvMainStore.setNowPanelOuterParamsInfoV2(rsp.data, dvId)
})
// iframe iframe
const attachParamsEncode = router.currentRoute.value.query.attachParams
if (attachParamsEncode || hasTicketArgs) {
try {
if (!!attachParamsEncode) {
attachParam = JSON.parse(Base64.decode(decodeURIComponent(attachParamsEncode as string)))
}
if (hasTicketArgs) {
attachParam = Object.assign({}, attachParam, argsObject)
}
} catch (e) {
console.error(e)
ElMessage.error(t('visualization.outer_param_decode_error'))
return
}
}
const req = dvType === 'dashboard' ? initCanvasDataMobile : initCanvasData
req(
dvId,
dvType,
function ({
canvasDataResult,
canvasStyleResult,
dvInfo,
canvasViewInfoPreview,
curPreviewGap
}) {
if (!dvInfo.mobileLayout && dvType === 'dashboard') {
router.push('/DashboardEmpty')
return
}
state.canvasDataPreview = canvasDataResult
state.canvasStylePreview = canvasStyleResult
state.canvasViewInfoPreview = canvasViewInfoPreview
state.dvInfo = dvInfo
state.curPreviewGap = curPreviewGap
if (jumpParam) {
dvMainStore.addViewTrackFilter(jumpParam)
}
state.initState = false
dvMainStore.addOuterParamsFilter(attachParam)
state.initState = true
if (props.publicLinkStatus) {
// title
document.title = dvInfo.name
setTitle(dvInfo.name)
}
initBrowserTimer()
}
)
}
let p = null
const XpackLoaded = () => p(true)
onMounted(async () => {
await new Promise(r => (p = r))
dvMainStore.setMobileInPc(true)
dvMainStore.setInMobile(true)
const dvId = embeddedStore.dvId || router.currentRoute.value.query.dvId
const { dvType, callBackFlag } = router.currentRoute.value.query
if (dvId) {
loadCanvasDataAsync(dvId, dvType)
return
}
dvMainStore.setEmbeddedCallBack(callBackFlag || 'no')
dvMainStore.setPublicLinkStatus(props.publicLinkStatus)
})
const initBrowserTimer = () => {
if (state.canvasStylePreview.refreshBrowserEnable) {
const gap = state.canvasStylePreview.refreshBrowserUnit === 'minute' ? 60 : 1
const browserRefreshTime = state.canvasStylePreview.refreshBrowserTime * gap * 1000
setTimeout(() => {
window.location.reload()
}, browserRefreshTime)
}
}
defineExpose({
loadCanvasDataAsync
})
</script>
<template>
<div class="content" v-if="state.initState">
<de-preview
ref="dvPreview"
v-if="state.canvasStylePreview"
:component-data="state.canvasDataPreview"
:canvas-style-data="state.canvasStylePreview"
:canvas-view-info="state.canvasViewInfoPreview"
:dv-info="state.dvInfo"
:cur-gap="state.curPreviewGap"
:is-selector="props.isSelector"
></de-preview>
</div>
<empty-background
v-if="!state.initState"
:description="t('visualization.no_params_tips')"
img-type="noneWhite"
/>
<XpackComponent
jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvTmV3V2luZG93SGFuZGxlcg=="
@loaded="XpackLoaded"
@load-fail="XpackLoaded"
/>
</template>
<style lang="less" scoped>
.content {
background-color: #ffffff;
width: 100vw;
height: 100vh;
align-items: center;
overflow-x: hidden;
overflow-y: auto;
::-webkit-scrollbar {
width: 0px !important;
height: 0px !important;
}
}
</style>

View File

@ -0,0 +1,299 @@
<script setup lang="ts">
import icon_collection_outlined from '@/assets/svg/icon_collection_outlined.svg'
import visualStar from '@/assets/svg/visual-star.svg'
import dvInfoSvg from '@/assets/svg/dv-info.svg'
import dvHeadMore from '@/assets/svg/dv-head-more.svg'
import icon_pc_fullscreen from '@/assets/svg/icon_pc_fullscreen.svg'
import icon_pc_outlined from '@/assets/svg/icon_pc_outlined.svg'
import icon_download_outlined from '@/assets/svg/icon_download_outlined.svg'
import icon_replace_outlined from '@/assets/svg/icon_replace_outlined.svg'
import icon_edit_outlined from '@/assets/svg/icon_edit_outlined.svg'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import { useI18n } from '@/hooks/web/useI18n'
import { useAppStoreWithOut } from '@/store/modules/app'
import DvDetailInfo from '@/views/common/DvDetailInfo.vue'
import { useEmbedded } from '@/store/modules/embedded'
import { storeApi, storeStatusApi } from '@/api/visualization/dataVisualization'
import { ref, watch, computed } from 'vue'
import ShareVisualHead from '@/views/share/share/ShareVisualHead.vue'
import { XpackComponent } from '@/components/plugin'
import { useEmitt } from '@/hooks/web/useEmitt'
import { useShareStoreWithOut } from '@/store/modules/share'
import { exportPermission } from '@/utils/utils'
import { useCache } from '@/hooks/web/useCache'
import { isDesktop } from '@/utils/ModelUtil'
const shareStore = useShareStoreWithOut()
const { wsCache } = useCache('localStorage')
const dvMainStore = dvMainStoreWithOut()
const appStore = useAppStoreWithOut()
const { dvInfo } = storeToRefs(dvMainStore)
const emit = defineEmits(['reload', 'download', 'downloadAsAppTemplate'])
const { t } = useI18n()
const embeddedStore = useEmbedded()
const openType = wsCache.get('open-backend') === '1' ? '_self' : '_blank'
const favorited = ref(false)
const preview = () => {
const baseUrl = isDataEaseBi.value ? embeddedStore.baseUrl : ''
const url = baseUrl + '#/preview?dvId=' + dvInfo.value.id + '&ignoreParams=true'
const newWindow = window.open(url, '_blank')
initOpenHandler(newWindow)
}
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
const isIframe = computed(() => appStore.getIsIframe)
const shareDisable = computed(() => shareStore.getShareDisable || isDesktop())
const exportPermissions = computed(() =>
exportPermission(dvInfo.value['weight'], dvInfo.value['ext'])
)
const reload = () => {
emit('reload', dvInfo.value.id)
}
const download = type => {
emit('download', type)
}
const downloadAsAppTemplate = downloadType => {
emit('downloadAsAppTemplate', downloadType)
}
const dvEdit = () => {
if (isDataEaseBi.value || isIframe.value) {
embeddedStore.clearState()
if (dvInfo.value.type === 'dataV') {
embeddedStore.setDvId(dvInfo.value.id)
} else {
embeddedStore.setResourceId(dvInfo.value.id)
}
useEmitt().emitter.emit(
'changeCurrentComponent',
dvInfo.value.type === 'dataV' ? 'VisualizationEditor' : 'DashboardEditor'
)
return
}
const baseUrl = dvInfo.value.type === 'dataV' ? '#/dvCanvas?dvId=' : '#/dashboard?resourceId='
const newWindow = window.open(baseUrl + dvInfo.value.id, openType)
initOpenHandler(newWindow)
}
const executeStore = () => {
const param = {
id: dvInfo.value.id,
type: dvInfo.value.type === 'dashboard' ? 'panel' : 'screen'
}
storeApi(param).then(() => {
storeQuery()
})
}
const storeQuery = () => {
if (!dvInfo?.value?.id) return
storeStatusApi(dvInfo.value.id).then(res => {
favorited.value = res.data
})
}
storeQuery()
watch(
() => dvInfo.value.id,
() => {
storeQuery()
}
)
const openHandler = ref(null)
const initOpenHandler = newWindow => {
if (openHandler?.value) {
const pm = {
methodName: 'initOpenHandler',
args: newWindow
}
openHandler.value.invokeMethod(pm)
}
}
</script>
<template>
<div class="preview-head flex-align-center">
<div :title="dvInfo.name" class="canvas-name ellipsis">{{ dvInfo.name }}</div>
<el-tooltip
effect="dark"
:content="favorited ? t('visualization.cancel_store') : t('visualization.store')"
placement="top"
>
<el-icon
class="custom-icon hover-icon"
@click="executeStore"
:style="{ color: favorited ? '#FFC60A' : '#646A73' }"
>
<icon
><component
class="svg-icon"
:is="favorited ? visualStar : icon_collection_outlined"
></component
></icon>
</el-icon>
</el-tooltip>
<el-divider style="margin: 0 16px 0 7px" direction="vertical" />
<div class="create-area flex-align-center">
<span style="line-height: 22px"
>{{ t('visualization.creator') }}:{{ dvInfo.creatorName }}</span
>
<el-popover show-arrow :offset="8" placement="bottom" width="400" trigger="hover">
<template #reference>
<el-icon class="info-tips"
><Icon name="dv-info"><dvInfoSvg class="svg-icon" /></Icon
></el-icon>
</template>
<dv-detail-info></dv-detail-info>
</el-popover>
</div>
<div class="canvas-opt-button">
<el-button
v-if="!isIframe"
secondary
@click="() => useEmitt().emitter.emit('canvasFullscreen')"
>
<template #icon>
<icon name="icon_pc_fullscreen"><icon_pc_fullscreen class="svg-icon" /></icon>
</template>
{{ t('visualization.fullscreen') }}</el-button
>
<el-button secondary @click="preview()">
<template #icon>
<icon name="icon_pc_outlined"><icon_pc_outlined class="svg-icon" /></icon>
</template>
{{ t('template_manage.preview') }}
</el-button>
<ShareVisualHead
v-if="!shareDisable"
:resource-id="dvInfo.id"
:weight="dvInfo.weight"
:resource-type="dvInfo.type"
/>
<el-button class="custom-button" v-if="dvInfo.weight > 6" type="primary" @click="dvEdit()">
<template #icon>
<icon name="icon_edit_outlined"><icon_edit_outlined class="svg-icon" /></icon>
</template>
{{ t('visualization.edit') }}</el-button
>
<el-dropdown popper-class="pad12" trigger="click">
<el-icon class="head-more-icon">
<Icon name="dv-head-more"><dvHeadMore class="svg-icon" /></Icon>
</el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="reload()"
><el-icon color="#646A73" size="16"><icon_replace_outlined /></el-icon
>{{ t('visualization.refresh_data') }}
</el-dropdown-item>
<el-dropdown
style="width: 100%; overflow: hidden"
trigger="hover"
popper-class="pad12"
placement="left-start"
v-if="exportPermissions[0]"
>
<div class="ed-dropdown-menu__item flex-align-center icon">
<el-icon color="#646A73" size="16"><icon_download_outlined /></el-icon>
{{ t('visualization.export_as') }}
<el-icon color="#646A73" size="16" class="arrow-right_icon"><ArrowRight /></el-icon>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="download('pdf')">PDF</el-dropdown-item>
<el-dropdown-item @click="downloadAsAppTemplate('template')">{{
t('visualization.style_template')
}}</el-dropdown-item>
<el-dropdown-item @click="downloadAsAppTemplate('app')">{{
t('visualization.apply_template')
}}</el-dropdown-item>
<el-dropdown-item @click="download('img')">{{
t('chart.image')
}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
<XpackComponent ref="openHandler" jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvT3BlbkhhbmRsZXI=" />
</template>
<style lang="less">
.pad12 {
.ed-dropdown-menu__item {
padding: 5px 36px 5px 12px !important;
.ed-icon {
margin-right: 8px;
}
.arrow-right_icon {
position: absolute;
right: 12px;
margin-right: 0;
}
&:has(.arrow-right_icon) {
width: 100%;
}
}
}
.preview-head {
width: 100%;
min-width: 300px;
height: 56px;
padding: 16px 24px;
border-bottom: 1px solid rgba(31, 35, 41, 0.15);
.canvas-name {
max-width: 200px;
font-size: 16px;
font-weight: 500;
}
.custom-icon {
cursor: pointer;
margin-left: 8px;
}
.create-area {
color: #646a73;
font-weight: 400;
font-size: 14px;
}
.canvas-opt-button {
display: flex;
justify-content: right;
align-items: center;
flex: 1;
.head-more-icon {
color: #1f2329;
margin-left: 12px;
cursor: pointer;
font-size: 20px;
border-radius: 4px;
position: relative;
&:hover {
&::after {
content: '';
position: absolute;
top: -4px;
left: -4px;
border-radius: 4px;
height: 28px;
width: 28px;
background: #1f23291a;
}
}
}
}
}
.info-tips {
margin-left: 4px;
font-size: 16px;
color: #646a73;
}
.custom-button {
margin-left: 12px;
}
</style>

View File

@ -0,0 +1,397 @@
<script setup lang="ts">
import icon_add_outlined from '@/assets/svg/icon_add_outlined.svg'
import DeResourceTree from '@/views/common/DeResourceTree.vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import ArrowSide from '@/views/common/DeResourceArrow.vue'
import { nextTick, onBeforeMount, reactive, ref, computed, onMounted } from 'vue'
import PreviewHead from '@/views/data-visualization/PreviewHead.vue'
import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue'
import { storeToRefs } from 'pinia'
import { useAppStoreWithOut } from '@/store/modules/app'
import { initCanvasData, initCanvasDataPrepare, onInitReady } from '@/utils/canvasUtils'
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, 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 { ElMessage } from 'element-plus-secondary'
import { useEmitt } from '@/hooks/web/useEmitt'
import { useUserStoreWithOut } from '@/store/modules/user'
import { useI18n } from '@/hooks/web/useI18n'
const userStore = useUserStoreWithOut()
const userName = computed(() => userStore.getName)
const { t } = useI18n()
const dvMainStore = dvMainStoreWithOut()
const { dvInfo, canvasViewDataInfo } = storeToRefs(dvMainStore)
const previewCanvasContainer = ref(null)
const dvPreviewRef = ref(null)
const slideShow = ref(true)
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,
type: String,
default: 'preview'
},
noClose: {
required: false,
type: Boolean,
default: false
}
})
const resourceTreeRef = ref()
const hasTreeData = computed(() => {
return resourceTreeRef.value?.hasData
})
const mounted = computed(() => {
return resourceTreeRef.value?.mounted
})
const rootManage = computed(() => {
return resourceTreeRef.value?.rootManage
})
const appStore = useAppStoreWithOut()
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
function createNew() {
resourceTreeRef.value?.createNewObject()
}
const loadCanvasData = (dvId, weight?, ext?) => {
const initMethod = props.showPosition === 'multiplexing' ? initCanvasDataPrepare : initCanvasData
dataInitState.value = false
initMethod(
dvId,
'dataV',
function ({
canvasDataResult,
canvasStyleResult,
dvInfo,
canvasViewInfoPreview,
curPreviewGap
}) {
dvInfo['weight'] = weight
dvInfo['ext'] = ext || 0
state.canvasDataPreview = canvasDataResult
state.canvasStylePreview = canvasStyleResult
state.canvasViewInfoPreview = canvasViewInfoPreview
state.dvInfo = dvInfo
state.curPreviewGap = curPreviewGap
dataInitState.value = true
if (props.showPosition === 'preview') {
dvMainStore.updateCurDvInfo(dvInfo)
nextTick(() => {
dvPreviewRef.value?.restore()
})
}
nextTick(() => {
onInitReady({ resourceId: dvId })
})
}
)
}
const download = type => {
downloadStatus.value = true
setTimeout(() => {
const vueDom = previewCanvasContainer.value.querySelector('.canvas-container')
downloadCanvas2(type, vueDom, state.dvInfo.name, () => {
downloadStatus.value = false
})
}, 200)
}
const fileDownload = (downloadType, attachParams) => {
downloadStatus.value = true
nextTick(() => {
const vueDom = previewCanvasContainer.value.querySelector('.canvas-container')
download2AppTemplate(downloadType, vueDom, state.dvInfo.name, attachParams, () => {
downloadStatus.value = false
})
})
}
const downloadAsAppTemplate = downloadType => {
if (downloadType === 'template') {
fileDownload(downloadType, null)
} else if (downloadType === 'app') {
downLoadToAppPre()
}
}
const downLoadToAppPre = () => {
const result = checkTemplate()
if (result && result.length > 0) {
ElMessage.warning(`当前仪表板中[${result}]属于模版图表,无法导出,请先设置数据集!`)
} else {
appExportFormRef.value.init({
appName: state.dvInfo.name,
icon: null,
version: '2.0',
creator: userName.value,
required: '2.9.0',
description: null
})
}
}
const checkTemplate = () => {
let templateViewNames = ','
Object.keys(canvasViewDataInfo.value).forEach(key => {
const viewInfo = canvasViewDataInfo.value[key]
if (viewInfo && viewInfo?.dataFrom === 'template') {
templateViewNames = templateViewNames + viewInfo.title + ','
}
})
return templateViewNames.slice(1)
}
const slideOpenChange = () => {
slideShow.value = !slideShow.value
}
const reload = id => {
loadCanvasData(id, state.dvInfo.weight, state.dvInfo.ext)
}
const resourceNodeClick = data => {
loadCanvasData(data.id, data.weight, data.ext)
}
const dataVKeepSize = computed(() => {
return state.canvasStylePreview?.screenAdaptor === 'keep'
})
const state = reactive({
canvasDataPreview: null,
canvasStylePreview: null,
canvasViewInfoPreview: null,
dvInfo: null,
curPreviewGap: 0
})
const sideTreeStatus = ref(true)
const changeSideTreeStatus = val => {
sideTreeStatus.value = val
}
const mouseenter = () => {
appStore.setArrowSide(true)
}
const mouseleave = () => {
appStore.setArrowSide(false)
}
const getPreviewStateInfo = () => {
return state
}
const downLoadApp = appAttachInfo => {
fileDownload('app', appAttachInfo)
}
onMounted(() => {
useEmitt({
name: 'canvasDownload',
callback: function () {
download('img')
}
})
})
defineExpose({
getPreviewStateInfo
})
onBeforeMount(() => {
if (props.showPosition === 'preview') {
dvMainStore.canvasDataInit()
}
})
</script>
<template>
<div class="dv-preview">
<ArrowSide
v-if="!noClose"
:style="{ left: (sideTreeStatus ? width - 12 : 0) + 'px' }"
@change-side-tree-status="changeSideTreeStatus"
:isInside="!sideTreeStatus"
></ArrowSide>
<el-aside
class="resource-area"
@mouseenter="mouseenter"
@mouseleave="mouseleave"
:class="{ 'close-side': !slideShow, retract: !sideTreeStatus }"
ref="node"
:style="{ width: width + 'px' }"
>
<ArrowSide
v-if="!noClose"
:isInside="!sideTreeStatus"
:style="{ left: (sideTreeStatus ? width - 12 : 0) + 'px' }"
@change-side-tree-status="changeSideTreeStatus"
></ArrowSide>
<de-resource-tree
ref="resourceTreeRef"
v-show="slideShow"
:cur-canvas-type="'dataV'"
:show-position="showPosition"
@node-click="resourceNodeClick"
/>
</el-aside>
<el-container
class="preview-area"
:class="{ 'no-data': !hasTreeData }"
v-loading="!dataInitState"
>
<div @click="slideOpenChange" class="flexible-button-area" v-if="false">
<el-icon v-if="slideShow"><ArrowLeft /></el-icon>
<el-icon v-else><ArrowRight /></el-icon>
</div>
<template v-if="dvInfo.name">
<preview-head
v-if="showPosition === 'preview'"
@reload="reload"
@download="download"
@downloadAsAppTemplate="downloadAsAppTemplate"
/>
<div
v-if="showPosition === 'multiplexing' && dataInitState"
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'"
:class="{ 'canvas_keep-size': dataVKeepSize }"
ref="previewCanvasContainer"
class="content"
>
<dv-preview
ref="dvPreviewRef"
v-if="state.canvasStylePreview && dataInitState"
:show-position="showPosition"
:canvas-data-preview="state.canvasDataPreview"
:canvas-style-preview="state.canvasStylePreview"
:canvas-view-info-preview="state.canvasViewInfoPreview"
:dv-info="state.dvInfo"
:cur-preview-gap="state.curPreviewGap"
:download-status="downloadStatus"
></dv-preview>
</div>
</template>
<template v-else-if="hasTreeData && mounted">
<empty-background :description="t('visualization.select_screen_tips')" img-type="select" />
</template>
<template v-else-if="mounted">
<empty-background :description="t('visualization.no_screen')" img-type="none">
<el-button v-if="rootManage && !isDataEaseBi" @click="createNew" type="primary">
<template #icon>
<Icon name="icon_add_outlined"><icon_add_outlined class="svg-icon" /></Icon>
</template>
{{ t('commons.create') }}{{ t('work_branch.big_data_screen') }}
</el-button>
</empty-background>
</template>
</el-container>
</div>
<app-export-form
ref="appExportFormRef"
:dv-info="state.dvInfo"
:component-data="state.canvasDataPreview"
:canvas-view-info="state.canvasViewInfoPreview"
@downLoadApp="downLoadApp"
></app-export-form>
</template>
<style lang="less">
.dv-preview {
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
background: #ffffff;
position: relative;
.resource-area {
position: relative;
height: 100%;
width: 279px;
padding: 0;
overflow: visible;
border-right: 1px solid #d7d7d7;
&.retract {
display: none;
}
}
.preview-area {
flex: 1;
display: flex;
flex-direction: column;
overflow-x: hidden;
overflow-y: auto;
position: relative;
//transition: 0.5s;
&.no-data {
background-color: rgba(245, 246, 247, 1);
}
.content {
flex: 1;
width: 100%;
overflow-x: hidden;
overflow-y: auto;
align-items: center;
}
}
}
.close-side {
width: 0px !important;
padding: 0px !important;
border-right: 0px !important;
}
.flexible-button-area {
position: absolute;
height: 60px;
width: 16px;
left: 0;
top: calc(50% - 30px);
background-color: #ffffff;
border-radius: 0 4px 4px 0;
cursor: pointer;
z-index: 10;
display: flex;
align-items: center;
border-top: 1px solid #d7d7d7;
border-right: 1px solid #d7d7d7;
border-bottom: 1px solid #d7d7d7;
}
.multiplexing-content {
padding: 12px;
background-color: rgb(245, 246, 247);
}
</style>

View File

@ -0,0 +1,774 @@
<script setup lang="ts">
import dvRuler from '@/assets/svg/dv-ruler.svg'
import CanvasAttr from '@/components/data-visualization/CanvasAttr.vue'
import { computed, watch, onMounted, reactive, ref, nextTick, onUnmounted } from 'vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
import { contextmenuStoreWithOut } from '@/store/modules/data-visualization/contextmenu'
import { composeStoreWithOut } from '@/store/modules/data-visualization/compose'
import { useAppStoreWithOut } from '@/store/modules/app'
import { storeToRefs } from 'pinia'
import DvToolbar from '../../components/data-visualization/DvToolbar.vue'
import ComponentToolBar from '../../components/data-visualization/ComponentToolBar.vue'
import eventBus from '../../utils/eventBus'
import { findComponentAttr } from '../../utils/components'
import DvSidebar from '../../components/visualization/DvSidebar.vue'
import router from '@/router'
import Editor from '@/views/chart/components/editor/index.vue'
import { guid } from '@/views/visualized/data/dataset/form/util.js'
import { getDatasetTree } from '@/api/dataset'
import { Tree } from '@/views/visualized/data/dataset/form/CreatDsGroup.vue'
import {
decompressionPre,
findDragComponent,
findNewComponent,
initCanvasData,
onInitReady
} from '@/utils/canvasUtils'
import CanvasCore from '@/components/data-visualization/canvas/CanvasCore.vue'
import { listenGlobalKeyDown, releaseAttachKey } from '@/utils/DeShortcutKey'
import { adaptCurThemeCommonStyle } from '@/utils/canvasStyle'
import { useEmbedded } from '@/store/modules/embedded'
import { changeComponentSizeWithScale } from '@/utils/changeComponentsSizeWithScale'
import { useEmitt } from '@/hooks/web/useEmitt'
import { check, compareStorage } from '@/utils/CrossPermission'
import { useCache } from '@/hooks/web/useCache'
import RealTimeListTree from '@/components/data-visualization/RealTimeListTree.vue'
import { interactiveStoreWithOut } from '@/store/modules/interactive'
import { watermarkFind } from '@/api/watermark'
import { XpackComponent } from '@/components/plugin'
import { Base64 } from 'js-base64'
import CanvasCacheDialog from '@/components/visualization/CanvasCacheDialog.vue'
import { deepCopy } from '@/utils/utils'
import DvPreview from '@/views/data-visualization/DvPreview.vue'
import DeRuler from '@/custom-component/common/DeRuler.vue'
import { useRequestStoreWithOut } from '@/store/modules/request'
import { usePermissionStoreWithOut } from '@/store/modules/permission'
import ChartStyleBatchSet from '@/views/chart/components/editor/editor-style/ChartStyleBatchSet.vue'
import CustomTabsSort from '@/custom-component/de-tabs/CustomTabsSort.vue'
import { useI18n } from '@/hooks/web/useI18n'
const interactiveStore = interactiveStoreWithOut()
const embeddedStore = useEmbedded()
const { wsCache } = useCache()
const dvPreviewRef = ref(null)
const { t } = useI18n()
const eventCheck = e => {
if (e.key === 'screen-weight' && !compareStorage(e.oldValue, e.newValue)) {
const opt = embeddedStore.opt || router.currentRoute.value.query.opt
if (!(opt && opt === 'create')) {
check(
wsCache.get('screen-weight'),
embeddedStore.dvId || (router.currentRoute.value.query.dvId as string),
4
)
}
}
}
const mainCanvasCoreRef = ref(null)
const customTabsSortRef = ref(null)
const appStore = useAppStoreWithOut()
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
const dvMainStore = dvMainStoreWithOut()
const snapshotStore = snapshotStoreWithOut()
const contextmenuStore = contextmenuStoreWithOut()
const composeStore = composeStoreWithOut()
const canvasCacheOutRef = ref(null)
const deWRulerRef = ref(null)
const deHRulerRef = ref(null)
const requestStore = useRequestStoreWithOut()
const permissionStore = usePermissionStoreWithOut()
const {
fullscreenFlag,
componentData,
curComponent,
isClickComponent,
canvasStyleData,
canvasViewInfo,
editMode,
dvInfo,
canvasState,
batchOptStatus
} = storeToRefs(dvMainStore)
const { editorMap, isSpaceDown } = storeToRefs(composeStore)
const canvasOut = ref(null)
const canvasInner = ref(null)
const leftSidebarRef = ref(null)
const dvLayout = ref(null)
const canvasCenterRef = ref(null)
const mainHeight = ref(300)
let createType = null
let isDragging = false //
let startX, startY, scrollLeft, scrollTop
const state = reactive({
sideShow: true,
countTime: 0,
datasetTree: [],
scaleHistory: null,
canvasId: 'canvas-main',
canvasInitStatus: false,
sourcePid: null,
resourceId: null,
opt: null,
baseWidth: 10,
baseHeight: 10
})
const tabSort = () => {
if (curComponent.value) {
customTabsSortRef.value.sortInit(curComponent.value)
}
}
//
const enableDragging = e => {
if (isSpaceDown.value) {
//
isDragging = true
startX = e.pageX - canvasOut.value.wrapRef.offsetLeft
startY = e.pageY - canvasOut.value.wrapRef.offsetTop
scrollLeft = canvasOut.value.wrapRef.scrollLeft
scrollTop = canvasOut.value.wrapRef.scrollTop
e.preventDefault()
e.stopPropagation()
}
}
//
const onMouseMove = e => {
if (!isDragging) return
e.preventDefault()
e.stopPropagation()
const x = e.pageX - canvasOut.value.wrapRef.offsetLeft
const y = e.pageY - canvasOut.value.wrapRef.offsetTop
const walkX = x - startX
const walkY = y - startY
canvasOut.value.wrapRef.scrollLeft = scrollLeft - walkX
canvasOut.value.wrapRef.scrollTop = scrollTop - walkY
}
//
const disableDragging = () => {
isDragging = false
}
const contentStyle = computed(() => {
const { width, height } = canvasStyleData.value
if (editMode.value === 'preview') {
return {
width: '100%',
height: 'auto',
overflow: 'hidden'
}
} else {
return {
minWidth: '1600px',
width: width * 1.5 + 'px',
height: height * 1.5 + 'px'
}
}
})
//
const handleNew = newComponentInfo => {
state.countTime++
const { componentName, innerType, staticMap } = newComponentInfo
if (componentName) {
const { width, height, scale } = canvasStyleData.value
const component = findNewComponent(componentName, innerType, staticMap)
component.style.top = ((height - component.style.height) * scale) / 200
component.style.left = ((width - component.style.width) * scale) / 200
component.id = guid()
const popComponents = componentData.value.filter(
ele => ele.category && ele.category === 'hidden'
)
//
if (
canvasState.value.curPointArea === 'hidden' &&
component.component === 'VQuery' &&
(!popComponents || popComponents.length === 0)
) {
component.category = canvasState.value.curPointArea
component.commonBackground.backgroundColor = 'rgba(41, 41, 41, 1)'
}
changeComponentSizeWithScale(component)
dvMainStore.addComponent({ component: component, index: undefined })
adaptCurThemeCommonStyle(component)
snapshotStore.recordSnapshotCache('renderChart', component.id)
if (state.countTime > 10) {
state.sideShow = false
nextTick(() => {
state.countTime = 0
state.sideShow = true
})
}
useEmitt().emitter.emit('initScroll')
}
}
const handleDrop = e => {
e.preventDefault()
e.stopPropagation()
const componentInfo = e.dataTransfer.getData('id')
const rectInfo = editorMap.value[state.canvasId].getBoundingClientRect()
if (componentInfo) {
const component = findDragComponent(componentInfo)
component.style.top = e.clientY - rectInfo.y
component.style.left = e.clientX - rectInfo.x
component.id = guid()
changeComponentSizeWithScale(component)
dvMainStore.addComponent({ component: component, index: undefined })
adaptCurThemeCommonStyle(component)
snapshotStore.recordSnapshotCache('renderChart', component.id)
}
}
const handleDragOver = e => {
e.preventDefault()
e.dataTransfer.dropEffect = 'copy'
}
const handleMouseDown = e => {
// e.stopPropagation()
if (isSpaceDown.value) {
return
}
dvMainStore.setClickComponentStatus(false)
// curComponent
dvMainStore.setCurComponent({ component: null, index: null })
dvMainStore.setInEditorStatus(true)
mainCanvasCoreRef.value.handleMouseDown(e)
}
const deselectCurComponent = e => {
if (isSpaceDown.value) {
return
}
if (!isClickComponent.value) {
curComponent.value && dvMainStore.setCurComponent({ component: null, index: null })
}
// 0 1 2
if (e.button != 2) {
contextmenuStore.hideContextMenu()
}
}
const initDataset = () => {
getDatasetTree({}).then(res => {
state.datasetTree = (res as unknown as Tree[]) || []
})
}
//
listenGlobalKeyDown()
const initScroll = () => {
nextTick(() => {
if (canvasCenterRef.value) {
const { width, height } = canvasStyleData.value
const mainWidth = canvasCenterRef.value.clientWidth
mainHeight.value = canvasCenterRef.value.clientHeight
const scrollX = (1.5 * width - mainWidth) / 2
const scrollY = (1.5 * height - mainHeight.value) / 2 + 20
//
canvasOut.value.scrollTo(scrollX, scrollY)
}
})
}
const doUseCache = flag => {
const canvasCache = wsCache.get('DE-DV-CATCH-' + state.resourceId)
if (flag && canvasCache) {
const canvasCacheSeries = deepCopy(canvasCache)
snapshotStore.snapshotPublish(canvasCacheSeries)
state.canvasInitStatus = true
nextTick(() => {
dvMainStore.setDataPrepareState(true)
snapshotStore.recordSnapshotCache('renderChart')
setTimeout(() => {
// 使
snapshotStore.recordSnapshotCache('renderChart')
}, 2000)
})
} else {
initLocalCanvasData()
wsCache.delete('DE-DV-CATCH-' + state.resourceId)
}
}
const initLocalCanvasData = async () => {
const { opt, sourcePid, resourceId } = state
const busiFlg = opt === 'copy' ? 'dataV-copy' : 'dataV'
await initCanvasData(resourceId, busiFlg, function () {
state.canvasInitStatus = true
// afterInit
nextTick(() => {
dvMainStore.setDataPrepareState(true)
snapshotStore.recordSnapshotCache('renderChart')
if (dvInfo.value && opt === 'copy') {
dvInfo.value.dataState = 'prepare'
dvInfo.value.optType = 'copy'
dvInfo.value.pid = sourcePid
setTimeout(() => {
snapshotStore.recordSnapshotCache('renderChart')
}, 1500)
}
onInitReady({ resourceId: resourceId })
})
})
}
const previewScaleChange = () => {
state.scaleHistory = canvasStyleData.value.scale
nextTick(() => {
let canvasWidth = dvLayout.value.clientWidth
const previewScale = (canvasWidth * 100) / canvasStyleData.value.width
canvasStyleData.value.scale = previewScale
})
}
watch(
() => editMode.value,
val => {
if (val === 'edit') {
if (state.scaleHistory) {
canvasStyleData.value.scale = state.scaleHistory
}
initScroll()
} else {
previewScaleChange()
}
}
)
const checkPer = async resourceId => {
if (!window.DataEaseBi || !resourceId) {
return true
}
const request = { busiFlag: 'dataV' }
await interactiveStore.setInteractive(request)
return check(wsCache.get('screen-weight'), resourceId, 4)
}
// targetSourceId ID
const winMsgHandle = event => {
const msgInfo = event.data
if (msgInfo?.targetSourceId === dvInfo.value.id + '')
if (msgInfo.type === 'webParams') {
//
winMsgWebParamsHandle(msgInfo)
}
}
const winMsgWebParamsHandle = msgInfo => {
const params = msgInfo.params
dvMainStore.addWebParamsFilter(params)
}
const loadFinish = ref(false)
const newWindowFromDiv = ref(false)
let p = null
const XpackLoaded = () => p(true)
onMounted(async () => {
dvMainStore.setCurComponent({ component: null, index: null })
snapshotStore.initSnapShot()
if (window.location.hash.includes('#/dvCanvas')) {
newWindowFromDiv.value = true
}
await new Promise(r => (p = r))
loadFinish.value = true
window.addEventListener('blur', releaseAttachKey)
window.addEventListener('message', winMsgHandle)
if (editMode.value === 'edit') {
window.addEventListener('storage', eventCheck)
}
const dvId = embeddedStore.dvId || router.currentRoute.value.query.dvId
const pid = embeddedStore.pid || router.currentRoute.value.query.pid
const templateParams =
embeddedStore.templateParams || router.currentRoute.value.query.templateParams
createType = embeddedStore.createType || router.currentRoute.value.query.createType
const opt = embeddedStore.opt || router.currentRoute.value.query.opt
const checkResult = await checkPer(dvId)
if (!checkResult) {
return
}
initDataset()
state.resourceId = dvId
state.sourcePid = pid
state.opt = opt
if (dvId) {
state.canvasInitStatus = false
const canvasCache = wsCache.get('DE-DV-CATCH-' + dvId)
if (canvasCache) {
canvasCacheOutRef.value?.dialogInit({ canvasType: 'dataV', resourceId: dvId })
} else {
await initLocalCanvasData()
}
} else if (opt && opt === 'create') {
state.canvasInitStatus = false
let watermarkBaseInfo
try {
await watermarkFind().then(rsp => {
watermarkBaseInfo = rsp.data
watermarkBaseInfo.settingContent = JSON.parse(watermarkBaseInfo.settingContent)
})
} catch (e) {
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, preName)
nextTick(() => {
state.canvasInitStatus = true
dvMainStore.setDataPrepareState(true)
snapshotStore.recordSnapshotCache('renderChart')
//
if (createType === 'template') {
dvMainStore.setComponentData(deTemplateData['componentData'])
dvMainStore.setCanvasStyle(deTemplateData['canvasStyleData'])
dvMainStore.setCanvasViewInfo(deTemplateData['canvasViewInfo'])
dvMainStore.setAppDataInfo(deTemplateData['appData'])
setTimeout(() => {
snapshotStore.recordSnapshotCache('template')
}, 1500)
}
if (dvMainStore.getAppDataInfo()) {
eventBus.emit('save')
}
})
} else {
let url = '#/screen/index'
window.open(url, '_self')
}
initScroll()
useEmitt({
name: 'initScroll',
callback: function () {
initScroll()
}
})
})
onUnmounted(() => {
window.removeEventListener('storage', eventCheck)
window.removeEventListener('blur', releaseAttachKey)
eventBus.off('handleNew', handleNew)
eventBus.off('tabSort', tabSort)
})
const previewStatus = computed(() => editMode.value === 'preview')
const otherEditorShow = computed(() => {
return Boolean(
curComponent.value &&
(!['UserView', 'GroupArea', 'VQuery'].includes(curComponent.value?.component) ||
(curComponent.value?.component === 'UserView' &&
curComponent.value?.innerType === 'picture-group')) &&
!batchOptStatus.value
)
})
const canvasPropertiesShow = computed(
() => !curComponent.value || ['GroupArea'].includes(curComponent.value.component)
)
const viewsPropertiesShow = computed(() => {
return Boolean(
curComponent.value &&
['UserView', 'VQuery'].includes(curComponent.value.component) &&
curComponent.value.innerType !== 'picture-group' &&
!batchOptStatus.value
)
})
const scrollCanvas = e => {
deWRulerRef.value.rulerScroll(e)
deHRulerRef.value.rulerScroll(e)
}
const coreComponentData = computed(() =>
componentData.value.filter(ele => !ele.category || ele.category !== 'hidden')
)
const popComponentData = computed(() =>
componentData.value.filter(ele => ele.category && ele.category === 'hidden')
)
eventBus.on('handleNew', handleNew)
eventBus.on('tabSort', tabSort)
</script>
<template>
<div
ref="dvLayout"
class="dv-common-layout"
:class="isDataEaseBi && !newWindowFromDiv && 'dataease-w-h'"
>
<DvToolbar />
<div class="custom-dv-divider" />
<el-container
v-if="loadFinish"
v-loading="requestStore.loadingMap && requestStore.loadingMap[permissionStore.currentPath]"
element-loading-background="rgba(0, 0, 0, 0)"
class="dv-layout-container"
:class="{ 'preview-layout-container': previewStatus }"
>
<!-- 左侧组件列表 -->
<dv-sidebar
:title="t('visualization.layer_management')"
:width="180"
:scroll-width="3"
:aside-position="'left'"
:side-name="'realTimeComponent'"
class="left-sidebar"
id="dv-main-left-sidebar"
:class="{ 'preview-aside': previewStatus }"
>
<real-time-list-tree />
</dv-sidebar>
<!-- 中间画布 -->
<main id="dv-main-center" class="center" ref="canvasCenterRef">
<div class="de-ruler-icon-outer">
<el-icon class="de-ruler-icon">
<Icon name="dv-ruler"><dvRuler class="svg-icon" /></Icon>
</el-icon>
</div>
<de-ruler ref="deWRulerRef" @update:tickSize="val => (state.baseWidth = val)"></de-ruler>
<de-ruler
direction="vertical"
@update:tickSize="val => (state.baseHeight = val)"
:size="mainHeight"
ref="deHRulerRef"
></de-ruler>
<el-scrollbar
ref="canvasOut"
@scroll="scrollCanvas"
class="content"
:class="{ 'preview-content': previewStatus }"
@mousedown="enableDragging"
@mouseup="disableDragging"
@mousemove="onMouseMove"
@mouseleave="disableDragging"
>
<div
id="canvas-dv-outer"
ref="canvasInner"
:style="contentStyle"
@drop="handleDrop"
@dragover="handleDragOver"
@mousedown="handleMouseDown"
@mouseup="deselectCurComponent"
>
<div v-if="isSpaceDown" class="canvas-drag"></div>
<div class="canvas-dv-inner">
<canvas-core
class="canvas-area-shadow editor-main"
v-if="state.canvasInitStatus"
ref="mainCanvasCoreRef"
:component-data="coreComponentData"
:pop-component-data="popComponentData"
:canvas-style-data="canvasStyleData"
:canvas-view-info="canvasViewInfo"
:canvas-id="state.canvasId"
:base-height="state.baseHeight"
:base-width="state.baseWidth"
:font-family="canvasStyleData.fontFamily"
>
<template v-slot:canvasDragTips>
<div class="canvas-drag-tip">
{{ t('visualization.hold_canvas_tips') }}
</div>
</template>
</canvas-core>
</div>
</div>
</el-scrollbar>
<ComponentToolBar :class="{ 'preview-aside-x': previewStatus }"></ComponentToolBar>
</main>
<!-- 右侧侧组件列表 -->
<div style="width: auto; height: 100%" ref="leftSidebarRef">
<template v-if="!batchOptStatus && state.sideShow">
<dv-sidebar
v-if="otherEditorShow"
:title="curComponent['name']"
:width="240"
:side-name="'componentProp'"
:aside-position="'right'"
class="left-sidebar"
:slide-index="2"
:themes="'dark'"
:element="curComponent"
:view="canvasViewInfo[curComponent.id]"
:class="{ 'preview-aside': editMode === 'preview' }"
>
<component :is="findComponentAttr(curComponent)" />
</dv-sidebar>
<dv-sidebar
v-show="canvasPropertiesShow"
:title="t('visualization.screen_config')"
:width="240"
:side-name="'canvas'"
:aside-position="'right'"
class="left-sidebar"
:class="{ 'preview-aside': editMode === 'preview' }"
>
<canvas-attr></canvas-attr>
</dv-sidebar>
<div
v-show="viewsPropertiesShow"
style="height: 100%"
:class="{ 'preview-aside': editMode === 'preview' }"
>
<editor
:view="canvasViewInfo[curComponent ? curComponent.id : 'default']"
themes="dark"
:dataset-tree="state.datasetTree"
></editor>
</div>
</template>
<dv-sidebar
v-if="batchOptStatus"
:theme-info="'dark'"
:title="t('visualization.batch_style_set')"
:width="280"
aside-position="right"
class="left-sidebar"
:side-name="'batchOpt'"
>
<chart-style-batch-set themes="dark"></chart-style-batch-set>
</dv-sidebar>
</div>
</el-container>
</div>
<XpackComponent
jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvTmV3V2luZG93SGFuZGxlcg=="
@loaded="XpackLoaded"
@load-fail="XpackLoaded"
/>
<xpack-component jsname="L2NvbXBvbmVudC90aHJlc2hvbGQtd2FybmluZy9UaHJlc2hvbGREaWFsb2c=" />
<canvas-cache-dialog ref="canvasCacheOutRef" @doUseCache="doUseCache"></canvas-cache-dialog>
<dv-preview
v-if="fullscreenFlag"
style="z-index: 10"
ref="dvPreviewRef"
:canvas-data-preview="componentData"
:canvas-style-preview="canvasStyleData"
:canvas-view-info-preview="canvasViewInfo"
:dv-info="dvInfo"
></dv-preview>
<custom-tabs-sort ref="customTabsSortRef"></custom-tabs-sort>
</template>
<style lang="less">
.preview-layout-container {
}
.preview-content {
align-items: center;
}
.dv-common-layout {
height: calc(100vh - 1px);
width: 100vw;
overflow: hidden;
color: @dv-canvas-main-font-color;
.dv-layout-container {
height: calc(100vh - @top-bar-height - 1px);
.left-sidebar {
height: 100%;
}
.center {
display: flex;
flex-direction: column;
height: 100%;
flex: 1;
position: relative;
background-color: rgba(51, 51, 51, 1);
overflow: auto;
.content {
position: relative;
flex: 1;
width: 100%;
overflow: auto;
margin: auto;
}
}
.right-sidebar {
height: 100%;
}
}
&.dataease-w-h {
height: 100%;
width: 100%;
.dv-layout-container {
height: calc(100% - @top-bar-height);
}
}
}
.preview-aside {
width: 0px !important;
overflow: hidden;
padding: 0px;
}
.preview-aside-x {
height: 0px !important;
overflow: hidden;
padding: 0px;
}
.canvas-area-shadow {
box-sizing: border-box;
border: 1px solid rgba(85, 85, 85, 0.4);
}
.custom-dv-divider {
width: 100%;
height: 1px;
background: #000;
}
.canvas-dv-inner {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.de-ruler-icon-outer {
background: #2c2c2c;
position: absolute;
width: 30px;
height: 30px;
z-index: 3;
color: #ebebeb;
.de-ruler-icon {
margin-left: 6px;
margin-top: 6px;
font-size: 24px;
color: #ebebeb;
}
}
.canvas-drag {
position: absolute;
z-index: 1;
opacity: 0.3;
cursor: pointer;
width: 100%;
height: 100%;
}
.canvas-drag-tip {
position: absolute;
right: 5px;
bottom: -20px;
font-size: 12px;
color: rgb(169, 175, 184);
}
</style>