Compare commits
5 Commits
972e76e655
...
ed0527bf27
| Author | SHA1 | Date | |
|---|---|---|---|
| ed0527bf27 | |||
| a56197a349 | |||
| f1ba609254 | |||
| 7b1dff9ea1 | |||
| b77a20f9b0 |
@@ -2,5 +2,6 @@
|
|||||||
"$schema": "https://json.schemastore.org/prettierrc",
|
"$schema": "https://json.schemastore.org/prettierrc",
|
||||||
"semi": false,
|
"semi": false,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"printWidth": 100
|
"printWidth": 100,
|
||||||
|
"trailingComma": "none"
|
||||||
}
|
}
|
||||||
|
|||||||
144
.qoder/quests/module-dependency-analysis.md
Normal file
144
.qoder/quests/module-dependency-analysis.md
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
# 模块依赖分析:EventCommunicationService 与 EventBuilderImpl 关系
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本文档旨在分析 Vue Desktop 项目中 [EventCommunicationService](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\EventCommunicationService.ts#L95-L638) 与 [EventBuilderImpl](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\impl\EventBuilderImpl.ts#L7-L95) 的关系,明确它们各自的职责以及是否需要同时存在这两个组件。
|
||||||
|
|
||||||
|
## 系统架构概览
|
||||||
|
|
||||||
|
Vue Desktop 使用分层的事件管理系统:
|
||||||
|
|
||||||
|
1. 内部事件总线 ([EventBuilderImpl](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\impl\EventBuilderImpl.ts#L7-L95)) - 处理组件间的直接通信
|
||||||
|
2. 应用间通信服务 ([EventCommunicationService](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\EventCommunicationService.ts#L95-L638)) - 处理跨应用/模块的复杂消息传递
|
||||||
|
|
||||||
|
## 模块详细分析
|
||||||
|
|
||||||
|
### EventBuilderImpl 分析
|
||||||
|
|
||||||
|
[EventBuilderImpl](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\impl\EventBuilderImpl.ts#L7-L95) 是一个通用的事件管理器实现,提供了基础的发布/订阅模式功能:
|
||||||
|
|
||||||
|
#### 主要职责
|
||||||
|
|
||||||
|
- 提供简单的事件监听(addEventListener)和触发(notifyEvent)机制
|
||||||
|
- 支持一次性事件监听(once选项)
|
||||||
|
- 支持立即执行(immediate选项)
|
||||||
|
- 实现 [IEventBuilder](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\IEventBuilder.ts#L1-L28) 接口
|
||||||
|
|
||||||
|
#### 特点
|
||||||
|
|
||||||
|
- 轻量级,适用于组件内部通信
|
||||||
|
- 不涉及消息队列、优先级、过期时间等复杂概念
|
||||||
|
- 基于回调函数直接调用
|
||||||
|
|
||||||
|
### EventCommunicationService 分析
|
||||||
|
|
||||||
|
[EventCommunicationService](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\EventCommunicationService.ts#L95-L638) 是一个高级消息通信系统,用于处理复杂的跨应用通信场景:
|
||||||
|
|
||||||
|
#### 主要职责
|
||||||
|
|
||||||
|
- 管理应用间的事件订阅和消息传递
|
||||||
|
- 实现消息优先级、过期时间、重试机制等功能
|
||||||
|
- 提供频道管理和权限控制
|
||||||
|
- 支持广播和点对点消息
|
||||||
|
- 维护消息历史和统计数据
|
||||||
|
|
||||||
|
#### 特点
|
||||||
|
|
||||||
|
- 功能丰富,支持复杂的消息路由和处理
|
||||||
|
- 异步处理消息队列
|
||||||
|
- 提供消息持久化和统计功能
|
||||||
|
- 支持权限验证和消息过滤
|
||||||
|
|
||||||
|
## 依赖关系分析
|
||||||
|
|
||||||
|
### 架构层级
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[SystemServiceIntegration] --> B[EventCommunicationService]
|
||||||
|
A --> C[WindowService]
|
||||||
|
A --> D[ResourceService]
|
||||||
|
|
||||||
|
B --> E[IEventBuilder]
|
||||||
|
C --> E
|
||||||
|
|
||||||
|
F[EventBuilderImpl] -.-> E
|
||||||
|
```
|
||||||
|
|
||||||
|
### 依赖说明
|
||||||
|
|
||||||
|
1. [EventCommunicationService](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\EventCommunicationService.ts#L95-L638) 依赖 [IEventBuilder](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\IEventBuilder.ts#L1-L28) 接口,但不直接依赖具体实现
|
||||||
|
2. [EventBuilderImpl](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\impl\EventBuilderImpl.ts#L7-L95) 是 [IEventBuilder](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\IEventBuilder.ts#L1-L28) 的具体实现之一
|
||||||
|
3. [SystemServiceIntegration](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\SystemServiceIntegration.ts#L36-L597) 同时使用 [EventCommunicationService](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\EventCommunicationService.ts#L95-L638) 和其他服务,并传入 [EventBuilderImpl](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\impl\EventBuilderImpl.ts#L7-L95) 实例
|
||||||
|
|
||||||
|
## 是否需要 EventBuilderImpl?
|
||||||
|
|
||||||
|
### 结论
|
||||||
|
|
||||||
|
**是的,项目仍然需要 [EventBuilderImpl](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\impl\EventBuilderImpl.ts#L7-L95)**。
|
||||||
|
|
||||||
|
### 原因分析
|
||||||
|
|
||||||
|
1. **职责分离**:
|
||||||
|
- [EventBuilderImpl](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\impl\EventBuilderImpl.ts#L7-L95) 处理组件内简单事件通信
|
||||||
|
- [EventCommunicationService](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\EventCommunicationService.ts#L95-L638) 处理跨应用复杂消息传递
|
||||||
|
|
||||||
|
2. **性能考虑**:
|
||||||
|
- 内部组件通信不需要 [EventCommunicationService](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\EventCommunicationService.ts#L95-L638) 的复杂特性
|
||||||
|
- [EventBuilderImpl](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\impl\EventBuilderImpl.ts#L7-L95) 提供轻量级解决方案
|
||||||
|
|
||||||
|
3. **架构清晰性**:
|
||||||
|
- 保持两种不同层次的事件通信机制有助于维护架构清晰性
|
||||||
|
- 避免将所有事件都通过复杂的消息系统传递
|
||||||
|
|
||||||
|
## 正确的类型使用
|
||||||
|
|
||||||
|
### EventCommunicationService 中的类型
|
||||||
|
|
||||||
|
在 [EventCommunicationService](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\EventCommunicationService.ts#L95-L638) 构造函数中,应该使用接口而不是具体实现:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
constructor(eventBus: IEventBuilder<any>)
|
||||||
|
```
|
||||||
|
|
||||||
|
### SystemServiceIntegration 中的类型
|
||||||
|
|
||||||
|
在 [SystemServiceIntegration](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\SystemServiceIntegration.ts#L36-L597) 中,需要创建 [EventBuilderImpl](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\impl\EventBuilderImpl.ts#L7-L95) 实例并将其注入到依赖它的服务中:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
private eventBus: IEventBuilder<any>
|
||||||
|
|
||||||
|
constructor(config: SystemServiceConfig = {}) {
|
||||||
|
// 使用具体实现创建实例
|
||||||
|
this.eventBus = new EventBuilderImpl<any>()
|
||||||
|
|
||||||
|
// 注入到依赖的服务中
|
||||||
|
// EventCommunicationService 需要 IEventBuilder 接口
|
||||||
|
// WindowService 也需要 IEventBuilder 接口
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 推荐实践
|
||||||
|
|
||||||
|
### 类型声明建议
|
||||||
|
|
||||||
|
对于 [EventCommunicationService](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\EventCommunicationService.ts#L95-L638) 和其他服务:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 接受接口而非具体实现
|
||||||
|
constructor(eventBus: IEventBuilder<any>)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 实例化建议
|
||||||
|
|
||||||
|
在 [SystemServiceIntegration](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\SystemServiceIntegration.ts#L36-L597) 中:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 创建具体实例
|
||||||
|
private eventBus: IEventBuilder<any>
|
||||||
|
this.eventBus = new EventBuilderImpl<any>()
|
||||||
|
```
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
[EventBuilderImpl](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\impl\EventBuilderImpl.ts#L7-L95) 和 [EventCommunicationService](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\EventCommunicationService.ts#L95-L638) 在系统中有不同的职责,两者都是必要的。[EventBuilderImpl](file://c:\Users\98354\Desktop\develop\vue-desktop\src\events\impl\EventBuilderImpl.ts#L7-L95) 提供基础事件机制,[EventCommunicationService](file://c:\Users\98354\Desktop\develop\vue-desktop\src\services\EventCommunicationService.ts#L95-L638) 提供高级消息通信功能。在类型使用上,应遵循依赖倒置原则,服务依赖接口而非具体实现。
|
||||||
370
.qoder/quests/ui-modularization.md
Normal file
370
.qoder/quests/ui-modularization.md
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
# UI模块化设计方案
|
||||||
|
|
||||||
|
## 1. 概述
|
||||||
|
|
||||||
|
本方案旨在将当前项目中的UI组件与核心业务逻辑进行彻底分离,实现UI层只负责数据渲染,核心层专注于数据处理的架构模式。通过模块化重构,提高代码的可维护性、可测试性和可扩展性。
|
||||||
|
|
||||||
|
### 1.1 设计目标
|
||||||
|
|
||||||
|
- 实现UI层与核心业务逻辑的完全解耦
|
||||||
|
- UI组件只负责接收数据并渲染,不包含任何业务逻辑
|
||||||
|
- 核心服务层提供统一的数据接口和状态管理
|
||||||
|
- 通过响应式数据绑定实现UI与数据的自动同步
|
||||||
|
|
||||||
|
### 1.2 核心原则
|
||||||
|
|
||||||
|
- **数据驱动**: UI组件通过props接收数据,通过事件触发操作
|
||||||
|
- **单一职责**: UI组件只负责渲染,核心服务只负责数据处理
|
||||||
|
- **响应式更新**: 数据变化自动触发UI更新
|
||||||
|
- **模块化设计**: 各模块职责清晰,依赖关系明确
|
||||||
|
|
||||||
|
### 1.3 项目现状分析
|
||||||
|
|
||||||
|
当前项目已经具备良好的分层架构:
|
||||||
|
|
||||||
|
- 使用Pinia进行状态管理
|
||||||
|
- 通过依赖注入提供系统服务
|
||||||
|
- UI组件与核心服务之间通过明确定义的接口交互
|
||||||
|
- 采用Vue 3 Composition API实现逻辑复用
|
||||||
|
|
||||||
|
## 2. 架构设计
|
||||||
|
|
||||||
|
### 2.1 整体架构图
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[UI层] --> B[状态管理层]
|
||||||
|
B --> C[核心服务层]
|
||||||
|
C --> D[数据源]
|
||||||
|
C --> E[外部API]
|
||||||
|
B --> A
|
||||||
|
|
||||||
|
subgraph UI层
|
||||||
|
A
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph 核心层
|
||||||
|
B
|
||||||
|
C
|
||||||
|
D
|
||||||
|
E
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 现有架构分析
|
||||||
|
|
||||||
|
项目当前已具备良好的分层架构,但存在部分逻辑混合的情况:
|
||||||
|
|
||||||
|
1. **UI层**:包含Vue组件和部分业务逻辑
|
||||||
|
2. **状态管理层**:使用Pinia管理全局状态
|
||||||
|
3. **核心服务层**:SystemServiceIntegration统一管理系统服务
|
||||||
|
4. **依赖注入**:通过Vue的provide/inject机制传递系统服务
|
||||||
|
|
||||||
|
### 2.3 模块划分优化
|
||||||
|
|
||||||
|
| 模块 | 职责 | 包含内容 |
|
||||||
|
| ------------ | ---------------------- | ------------------------------ |
|
||||||
|
| UI组件模块 | 负责界面展示和用户交互 | 所有Vue组件、样式文件 |
|
||||||
|
| 状态管理模块 | 管理应用状态和数据流 | Pinia stores、响应式数据 |
|
||||||
|
| 核心服务模块 | 处理业务逻辑和数据操作 | 系统服务、应用管理、窗体管理等 |
|
||||||
|
| 工具模块 | 提供通用工具函数 | 工具函数、类型定义 |
|
||||||
|
|
||||||
|
## 3. UI模块重构方案
|
||||||
|
|
||||||
|
### 3.1 当前结构分析
|
||||||
|
|
||||||
|
当前项目UI相关代码分散在以下目录中:
|
||||||
|
|
||||||
|
- `src/ui/`: 主要UI组件
|
||||||
|
- `src/apps/`: 内置应用组件
|
||||||
|
- `src/common/`: 通用UI工具和组件
|
||||||
|
|
||||||
|
### 3.2 重构后的UI模块结构
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── ui/
|
||||||
|
│ ├── components/ # 通用UI组件
|
||||||
|
│ ├── containers/ # 容器组件
|
||||||
|
│ ├── views/ # 页面视图组件
|
||||||
|
│ ├── composables/ # UI专用组合式函数
|
||||||
|
│ ├── styles/ # 样式文件
|
||||||
|
│ └── types/ # UI相关类型定义
|
||||||
|
├── core/ # 核心业务逻辑(从UI中抽离)
|
||||||
|
├── stores/ # 状态管理
|
||||||
|
└── services/ # 核心服务层
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 UI组件分类
|
||||||
|
|
||||||
|
#### 3.3.1 展示组件(Pure Components)
|
||||||
|
|
||||||
|
展示组件只负责接收数据并渲染,不包含任何业务逻辑:
|
||||||
|
|
||||||
|
| 组件名称 | 职责 | 输入数据 | 输出事件 |
|
||||||
|
| ----------- | ------------ | ---------------------- | ------------------------------- |
|
||||||
|
| AppIcon | 应用图标展示 | iconInfo, gridTemplate | dragStart, dragEnd, doubleClick |
|
||||||
|
| AppRenderer | 应用渲染器 | appId, windowId | loaded, error |
|
||||||
|
|
||||||
|
#### 3.3.2 容器组件(Container Components)
|
||||||
|
|
||||||
|
容器组件负责连接展示组件与状态管理:
|
||||||
|
|
||||||
|
| 组件名称 | 职责 | 连接状态 | 业务逻辑 |
|
||||||
|
| ---------------- | ---------- | ----------------------- | ------------------ |
|
||||||
|
| DesktopContainer | 桌面容器 | appIcons, systemStatus | 应用启动、状态更新 |
|
||||||
|
| WindowManager | 窗口管理器 | windows | 窗口创建、销毁 |
|
||||||
|
| AppRenderer | 应用渲染器 | appComponent, iframeUrl | 应用加载、错误处理 |
|
||||||
|
|
||||||
|
## 4. 数据流设计
|
||||||
|
|
||||||
|
### 4.1 数据流向图
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
A[核心服务] --> B[状态管理]
|
||||||
|
B --> C[UI组件]
|
||||||
|
C --> D[用户操作]
|
||||||
|
D --> E[事件处理]
|
||||||
|
E --> A
|
||||||
|
|
||||||
|
style A fill:#cde4ff
|
||||||
|
style B fill:#ffd7c1
|
||||||
|
style C fill:#d3f8d3
|
||||||
|
style D fill:#fff2c1
|
||||||
|
style E fill:#e6d7ff
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 状态管理设计
|
||||||
|
|
||||||
|
项目已使用Pinia进行状态管理,建议继续沿用并扩展:
|
||||||
|
|
||||||
|
#### 4.2.1 桌面状态
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface DesktopState {
|
||||||
|
appIcons: IDesktopAppIcon[]
|
||||||
|
gridTemplate: IGridTemplateParams
|
||||||
|
systemStatus: SystemStatus
|
||||||
|
showSystemStatus: boolean
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4.2.2 窗口状态
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface WindowState {
|
||||||
|
windows: BuiltInWindow[]
|
||||||
|
activeWindowId: string | null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 数据获取与更新流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant U as UI组件
|
||||||
|
participant S as 状态管理
|
||||||
|
participant C as 核心服务
|
||||||
|
|
||||||
|
U->>S: 读取状态数据
|
||||||
|
S-->>U: 返回响应式数据
|
||||||
|
U->>U: 渲染界面
|
||||||
|
|
||||||
|
U->>C: 触发用户操作
|
||||||
|
C->>S: 更新状态
|
||||||
|
S->>U: 自动更新界面
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.4 依赖注入机制
|
||||||
|
|
||||||
|
项目通过Vue的provide/inject机制实现服务注入:
|
||||||
|
|
||||||
|
1. 在main.ts中创建并提供SystemServiceIntegration实例
|
||||||
|
2. UI组件通过inject获取系统服务
|
||||||
|
3. 系统服务提供统一的API访问核心功能
|
||||||
|
|
||||||
|
## 5. 接口设计
|
||||||
|
|
||||||
|
### 5.1 UI组件接口规范
|
||||||
|
|
||||||
|
#### 5.1.1 展示组件接口
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// AppIcon组件接口
|
||||||
|
interface AppIconProps {
|
||||||
|
iconInfo: IDesktopAppIcon
|
||||||
|
gridTemplate: IGridTemplateParams
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AppIconEvents {
|
||||||
|
(e: 'dragStart', event: DragEvent): void
|
||||||
|
(e: 'dragEnd', event: DragEvent): void
|
||||||
|
(e: 'doubleClick'): void
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppRenderer组件接口
|
||||||
|
interface AppRendererProps {
|
||||||
|
appId: string
|
||||||
|
windowId?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AppRendererEvents {
|
||||||
|
(e: 'loaded'): void
|
||||||
|
(e: 'error', error: Error): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.1.2 容器组件接口
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// DesktopContainer组件接口
|
||||||
|
interface DesktopContainerProps {
|
||||||
|
// 无需props,直接从状态管理获取数据
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DesktopContainerMethods {
|
||||||
|
refreshApps(): Promise<void>
|
||||||
|
runApp(appId: string): Promise<void>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 状态管理接口
|
||||||
|
|
||||||
|
#### 5.2.1 桌面状态接口
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface DesktopStore {
|
||||||
|
// 状态
|
||||||
|
appIcons: Ref<IDesktopAppIcon[]>
|
||||||
|
gridTemplate: Ref<IGridTemplateParams>
|
||||||
|
systemStatus: Ref<SystemStatus>
|
||||||
|
showSystemStatus: Ref<boolean>
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
gridStyle: ComputedRef<GridStyle>
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
updateAppIcons(icons: IDesktopAppIcon[]): void
|
||||||
|
updateSystemStatus(status: SystemStatus): void
|
||||||
|
toggleSystemStatus(show: boolean): void
|
||||||
|
saveIconPositions(): void
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 实现方案
|
||||||
|
|
||||||
|
### 6.1 UI模块独立化步骤
|
||||||
|
|
||||||
|
1. **创建独立的UI模块目录结构**
|
||||||
|
- 将现有的UI组件按功能重新组织
|
||||||
|
- 分离展示组件和容器组件
|
||||||
|
|
||||||
|
2. **抽离业务逻辑到核心服务**
|
||||||
|
- 将应用启动逻辑移至SystemServiceIntegration
|
||||||
|
- 将状态管理移至Pinia stores
|
||||||
|
|
||||||
|
3. **重构组件数据流**
|
||||||
|
- 使用props传递数据
|
||||||
|
- 使用emit触发事件
|
||||||
|
- 通过状态管理实现响应式更新
|
||||||
|
|
||||||
|
### 6.2 现有组件优化
|
||||||
|
|
||||||
|
1. **DesktopContainer组件**
|
||||||
|
- 保持现有的inject机制获取系统服务
|
||||||
|
- 将应用启动逻辑完全委托给系统服务
|
||||||
|
- 通过响应式数据驱动UI更新
|
||||||
|
|
||||||
|
2. **AppIcon组件**
|
||||||
|
- 保持纯展示组件特性
|
||||||
|
- 通过事件与父组件通信
|
||||||
|
|
||||||
|
3. **WindowManager组件**
|
||||||
|
- 保持现有的窗口管理逻辑
|
||||||
|
- 通过inject获取系统服务
|
||||||
|
|
||||||
|
4. **AppRenderer组件**
|
||||||
|
- 负责应用渲染逻辑
|
||||||
|
- 支持内置应用和外部应用渲染
|
||||||
|
- 内置应用使用Vue组件直接渲染
|
||||||
|
- 外部应用使用iframe加载渲染
|
||||||
|
|
||||||
|
### 6.3 核心改造点
|
||||||
|
|
||||||
|
#### 6.3.1 DesktopContainer组件改造
|
||||||
|
|
||||||
|
当前的DesktopContainer组件包含了太多业务逻辑,需要进行以下优化:
|
||||||
|
|
||||||
|
1. **保持组件结构**:无需拆分为多个组件,但需明确职责划分
|
||||||
|
2. **业务逻辑委托**:将应用启动等业务逻辑完全委托给系统服务
|
||||||
|
3. **状态管理优化**:通过响应式数据驱动UI更新
|
||||||
|
|
||||||
|
#### 6.3.2 应用启动流程优化
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[用户双击图标] --> B[DesktopContainer组件]
|
||||||
|
B --> C[调用系统服务]
|
||||||
|
C --> D[SystemServiceIntegration]
|
||||||
|
D --> E[ApplicationLifecycleManager]
|
||||||
|
E --> F[创建应用实例]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6.3.3 子应用加载机制
|
||||||
|
|
||||||
|
项目需要支持两种类型的应用加载:
|
||||||
|
|
||||||
|
1. **内置应用加载**:
|
||||||
|
- 直接使用Vue 3组件加载机制
|
||||||
|
- 通过AppRegistry获取应用组件
|
||||||
|
- 无需异步加载,立即渲染
|
||||||
|
|
||||||
|
2. **外部应用加载**:
|
||||||
|
- 使用iframe沙箱环境加载
|
||||||
|
- 通过ExternalAppDiscovery发现应用
|
||||||
|
- 异步加载应用资源
|
||||||
|
- 外部加载逻辑可暂时留空,后期实现
|
||||||
|
|
||||||
|
### 6.4 依赖注入优化
|
||||||
|
|
||||||
|
项目已通过Vue的provide/inject机制实现服务注入,建议保持现有模式并进行优化:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// main.ts - 保持现有实现
|
||||||
|
app.provide('systemService', systemService)
|
||||||
|
|
||||||
|
// UI组件中使用 - 保持现有实现
|
||||||
|
const systemService = inject<SystemServiceIntegration>('systemService')
|
||||||
|
|
||||||
|
// 建议添加类型安全检查
|
||||||
|
const systemService = inject<SystemServiceIntegration>('systemService')
|
||||||
|
if (!systemService) {
|
||||||
|
throw new Error('系统服务未正确注入')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. 测试策略
|
||||||
|
|
||||||
|
### 7.1 UI组件测试
|
||||||
|
|
||||||
|
- 展示组件:测试不同props输入下的渲染结果
|
||||||
|
- 容器组件:测试与状态管理和服务的交互
|
||||||
|
|
||||||
|
### 7.2 状态管理测试
|
||||||
|
|
||||||
|
- 测试状态更新的正确性
|
||||||
|
- 测试响应式数据的自动更新
|
||||||
|
|
||||||
|
### 7.3 集成测试
|
||||||
|
|
||||||
|
- 测试完整的数据流和用户操作流程
|
||||||
|
- 验证UI与核心服务的交互正确性
|
||||||
|
|
||||||
|
### 7.4 现有测试策略优化
|
||||||
|
|
||||||
|
项目已具备基本的测试框架,建议:
|
||||||
|
|
||||||
|
1. 增加对展示组件的单元测试
|
||||||
|
2. 完善容器组件与服务的集成测试
|
||||||
|
3. 建立端到端测试覆盖核心用户场景
|
||||||
167
PRETTIER_CONFIG_GUIDE.md
Normal file
167
PRETTIER_CONFIG_GUIDE.md
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
# Prettier 常用配置项详解
|
||||||
|
|
||||||
|
## 基础格式化选项
|
||||||
|
|
||||||
|
### semi (boolean)
|
||||||
|
|
||||||
|
- **默认值**: true
|
||||||
|
- **说明**: 在语句末尾打印分号
|
||||||
|
- **示例**:
|
||||||
|
- true: `console.log('hello');`
|
||||||
|
- false: `console.log('hello')`
|
||||||
|
|
||||||
|
### singleQuote (boolean)
|
||||||
|
|
||||||
|
- **默认值**: false
|
||||||
|
- **说明**: 使用单引号而不是双引号
|
||||||
|
- **示例**:
|
||||||
|
- true: `const str = 'hello';`
|
||||||
|
- false: `const str = "hello";`
|
||||||
|
|
||||||
|
### printWidth (number)
|
||||||
|
|
||||||
|
- **默认值**: 80
|
||||||
|
- **说明**: 指定每行代码的最大字符数,超过这个长度会自动换行
|
||||||
|
|
||||||
|
### useTabs (boolean)
|
||||||
|
|
||||||
|
- **默认值**: false
|
||||||
|
- **说明**: 使用制表符(tab)缩进而不是空格
|
||||||
|
|
||||||
|
### tabWidth (number)
|
||||||
|
|
||||||
|
- **默认值**: 2
|
||||||
|
- **说明**: 指定每个缩进级别的空格数
|
||||||
|
|
||||||
|
## 对象和数组格式化
|
||||||
|
|
||||||
|
### bracketSpacing (boolean)
|
||||||
|
|
||||||
|
- **默认值**: true
|
||||||
|
- **说明**: 在对象字面量中的括号之间打印空格
|
||||||
|
- **示例**:
|
||||||
|
- true: `{ foo: bar }`
|
||||||
|
- false: `{foo: bar}`
|
||||||
|
|
||||||
|
### bracketSameLine (boolean)
|
||||||
|
|
||||||
|
- **默认值**: false
|
||||||
|
- **说明**: 将多行 HTML 元素的 > 放在最后一行的末尾,而不是单独放在下一行
|
||||||
|
|
||||||
|
### trailingComma (string)
|
||||||
|
|
||||||
|
- **默认值**: "es5"
|
||||||
|
- **可选值**: "none" | "es5" | "all"
|
||||||
|
- **说明**: 在多行对象或数组的最后一个元素后是否添加逗号
|
||||||
|
- **示例**:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// "none"
|
||||||
|
{
|
||||||
|
foo: 'bar'
|
||||||
|
}
|
||||||
|
|
||||||
|
// "es5"
|
||||||
|
{
|
||||||
|
foo: 'bar',
|
||||||
|
}
|
||||||
|
|
||||||
|
// "all"
|
||||||
|
{
|
||||||
|
foo: 'bar',
|
||||||
|
baz: 'qux',
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## JSX 特定选项
|
||||||
|
|
||||||
|
### jsxSingleQuote (boolean)
|
||||||
|
|
||||||
|
- **默认值**: false
|
||||||
|
- **说明**: 在 JSX 中使用单引号而不是双引号
|
||||||
|
|
||||||
|
### singleAttributePerLine (boolean)
|
||||||
|
|
||||||
|
- **默认值**: false
|
||||||
|
- **说明**: 强制每个 HTML、Vue 和 JSX 属性独占一行
|
||||||
|
- **示例**:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- singleAttributePerLine: false -->
|
||||||
|
<button class="btn" id="submit" type="submit">Submit</button>
|
||||||
|
|
||||||
|
<!-- singleAttributePerLine: true -->
|
||||||
|
<button class="btn" id="submit" type="submit">Submit</button>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 函数和箭头函数
|
||||||
|
|
||||||
|
### arrowParens (string)
|
||||||
|
|
||||||
|
- **默认值**: "always"
|
||||||
|
- **可选值**: "always" | "avoid"
|
||||||
|
- **说明**: 在箭头函数参数周围始终包含括号
|
||||||
|
- **示例**:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// "always"
|
||||||
|
const fn = (x) => x
|
||||||
|
|
||||||
|
// "avoid"
|
||||||
|
const fn = (x) => x
|
||||||
|
const fn2 = (x, y) => x + y
|
||||||
|
```
|
||||||
|
|
||||||
|
## 其他格式化选项
|
||||||
|
|
||||||
|
### endOfLine (string)
|
||||||
|
|
||||||
|
- **默认值**: "lf"
|
||||||
|
- **可选值**: "auto" | "lf" | "crlf" | "cr"
|
||||||
|
- **说明**: 指定换行符风格
|
||||||
|
- "lf": \n (Linux/macOS)
|
||||||
|
- "crlf": \r\n (Windows)
|
||||||
|
|
||||||
|
### quoteProps (string)
|
||||||
|
|
||||||
|
- **默认值**: "as-needed"
|
||||||
|
- **可选值**: "as-needed" | "consistent" | "preserve"
|
||||||
|
- **说明**: 对象属性引用方式
|
||||||
|
- "as-needed": 仅在需要时才引用属性
|
||||||
|
- "consistent": 如果至少有一个属性需要引用,则引用所有属性
|
||||||
|
- "preserve": 保持原样
|
||||||
|
|
||||||
|
### htmlWhitespaceSensitivity (string)
|
||||||
|
|
||||||
|
- **默认值**: "css"
|
||||||
|
- **可选值**: "css" | "strict" | "ignore"
|
||||||
|
- **说明**: 指定 HTML、Vue、Angular 文件中全局空白敏感度
|
||||||
|
- "css": 尊重 CSS display 属性的默认值
|
||||||
|
- "strict": 所有空白都被认为是重要的
|
||||||
|
- "ignore": 所有空白都被认为是不重要的
|
||||||
|
|
||||||
|
### vueIndentScriptAndStyle (boolean)
|
||||||
|
|
||||||
|
- **默认值**: false
|
||||||
|
- **说明**: Vue 文件中单文件组件的 `<script>` 和 `<style>` 标签内的代码是否缩进
|
||||||
|
|
||||||
|
### experimentalTernaries (boolean)
|
||||||
|
|
||||||
|
- **默认值**: false
|
||||||
|
- **说明**: 尝试 Prettier 的新三元表达式格式化方式,在成为默认行为之前使用
|
||||||
|
|
||||||
|
### experimentalOperatorPosition (string)
|
||||||
|
|
||||||
|
- **默认值**: "end"
|
||||||
|
- **可选值**: "start" | "end"
|
||||||
|
- **说明**: 当二元表达式换行时,操作符的位置
|
||||||
|
- "start": 操作符放在新行的开头
|
||||||
|
- "end": 操作符放在前一行的末尾(默认行为)
|
||||||
|
|
||||||
|
### objectWrap (string)
|
||||||
|
|
||||||
|
- **默认值**: "preserve"
|
||||||
|
- **可选值**: "preserve" | "collapse"
|
||||||
|
- **说明**: 配置 Prettier 如何包装对象字面量
|
||||||
|
- "preserve": 如果在开括号和第一个属性之间有换行符,则保持多行格式
|
||||||
|
- "collapse": 如果可能,将对象压缩到单行
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { AppRegistration } from './types/AppManifest'
|
import type { AppRegistration } from './types/AppManifest'
|
||||||
import { reactive, markRaw } from 'vue'
|
import { reactive } from 'vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用注册中心
|
* 应用注册中心
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import { EventBuilderImpl } from '@/events/impl/EventBuilderImpl.ts'
|
|
||||||
import type { IEventMap } from '@/events/IEventBuilder.ts'
|
|
||||||
import type { IDesktopAppIcon } from '@/ui/types/IDesktopAppIcon.ts'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 桌面相关的事件
|
|
||||||
*/
|
|
||||||
export interface IDesktopEvent extends IEventMap {
|
|
||||||
/**
|
|
||||||
* 桌面应用位置改变
|
|
||||||
*/
|
|
||||||
desktopAppPosChange: (info: IDesktopAppIcon) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 窗口事件管理器 */
|
|
||||||
export const desktopEM = new EventBuilderImpl<IDesktopEvent>()
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import { EventBuilderImpl } from '@/core/events/impl/EventBuilderImpl.ts'
|
|
||||||
import type { IEventMap } from '@/core/events/IEventBuilder.ts'
|
|
||||||
import type { IDesktopAppIcon } from '@/core/desktop/types/IDesktopAppIcon.ts'
|
|
||||||
|
|
||||||
export const eventManager = new EventBuilderImpl<IAllEvent>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 系统进程的事件
|
|
||||||
* @description
|
|
||||||
* <p>onAuthChange - 认证状态改变</p>
|
|
||||||
* <p>onThemeChange - 主题改变</p>
|
|
||||||
*/
|
|
||||||
export interface IBasicSystemEvent extends IEventMap {
|
|
||||||
/** 认证状态改变 */
|
|
||||||
onAuthChange: () => {},
|
|
||||||
/** 主题改变 */
|
|
||||||
onThemeChange: (theme: string) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 桌面进程的事件
|
|
||||||
* @description
|
|
||||||
* <p>onDesktopRootDomResize - 桌面根dom尺寸改变</p>
|
|
||||||
* <p>onDesktopProcessInitialize - 桌面进程初始化完成</p>
|
|
||||||
*/
|
|
||||||
export interface IDesktopEvent extends IEventMap {
|
|
||||||
/** 桌面根dom尺寸改变 */
|
|
||||||
onDesktopRootDomResize: (width: number, height: number) => void
|
|
||||||
/** 桌面进程初始化完成 */
|
|
||||||
onDesktopProcessInitialize: () => void
|
|
||||||
/** 桌面应用图标位置改变 */
|
|
||||||
onDesktopAppIconPos: (iconInfo: IDesktopAppIcon) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IAllEvent extends IDesktopEvent, IBasicSystemEvent {}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { IDestroyable } from '@/core/common/types/IDestroyable.ts'
|
import type { IDestroyable } from '@/common/types/IDestroyable'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 事件定义
|
* 事件定义
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
import { EventBuilderImpl } from '@/events/impl/EventBuilderImpl.ts'
|
|
||||||
import type { IEventMap } from '@/events/IEventBuilder.ts'
|
|
||||||
import type { TWindowFormState } from '@/ui/types/WindowFormTypes.ts'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 窗口的事件
|
|
||||||
*/
|
|
||||||
export interface IWindowFormEvent extends IEventMap {
|
|
||||||
/**
|
|
||||||
* 窗口最小化
|
|
||||||
* @param id 窗口id
|
|
||||||
*/
|
|
||||||
windowFormMinimize: (id: string) => void
|
|
||||||
/**
|
|
||||||
* 窗口最大化
|
|
||||||
* @param id 窗口id
|
|
||||||
*/
|
|
||||||
windowFormMaximize: (id: string) => void
|
|
||||||
/**
|
|
||||||
* 窗口还原
|
|
||||||
* @param id 窗口id
|
|
||||||
*/
|
|
||||||
windowFormRestore: (id: string) => void
|
|
||||||
/**
|
|
||||||
* 窗口关闭
|
|
||||||
* @param id 窗口id
|
|
||||||
*/
|
|
||||||
windowFormClose: (id: string) => void
|
|
||||||
/**
|
|
||||||
* 窗口聚焦
|
|
||||||
* @param id 窗口id
|
|
||||||
*/
|
|
||||||
windowFormFocus: (id: string) => void
|
|
||||||
/**
|
|
||||||
* 窗口数据更新
|
|
||||||
* @param data 窗口数据
|
|
||||||
*/
|
|
||||||
windowFormDataUpdate: (data: IWindowFormDataUpdateParams) => void
|
|
||||||
/**
|
|
||||||
* 窗口创建完成
|
|
||||||
*/
|
|
||||||
windowFormCreated: () => void
|
|
||||||
/**
|
|
||||||
* 开始调整窗体尺寸
|
|
||||||
* @param id 窗口id
|
|
||||||
*/
|
|
||||||
windowFormResizeStart: (id: string) => void
|
|
||||||
/**
|
|
||||||
* 调整尺寸过程中
|
|
||||||
* @param data 窗口数据
|
|
||||||
*/
|
|
||||||
windowFormResizing: (data: IWindowFormDataUpdateParams) => void
|
|
||||||
/**
|
|
||||||
* 完成窗体尺寸调整
|
|
||||||
* @param id 窗口id
|
|
||||||
*/
|
|
||||||
windowFormResizeEnd: (id: string) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IWindowFormDataUpdateParams {
|
|
||||||
/** 窗口id */
|
|
||||||
id: string
|
|
||||||
/** 窗口状态 */
|
|
||||||
state: TWindowFormState
|
|
||||||
/** 窗口宽度 */
|
|
||||||
width: number
|
|
||||||
/** 窗口高度 */
|
|
||||||
height: number
|
|
||||||
/** 窗口x坐标(左上角) */
|
|
||||||
x: number
|
|
||||||
/** 窗口y坐标(左上角) */
|
|
||||||
y: number
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 窗口事件管理器 */
|
|
||||||
export const wfem = new EventBuilderImpl<IWindowFormEvent>()
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { IEventBuilder, IEventMap } from '@/core/events/IEventBuilder.ts'
|
import type { IEventBuilder, IEventMap } from '../IEventBuilder.ts'
|
||||||
|
|
||||||
interface HandlerWrapper<T extends (...args: any[]) => any> {
|
interface HandlerWrapper<T extends (...args: any[]) => any> {
|
||||||
fn: T
|
fn: T
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ app.use(naiveUi)
|
|||||||
app.provide('systemService', systemService)
|
app.provide('systemService', systemService)
|
||||||
|
|
||||||
// 初始化系统服务然后挂载应用
|
// 初始化系统服务然后挂载应用
|
||||||
systemService.initialize()
|
systemService
|
||||||
|
.initialize()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
console.log('桌面系统启动完成')
|
console.log('桌面系统启动完成')
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { reactive } from 'vue'
|
import { reactive } from 'vue'
|
||||||
import type { IEventBuilder } from '@/events/IEventBuilder'
|
import type { IEventBuilder, IEventMap } from '@/events/IEventBuilder'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
import type { WindowState } from './WindowService'
|
||||||
|
import type { ResourceType } from './ResourceService'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息类型枚举
|
* 消息类型枚举
|
||||||
@@ -90,6 +92,51 @@ export interface EventStatistics {
|
|||||||
channelUsage: Map<string, number>
|
channelUsage: Map<string, number>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 窗体数据更新参数
|
||||||
|
*/
|
||||||
|
export interface WindowFormDataUpdateParams {
|
||||||
|
/** 窗口id */
|
||||||
|
id: string
|
||||||
|
/** 窗口状态 */
|
||||||
|
state: WindowState
|
||||||
|
/** 窗口宽度 */
|
||||||
|
width: number
|
||||||
|
/** 窗口高度 */
|
||||||
|
height: number
|
||||||
|
/** 窗口x坐标(左上角) */
|
||||||
|
x: number
|
||||||
|
/** 窗口y坐标(左上角) */
|
||||||
|
y: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件通信服务事件接口
|
||||||
|
*/
|
||||||
|
export interface EventCommunicationEvents extends IEventMap {
|
||||||
|
// 系统就绪事件
|
||||||
|
onSystemReady: (data: { timestamp: Date; services: string[] }) => void
|
||||||
|
|
||||||
|
// 窗体相关事件
|
||||||
|
onWindowStateChanged: (windowId: string, newState: string, oldState: string) => void
|
||||||
|
onWindowFormDataUpdate: (data: WindowFormDataUpdateParams) => void
|
||||||
|
onWindowFormResizeStart: (windowId: string) => void
|
||||||
|
onWindowFormResizing: (windowId: string, width: number, height: number) => void
|
||||||
|
onWindowFormResizeEnd: (windowId: string) => void
|
||||||
|
onWindowClose: (windowId: string) => void
|
||||||
|
|
||||||
|
// 应用生命周期事件
|
||||||
|
onAppLifecycle: (data: { appId: string; event: string; timestamp: Date }) => void
|
||||||
|
|
||||||
|
// 资源相关事件
|
||||||
|
onResourceQuotaExceeded: (appId: string, resourceType: ResourceType) => void
|
||||||
|
onPerformanceAlert: (data: { type: 'memory' | 'cpu'; usage: number; limit: number }) => void
|
||||||
|
|
||||||
|
// 消息处理事件
|
||||||
|
onMessageProcessed: (message: EventMessage) => void
|
||||||
|
onMessageFailed: (message: EventMessage, error: any) => void
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 事件通信服务类
|
* 事件通信服务类
|
||||||
*/
|
*/
|
||||||
@@ -108,9 +155,9 @@ export class EventCommunicationService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
private processingInterval: number | null = null
|
private processingInterval: number | null = null
|
||||||
private eventBus: IEventBuilder<any>
|
private eventBus: IEventBuilder<EventCommunicationEvents>
|
||||||
|
|
||||||
constructor(eventBus: IEventBuilder<any>) {
|
constructor(eventBus: IEventBuilder<EventCommunicationEvents>) {
|
||||||
this.eventBus = eventBus
|
this.eventBus = eventBus
|
||||||
this.initializeDefaultChannels()
|
this.initializeDefaultChannels()
|
||||||
this.startMessageProcessing()
|
this.startMessageProcessing()
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import { reactive, ref } from 'vue'
|
import { reactive, ref } from 'vue'
|
||||||
import { EventBuilderImpl } from '@/events/impl/EventBuilderImpl'
|
import { EventBuilderImpl } from '@/events/impl/EventBuilderImpl'
|
||||||
import type { IEventBuilder } from '@/events/IEventBuilder'
|
import type { IEventBuilder } from '@/events/IEventBuilder'
|
||||||
|
import type {
|
||||||
|
EventCommunicationEvents,
|
||||||
|
WindowFormDataUpdateParams,
|
||||||
|
} from './EventCommunicationService'
|
||||||
|
import type { ResourceType } from './ResourceService'
|
||||||
|
|
||||||
// 导入所有服务
|
// 导入所有服务
|
||||||
import { WindowService } from './WindowService'
|
import { WindowService } from './WindowService'
|
||||||
@@ -9,7 +14,6 @@ import { EventCommunicationService } from './EventCommunicationService'
|
|||||||
import { ApplicationSandboxEngine } from './ApplicationSandboxEngine'
|
import { ApplicationSandboxEngine } from './ApplicationSandboxEngine'
|
||||||
import { ApplicationLifecycleManager } from './ApplicationLifecycleManager'
|
import { ApplicationLifecycleManager } from './ApplicationLifecycleManager'
|
||||||
import { externalAppDiscovery } from './ExternalAppDiscovery'
|
import { externalAppDiscovery } from './ExternalAppDiscovery'
|
||||||
import type { IWindowFormDataUpdateParams } from '@/events/WindowFormEventManager'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 系统服务配置接口
|
* 系统服务配置接口
|
||||||
@@ -68,7 +72,7 @@ export class SystemServiceIntegration {
|
|||||||
private startTime: Date
|
private startTime: Date
|
||||||
|
|
||||||
// 核心服务实例
|
// 核心服务实例
|
||||||
private eventBus: IEventBuilder<any>
|
private eventBus: IEventBuilder<EventCommunicationEvents>
|
||||||
private windowService!: WindowService
|
private windowService!: WindowService
|
||||||
private resourceService!: ResourceService
|
private resourceService!: ResourceService
|
||||||
private eventService!: EventCommunicationService
|
private eventService!: EventCommunicationService
|
||||||
@@ -112,7 +116,7 @@ export class SystemServiceIntegration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.startTime = new Date()
|
this.startTime = new Date()
|
||||||
this.eventBus = new EventBuilderImpl()
|
this.eventBus = new EventBuilderImpl<EventCommunicationEvents>()
|
||||||
|
|
||||||
this.setupGlobalErrorHandling()
|
this.setupGlobalErrorHandling()
|
||||||
}
|
}
|
||||||
@@ -373,7 +377,7 @@ export class SystemServiceIntegration {
|
|||||||
|
|
||||||
// 监听窗体状态变化(来自 WindowService 的 onStateChange 事件)
|
// 监听窗体状态变化(来自 WindowService 的 onStateChange 事件)
|
||||||
this.eventBus.addEventListener(
|
this.eventBus.addEventListener(
|
||||||
'onStateChange',
|
'onWindowStateChanged',
|
||||||
(windowId: string, newState: string, oldState: string) => {
|
(windowId: string, newState: string, oldState: string) => {
|
||||||
console.log(
|
console.log(
|
||||||
`[SystemIntegration] 接收到窗体状态变化事件: ${windowId} ${oldState} -> ${newState}`,
|
`[SystemIntegration] 接收到窗体状态变化事件: ${windowId} ${oldState} -> ${newState}`,
|
||||||
@@ -388,7 +392,7 @@ export class SystemServiceIntegration {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 监听窗体关闭事件,自动停止对应的应用
|
// 监听窗体关闭事件,自动停止对应的应用
|
||||||
this.eventBus.addEventListener('onClose', async (windowId: string) => {
|
this.eventBus.addEventListener('onWindowClose', async (windowId: string) => {
|
||||||
console.log(`[SystemIntegration] 接收到窗体关闭事件: ${windowId}`)
|
console.log(`[SystemIntegration] 接收到窗体关闭事件: ${windowId}`)
|
||||||
// 查找对应的应用
|
// 查找对应的应用
|
||||||
const runningApps = this.lifecycleManager.getRunningApps()
|
const runningApps = this.lifecycleManager.getRunningApps()
|
||||||
@@ -406,9 +410,7 @@ export class SystemServiceIntegration {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 监听窗体数据更新事件
|
// 监听窗体数据更新事件
|
||||||
this.eventBus.addEventListener(
|
this.eventBus.addEventListener('onWindowFormDataUpdate', (data: WindowFormDataUpdateParams) => {
|
||||||
'onWindowFormDataUpdate',
|
|
||||||
(data: IWindowFormDataUpdateParams) => {
|
|
||||||
console.log(`[SystemIntegration] 接收到窗体数据更新事件:`, data)
|
console.log(`[SystemIntegration] 接收到窗体数据更新事件:`, data)
|
||||||
// 只有在有订阅者时才发送消息
|
// 只有在有订阅者时才发送消息
|
||||||
if (this.eventService.getChannelSubscriberCount('window-form-data-update') > 0) {
|
if (this.eventService.getChannelSubscriberCount('window-form-data-update') > 0) {
|
||||||
@@ -417,11 +419,10 @@ export class SystemServiceIntegration {
|
|||||||
} else {
|
} else {
|
||||||
console.log(`[SystemIntegration] 无订阅者,跳过发送 window-form-data-update 消息`)
|
console.log(`[SystemIntegration] 无订阅者,跳过发送 window-form-data-update 消息`)
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
)
|
|
||||||
|
|
||||||
// 监听窗体调整尺寸开始事件
|
// 监听窗体调整尺寸开始事件
|
||||||
this.eventBus.addEventListener('onResizeStart', (windowId: string) => {
|
this.eventBus.addEventListener('onWindowFormResizeStart', (windowId: string) => {
|
||||||
console.log(`[SystemIntegration] 接收到窗体调整尺寸开始事件: ${windowId}`)
|
console.log(`[SystemIntegration] 接收到窗体调整尺寸开始事件: ${windowId}`)
|
||||||
// 只有在有订阅者时才发送消息
|
// 只有在有订阅者时才发送消息
|
||||||
if (this.eventService.getChannelSubscriberCount('window-form-resize-start') > 0) {
|
if (this.eventService.getChannelSubscriberCount('window-form-resize-start') > 0) {
|
||||||
@@ -433,7 +434,7 @@ export class SystemServiceIntegration {
|
|||||||
|
|
||||||
// 监听窗体调整尺寸过程中事件
|
// 监听窗体调整尺寸过程中事件
|
||||||
this.eventBus.addEventListener(
|
this.eventBus.addEventListener(
|
||||||
'onResizing',
|
'onWindowFormResizing',
|
||||||
(windowId: string, width: number, height: number) => {
|
(windowId: string, width: number, height: number) => {
|
||||||
console.log(`[SystemIntegration] 接收到窗体调整尺寸过程中事件: ${windowId}`, {
|
console.log(`[SystemIntegration] 接收到窗体调整尺寸过程中事件: ${windowId}`, {
|
||||||
width,
|
width,
|
||||||
@@ -453,7 +454,7 @@ export class SystemServiceIntegration {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 监听窗体调整尺寸结束事件
|
// 监听窗体调整尺寸结束事件
|
||||||
this.eventBus.addEventListener('onResizeEnd', (windowId: string) => {
|
this.eventBus.addEventListener('onWindowFormResizeEnd', (windowId: string) => {
|
||||||
console.log(`[SystemIntegration] 接收到窗体调整尺寸结束事件: ${windowId}`)
|
console.log(`[SystemIntegration] 接收到窗体调整尺寸结束事件: ${windowId}`)
|
||||||
// 只有在有订阅者时才发送消息
|
// 只有在有订阅者时才发送消息
|
||||||
if (this.eventService.getChannelSubscriberCount('window-form-resize-end') > 0) {
|
if (this.eventService.getChannelSubscriberCount('window-form-resize-end') > 0) {
|
||||||
@@ -466,7 +467,7 @@ export class SystemServiceIntegration {
|
|||||||
// 监听资源配额超出
|
// 监听资源配额超出
|
||||||
this.eventBus.addEventListener(
|
this.eventBus.addEventListener(
|
||||||
'onResourceQuotaExceeded',
|
'onResourceQuotaExceeded',
|
||||||
(appId: string, resourceType: string) => {
|
(appId: string, resourceType: ResourceType) => {
|
||||||
console.log(`[SystemIntegration] 接收到资源配额超出事件: ${appId} - ${resourceType}`)
|
console.log(`[SystemIntegration] 接收到资源配额超出事件: ${appId} - ${resourceType}`)
|
||||||
this.eventService.sendMessage('system', 'resource-quota-exceeded', {
|
this.eventService.sendMessage('system', 'resource-quota-exceeded', {
|
||||||
appId,
|
appId,
|
||||||
@@ -627,17 +628,17 @@ export class SystemServiceIntegration {
|
|||||||
|
|
||||||
case 'move':
|
case 'move':
|
||||||
// 实现窗体移动功能
|
// 实现窗体移动功能
|
||||||
const window = this.windowService.getWindow(windowId);
|
const window = this.windowService.getWindow(windowId)
|
||||||
if (window && window.element) {
|
if (window && window.element) {
|
||||||
// 更新窗体位置
|
// 更新窗体位置
|
||||||
window.config.x = data.x;
|
window.config.x = data.x
|
||||||
window.config.y = data.y;
|
window.config.y = data.y
|
||||||
window.element.style.left = `${data.x}px`;
|
window.element.style.left = `${data.x}px`
|
||||||
window.element.style.top = `${data.y}px`;
|
window.element.style.top = `${data.y}px`
|
||||||
window.element.style.transform = 'none'; // 确保移除transform
|
window.element.style.transform = 'none' // 确保移除transform
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
return false;
|
return false
|
||||||
|
|
||||||
case 'minimize':
|
case 'minimize':
|
||||||
return this.windowService.minimizeWindow(windowId)
|
return this.windowService.minimizeWindow(windowId)
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ export class WindowService {
|
|||||||
width: '100vw',
|
width: '100vw',
|
||||||
height: 'calc(100vh - 40px)', // 减去任务栏高度
|
height: 'calc(100vh - 40px)', // 减去任务栏高度
|
||||||
display: 'block',
|
display: 'block',
|
||||||
transform: 'none' // 确保移除transform
|
transform: 'none', // 确保移除transform
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,12 +331,12 @@ export class WindowService {
|
|||||||
height: originalHeight ? `${originalHeight}px` : `${window.config.height}px`,
|
height: originalHeight ? `${originalHeight}px` : `${window.config.height}px`,
|
||||||
left: originalX ? `${originalX}px` : '0px',
|
left: originalX ? `${originalX}px` : '0px',
|
||||||
top: originalY ? `${originalY}px` : '0px',
|
top: originalY ? `${originalY}px` : '0px',
|
||||||
transform: 'none' // 确保移除transform
|
transform: 'none', // 确保移除transform
|
||||||
})
|
})
|
||||||
|
|
||||||
// 更新配置中的位置
|
// 更新配置中的位置
|
||||||
if (originalX) window.config.x = parseFloat(originalX);
|
if (originalX) window.config.x = parseFloat(originalX)
|
||||||
if (originalY) window.config.y = parseFloat(originalY);
|
if (originalY) window.config.y = parseFloat(originalY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,7 +381,11 @@ export class WindowService {
|
|||||||
|
|
||||||
// 检查尺寸限制
|
// 检查尺寸限制
|
||||||
const finalWidth = this.clampDimension(width, window.config.minWidth, window.config.maxWidth)
|
const finalWidth = this.clampDimension(width, window.config.minWidth, window.config.maxWidth)
|
||||||
const finalHeight = this.clampDimension(height, window.config.minHeight, window.config.maxHeight)
|
const finalHeight = this.clampDimension(
|
||||||
|
height,
|
||||||
|
window.config.minHeight,
|
||||||
|
window.config.maxHeight,
|
||||||
|
)
|
||||||
|
|
||||||
window.config.width = finalWidth
|
window.config.width = finalWidth
|
||||||
window.config.height = finalHeight
|
window.config.height = finalHeight
|
||||||
@@ -465,15 +469,15 @@ export class WindowService {
|
|||||||
windowElement.id = `window-${id}`
|
windowElement.id = `window-${id}`
|
||||||
|
|
||||||
// 计算初始位置
|
// 计算初始位置
|
||||||
let left = config.x;
|
let left = config.x
|
||||||
let top = config.y;
|
let top = config.y
|
||||||
|
|
||||||
// 如果没有指定位置,则居中显示
|
// 如果没有指定位置,则居中显示
|
||||||
if (left === undefined || top === undefined) {
|
if (left === undefined || top === undefined) {
|
||||||
const centerX = Math.max(0, (window.innerWidth - config.width) / 2);
|
const centerX = Math.max(0, (window.innerWidth - config.width) / 2)
|
||||||
const centerY = Math.max(0, (window.innerHeight - config.height) / 2);
|
const centerY = Math.max(0, (window.innerHeight - config.height) / 2)
|
||||||
left = left !== undefined ? left : centerX;
|
left = left !== undefined ? left : centerX
|
||||||
top = top !== undefined ? top : centerY;
|
top = top !== undefined ? top : centerY
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置基本样式
|
// 设置基本样式
|
||||||
@@ -489,11 +493,11 @@ export class WindowService {
|
|||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
boxShadow: '0 4px 20px rgba(0,0,0,0.15)',
|
boxShadow: '0 4px 20px rgba(0,0,0,0.15)',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
});
|
})
|
||||||
|
|
||||||
// 保存初始位置到配置中
|
// 保存初始位置到配置中
|
||||||
windowInstance.config.x = left;
|
windowInstance.config.x = left
|
||||||
windowInstance.config.y = top;
|
windowInstance.config.y = top
|
||||||
|
|
||||||
// 创建窗体标题栏
|
// 创建窗体标题栏
|
||||||
const titleBar = this.createTitleBar(windowInstance)
|
const titleBar = this.createTitleBar(windowInstance)
|
||||||
@@ -676,13 +680,13 @@ export class WindowService {
|
|||||||
titleBar.addEventListener('mousedown', (e) => {
|
titleBar.addEventListener('mousedown', (e) => {
|
||||||
// 检查是否正在调整尺寸,如果是则不处理拖拽
|
// 检查是否正在调整尺寸,如果是则不处理拖拽
|
||||||
if (windowInstance.resizeState?.isResizing) {
|
if (windowInstance.resizeState?.isResizing) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否点击在调整尺寸手柄上,如果是则不处理拖拽
|
// 检查是否点击在调整尺寸手柄上,如果是则不处理拖拽
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement
|
||||||
if (target.classList.contains('resize-handle')) {
|
if (target.classList.contains('resize-handle')) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!windowInstance.element) return
|
if (!windowInstance.element) return
|
||||||
@@ -694,13 +698,16 @@ export class WindowService {
|
|||||||
const rect = windowInstance.element.getBoundingClientRect()
|
const rect = windowInstance.element.getBoundingClientRect()
|
||||||
|
|
||||||
// 如果使用了transform,需要转换为实际坐标
|
// 如果使用了transform,需要转换为实际坐标
|
||||||
if (windowInstance.element.style.transform && windowInstance.element.style.transform.includes('translate')) {
|
if (
|
||||||
|
windowInstance.element.style.transform &&
|
||||||
|
windowInstance.element.style.transform.includes('translate')
|
||||||
|
) {
|
||||||
// 移除transform并设置实际的left/top值
|
// 移除transform并设置实际的left/top值
|
||||||
windowInstance.element.style.transform = 'none';
|
windowInstance.element.style.transform = 'none'
|
||||||
windowInstance.config.x = rect.left;
|
windowInstance.config.x = rect.left
|
||||||
windowInstance.config.y = rect.top;
|
windowInstance.config.y = rect.top
|
||||||
windowInstance.element.style.left = `${rect.left}px`;
|
windowInstance.element.style.left = `${rect.left}px`
|
||||||
windowInstance.element.style.top = `${rect.top}px`;
|
windowInstance.element.style.top = `${rect.top}px`
|
||||||
}
|
}
|
||||||
|
|
||||||
startLeft = rect.left
|
startLeft = rect.left
|
||||||
@@ -716,7 +723,7 @@ export class WindowService {
|
|||||||
document.addEventListener('mousemove', (e) => {
|
document.addEventListener('mousemove', (e) => {
|
||||||
// 检查是否正在调整尺寸,如果是则不处理拖拽
|
// 检查是否正在调整尺寸,如果是则不处理拖拽
|
||||||
if (windowInstance.resizeState?.isResizing) {
|
if (windowInstance.resizeState?.isResizing) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDragging || !windowInstance.element) return
|
if (!isDragging || !windowInstance.element) return
|
||||||
@@ -758,14 +765,14 @@ export class WindowService {
|
|||||||
startWidth: 0,
|
startWidth: 0,
|
||||||
startHeight: 0,
|
startHeight: 0,
|
||||||
startXPosition: 0,
|
startXPosition: 0,
|
||||||
startYPosition: 0
|
startYPosition: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建8个调整尺寸的手柄
|
// 创建8个调整尺寸的手柄
|
||||||
const resizeHandles = this.createResizeHandles(windowElement)
|
const resizeHandles = this.createResizeHandles(windowElement)
|
||||||
|
|
||||||
// 添加鼠标事件监听器
|
// 添加鼠标事件监听器
|
||||||
resizeHandles.forEach(handle => {
|
resizeHandles.forEach((handle) => {
|
||||||
this.addResizeHandleEvents(handle, windowElement, windowInstance)
|
this.addResizeHandleEvents(handle, windowElement, windowInstance)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -787,12 +794,17 @@ export class WindowService {
|
|||||||
private createResizeHandles(windowElement: HTMLElement): HTMLElement[] {
|
private createResizeHandles(windowElement: HTMLElement): HTMLElement[] {
|
||||||
const handles: HTMLElement[] = []
|
const handles: HTMLElement[] = []
|
||||||
const directions: ResizeDirection[] = [
|
const directions: ResizeDirection[] = [
|
||||||
'topLeft', 'top', 'topRight',
|
'topLeft',
|
||||||
'right', 'bottomRight', 'bottom',
|
'top',
|
||||||
'bottomLeft', 'left'
|
'topRight',
|
||||||
|
'right',
|
||||||
|
'bottomRight',
|
||||||
|
'bottom',
|
||||||
|
'bottomLeft',
|
||||||
|
'left',
|
||||||
]
|
]
|
||||||
|
|
||||||
directions.forEach(direction => {
|
directions.forEach((direction) => {
|
||||||
const handle = document.createElement('div')
|
const handle = document.createElement('div')
|
||||||
handle.className = `resize-handle resize-handle-${direction}`
|
handle.className = `resize-handle resize-handle-${direction}`
|
||||||
|
|
||||||
@@ -872,9 +884,12 @@ export class WindowService {
|
|||||||
private addResizeHandleEvents(
|
private addResizeHandleEvents(
|
||||||
handle: HTMLElement,
|
handle: HTMLElement,
|
||||||
windowElement: HTMLElement,
|
windowElement: HTMLElement,
|
||||||
windowInstance: WindowInstance
|
windowInstance: WindowInstance,
|
||||||
): void {
|
): void {
|
||||||
const direction = handle.className.split(' ').find(cls => cls.startsWith('resize-handle-'))?.split('-')[2] as ResizeDirection
|
const direction = handle.className
|
||||||
|
.split(' ')
|
||||||
|
.find((cls) => cls.startsWith('resize-handle-'))
|
||||||
|
?.split('-')[2] as ResizeDirection
|
||||||
|
|
||||||
handle.addEventListener('mousedown', (e) => {
|
handle.addEventListener('mousedown', (e) => {
|
||||||
if (!windowInstance.resizeState) return
|
if (!windowInstance.resizeState) return
|
||||||
@@ -884,15 +899,18 @@ export class WindowService {
|
|||||||
|
|
||||||
// 确保窗体位置是最新的
|
// 确保窗体位置是最新的
|
||||||
if (windowInstance.element) {
|
if (windowInstance.element) {
|
||||||
const rect = windowInstance.element.getBoundingClientRect();
|
const rect = windowInstance.element.getBoundingClientRect()
|
||||||
// 如果使用了transform,需要转换为实际坐标
|
// 如果使用了transform,需要转换为实际坐标
|
||||||
if (windowInstance.element.style.transform && windowInstance.element.style.transform.includes('translate')) {
|
if (
|
||||||
|
windowInstance.element.style.transform &&
|
||||||
|
windowInstance.element.style.transform.includes('translate')
|
||||||
|
) {
|
||||||
// 移除transform并设置实际的left/top值
|
// 移除transform并设置实际的left/top值
|
||||||
windowInstance.element.style.transform = 'none';
|
windowInstance.element.style.transform = 'none'
|
||||||
windowInstance.config.x = rect.left;
|
windowInstance.config.x = rect.left
|
||||||
windowInstance.config.y = rect.top;
|
windowInstance.config.y = rect.top
|
||||||
windowInstance.element.style.left = `${rect.left}px`;
|
windowInstance.element.style.left = `${rect.left}px`
|
||||||
windowInstance.element.style.top = `${rect.top}px`;
|
windowInstance.element.style.top = `${rect.top}px`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -923,7 +941,7 @@ export class WindowService {
|
|||||||
private updateCursorForResize(
|
private updateCursorForResize(
|
||||||
e: MouseEvent,
|
e: MouseEvent,
|
||||||
windowElement: HTMLElement,
|
windowElement: HTMLElement,
|
||||||
windowInstance: WindowInstance
|
windowInstance: WindowInstance,
|
||||||
): void {
|
): void {
|
||||||
if (!windowInstance.resizeState) return
|
if (!windowInstance.resizeState) return
|
||||||
|
|
||||||
@@ -942,22 +960,40 @@ export class WindowService {
|
|||||||
direction = 'topRight'
|
direction = 'topRight'
|
||||||
} else if (x >= 0 && x < edgeSize && y > rect.height - edgeSize && y <= rect.height) {
|
} else if (x >= 0 && x < edgeSize && y > rect.height - edgeSize && y <= rect.height) {
|
||||||
direction = 'bottomLeft'
|
direction = 'bottomLeft'
|
||||||
} else if (x > rect.width - edgeSize && x <= rect.width && y > rect.height - edgeSize && y <= rect.height) {
|
} else if (
|
||||||
|
x > rect.width - edgeSize &&
|
||||||
|
x <= rect.width &&
|
||||||
|
y > rect.height - edgeSize &&
|
||||||
|
y <= rect.height
|
||||||
|
) {
|
||||||
direction = 'bottomRight'
|
direction = 'bottomRight'
|
||||||
}
|
}
|
||||||
// 然后检查边缘区域
|
// 然后检查边缘区域
|
||||||
else if (x >= 0 && x < edgeSize && y >= edgeSize && y <= rect.height - edgeSize) {
|
else if (x >= 0 && x < edgeSize && y >= edgeSize && y <= rect.height - edgeSize) {
|
||||||
direction = 'left'
|
direction = 'left'
|
||||||
} else if (x > rect.width - edgeSize && x <= rect.width && y >= edgeSize && y <= rect.height - edgeSize) {
|
} else if (
|
||||||
|
x > rect.width - edgeSize &&
|
||||||
|
x <= rect.width &&
|
||||||
|
y >= edgeSize &&
|
||||||
|
y <= rect.height - edgeSize
|
||||||
|
) {
|
||||||
direction = 'right'
|
direction = 'right'
|
||||||
} else if (y >= 0 && y < edgeSize && x >= edgeSize && x <= rect.width - edgeSize) {
|
} else if (y >= 0 && y < edgeSize && x >= edgeSize && x <= rect.width - edgeSize) {
|
||||||
direction = 'top'
|
direction = 'top'
|
||||||
} else if (y > rect.height - edgeSize && y <= rect.height && x >= edgeSize && x <= rect.width - edgeSize) {
|
} else if (
|
||||||
|
y > rect.height - edgeSize &&
|
||||||
|
y <= rect.height &&
|
||||||
|
x >= edgeSize &&
|
||||||
|
x <= rect.width - edgeSize
|
||||||
|
) {
|
||||||
direction = 'bottom'
|
direction = 'bottom'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新光标样式
|
// 更新光标样式
|
||||||
windowElement.style.cursor = direction === 'none' ? 'default' : `${direction.replace(/([A-Z])/g, '-$1').toLowerCase()}-resize`
|
windowElement.style.cursor =
|
||||||
|
direction === 'none'
|
||||||
|
? 'default'
|
||||||
|
: `${direction.replace(/([A-Z])/g, '-$1').toLowerCase()}-resize`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -981,21 +1017,14 @@ export class WindowService {
|
|||||||
private handleResizeMouseMove(e: MouseEvent): void {
|
private handleResizeMouseMove(e: MouseEvent): void {
|
||||||
// 找到正在调整尺寸的窗体
|
// 找到正在调整尺寸的窗体
|
||||||
const resizingWindow = Array.from(this.windows.values()).find(
|
const resizingWindow = Array.from(this.windows.values()).find(
|
||||||
window => window.resizeState?.isResizing
|
(window) => window.resizeState?.isResizing,
|
||||||
)
|
)
|
||||||
|
|
||||||
// 如果没有正在调整尺寸的窗体,直接返回
|
// 如果没有正在调整尺寸的窗体,直接返回
|
||||||
if (!resizingWindow || !resizingWindow.resizeState || !resizingWindow.element) return
|
if (!resizingWindow || !resizingWindow.resizeState || !resizingWindow.element) return
|
||||||
|
|
||||||
const {
|
const { direction, startX, startY, startWidth, startHeight, startXPosition, startYPosition } =
|
||||||
direction,
|
resizingWindow.resizeState
|
||||||
startX,
|
|
||||||
startY,
|
|
||||||
startWidth,
|
|
||||||
startHeight,
|
|
||||||
startXPosition,
|
|
||||||
startYPosition
|
|
||||||
} = resizingWindow.resizeState
|
|
||||||
|
|
||||||
const deltaX = e.clientX - startX
|
const deltaX = e.clientX - startX
|
||||||
const deltaY = e.clientY - startY
|
const deltaY = e.clientY - startY
|
||||||
@@ -1044,8 +1073,16 @@ export class WindowService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 应用尺寸限制
|
// 应用尺寸限制
|
||||||
newWidth = this.clampDimension(newWidth, resizingWindow.config.minWidth, resizingWindow.config.maxWidth)
|
newWidth = this.clampDimension(
|
||||||
newHeight = this.clampDimension(newHeight, resizingWindow.config.minHeight, resizingWindow.config.maxHeight)
|
newWidth,
|
||||||
|
resizingWindow.config.minWidth,
|
||||||
|
resizingWindow.config.maxWidth,
|
||||||
|
)
|
||||||
|
newHeight = this.clampDimension(
|
||||||
|
newHeight,
|
||||||
|
resizingWindow.config.minHeight,
|
||||||
|
resizingWindow.config.maxHeight,
|
||||||
|
)
|
||||||
|
|
||||||
// 应用新尺寸和位置
|
// 应用新尺寸和位置
|
||||||
resizingWindow.config.width = newWidth
|
resizingWindow.config.width = newWidth
|
||||||
@@ -1074,7 +1111,7 @@ export class WindowService {
|
|||||||
private handleResizeMouseUp(): void {
|
private handleResizeMouseUp(): void {
|
||||||
// 找到正在调整尺寸的窗体
|
// 找到正在调整尺寸的窗体
|
||||||
const resizingWindow = Array.from(this.windows.values()).find(
|
const resizingWindow = Array.from(this.windows.values()).find(
|
||||||
window => window.resizeState?.isResizing
|
(window) => window.resizeState?.isResizing,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!resizingWindow || !resizingWindow.resizeState || !resizingWindow.element) return
|
if (!resizingWindow || !resizingWindow.resizeState || !resizingWindow.element) return
|
||||||
@@ -1114,7 +1151,7 @@ export class WindowService {
|
|||||||
width: window.config.width,
|
width: window.config.width,
|
||||||
height: window.config.height,
|
height: window.config.height,
|
||||||
x: window.config.x !== undefined ? window.config.x : rect.left,
|
x: window.config.x !== undefined ? window.config.x : rect.left,
|
||||||
y: window.config.y !== undefined ? window.config.y : rect.top
|
y: window.config.y !== undefined ? window.config.y : rect.top,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送事件到事件总线
|
// 发送事件到事件总线
|
||||||
|
|||||||
Reference in New Issue
Block a user