import { reactive, ref } from 'vue' import type { IEventBuilder, IEventMap } from '@/events/IEventBuilder' import { v4 as uuidv4 } from 'uuid' import type { ResizeDirection, ResizeState } from '@/ui/types/WindowFormTypes' import { appRegistry } from '@/apps/AppRegistry' /** * 窗体状态枚举 */ export enum WindowState { CREATING = 'creating', LOADING = 'loading', ACTIVE = 'active', MINIMIZED = 'minimized', MAXIMIZED = 'maximized', CLOSING = 'closing', DESTROYED = 'destroyed', ERROR = 'error' } /** * 窗体配置接口 */ /** * 窗体配置接口 */ export interface WindowConfig { /** * 窗体标题 */ title: string /** * 窗体宽度(像素) */ width: number /** * 窗体高度(像素) */ height: number /** * 窗体最小宽度(像素) */ minWidth?: number /** * 窗体最小高度(像素) */ minHeight?: number /** * 窗体最大宽度(像素) */ maxWidth?: number /** * 窗体最大高度(像素) */ maxHeight?: number /** * 是否可调整大小 */ resizable?: boolean /** * 是否可移动 */ movable?: boolean /** * 是否可关闭 */ closable?: boolean /** * 是否可最小化 */ minimizable?: boolean /** * 是否可最大化 */ maximizable?: boolean /** * 是否为模态窗体 */ modal?: boolean /** * 是否始终置顶 */ alwaysOnTop?: boolean /** * 窗体X坐标位置 */ x?: number /** * 窗体Y坐标位置 */ y?: number } /** * 窗体实例接口 */ export interface WindowInstance { /** * 窗体唯一标识符 */ id: string /** * 关联应用标识符 */ appId: string /** * 窗体配置信息 */ config: WindowConfig /** * 窗体当前状态 */ state: WindowState /** * 窗体DOM元素 */ element?: HTMLElement /** * 窗体内嵌iframe元素 */ iframe?: HTMLIFrameElement /** * 窗体层级索引 */ zIndex: number /** * 窗体创建时间 */ createdAt: Date /** * 窗体更新时间 */ updatedAt: Date /** * 拖拽调整尺寸状态 */ resizeState?: ResizeState } /** * 窗体事件接口 */ export interface WindowEvents extends IEventMap { onStateChange: (windowId: string, newState: WindowState, oldState: WindowState) => void onResize: (windowId: string, width: number, height: number) => void onMove: (windowId: string, x: number, y: number) => void onFocus: (windowId: string) => void onBlur: (windowId: string) => void onClose: (windowId: string) => void onResizeStart: (windowId: string) => void onResizing: (windowId: string, width: number, height: number) => void onResizeEnd: (windowId: string) => void } /** * 窗体管理服务类 */ export class WindowFormService { private windowsForm = reactive(new Map()) private activeWindowId = ref(null) private nextZIndex = 1000 private eventBus: IEventBuilder constructor(eventBus: IEventBuilder) { this.eventBus = eventBus this.setupGlobalResizeEvents() } /** * 创建新窗体 */ async createWindow(appId: string, config: WindowConfig): Promise { const windowId = uuidv4() const now = new Date() const windowInstance: WindowInstance = { id: windowId, appId, config, state: WindowState.CREATING, zIndex: this.nextZIndex++, createdAt: now, updatedAt: now } this.windowsForm.set(windowId, windowInstance) try { // 创建窗体DOM元素 await this.createWindowElement(windowInstance) // 更新状态为加载中 this.updateWindowState(windowId, WindowState.LOADING) // 模拟应用加载过程 await this.loadApplication(windowInstance) // 激活窗体 this.updateWindowState(windowId, WindowState.ACTIVE) this.setActiveWindow(windowId) return windowInstance } catch (error) { this.updateWindowState(windowId, WindowState.ERROR) throw error } } /** * 销毁窗体 */ async destroyWindow(windowId: string): Promise { const window = this.windowsForm.get(windowId) if (!window) return false try { this.updateWindowState(windowId, WindowState.CLOSING) // 清理DOM元素 if (window.element) { window.element.remove() } // 从集合中移除 this.windowsForm.delete(windowId) // 更新活跃窗体 if (this.activeWindowId.value === windowId) { this.activeWindowId.value = null // 激活最后一个窗体 const lastWindow = Array.from(this.windowsForm.values()).pop() if (lastWindow) { this.setActiveWindow(lastWindow.id) } } this.eventBus.notifyEvent('onClose', windowId) return true } catch (error) { console.error('销毁窗体失败:', error) return false } } /** * 最小化窗体 */ minimizeWindow(windowId: string): boolean { const window = this.windowsForm.get(windowId) if (!window || window.state === WindowState.MINIMIZED) return false this.updateWindowState(windowId, WindowState.MINIMIZED) if (window.element) { window.element.style.display = 'none' } // 发送窗体数据更新事件 this.notifyWindowFormDataUpdate(windowId) return true } /** * 最大化窗体 */ maximizeWindow(windowId: string): boolean { const window = this.windowsForm.get(windowId) if (!window || window.state === WindowState.MAXIMIZED) return false const oldState = window.state this.updateWindowState(windowId, WindowState.MAXIMIZED) if (window.element) { // 保存原始尺寸和位置 window.element.dataset.originalWidth = window.config.width.toString() window.element.dataset.originalHeight = window.config.height.toString() window.element.dataset.originalX = (window.config.x || 0).toString() window.element.dataset.originalY = (window.config.y || 0).toString() // 设置最大化样式 Object.assign(window.element.style, { position: 'fixed', top: '0', left: '0', width: '100vw', height: 'calc(100vh - 40px)', // 减去任务栏高度 display: 'block', transform: 'none' // 确保移除transform }) } this.setActiveWindow(windowId) // 发送窗体数据更新事件 this.notifyWindowFormDataUpdate(windowId) return true } /** * 还原窗体 */ restoreWindow(windowId: string): boolean { const window = this.windowsForm.get(windowId) if (!window) return false const targetState = window.state === WindowState.MINIMIZED ? WindowState.ACTIVE : window.state === WindowState.MAXIMIZED ? WindowState.ACTIVE : window.state this.updateWindowState(windowId, targetState) if (window.element) { if (window.state === WindowState.MINIMIZED) { window.element.style.display = 'block' } else if (window.state === WindowState.MAXIMIZED) { // 恢复原始尺寸和位置 const originalWidth = window.element.dataset.originalWidth const originalHeight = window.element.dataset.originalHeight const originalX = window.element.dataset.originalX const originalY = window.element.dataset.originalY Object.assign(window.element.style, { width: originalWidth ? `${originalWidth}px` : `${window.config.width}px`, height: originalHeight ? `${originalHeight}px` : `${window.config.height}px`, left: originalX ? `${originalX}px` : '0px', top: originalY ? `${originalY}px` : '0px', transform: 'none' // 确保移除transform }) // 更新配置中的位置 if (originalX) window.config.x = parseFloat(originalX) if (originalY) window.config.y = parseFloat(originalY) } } this.setActiveWindow(windowId) // 发送窗体数据更新事件 this.notifyWindowFormDataUpdate(windowId) return true } /** * 设置窗体标题 */ setWindowTitle(windowId: string, title: string): boolean { const window = this.windowsForm.get(windowId) if (!window) return false window.config.title = title window.updatedAt = new Date() // 更新DOM元素标题 if (window.element) { const titleElement = window.element.querySelector('.window-title') if (titleElement) { titleElement.textContent = title } } // 发送窗体数据更新事件 this.notifyWindowFormDataUpdate(windowId) return true } /** * 设置窗体尺寸 */ setWindowSize(windowId: string, width: number, height: number): boolean { const window = this.windowsForm.get(windowId) if (!window) return false // 检查尺寸限制 const finalWidth = this.clampDimension(width, window.config.minWidth, window.config.maxWidth) const finalHeight = this.clampDimension( height, window.config.minHeight, window.config.maxHeight ) window.config.width = finalWidth window.config.height = finalHeight window.updatedAt = new Date() if (window.element) { window.element.style.width = `${finalWidth}px` window.element.style.height = `${finalHeight}px` } this.eventBus.notifyEvent('onResize', windowId, finalWidth, finalHeight) // 发送窗体数据更新事件 this.notifyWindowFormDataUpdate(windowId) return true } /** * 获取窗体实例 */ getWindow(windowId: string): WindowInstance | undefined { return this.windowsForm.get(windowId) } /** * 获取所有窗体 */ getAllWindows(): WindowInstance[] { return Array.from(this.windowsForm.values()) } /** * 获取活跃窗体ID */ getActiveWindowId(): string | null { return this.activeWindowId.value } /** * 设置活跃窗体 */ setActiveWindow(windowId: string): boolean { const window = this.windowsForm.get(windowId) if (!window) return false this.activeWindowId.value = windowId window.zIndex = this.nextZIndex++ if (window.element) { window.element.style.zIndex = window.zIndex.toString() } this.eventBus.notifyEvent('onFocus', windowId) // 发送窗体数据更新事件 this.notifyWindowFormDataUpdate(windowId) return true } /** * 创建窗体DOM元素 */ private async createWindowElement(windowInstance: WindowInstance): Promise { const { id, config, appId } = windowInstance // 创建窗体容器 const windowElement = document.createElement('div') windowElement.className = 'system-window' windowElement.id = `window-${id}` // 计算初始位置 let left = config.x let top = config.y // 如果没有指定位置,则居中显示 if (left === undefined || top === undefined) { const centerX = Math.max(0, (window.innerWidth - config.width) / 2) const centerY = Math.max(0, (window.innerHeight - config.height) / 2) left = left !== undefined ? left : centerX top = top !== undefined ? top : centerY } // 设置基本样式 Object.assign(windowElement.style, { position: 'absolute', width: `${config.width}px`, height: `${config.height}px`, left: `${left}px`, top: `${top}px`, zIndex: windowInstance.zIndex.toString(), backgroundColor: '#fff', border: '1px solid #ccc', borderRadius: '8px', boxShadow: '0 4px 20px rgba(0,0,0,0.15)', overflow: 'hidden' }) // 保存初始位置到配置中 windowInstance.config.x = left windowInstance.config.y = top // 创建窗体标题栏 const titleBar = this.createTitleBar(windowInstance) windowElement.appendChild(titleBar) // 创建窗体内容区域 const contentArea = document.createElement('div') contentArea.className = 'window-content' contentArea.style.cssText = ` width: 100%; height: calc(100% - 40px); overflow: hidden; ` // 检查是否为内置应用 if (appRegistry.hasApp(appId)) { // 内置应用:创建普通 div 容器,AppRenderer 组件会在这里渲染内容 const appContainer = document.createElement('div') appContainer.className = 'built-in-app-container' appContainer.id = `app-container-${appId}` appContainer.style.cssText = ` width: 100%; height: 100%; background: #fff; ` contentArea.appendChild(appContainer) console.log(`[WindowService] 为内置应用 ${appId} 创建了普通容器`) } else { // 外部应用:创建 iframe 容器 const iframe = document.createElement('iframe') iframe.style.cssText = ` width: 100%; height: 100%; border: none; background: #fff; ` iframe.sandbox = 'allow-scripts allow-forms allow-popups' // 移除allow-same-origin以提高安全性 contentArea.appendChild(iframe) // 保存 iframe 引用(仅对外部应用) windowInstance.iframe = iframe console.log(`[WindowService] 为外部应用 ${appId} 创建了 iframe 容器`) } windowElement.appendChild(contentArea) // 添加拖拽调整尺寸功能 if (config.resizable !== false) { this.addResizeFunctionality(windowElement, windowInstance) } // 添加到页面 document.body.appendChild(windowElement) // 保存引用 windowInstance.element = windowElement } /** * 创建窗体标题栏 */ private createTitleBar(windowInstance: WindowInstance): HTMLElement { const titleBar = document.createElement('div') titleBar.className = 'window-title-bar' titleBar.style.cssText = ` height: 40px; background: linear-gradient(to bottom, #f8f9fa, #e9ecef); border-bottom: 1px solid #dee2e6; display: flex; align-items: center; justify-content: space-between; padding: 0 12px; cursor: move; user-select: none; ` // 窗体标题 const title = document.createElement('span') title.className = 'window-title' title.textContent = windowInstance.config.title title.style.cssText = ` font-size: 14px; font-weight: 500; color: #333; ` // 控制按钮组 const controls = document.createElement('div') controls.className = 'window-controls' controls.style.cssText = ` display: flex; gap: 8px; ` // 最小化按钮 if (windowInstance.config.minimizable !== false) { const minimizeBtn = this.createControlButton('−', () => { this.minimizeWindow(windowInstance.id) }) controls.appendChild(minimizeBtn) } // 最大化按钮 if (windowInstance.config.maximizable !== false) { const maximizeBtn = this.createControlButton('□', () => { if (windowInstance.state === WindowState.MAXIMIZED) { this.restoreWindow(windowInstance.id) } else { this.maximizeWindow(windowInstance.id) } }) controls.appendChild(maximizeBtn) } // 关闭按钮 if (windowInstance.config.closable !== false) { const closeBtn = this.createControlButton('×', () => { this.destroyWindow(windowInstance.id) }) closeBtn.style.color = '#dc3545' controls.appendChild(closeBtn) } titleBar.appendChild(title) titleBar.appendChild(controls) // 添加拖拽功能 if (windowInstance.config.movable !== false) { this.addDragFunctionality(titleBar, windowInstance) } return titleBar } /** * 创建控制按钮 */ private createControlButton(text: string, onClick: () => void): HTMLElement { const button = document.createElement('button') button.textContent = text button.style.cssText = ` width: 24px; height: 24px; border: none; background: transparent; cursor: pointer; display: flex; align-items: center; justify-content: center; border-radius: 4px; font-size: 16px; line-height: 1; ` button.addEventListener('click', onClick) // 添加悬停效果 button.addEventListener('mouseenter', () => { button.style.backgroundColor = '#e9ecef' }) button.addEventListener('mouseleave', () => { button.style.backgroundColor = 'transparent' }) return button } /** * 添加窗体拖拽功能 */ private addDragFunctionality(titleBar: HTMLElement, windowInstance: WindowInstance): void { let isDragging = false let startX = 0 let startY = 0 let startLeft = 0 let startTop = 0 titleBar.addEventListener('mousedown', (e) => { // 检查是否正在调整尺寸,如果是则不处理拖拽 if (windowInstance.resizeState?.isResizing) { return } // 检查是否点击在调整尺寸手柄上,如果是则不处理拖拽 const target = e.target as HTMLElement if (target.classList.contains('resize-handle')) { return } if (!windowInstance.element) return isDragging = true startX = e.clientX startY = e.clientY const rect = windowInstance.element.getBoundingClientRect() // 如果使用了transform,需要转换为实际坐标 if ( windowInstance.element.style.transform && windowInstance.element.style.transform.includes('translate') ) { // 移除transform并设置实际的left/top值 windowInstance.element.style.transform = 'none' windowInstance.config.x = rect.left windowInstance.config.y = rect.top windowInstance.element.style.left = `${rect.left}px` windowInstance.element.style.top = `${rect.top}px` } startLeft = rect.left startTop = rect.top // 设置为活跃窗体 this.setActiveWindow(windowInstance.id) e.preventDefault() e.stopPropagation() }) document.addEventListener('mousemove', (e) => { // 检查是否正在调整尺寸,如果是则不处理拖拽 if (windowInstance.resizeState?.isResizing) { return } if (!isDragging || !windowInstance.element) return const deltaX = e.clientX - startX const deltaY = e.clientY - startY const newLeft = startLeft + deltaX const newTop = startTop + deltaY windowInstance.element.style.left = `${newLeft}px` windowInstance.element.style.top = `${newTop}px` // 更新配置 windowInstance.config.x = newLeft windowInstance.config.y = newTop this.eventBus.notifyEvent('onMove', windowInstance.id, newLeft, newTop) // 发送窗体数据更新事件 this.notifyWindowFormDataUpdate(windowInstance.id) }) document.addEventListener('mouseup', () => { isDragging = false }) } /** * 添加窗体调整尺寸功能 */ private addResizeFunctionality(windowElement: HTMLElement, windowInstance: WindowInstance): void { // 初始化调整尺寸状态 windowInstance.resizeState = { isResizing: false, direction: 'none', startX: 0, startY: 0, startWidth: 0, startHeight: 0, startXPosition: 0, startYPosition: 0 } // 创建8个调整尺寸的手柄 const resizeHandles = this.createResizeHandles(windowElement) // 添加鼠标事件监听器 resizeHandles.forEach((handle) => { this.addResizeHandleEvents(handle, windowElement, windowInstance) }) // 添加窗口边缘检测 windowElement.addEventListener('mousemove', (e) => { if (!windowInstance.resizeState || windowInstance.resizeState.isResizing) return this.updateCursorForResize(e, windowElement, windowInstance) }) windowElement.addEventListener('mouseleave', () => { if (!windowInstance.resizeState || windowInstance.resizeState.isResizing) return windowElement.style.cursor = 'default' }) } /** * 创建调整尺寸的手柄 */ private createResizeHandles(windowElement: HTMLElement): HTMLElement[] { const handles: HTMLElement[] = [] const directions: ResizeDirection[] = [ 'topLeft', 'top', 'topRight', 'right', 'bottomRight', 'bottom', 'bottomLeft', 'left' ] directions.forEach((direction) => { const handle = document.createElement('div') handle.className = `resize-handle resize-handle-${direction}` // 设置手柄样式 handle.style.position = 'absolute' handle.style.zIndex = '1001' // 根据方向设置位置和光标 switch (direction) { case 'topLeft': handle.style.top = '-6px' handle.style.left = '-6px' handle.style.cursor = 'nw-resize' break case 'top': handle.style.top = '-4px' handle.style.left = '6px' handle.style.right = '6px' handle.style.cursor = 'n-resize' break case 'topRight': handle.style.top = '-6px' handle.style.right = '-6px' handle.style.cursor = 'ne-resize' break case 'right': handle.style.top = '6px' handle.style.bottom = '6px' handle.style.right = '-4px' handle.style.cursor = 'e-resize' break case 'bottomRight': handle.style.bottom = '-6px' handle.style.right = '-6px' handle.style.cursor = 'se-resize' break case 'bottom': handle.style.bottom = '-4px' handle.style.left = '6px' handle.style.right = '6px' handle.style.cursor = 's-resize' break case 'bottomLeft': handle.style.bottom = '-6px' handle.style.left = '-6px' handle.style.cursor = 'sw-resize' break case 'left': handle.style.top = '6px' handle.style.bottom = '6px' handle.style.left = '-4px' handle.style.cursor = 'w-resize' break } // 设置手柄尺寸 if (direction === 'top' || direction === 'bottom') { handle.style.height = '8px' } else if (direction === 'left' || direction === 'right') { handle.style.width = '8px' } else { // 对角方向的手柄需要更大的点击区域 handle.style.width = '12px' handle.style.height = '12px' } windowElement.appendChild(handle) handles.push(handle) }) return handles } /** * 添加调整尺寸手柄的事件监听器 */ private addResizeHandleEvents( handle: HTMLElement, windowElement: HTMLElement, windowInstance: WindowInstance ): void { const direction = handle.className .split(' ') .find((cls) => cls.startsWith('resize-handle-')) ?.split('-')[2] as ResizeDirection handle.addEventListener('mousedown', (e) => { if (!windowInstance.resizeState) return e.preventDefault() e.stopPropagation() // 确保窗体位置是最新的 if (windowInstance.element) { const rect = windowInstance.element.getBoundingClientRect() // 如果使用了transform,需要转换为实际坐标 if ( windowInstance.element.style.transform && windowInstance.element.style.transform.includes('translate') ) { // 移除transform并设置实际的left/top值 windowInstance.element.style.transform = 'none' windowInstance.config.x = rect.left windowInstance.config.y = rect.top windowInstance.element.style.left = `${rect.left}px` windowInstance.element.style.top = `${rect.top}px` } } // 开始调整尺寸 windowInstance.resizeState.isResizing = true windowInstance.resizeState.direction = direction windowInstance.resizeState.startX = e.clientX windowInstance.resizeState.startY = e.clientY windowInstance.resizeState.startWidth = windowInstance.config.width windowInstance.resizeState.startHeight = windowInstance.config.height windowInstance.resizeState.startXPosition = windowInstance.config.x || 0 windowInstance.resizeState.startYPosition = windowInstance.config.y || 0 // 添加半透明遮罩效果 windowElement.style.opacity = '0.8' // 触发开始调整尺寸事件 this.eventBus.notifyEvent('onResizeStart', windowInstance.id) e.preventDefault() e.stopPropagation() }) } /** * 根据鼠标位置更新光标样式 */ private updateCursorForResize( e: MouseEvent, windowElement: HTMLElement, windowInstance: WindowInstance ): void { if (!windowInstance.resizeState) return const rect = windowElement.getBoundingClientRect() const x = e.clientX - rect.left const y = e.clientY - rect.top const edgeSize = 8 // 检查鼠标位置确定调整方向 let direction: ResizeDirection = 'none' // 优先检查角落区域,使用更精确的检测 if (x >= 0 && x < edgeSize && y >= 0 && y < edgeSize) { direction = 'topLeft' } else if (x > rect.width - edgeSize && x <= rect.width && y >= 0 && y < edgeSize) { direction = 'topRight' } else if (x >= 0 && x < edgeSize && y > rect.height - edgeSize && y <= rect.height) { direction = 'bottomLeft' } else if ( x > rect.width - edgeSize && x <= rect.width && y > rect.height - edgeSize && y <= rect.height ) { direction = 'bottomRight' } // 然后检查边缘区域 else if (x >= 0 && x < edgeSize && y >= edgeSize && y <= rect.height - edgeSize) { direction = 'left' } else if ( x > rect.width - edgeSize && x <= rect.width && y >= edgeSize && y <= rect.height - edgeSize ) { direction = 'right' } else if (y >= 0 && y < edgeSize && x >= edgeSize && x <= rect.width - edgeSize) { direction = 'top' } else if ( y > rect.height - edgeSize && y <= rect.height && x >= edgeSize && x <= rect.width - edgeSize ) { direction = 'bottom' } // 更新光标样式 windowElement.style.cursor = direction === 'none' ? 'default' : `${direction.replace(/([A-Z])/g, '-$1').toLowerCase()}-resize` } /** * 设置全局调整尺寸事件监听器 */ private setupGlobalResizeEvents(): void { document.addEventListener('mousemove', (e) => { // 处理调整尺寸过程中的鼠标移动 this.handleResizeMouseMove(e) }) document.addEventListener('mouseup', () => { // 处理调整尺寸结束 this.handleResizeMouseUp() }) } /** * 处理调整尺寸过程中的鼠标移动 */ private handleResizeMouseMove(e: MouseEvent): void { // 找到正在调整尺寸的窗体 const resizingWindow = Array.from(this.windowsForm.values()).find( (window) => window.resizeState?.isResizing ) // 如果没有正在调整尺寸的窗体,直接返回 if (!resizingWindow || !resizingWindow.resizeState || !resizingWindow.element) return const { direction, startX, startY, startWidth, startHeight, startXPosition, startYPosition } = resizingWindow.resizeState const deltaX = e.clientX - startX const deltaY = e.clientY - startY let newWidth = startWidth let newHeight = startHeight let newX = startXPosition let newY = startYPosition // 根据调整方向计算新尺寸和位置 switch (direction) { case 'topLeft': newWidth = Math.max(200, startWidth - deltaX) newHeight = Math.max(150, startHeight - deltaY) newX = startXPosition + (startWidth - newWidth) newY = startYPosition + (startHeight - newHeight) break case 'top': newHeight = Math.max(150, startHeight - deltaY) newY = startYPosition + (startHeight - newHeight) break case 'topRight': newWidth = Math.max(200, startWidth + deltaX) newHeight = Math.max(150, startHeight - deltaY) newY = startYPosition + (startHeight - newHeight) break case 'right': newWidth = Math.max(200, startWidth + deltaX) break case 'bottomRight': newWidth = Math.max(200, startWidth + deltaX) newHeight = Math.max(150, startHeight + deltaY) break case 'bottom': newHeight = Math.max(150, startHeight + deltaY) break case 'bottomLeft': newWidth = Math.max(200, startWidth - deltaX) newHeight = Math.max(150, startHeight + deltaY) newX = startXPosition + (startWidth - newWidth) break case 'left': newWidth = Math.max(200, startWidth - deltaX) newX = startXPosition + (startWidth - newWidth) break } // 应用尺寸限制 newWidth = this.clampDimension( newWidth, resizingWindow.config.minWidth, resizingWindow.config.maxWidth ) newHeight = this.clampDimension( newHeight, resizingWindow.config.minHeight, resizingWindow.config.maxHeight ) // 应用新尺寸和位置 resizingWindow.config.width = newWidth resizingWindow.config.height = newHeight resizingWindow.config.x = newX resizingWindow.config.y = newY if (resizingWindow.element) { resizingWindow.element.style.width = `${newWidth}px` resizingWindow.element.style.height = `${newHeight}px` resizingWindow.element.style.left = `${newX}px` resizingWindow.element.style.top = `${newY}px` resizingWindow.element.style.transform = 'none' } // 触发调整尺寸事件 this.eventBus.notifyEvent('onResizing', resizingWindow.id, newWidth, newHeight) // 发送窗体数据更新事件 this.notifyWindowFormDataUpdate(resizingWindow.id) } /** * 处理调整尺寸结束 */ private handleResizeMouseUp(): void { // 找到正在调整尺寸的窗体 const resizingWindow = Array.from(this.windowsForm.values()).find( (window) => window.resizeState?.isResizing ) if (!resizingWindow || !resizingWindow.resizeState || !resizingWindow.element) return // 结束调整尺寸 resizingWindow.resizeState.isResizing = false // 移除半透明遮罩效果 resizingWindow.element.style.opacity = '1' // 触发调整尺寸结束事件 this.eventBus.notifyEvent('onResizeEnd', resizingWindow.id) // 发送窗体数据更新事件 this.notifyWindowFormDataUpdate(resizingWindow.id) } /** * 限制尺寸在最小值和最大值之间 */ private clampDimension(value: number, min: number = 0, max: number = Infinity): number { return Math.max(min, Math.min(max, value)) } /** * 发送窗体数据更新事件 */ private notifyWindowFormDataUpdate(windowId: string): void { const window = this.windowsForm.get(windowId) if (!window || !window.element) return // 获取窗体数据 const rect = window.element.getBoundingClientRect() const data = { id: windowId, state: window.state, width: window.config.width, height: window.config.height, x: window.config.x !== undefined ? window.config.x : rect.left, y: window.config.y !== undefined ? window.config.y : rect.top } // 发送事件到事件总线 this.eventBus.notifyEvent('onWindowFormDataUpdate', data) } /** * 加载应用 */ private async loadApplication(windowInstance: WindowInstance): Promise { // 动态导入 AppRegistry 检查是否为内置应用 try { // 如果是内置应用,直接返回,不需要等待 if (appRegistry.hasApp(windowInstance.appId)) { console.log(`[WindowService] 内置应用 ${windowInstance.appId} 无需等待加载`) return Promise.resolve() } } catch (error) { console.warn('无法导入 AppRegistry,使用传统加载方式') } // 对于外部应用,保持原有的加载逻辑 return new Promise((resolve) => { console.log(`[WindowService] 开始加载外部应用 ${windowInstance.appId}`) setTimeout(() => { if (windowInstance.iframe) { // 这里可以设置 iframe 的 src 来加载具体应用 windowInstance.iframe.src = 'about:blank' // 添加一些示例内容 const doc = windowInstance.iframe.contentDocument if (doc) { doc.body.innerHTML = `

应用: ${windowInstance.config.title}

应用ID: ${windowInstance.appId}

窗体ID: ${windowInstance.id}

这是一个示例应用内容。

` } } console.log(`[WindowService] 外部应用 ${windowInstance.appId} 加载完成`) resolve() }, 200) }) } /** * 更新窗体状态 */ private updateWindowState(windowId: string, newState: WindowState): void { const window = this.windowsForm.get(windowId) if (!window) return const oldState = window.state // 只有状态真正发生变化时才触发事件 if (oldState === newState) return window.state = newState window.updatedAt = new Date() // 所有状态变化都应该触发事件,这是正常的系统行为 console.log(`[WindowService] 窗体状态变化: ${windowId} ${oldState} -> ${newState}`) this.eventBus.notifyEvent('onStateChange', windowId, newState, oldState) // 发送窗体数据更新事件 this.notifyWindowFormDataUpdate(windowId) } }