fix: 修复画布行列容器组件中的分割工具位置错误的问题 (#458)

* fix: 修复画布行列容器组件中的分割工具位置错误的问题
This commit is contained in:
Gene 2024-05-16 20:40:47 -07:00 committed by GitHub
parent 6d9241ed6a
commit 0d36df07a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 90 additions and 80 deletions

View File

@ -117,6 +117,7 @@ import {
IconEyeclose IconEyeclose
} from '@opentiny/vue-icon' } from '@opentiny/vue-icon'
import { import {
canvasState,
getCurrent, getCurrent,
removeNodeById, removeNodeById,
selectNode, selectNode,
@ -379,15 +380,14 @@ export default {
} }
} }
const getStyleValues = (selectState, siteCanvasWidth, siteCanvasHeight) => { const getStyleValues = (selectState, canvasSize, labelWidth, optionWidth) => {
const { left, top, width, height, doc } = selectState const { left, top, width, height, doc } = selectState
const labelRect = labelRef.value.getBoundingClientRect() const { width: canvasWidth, height: canvasHeight } = canvasSize
const optionRect = optionRef.value.getBoundingClientRect()
// //
const fullRectWidth = labelRect.width + optionRect.width + OPTION_SPACE const fullRectWidth = labelWidth + optionWidth + OPTION_SPACE
// label top // label top
const isLabelAtBottom = top <= LABEL_HEIGHT const isLabelAtBottom = top < LABEL_HEIGHT
const labelAlign = new Align({ const labelAlign = new Align({
alignLeft: true, alignLeft: true,
horizontalValue: 0, horizontalValue: 0,
@ -396,7 +396,7 @@ export default {
}) })
// //
const isOptionAtBottom = siteCanvasHeight - top - height > OPTION_BAR_HEIGHT const isOptionAtBottom = canvasHeight - top - height >= OPTION_BAR_HEIGHT
const optionAlign = new Align({ const optionAlign = new Align({
alignLeft: false, alignLeft: false,
horizontalValue: 0, horizontalValue: 0,
@ -410,13 +410,13 @@ export default {
// //
// labelabellabel // labelabellabel
const isLabelAlignRight = labelRect.width > width && left + labelRect.width + scrollBarWidth > siteCanvasWidth const isLabelAlignRight = labelWidth > width && left + labelWidth + scrollBarWidth > canvasWidth
if (isLabelAlignRight) { if (isLabelAlignRight) {
labelAlign.align(positions.RIGHT) labelAlign.align(positions.RIGHT)
} }
// optionoptionoption // optionoptionoption
const isOptionAlignLeft = optionRect.width > width && left + width - optionRect.width < 0 const isOptionAlignLeft = optionWidth > width && left + width - optionWidth < 0
if (isOptionAlignLeft) { if (isOptionAlignLeft) {
optionAlign.align(positions.LEFT) optionAlign.align(positions.LEFT)
} }
@ -424,14 +424,14 @@ export default {
if (isLabelAtBottom === isOptionAtBottom) { if (isLabelAtBottom === isOptionAtBottom) {
// //
if (left + fullRectWidth < siteCanvasWidth) { if (left + fullRectWidth < canvasWidth) {
// //
labelAlign.align(positions.LEFT) labelAlign.align(positions.LEFT)
optionAlign.align(positions.LEFT, labelRect.width + OPTION_SPACE) optionAlign.align(positions.LEFT, labelWidth + OPTION_SPACE)
} else { } else {
// //
optionAlign.align(positions.RIGHT) optionAlign.align(positions.RIGHT)
labelAlign.align(positions.RIGHT, optionRect.width + OPTION_SPACE) labelAlign.align(positions.RIGHT, optionWidth + OPTION_SPACE)
} }
} }
} else { } else {
@ -439,10 +439,10 @@ export default {
labelAlign.align(positions.LEFT, Math.min(-left, width - fullRectWidth)) labelAlign.align(positions.LEFT, Math.min(-left, width - fullRectWidth))
} }
if (left + width + scrollBarWidth > siteCanvasWidth) { if (left + width + scrollBarWidth > canvasWidth) {
optionAlign.align( optionAlign.align(
positions.RIGHT, positions.RIGHT,
Math.min(left + width + scrollBarWidth - siteCanvasWidth, width - fullRectWidth) Math.min(left + width + scrollBarWidth - canvasWidth, width - fullRectWidth)
) )
} }
} }
@ -454,8 +454,7 @@ export default {
} }
watchPostEffect(async () => { watchPostEffect(async () => {
let { left } = props.selectState const { left, top, width, height, doc } = props.selectState
const { top, width, height, doc } = props.selectState
// nextTickrefawait // nextTickrefawait
await nextTick() await nextTick()
@ -470,15 +469,17 @@ export default {
return return
} }
const siteCanvasRect = document.querySelector('.site-canvas').getBoundingClientRect()
const scale = useLayout().getScale() const scale = useLayout().getScale()
const canvasRect = canvasState.iframe.getBoundingClientRect()
const { width: labelWidth } = labelRef.value.getBoundingClientRect()
const { width: optionWidth } = optionRef.value.getBoundingClientRect()
left -= ((1 - scale) / 2) * siteCanvasRect.width // canvasiframeiframeclientRectscale
const { labelStyleValue, optionStyleValue } = getStyleValues( const { labelStyleValue, optionStyleValue } = getStyleValues(
{ left, top, width, height, doc }, { left, top, width, height, doc },
siteCanvasRect.width * scale, { width: canvasRect.width / scale, height: canvasRect.height / scale },
siteCanvasRect.height labelWidth / scale,
optionWidth / scale
) )
labelStyle.value = labelStyleValue labelStyle.value = labelStyleValue

View File

@ -50,7 +50,6 @@ import {
updateRect, updateRect,
getElement, getElement,
dragStart, dragStart,
getOffset,
selectNode, selectNode,
initCanvas, initCanvas,
clearLineState, clearLineState,
@ -101,7 +100,7 @@ export default {
// //
if (event.button === 2) { if (event.button === 2) {
openMenu(getOffset(event.target), event) openMenu(event)
} }
} }
} }

View File

@ -202,7 +202,7 @@ export default {
<style lang="less" scoped> <style lang="less" scoped>
.divider-wrapper { .divider-wrapper {
position: fixed; position: absolute;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -239,7 +239,7 @@ export default {
} }
.divider-line { .divider-line {
position: fixed; position: absolute;
border: 1px dashed var(--ti-lowcode-common-primary-color); border: 1px dashed var(--ti-lowcode-common-primary-color);
z-index: 2; z-index: 2;
} }

View File

@ -15,7 +15,7 @@
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
<span v-if="item.items"><icon-right></icon-right></span> <span v-if="item.items"><icon-right></icon-right></span>
</div> </div>
<ul v-if="item.items && current === item" class="sub-menu menu-item"> <ul v-if="item.items && current === item" class="sub-menu menu-item" :style="subMenuStyles">
<template v-for="(subItem, subIndex) in item.items" :key="subIndex"> <template v-for="(subItem, subIndex) in item.items" :key="subIndex">
<li <li
:class="[{ 'menu-item-disabled': subItem.check && !subItem.check?.() }]" :class="[{ 'menu-item-disabled': subItem.check && !subItem.check?.() }]"
@ -33,7 +33,7 @@
<script lang="jsx"> <script lang="jsx">
import { ref, reactive, nextTick } from 'vue' import { ref, reactive, nextTick } from 'vue'
import { getConfigure, getController, getCurrent, copyNode, removeNodeById } from './container' import { canvasState, getConfigure, getController, getCurrent, copyNode, removeNodeById } from './container'
import { useLayout, useModal, useCanvas } from '@opentiny/tiny-engine-controller' import { useLayout, useModal, useCanvas } from '@opentiny/tiny-engine-controller'
import { iconRight } from '@opentiny/vue-icon' import { iconRight } from '@opentiny/vue-icon'
@ -45,27 +45,35 @@ const menuState = reactive({
const current = ref(null) const current = ref(null)
const menuDom = ref(null) const menuDom = ref(null)
const subMenuStyles = ref(null)
export const closeMenu = () => { export const closeMenu = () => {
menuState.show = false menuState.show = false
current.value = null current.value = null
} }
export const openMenu = (offset, event) => { export const openMenu = (event) => {
const { x, y } = offset
const { getScale } = useLayout()
menuState.position = { menuState.position = {
// TODO left: event.clientX + 2 + 'px',
left: event.clientX * getScale() + x + 2 + 'px', top: event.clientY + 'px'
top: event.clientY * getScale() + y + 'px'
} }
menuState.show = sessionStorage.getItem('pageInfo') ? true : false menuState.show = sessionStorage.getItem('pageInfo') ? true : false
nextTick(() => { nextTick(() => {
if (menuDom.value) { if (menuDom.value) {
const { bottom, height, top } = menuDom.value.getBoundingClientRect() const { right, bottom, width, height } = menuDom.value.getBoundingClientRect()
if (bottom > document.body?.clientHeight) { const canvasRect = canvasState.iframe.getBoundingClientRect()
menuState.position.top = top - height + 'px' if (bottom > canvasRect.bottom) {
menuState.position.top = `${parseInt(menuState.position.top) - height}px`
}
if (right > canvasRect.right) {
menuState.position.left = `${parseInt(menuState.position.left) - width - 2}px`
}
// sub-menuwidth100px100
if (right + 100 < canvasRect.right) {
subMenuStyles.value = { right: '-100px' }
} else {
subMenuStyles.value = { left: '-100px' }
} }
} }
}) })
@ -226,6 +234,7 @@ export default {
close, close,
current, current,
menuDom, menuDom,
subMenuStyles,
actionDisabled, actionDisabled,
onShowChildrenMenu onShowChildrenMenu
} }
@ -235,7 +244,7 @@ export default {
<style lang="less" scoped> <style lang="less" scoped>
.context-menu { .context-menu {
position: fixed; position: absolute;
z-index: 10; z-index: 10;
} }
.menu-item { .menu-item {
@ -276,7 +285,6 @@ export default {
&.sub-menu { &.sub-menu {
width: 100px; width: 100px;
position: absolute; position: absolute;
right: -100px;
top: -2px; top: -2px;
} }
} }

View File

@ -17,13 +17,12 @@ import { canvasState } from './container'
export default { export default {
setup() { setup() {
const sizeStyle = computed(() => { const sizeStyle = computed(() => {
const { width, maxWidth, minWidth, scale } = useLayout().getDimension() const { width, height, maxWidth, minWidth } = useLayout().getDimension()
return { return {
width, width,
height,
maxWidth, maxWidth,
minWidth, minWidth
height: `${100 / scale}%`,
transform: `scale(${scale}) translateY(-${(100 / scale - 100) / 2}%)`
} }
}) })

View File

@ -8,6 +8,7 @@
<script> <script>
import { reactive, watch } from 'vue' import { reactive, watch } from 'vue'
import { useLayout } from '@opentiny/tiny-engine-controller'
import { getCurrent, updateRect, selectState, querySelectById } from './container' import { getCurrent, updateRect, selectState, querySelectById } from './container'
export default { export default {
@ -38,12 +39,17 @@ export default {
} }
const handleResize = (event, type) => { const handleResize = (event, type) => {
let { clientX, clientY } = event if (!props.iframe) {
return
}
if (type === 'iframe' && props.iframe) { let { clientX, clientY } = event
const iframeRect = props.iframe.getBoundingClientRect() const iframeRect = props.iframe.getBoundingClientRect()
clientX += iframeRect.left const scale = useLayout().getScale()
clientY += iframeRect.top
if (type !== 'iframe') {
clientX = (clientX - iframeRect.left) / scale
clientY = (clientY - iframeRect.top) / scale
} }
const { parent, schema } = getCurrent() const { parent, schema } = getCurrent()
@ -65,14 +71,9 @@ export default {
const parentWidth = parseInt(window.getComputedStyle(parentDomNode).width, 10) const parentWidth = parseInt(window.getComputedStyle(parentDomNode).width, 10)
// //
if (newWidth >= parentWidth) { newWidth = Math.min(newWidth, parentWidth)
newWidth = parentWidth
}
// 32 // 32
if (newWidth <= 32) { newWidth = Math.max(newWidth, 32)
newWidth = 32
}
schema.props.flexBasis = `${newWidth}px` schema.props.flexBasis = `${newWidth}px`
schema.props.widthType = 'fixed' schema.props.widthType = 'fixed'
@ -186,7 +187,7 @@ export default {
<style lang="less" scoped> <style lang="less" scoped>
.resize-border { .resize-border {
position: fixed; position: absolute;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;

View File

@ -348,16 +348,13 @@ const setSelectRect = (element) => {
element = element || getDocument().body element = element || getDocument().body
const { left, height, top, width } = getRect(element) const { left, height, top, width } = getRect(element)
const { x, y } = getOffset(element)
const siteCanvasRect = document.querySelector('.site-canvas').getBoundingClientRect()
const componentName = getCurrent().schema?.componentName || '' const componentName = getCurrent().schema?.componentName || ''
const scale = useLayout().getScale()
clearHover() clearHover()
Object.assign(selectState, { Object.assign(selectState, {
width: width * scale, width,
height: height * scale, height,
top: top * scale + y - siteCanvasRect.y, top,
left: left * scale + x - siteCanvasRect.x, left,
componentName, componentName,
doc: getDocument() doc: getDocument()
}) })
@ -481,9 +478,6 @@ const setHoverRect = (element, data) => {
const configure = getConfigure(componentName) const configure = getConfigure(componentName)
const rect = getRect(element) const rect = getRect(element)
const { left, height, top, width } = rect const { left, height, top, width } = rect
const { x, y } = getOffset(element)
const siteCanvasRect = document.querySelector('.site-canvas').getBoundingClientRect()
const scale = useLayout().getScale()
hoverState.configure = configure hoverState.configure = configure
@ -512,23 +506,22 @@ const setHoverRect = (element, data) => {
if (childEle) { if (childEle) {
const childRect = getRect(childEle) const childRect = getRect(childEle)
const { left, height, top, width } = childRect const { left, height, top, width } = childRect
const { x, y } = getOffset(childEle)
const posLine = getPosLine(childRect, lineState.configure) const posLine = getPosLine(childRect, lineState.configure)
Object.assign(lineState, { Object.assign(lineState, {
width: width * scale, width,
height: height * scale, height,
top: top * scale + y - siteCanvasRect.y, top,
left: left * scale + x - siteCanvasRect.x, left,
position: canvasState.type === 'absolute' || posLine.type, position: canvasState.type === 'absolute' || posLine.type,
forbidden: posLine.forbidden forbidden: posLine.forbidden
}) })
} else { } else {
const posLine = getPosLine(rect, configure) const posLine = getPosLine(rect, configure)
Object.assign(lineState, { Object.assign(lineState, {
width: width * scale, width,
height: height * scale, height,
top: top * scale + y - siteCanvasRect.y, top,
left: left * scale + x - siteCanvasRect.x, left,
position: canvasState.type === 'absolute' || posLine.type, position: canvasState.type === 'absolute' || posLine.type,
forbidden: posLine.forbidden forbidden: posLine.forbidden
}) })
@ -539,10 +532,10 @@ const setHoverRect = (element, data) => {
// 设置元素hover状态 // 设置元素hover状态
Object.assign(hoverState, { Object.assign(hoverState, {
width: width * scale, width,
height: height * scale, height,
top: top * scale + y - siteCanvasRect.y, top,
left: left * scale + x - siteCanvasRect.x, left,
componentName componentName
}) })
return undefined return undefined

View File

@ -1,6 +1,6 @@
<template> <template>
<div id="canvas-wrap" ref="canvasRef"> <div id="canvas-wrap" ref="canvasRef">
<div ref="siteCanvas" class="site-canvas"> <div ref="siteCanvas" class="site-canvas" :style="siteCanvasStyle">
<canvas-container <canvas-container
:controller="controller" :controller="controller"
:materials-panel="materialsPanel" :materials-panel="materialsPanel"
@ -14,7 +14,7 @@
</template> </template>
<script> <script>
import { ref, watch, onUnmounted } from 'vue' import { ref, watch, computed, onUnmounted } from 'vue'
import { CanvasContainer, CanvasFooter } from '@opentiny/tiny-engine-canvas' import { CanvasContainer, CanvasFooter } from '@opentiny/tiny-engine-canvas'
import { import {
useProperties, useProperties,
@ -60,6 +60,14 @@ export default {
pageState.properties = null pageState.properties = null
} }
const siteCanvasStyle = computed(() => {
const { scale } = useLayout().getDimension()
return {
height: `calc((100% - var(--base-bottom-panel-height, 30px) - 36px) / ${scale})`,
transform: `scale(${scale})`
}
})
watch( watch(
[() => useCanvas().isSaved(), () => useLayout().layoutState.pageStatus, () => useCanvas().getPageSchema()], [() => useCanvas().isSaved(), () => useLayout().layoutState.pageStatus, () => useCanvas().getPageSchema()],
([isSaved, pageStatus, pageSchema], [oldIsSaved, _oldPageStatus, oldPageSchema]) => { ([isSaved, pageStatus, pageSchema], [oldIsSaved, _oldPageStatus, oldPageSchema]) => {
@ -180,6 +188,7 @@ export default {
request: useHttp(), request: useHttp(),
ast ast
}, },
siteCanvasStyle,
canvasRef canvasRef
} }
} }
@ -196,11 +205,11 @@ export default {
position: relative; position: relative;
.site-canvas { .site-canvas {
height: calc(100% - var(--base-bottom-panel-height, 30px) - 36px);
background: var(--ti-lowcode-breadcrumb-hover-bg); background: var(--ti-lowcode-breadcrumb-hover-bg);
position: absolute; position: absolute;
overflow: hidden; overflow: hidden;
margin: 18px 0; margin: 18px 0;
transform-origin: top;
} }
} }
</style> </style>