import { v4 as uuidv4 } from 'uuid' import type { IEventBuilder, IEventMap } from '@/events/IEventBuilder' /** 窗口状态枚举 */ export enum EWindowFormState { /** 创建中 */ CREATING = 'creating', /** 加载中 */ LOADING = 'loading', /** 激活 */ ACTIVE = 'active', /** 未激活 - 在后台,失去焦点 */ INACTIVE = 'inactive', /** 最小化 */ MINIMIZED = 'minimized', /** 最大化状态 */ MAXIMIZED = 'maximized', /** 关闭中 */ CLOSING = 'closing', /** 销毁 */ DESTROYED = 'destroyed', /** 错误 */ ERROR = 'error' } /** 窗口系统事件接口 */ export interface IWindowFormEvents extends IEventMap { onWindowFormDataUpdate: (data: { id: string state: EWindowFormState width: number height: number x: number y: number }) => void /** * 窗体创建事件 */ onCreating: (wid: string) => void /** * 窗体加载事件 */ onLoading: (wid: string) => void /** * 窗体加载完成事件 */ onLoaded: (wid: string) => void /** * 窗体聚焦事件 */ onFocus: (wid: string) => void /** * 窗体失焦事件 */ onBlur: (wid: string) => void /** * 窗体激活事件 */ onActivate: (wid: string) => void /** * 窗体失活事件 */ onDeactivate: (wid: string) => void /** * 窗体最小化事件 */ onMinimize: (wid: string) => void /** * 窗体最大化事件 */ onMaximize: (wid: string) => void /** * 窗体还原、恢复事件 */ onRestore: (wid: string) => void /** * 窗体关闭前事件 * @param id 窗体ID * @param cancel */ onBeforeClose: (id: string, cancel: () => void) => void /** * 窗体关闭事件 */ onClose: (wid: string) => void /** * 窗体销毁事件 */ onDestroy: (wid: string) => void /** * 窗体错误事件 */ onError: (wid: string, error: Error) => void } /** 窗口配置参数 */ export interface IWindowFormConfig { /** * 窗体标题 */ 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 IWindowFormData { /** * 窗体标题 */ 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 IWindowFormInstance { /** 窗口ID */ id: string /** 应用ID */ appId: string /** 窗口参数 */ config: IWindowFormData /** 窗口状态 */ state: EWindowFormState /** 窗口ZIndex */ zIndex: number /** 创建时间 - 时间戳 */ createdAt: number /** 更新时间 - 时间戳 */ updatedAt: number /** 窗口DOM元素 */ element?: HTMLElement /** 窗口iframe元素 */ iframe?: HTMLIFrameElement /** 记录事件解绑函数 */ subscriptions: (() => void)[] } /** * WindowFormDataManager * ------------------- * 窗口数据与状态的中心管理器。 * - 管理 Map * - 控制 ZIndex * - 管理状态变更与事件通知 */ export class WindowFormDataManager { private windowForms = new Map() private activeWindowId: string | null = null private nextZIndex = 1000 private wfEventBus: IEventBuilder constructor(wfEventBus: IEventBuilder) { this.wfEventBus = wfEventBus } /** 创建窗口实例数据对象(不含DOM) */ async createWindowForm(appId: string, config: IWindowFormConfig): Promise { const id = uuidv4() const now = new Date().getTime() const mergeConfig: IWindowFormData = { title: config.title ?? '窗口', width: config.width ?? 300, height: config.height ?? 300, minWidth: config.minWidth ?? 0, minHeight: config.minHeight ?? 0, maxWidth: config.maxWidth ?? window.innerWidth, maxHeight: config.maxHeight ?? window.innerHeight, resizable: config.resizable ?? true, movable: config.movable ?? true, closable: config.closable ?? true, minimizable: config.minimizable ?? true, maximizable: config.maximizable ?? true, modal: config.modal ?? false, alwaysOnTop: config.alwaysOnTop ?? false, x: config.x ?? 0, y: config.y ?? 0 } const instance: IWindowFormInstance = { id, appId, config: mergeConfig, state: EWindowFormState.CREATING, zIndex: this.nextZIndex++, createdAt: now, updatedAt: now, subscriptions: [] } this.windowForms.set(id, instance) return instance } /** 获取窗口实例 */ getWindowForm(windowId: string): IWindowFormInstance | undefined { return this.windowForms.get(windowId) } /** 删除窗口实例 */ removeWindowForm(windowId: string) { this.windowForms.delete(windowId) } /** 更新窗口状态 */ updateState(windowId: string, newState: EWindowFormState, error?: Error) { const win = this.windowForms.get(windowId) if (!win) return const old = win.state if (old === newState) return win.state = newState this.wfEventBus.notify('onStateChange', windowId, newState, old) this.notifyUpdate(win) this.transition(win, newState, error) } /** 生命周期状态分发器 */ private transition(win: IWindowFormInstance, newState: EWindowFormState, error?: Error) { const id = win.id switch (newState) { case EWindowFormState.CREATING: this.wfEventBus.notify('onCreating', id) break case EWindowFormState.LOADING: this.wfEventBus.notify('onLoading', id) break case EWindowFormState.ACTIVE: this.wfEventBus.notify('onActivate', id) break case EWindowFormState.INACTIVE: this.wfEventBus.notify('onDeactivate', id) break case EWindowFormState.MINIMIZED: this.wfEventBus.notify('onMinimize', id) break case EWindowFormState.MAXIMIZED: this.wfEventBus.notify('onMaximize', id) break case EWindowFormState.CLOSING: this.wfEventBus.notify('onClose', id) break case EWindowFormState.DESTROYED: this.wfEventBus.notify('onDestroy', id) break case EWindowFormState.ERROR: this.wfEventBus.notify('onError', id, error ?? new Error('未知错误')) break } } /** 聚焦窗口 */ focus(windowId: string) { const win = this.windowForms.get(windowId) if (!win) return this.activeWindowId = windowId win.zIndex = this.nextZIndex++ if (win.element) win.element.style.zIndex = `${win.zIndex}` this.wfEventBus.notify('onFocus', windowId) this.notifyUpdate(win) } /** 最小化窗口 */ minimize(windowId: string) { const win = this.windowForms.get(windowId) if (!win || !win.element) return win.element.style.display = 'none' this.updateState(windowId, EWindowFormState.MINIMIZED) } /** 最大化窗口 */ maximize(windowId: string) { const win = this.windowForms.get(windowId) if (!win || !win.element) return win.element.dataset.originalWidth = win.element.style.width win.element.dataset.originalHeight = win.element.style.height win.element.style.position = 'fixed' Object.assign(win.element.style, { top: '0', left: '0', width: '100vw', height: '100vh' }) this.updateState(windowId, EWindowFormState.MAXIMIZED) } /** 还原窗口 */ restore(windowId: string) { const win = this.windowForms.get(windowId) if (!win || !win.element) return Object.assign(win.element.style, { position: 'absolute', width: win.element.dataset.originalWidth, height: win.element.dataset.originalHeight }) this.updateState(windowId, EWindowFormState.ACTIVE) } /** 通知窗口数据更新 */ private notifyUpdate(win: IWindowFormInstance) { const rect = win.element?.getBoundingClientRect() if (!rect) return this.wfEventBus.notify('onWindowFormDataUpdate', { id: win.id, state: win.state, width: rect.width, height: rect.height, x: rect.left, y: rect.top }) } }