添加大屏模块
This commit is contained in:
parent
80bb62a774
commit
349722797e
@ -51,8 +51,8 @@ function paramsResetForm() {
|
||||
emit('closeClick', false)
|
||||
}
|
||||
function saveParamsObj(){
|
||||
|
||||
if(isSwitch.value == false){
|
||||
debugger
|
||||
if(isSwitch.value == true){
|
||||
return
|
||||
}
|
||||
isSwitch.value = true
|
||||
|
@ -5,6 +5,7 @@ import { ElMessage,ElMessageBox } from 'element-plus-secondary'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { publicTree } from '@/utils/validate';
|
||||
import { moduleList,moduleAdd,moduleUpdate,moduleDel } from '@/api/application/module'
|
||||
const emit = defineEmits(['handleNodeClick'])
|
||||
const { t } = useI18n()
|
||||
const props = defineProps({
|
||||
projectInfo: {
|
||||
@ -12,6 +13,28 @@ const props = defineProps({
|
||||
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 isSwitch =ref(false)
|
||||
const treeloading = ref(false) // 树加载中
|
||||
@ -23,6 +46,7 @@ const isTreeDrag =ref(false)
|
||||
const dataInfo:any =ref({
|
||||
pid: '',
|
||||
level: '',
|
||||
type: '',
|
||||
nodeType: '',
|
||||
name:'',
|
||||
appId:''
|
||||
@ -35,6 +59,11 @@ watch(() => props.projectInfo, val => { // 初始化数据
|
||||
getInit()
|
||||
})
|
||||
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
|
||||
})
|
||||
function getInit() { // 初始化
|
||||
@ -50,6 +79,7 @@ function addClic(name:any,level:any,pid:any){ // 新建目录、模块
|
||||
level: level,
|
||||
nodeType: "",
|
||||
name:'',
|
||||
type:'',
|
||||
}
|
||||
|
||||
if(name == '目录'){
|
||||
@ -68,7 +98,13 @@ function handleDatasetName(){ // 关键字搜索
|
||||
|
||||
}
|
||||
const handleNodeClick = (data: any) => { // 树节点点击
|
||||
|
||||
if(data.nodeType == '01'){
|
||||
}else{
|
||||
data.id = '1097641013486424064'
|
||||
emit('handleNodeClick', data)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
function moreClic(event:any ,data:any){ // 更多操作
|
||||
let element = document.getElementById("drag_main_area")
|
||||
@ -88,7 +124,8 @@ function moreClic(event:any ,data:any){ // 更多操作
|
||||
isTreeDrag.value = true
|
||||
}
|
||||
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){ // // 添加子节点
|
||||
let element = document.getElementById("drag_main_area")
|
||||
@ -105,6 +142,7 @@ function addTreeChildNode(event:any,level:any,pid:any){ // // 添加子节点
|
||||
level: level,
|
||||
nodeType: "",
|
||||
name:'',
|
||||
type:''
|
||||
}
|
||||
isTreeDrag.value = true
|
||||
}
|
||||
@ -130,16 +168,21 @@ function saveData(){ // 保存
|
||||
if(dataInfo.value.id !=null && dataInfo.value.id !=''){
|
||||
let data = {
|
||||
id:dataInfo.value.id,
|
||||
name:dataInfo.value.name
|
||||
name:dataInfo.value.name,
|
||||
type:dataInfo.value.type,
|
||||
}
|
||||
moduleUpdate(data).then(() => {
|
||||
ElMessage.success('添加成功')
|
||||
ElMessage.success('修改成功')
|
||||
getInit()
|
||||
isDialog.value = false
|
||||
}).catch(() => {
|
||||
isSwitch.value = false
|
||||
})
|
||||
}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(() => {
|
||||
ElMessage.success('添加成功')
|
||||
getInit()
|
||||
@ -179,6 +222,7 @@ function delTreeClic(){ // 删除
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="project-left-box">
|
||||
@ -211,13 +255,13 @@ function delTreeClic(){ // 删除
|
||||
<div style="
|
||||
width: 20px;
|
||||
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 == '02'" src="@/assets/newimg/icon/bianji.png" alt="" style="width: 14px;height: 13px;" @click="editClic(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.stop="editClic(data)">
|
||||
|
||||
</div>
|
||||
<div style="
|
||||
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;">
|
||||
</div>
|
||||
|
||||
@ -236,6 +280,7 @@ function delTreeClic(){ // 删除
|
||||
<el-form
|
||||
ref="paramsObjRef"
|
||||
:model="dataInfo"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item :label="dataInfo.nodeType=='01'?'目录名称:':'模块名称:'" prop="name">
|
||||
<el-input
|
||||
@ -243,6 +288,24 @@ function delTreeClic(){ // 删除
|
||||
v-model="dataInfo.name"
|
||||
/>
|
||||
</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>
|
||||
<template #footer>
|
||||
<el-button @click="paramsCloseForm">{{ t('dataset.cancel') }} </el-button>
|
||||
@ -384,7 +447,16 @@ function delTreeClic(){ // 删除
|
||||
:deep(.ed-input__wrapper:hover){
|
||||
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>
|
||||
.create-project-dialog{
|
||||
|
@ -1,10 +1,57 @@
|
||||
<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 Leftmenu from './Leftmenu/index.vue'
|
||||
import Rightmenu from './Rightmenu/index.vue'
|
||||
import { findApplicationById } from "@/api/application/application";
|
||||
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 projectInfo:any = ref({})
|
||||
const applicationId:any = ref('')
|
||||
@ -17,12 +64,64 @@ function getInit() {
|
||||
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>
|
||||
<template>
|
||||
<div class="project-box">
|
||||
<Header :projectInfo="projectInfo"/>
|
||||
<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>
|
||||
</template>
|
||||
|
111
core/core-frontend/src/viewsnew/data-visualization/DvPreview.vue
Normal file
111
core/core-frontend/src/viewsnew/data-visualization/DvPreview.vue
Normal 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>
|
@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<link-index :error-tips="true" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import LinkIndex from '@/views/share/link/index.vue'
|
||||
</script>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
774
core/core-frontend/src/viewsnew/data-visualization/index.vue
Normal file
774
core/core-frontend/src/viewsnew/data-visualization/index.vue
Normal 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>
|
Loading…
Reference in New Issue
Block a user