431 lines
9.5 KiB
TypeScript
431 lines
9.5 KiB
TypeScript
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<id, IWindowFormInstance>
|
||
* - 控制 ZIndex
|
||
* - 管理状态变更与事件通知
|
||
*/
|
||
export class WindowFormDataManager {
|
||
private windowForms = new Map<string, IWindowFormInstance>()
|
||
private activeWindowId: string | null = null
|
||
private nextZIndex = 1000
|
||
private wfEventBus: IEventBuilder<IWindowFormEvents>
|
||
|
||
constructor(wfEventBus: IEventBuilder<IWindowFormEvents>) {
|
||
this.wfEventBus = wfEventBus
|
||
}
|
||
|
||
/** 创建窗口实例数据对象(不含DOM) */
|
||
async createWindowForm(appId: string, config: IWindowFormConfig): Promise<IWindowFormInstance> {
|
||
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
|
||
})
|
||
}
|
||
}
|