采用嵌套呈现方式处理

This commit is contained in:
zqm
2025-11-19 13:57:51 +08:00
parent 7acf65356f
commit 0e7207adce
5 changed files with 398 additions and 186 deletions

View File

@@ -43,84 +43,35 @@
/>
</div>
</Area>
<!-- 浮动区域直接渲染不使用额外的div包装 -->
<Area
<!-- 浮动区域使用Render组件统一渲染 -->
<Render
v-for="area in floatingAreas"
:key="area.id"
:id="area.id"
:title="area.title"
v-model:WindowState="area.WindowState"
:showTitleBar="true"
:width="area.width"
:height="area.height"
:left="area.WindowState !== '最大化' ? area.x : undefined"
:top="area.WindowState !== '最大化' ? area.y : undefined"
:style="area.WindowState !== '最大化' ? {
position: 'absolute',
zIndex: 1000,
background: 'rgba(255, 255, 255, 1)',
border: '1px solid #4f72b3'
} : {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
zIndex: 1000
}"
@close="onCloseFloatingArea(area.id)"
@update:position="onUpdatePosition(area.id, $event)"
:type="'area'"
:config="area"
:style="{ zIndex: area.zIndex || zIndexManager.getFloatingAreaZIndex(area.id) }"
@close="() => onCloseFloatingArea(area.id)"
@update:position="(position) => onUpdatePosition(area.id, position)"
@panelMaximizeSync="onPanelMaximizeSync"
@areaDragStart="onAreaDragStart(area.id, $event)"
@areaDragMove="onAreaDragMove(area.id, $event)"
@areaDragEnd="onAreaDragEnd(area.id, $event)"
>
<!-- 每个Area内渲染其包含的TabPages -->
<TabPage
v-for="tabPage in area.tabPages"
:key="tabPage.id"
:id="tabPage.id"
:title="tabPage.title"
:panels="tabPage.panels"
:tabPosition="'bottom'"
@tab-change="onTabChange"
@tab-close="onTabClose"
@tab-add="onTabAdd"
@tabDragStart="onTabDragStart(area.id, $event)"
@tabDragMove="onTabDragMove(area.id, $event)"
@tabDragEnd="onTabDragEnd"
@toggle-collapse="(panelId) => $emit('toggleCollapse', panelId)"
@maximize="(panelId) => onMaximize(panelId)"
@close="(panelId) => onClosePanel(area.id, panelId)"
@toggle-toolbar="(panelId) => $emit('toggleToolbar', panelId)"
@dragStart="onPanelDragStartFromTabPage(area.id, $event)"
@dragMove="onPanelDragMoveFromTabPage(area.id, $event)"
@dragEnd="onPanelDragEndFromTabPage"
@dragover="handleAreaDragOver"
@dragleave="handleAreaDragLeave"
>
<!-- 在TabPage内渲染其包含的Panels -->
<Panel
v-for="panel in tabPage.panels"
:key="panel.id"
:id="panel.id"
:title="panel.title"
:collapsed="panel.collapsed"
:toolbarExpanded="panel.toolbarExpanded"
:maximized="panel.maximized"
:content="panel.content"
@toggleCollapse="onToggleCollapse"
@maximize="(panelId) => $emit('maximize', panelId)"
@close="onClosePanel(area.id, panel.id)"
@toggleToolbar="onToggleToolbar"
@dragStart="onPanelDragStartFromTabPage(area.id, $event)"
@dragMove="onPanelDragMove(area.id, $event)"
@dragEnd="onPanelDragEnd"
@dragover="handleAreaDragOver"
@dragleave="handleAreaDragLeave"
/>
</TabPage>
</Area>
@areaDragStart="(event) => onAreaDragStart(area.id, event)"
@areaDragMove="(event) => onAreaDragMove(area.id, event)"
@areaDragEnd="(event) => onAreaDragEnd(area.id, event)"
@tab-change="onTabChange"
@tab-close="onTabClose"
@tab-add="onTabAdd"
@tabDragStart="(event) => onTabDragStart(area.id, event)"
@tabDragMove="(event) => onTabDragMove(area.id, event)"
@tabDragEnd="onTabDragEnd"
@toggleCollapse="(panelId) => $emit('toggleCollapse', panelId)"
@maximize="(panelId) => onMaximize(panelId)"
@closePanel="(panelId) => onClosePanel(area.id, panelId)"
@toggleToolbar="(panelId) => $emit('toggleToolbar', panelId)"
@dragStart="(event) => onPanelDragStartFromTabPage(area.id, event)"
@dragMove="(event) => onPanelDragMoveFromTabPage(area.id, event)"
@dragEnd="onPanelDragEndFromTabPage"
@dragover="handleAreaDragOver"
@dragleave="handleAreaDragLeave"
/>
</div>
</template>
@@ -131,6 +82,8 @@ import Panel from './Panel.vue';
import TabPage from './TabPage.vue';
import DockIndicator from './DockIndicator.vue';
import ResizeBar from './ResizeBar.vue';
import Render from './Render.vue';
import { Z_INDEX_LAYERS, zIndexManager } from './dockLayers.js';
// 定义组件可以发出的事件
const emit = defineEmits([
@@ -295,51 +248,69 @@ const addFloatingPanel = () => {
// 如果容器已渲染,计算居中位置
if (dockLayoutRef.value) {
const containerRect = dockLayoutRef.value.getBoundingClientRect()
const width = 300
const height = 250
const width = 280
const height = 200
x = Math.floor((containerRect.width - width) / 2)
y = Math.floor((containerRect.height - height) / 2)
}
// 1. 添加一个Area。使用Area的初始设置
// 获取当前ID并递增
const currentId = areaIdCounter++
const areaId = `floating-area-${currentId}`
// 使用z-index管理器为新浮动区域分配z-index
const newZIndex = zIndexManager.getFloatingAreaZIndex(areaId)
// 直接创建符合Render config的数据结构确保响应式同步
const newArea = {
id: `floating-area-${areaIdCounter++}`,
title: `浮动区域 ${areaIdCounter - 1}`,
id: areaId,
title: `浮动区域 ${currentId}`,
x: x,
y: y,
width: 300,
height: 250,
WindowState: '正常',
width: 280,
height: 200,
windowState: '正常',
showTitleBar: true,
// 2. 向Area添加一个TabPage。TabPage的初始设置为填充满父容器
tabPages: [
resizable: true,
draggable: true,
zIndex: newZIndex, // 使用z-index管理器分配的层级
// 使用Render期望的children结构
children: [
{
type: 'tabpage',
id: `tabpage-${currentId}-1`,
title: `标签页 1`,
tabPosition: 'bottom',
children: {
type: 'panel',
items: [
{
type: 'panel',
id: `panel-${currentId}-1-1`,
title: `面板 ${currentId}`,
x: 0,
y: 0,
width: 280,
height: 200,
collapsed: false,
toolbarExpanded: false,
maximized: false,
content: generateRandomContent(currentId)
}
]
}
}
]
}
const newTabPage = {
id: `tabpage-${areaIdCounter - 1}-1`,
title: `标签页 1`,
// TabPage填充满父容器
width: 100,
height: 100,
// 3. 向TabPage添加一个Panel。Panel的初始设置为填充满父容器
panels: [
]
}
const newPanel = {
id: `panel-${areaIdCounter - 1}-1-1`,
title: `面板 ${areaIdCounter - 1}`,
x: 0,
y: 0,
width: 100,
height: 100,
collapsed: false,
toolbarExpanded: false,
// 添加随机测试内容以便合并测试
content: generateRandomContent(areaIdCounter - 1)
}
newTabPage.panels.push(newPanel)
newArea.tabPages.push(newTabPage)
floatingAreas.value.push(newArea)
console.log('✅ 创建浮动面板成功:', newArea.id, 'z-index:', newZIndex)
console.log('🔍 浮动面板数据结构:', JSON.stringify(newArea, null, 2))
// 使用nextTick确保DOM更新后再进行后续操作
nextTick(() => {
console.log('🔍 浮动面板已添加到DOMfloatingAreas长度:', floatingAreas.value.length)
})
}
// 更新区域位置
const onUpdatePosition = (id, position) => {
@@ -362,28 +333,31 @@ const onToggleCollapse = (id) => {
const onMaximize = (panelId) => {
// 查找包含该面板的区域
for (const area of floatingAreas.value) {
// 正确处理层级结构Area -> TabPage -> Panel
if (area.tabPages) {
for (const tabPage of area.tabPages) {
if (tabPage.panels && tabPage.panels.length === 1 && tabPage.panels[0].id === panelId) {
// 当区域只包含一个Panel时切换Area和Panel的最大化状态
const isCurrentlyMaximized = area.WindowState === '最大化' || area.WindowState === 'maximized'
// 使用Vue推荐的方式更新响应式数据
if (isCurrentlyMaximized) {
// 切换为正常状态
area.WindowState = '正常'
// 确保Panel也恢复正常状态 - 使用展开运算符创建新对象确保响应式
tabPage.panels[0] = { ...tabPage.panels[0], maximized: false }
} else {
// 切换为最大化状态
area.WindowState = '最大化'
// 同时最大化Panel - 使用展开运算符创建新对象确保响应式
tabPage.panels[0] = { ...tabPage.panels[0], maximized: true }
if (area.children) {
for (const child of area.children) {
if (child.type === 'tabpage' && child.children && child.children.type === 'panel') {
const panels = child.children.items || []
if (panels.length === 1 && panels[0].id === panelId) {
// 当区域只包含一个Panel时切换Area和Panel的最大化状态
const isCurrentlyMaximized = area.windowState === '最大化' || area.windowState === 'maximized'
if (isCurrentlyMaximized) {
// 切换为正常状态
area.windowState = '正常'
// 确保Panel也恢复正常状态 - 使用展开运算符创建新对象确保响应式
child.children.items[0] = { ...panels[0], maximized: false }
} else {
// 切换为最大化状态
area.windowState = '最大化'
// 同时最大化Panel - 使用展开运算符创建新对象确保响应式
child.children.items[0] = { ...panels[0], maximized: true }
// 激活浮动区域,将其置于最顶层
zIndexManager.activateFloatingArea(area.id)
area.zIndex = zIndexManager.getFloatingAreaZIndex(area.id, true)
}
break
}
break
}
}
}
@@ -397,6 +371,9 @@ const onCloseFloatingArea = (id) => {
// 获取要移除的Area
const areaToRemove = floatingAreas.value[index]
// 从z-index管理器中移除该区域的层级管理
zIndexManager.removeFloatingArea(id)
// 清理Panel引用确保Panel被正确移除
if (areaToRemove.panels) {
// 这里可以添加任何需要的Panel清理逻辑
@@ -411,19 +388,19 @@ const onCloseFloatingArea = (id) => {
}
}
// 关闭面板
// 关闭面板 - 适配children数据结构
const onClosePanel = (areaId, panelId) => {
const area = floatingAreas.value.find(a => a.id === areaId)
// 正确处理层级结构Area -> TabPage -> Panel
if (area && area.tabPages) {
for (const tabPage of area.tabPages) {
if (tabPage.panels) {
const panelIndex = tabPage.panels.findIndex(p => p.id === panelId)
if (area && area.children) {
for (const child of area.children) {
if (child.type === 'tabpage' && child.children && child.children.type === 'panel') {
const panels = child.children.items || []
const panelIndex = panels.findIndex(p => p.id === panelId)
if (panelIndex !== -1) {
tabPage.panels.splice(panelIndex, 1)
panels.splice(panelIndex, 1)
// 如果区域内没有面板了,可以考虑关闭整个区域
if (tabPage.panels.length === 0) {
if (panels.length === 0) {
onCloseFloatingArea(areaId)
}
break
@@ -2333,8 +2310,12 @@ const adjustSourceAreaForLayout = (sourceArea, targetArea, dockZone) => {
}
}
// 暴露方法给父组件
// 暴露数据和方法给父组件
defineExpose({
// 数据属性
floatingAreas,
hiddenAreas,
// 原有方法
addFloatingPanel,