forked from opentiny/tiny-engine
parent
6d9241ed6a
commit
0d36df07a7
|
@ -117,6 +117,7 @@ import {
|
|||
IconEyeclose
|
||||
} from '@opentiny/vue-icon'
|
||||
import {
|
||||
canvasState,
|
||||
getCurrent,
|
||||
removeNodeById,
|
||||
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 labelRect = labelRef.value.getBoundingClientRect()
|
||||
const optionRect = optionRef.value.getBoundingClientRect()
|
||||
const { width: canvasWidth, height: canvasHeight } = canvasSize
|
||||
// 标签宽度和工具操作条宽度之和加上间距
|
||||
const fullRectWidth = labelRect.width + optionRect.width + OPTION_SPACE
|
||||
const fullRectWidth = labelWidth + optionWidth + OPTION_SPACE
|
||||
|
||||
// 是否 将label 标签放置到底部,判断 top 距离
|
||||
const isLabelAtBottom = top <= LABEL_HEIGHT
|
||||
const isLabelAtBottom = top < LABEL_HEIGHT
|
||||
const labelAlign = new Align({
|
||||
alignLeft: true,
|
||||
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({
|
||||
alignLeft: false,
|
||||
horizontalValue: 0,
|
||||
|
@ -410,13 +410,13 @@ export default {
|
|||
// 选中框宽度小于标签宽度和工具操作条宽度之和加上间距
|
||||
|
||||
// 如果labe宽度大于选中框宽度,并且label右侧已经超出画布,则label对齐右侧
|
||||
const isLabelAlignRight = labelRect.width > width && left + labelRect.width + scrollBarWidth > siteCanvasWidth
|
||||
const isLabelAlignRight = labelWidth > width && left + labelWidth + scrollBarWidth > canvasWidth
|
||||
if (isLabelAlignRight) {
|
||||
labelAlign.align(positions.RIGHT)
|
||||
}
|
||||
|
||||
// 如果option宽度大于选中框宽度,并且option左侧已经超出画布,则option对齐左侧
|
||||
const isOptionAlignLeft = optionRect.width > width && left + width - optionRect.width < 0
|
||||
const isOptionAlignLeft = optionWidth > width && left + width - optionWidth < 0
|
||||
if (isOptionAlignLeft) {
|
||||
optionAlign.align(positions.LEFT)
|
||||
}
|
||||
|
@ -424,14 +424,14 @@ export default {
|
|||
if (isLabelAtBottom === isOptionAtBottom) {
|
||||
// 标签框和工具操作框都在顶部或者都在底部
|
||||
|
||||
if (left + fullRectWidth < siteCanvasWidth) {
|
||||
if (left + fullRectWidth < canvasWidth) {
|
||||
// 都放在左侧
|
||||
labelAlign.align(positions.LEFT)
|
||||
optionAlign.align(positions.LEFT, labelRect.width + OPTION_SPACE)
|
||||
optionAlign.align(positions.LEFT, labelWidth + OPTION_SPACE)
|
||||
} else {
|
||||
// 都放在右侧
|
||||
optionAlign.align(positions.RIGHT)
|
||||
labelAlign.align(positions.RIGHT, optionRect.width + OPTION_SPACE)
|
||||
labelAlign.align(positions.RIGHT, optionWidth + OPTION_SPACE)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -439,10 +439,10 @@ export default {
|
|||
labelAlign.align(positions.LEFT, Math.min(-left, width - fullRectWidth))
|
||||
}
|
||||
|
||||
if (left + width + scrollBarWidth > siteCanvasWidth) {
|
||||
if (left + width + scrollBarWidth > canvasWidth) {
|
||||
optionAlign.align(
|
||||
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 () => {
|
||||
let { left } = props.selectState
|
||||
const { top, width, height, doc } = props.selectState
|
||||
const { left, top, width, height, doc } = props.selectState
|
||||
|
||||
// nextTick后ref才能获取到元素。需要把监听的依赖放在await之前,否则无法监听变化
|
||||
await nextTick()
|
||||
|
@ -470,15 +469,17 @@ export default {
|
|||
return
|
||||
}
|
||||
|
||||
const siteCanvasRect = document.querySelector('.site-canvas').getBoundingClientRect()
|
||||
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
|
||||
|
||||
// canvas容器中,iframe以及iframe之外的元素clientRect的尺寸都是缩放过的,除以scale得到原始大小
|
||||
const { labelStyleValue, optionStyleValue } = getStyleValues(
|
||||
{ left, top, width, height, doc },
|
||||
siteCanvasRect.width * scale,
|
||||
siteCanvasRect.height
|
||||
{ width: canvasRect.width / scale, height: canvasRect.height / scale },
|
||||
labelWidth / scale,
|
||||
optionWidth / scale
|
||||
)
|
||||
|
||||
labelStyle.value = labelStyleValue
|
||||
|
|
|
@ -50,7 +50,6 @@ import {
|
|||
updateRect,
|
||||
getElement,
|
||||
dragStart,
|
||||
getOffset,
|
||||
selectNode,
|
||||
initCanvas,
|
||||
clearLineState,
|
||||
|
@ -101,7 +100,7 @@ export default {
|
|||
|
||||
// 如果是点击右键则打开右键菜单
|
||||
if (event.button === 2) {
|
||||
openMenu(getOffset(event.target), event)
|
||||
openMenu(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -202,7 +202,7 @@ export default {
|
|||
|
||||
<style lang="less" scoped>
|
||||
.divider-wrapper {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
@ -239,7 +239,7 @@ export default {
|
|||
}
|
||||
|
||||
.divider-line {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
border: 1px dashed var(--ti-lowcode-common-primary-color);
|
||||
z-index: 2;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<span>{{ item.name }}</span>
|
||||
<span v-if="item.items"><icon-right></icon-right></span>
|
||||
</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">
|
||||
<li
|
||||
:class="[{ 'menu-item-disabled': subItem.check && !subItem.check?.() }]"
|
||||
|
@ -33,7 +33,7 @@
|
|||
|
||||
<script lang="jsx">
|
||||
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 { iconRight } from '@opentiny/vue-icon'
|
||||
|
||||
|
@ -45,27 +45,35 @@ const menuState = reactive({
|
|||
|
||||
const current = ref(null)
|
||||
const menuDom = ref(null)
|
||||
const subMenuStyles = ref(null)
|
||||
|
||||
export const closeMenu = () => {
|
||||
menuState.show = false
|
||||
current.value = null
|
||||
}
|
||||
|
||||
export const openMenu = (offset, event) => {
|
||||
const { x, y } = offset
|
||||
const { getScale } = useLayout()
|
||||
export const openMenu = (event) => {
|
||||
menuState.position = {
|
||||
// 位置处于画布右侧边缘时需要调整显示方向 TODO
|
||||
left: event.clientX * getScale() + x + 2 + 'px',
|
||||
top: event.clientY * getScale() + y + 'px'
|
||||
left: event.clientX + 2 + 'px',
|
||||
top: event.clientY + 'px'
|
||||
}
|
||||
menuState.show = sessionStorage.getItem('pageInfo') ? true : false
|
||||
|
||||
nextTick(() => {
|
||||
if (menuDom.value) {
|
||||
const { bottom, height, top } = menuDom.value.getBoundingClientRect()
|
||||
if (bottom > document.body?.clientHeight) {
|
||||
menuState.position.top = top - height + 'px'
|
||||
const { right, bottom, width, height } = menuDom.value.getBoundingClientRect()
|
||||
const canvasRect = canvasState.iframe.getBoundingClientRect()
|
||||
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-menu样式width为100px,少于100宽度的空白区域则放置到左侧
|
||||
if (right + 100 < canvasRect.right) {
|
||||
subMenuStyles.value = { right: '-100px' }
|
||||
} else {
|
||||
subMenuStyles.value = { left: '-100px' }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -226,6 +234,7 @@ export default {
|
|||
close,
|
||||
current,
|
||||
menuDom,
|
||||
subMenuStyles,
|
||||
actionDisabled,
|
||||
onShowChildrenMenu
|
||||
}
|
||||
|
@ -235,7 +244,7 @@ export default {
|
|||
|
||||
<style lang="less" scoped>
|
||||
.context-menu {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
.menu-item {
|
||||
|
@ -276,7 +285,6 @@ export default {
|
|||
&.sub-menu {
|
||||
width: 100px;
|
||||
position: absolute;
|
||||
right: -100px;
|
||||
top: -2px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,12 @@ import { canvasState } from './container'
|
|||
export default {
|
||||
setup() {
|
||||
const sizeStyle = computed(() => {
|
||||
const { width, maxWidth, minWidth, scale } = useLayout().getDimension()
|
||||
const { width, height, maxWidth, minWidth } = useLayout().getDimension()
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
maxWidth,
|
||||
minWidth,
|
||||
height: `${100 / scale}%`,
|
||||
transform: `scale(${scale}) translateY(-${(100 / scale - 100) / 2}%)`
|
||||
minWidth
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
<script>
|
||||
import { reactive, watch } from 'vue'
|
||||
import { useLayout } from '@opentiny/tiny-engine-controller'
|
||||
import { getCurrent, updateRect, selectState, querySelectById } from './container'
|
||||
|
||||
export default {
|
||||
|
@ -38,12 +39,17 @@ export default {
|
|||
}
|
||||
|
||||
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()
|
||||
clientX += iframeRect.left
|
||||
clientY += iframeRect.top
|
||||
const scale = useLayout().getScale()
|
||||
|
||||
if (type !== 'iframe') {
|
||||
clientX = (clientX - iframeRect.left) / scale
|
||||
clientY = (clientY - iframeRect.top) / scale
|
||||
}
|
||||
|
||||
const { parent, schema } = getCurrent()
|
||||
|
@ -65,14 +71,9 @@ export default {
|
|||
const parentWidth = parseInt(window.getComputedStyle(parentDomNode).width, 10)
|
||||
|
||||
// 最大宽度不能大于父组件宽度
|
||||
if (newWidth >= parentWidth) {
|
||||
newWidth = parentWidth
|
||||
}
|
||||
|
||||
newWidth = Math.min(newWidth, parentWidth)
|
||||
// 最小宽度32
|
||||
if (newWidth <= 32) {
|
||||
newWidth = 32
|
||||
}
|
||||
newWidth = Math.max(newWidth, 32)
|
||||
|
||||
schema.props.flexBasis = `${newWidth}px`
|
||||
schema.props.widthType = 'fixed'
|
||||
|
@ -186,7 +187,7 @@ export default {
|
|||
|
||||
<style lang="less" scoped>
|
||||
.resize-border {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
|
|
@ -348,16 +348,13 @@ const setSelectRect = (element) => {
|
|||
element = element || getDocument().body
|
||||
|
||||
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 scale = useLayout().getScale()
|
||||
clearHover()
|
||||
Object.assign(selectState, {
|
||||
width: width * scale,
|
||||
height: height * scale,
|
||||
top: top * scale + y - siteCanvasRect.y,
|
||||
left: left * scale + x - siteCanvasRect.x,
|
||||
width,
|
||||
height,
|
||||
top,
|
||||
left,
|
||||
componentName,
|
||||
doc: getDocument()
|
||||
})
|
||||
|
@ -481,9 +478,6 @@ const setHoverRect = (element, data) => {
|
|||
const configure = getConfigure(componentName)
|
||||
const rect = getRect(element)
|
||||
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
|
||||
|
||||
|
@ -512,23 +506,22 @@ const setHoverRect = (element, data) => {
|
|||
if (childEle) {
|
||||
const childRect = getRect(childEle)
|
||||
const { left, height, top, width } = childRect
|
||||
const { x, y } = getOffset(childEle)
|
||||
const posLine = getPosLine(childRect, lineState.configure)
|
||||
Object.assign(lineState, {
|
||||
width: width * scale,
|
||||
height: height * scale,
|
||||
top: top * scale + y - siteCanvasRect.y,
|
||||
left: left * scale + x - siteCanvasRect.x,
|
||||
width,
|
||||
height,
|
||||
top,
|
||||
left,
|
||||
position: canvasState.type === 'absolute' || posLine.type,
|
||||
forbidden: posLine.forbidden
|
||||
})
|
||||
} else {
|
||||
const posLine = getPosLine(rect, configure)
|
||||
Object.assign(lineState, {
|
||||
width: width * scale,
|
||||
height: height * scale,
|
||||
top: top * scale + y - siteCanvasRect.y,
|
||||
left: left * scale + x - siteCanvasRect.x,
|
||||
width,
|
||||
height,
|
||||
top,
|
||||
left,
|
||||
position: canvasState.type === 'absolute' || posLine.type,
|
||||
forbidden: posLine.forbidden
|
||||
})
|
||||
|
@ -539,10 +532,10 @@ const setHoverRect = (element, data) => {
|
|||
|
||||
// 设置元素hover状态
|
||||
Object.assign(hoverState, {
|
||||
width: width * scale,
|
||||
height: height * scale,
|
||||
top: top * scale + y - siteCanvasRect.y,
|
||||
left: left * scale + x - siteCanvasRect.x,
|
||||
width,
|
||||
height,
|
||||
top,
|
||||
left,
|
||||
componentName
|
||||
})
|
||||
return undefined
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div id="canvas-wrap" ref="canvasRef">
|
||||
<div ref="siteCanvas" class="site-canvas">
|
||||
<div ref="siteCanvas" class="site-canvas" :style="siteCanvasStyle">
|
||||
<canvas-container
|
||||
:controller="controller"
|
||||
:materials-panel="materialsPanel"
|
||||
|
@ -14,7 +14,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, watch, onUnmounted } from 'vue'
|
||||
import { ref, watch, computed, onUnmounted } from 'vue'
|
||||
import { CanvasContainer, CanvasFooter } from '@opentiny/tiny-engine-canvas'
|
||||
import {
|
||||
useProperties,
|
||||
|
@ -60,6 +60,14 @@ export default {
|
|||
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(
|
||||
[() => useCanvas().isSaved(), () => useLayout().layoutState.pageStatus, () => useCanvas().getPageSchema()],
|
||||
([isSaved, pageStatus, pageSchema], [oldIsSaved, _oldPageStatus, oldPageSchema]) => {
|
||||
|
@ -180,6 +188,7 @@ export default {
|
|||
request: useHttp(),
|
||||
ast
|
||||
},
|
||||
siteCanvasStyle,
|
||||
canvasRef
|
||||
}
|
||||
}
|
||||
|
@ -196,11 +205,11 @@ export default {
|
|||
position: relative;
|
||||
|
||||
.site-canvas {
|
||||
height: calc(100% - var(--base-bottom-panel-height, 30px) - 36px);
|
||||
background: var(--ti-lowcode-breadcrumb-hover-bg);
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
margin: 18px 0;
|
||||
transform-origin: top;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue