采用嵌套呈现方式处理
This commit is contained in:
@@ -135,6 +135,7 @@
|
||||
import { defineProps, computed, defineEmits, ref, onMounted, watch, defineExpose } from 'vue'
|
||||
import TabPage from './TabPage.vue'
|
||||
import Panel from './Panel.vue'
|
||||
import { zIndexManager, Z_INDEX_LAYERS } from './dockLayers.js'
|
||||
|
||||
const props = defineProps({
|
||||
id: { type: String, required: true },
|
||||
@@ -222,14 +223,14 @@ watch(() => props.WindowState, (newState) => {
|
||||
// 根据状态计算尺寸和位置样式
|
||||
const areaStyle = computed(() => {
|
||||
if (isMaximized.value) {
|
||||
// 最大化时填充满父容器
|
||||
// 最大化时填充满父容器,使用更高的z-index确保在最顶层
|
||||
return {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 100,
|
||||
zIndex: Z_INDEX_LAYERS.CONTENT_ACTIVE, // 使用统一的z-index层级
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}
|
||||
@@ -238,7 +239,8 @@ const areaStyle = computed(() => {
|
||||
// 非最大化状态:使用原始位置或默认居中
|
||||
const style = {
|
||||
width: `${originalPosition.value.width}px`,
|
||||
height: `${originalPosition.value.height}px`
|
||||
height: `${originalPosition.value.height}px`,
|
||||
zIndex: Z_INDEX_LAYERS.CONTENT // 使用统一的z-index层级
|
||||
}
|
||||
|
||||
// 如果有明确的位置,则使用指定位置
|
||||
|
||||
@@ -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'"
|
||||
@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="onTabDragStart(area.id, $event)"
|
||||
@tabDragMove="onTabDragMove(area.id, $event)"
|
||||
@tabDragStart="(event) => onTabDragStart(area.id, event)"
|
||||
@tabDragMove="(event) => onTabDragMove(area.id, event)"
|
||||
@tabDragEnd="onTabDragEnd"
|
||||
@toggle-collapse="(panelId) => $emit('toggleCollapse', panelId)"
|
||||
@toggleCollapse="(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)"
|
||||
@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"
|
||||
>
|
||||
<!-- 在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>
|
||||
</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: [
|
||||
]
|
||||
}
|
||||
const newTabPage = {
|
||||
id: `tabpage-${areaIdCounter - 1}-1`,
|
||||
resizable: true,
|
||||
draggable: true,
|
||||
zIndex: newZIndex, // 使用z-index管理器分配的层级
|
||||
// 使用Render期望的children结构
|
||||
children: [
|
||||
{
|
||||
type: 'tabpage',
|
||||
id: `tabpage-${currentId}-1`,
|
||||
title: `标签页 1`,
|
||||
// TabPage填充满父容器
|
||||
width: 100,
|
||||
height: 100,
|
||||
// 3. 向TabPage添加一个Panel。Panel的初始设置为填充满父容器
|
||||
panels: [
|
||||
]
|
||||
}
|
||||
const newPanel = {
|
||||
id: `panel-${areaIdCounter - 1}-1-1`,
|
||||
title: `面板 ${areaIdCounter - 1}`,
|
||||
tabPosition: 'bottom',
|
||||
children: {
|
||||
type: 'panel',
|
||||
items: [
|
||||
{
|
||||
type: 'panel',
|
||||
id: `panel-${currentId}-1-1`,
|
||||
title: `面板 ${currentId}`,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
width: 280,
|
||||
height: 200,
|
||||
collapsed: false,
|
||||
toolbarExpanded: false,
|
||||
// 添加随机测试内容以便合并测试
|
||||
content: generateRandomContent(areaIdCounter - 1)
|
||||
maximized: false,
|
||||
content: generateRandomContent(currentId)
|
||||
}
|
||||
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,33 +333,36 @@ 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) {
|
||||
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'
|
||||
const isCurrentlyMaximized = area.windowState === '最大化' || area.windowState === 'maximized'
|
||||
|
||||
// 使用Vue推荐的方式更新响应式数据
|
||||
if (isCurrentlyMaximized) {
|
||||
// 切换为正常状态
|
||||
area.WindowState = '正常'
|
||||
area.windowState = '正常'
|
||||
// 确保Panel也恢复正常状态 - 使用展开运算符创建新对象确保响应式
|
||||
tabPage.panels[0] = { ...tabPage.panels[0], maximized: false }
|
||||
child.children.items[0] = { ...panels[0], maximized: false }
|
||||
} else {
|
||||
// 切换为最大化状态
|
||||
area.WindowState = '最大化'
|
||||
area.windowState = '最大化'
|
||||
// 同时最大化Panel - 使用展开运算符创建新对象确保响应式
|
||||
tabPage.panels[0] = { ...tabPage.panels[0], maximized: true }
|
||||
child.children.items[0] = { ...panels[0], maximized: true }
|
||||
|
||||
// 激活浮动区域,将其置于最顶层
|
||||
zIndexManager.activateFloatingArea(area.id)
|
||||
area.zIndex = zIndexManager.getFloatingAreaZIndex(area.id, true)
|
||||
}
|
||||
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭浮动区域 - 同时移除内容区的Panel
|
||||
const onCloseFloatingArea = (id) => {
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -5,22 +5,74 @@
|
||||
v-bind="componentProps"
|
||||
v-on="componentListeners"
|
||||
>
|
||||
<!-- Area组件的slot内容 -->
|
||||
<template v-if="type === 'area'" #default>
|
||||
<!-- 渲染Area的子组件 -->
|
||||
<template v-for="child in normalizedChildren" :key="child.id">
|
||||
<Renderer :type="child.type" :config="child" />
|
||||
<!-- 对于有children配置的情况,需要手动渲染子组件 -->
|
||||
<template v-if="type === 'area' && config.children">
|
||||
<!-- 如果children是数组 -->
|
||||
<template v-if="Array.isArray(config.children)">
|
||||
<div v-for="child in config.children" :key="child.id" style="width: 100%; height: 100%;">
|
||||
<Render
|
||||
v-if="child.type === 'tabpage'"
|
||||
:type="'tabpage'"
|
||||
:config="child"
|
||||
:debug="debug"
|
||||
@tab-change="$emit('tab-change', $event)"
|
||||
@tab-close="$emit('tab-close', $event)"
|
||||
@tab-add="$emit('tab-add', $event)"
|
||||
@tabDragStart="$emit('tabDragStart', $event)"
|
||||
@tabDragMove="$emit('tabDragMove', $event)"
|
||||
@tabDragEnd="$emit('tabDragEnd', $event)"
|
||||
@toggleCollapse="$emit('toggleCollapse', $event)"
|
||||
@maximize="$emit('maximize', $event)"
|
||||
@close="$emit('close', $event)"
|
||||
@toggleToolbar="$emit('toggleToolbar', $event)"
|
||||
@dragStart="$emit('dragStart', $event)"
|
||||
@dragMove="$emit('dragMove', $event)"
|
||||
@dragEnd="$emit('dragEnd', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 如果children是对象 -->
|
||||
<template v-else-if="config.children.type === 'tabpage'">
|
||||
<div v-for="tabPage in (Array.isArray(config.children.items) ? config.children.items : [config.children])" :key="tabPage.id" style="width: 100%; height: 100%;">
|
||||
<Render
|
||||
:type="'tabpage'"
|
||||
:config="tabPage"
|
||||
:debug="debug"
|
||||
@tab-change="$emit('tab-change', $event)"
|
||||
@tab-close="$emit('tab-close', $event)"
|
||||
@tab-add="$emit('tab-add', $event)"
|
||||
@tabDragStart="$emit('tabDragStart', $event)"
|
||||
@tabDragMove="$emit('tabDragMove', $event)"
|
||||
@tabDragEnd="$emit('tabDragEnd', $event)"
|
||||
@toggleCollapse="$emit('toggleCollapse', $event)"
|
||||
@maximize="$emit('maximize', $event)"
|
||||
@close="$emit('close', $event)"
|
||||
@toggleToolbar="$emit('toggleToolbar', $event)"
|
||||
@dragStart="$emit('dragStart', $event)"
|
||||
@dragMove="$emit('dragMove', $event)"
|
||||
@dragEnd="$emit('dragEnd', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- TabPage组件的panels prop -->
|
||||
<template v-else-if="type === 'tabpage'" #panels>
|
||||
<!-- TabPage的panels由config提供,不需要额外的slot -->
|
||||
</template>
|
||||
|
||||
<!-- Panel组件的默认内容 -->
|
||||
<template v-else-if="type === 'panel'" #default>
|
||||
<!-- Panel的内容由config.content提供 -->
|
||||
<template v-else-if="type === 'tabpage' && config.children && config.children.type === 'panel'">
|
||||
<!-- 手动渲染Panel组件 -->
|
||||
<div v-for="panel in (Array.isArray(config.children.items) ? config.children.items : [config.children])" :key="panel.id" style="width: 100%; height: 100%;">
|
||||
<Render
|
||||
:type="'panel'"
|
||||
:config="panel"
|
||||
:debug="debug"
|
||||
@toggleCollapse="$emit('toggleCollapse', $event)"
|
||||
@maximize="$emit('maximize', $event)"
|
||||
@close="$emit('close', $event)"
|
||||
@toggleToolbar="$emit('toggleToolbar', $event)"
|
||||
@dragStart="$emit('dragStart', $event)"
|
||||
@dragMove="$emit('dragMove', $event)"
|
||||
@dragEnd="$emit('dragEnd', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</component>
|
||||
</template>
|
||||
@@ -91,14 +143,20 @@ const componentProps = computed(() => {
|
||||
showTitleBar: config.showTitleBar !== false,
|
||||
left: config.left,
|
||||
top: config.top,
|
||||
draggable: config.draggable !== false
|
||||
draggable: config.draggable !== false,
|
||||
// 如果有children配置,将其转换为TabPages格式
|
||||
tabPages: config.children ? (
|
||||
config.children.type === 'tabpage'
|
||||
? (Array.isArray(config.children.items) ? config.children.items : [config.children])
|
||||
: config.children // 如果直接是tabPages数组
|
||||
) : config.tabPages || []
|
||||
}
|
||||
|
||||
case 'tabpage':
|
||||
return {
|
||||
id: config.id,
|
||||
title: config.title || '标签页',
|
||||
panels: config.panels || [],
|
||||
panels: config.panels || config.children?.items || [],
|
||||
showTabs: config.showTabs !== false,
|
||||
tabPosition: config.tabPosition || 'top'
|
||||
}
|
||||
@@ -130,43 +188,43 @@ const componentListeners = computed(() => {
|
||||
if (props.type === 'area') {
|
||||
// Area组件的事件
|
||||
allListeners['areaDragStart'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] areaDragStart:`, event)
|
||||
if (props.debug) console.log(`[Render-Area ${props.config.id}] areaDragStart:`, event)
|
||||
emit('areaDragStart', event)
|
||||
}
|
||||
allListeners['areaDragMove'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] areaDragMove:`, event)
|
||||
if (props.debug) console.log(`[Render-Area ${props.config.id}] areaDragMove:`, event)
|
||||
emit('areaDragMove', event)
|
||||
}
|
||||
allListeners['areaDragEnd'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] areaDragEnd:`, event)
|
||||
if (props.debug) console.log(`[Render-Area ${props.config.id}] areaDragEnd:`, event)
|
||||
emit('areaDragEnd', event)
|
||||
}
|
||||
allListeners['area-merged'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] area-merged:`, event)
|
||||
if (props.debug) console.log(`[Render-Area ${props.config.id}] area-merged:`, event)
|
||||
emit('area-merged', event)
|
||||
}
|
||||
allListeners['toggleCollapse'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] toggleCollapse:`, event)
|
||||
if (props.debug) console.log(`[Render-Area ${props.config.id}] toggleCollapse:`, event)
|
||||
emit('toggleCollapse', event)
|
||||
}
|
||||
allListeners['maximize'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] maximize:`, event)
|
||||
if (props.debug) console.log(`[Render-Area ${props.config.id}] maximize:`, event)
|
||||
emit('maximize', event)
|
||||
}
|
||||
allListeners['close'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] close:`, event)
|
||||
if (props.debug) console.log(`[Render-Area ${props.config.id}] close:`, event)
|
||||
emit('close', event)
|
||||
}
|
||||
allListeners['toggleToolbar'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] toggleToolbar:`, event)
|
||||
if (props.debug) console.log(`[Render-Area ${props.config.id}] toggleToolbar:`, event)
|
||||
emit('toggleToolbar', event)
|
||||
}
|
||||
allListeners['update:windowState'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] update:windowState:`, event)
|
||||
if (props.debug) console.log(`[Render-Area ${props.config.id}] update:windowState:`, event)
|
||||
emit('update:windowState', event)
|
||||
}
|
||||
allListeners['update:position'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Area ${props.config.id}] update:position:`, event)
|
||||
if (props.debug) console.log(`[Render-Area ${props.config.id}] update:position:`, event)
|
||||
emit('update:position', event)
|
||||
}
|
||||
}
|
||||
@@ -174,23 +232,23 @@ const componentListeners = computed(() => {
|
||||
if (props.type === 'tabpage') {
|
||||
// TabPage组件的事件
|
||||
allListeners['tabChange'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-TabPage ${props.config.id}] tabChange:`, event)
|
||||
if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabChange:`, event)
|
||||
emit('tabChange', event)
|
||||
}
|
||||
allListeners['tabClose'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-TabPage ${props.config.id}] tabClose:`, event)
|
||||
if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabClose:`, event)
|
||||
emit('tabClose', event)
|
||||
}
|
||||
allListeners['tabAdd'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-TabPage ${props.config.id}] tabAdd:`, event)
|
||||
if (props.debug) console.log(`[Render-TabPage ${props.config.id}] tabAdd:`, event)
|
||||
emit('tabAdd', event)
|
||||
}
|
||||
allListeners['maximize'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-TabPage ${props.config.id}] maximize:`, event)
|
||||
if (props.debug) console.log(`[Render-TabPage ${props.config.id}] maximize:`, event)
|
||||
emit('maximize', event)
|
||||
}
|
||||
allListeners['close'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-TabPage ${props.config.id}] close:`, event)
|
||||
if (props.debug) console.log(`[Render-TabPage ${props.config.id}] close:`, event)
|
||||
emit('close', event)
|
||||
}
|
||||
}
|
||||
@@ -198,31 +256,31 @@ const componentListeners = computed(() => {
|
||||
if (props.type === 'panel') {
|
||||
// Panel组件的事件
|
||||
allListeners['toggleCollapse'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] toggleCollapse:`, event)
|
||||
if (props.debug) console.log(`[Render-Panel ${props.config.id}] toggleCollapse:`, event)
|
||||
emit('panelToggleCollapse', event)
|
||||
}
|
||||
allListeners['maximize'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] maximize:`, event)
|
||||
if (props.debug) console.log(`[Render-Panel ${props.config.id}] maximize:`, event)
|
||||
emit('panelMaximize', event)
|
||||
}
|
||||
allListeners['close'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] close:`, event)
|
||||
if (props.debug) console.log(`[Render-Panel ${props.config.id}] close:`, event)
|
||||
emit('panelClose', event)
|
||||
}
|
||||
allListeners['toggleToolbar'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] toggleToolbar:`, event)
|
||||
if (props.debug) console.log(`[Render-Panel ${props.config.id}] toggleToolbar:`, event)
|
||||
emit('panelToggleToolbar', event)
|
||||
}
|
||||
allListeners['dragStart'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] dragStart:`, event)
|
||||
if (props.debug) console.log(`[Render-Panel ${props.config.id}] dragStart:`, event)
|
||||
emit('dragStart', event)
|
||||
}
|
||||
allListeners['dragMove'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] dragMove:`, event)
|
||||
if (props.debug) console.log(`[Render-Panel ${props.config.id}] dragMove:`, event)
|
||||
emit('dragMove', event)
|
||||
}
|
||||
allListeners['dragEnd'] = (event) => {
|
||||
if (props.debug) console.log(`[Renderer-Panel ${props.config.id}] dragEnd:`, event)
|
||||
if (props.debug) console.log(`[Render-Panel ${props.config.id}] dragEnd:`, event)
|
||||
emit('dragEnd', event)
|
||||
}
|
||||
}
|
||||
@@ -255,5 +313,5 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Renderer组件本身不添加额外样式,由子组件控制 */
|
||||
/* Render组件本身不添加额外样式,由子组件控制 */
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="tab-page" :class="[`tab-page-${tabPosition}`]">
|
||||
<div class="tab-page" :class="[`tab-page-${tabPosition}`]" style="width: 100%; height: 100%;">
|
||||
<!-- 顶部标签栏 -->
|
||||
<div v-if="tabPosition === 'top' && shouldShowTabs && panels && panels.length > 0" class="tab-header tab-header-horizontal">
|
||||
<div
|
||||
@@ -84,6 +84,7 @@
|
||||
<div class="tab-placeholder"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Tab页内容区域 -->
|
||||
<div class="tab-content">
|
||||
<!-- 渲染当前激活的Panel内容 -->
|
||||
@@ -293,18 +294,26 @@ const onTabDragEnd = () => {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
/* 顶部和底部位置:水平布局 */
|
||||
.tab-page-top,
|
||||
.tab-page-bottom {
|
||||
/* 顶部位置:标签栏 -> 工具栏 -> 内容区 */
|
||||
.tab-page-top {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 左侧和右侧位置:水平布局但标签栏垂直 */
|
||||
.tab-page-left,
|
||||
.tab-page-right {
|
||||
/* 底部位置:内容区 -> 工具栏 -> 标签栏 */
|
||||
.tab-page-bottom {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
/* 左侧位置:标签栏 -> 工具栏 -> 内容区(垂直排列) */
|
||||
.tab-page-left {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
/* 右侧位置:标签栏 -> 工具栏 -> 内容区(垂直排列) */
|
||||
.tab-page-right {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
:root {
|
||||
--vs-blue-top: #4f72b3;
|
||||
--vs-blue-bottom: #3c5a99;
|
||||
|
||||
162
AutoRobot/Windows/Robot/Web/src/DockLayout/dockLayers.js
Normal file
162
AutoRobot/Windows/Robot/Web/src/DockLayout/dockLayers.js
Normal file
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* DockLayout Z-Index 全局管理器
|
||||
* 统一管理所有组件的层级,确保正确的显示顺序
|
||||
*/
|
||||
|
||||
// Z-Index 分层策略
|
||||
export const Z_INDEX_LAYERS = {
|
||||
// 基础层级
|
||||
BACKGROUND: 1, // 背景层
|
||||
|
||||
// 内容层级
|
||||
CONTENT: 10, // 普通内容层
|
||||
CONTENT_ACTIVE: 15, // 活跃内容层
|
||||
|
||||
// 交互组件
|
||||
RESIZE_BAR: 100, // 调整手柄
|
||||
|
||||
// 标题栏和工具栏
|
||||
TITLE_BAR: 200, // 标题栏
|
||||
TOOLBAR: 300, // 工具栏
|
||||
|
||||
// 浮动区域
|
||||
FLOATING_AREA: 1000, // 浮动区域
|
||||
FLOATING_AREA_ACTIVE: 1100, // 活跃浮动区域
|
||||
|
||||
// 指示器和交互反馈
|
||||
DOCK_INDICATOR: 5000, // 停靠指示器
|
||||
DRAG_PREVIEW: 6000, // 拖拽预览
|
||||
|
||||
// 最高优先级
|
||||
MODAL: 8000, // 模态框
|
||||
TOOLTIP: 9000, // 工具提示
|
||||
NOTIFICATION: 9500, // 通知
|
||||
CRITICAL: 9999 // 关键提示
|
||||
}
|
||||
|
||||
// 动态z-index管理
|
||||
class ZIndexManager {
|
||||
constructor() {
|
||||
this.floatingAreas = new Map() // 浮动区域层级管理
|
||||
this.dockIndicators = new Set() // 停靠指示器层级
|
||||
this.modalCount = 0 // 模态框计数器
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取浮动区域的z-index
|
||||
* @param {string} areaId - 区域ID
|
||||
* @param {boolean} isActive - 是否为活跃状态
|
||||
* @returns {number} z-index值
|
||||
*/
|
||||
getFloatingAreaZIndex(areaId, isActive = false) {
|
||||
if (isActive) {
|
||||
return this.floatingAreas.get(areaId) || Z_INDEX_LAYERS.FLOATING_AREA_ACTIVE
|
||||
}
|
||||
|
||||
// 为新区域分配z-index
|
||||
if (!this.floatingAreas.has(areaId)) {
|
||||
const maxZIndex = Math.max(...Array.from(this.floatingAreas.values()), Z_INDEX_LAYERS.FLOATING_AREA)
|
||||
const newZIndex = maxZIndex + 1
|
||||
this.floatingAreas.set(areaId, newZIndex)
|
||||
return newZIndex
|
||||
}
|
||||
|
||||
return this.floatingAreas.get(areaId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活浮动区域(将其置于最顶层)
|
||||
* @param {string} areaId - 区域ID
|
||||
*/
|
||||
activateFloatingArea(areaId) {
|
||||
// 获取当前最大z-index
|
||||
const maxZIndex = Math.max(...Array.from(this.floatingAreas.values()), Z_INDEX_LAYERS.FLOATING_AREA)
|
||||
this.floatingAreas.set(areaId, maxZIndex + 1)
|
||||
|
||||
// 触发重排以确保层级生效
|
||||
this.reorderFloatingAreas()
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新排序浮动区域层级
|
||||
*/
|
||||
reorderFloatingAreas() {
|
||||
const areas = Array.from(this.floatingAreas.entries())
|
||||
areas.sort((a, b) => a[1] - b[1]) // 按z-index排序
|
||||
|
||||
areas.forEach(([areaId], index) => {
|
||||
this.floatingAreas.set(areaId, Z_INDEX_LAYERS.FLOATING_AREA + index + 1)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除浮动区域
|
||||
* @param {string} areaId - 区域ID
|
||||
*/
|
||||
removeFloatingArea(areaId) {
|
||||
this.floatingAreas.delete(areaId)
|
||||
this.reorderFloatingAreas()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取停靠指示器的z-index
|
||||
* @param {string} indicatorType - 指示器类型
|
||||
* @returns {number} z-index值
|
||||
*/
|
||||
getDockIndicatorZIndex(indicatorType = 'default') {
|
||||
return Z_INDEX_LAYERS.DOCK_INDICATOR
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取拖拽预览的z-index
|
||||
* @returns {number} z-index值
|
||||
*/
|
||||
getDragPreviewZIndex() {
|
||||
return Z_INDEX_LAYERS.DRAG_PREVIEW
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模态框的z-index
|
||||
* @returns {number} z-index值
|
||||
*/
|
||||
getModalZIndex() {
|
||||
this.modalCount++
|
||||
return Z_INDEX_LAYERS.MODAL + this.modalCount
|
||||
}
|
||||
|
||||
/**
|
||||
* 释放模态框z-index
|
||||
*/
|
||||
releaseModalZIndex() {
|
||||
if (this.modalCount > 0) {
|
||||
this.modalCount--
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置所有z-index(用于调试或清理)
|
||||
*/
|
||||
resetAll() {
|
||||
this.floatingAreas.clear()
|
||||
this.dockIndicators.clear()
|
||||
this.modalCount = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前状态信息(用于调试)
|
||||
*/
|
||||
getStatus() {
|
||||
return {
|
||||
floatingAreas: Object.fromEntries(this.floatingAreas),
|
||||
dockIndicators: Array.from(this.dockIndicators),
|
||||
modalCount: this.modalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建全局实例
|
||||
const zIndexManager = new ZIndexManager()
|
||||
|
||||
// 导出管理器实例和相关常量
|
||||
export { zIndexManager }
|
||||
export default zIndexManager
|
||||
Reference in New Issue
Block a user