Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 150 additions & 21 deletions packages/canvas/container/src/CanvasContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@
:windowGetClickEventTarget="target"
:resize="canvasState.type === 'absolute'"
:multiStateLength="multiStateLength"
:isMultiDragging="isMultiDragging"
@select-slot="selectSlot"
@setting="settingModel"
></canvas-action>
</div>
<canvas-multi-drag-indicator
:lineState="lineState"
:multiDragState="multiDragState"
:multiStateLength="multiStateLength"
:isMultiDragging="isMultiDragging"
:getMultiDragPositionText="getMultiDragPositionText"
></canvas-multi-drag-indicator>
<canvas-router-jumper :hoverState="hoverState" :inactiveHoverState="inactiveHoverState"></canvas-router-jumper>
<canvas-viewer-switcher :hoverState="hoverState" :inactiveHoverState="inactiveHoverState"></canvas-viewer-switcher>
<canvas-divider :selectState="computedSelectState"></canvas-divider>
Expand Down Expand Up @@ -62,7 +70,9 @@ import CanvasViewerSwitcher from './components/CanvasViewerSwitcher.vue'
import CanvasResize from './components/CanvasResize.vue'
import CanvasDivider from './components/CanvasDivider.vue'
import CanvasResizeBorder from './components/CanvasResizeBorder.vue'
import CanvasMultiDragIndicator from './components/CanvasMultiDragIndicator.vue'
import { useMultiSelect } from './composables/useMultiSelect'
import { useMultiDrag } from './composables/useMultiDrag'
import {
canvasState,
onMouseUp,
Expand Down Expand Up @@ -92,7 +102,8 @@ export default {
CanvasDivider,
CanvasResizeBorder,
CanvasRouterJumper,
CanvasViewerSwitcher
CanvasViewerSwitcher,
CanvasMultiDragIndicator
},
props: {
controller: Object,
Expand All @@ -113,9 +124,30 @@ export default {
const containerPanel = ref(null)
const insertContainer = ref(false)

const { multiSelectedStates } = useMultiSelect()
const DRAG_TYPE = {
// 无拖拽
NONE: 'none',
// 单选拖拽
SINGLE: 'single',
// 多选拖拽
MULTI: 'multi'
}

// 当前拖拽类型状态
const currentDragType = ref(DRAG_TYPE.NONE)

const { multiSelectedStates, isMouseDown } = useMultiSelect()

const multiStateLength = computed(() => multiSelectedStates.value.length)
const {
startMultiDrag,
moveMultiDrag,
endMultiDrag,
isMultiDragging,
getMultiDragPositionText,
multiDragState,
cleanupDragState
} = useMultiDrag()

const computedSelectState = computed(() => {
if (multiSelectedStates.value.length === 1) {
Expand All @@ -125,13 +157,37 @@ export default {
return initialRectState
})

// 强制清除所有拖拽指示状态
const clearAllDragStates = () => {
clearLineState()
cleanupDragState()
currentDragType.value = DRAG_TYPE.NONE
}

const setCurrentNode = async (event) => {
const { clientX, clientY } = event
const element = getElement(event.target)
closeMenu()

if (!element) return

// 优先处理右键菜单
if (event.button === 2) {
openMenu(event)
return
}

let node = getCurrent().schema

if (element) {
// 首先尝试处理多选拖拽开始
if (startMultiDrag(event, element)) {
// 设置为多选拖拽状态
currentDragType.value = DRAG_TYPE.MULTI
return
}

// 只有当不是多选拖拽的情况下,才进行选择操作
const currentElement = querySelectById(getCurrent().schema?.id)

// 如果是点击右键则打开右键菜单
Expand All @@ -150,10 +206,14 @@ export default {
}
}

// 处理单节点拖拽开始
if (event.button === 0 && element !== element.ownerDocument.body) {
const { x, y } = element.getBoundingClientRect()

dragStart(node, element, { offsetX: clientX - x, offsetY: clientY - y })
if (multiStateLength.value === 1) {
dragStart(node, element, { offsetX: clientX - x, offsetY: clientY - y })
// 设置为单选拖拽状态
currentDragType.value = DRAG_TYPE.SINGLE
}
}
}
}
Expand Down Expand Up @@ -207,9 +267,10 @@ export default {
const doc = iframe.value.contentDocument
const win = iframe.value.contentWindow

// eslint-disable-next-line @typescript-eslint/no-unused-vars
let isScrolling = false

// 以下是内部iframe监听的事件
// 监听鼠标按下事件
win.addEventListener('mousedown', (event) => {
handleCanvasEvent(() => {
// html元素使用scroll和mouseup事件处理
Expand All @@ -223,6 +284,10 @@ export default {
return
}

isMouseDown.value = true
// 重置拖拽状态
currentDragType.value = DRAG_TYPE.NONE

insertPosition.value = false
insertContainer.value = false
setCurrentNode(event)
Expand All @@ -236,32 +301,92 @@ export default {
isScrolling = true
})

win.addEventListener('mouseup', (event) => {
if (event.target !== doc.documentElement || isScrolling) {
return
}
// 监听鼠标移动事件
win.addEventListener('mousemove', (ev) => {
handleCanvasEvent(() => {
// 根据当前拖拽类型执行相应操作
switch (currentDragType.value) {
case DRAG_TYPE.MULTI:
moveMultiDrag(ev)
break
case DRAG_TYPE.SINGLE:
dragMove(ev, true)
break
case DRAG_TYPE.NONE:
// 如果尚未确定拖拽类型,尝试确定
if (isMouseDown.value) {
if (multiDragState.keydown) {
currentDragType.value = DRAG_TYPE.MULTI
moveMultiDrag(ev)
} else if (dragState.element) {
currentDragType.value = DRAG_TYPE.SINGLE
dragMove(ev, true)
}
}
break
}
})
})

// 监听拖拽结束事件
win.addEventListener('mouseup', (ev) => {
handleCanvasEvent(() => {
if (ev.button === 0 && isMouseDown.value) {
isMouseDown.value = false

// 判断是否需要切换到单选状态
// 只有当点击多选节点但没有拖动时,才需要切换到单选状态
if (multiDragState.keydown && !multiDragState.dragStarted && multiStateLength.value > 1) {
const element = getElement(ev.target)
if (element) {
const clickedNodeId = element?.getAttribute(NODE_UID)
// 只有点击的是多选节点中的一个时才切换到单选
if (clickedNodeId && multiSelectedStates.value.some((state) => state.id === clickedNodeId)) {
selectNode(clickedNodeId)
}
}
}
}

// 根据当前拖拽类型执行相应的结束操作
switch (currentDragType.value) {
case DRAG_TYPE.MULTI:
endMultiDrag()
break
case DRAG_TYPE.SINGLE:
onMouseUp(ev)
break
}

insertPosition.value = false
insertContainer.value = false
setCurrentNode(event)
target.value = event.target
clearAllDragStates()
})
})

// 监听拖拽过程事件
win.addEventListener('dragover', (ev) => {
ev.dataTransfer.dropEffect = 'move'
ev.preventDefault()
dragMove(ev)

// 根据当前拖拽类型执行相应操作
if (currentDragType.value === DRAG_TYPE.MULTI) {
moveMultiDrag(ev)
} else {
dragMove(ev)
}
})

// 监听放置事件
win.addEventListener('drop', (ev) => {
ev.preventDefault()
onMouseUp(ev)
})

win.addEventListener('mousemove', (ev) => {
handleCanvasEvent(() => {
dragMove(ev, true)
})
// 根据当前拖拽类型执行相应的结束操作
if (currentDragType.value === DRAG_TYPE.MULTI) {
endMultiDrag()
} else {
onMouseUp(ev)
}

clearAllDragStates()
})

// 阻止浏览器默认的右键菜单功能
Expand Down Expand Up @@ -328,6 +453,7 @@ export default {
document.addEventListener('canvasReady', canvasReady)

return {
isMouseDown,
iframe,
dragState,
hoverState,
Expand All @@ -348,7 +474,10 @@ export default {
insertPosition,
insertContainer,
loading,
srcAttrName
srcAttrName,
isMultiDragging,
multiDragState,
getMultiDragPositionText
}
}
}
Expand Down
58 changes: 57 additions & 1 deletion packages/canvas/container/src/components/CanvasAction.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div
v-show="selectState.height && selectState.width"
class="canvas-rect select"
:class="['canvas-rect select', { 'multi-select': multiStateLength > 1 }, { dragging: isMultiDragging }]"
:style="{
top: selectState.top + 'px',
left: selectState.left + 'px',
Expand Down Expand Up @@ -189,6 +189,10 @@ export default {
type: Boolean,
default: false
},
isMultiDragging: {
type: Boolean,
default: false
},
windowGetClickEventTarget: Object
},
emits: ['remove', 'selectSlot', 'setting'],
Expand Down Expand Up @@ -716,7 +720,59 @@ export default {
}
}
}

&.multi-select {
border-color: var(--te-canvas-container-border-color-checked);
border-style: solid;
border-width: 2px;

.corner-mark-left {
background-color: var(--te-canvas-container-bg-color-checked);
color: var(--te-canvas-container-text-color-white);
}

&.dragging {
opacity: 0.7;
border-color: var(--te-canvas-container-border-color-multi, #1890ff);
border-style: dashed;
border-width: 2px;
background-color: rgba(24, 144, 255, 0.15);
box-shadow: 0 0 12px rgba(24, 144, 255, 0.4);
transition: all 0.2s ease;
animation: pulse-border 1.5s infinite;

.corner-mark-left {
background-color: var(--te-canvas-container-border-color-multi, #1890ff);
animation: pulse-bg 1.5s infinite;
}
}
}
}

@keyframes pulse-border {
0% {
box-shadow: 0 0 0 0 rgba(24, 144, 255, 0.4);
}
70% {
box-shadow: 0 0 0 6px rgba(24, 144, 255, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(24, 144, 255, 0);
}
}

@keyframes pulse-bg {
0% {
background-color: rgba(24, 144, 255, 1);
}
50% {
background-color: rgba(24, 144, 255, 0.7);
}
100% {
background-color: rgba(24, 144, 255, 1);
}
}

.short-cut-set.short-cut-set.tiny-popper.tiny-popover {
.tiny-popover__title {
color: var(--te-canvas-container-text-color-primary);
Expand Down
Loading