面板最大化和还原

This commit is contained in:
zqm
2025-12-30 10:50:48 +08:00
parent 91cae667ac
commit 5f0794aab0
4 changed files with 123 additions and 18 deletions

View File

@@ -48,6 +48,11 @@
<script setup>
import { ref, computed, onMounted, onUnmounted, defineEmits } from 'vue'
import Area from './Area.vue';
import { getAreaHandler } from './handlers/AreaHandler';
// 获取AreaHandler实例
const areaHandler = getAreaHandler();
import Panel from './Panel.vue';
import TabPage from './TabPage.vue';
import DockIndicator from './DockIndicator.vue';
@@ -171,6 +176,44 @@ const onUpdatePosition = (event) => {
}
};
// 处理Area更新事件
const onAreaUpdated = (event) => {
const id = event.areaId;
const updates = event.updates;
const area = floatingAreas.value.find(a => a.id === id);
if (area) {
// 合并更新到Area对象
Object.assign(area, updates);
// 如果是最大化状态变化发送panel.maximize.sync事件
if ('maximized' in updates || 'windowState' in updates) {
// 查找该区域下的所有Panel
const areaState = areaHandler.areaStateManager.getState(id);
if (areaState && areaState.children) {
const childrenArray = Array.isArray(areaState.children) ? areaState.children : [areaState.children];
// 查找TabPage
childrenArray.forEach(child => {
if (child.type === 'TabPage' && child.children) {
const tabChildrenArray = Array.isArray(child.children) ? child.children : [child.children];
// 发送panel.maximize.sync事件给每个Panel
tabChildrenArray.forEach(tabChild => {
if (tabChild.type === 'Panel') {
eventBus.emit(EVENT_TYPES.PANEL_MAXIMIZE_SYNC, {
panelId: tabChild.id,
areaId: id,
maximized: updates.maximized !== undefined ? updates.maximized : (updates.windowState === '最大化' || updates.windowState === 'maximized')
}, { componentId: 'dock-layout' });
}
});
}
});
}
}
}
};
const onMaximize = (event) => {
const panelId = event.panelId;
const areaId = event.areaId;
@@ -431,10 +474,10 @@ const setupEventListeners = () => {
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_DRAG_OVER, handleAreaDragOver, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_DRAG_LEAVE, handleAreaDragLeave, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_MERGE_REQUEST, handleAreaMergeRequest, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_UPDATED, onAreaUpdated, { componentId: 'dock-layout' }));
// 添加新的下降事件监听器
unsubscribeFunctions.push(eventBus.on('area.position.update', onAreaPositionUpdate, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on('area.drag.state.update', onAreaDragStateUpdate, { componentId: 'dock-layout' }));
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.AREA_DRAG_STATE_UPDATE, onAreaDragStateUpdate, { componentId: 'dock-layout' }));
// Tab相关事件
unsubscribeFunctions.push(eventBus.on(EVENT_TYPES.TAB_CHANGE, onTabChange, { componentId: 'dock-layout' }));

View File

