Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: enhance canvas foundation capabilities #1055

Open
wants to merge 5 commits into
base: refactor/develop
Choose a base branch
from
Open
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
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 @@ -261,57 +261,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 @@ -343,7 +348,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-divider :selectState="selectState"></canvas-divider>
<canvas-resize-border :iframe="iframe"></canvas-resize-border>
Expand All @@ -28,14 +31,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 @@ -60,7 +72,10 @@ import {
clearLineState,
querySelectById,
getCurrent,
canvasApi
canvasApi,
getMultiState,
setMultiState,
handleMultiState
} from './container'

export default {
Expand All @@ -81,7 +96,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 @@ -91,6 +111,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 @@ -172,8 +195,23 @@ export default {
return
}

const element = getElement(event.target)
if (!element) {
return
}

// 多选组合键触发
if (element) {
const selectedState = getMultiState(element, doc)
if (event.ctrlKey && event.button === 0) {
handleMultiState(multiSelectedStates, selectedState)
return
}
}

insertPosition.value = false
setCurrentNode(event)
insertContainer.value = false
setCurrentNode(event, doc)
target.value = event.target
})
})
Expand All @@ -188,6 +226,7 @@ export default {
}

insertPosition.value = false
insertContainer.value = false
setCurrentNode(event)
target.value = event.target
})
Expand All @@ -214,7 +253,7 @@ export default {
e.preventDefault()
}

registerHostkeyEvent(doc)
registerHotkeyEvent(doc)

win.addEventListener('scroll', updateRect, true)
}
Expand All @@ -237,6 +276,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 @@ -249,17 +289,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 @@ -274,15 +328,19 @@ export default {
inactiveHoverState,
selectState,
lineState,
multiSelectedStates,
multiStateLength,
removeNodeById,
selectSlot,
canvasState,
insertComponent,
insertPanel,
containerPanel,
settingModel,
target,
showSettingModel,
insertPosition,
insertContainer,
loading,
srcAttrName
}
Expand Down
Loading
Loading