refactor: dashboard view

This commit is contained in:
wangjiahao
2025-06-19 08:46:46 +08:00
parent 26fc930a90
commit 76b757823e
17 changed files with 167 additions and 48 deletions
@@ -18,11 +18,11 @@ depends_on = None
def upgrade():
op.add_column('core_dashboard', sa.Column('canvas_views', sa.Text(), nullable=True))
op.add_column('core_dashboard', sa.Column('canvas_view_info', sa.Text(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('core_dashboard', 'canvas_views')
op.drop_column('core_dashboard', 'canvas_view_info')
# ### end Alembic commands ###
@@ -71,7 +71,7 @@ def create_canvas(session: SessionDep, user: CurrentUser, dashboard: CreateDashb
record.node_type = dashboard.node_type
record.component_data = dashboard.component_data
record.canvas_style_data = dashboard.canvas_style_data
record.canvas_views = dashboard.canvas_views
record.canvas_view_info = dashboard.canvas_view_info
session.add(record)
session.flush()
session.refresh(record)
@@ -86,7 +86,7 @@ def update_canvas(session: SessionDep, user: CurrentUser, dashboard: CreateDashb
record.update_time = int(time.time())
record.component_data = dashboard.component_data
record.canvas_style_data = dashboard.canvas_style_data
record.canvas_views = dashboard.canvas_views
record.canvas_view_info = dashboard.canvas_view_info
session.add(record)
session.commit()
return record
@@ -50,7 +50,7 @@ class CoreDashboard(SQLModel, table=True):
default=None,
sa_column=Column(Text, nullable=True)
)
canvas_views: str = Field(
canvas_view_info: str = Field(
default=None,
sa_column=Column(Text, nullable=True)
)
@@ -160,5 +160,5 @@ class QueryDashboard(BaseDashboard):
class CreateDashboard(QueryDashboard):
canvas_style_data: str =''
component_data: str = ''
canvas_views: str = ''
canvas_view_info: str = ''
description: str = ''
+3 -1
View File
@@ -18,6 +18,7 @@
"dependencies": {
"@antv/g2": "^5.3.3",
"@antv/s2": "^2.4.3",
"@eslint/js": "^9.28.0",
"@npkg/tinymce-plugins": "^0.0.7",
"@tinymce/tinymce-vue": "^5.1.0",
"dayjs": "^1.11.13",
@@ -27,7 +28,8 @@
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"snowflake-id": "^1.1.0",
"tinymce": "^5.8.2",
"tinymce": "^5.10.9",
"html2canvas": "^1.4.1",
"vue": "^3.5.13",
"vue-dompurify-html": "^5.3.0",
"vue-i18n": "^9.14.4",
+8
View File
@@ -137,3 +137,11 @@ body {
min-width: 780px !important;
flex-wrap: wrap !important;
}
.clone_img {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 100;
}
+2 -2
View File
@@ -41,9 +41,9 @@ export const useEmitt = (option?: Option) => {
* Debounced event emitter
* @param eventName - Name of the event to emit
* @param params - Parameters to pass with the event
* @param delay - Debounce delay in milliseconds (default: 300ms)
* @param delay - Debounce delay in milliseconds (default: 200ms)
*/
export const useEmittLazy = (eventName: string, params: any = null, delay = 500) => {
export const useEmittLazy = (eventName: string, params: any = null, delay = 150) => {
// If there's already a pending execution, skip this call
if (lazyDebounceMap.has(eventName)) {
const entry = lazyDebounceMap.get(eventName)!
@@ -6,7 +6,8 @@ import { type CanvasCoord, type CanvasItem } from '@/utils/canvas.ts'
import CanvasShape from './CanvasShape.vue'
import { findComponent } from '@/views/dashboard/components/component-list.ts'
import { storeToRefs } from 'pinia'
import { useEmittLazy } from '@/utils/useEmitt.ts'
import { useEmitt, useEmittLazy } from '@/utils/useEmitt.ts'
import html2canvas from 'html2canvas'
const dashboardStore = dashboardStoreWithOut()
const canvasLocked = ref(false) // Is the canvas movement locked, Default false
@@ -815,7 +816,6 @@ function startMove(e: MouseEvent, item: CanvasItem, index: number) {
infoBox.value.moveItem = item
infoBox.value.moveItemIndex = index
}
infoBox.value.cloneItem = null
infoBox.value.nowItemNode = null
// @ts-expect-error eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -829,6 +829,15 @@ function startMove(e: MouseEvent, item: CanvasItem, index: number) {
infoBox.value.cloneItem = infoBox.value.nowItemNode.cloneNode(true)
}
infoBox.value.cloneItem.classList.add('cloneNode')
const img = new Image()
img.classList.add('clone_img')
const clonedSlot = infoBox.value.nowItemNode.querySelector('.slot-component')
html2canvas(clonedSlot).then((canvas) => {
img.src = canvas.toDataURL()
infoBox.value.cloneItem.appendChild(img)
})
if (containerRef.value) {
containerRef.value.append(infoBox.value.cloneItem)
}
@@ -1130,6 +1139,11 @@ function tabMoveInCheckSQ() {
}
}
useEmitt({
name: `editor-delete-${props.canvasId}`,
callback: removeItemById,
})
onMounted(() => {
currentInstance = getCurrentInstance()
})
@@ -1171,12 +1185,13 @@ defineExpose({
:move-animate="moveAnimate"
:start-move="startMove"
:start-resize="startResize"
:canvas-id="canvasId"
:style="nowItemStyle(item)"
>
<component
:is="findComponent(item.component)"
:ref="'shape_component_' + item.id"
class="sql-component dragHandle"
class="sql-component slot-component dragHandle"
:config-item="item"
:view-info="canvasViewInfo[item.id]"
@parent-add-item-box="(subItem: any) => addItemBox(subItem)"
@@ -2,6 +2,8 @@
import { type CanvasItem } from '@/utils/canvas.ts'
import { ref, toRefs, type PropType } from 'vue'
import ResizeHandle from '@/views/dashboard/canvas/ResizeHandle.vue'
import DragHandle from '@/views/dashboard/canvas/DragHandle.vue'
import ComponentBar from '@/views/dashboard/canvas/ComponentBar.vue'
const shapeRef = ref(null)
// Props
@@ -38,6 +40,10 @@ const props = defineProps({
return {}
},
},
canvasId: {
type: String,
default: 'canvas-main',
},
})
const { draggable } = toRefs(props)
@@ -61,6 +67,13 @@ const shapeClick = (e: MouseEvent) => {
@click="shapeClick"
@mousedown="startMove($event, configItem, itemIndex)"
>
<component-bar
:config-item="configItem"
:active="active"
:show-position="'canvas'"
:canvas-id="canvasId"
></component-bar>
<drag-handle></drag-handle>
<slot></slot>
<resize-handle
v-if="active"
@@ -0,0 +1,69 @@
<script setup lang="ts">
import { toRefs } from 'vue'
import icon_delete from '@/assets/svg/icon_delete.svg'
import { Icon } from '@/components/icon-custom'
import { useEmitt } from '@/utils/useEmitt.ts'
const props = defineProps({
active: {
type: Boolean,
default: false,
},
configItem: {
type: Object,
required: true,
},
showPosition: {
required: false,
type: String,
default: 'preview',
},
canvasId: {
type: String,
default: 'canvas-main',
},
})
const { configItem } = toRefs(props)
const doDeleteComponent = (e: MouseEvent) => {
e.stopPropagation()
e.preventDefault()
useEmitt().emitter.emit(`editor-delete-${props.canvasId}`, configItem.value.id)
}
</script>
<template>
<div class="component-bar-main" :class="{ 'bar-hidden': !active }">
<div>
<el-icon class="handle-icon" @click="doDeleteComponent">
<Icon>
<icon_delete class="svg-icon"></icon_delete>
</Icon>
</el-icon>
</div>
</div>
</template>
<style scoped lang="less">
.component-bar-main {
height: 20px;
position: absolute;
right: 5px;
top: 5px;
display: flex;
z-index: 5;
cursor: pointer !important;
.handle-icon {
color: #646a73;
}
&:hover {
background-color: #e8f0fe;
color: var(--el-color-primary);
}
}
.bar-hidden {
display: none;
}
</style>
@@ -60,7 +60,7 @@ const shadowStyle = (size: number, index: number) => {
.dragHandle {
background: #c1e2e8;
position: absolute;
cursor: move;
cursor: move !important;
z-index: 3;
}
</style>
@@ -36,6 +36,7 @@
.sql-component {
width: 100%;
height: 100%;
overflow: hidden;
}
}
@@ -76,7 +77,7 @@
}
.slot-component {
display: none;
opacity: 0;
}
}
@@ -105,4 +106,4 @@
border: 1px solid;
}
}
}
@@ -36,6 +36,7 @@ const curSelectChange = (value: boolean) => {
position: relative;
padding: 15px;
float: left;
overflow: hidden;
.select-area {
position: absolute;
top: 20px;
@@ -272,6 +272,7 @@ const addChatChart = (views: any) => {
width: 300px;
text-align: left;
color: #a6a6a6;
display: none;
.opt-icon-redo {
margin-left: 12px;
@@ -13,17 +13,17 @@ import { initCanvasData } from '@/views/dashboard/utils/canvasUtils.ts'
const dashboardStore = dashboardStoreWithOut()
const { componentData, canvasViewInfo } = storeToRefs(dashboardStore)
const dataInitState = ref(false)
const dataInitState = ref(true)
const state = reactive({
routerPid: null,
resourceId: null,
opt: null,
})
const dashboardEditorRef = ref(null)
const dashboardEditorInnerRef = ref(null)
const addComponent = (componentType: string, viewInfo?: any) => {
const component = cloneDeep(findNewComponentFromList(componentType))
if (component && dashboardEditorRef.value) {
if (component && dashboardEditorInnerRef.value) {
component.id = guid()
// add view
if (component?.component === 'SQView' && !!viewInfo) {
@@ -38,7 +38,7 @@ const addComponent = (componentType: string, viewInfo?: any) => {
component.activeTabName = subTabName
}
// @ts-expect-error eslint-disable-next-line @typescript-eslint/ban-ts-comment
dashboardEditorRef.value.addItemToBox(component)
dashboardEditorInnerRef.value.addItemToBox(component)
}
}
@@ -77,7 +77,7 @@ const baseParams = computed(() => {
<Toolbar :base-params="baseParams" @add-component="addComponent"></Toolbar>
<DashboardEditor
v-if="dataInitState"
ref="dashboardEditorRef"
ref="dashboardEditorInnerRef"
:canvas-component-data="componentData"
:canvas-view-info="canvasViewInfo"
>
@@ -55,14 +55,13 @@ const viewDemoInnerId = computed(() => 'enlarge-inner-content-' + configItem.val
<style lang="less" scoped>
.wrapper-outer {
position: absolute;
overflow: hidden;
.wrapper-inner {
width: 100%;
height: 100%;
position: relative;
background: #fff;
background-size: 100% 100% !important;
.wrapper-inner-adaptor {
position: relative;
transform-style: preserve-3d;
@@ -9,6 +9,7 @@ import { dashboardStoreWithOut } from '@/stores/dashboard/dashboard.ts'
import { storeToRefs } from 'pinia'
import SQComponentWrapper from '@/views/dashboard/preview/SQComponentWrapper.vue'
import type { CanvasItem } from '@/utils/canvas.ts'
import { useEmittLazy } from '@/utils/useEmitt.ts'
const props = defineProps({
canvasStyleData: {
@@ -48,7 +49,7 @@ const props = defineProps({
},
})
const { canvasStyleData, componentData, showPosition, canvasId } = toRefs(props)
const { componentData, showPosition, canvasId } = toRefs(props)
const domId = 'preview-' + canvasId.value
const previewCanvas = ref(null)
const renderReady = ref(true)
@@ -93,6 +94,7 @@ const sizeInit = () => {
cellWidth.value = baseWidth.value + baseMarginLeft.value
cellHeight.value = baseHeight.value + baseMarginTop.value
}
useEmittLazy('view-render-all')
}
onMounted(() => {
@@ -120,7 +122,7 @@ defineExpose({
:key="index"
:active="!!curComponent && item.id === curComponent['id']"
:config-item="item"
:canvas-view-info="canvasStyleData"
:canvas-view-info="canvasViewInfo"
:show-position="showPosition"
:canvas-id="canvasId"
:style="nowItemStyle(item)"
@@ -3,37 +3,43 @@ import { dashboardStoreWithOut } from '@/stores/dashboard/dashboard.ts'
import { storeToRefs } from 'pinia'
const dashboardStore = dashboardStoreWithOut()
const { componentData, canvasStyleData } = storeToRefs(dashboardStore)
const { componentData, canvasStyleData, canvasViewInfo } = storeToRefs(dashboardStore)
const workspace_id = 'default' // temp
export const load_resource_prepare = (params: any, callBack: (obj: any) => void) => {
dashboardApi.load_resource(params).then((canvasInfo: any) => {
const dashboardInfo = {
id: canvasInfo.id,
name: canvasInfo.name,
pid: canvasInfo.pid,
workspaceId: canvasInfo.workspace_id,
status: canvasInfo.status,
type: canvasInfo.type,
createName: canvasInfo.create_name,
updateName: canvasInfo.update_name,
createTime: canvasInfo.create_time,
updateTime: canvasInfo.update_time,
contentId: canvasInfo.content_id,
}
const canvasDataResult = JSON.parse(canvasInfo.component_data)
const canvasStyleResult = JSON.parse(canvasInfo.canvas_style_data)
const canvasViewInfoPreview = JSON.parse(canvasInfo.canvas_view_Info || '{}')
callBack({ dashboardInfo, canvasDataResult, canvasStyleResult, canvasViewInfoPreview })
})
dashboardApi
.load_resource(params)
.then((canvasInfo: any) => {
const dashboardInfo = {
id: canvasInfo.id,
name: canvasInfo.name,
pid: canvasInfo.pid,
workspaceId: canvasInfo.workspace_id,
status: canvasInfo.status,
type: canvasInfo.type,
createName: canvasInfo.create_name,
updateName: canvasInfo.update_name,
createTime: canvasInfo.create_time,
updateTime: canvasInfo.update_time,
contentId: canvasInfo.content_id,
}
const canvasDataResult = JSON.parse(canvasInfo.component_data)
const canvasStyleResult = JSON.parse(canvasInfo.canvas_style_data)
const canvasViewInfoPreview = JSON.parse(canvasInfo.canvas_view_info || '{}')
callBack({ dashboardInfo, canvasDataResult, canvasStyleResult, canvasViewInfoPreview })
})
.catch((err) => {
console.error('load_resource_prepare', err)
callBack({})
})
}
export const initCanvasData = (params: any, callBack: () => void) => {
load_resource_prepare(params, function (result: any) {
dashboardStore.setDashboardInfo(result.dashboardInfo)
dashboardStore.setCanvasStyleData(result.canvasStyleResult)
dashboardStore.setComponentData(result.canvasDataResult)
dashboardStore.setCanvasViewInfo(result.canvasViewInfoPreview)
dashboardStore.setDashboardInfo(result?.dashboardInfo)
dashboardStore.setCanvasStyleData(result?.canvasStyleResult)
dashboardStore.setComponentData(result?.canvasDataResult)
dashboardStore.setCanvasViewInfo(result?.canvasViewInfoPreview)
callBack()
})
}
@@ -49,6 +55,7 @@ export const saveDashboardResource = (params: any, callBack: Function) => {
...params,
component_data: JSON.stringify(componentData.value),
canvas_style_data: JSON.stringify(canvasStyleData.value),
canvas_view_info: JSON.stringify(canvasViewInfo.value),
}
dashboardApi.create_canvas(requestParams).then((res: any) => {
dashboardStore.updateDashboardInfo({ id: res.id, dataState: 'ready' })
@@ -67,6 +74,7 @@ export const saveDashboardResource = (params: any, callBack: Function) => {
...params,
component_data: JSON.stringify(componentData.value),
canvas_style_data: JSON.stringify(canvasStyleData.value),
canvas_view_info: JSON.stringify(canvasViewInfo.value),
}
dashboardApi.update_canvas(requestParams).then(() => {
callBack()