Pr@dev v3@feat transform (#17950)

* refactor: 样式调整

* feat: 数据大屏支持transform画布
This commit is contained in:
王嘉豪
2026-02-10 09:44:41 +08:00
committed by GitHub
parent a34a10cdea
commit 9db6355db1
9 changed files with 169 additions and 92 deletions

View File

@@ -33,6 +33,7 @@ import {
componentPreSort,
findDragComponent,
findNewComponent,
getTransformParams,
isDashboard,
isGroupOrTabCanvas,
isMainCanvas,
@@ -407,6 +408,7 @@ let snapshotTimer = ref(null)
// 根据需要需要扩充外部scroll区域也可以进行组合的功能 此方法变更为外部组件调用
const handleMouseDown = e => {
const transformParams = getTransformParams()
// 仪表板和预览状态不显示菜单和组创建
if (dashboardActive.value || editMode.value === 'preview') {
return
@@ -426,20 +428,20 @@ const handleMouseDown = e => {
const startX = e.clientX
const startY = e.clientY
start.value.x = startX - editorX.value
start.value.y = startY - editorY.value
start.value.x = (startX - editorX.value) * transformParams.tOffsetSpeed
start.value.y = (startY - editorY.value) * transformParams.tOffsetSpeed
// 展示选中区域
isShowArea.value = true
const move = moveEvent => {
width.value = Math.abs(moveEvent.clientX - startX)
height.value = Math.abs(moveEvent.clientY - startY)
width.value = Math.abs((moveEvent.clientX - startX) * transformParams.tOffsetSpeed)
height.value = Math.abs((moveEvent.clientY - startY) * transformParams.tOffsetSpeed)
if (moveEvent.clientX < startX) {
start.value.x = moveEvent.clientX - editorX.value
start.value.x = (moveEvent.clientX - editorX.value) * transformParams.tOffsetSpeed
}
if (moveEvent.clientY < startY) {
start.value.y = moveEvent.clientY - editorY.value
start.value.y = (moveEvent.clientY - editorY.value) * transformParams.tOffsetSpeed
}
}
@@ -577,8 +579,8 @@ const handleContextMenu = event => {
const offsetY = rect.top
// 计算鼠标相对于最外层 div 的坐标
const left = mouseX - offsetX
let top = mouseY - offsetY
const left = (mouseX - offsetX) / canvasStyleData.value.tScale
let top = (mouseY - offsetY) / canvasStyleData.value.tScale
// 组件处于编辑状态的时候 如富文本 不弹出右键菜单
if (!curComponent.value || (curComponent.value && !curComponent.value.editing)) {
if (

View File

@@ -122,6 +122,7 @@ const scaleWidthPoint = ref(100)
const scaleHeightPoint = ref(100)
const scaleMin = ref(100)
const previewCanvas = ref(null)
const previewCanvasInner = ref(null)
const cellWidth = ref(10)
const cellHeight = ref(10)
const userViewEnlargeRef = ref(null)
@@ -169,6 +170,24 @@ const baseComponentData = computed(() =>
(!ele?.dashboardHidden || (ele?.dashboardHidden && isMobile()))
)
)
const canvasStyleInner = computed(() => {
if (
dvInfo.value.type === 'dataV' &&
['keep', 'widthFirst', 'heightFirst'].includes(canvasStyleData.value?.screenAdaptor)
) {
const curScale = scaleMin.value / 100
return {
position: 'absolute',
height: 100 / curScale + '%!important',
width: 100 / curScale + '%!important',
left: 50 * (1 - 1 / curScale) + '%', // 放大余量 除以 2
top: 50 * (1 - 1 / curScale) + '%', // 放大余量 除以 2
transform: 'scale(' + curScale + ') translateZ(0)'
}
} else {
return {}
}
})
const canvasStyle = computed(() => {
let style = {}
if (isMainCanvas(canvasId.value) && !isDashboard()) {
@@ -290,13 +309,26 @@ const resetLayout = () => {
// 需要保持宽高比例时 高度伸缩和宽度伸缩保持一致 否则 高度伸缩单独计算
// tip 当当前画布是tab时 使用的事 outerScale.value 因为 canvasStyleData.value为 {} 此处取数逻辑需进一步优化
const scaleMinHeight = dataVKeepRadio.value ? scaleMin.value : scaleHeightPoint.value
changeRefComponentsSizeWithScalePoint(
baseComponentData.value,
canvasStyleData.value,
scaleMin.value || outerScale.value * 100,
scaleMinHeight || outerScale.value * 100,
outerScale.value * 100
)
if (
dvInfo.value.type === 'dataV' &&
['keep', 'widthFirst', 'heightFirst'].includes(canvasStyleData.value?.screenAdaptor)
) {
changeRefComponentsSizeWithScalePoint(
baseComponentData.value,
canvasStyleData.value,
100,
100,
100
)
} else {
changeRefComponentsSizeWithScalePoint(
baseComponentData.value,
canvasStyleData.value,
scaleMin.value || outerScale.value * 100,
scaleMinHeight || outerScale.value * 100,
outerScale.value * 100
)
}
scaleMin.value = isMainCanvas(canvasId.value) ? scaleMin.value : outerScale.value * 100
}
renderReady.value = true
@@ -514,71 +546,77 @@ defineExpose({
@scroll="scrollPreview"
v-if="state.initState"
>
<!--弹框触发区域-->
<canvas-filter-btn :is-fixed="isOverSize" v-if="filterBtnShow"></canvas-filter-btn>
<!-- 弹框区域 -->
<PopArea
v-if="popAreaAvailable"
:dv-info="dvInfo"
:canvas-id="canvasId"
:canvas-style-data="canvasStyleData"
:canvasViewInfo="canvasViewInfo"
:pop-component-data="popComponentData"
:scale="scaleMin"
:canvas-state="canvasState"
:show-position="'preview'"
></PopArea>
<canvas-opt-bar
v-if="showLinkageButton"
:canvas-id="canvasId"
:canvas-style-data="canvasStyleData"
:component-data="baseComponentData"
:is-fixed="isOverSize"
></canvas-opt-bar>
<template v-if="renderReady && !showUnpublishFlag">
<component-wrapper
v-for="(item, index) in baseComponentData"
v-show="item.isShow"
:active="item.id === (curComponent || {})['id']"
<div ref="previewCanvasInner" :style="canvasStyleInner">
<!--弹框触发区域-->
<canvas-filter-btn :is-fixed="isOverSize" v-if="filterBtnShow"></canvas-filter-btn>
<!-- 弹框区域 -->
<PopArea
v-if="popAreaAvailable"
:dv-info="dvInfo"
:canvas-id="canvasId"
:canvas-style-data="canvasStyleData"
:dv-info="dvInfo"
:canvas-view-info="canvasViewInfo"
:view-info="canvasViewInfo[item.id]"
:key="index"
:config="item"
:style="getShapeItemShowStyle(item)"
:show-position="showPosition"
:search-count="curSearchCount"
:scale="mobileInPc && isDashboard() ? 100 : scaleMin"
:is-selector="props.isSelector"
:font-family="canvasStyleData.fontFamily || fontFamily"
:scroll-main="state.scrollMain"
@userViewEnlargeOpen="userViewEnlargeOpen($event, item)"
@datasetParamsInit="datasetParamsInit(item)"
@onPointClick="onPointClick"
:index="index"
/>
</template>
<empty-background
v-if="showUnpublishFlag"
:description="t('visualization.resource_not_published')"
img-type="none"
>
</empty-background>
<user-view-enlarge ref="userViewEnlargeRef"></user-view-enlarge>
:canvasViewInfo="canvasViewInfo"
:pop-component-data="popComponentData"
:scale="scaleMin"
:canvas-state="canvasState"
:show-position="'preview'"
></PopArea>
<canvas-opt-bar
v-if="showLinkageButton"
:canvas-id="canvasId"
:canvas-style-data="canvasStyleData"
:component-data="baseComponentData"
:is-fixed="isFixedFlag"
></canvas-opt-bar>
<template v-if="renderReady && !showUnpublishFlag">
<component-wrapper
v-for="(item, index) in baseComponentData"
v-show="item.isShow"
:active="item.id === (curComponent || {})['id']"
:canvas-id="canvasId"
:canvas-style-data="canvasStyleData"
:dv-info="dvInfo"
:canvas-view-info="canvasViewInfo"
:view-info="canvasViewInfo[item.id]"
:key="index"
:config="item"
:style="getShapeItemShowStyle(item)"
:cur-style="getShapeItemShowStyle(item)"
:show-position="showPosition"
:search-count="curSearchCount"
:scale="mobileInPc && isDashboard() ? 100 : scaleMin"
:is-selector="props.isSelector"
:font-family="canvasStyleData.fontFamily || fontFamily"
:scroll-main="state.scrollMain"
@userViewEnlargeOpen="userViewEnlargeOpen($event, item)"
@datasetParamsInit="datasetParamsInit(item)"
@onPointClick="onPointClick"
:index="index"
/>
</template>
<empty-background
v-if="showUnpublishFlag"
:description="t('visualization.resource_not_published')"
img-type="none"
>
</empty-background>
<user-view-enlarge ref="userViewEnlargeRef"></user-view-enlarge>
</div>
<empty-background v-if="!state.initState" description="参数不能为空" img-type="noneWhite" />
<de-fullscreen ref="fullScreeRef"></de-fullscreen>
<dataset-params-component ref="customDatasetParamsRef"></dataset-params-component>
<XpackComponent
ref="openHandler"
jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvT3BlbkhhbmRsZXI="
/>
<link-opt-bar
v-if="linkOptBarShow"
ref="link-opt-bar"
:terminal="'pc'"
:canvas-style-data="canvasStyleData"
@link-export-pdf="downloadAsPDF"
/>
</div>
<empty-background v-if="!state.initState" description="参数不能为空" img-type="noneWhite" />
<de-fullscreen ref="fullScreeRef"></de-fullscreen>
<dataset-params-component ref="customDatasetParamsRef"></dataset-params-component>
<XpackComponent ref="openHandler" jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvT3BlbkhhbmRsZXI=" />
<link-opt-bar
v-if="linkOptBarShow"
ref="link-opt-bar"
:terminal="'pc'"
:canvas-style-data="canvasStyleData"
@link-export-pdf="downloadAsPDF"
/>
</template>
<style lang="less" scoped>

View File

@@ -148,6 +148,7 @@ import dvHidden from '@/assets/svg/dv-hidden.svg'
import { groupSizeStyleAdaptor, groupStyleRevert, tabInnerStyleRevert } from '@/utils/style'
import {
checkJoinTab,
getTransformParams,
isDashboard,
isGroupCanvas,
isMainCanvas,
@@ -553,6 +554,7 @@ const handleMouseDownOnShape = e => {
e.stopPropagation()
return
}
const { tOffsetX, tOffsetY, tOffsetSpeed } = getTransformParams()
dashboardActive.value && emit('onStartMove', e)
// 将当前点击组件的事件传播出去
nextTick(() => eventBus.emit('componentClick'))
@@ -569,8 +571,8 @@ const handleMouseDownOnShape = e => {
cursors.value = getCursor() // 根据旋转角度获取光标位置
const pos = { ...defaultStyle.value }
const startY = e.clientY
const startX = e.clientX
const startY = e.clientY - tOffsetY
const startX = e.clientX - tOffsetX
const offsetY = e.offsetY
const offsetX = e.offsetX
@@ -594,10 +596,10 @@ const handleMouseDownOnShape = e => {
const curDom = document.getElementById(domId.value)
const move = moveEvent => {
hasMove = true
const curX = moveEvent.clientX
const curY = moveEvent.clientY
const top = curY - startY + startTop
const left = curX - startX + startLeft
const curX = moveEvent.clientX - tOffsetX
const curY = moveEvent.clientY - tOffsetY
const top = (curY - startY) * tOffsetSpeed + startTop
const left = (curX - startX) * tOffsetSpeed + startLeft
pos['top'] = top
pos['left'] = left
// 非主画布非分组画布的情况 需要检测是否从Tab中移除组件(向左移除30px 或者向右移除30px 向左移除30px)
@@ -733,6 +735,7 @@ const handleMouseDownOnPoint = (point, e) => {
dvMainStore.setClickComponentStatus(true)
e.stopPropagation()
e.preventDefault()
const { tOffsetX, tOffsetY, tOffsetSpeed, tScale } = getTransformParams()
const style = { ...defaultStyle.value }
// 组件宽高比
@@ -754,14 +757,12 @@ const handleMouseDownOnPoint = (point, e) => {
// 当前点击圆点相对于画布的中心坐标
const curPoint = {
x: Math.round(
pointRect.left -
editorRectInfo.left +
(pointRect.left - editorRectInfo.left) * tOffsetSpeed +
e.target.offsetWidth / 2 +
offsetGapAdaptor('x', point) / 2
),
y: Math.round(
pointRect.top -
editorRectInfo.top +
(pointRect.top - editorRectInfo.top) * tOffsetSpeed +
e.target.offsetHeight / 2 +
offsetGapAdaptor('y', point) / 2
)
@@ -802,8 +803,12 @@ const handleMouseDownOnPoint = (point, e) => {
needSave = true
const curPosition = {
x: moveEvent.clientX - Math.round(editorRectInfo.left) + offsetGapAdaptor('x', point),
y: moveEvent.clientY - Math.round(editorRectInfo.top) + offsetGapAdaptor('y', point)
x:
(moveEvent.clientX - Math.round(editorRectInfo.left)) * tOffsetSpeed +
offsetGapAdaptor('x', point),
y:
(moveEvent.clientY - Math.round(editorRectInfo.top)) * tOffsetSpeed +
offsetGapAdaptor('y', point)
}
calculateComponentPositionAndSize(point, style, curPosition, proportion, needLockProportion, {
center,

View File

@@ -4,7 +4,7 @@ import { propTypes } from '@/utils/propTypes'
import { useEmitt } from '@/hooks/web/useEmitt'
const VisualizationEditor = defineAsyncComponent(
() => import('@/views/data-visualization/index.vue')
() => import('@/views/data-visualization/indexV3.vue')
)
const DashboardEditor = defineAsyncComponent(() => import('@/views/dashboard/index.vue'))

View File

@@ -46,7 +46,7 @@ export const routes: AppRouteRecordRaw[] = [
name: 'dvCanvas',
hidden: true,
meta: {},
component: () => import('@/views/data-visualization/index.vue')
component: () => import('@/views/data-visualization/indexV3.vue')
},
{
path: '/dashboard',

View File

@@ -1654,6 +1654,21 @@ export const dvMainStore = defineStore('dataVisualization', {
this.canvasStyleData = deepCopy(canvasStyleDataNew)
this.canvasStyleData.dvType = dvType
if (dvType === 'dataV') {
this.canvasStyleData.scale = 100
this.canvasStyleData.scaleWidth = 100
this.canvasStyleData.scaleHeight = 100
this.canvasStyleData.tScale = 0.6
this.canvasStyleData.tScaleWidth = 0.6
this.canvasStyleData.tScaleHeight = 0.6
} else {
this.canvasStyleData.scale = 60
this.canvasStyleData.scaleWidth = 60
this.canvasStyleData.scaleHeight = 60
this.canvasStyleData.tScale = 1
this.canvasStyleData.tScaleWidth = 1
this.canvasStyleData.tScaleHeight = 1
}
this.componentData = []
this.canvasViewInfo = {}
},

View File

@@ -300,6 +300,19 @@ export function historyAdaptor(
componentItem.canvasId = 'canvas-main'
})
}
canvasStyleResult['dvType'] = attachInfo.dvType
if (attachInfo.dvType === 'dataV') {
// 首次赋值
canvasStyleResult['tScale'] = canvasStyleResult['tScale'] || canvasStyleResult.scale / 100
canvasStyleResult['tScaleWidth'] =
canvasStyleResult['tScaleWidth'] || canvasStyleResult.scaleWidth / 100
canvasStyleResult['tScaleHeight'] =
canvasStyleResult['tScaleHeight'] || canvasStyleResult.scaleHeight / 100
} else {
canvasStyleResult['tScale'] = 1
canvasStyleResult['tScaleWidth'] = 1
canvasStyleResult['tScaleHeight'] = 1
}
const curVersion = wsCache.get('x-de-execute-version')
// 含有定时报告过滤项每次都需要匹配

View File

@@ -17,7 +17,7 @@ const { close } = useLoading()
const currentComponent = shallowRef()
const Preview = defineAsyncComponent(() => import('@/views/data-visualization/PreviewCanvas.vue'))
const VisualizationEditor = defineAsyncComponent(
() => import('@/views/data-visualization/index.vue')
() => import('@/views/data-visualization/indexV3.vue')
)
const DashboardEditor = defineAsyncComponent(() => import('@/views/dashboard/index.vue'))

View File

@@ -98,6 +98,10 @@ export const DEFAULT_CANVAS_STYLE_DATA_BASE = {
scale: 60,
scaleWidth: 60,
scaleHeight: 60,
dvType: 'dashboard', // screen
tScale: 0.6, // transformScale
tScaleWidth: 0.6,
tScaleHeight: 0.6,
backgroundColorSelect: true,
backgroundImageEnable: false,
backgroundType: 'backgroundColor', // 废弃