Skip to content

Commit

Permalink
feat: enhance canvas foundation capabilities (opentiny#1055)
Browse files Browse the repository at this point in the history
  • Loading branch information
SonyLeo authored Feb 19, 2025
1 parent db1934d commit 8d51ad4
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 93 deletions.
63 changes: 34 additions & 29 deletions packages/canvas/DesignCanvas/src/api/useCanvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,57 +294,62 @@ const operationTypeMap = {
insert: (operation) => {
const { parentId, newNodeData, position, referTargetNodeId } = operation
const parentNode = getNode(parentId) || pageState.pageSchema

// 1. 确认是否存在 ParentNode
if (!parentNode) {
return {}
}

parentNode.children = parentNode.children || []

// 2. 确保 newNodeData 有唯一 ID, 如果没有,则生成新 ID
if (!newNodeData.id) {
newNodeData.id = utils.guid()
}

// 3. 查找参考节点
let referenceNode = null
if (referTargetNodeId) {
const referenceNode = getNode(referTargetNodeId)
let index = parentNode.children.indexOf(referenceNode)

if (index === -1) {
index = 0
}

index = position === 'before' ? index : index + 1

parentNode.children.splice(index, 0, newNodeData)

setNode(newNodeData, parentNode)

// 递归构建 nodeMap
if (Array.isArray(newNodeData?.children) && newNodeData.children.length) {
const newNode = getNode(newNodeData.id)
generateNodesMap(newNodeData.children, newNode)
referenceNode = getNode(referTargetNodeId)
if (!referenceNode) {
throw new Error(`Reference node with ID ${referTargetNodeId} not found`)
}
}

return {
current: newNodeData,
previous: undefined
}
// 4. 根据position参数选择插入位置
let index = parentNode.children.indexOf(referenceNode)
if (index === -1 && referTargetNodeId) {
index = parentNode.children.length
}

if (position === 'before') {
parentNode.children.unshift(newNodeData)
} else {
parentNode.children.push(newNodeData)
// 5. 插入节点的逻辑
const childrenNode = toRaw(referenceNode)
switch (position) {
case 'before':
parentNode.children.unshift(newNodeData)
break
case 'out':
if (childrenNode) {
newNodeData.children = Array.isArray(childrenNode) ? [...childrenNode] : [childrenNode]
parentNode.children.splice(index, 1, newNodeData)
}
break
case 'bottom':
parentNode.children.splice(index + 1, 0, newNodeData)
break
default:
parentNode.children.push(newNodeData)
break
}

setNode(newNodeData, parentNode)

// 递归构建 nodeMap
if (Array.isArray(newNodeData?.children) && newNodeData.children.length) {
// 6. 如果新节点有子节点,递归构建 nodeMap
if (Array.isArray(newNodeData?.children) && newNodeData.children.length > 0) {
const newNode = getNode(newNodeData.id)
generateNodesMap(newNodeData.children, newNode)
}

// 7. 返回插入结果
return {
current: newNodeData,
previous: undefined
Expand Down Expand Up @@ -376,7 +381,7 @@ const operationTypeMap = {
const nodeItem = getNode(item.id)
nodesMap.value.delete(item.id)

if (Array.isArray(nodeItem.children) && nodeItem.children.length) {
if (Array.isArray(nodeItem?.children) && nodeItem?.children.length) {
children.push(...nodeItem.children)
}
})
Expand Down
90 changes: 74 additions & 16 deletions packages/canvas/container/src/CanvasContainer.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
<template>
<canvas-action
:hoverState="hoverState"
:inactiveHoverState="inactiveHoverState"
:selectState="selectState"
:lineState="lineState"
:windowGetClickEventTarget="target"
:resize="canvasState.type === 'absolute'"
@select-slot="selectSlot"
@setting="settingModel"
></canvas-action>
<div v-for="multiState in multiSelectedStates" :key="multiState.id">
<canvas-action
:hoverState="hoverState"
:inactiveHoverState="inactiveHoverState"
:selectState="multiStateLength > 1 ? multiState : selectState"
:lineState="lineState"
:windowGetClickEventTarget="target"
:resize="canvasState.type === 'absolute'"
:multiStateLength="multiStateLength"
@select-slot="selectSlot"
@setting="settingModel"
></canvas-action>
</div>
<canvas-router-jumper :hoverState="hoverState" :inactiveHoverState="inactiveHoverState"></canvas-router-jumper>
<canvas-viewer-switcher :hoverState="hoverState" :inactiveHoverState="inactiveHoverState"></canvas-viewer-switcher>
<canvas-divider :selectState="selectState"></canvas-divider>
Expand All @@ -29,14 +32,23 @@
<div v-if="insertPosition" ref="insertPanel" class="insert-panel">
<component :is="materialsPanel" :shortcut="insertPosition" @close="insertPosition = false"></component>
</div>
<!-- 【添加父级容器】快捷选择物料面板 -->
<div v-if="insertContainer" ref="containerPanel" class="insert-panel">
<component
:is="materialsPanel"
:shortcut="insertContainer"
groupName="layout"
@close="insertContainer = false"
></component>
</div>
</template>

<script>
import { onMounted, ref, computed, onUnmounted, watch, watchEffect } from 'vue'
import { iframeMonitoring } from '@opentiny/tiny-engine-common/js/monitor'
import { useTranslate, useCanvas, useMessage, useResource } from '@opentiny/tiny-engine-meta-register'
import { NODE_UID, NODE_LOOP, DESIGN_MODE } from '../../common'
import { registerHostkeyEvent, removeHostkeyEvent } from './keyboard'
import { registerHotkeyEvent, removeHotkeyEvent, multiSelectedStates } from './keyboard'
import CanvasMenu, { closeMenu, openMenu } from './components/CanvasMenu.vue'
import CanvasAction from './components/CanvasAction.vue'
import CanvasRouterJumper from './components/CanvasRouterJumper.vue'
Expand All @@ -62,7 +74,10 @@ import {
clearLineState,
querySelectById,
getCurrent,
canvasApi
canvasApi,
getMultiState,
setMultiState,
handleMultiState
} from './container'
export default {
Expand Down Expand Up @@ -91,7 +106,12 @@ export default {
let target = ref(null)
const srcAttrName = computed(() => (props.canvasSrc ? 'src' : 'srcdoc'))
const setCurrentNode = async (event) => {
const containerPanel = ref(null)
const insertContainer = ref(false)
const multiStateLength = computed(() => multiSelectedStates.value.length)
const setCurrentNode = async (event, doc = null) => {
const { clientX, clientY } = event
const element = getElement(event.target)
closeMenu()
Expand All @@ -101,6 +121,9 @@ export default {
const currentElement = querySelectById(getCurrent().schema?.id)
if (!currentElement?.contains(element) || event.button === 0) {
const selectedState = getMultiState(element, doc)
setMultiState(multiSelectedStates, selectedState)
const loopId = element.getAttribute(NODE_LOOP)
if (loopId) {
node = await selectNode(element.getAttribute(NODE_UID), `loop-id=${loopId}`)
Expand Down Expand Up @@ -182,8 +205,23 @@ export default {
return
}
const element = getElement(event.target)
if (!element) {
return
}
// 多选组合键触发
if (element) {
const selectedState = getMultiState(element, doc)
if ((event.ctrlKey || event.metaKey) && event.button === 0) {
handleMultiState(multiSelectedStates, selectedState)
return
}
}
insertPosition.value = false
setCurrentNode(event)
insertContainer.value = false
setCurrentNode(event, doc)
target.value = event.target
})
})
Expand All @@ -198,6 +236,7 @@ export default {
}
insertPosition.value = false
insertContainer.value = false
setCurrentNode(event)
target.value = event.target
})
Expand All @@ -224,7 +263,7 @@ export default {
e.preventDefault()
}
registerHostkeyEvent(doc)
registerHotkeyEvent(doc)
win.addEventListener('scroll', updateRect, true)
}
Expand All @@ -247,6 +286,7 @@ export default {
// 以下是外部window需要监听的事件
window.addEventListener('mousedown', (e) => {
insertPosition.value = insertPanel.value?.contains(e.target)
insertContainer.value = containerPanel.value?.contains(e.target)
target.value = e.target
})
Expand All @@ -259,17 +299,31 @@ export default {
}
const insertComponent = (position) => {
if (position === 'out') {
insertContainer.value = position
return
}
insertPosition.value = position
}
const selectSlot = (slotName) => {
hoverState.slot = slotName
}
watch(
() => multiStateLength.value,
(newVal) => {
if (newVal > 1) {
// 清空属性面板
selectNode(null)
}
}
)
onMounted(() => run(iframe))
onUnmounted(() => {
if (iframe.value?.contentDocument) {
removeHostkeyEvent(iframe.value.contentDocument)
removeHotkeyEvent(iframe.value.contentDocument)
}
window.removeEventListener('message', updateI18n, false)
})
Expand All @@ -284,15 +338,19 @@ export default {
inactiveHoverState,
selectState,
lineState,
multiSelectedStates,
multiStateLength,
removeNodeById,
selectSlot,
canvasState,
insertComponent,
insertPanel,
containerPanel,
settingModel,
target,
showSettingModel,
insertPosition,
insertContainer,
loading,
srcAttrName
}
Expand Down
Loading

0 comments on commit 8d51ad4

Please sign in to comment.