采用嵌套呈现方式处理
This commit is contained in:
@@ -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('🔍 浮动面板已添加到DOM,floatingAreas长度:', 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,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user