@@ -22,9 +22,9 @@
<button class="button-icon p-[2px] rounded hover:opacity-100 opacity-80"
@click.stop="onMaximize"
@mousedown.stop
:aria-label="maximized ? '还原' : '最大化'">
:aria-label="isMaximized ? '还原' : '最大化'">
<!-- 最大化图标 -->
<template v-if="!maximized">
<template v-if="!isMaximized">
<!-- 最大化图标外框 + 内部线条 -->
<svg class="icon-square-svg" width="11" height="11" viewBox="0 0 11 11" aria-hidden="true">
<rect x="0.5" y="0.5" width="10" height="10" fill="#cbd6ff" stroke="#8ea3d8" stroke-width="1" />
@@ -112,7 +112,7 @@
</template>
<script setup>
import { defineProps, onMounted, onUnmounted } from 'vue';
import { defineProps, onMounted, onUnmounted, ref } from 'vue';
import {
eventBus,
EVENT_TYPES,
@@ -174,6 +174,9 @@ const props = defineProps({
const subscriptions = new Set();
const subscriptionRegistry = new Map();
// 响应式的最大化状态初始化为props.maximized
const isMaximized = ref(props.maximized);
const getCurrentAreaId = () => {
const panelElement = document.querySelector(`[data-panel-id="${props.id}"]`);
if (panelElement) {
@@ -212,7 +215,7 @@ const onMaximize = () => {
emitEvent(EVENT_TYPES.PANEL_MAXIMIZE, {
panelId: props.id,
areaId: getCurrentAreaId(),
currentState: props.maximized
currentState: isMaximized.value
}, {
source: { component: 'Panel', panelId: props.id }
})
@@ -303,7 +306,7 @@ const onDragStart = (e) => {
}
};
const onDragEnd = () => {
const onDragEnd = (e) => {
if (isDragging) {
isDragging = false
// 获取所有浮动区域信息,用于单面板检测
@@ -313,6 +316,7 @@ const onDragStart = (e) => {
dragId: currentDragId,
panelId: props.id,
areaId: currentAreaId,
position: { x: e.clientX, y: e.clientY },
timestamp: Date.now(),
layout: {
areas: floatingAreas
@@ -349,8 +353,10 @@ const setupEventListeners = () => {
// 监听面板最大化同步事件
const unsubscribeMaximizeSync = onEvent(EVENT_TYPES.PANEL_MAXIMIZE_SYNC, (data) => {
if (data.panelId === props.id) {
console.log(`[Panel:${props.id}] 收到最大化同步事件`)
// 同时检查panelId和areaId确保正确匹配
if (data.panelId === props.id || data.areaId === getCurrentAreaId()) {
console.log(`[Panel:${props.id}] 收到最大化同步事件:`, data);
isMaximized.value = data.maximized;
}
}, { componentId: `panel-${props.id}` })

View File

@@ -47,6 +47,44 @@ import { nanoid } from 'nanoid'
*/
export const EVENT_TYPES = {
// 系统级事件
SYSTEM_INIT: 'system.init',
SYSTEM_READY: 'system.ready',
SYSTEM_DESTROY: 'system.destroy',
SYSTEM_ERROR: 'system.error',
SYSTEM_PERFORMANCE: 'system.performance',
// 事件路由
EVENT_ROUTE_START: 'event.route.start',
EVENT_ROUTE_SUCCESS: 'event.route.success',
EVENT_ROUTE_ERROR: 'event.route.error',
EVENT_ROUTE_FALLBACK: 'event.route.fallback',
EVENT_RISING: 'event.rising',
EVENT_FALLING: 'event.falling',
// 跨组件通信
CROSS_COMPONENT_BROADCAST: 'cross.component.broadcast',
CROSS_COMPONENT_REQUEST: 'cross.component.request',
CROSS_COMPONENT_RESPONSE: 'cross.component.response',
// 事件链
EVENT_CHAIN_START: 'event.chain.start',
EVENT_CHAIN_PROGRESS: 'event.chain.progress',
EVENT_CHAIN_COMPLETE: 'event.chain.complete',
EVENT_CHAIN_ERROR: 'event.chain.error',
// 性能监控
PERFORMANCE_MONITOR_START: 'performance.monitor.start',
PERFORMANCE_MONITOR_END: 'performance.monitor.end',
PERFORMANCE_THRESHOLD_EXCEEDED: 'performance.threshold.exceeded',
// 调试和日志
DEBUG_EVENT_EMIT: 'debug.event.emit',
DEBUG_EVENT_HANDLE: 'debug.event.handle',
DEBUG_PERFORMANCE: 'debug.performance',
DEBUG_MEMORY: 'debug.memory',
DEBUG_TOGGLE: 'debug.toggle',
AREA_CLOSE: 'area.close',
AREA_POSITION_UPDATE: 'area.position.update',
AREA_DRAG_START: 'area.drag.start',
@@ -102,6 +140,7 @@ export const EVENT_TYPES = {
AREA_TABPAGE_MERGE: 'area.tabpage.merge',
AREA_TABPAGE_SYNC: 'area.tabpage.sync',
AREA_PANEL_SYNC: 'area.panel.sync',
AREA_DRAG_STATE_UPDATE: 'area.drag.state.update',
PANEL_MAXIMIZE_SYNC: 'panel.maximize.sync',
PANEL_MAXIMIZE: 'panel.maximize',

View File

@@ -258,8 +258,11 @@ class GlobalEventManager {
this.debugMode = false;
this.eventListenerUnsubscribers = [];
// 添加拖拽状态缓存,避免在每次移动事件中重复检测
this.dragOperationCache = new Map();
this._onGlobalEvent = this._onGlobalEvent.bind(this);
this._onSystemError = this._handleSystemError.bind(this);
this._handleSystemError = this._handleSystemError.bind(this);
this._cleanupExpiredData = this._cleanupExpiredData.bind(this);
GlobalEventManager.instance = this;
@@ -513,9 +516,15 @@ class GlobalEventManager {
console.log('👋 处理面板拖拽开始:', data);
}
// 检查是否应该操作区域而非面板
// 检查是否应该操作区域而非面板,并缓存结果
const shouldOperateArea = this._shouldOperateAreaInsteadOfPanel(data);
// 缓存检测结果,用于后续的拖拽移动和结束事件
this.dragOperationCache.set(data.dragId, {
shouldOperateArea,
timestamp: Date.now()
});
if (shouldOperateArea) {
// 转换为区域拖拽事件
const areaDragStartData = {
@@ -544,8 +553,9 @@ class GlobalEventManager {
console.log('✋ 处理面板拖拽移动:', data);
}
// 检查是否应该操作区域而非面板
const shouldOperateArea = this._shouldOperateAreaInsteadOfPanel(data);
// 从缓存中获取单面板检测结果,避免重复检测
const cache = this.dragOperationCache.get(data.dragId);
const shouldOperateArea = cache ? cache.shouldOperateArea : this._shouldOperateAreaInsteadOfPanel(data);
if (shouldOperateArea) {
// 转换为区域拖拽移动事件
@@ -574,8 +584,9 @@ class GlobalEventManager {
console.log('✋ 处理面板拖拽结束:', data);
}
// 检查是否应该操作区域而非面板
const shouldOperateArea = this._shouldOperateAreaInsteadOfPanel(data);
// 从缓存中获取单面板检测结果
const cache = this.dragOperationCache.get(data.dragId);
const shouldOperateArea = cache ? cache.shouldOperateArea : this._shouldOperateAreaInsteadOfPanel(data);
if (shouldOperateArea) {
// 转换为区域拖拽结束事件
@@ -593,6 +604,9 @@ class GlobalEventManager {
status: 'ended'
});
}
// 清理缓存
this.dragOperationCache.delete(data.dragId);
}
/**
@@ -609,6 +623,9 @@ class GlobalEventManager {
...data,
status: 'cancelled'
});
// 清理缓存
this.dragOperationCache.delete(data.dragId);
}
/**
@@ -799,10 +816,10 @@ class GlobalEventManager {
*/
_startDebugMode() {
// 监听调试命令
eventBus.on('debug.toggle', () => {
eventBus.on(EVENT_TYPES.DEBUG_TOGGLE, () => {
this.debugMode = !this.debugMode;
console.log(`🔧 调试模式${this.debugMode ? '开启' : '关闭'}`);
});
}, { componentId: 'global-event-manager' });
}
/**
@@ -1181,7 +1198,7 @@ export const globalEventActions = {
* 切换调试模式
*/
toggleDebug: () => {
eventBus.emit('debug.toggle');
eventBus.emit(EVENT_TYPES.DEBUG_TOGGLE, {}, { componentId: 'global-event-manager' });
},
/**