<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import { findComponentAttr } from '../../utils/components'
import DvSidebar from '../../components/visualization/DvSidebar.vue'
import router from '@/router'
import MobileConfigPanel from './MobileConfigPanel.vue'
import { useAppStoreWithOut } from '@/store/modules/app'
import { useEmitt } from '@/hooks/web/useEmitt'
import DbToolbar from '@/components/dashboard/DbToolbar.vue'
import ViewEditor from '@/views/chart/components/editor/index.vue'
import { getDatasetTree } from '@/api/dataset'
import { Tree } from '@/views/visualized/data/dataset/form/CreatDsGroup.vue'
import DbCanvasAttr from '@/components/dashboard/DbCanvasAttr.vue'
import { decompressionPre, initCanvasData, onInitReady } from '@/utils/canvasUtils'
import ChartStyleBatchSet from '@/views/chart/components/editor/editor-style/ChartStyleBatchSet.vue'
import DeCanvas from '@/views/canvas/DeCanvas.vue'
import { check, compareStorage } from '@/utils/CrossPermission'
import { useCache } from '@/hooks/web/useCache'
import { cloneDeep } from 'lodash-es'
import { useEmbedded } from '@/store/modules/embedded'
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
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'
const interactiveStore = interactiveStoreWithOut()
import { useRequestStoreWithOut } from '@/store/modules/request'
import { usePermissionStoreWithOut } from '@/store/modules/permission'
import eventBus from '@/utils/eventBus'
import { useI18n } from '@/hooks/web/useI18n'
import DashboardHiddenComponent from '@/components/dashboard/DashboardHiddenComponent.vue'
import { recoverToPublished } from '@/api/visualization/dataVisualization'
import SqlAssistant from '@/views/sqlbot/assistant.vue'
import { contextmenuStoreWithOut } from '@/store/modules/data-visualization/contextmenu'
const contextmenuStore = contextmenuStoreWithOut()
const embeddedStore = useEmbedded()
const { wsCache } = useCache()
const canvasCacheOutRef = ref(null)
const deCanvasRef = ref(null)
const eventCheck = e => {
if (e.key === 'panel-weight' && !compareStorage(e.oldValue, e.newValue)) {
const resourceId = embeddedStore.resourceId || router.currentRoute.value.query.resourceId
const opt = embeddedStore.opt || router.currentRoute.value.query.opt
if (!(opt && opt === 'create')) {
check(wsCache.get('panel-weight'), resourceId as string, 4)
}
}
}
const dvMainStore = dvMainStoreWithOut()
const snapshotStore = snapshotStoreWithOut()
const requestStore = useRequestStoreWithOut()
const permissionStore = usePermissionStoreWithOut()
const {
fullscreenFlag,
componentData,
curComponent,
canvasStyleData,
canvasViewInfo,
editMode,
batchOptStatus,
hiddenListStatus,
lastHiddenComponent,
dvInfo
} = storeToRefs(dvMainStore)
const dataInitState = ref(false)
const appStore = useAppStoreWithOut()
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
const { t } = useI18n()
const state = reactive({
datasetTree: [],
sourcePid: null,
canvasId: 'canvas-main',
opt: null,
resourceId: null
})
const initDataset = () => {
getDatasetTree({}).then(res => {
state.datasetTree = (res as unknown as Tree[]) || []
})
}
const otherEditorShow = computed(() => {
return Boolean(
curComponent.value &&
(!['UserView', 'VQuery'].includes(curComponent.value?.component) ||
(curComponent.value?.component === 'UserView' &&
curComponent.value?.innerType === 'picture-group')) &&
!batchOptStatus.value &&
!hiddenListStatus.value
)
})
const otherEditorTitle = computed(() => {
return curComponent.value?.component === 'UserView'
? t('visualization.attribute')
: curComponent.value?.label || t('visualization.attribute')
})
const viewEditorShow = computed(() => {
return Boolean(
curComponent.value &&
['UserView', 'VQuery'].includes(curComponent.value.component) &&
curComponent.value.innerType !== 'picture-group' &&
!batchOptStatus.value &&
!hiddenListStatus.value
)
})
const checkPer = async resourceId => {
if (!window.DataEaseBi || !resourceId) {
return true
}
const request = { busiFlag: 'dashboard', resourceTable: 'core' }
await interactiveStore.setInteractive(request)
return check(wsCache.get('panel-weight'), resourceId, 4)
}
const mobileConfig = ref(false)
const onMobileConfig = () => {
const canvasStyleDataCopy = cloneDeep(canvasStyleData.value)
if (!canvasStyleDataCopy.mobileSetting) {
canvasStyleDataCopy.mobileSetting = {
backgroundColorSelect: false,
background: '',
color: '#ffffff',
backgroundImageEnable: false,
customSetting: false
}
}
dvMainStore.setCanvasStyle(canvasStyleDataCopy)
nextTick(() => {
mobileConfig.value = true
dvMainStore.setCurComponent({ component: null, index: null })
})
}
const loadFinish = ref(false)
const newWindowFromDiv = ref(false)
let p = null
const XpackLoaded = () => p(true)
const doUseCache = flag => {
const canvasCache = wsCache.get('DE-DV-CATCH-' + state.resourceId)
if (flag && canvasCache) {
const canvasCacheSeries = deepCopy(canvasCache)
snapshotStore.snapshotPublish(canvasCacheSeries)
dataInitState.value = true
setTimeout(() => {
snapshotStore.recordSnapshotCache('doUseCache')
// 使用缓存时,初始化的保存按钮为激活状态
snapshotStore.recordSnapshotCache('renderChart')
}, 1500)
} else {
initLocalCanvasData()
wsCache.delete('DE-DV-CATCH-' + state.resourceId)
}
}
const initLocalCanvasData = callBack => {
const { resourceId, opt, sourcePid } = state
const busiFlag = opt === 'copy' ? 'dashboard-copy' : 'dashboard'
initCanvasData(
resourceId,
{ busiFlag, resourceTable: 'snapshot', source: 'main-edit' },
function () {
dataInitState.value = true
if (dvInfo.value && opt === 'copy') {
dvInfo.value.dataState = 'prepare'
dvInfo.value.optType = 'copy'
dvInfo.value.pid = sourcePid
setTimeout(() => {
snapshotStore.recordSnapshotCache('initLocalCanvasData')
}, 1500)
}
onInitReady({ resourceId: resourceId })
callBack && callBack()
}
)
}
onMounted(async () => {
document.body.style.overflow = 'hidden'
dvMainStore.setCurComponent({ component: null, index: null })
dvMainStore.setHiddenListStatus(false)
snapshotStore.initSnapShot()
contextmenuStore.hideContextMenu()
if (window.location.hash.includes('#/dashboard')) {
newWindowFromDiv.value = true
}
await new Promise(r => (p = r))
loadFinish.value = true
useEmitt({
name: 'mobileConfig',
callback: () => {
onMobileConfig()
}
})
window.addEventListener('storage', eventCheck)
window.addEventListener('message', winMsgHandle)
const resourceId = embeddedStore.resourceId || router.currentRoute.value.query.resourceId
const pid = embeddedStore.pid || router.currentRoute.value.query.pid
const opt = embeddedStore.opt || router.currentRoute.value.query.opt
const createType = embeddedStore.createType || router.currentRoute.value.query.createType
const templateParams =
embeddedStore.templateParams || router.currentRoute.value.query.templateParams
const checkResourceId = opt && opt === 'copy' ? null : resourceId
const checkResult = await checkPer(checkResourceId as string)
if (!checkResult) {
return
}
initDataset()
state.sourcePid = pid
state.opt = opt
state.resourceId = resourceId
if (resourceId) {
dataInitState.value = false
const canvasCache = wsCache.get('DE-DV-CATCH-' + resourceId)
if (canvasCache) {
canvasCacheOutRef.value?.dialogInit({ canvasType: 'dashboard', resourceId: resourceId })
} else {
initLocalCanvasData(() => {
// do init
})
}
} else if (opt && opt === 'create') {
dataInitState.value = 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
})
}
nextTick(() => {
dvMainStore.createInit('dashboard', null, pid, watermarkBaseInfo, preName)
// 从模板新建
if (createType === 'template') {
wsCache.delete('de-template-data')
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')
}
}
dataInitState.value = true
// preOpt
canvasStyleData.value.component.chartTitle.color = '#000000'
})
} else {
let url = '#/panel/index'
window.open(url, '_self')
}
})
// 目标校验: 需要校验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 dashboardComponentData = computed(() =>
componentData.value.filter(item => !item.dashboardHidden)
)
const cancelHidden = item => {
if (deCanvasRef.value) {
if (!(lastHiddenComponent.value?.length && lastHiddenComponent.value.includes(item.id))) {
item.y = undefined
}
deCanvasRef.value.addItemBox(item)
nextTick(() => {
deCanvasRef.value.canvasInit(false)
})
snapshotStore.recordSnapshotCache('cancelHidden')
}
}
const doRecoverToPublished = () => {
recoverToPublished({ id: dvInfo.value.id, type: 'dashboard', name: dvInfo.value.name }).then(
() => {
state.resourceId = dvInfo.value.id
state.sourcePid = dvInfo.value.pid
state.opt = null
initLocalCanvasData(() => {
nextTick(() => {
deCanvasRef.value.canvasInit(false)
dvMainStore.updateDvInfoCall(1)
useEmitt().emitter.emit('calcData-all')
})
})
}
)
}
onUnmounted(() => {
document.body.style.overflow = ''
window.removeEventListener('storage', eventCheck)
window.removeEventListener('message', winMsgHandle)
})
</script>
<template>
<div
class="dv-common-layout dv-teleport-query"
:class="isDataEaseBi && !newWindowFromDiv && 'dataease-w-h'"
v-loading="requestStore.loadingMap[permissionStore.currentPath]"
v-if="loadFinish && !mobileConfig"
>
<DbToolbar @recoverToPublished="doRecoverToPublished" />
<el-container
class="dv-layout-container"
:class="{ 'preview-content': editMode === 'preview' }"
element-loading-background="rgba(0, 0, 0, 0)"
>
<!-- <SqlAssistant></SqlAssistant>-->
<!-- 中间画布 -->
<main class="center" :class="{ 'de-screen-full': fullscreenFlag }">
<de-canvas
style="overflow-x: hidden"
v-if="dataInitState"
ref="deCanvasRef"
:canvas-id="state.canvasId"
:component-data="dashboardComponentData"
:canvas-style-data="canvasStyleData"
:canvas-view-info="canvasViewInfo"
:font-family="canvasStyleData.fontFamily"
></de-canvas>
</main>
<!-- 右侧侧组件列表 -->
<dv-sidebar
v-if="otherEditorShow"
:theme-info="'light'"
:title="otherEditorTitle"
:width="420"
:side-name="'componentProp'"
:aside-position="'right'"
:view="canvasViewInfo[curComponent.id]"
:element="curComponent"
class="left-sidebar"
>
<component :is="findComponentAttr(curComponent)" :themes="'light'" />
</dv-sidebar>
<dv-sidebar
v-show="!curComponent && !batchOptStatus && !hiddenListStatus"
:theme-info="'light'"
:title="t('visualization.dashboard_configuration')"
:width="420"
aside-position="right"
side-name="canvas"
class="left-sidebar"
>
<DbCanvasAttr></DbCanvasAttr>
</dv-sidebar>
<div v-show="viewEditorShow" style="height: 100%">
<view-editor
:themes="'light'"
:view="canvasViewInfo[curComponent ? curComponent.id : 'default']"
:dataset-tree="state.datasetTree"
></view-editor>
</div>
<dv-sidebar
v-if="batchOptStatus"
:theme-info="'light'"
:title="t('visualization.batch_style_set')"
:width="280"
aside-position="right"
class="left-sidebar"
:side-name="'batchOpt'"
>
<chart-style-batch-set></chart-style-batch-set>
</dv-sidebar>
<dv-sidebar
v-if="hiddenListStatus"
:theme-info="'light'"
:title="t('visualization.hidden_components')"
:width="280"
aside-position="right"
class="left-sidebar"
>
<DashboardHiddenComponent @cancel-hidden="cancelHidden"></DashboardHiddenComponent>
</dv-sidebar>
</el-container>
</div>
<MobileConfigPanel
@pcMode="mobileConfig = false"
v-else-if="loadFinish && mobileConfig"
></MobileConfigPanel>
<XpackComponent
jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvTmV3V2luZG93SGFuZGxlcg=="
@loaded="XpackLoaded"
@load-fail="XpackLoaded"
/>
<xpack-component jsname="L2NvbXBvbmVudC90aHJlc2hvbGQtd2FybmluZy9UaHJlc2hvbGREaWFsb2c=" />
<canvas-cache-dialog ref="canvasCacheOutRef" @doUseCache="doUseCache"></canvas-cache-dialog>
</template>
<style lang="less">
.dv-common-layout {
height: 100vh;
width: 100vw;
.dv-layout-container {
height: calc(100vh - @top-bar-height);
.left-sidebar {
height: 100%;
}
.center {
display: flex;
flex-direction: column;
height: 100%;
flex: 1;
position: relative;
overflow: auto;
.content {
flex: 1;
width: 100%;
.db-canvas {
padding: 2px;
background-size: 100% 100% !important;
overflow-y: auto;
width: 100%;
height: 100%;
}
}
}
.right-sidebar {
height: 100%;
}
}
&.dataease-w-h {
height: 100%;
width: 100%;
.dv-layout-container {
height: calc(100% - @top-bar-height);
}
}
}
.preview-aside {
border: 0px !important;
width: 0px !important;
overflow: hidden;
padding: 0px;
}
.preview-content {
:deep(.editor-light) {
border: 0 !important;
}
}
</style>