1
This commit is contained in:
@@ -1,185 +1,220 @@
|
||||
# Vue Desktop - 系统与业务解耦架构
|
||||
# Vue Desktop
|
||||
|
||||
基于设计文档实现的高性能类Windows桌面前端系统,实现了系统框架与业务应用的完全解耦。
|
||||
一个基于 Vue 3 + TypeScript + Vite 的现代化桌面环境模拟器
|
||||
|
||||
## 🎯 项目概述
|
||||
## 🎯 项目目标
|
||||
|
||||
本项目成功实现了设计文档中描述的**系统与业务解耦架构**,构建了一个完整的桌面环境模拟系统,包含:
|
||||
构建一个轻量级、模块化的桌面环境,支持:
|
||||
|
||||
- ✅ **完全隔离**:系统与应用在运行时完全隔离,应用无法直接访问系统资源
|
||||
- ✅ **标准化接口**:通过统一的SDK提供标准化的系统服务接口
|
||||
- ✅ **性能优先**:采用微前端沙箱、虚拟化渲染等技术确保高性能
|
||||
- ✅ **框架无关**:支持任意前端框架开发的第三方应用
|
||||
- ✅ **安全可控**:严格的权限控制和安全沙箱机制
|
||||
- 内置应用(计算器、记事本、待办事项等)
|
||||
- 外置应用加载(通过 iframe 沙箱)
|
||||
- 窗口管理(创建、移动、缩放、最小化等)
|
||||
- 资源管理(存储、权限控制)
|
||||
- 应用生命周期管理
|
||||
- 安全沙箱机制
|
||||
|
||||
## 🏗️ 架构实现
|
||||
|
||||
### 核心服务层
|
||||
|
||||
- **[WindowService](./src/services/WindowService.ts)** - 窗体管理服务,支持完整的窗体生命周期
|
||||
- **[ResourceService](./src/services/ResourceService.ts)** - 资源管理服务,提供权限控制和资源访问
|
||||
- **[EventCommunicationService](./src/services/EventCommunicationService.ts)** - 事件通信服务,支持跨应用消息传递
|
||||
- **[ApplicationSandboxEngine](./src/services/ApplicationSandboxEngine.ts)** - 应用沙箱引擎,多层安全隔离
|
||||
- **[ApplicationLifecycleManager](./src/services/ApplicationLifecycleManager.ts)** - 应用生命周期管理
|
||||
- **[SystemServiceIntegration](./src/services/SystemServiceIntegration.ts)** - 系统服务集成层
|
||||
|
||||
### SDK接口层
|
||||
- **[SDK类型定义](./src/sdk/types.ts)** - 完整的TypeScript接口定义
|
||||
- **[SDK实现](./src/sdk/index.ts)** - 统一的SDK实现,提供标准化API
|
||||
|
||||
### 应用层
|
||||
- **内置应用**:计算器、记事本、系统状态监控
|
||||
- **第三方应用**:[待办事项应用](./public/apps/todo/index.html)示例
|
||||
- **[SystemSDK](./src/sdk/index.ts)** - 统一SDK接口,为应用提供系统能力访问
|
||||
|
||||
## 🚀 快速开始
|
||||
### 事件系统
|
||||
|
||||
### 环境要求
|
||||
- Node.js >= 20.19.0
|
||||
- pnpm
|
||||
- **[IEventBuilder](./src/events/IEventBuilder.ts)** - 事件总线接口
|
||||
- **[EventBuilderImpl](./src/events/impl/EventBuilderImpl.ts)** - 事件总线实现
|
||||
|
||||
### 安装与运行
|
||||
```bash
|
||||
# 安装依赖
|
||||
pnpm install
|
||||
### 应用管理
|
||||
|
||||
# 启动开发服务器
|
||||
pnpm dev
|
||||
|
||||
# 构建生产版本
|
||||
pnpm build
|
||||
```
|
||||
|
||||
### 访问应用
|
||||
- 开发环境:http://localhost:5174/
|
||||
- 生产环境:部署后的域名
|
||||
|
||||
## 🎮 使用说明
|
||||
|
||||
### 桌面交互
|
||||
1. **双击图标**启动应用
|
||||
2. **拖拽图标**重新排列桌面布局
|
||||
3. **右键菜单**访问系统功能(计划中)
|
||||
|
||||
### 内置应用
|
||||
- **🧮 计算器**:基础的数学计算功能
|
||||
- **📝 记事本**:文本编辑和文件保存
|
||||
- **⚙️ 系统状态**:实时系统性能监控
|
||||
- **✓ 待办事项**:完整的任务管理应用
|
||||
|
||||
### 窗体操作
|
||||
- **最小化/最大化/还原**:标准窗体控制
|
||||
- **拖拽移动**:通过标题栏拖拽窗体
|
||||
- **调整大小**:支持窗体尺寸调整(计划中)
|
||||
|
||||
## 🔧 技术特性
|
||||
|
||||
### 系统架构特性
|
||||
- **微前端架构**:每个应用运行在独立的沙箱中
|
||||
- **事件驱动**:基于发布订阅模式的事件通信
|
||||
- **权限控制**:细粒度的资源访问权限管理
|
||||
- **性能监控**:实时的系统和应用性能监控
|
||||
- **错误隔离**:应用错误不会影响系统稳定性
|
||||
|
||||
### 开发特性
|
||||
- **TypeScript**:完整的类型安全保障
|
||||
- **Vue 3**:现代化的响应式框架
|
||||
- **Vite**:快速的构建工具
|
||||
- **UnoCSS**:原子化CSS框架
|
||||
- **Naive UI**:优雅的UI组件库
|
||||
- **[AppRegistry](./src/apps/AppRegistry.ts)** - 应用注册中心
|
||||
- **[ExternalAppDiscovery](./src/services/ExternalAppDiscovery.ts)** - 外置应用发现服务
|
||||
|
||||
## 📁 项目结构
|
||||
|
||||
```
|
||||
vue-desktop/
|
||||
├── src/
|
||||
│ ├── services/ # 核心服务层
|
||||
│ │ ├── WindowService.ts
|
||||
│ │ ├── ResourceService.ts
|
||||
│ │ ├── EventCommunicationService.ts
|
||||
│ │ ├── ApplicationSandboxEngine.ts
|
||||
.
|
||||
├── public\apps
|
||||
│ ├── music-player
|
||||
│ │ ├── README.md
|
||||
│ │ ├── app.js
|
||||
│ │ ├── index.html
|
||||
│ │ ├── manifest.json
|
||||
│ │ └── style.css
|
||||
│ └── README.md
|
||||
├── src
|
||||
│ ├── apps
|
||||
│ │ ├── calculator
|
||||
│ │ │ └── Calculator.vue
|
||||
│ │ ├── components
|
||||
│ │ │ └── BuiltInApp.vue
|
||||
│ │ ├── notepad
|
||||
│ │ │ └── Notepad.vue
|
||||
│ │ ├── todo
|
||||
│ │ │ └── Todo.vue
|
||||
│ │ ├── types
|
||||
│ │ │ └── AppManifest.ts
|
||||
│ │ ├── AppRegistry.ts
|
||||
│ │ └── index.ts
|
||||
│ ├── common
|
||||
│ │ ├── hooks
|
||||
│ │ │ ├── useClickFocus.ts
|
||||
│ │ │ └── useObservableVue.ts
|
||||
│ │ ├── naive-ui
|
||||
│ │ │ ├── components.ts
|
||||
│ │ │ ├── discrete-api.ts
|
||||
│ │ │ └── theme.ts
|
||||
│ │ └── types
|
||||
│ │ ├── IDestroyable.ts
|
||||
│ │ └── IVersion.ts
|
||||
│ ├── css
|
||||
│ │ └── basic.css
|
||||
│ ├── events
|
||||
│ │ ├── impl
|
||||
│ │ │ └── EventBuilderImpl.ts
|
||||
│ │ └── IEventBuilder.ts
|
||||
│ ├── sdk
|
||||
│ │ ├── index.ts
|
||||
│ │ └── types.ts
|
||||
│ ├── services
|
||||
│ │ ├── ApplicationLifecycleManager.ts
|
||||
│ │ └── SystemServiceIntegration.ts
|
||||
│ ├── sdk/ # SDK接口层
|
||||
│ │ ├── types.ts # 类型定义
|
||||
│ │ └── index.ts # SDK实现
|
||||
│ ├── ui/ # 用户界面
|
||||
│ │ ├── desktop-container/
|
||||
│ │ ├── ApplicationSandboxEngine.ts
|
||||
│ │ ├── ExternalAppDiscovery.ts
|
||||
│ │ ├── ResourceService.ts
|
||||
│ │ ├── SystemServiceIntegration.ts
|
||||
│ │ └── WindowService.ts
|
||||
│ ├── stores
|
||||
│ │ └── counter.ts
|
||||
│ ├── ui
|
||||
│ │ ├── components
|
||||
│ │ │ ├── AppRenderer.vue
|
||||
│ │ │ └── WindowManager.vue
|
||||
│ │ ├── desktop-container
|
||||
│ │ │ ├── AppIcon.vue
|
||||
│ │ │ ├── DesktopContainer.vue
|
||||
│ │ │ ├── useDesktopContainerInit.ts
|
||||
│ │ │ └── useDynamicAppIcons.ts
|
||||
│ │ ├── types
|
||||
│ │ │ ├── IDesktopAppIcon.ts
|
||||
│ │ │ ├── IGridTemplateParams.ts
|
||||
│ │ │ └── WindowFormTypes.ts
|
||||
│ │ └── App.vue
|
||||
│ ├── events/ # 事件系统
|
||||
│ ├── stores/ # 状态管理
|
||||
│ └── main.ts # 应用入口
|
||||
├── public/
|
||||
│ └── apps/ # 第三方应用
|
||||
│ └── todo/ # 待办事项应用
|
||||
└── README.md
|
||||
│ └── main.ts
|
||||
├── PRETTIER_CONFIG_GUIDE.md
|
||||
├── PROJECT_SUMMARY.md
|
||||
├── README.md
|
||||
├── env.d.ts
|
||||
├── index.html
|
||||
├── package.json
|
||||
├── pnpm-lock.yaml
|
||||
├── tsconfig.app.json
|
||||
├── tsconfig.json
|
||||
├── tsconfig.node.json
|
||||
├── uno.config.ts
|
||||
└── vite.config.ts
|
||||
```
|
||||
|
||||
## 🔒 安全机制
|
||||
## 🚀 快速开始
|
||||
|
||||
### 沙箱隔离
|
||||
- **iframe隔离**:每个应用运行在独立的iframe中
|
||||
- **CSP策略**:严格的内容安全策略
|
||||
- **权限控制**:基于白名单的权限管理
|
||||
- **API代理**:所有系统调用通过SDK代理
|
||||
### 环境要求
|
||||
|
||||
### 权限模型
|
||||
- **存储权限**:应用独立的存储空间
|
||||
- **网络权限**:基于域名白名单的网络访问
|
||||
- **通知权限**:用户确认的通知功能
|
||||
- **剪贴板权限**:受控的剪贴板访问
|
||||
- Node.js >= 16.0.0
|
||||
- pnpm >= 7.0.0
|
||||
|
||||
## 📊 性能优化
|
||||
### 安装依赖
|
||||
|
||||
### 渲染优化
|
||||
- **虚拟化布局**:高效的图标网格布局
|
||||
- **按需加载**:应用按需动态加载
|
||||
- **内存管理**:智能的内存回收机制
|
||||
- **性能监控**:实时的性能指标收集
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### 系统优化
|
||||
- **事件节流**:防止事件风暴
|
||||
- **资源缓存**:智能的资源缓存策略
|
||||
- **自动清理**:定期的系统资源清理
|
||||
- **错误恢复**:优雅的错误处理和恢复
|
||||
### 开发模式
|
||||
|
||||
## 🔮 未来规划
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### 短期计划
|
||||
- [ ] 窗体拖拽和调整大小功能
|
||||
- [ ] 右键菜单和快捷方式
|
||||
- [ ] 更多内置应用(文件管理器、浏览器等)
|
||||
- [ ] 应用商店和在线安装
|
||||
### 构建生产版本
|
||||
|
||||
### 长期计划
|
||||
- [ ] 多桌面和工作区
|
||||
- [ ] 插件系统和扩展机制
|
||||
- [ ] 云同步和备份
|
||||
- [ ] 移动端适配
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## 🤝 贡献指南
|
||||
### 预览生产构建
|
||||
|
||||
欢迎贡献代码、报告bug或提出功能建议!
|
||||
```bash
|
||||
pnpm preview
|
||||
```
|
||||
|
||||
### 开发流程
|
||||
1. Fork项目
|
||||
2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
|
||||
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
||||
5. 创建Pull Request
|
||||
## 🛠️ 开发指南
|
||||
|
||||
### 代码规范
|
||||
- 使用TypeScript进行类型安全
|
||||
- 遵循Vue 3组合式API最佳实践
|
||||
- 使用ESLint和Prettier保持代码风格一致
|
||||
- 为新功能编写测试用例
|
||||
### 添加内置应用
|
||||
|
||||
## 📄 许可证
|
||||
1. 在 `src/apps/` 目录下创建应用文件夹
|
||||
2. 创建 Vue 组件文件(如 `MyApp.vue`)
|
||||
3. 在 `src/apps/AppRegistry.ts` 中注册应用
|
||||
|
||||
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
|
||||
### 添加外置应用
|
||||
|
||||
## 🙏 致谢
|
||||
1. 在 `public/apps/` 目录下创建应用文件夹
|
||||
2. 添加 `manifest.json` 应用清单文件
|
||||
3. 添加应用的 HTML/CSS/JS 文件
|
||||
4. 系统会自动发现并加载该应用
|
||||
|
||||
感谢所有参与项目开发和测试的贡献者,以及提供技术支持的开源社区。
|
||||
### 系统服务使用
|
||||
|
||||
---
|
||||
通过依赖注入获取系统服务:
|
||||
|
||||
**Vue Desktop** - 让Web应用拥有桌面应用的体验 🚀
|
||||
```typescript
|
||||
import type { SystemServiceIntegration } from '@/services/SystemServiceIntegration'
|
||||
const systemService = inject<SystemServiceIntegration>('systemService')
|
||||
```
|
||||
|
||||
可用服务:
|
||||
|
||||
- `getWindowService()` - 窗体服务
|
||||
- `getResourceService()` - 资源服务
|
||||
- `getSandboxEngine()` - 沙箱引擎
|
||||
- `getLifecycleManager()` - 生命周期管理器
|
||||
|
||||
## 📖 技术文档
|
||||
|
||||
### 窗体系统
|
||||
|
||||
窗体系统支持完整的生命周期管理,包括创建、移动、缩放、最小化、最大化等操作。
|
||||
|
||||
### 资源管理
|
||||
|
||||
资源服务提供安全的存储访问和权限控制机制。
|
||||
|
||||
### 沙箱安全
|
||||
|
||||
应用沙箱引擎提供多层安全隔离,防止恶意代码访问系统资源。
|
||||
|
||||
### 应用生命周期
|
||||
|
||||
应用生命周期管理器负责应用的安装、启动、停止、卸载等操作。
|
||||
|
||||
## 🧪 测试
|
||||
|
||||
### 单元测试
|
||||
|
||||
```bash
|
||||
pnpm test
|
||||
```
|
||||
|
||||
### 端到端测试
|
||||
|
||||
```bash
|
||||
pnpm test:e2e
|
||||
```
|
||||
|
||||
## 📦 部署
|
||||
|
||||
构建产物可直接部署到任何静态文件服务器上。
|
||||
|
||||
@@ -2,40 +2,34 @@
|
||||
<BuiltInApp app-id="calculator" title="计算器">
|
||||
<div class="calculator">
|
||||
<div class="display">
|
||||
<input
|
||||
v-model="displayValue"
|
||||
type="text"
|
||||
readonly
|
||||
class="display-input"
|
||||
:class="{ 'error': hasError }"
|
||||
>
|
||||
<input v-model="displayValue" type="text" readonly class="display-input" :class="{ 'error': hasError }">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="buttons">
|
||||
<!-- 第一行 -->
|
||||
<button @click="clear" class="btn btn-clear">C</button>
|
||||
<button @click="deleteLast" class="btn btn-operation">←</button>
|
||||
<button @click="appendOperation('/')" class="btn btn-operation">÷</button>
|
||||
<button @click="appendOperation('*')" class="btn btn-operation">×</button>
|
||||
|
||||
|
||||
<!-- 第二行 -->
|
||||
<button @click="appendNumber('7')" class="btn btn-number">7</button>
|
||||
<button @click="appendNumber('8')" class="btn btn-number">8</button>
|
||||
<button @click="appendNumber('9')" class="btn btn-number">9</button>
|
||||
<button @click="appendOperation('-')" class="btn btn-operation">-</button>
|
||||
|
||||
|
||||
<!-- 第三行 -->
|
||||
<button @click="appendNumber('4')" class="btn btn-number">4</button>
|
||||
<button @click="appendNumber('5')" class="btn btn-number">5</button>
|
||||
<button @click="appendNumber('6')" class="btn btn-number">6</button>
|
||||
<button @click="appendOperation('+')" class="btn btn-operation">+</button>
|
||||
|
||||
|
||||
<!-- 第四行 -->
|
||||
<button @click="appendNumber('1')" class="btn btn-number">1</button>
|
||||
<button @click="appendNumber('2')" class="btn btn-number">2</button>
|
||||
<button @click="appendNumber('3')" class="btn btn-number">3</button>
|
||||
<button @click="calculate" class="btn btn-equals" rowspan="2">=</button>
|
||||
|
||||
|
||||
<!-- 第五行 -->
|
||||
<button @click="appendNumber('0')" class="btn btn-number btn-zero">0</button>
|
||||
<button @click="appendNumber('.')" class="btn btn-number">.</button>
|
||||
@@ -79,27 +73,28 @@ const saveHistory = async (expression: string, result: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 直接使用事件服务发送通知
|
||||
const showNotification = (message: string) => {
|
||||
if (systemService) {
|
||||
const eventService = systemService.getEventService()
|
||||
eventService.sendMessage('calculator', 'user-interaction', {
|
||||
type: 'notification',
|
||||
message,
|
||||
timestamp: new Date()
|
||||
})
|
||||
}
|
||||
}
|
||||
// 移除事件服务相关代码
|
||||
// // 直接使用事件服务发送通知
|
||||
// const showNotification = (message: string) => {
|
||||
// if (systemService) {
|
||||
// const eventService = systemService.getEventService()
|
||||
// eventService.sendMessage('calculator', 'user-interaction', {
|
||||
// type: 'notification',
|
||||
// message,
|
||||
// timestamp: new Date()
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
// 添加数字
|
||||
const appendNumber = (num: string) => {
|
||||
hasError.value = false
|
||||
|
||||
|
||||
if (shouldResetDisplay.value) {
|
||||
displayValue.value = '0'
|
||||
shouldResetDisplay.value = false
|
||||
}
|
||||
|
||||
|
||||
if (num === '.') {
|
||||
if (!displayValue.value.includes('.')) {
|
||||
displayValue.value += num
|
||||
@@ -117,10 +112,10 @@ const appendNumber = (num: string) => {
|
||||
const appendOperation = (op: string) => {
|
||||
hasError.value = false
|
||||
shouldResetDisplay.value = false
|
||||
|
||||
|
||||
const lastChar = displayValue.value.slice(-1)
|
||||
const operations = ['+', '-', '*', '/']
|
||||
|
||||
|
||||
// 如果最后一个字符是运算符,替换它
|
||||
if (operations.includes(lastChar)) {
|
||||
displayValue.value = displayValue.value.slice(0, -1) + op
|
||||
@@ -136,29 +131,30 @@ const calculate = async () => {
|
||||
let expression = displayValue.value
|
||||
.replace(/×/g, '*')
|
||||
.replace(/÷/g, '/')
|
||||
|
||||
|
||||
// 简单的表达式验证
|
||||
if (/[+\-*/]$/.test(expression)) {
|
||||
return // 以运算符结尾,不计算
|
||||
}
|
||||
|
||||
|
||||
const originalExpression = displayValue.value
|
||||
const result = eval(expression)
|
||||
|
||||
|
||||
if (!isFinite(result)) {
|
||||
throw new Error('除零错误')
|
||||
}
|
||||
|
||||
|
||||
displayValue.value = result.toString()
|
||||
lastResult.value = result
|
||||
shouldResetDisplay.value = true
|
||||
|
||||
|
||||
// 保存历史记录
|
||||
await saveHistory(originalExpression, result.toString())
|
||||
|
||||
// 发送通知
|
||||
showNotification(`计算结果: ${result}`)
|
||||
|
||||
|
||||
// 移除事件服务相关代码
|
||||
// // 发送通知
|
||||
// showNotification(`计算结果: ${result}`)
|
||||
|
||||
} catch (error) {
|
||||
hasError.value = true
|
||||
displayValue.value = '错误'
|
||||
@@ -179,12 +175,12 @@ const clear = () => {
|
||||
// 删除最后一个字符
|
||||
const deleteLast = () => {
|
||||
hasError.value = false
|
||||
|
||||
|
||||
if (shouldResetDisplay.value) {
|
||||
clear()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (displayValue.value.length > 1) {
|
||||
displayValue.value = displayValue.value.slice(0, -1)
|
||||
} else {
|
||||
@@ -195,9 +191,9 @@ const deleteLast = () => {
|
||||
// 键盘事件处理
|
||||
const handleKeyboard = (event: KeyboardEvent) => {
|
||||
event.preventDefault()
|
||||
|
||||
|
||||
const key = event.key
|
||||
|
||||
|
||||
if (/[0-9.]/.test(key)) {
|
||||
appendNumber(key)
|
||||
} else if (['+', '-', '*', '/'].includes(key)) {
|
||||
@@ -330,17 +326,17 @@ onMounted(() => {
|
||||
margin: 10px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
|
||||
.display-input {
|
||||
height: 60px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
|
||||
.buttons {
|
||||
height: 280px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
|
||||
.btn {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ const appContext = {
|
||||
systemService,
|
||||
// 直接暴露系统服务的方法,简化调用
|
||||
storage: systemService?.getResourceService(),
|
||||
events: systemService?.getEventService(),
|
||||
// 移除 events: systemService?.getEventService(),
|
||||
lifecycle: systemService?.getLifecycleManager(),
|
||||
window: {
|
||||
setTitle: (title: string) => {
|
||||
@@ -52,11 +52,11 @@ onMounted(async () => {
|
||||
version: '1.0.0',
|
||||
permissions: ['storage', 'notification']
|
||||
})
|
||||
|
||||
|
||||
if (props.title) {
|
||||
await window.SystemSDK.window.setTitle(props.title)
|
||||
}
|
||||
|
||||
|
||||
console.log(`内置应用 ${props.appId} 初始化成功`)
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,10 +1,48 @@
|
||||
import type { IDestroyable } from '@/common/types/IDestroyable'
|
||||
import type { WindowState } from '@/services/WindowService'
|
||||
import type { ResourceType } from '@/services/ResourceService'
|
||||
|
||||
/**
|
||||
* 窗体数据更新参数
|
||||
*/
|
||||
export interface WindowFormDataUpdateParams {
|
||||
/** 窗口id */
|
||||
id: string
|
||||
/** 窗口状态 */
|
||||
state: WindowState
|
||||
/** 窗口宽度 */
|
||||
width: number
|
||||
/** 窗口高度 */
|
||||
height: number
|
||||
/** 窗口x坐标(左上角) */
|
||||
x: number
|
||||
/** 窗口y坐标(左上角) */
|
||||
y: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件定义
|
||||
* @interface IEventMap 事件定义 键是事件名称,值是事件处理函数
|
||||
*/
|
||||
export interface 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
|
||||
|
||||
/**
|
||||
* 事件处理函数映射
|
||||
*/
|
||||
@@ -29,7 +67,7 @@ export interface IEventBuilder<Events extends IEventMap> extends IDestroyable {
|
||||
immediate?: boolean
|
||||
immediateArgs?: Parameters<F>
|
||||
once?: boolean
|
||||
},
|
||||
}
|
||||
): void
|
||||
|
||||
/**
|
||||
@@ -46,5 +84,8 @@ export interface IEventBuilder<Events extends IEventMap> extends IDestroyable {
|
||||
* @param args 参数
|
||||
* @returns void
|
||||
*/
|
||||
notifyEvent<E extends keyof Events, F extends Events[E]>(eventName: E, ...args: Parameters<F>): void
|
||||
}
|
||||
notifyEvent<E extends keyof Events, F extends Events[E]>(
|
||||
eventName: E,
|
||||
...args: Parameters<F>
|
||||
): void
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { reactive } from 'vue'
|
||||
import type { WindowService } from './WindowService'
|
||||
import type { ResourceService } from './ResourceService'
|
||||
import type { EventCommunicationService } from './EventCommunicationService'
|
||||
import type { ApplicationSandboxEngine } from './ApplicationSandboxEngine'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { externalAppDiscovery } from './ExternalAppDiscovery'
|
||||
@@ -121,21 +120,16 @@ export class ApplicationLifecycleManager {
|
||||
|
||||
private windowService: WindowService
|
||||
private resourceService: ResourceService
|
||||
private eventService: EventCommunicationService
|
||||
private sandboxEngine: ApplicationSandboxEngine
|
||||
|
||||
constructor(
|
||||
windowService: WindowService,
|
||||
resourceService: ResourceService,
|
||||
eventService: EventCommunicationService,
|
||||
sandboxEngine: ApplicationSandboxEngine
|
||||
) {
|
||||
this.windowService = windowService
|
||||
this.resourceService = resourceService
|
||||
this.eventService = eventService
|
||||
this.sandboxEngine = sandboxEngine
|
||||
|
||||
this.setupEventListeners()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,49 +379,6 @@ export class ApplicationLifecycleManager {
|
||||
}
|
||||
}
|
||||
|
||||
// 私有方法
|
||||
|
||||
/**
|
||||
* 为内置应用挂载 AppRenderer 组件
|
||||
*/
|
||||
private async mountBuiltInApp(appId: string, windowInstance: any): Promise<void> {
|
||||
try {
|
||||
// 动态导入 Vue 和 AppRenderer
|
||||
const { createApp } = await import('vue')
|
||||
const AppRenderer = (await import('../ui/components/AppRenderer.vue')).default
|
||||
|
||||
console.log(`[LifecycleManager] 为内置应用 ${appId} 创建 AppRenderer 组件`)
|
||||
|
||||
console.log('----------------------------------')
|
||||
|
||||
const app = createApp({
|
||||
components: { AppRenderer },
|
||||
template: `<AppRenderer :app-id="'${appId}'" :window-id="'${windowInstance.id}'"/>`
|
||||
})
|
||||
|
||||
// 提供系统服务(使用当前实例所在的系统服务)
|
||||
app.provide('systemService', {
|
||||
getWindowService: () => this.windowService,
|
||||
getResourceService: () => this.resourceService,
|
||||
getEventService: () => this.eventService,
|
||||
getSandboxEngine: () => this.sandboxEngine,
|
||||
getLifecycleManager: () => this
|
||||
})
|
||||
|
||||
// 挂载到窗口内容区域
|
||||
const contentArea = windowInstance.element?.querySelector('.window-content')
|
||||
if (contentArea) {
|
||||
app.mount(contentArea)
|
||||
console.log(`[LifecycleManager] AppRenderer 组件已挂载到窗口 ${windowInstance.id}`)
|
||||
} else {
|
||||
throw new Error('未找到窗口内容区域')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`内置应用 ${appId} 挂载失败:`, error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查权限
|
||||
*/
|
||||
@@ -510,28 +461,6 @@ export class ApplicationLifecycleManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置事件监听器
|
||||
*/
|
||||
private setupEventListeners(): void {
|
||||
// 监听沙箱状态变化
|
||||
this.eventService.subscribe('system', 'sandbox-state-change', (message) => {
|
||||
const { sandboxId, newState } = message.payload
|
||||
|
||||
// 查找对应的应用
|
||||
for (const app of this.installedApps.values()) {
|
||||
if (app.sandboxId === sandboxId) {
|
||||
if (newState === 'error') {
|
||||
this.handleAppError(app.id, new Error('沙箱错误'))
|
||||
} else if (newState === 'destroyed') {
|
||||
this.handleAppCrash(app.id, '沙箱被销毁')
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理应用错误
|
||||
*/
|
||||
@@ -541,12 +470,6 @@ export class ApplicationLifecycleManager {
|
||||
|
||||
app.errorCount++
|
||||
|
||||
this.eventService.sendMessage('system', 'app-lifecycle', {
|
||||
type: 'error',
|
||||
appId,
|
||||
error: error.message
|
||||
})
|
||||
|
||||
console.error(`应用 ${appId} 发生错误:`, error)
|
||||
}
|
||||
|
||||
@@ -577,11 +500,12 @@ export class ApplicationLifecycleManager {
|
||||
const oldState = app.state
|
||||
app.state = newState
|
||||
|
||||
this.eventService.sendMessage('system', 'app-lifecycle', {
|
||||
type: 'stateChanged',
|
||||
appId: app.id,
|
||||
newState,
|
||||
oldState
|
||||
})
|
||||
// 移除事件服务消息发送
|
||||
// this.eventService.sendMessage('system', 'app-lifecycle', {
|
||||
// type: 'stateChanged',
|
||||
// appId: app.id,
|
||||
// newState,
|
||||
// oldState
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { reactive } from 'vue'
|
||||
import type { ResourceService } from './ResourceService'
|
||||
import type { EventCommunicationService } from './EventCommunicationService'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
/**
|
||||
@@ -107,11 +106,9 @@ export class ApplicationSandboxEngine {
|
||||
private performanceData = reactive(new Map<string, SandboxPerformance[]>())
|
||||
private monitoringInterval: number | null = null
|
||||
private resourceService: ResourceService
|
||||
private eventService: EventCommunicationService
|
||||
|
||||
constructor(resourceService: ResourceService, eventService: EventCommunicationService) {
|
||||
constructor(resourceService: ResourceService) {
|
||||
this.resourceService = resourceService
|
||||
this.eventService = eventService
|
||||
this.startPerformanceMonitoring()
|
||||
}
|
||||
|
||||
@@ -1163,27 +1160,7 @@ export class ApplicationSandboxEngine {
|
||||
/**
|
||||
* 检查性能阈值
|
||||
*/
|
||||
private checkPerformanceThresholds(sandbox: SandboxInstance, metrics: SandboxPerformance): void {
|
||||
// 内存使用检查
|
||||
if (metrics.memoryUsage > sandbox.config.maxMemoryUsage) {
|
||||
this.eventService.sendMessage('system', 'performance-alert', {
|
||||
sandboxId: sandbox.id,
|
||||
type: 'memory',
|
||||
usage: metrics.memoryUsage,
|
||||
limit: sandbox.config.maxMemoryUsage
|
||||
})
|
||||
}
|
||||
|
||||
// CPU使用检查
|
||||
if (metrics.cpuUsage > sandbox.config.maxCpuUsage) {
|
||||
this.eventService.sendMessage('system', 'performance-alert', {
|
||||
sandboxId: sandbox.id,
|
||||
type: 'cpu',
|
||||
usage: metrics.cpuUsage,
|
||||
limit: sandbox.config.maxCpuUsage
|
||||
})
|
||||
}
|
||||
}
|
||||
private checkPerformanceThresholds(sandbox: SandboxInstance, metrics: SandboxPerformance): void {}
|
||||
|
||||
/**
|
||||
* 开始性能监控
|
||||
@@ -1220,11 +1197,11 @@ export class ApplicationSandboxEngine {
|
||||
const oldState = sandbox.state
|
||||
sandbox.state = newState
|
||||
|
||||
// 触发状态变化事件
|
||||
this.eventService.sendMessage('system', 'sandbox-state-change', {
|
||||
sandboxId: sandbox.id,
|
||||
newState,
|
||||
oldState
|
||||
})
|
||||
// 移除事件服务消息发送
|
||||
// this.eventService.sendMessage('system', 'sandbox-state-change', {
|
||||
// sandboxId: sandbox.id,
|
||||
// newState,
|
||||
// oldState
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,748 +0,0 @@
|
||||
import { reactive } from 'vue'
|
||||
import type { IEventBuilder, IEventMap } from '@/events/IEventBuilder'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import type { WindowState } from './WindowService'
|
||||
import type { ResourceType } from './ResourceService'
|
||||
|
||||
/**
|
||||
* 消息类型枚举
|
||||
*/
|
||||
export enum MessageType {
|
||||
/** 系统事件 */
|
||||
SYSTEM = 'system',
|
||||
/** 应用事件 */
|
||||
APPLICATION = 'application',
|
||||
/** 用户交互事件 */
|
||||
USER_INTERACTION = 'user_interaction',
|
||||
/** 跨应用事件 */
|
||||
CROSS_APP = 'cross_app',
|
||||
/** 广播事件 */
|
||||
BROADCAST = 'broadcast'
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息优先级枚举
|
||||
*/
|
||||
export enum MessagePriority {
|
||||
/** 低优先级 */
|
||||
LOW = 0,
|
||||
/** 正常优先级 */
|
||||
NORMAL = 1,
|
||||
/** 高优先级 */
|
||||
HIGH = 2,
|
||||
/** 紧急优先级 */
|
||||
CRITICAL = 3
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息状态枚举
|
||||
*/
|
||||
export enum MessageStatus {
|
||||
/** 等待发送 */
|
||||
PENDING = 'pending',
|
||||
/** 已发送 */
|
||||
SENT = 'sent',
|
||||
/** 已送达 */
|
||||
DELIVERED = 'delivered',
|
||||
/** 发送失败 */
|
||||
FAILED = 'failed',
|
||||
/** 已取消 */
|
||||
EXPIRED = 'expired'
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件消息接口
|
||||
*/
|
||||
export interface EventMessage {
|
||||
/** 消息ID */
|
||||
id: string
|
||||
/** 消息类型 */
|
||||
type: MessageType
|
||||
/** 消息优先级 */
|
||||
priority: MessagePriority
|
||||
/** 发送者ID */
|
||||
senderId: string
|
||||
/** 接收者ID, undefined表示广播消息 */
|
||||
receiverId?: string
|
||||
/** 消息频道 */
|
||||
channel: string
|
||||
/** 消息内容 */
|
||||
payload: any
|
||||
/** 消息发送时间 */
|
||||
timestamp: Date
|
||||
/** 消息过期时间 */
|
||||
expiresAt?: Date
|
||||
/** 消息状态 */
|
||||
status: MessageStatus
|
||||
/** 重试次数 */
|
||||
retryCount: number
|
||||
/** 最大重试次数 */
|
||||
maxRetries: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件订阅者接口
|
||||
*/
|
||||
export interface EventSubscriber {
|
||||
/** 订阅者ID */
|
||||
id: string
|
||||
/** 应用ID */
|
||||
appId: string
|
||||
/** 消息频道 */
|
||||
channel: string
|
||||
/** 订阅者处理函数 */
|
||||
handler: (message: EventMessage) => void | Promise<void>
|
||||
/** 消息过滤器 */
|
||||
filter?: (message: EventMessage) => boolean
|
||||
/** 订阅者优先级 */
|
||||
priority: MessagePriority
|
||||
/** 创建时间 */
|
||||
createdAt: Date
|
||||
/** 是否启用 */
|
||||
active: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 通信通道接口
|
||||
*/
|
||||
export interface CommunicationChannel {
|
||||
/** 通道名称 */
|
||||
name: string
|
||||
/** 通道描述 */
|
||||
description: string
|
||||
/** 是否需要权限 */
|
||||
restricted: boolean
|
||||
/** 允许的源应用ID列表 */
|
||||
/** 允许访问的应用ID列表 */
|
||||
allowedApps: string[]
|
||||
/** 最大消息大小(字节) */
|
||||
maxMessageSize: number
|
||||
/** 消息保留时间(毫秒) */
|
||||
messageRetention: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件统计信息
|
||||
*/
|
||||
export interface EventStatistics {
|
||||
/** 总发送消息数 */
|
||||
totalMessagesSent: number
|
||||
/** 总接收消息数 */
|
||||
totalMessagesReceived: number
|
||||
/** 总广播数 */
|
||||
totalBroadcasts: number
|
||||
/** 失败消息数 */
|
||||
failedMessages: number
|
||||
/** 激活的订阅者数 */
|
||||
activeSubscribers: 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件通信服务类
|
||||
*/
|
||||
export class EventCommunicationService {
|
||||
private subscribers = reactive(new Map<string, EventSubscriber>())
|
||||
private messageQueue = reactive(new Map<string, EventMessage[]>()) // 按应用分组的消息队列
|
||||
private messageHistory = reactive(new Map<string, EventMessage[]>()) // 消息历史记录
|
||||
private channels = reactive(new Map<string, CommunicationChannel>())
|
||||
private statistics = reactive<EventStatistics>({
|
||||
totalMessagesSent: 0,
|
||||
totalMessagesReceived: 0,
|
||||
totalBroadcasts: 0,
|
||||
failedMessages: 0,
|
||||
activeSubscribers: 0,
|
||||
channelUsage: new Map()
|
||||
})
|
||||
|
||||
private processingInterval: number | null = null
|
||||
private lastProcessTime: number = 0
|
||||
private processInterval: number = 100 // 处理间隔(毫秒)
|
||||
private eventBus: IEventBuilder<EventCommunicationEvents>
|
||||
|
||||
constructor(eventBus: IEventBuilder<EventCommunicationEvents>) {
|
||||
this.eventBus = eventBus
|
||||
this.initializeDefaultChannels()
|
||||
this.startMessageProcessing()
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅事件频道
|
||||
*/
|
||||
subscribe(
|
||||
appId: string,
|
||||
channel: string,
|
||||
handler: (message: EventMessage) => void | Promise<void>,
|
||||
options: {
|
||||
filter?: (message: EventMessage) => boolean
|
||||
priority?: MessagePriority
|
||||
} = {}
|
||||
): string {
|
||||
// 检查通道权限
|
||||
if (!this.canAccessChannel(appId, channel)) {
|
||||
throw new Error(`应用 ${appId} 无权访问频道 ${channel}`)
|
||||
}
|
||||
|
||||
const subscriberId = uuidv4()
|
||||
const subscriber: EventSubscriber = {
|
||||
id: subscriberId,
|
||||
appId,
|
||||
channel,
|
||||
handler,
|
||||
filter: options.filter,
|
||||
priority: options.priority || MessagePriority.NORMAL,
|
||||
createdAt: new Date(),
|
||||
active: true
|
||||
}
|
||||
|
||||
this.subscribers.set(subscriberId, subscriber)
|
||||
this.updateActiveSubscribersCount()
|
||||
|
||||
console.log(`应用 ${appId} 订阅了频道 ${channel}`)
|
||||
return subscriberId
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消订阅
|
||||
*/
|
||||
unsubscribe(subscriberId: string): boolean {
|
||||
const result = this.subscribers.delete(subscriberId)
|
||||
if (result) {
|
||||
this.updateActiveSubscribersCount()
|
||||
console.log(`取消订阅: ${subscriberId}`)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*/
|
||||
async sendMessage(
|
||||
senderId: string,
|
||||
channel: string,
|
||||
payload: any,
|
||||
options: {
|
||||
receiverId?: string
|
||||
priority?: MessagePriority
|
||||
type?: MessageType
|
||||
expiresIn?: number // 过期时间(毫秒)
|
||||
maxRetries?: number
|
||||
} = {}
|
||||
): Promise<string> {
|
||||
if (this.getChannelSubscriberCount(channel) === 0) {
|
||||
throw new Error(`频道 ${channel} 无订阅者`)
|
||||
}
|
||||
// 检查发送者权限
|
||||
if (!this.canAccessChannel(senderId, channel)) {
|
||||
throw new Error(`应用 ${senderId} 无权向频道 ${channel} 发送消息`)
|
||||
}
|
||||
|
||||
// 检查消息大小
|
||||
const messageSize = JSON.stringify(payload).length
|
||||
const channelConfig = this.channels.get(channel)
|
||||
if (channelConfig && messageSize > channelConfig.maxMessageSize) {
|
||||
throw new Error(`消息大小超出限制: ${messageSize} > ${channelConfig.maxMessageSize}`)
|
||||
}
|
||||
|
||||
const messageId = uuidv4()
|
||||
const now = new Date()
|
||||
|
||||
const message: EventMessage = {
|
||||
id: messageId,
|
||||
type: options.type || MessageType.APPLICATION,
|
||||
priority: options.priority || MessagePriority.NORMAL,
|
||||
senderId,
|
||||
receiverId: options.receiverId,
|
||||
channel,
|
||||
payload,
|
||||
timestamp: now,
|
||||
expiresAt: options.expiresIn ? new Date(now.getTime() + options.expiresIn) : undefined,
|
||||
status: MessageStatus.PENDING,
|
||||
retryCount: 0,
|
||||
maxRetries: options.maxRetries || 3
|
||||
}
|
||||
|
||||
// 如果是点对点消息,直接发送
|
||||
if (options.receiverId) {
|
||||
await this.deliverMessage(message)
|
||||
} else {
|
||||
// 广播消息,加入队列处理
|
||||
this.addToQueue(message)
|
||||
}
|
||||
|
||||
// 更新统计信息
|
||||
this.statistics.totalMessagesSent++
|
||||
if (!options.receiverId) {
|
||||
this.statistics.totalBroadcasts++
|
||||
}
|
||||
|
||||
const channelUsage = this.statistics.channelUsage.get(channel) || 0
|
||||
this.statistics.channelUsage.set(channel, channelUsage + 1)
|
||||
|
||||
// 记录消息历史
|
||||
this.recordMessage(message)
|
||||
|
||||
console.log(
|
||||
`[EventCommunication] 消息 ${messageId} 已发送到频道 ${channel}[发送者: ${senderId}]`
|
||||
)
|
||||
return messageId
|
||||
}
|
||||
|
||||
/**
|
||||
* 广播消息到所有订阅者
|
||||
*/
|
||||
async broadcast(
|
||||
senderId: string,
|
||||
channel: string,
|
||||
payload: any,
|
||||
options: {
|
||||
priority?: MessagePriority
|
||||
expiresIn?: number
|
||||
} = {}
|
||||
): Promise<string> {
|
||||
return this.sendMessage(senderId, channel, payload, {
|
||||
...options,
|
||||
type: MessageType.BROADCAST
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送跨应用消息
|
||||
*/
|
||||
async sendCrossAppMessage(
|
||||
senderId: string,
|
||||
receiverId: string,
|
||||
payload: any,
|
||||
options: {
|
||||
priority?: MessagePriority
|
||||
expiresIn?: number
|
||||
} = {}
|
||||
): Promise<string> {
|
||||
const channel = 'cross-app'
|
||||
|
||||
return this.sendMessage(senderId, channel, payload, {
|
||||
...options,
|
||||
receiverId,
|
||||
type: MessageType.CROSS_APP
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息历史
|
||||
*/
|
||||
getMessageHistory(
|
||||
appId: string,
|
||||
options: {
|
||||
channel?: string
|
||||
limit?: number
|
||||
since?: Date
|
||||
} = {}
|
||||
): EventMessage[] {
|
||||
const history = this.messageHistory.get(appId) || []
|
||||
|
||||
let filtered = history.filter((msg) => msg.senderId === appId || msg.receiverId === appId)
|
||||
|
||||
if (options.channel) {
|
||||
filtered = filtered.filter((msg) => msg.channel === options.channel)
|
||||
}
|
||||
|
||||
if (options.since) {
|
||||
filtered = filtered.filter((msg) => msg.timestamp >= options.since!)
|
||||
}
|
||||
|
||||
if (options.limit) {
|
||||
filtered = filtered.slice(-options.limit)
|
||||
}
|
||||
|
||||
return filtered.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime())
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用的订阅列表
|
||||
*/
|
||||
getAppSubscriptions(appId: string): EventSubscriber[] {
|
||||
return Array.from(this.subscribers.values()).filter((sub) => sub.appId === appId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取频道订阅者数量
|
||||
*/
|
||||
getChannelSubscriberCount(channel: string): number {
|
||||
return Array.from(this.subscribers.values()).filter(
|
||||
(sub) => sub.channel === channel && sub.active
|
||||
).length
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建通信频道
|
||||
*/
|
||||
createChannel(channel: string, config: Omit<CommunicationChannel, 'name'>): boolean {
|
||||
if (this.channels.has(channel)) {
|
||||
return false
|
||||
}
|
||||
|
||||
this.channels.set(channel, {
|
||||
name: channel,
|
||||
...config
|
||||
})
|
||||
|
||||
console.log(`创建通信频道: ${channel}`)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除通信频道
|
||||
*/
|
||||
deleteChannel(channel: string): boolean {
|
||||
// 移除所有相关订阅
|
||||
const subscribersToRemove = Array.from(this.subscribers.entries())
|
||||
.filter(([, sub]) => sub.channel === channel)
|
||||
.map(([id]) => id)
|
||||
|
||||
subscribersToRemove.forEach((id) => this.unsubscribe(id))
|
||||
|
||||
// 删除频道
|
||||
const result = this.channels.delete(channel)
|
||||
|
||||
if (result) {
|
||||
console.log(`删除通信频道: ${channel}`)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取统计信息
|
||||
*/
|
||||
getStatistics(): EventStatistics {
|
||||
return { ...this.statistics }
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期消息和订阅
|
||||
*/
|
||||
cleanup(): void {
|
||||
const now = new Date()
|
||||
|
||||
// 清理过期消息
|
||||
for (const [appId, messages] of this.messageQueue.entries()) {
|
||||
const validMessages = messages.filter((msg) => !msg.expiresAt || msg.expiresAt > now)
|
||||
|
||||
if (validMessages.length !== messages.length) {
|
||||
this.messageQueue.set(appId, validMessages)
|
||||
}
|
||||
}
|
||||
|
||||
// 清理消息历史(保留最近7天)
|
||||
const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
|
||||
for (const [appId, history] of this.messageHistory.entries()) {
|
||||
const recentHistory = history.filter((msg) => msg.timestamp > sevenDaysAgo)
|
||||
this.messageHistory.set(appId, recentHistory)
|
||||
}
|
||||
|
||||
console.log('事件通信服务清理完成')
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁服务
|
||||
*/
|
||||
destroy(): void {
|
||||
if (this.processingInterval) {
|
||||
cancelAnimationFrame(this.processingInterval)
|
||||
this.processingInterval = null
|
||||
}
|
||||
|
||||
this.subscribers.clear()
|
||||
this.messageQueue.clear()
|
||||
this.messageHistory.clear()
|
||||
this.channels.clear()
|
||||
|
||||
console.log('事件通信服务已销毁')
|
||||
}
|
||||
|
||||
// 私有方法
|
||||
|
||||
/**
|
||||
* 初始化默认频道
|
||||
*/
|
||||
private initializeDefaultChannels(): void {
|
||||
// 系统事件频道
|
||||
this.createChannel('system', {
|
||||
description: '系统级事件通信',
|
||||
restricted: true,
|
||||
allowedApps: ['system'],
|
||||
maxMessageSize: 1024 * 10, // 10KB
|
||||
messageRetention: 24 * 60 * 60 * 1000 // 24小时
|
||||
})
|
||||
|
||||
// 应用间通信频道
|
||||
this.createChannel('cross-app', {
|
||||
description: '应用间通信',
|
||||
restricted: false,
|
||||
allowedApps: [],
|
||||
maxMessageSize: 1024 * 100, // 100KB
|
||||
messageRetention: 7 * 24 * 60 * 60 * 1000 // 7天
|
||||
})
|
||||
|
||||
// 用户交互频道
|
||||
this.createChannel('user-interaction', {
|
||||
description: '用户交互事件',
|
||||
restricted: false,
|
||||
allowedApps: [],
|
||||
maxMessageSize: 1024 * 5, // 5KB
|
||||
messageRetention: 60 * 60 * 1000 // 1小时
|
||||
})
|
||||
|
||||
// 广播频道
|
||||
this.createChannel('broadcast', {
|
||||
description: '系统广播',
|
||||
restricted: true,
|
||||
allowedApps: ['system'],
|
||||
maxMessageSize: 1024 * 50, // 50KB
|
||||
messageRetention: 24 * 60 * 60 * 1000 // 24小时
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查应用是否可以访问频道
|
||||
*/
|
||||
private canAccessChannel(appId: string, channel: string): boolean {
|
||||
const channelConfig = this.channels.get(channel)
|
||||
|
||||
if (!channelConfig) {
|
||||
// 频道不存在,默认允许
|
||||
return true
|
||||
}
|
||||
|
||||
if (!channelConfig.restricted) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 系统应用总是有权限
|
||||
if (appId === 'system') {
|
||||
return true
|
||||
}
|
||||
|
||||
return channelConfig.allowedApps.includes(appId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加消息到队列
|
||||
*/
|
||||
private addToQueue(message: EventMessage): void {
|
||||
const queueKey = message.receiverId || 'broadcast'
|
||||
|
||||
if (!this.messageQueue.has(queueKey)) {
|
||||
this.messageQueue.set(queueKey, [])
|
||||
}
|
||||
|
||||
const queue = this.messageQueue.get(queueKey)!
|
||||
|
||||
// 按优先级插入
|
||||
const insertIndex = queue.findIndex((msg) => msg.priority < message.priority)
|
||||
if (insertIndex === -1) {
|
||||
queue.push(message)
|
||||
} else {
|
||||
queue.splice(insertIndex, 0, message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接投递消息
|
||||
*/
|
||||
private async deliverMessage(message: EventMessage): Promise<void> {
|
||||
try {
|
||||
const subscribers = this.getRelevantSubscribers(message)
|
||||
|
||||
if (subscribers.length === 0) {
|
||||
message.status = MessageStatus.FAILED
|
||||
// 只对非系统频道显示警告信息
|
||||
if (message.channel !== 'system') {
|
||||
console.warn(
|
||||
`[EventCommunication] 没有找到频道 ${message.channel} 的订阅者[消息 ID: ${message.id}]`
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 并行发送给所有订阅者
|
||||
const deliveryPromises = subscribers.map(async (subscriber) => {
|
||||
try {
|
||||
// 应用过滤器
|
||||
if (subscriber.filter && !subscriber.filter(message)) {
|
||||
return
|
||||
}
|
||||
|
||||
await subscriber.handler(message)
|
||||
this.statistics.totalMessagesReceived++
|
||||
console.log(
|
||||
`[EventCommunication] 消息 ${message.id} 已投递给订阅者 ${subscriber.id}[频道: ${message.channel}]`
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(`向订阅者 ${subscriber.id} 发送消息失败:`, error)
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
await Promise.allSettled(deliveryPromises)
|
||||
message.status = MessageStatus.DELIVERED
|
||||
} catch (error) {
|
||||
message.status = MessageStatus.FAILED
|
||||
this.statistics.failedMessages++
|
||||
console.error('消息投递失败:', error)
|
||||
|
||||
// 重试机制
|
||||
if (message.retryCount < message.maxRetries) {
|
||||
message.retryCount++
|
||||
message.status = MessageStatus.PENDING
|
||||
setTimeout(() => this.deliverMessage(message), 1000 * message.retryCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相关订阅者
|
||||
*/
|
||||
private getRelevantSubscribers(message: EventMessage): EventSubscriber[] {
|
||||
return Array.from(this.subscribers.values()).filter((subscriber) => {
|
||||
if (!subscriber.active) return false
|
||||
if (subscriber.channel !== message.channel) return false
|
||||
|
||||
// 点对点消息检查接收者
|
||||
if (message.receiverId && subscriber.appId !== message.receiverId) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始消息处理循环
|
||||
*/
|
||||
private startMessageProcessing(): void {
|
||||
const processFrame = (timestamp: number) => {
|
||||
// 如果距离上次处理时间超过设定间隔,则处理消息
|
||||
if (timestamp - this.lastProcessTime >= this.processInterval) {
|
||||
this.processMessageQueue()
|
||||
this.cleanupExpiredMessages()
|
||||
this.lastProcessTime = timestamp
|
||||
}
|
||||
|
||||
// 继续下一帧
|
||||
this.processingInterval = requestAnimationFrame(processFrame)
|
||||
}
|
||||
|
||||
// 启动处理循环
|
||||
this.processingInterval = requestAnimationFrame(processFrame)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消息队列
|
||||
*/
|
||||
private processMessageQueue(): void {
|
||||
for (const [queueKey, messages] of this.messageQueue.entries()) {
|
||||
if (messages.length === 0) continue
|
||||
|
||||
// 处理优先级最高的消息
|
||||
const message = messages.shift()!
|
||||
|
||||
// 检查消息是否过期
|
||||
if (message.expiresAt && message.expiresAt <= new Date()) {
|
||||
message.status = MessageStatus.EXPIRED
|
||||
continue
|
||||
}
|
||||
|
||||
this.deliverMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期消息
|
||||
*/
|
||||
private cleanupExpiredMessages(): void {
|
||||
const now = new Date()
|
||||
|
||||
for (const [queueKey, messages] of this.messageQueue.entries()) {
|
||||
const validMessages = messages.filter((msg) => !msg.expiresAt || msg.expiresAt > now)
|
||||
|
||||
if (validMessages.length !== messages.length) {
|
||||
this.messageQueue.set(queueKey, validMessages)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录消息历史
|
||||
*/
|
||||
private recordMessage(message: EventMessage): void {
|
||||
// 记录发送者历史
|
||||
if (!this.messageHistory.has(message.senderId)) {
|
||||
this.messageHistory.set(message.senderId, [])
|
||||
}
|
||||
this.messageHistory.get(message.senderId)!.push(message)
|
||||
|
||||
// 记录接收者历史
|
||||
if (message.receiverId && message.receiverId !== message.senderId) {
|
||||
if (!this.messageHistory.has(message.receiverId)) {
|
||||
this.messageHistory.set(message.receiverId, [])
|
||||
}
|
||||
this.messageHistory.get(message.receiverId)!.push(message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新活跃订阅者数量
|
||||
*/
|
||||
private updateActiveSubscribersCount(): void {
|
||||
this.statistics.activeSubscribers = Array.from(this.subscribers.values()).filter(
|
||||
(sub) => sub.active
|
||||
).length
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,11 @@
|
||||
import { reactive, ref } from 'vue'
|
||||
import { EventBuilderImpl } from '@/events/impl/EventBuilderImpl'
|
||||
import type { IEventBuilder } from '@/events/IEventBuilder'
|
||||
import type {
|
||||
EventCommunicationEvents,
|
||||
WindowFormDataUpdateParams
|
||||
} from './EventCommunicationService'
|
||||
import type { IEventBuilder, IEventMap, WindowFormDataUpdateParams } from '@/events/IEventBuilder'
|
||||
import type { ResourceType } from './ResourceService'
|
||||
|
||||
// 导入所有服务
|
||||
import { WindowService } from './WindowService'
|
||||
import { ResourceService } from './ResourceService'
|
||||
import { EventCommunicationService } from './EventCommunicationService'
|
||||
import { ApplicationSandboxEngine } from './ApplicationSandboxEngine'
|
||||
import { ApplicationLifecycleManager } from './ApplicationLifecycleManager'
|
||||
import { externalAppDiscovery } from './ExternalAppDiscovery'
|
||||
@@ -33,7 +28,6 @@ export interface SystemStatus {
|
||||
servicesStatus: {
|
||||
windowService: boolean // 窗体服务是否启动
|
||||
resourceService: boolean // 资源服务是否启动
|
||||
eventService: boolean // 事件服务是否启动
|
||||
sandboxEngine: boolean // 沙箱引擎是否启动
|
||||
lifecycleManager: boolean // 生命周期管理器是否启动
|
||||
}
|
||||
@@ -62,10 +56,9 @@ export class SystemServiceIntegration {
|
||||
private startTime: Date
|
||||
|
||||
// 核心服务实例
|
||||
private eventBus: IEventBuilder<EventCommunicationEvents>
|
||||
private eventBus: IEventBuilder<any>
|
||||
private windowService!: WindowService
|
||||
private resourceService!: ResourceService
|
||||
private eventService!: EventCommunicationService
|
||||
private sandboxEngine!: ApplicationSandboxEngine
|
||||
private lifecycleManager!: ApplicationLifecycleManager
|
||||
|
||||
@@ -76,7 +69,6 @@ export class SystemServiceIntegration {
|
||||
servicesStatus: {
|
||||
windowService: false,
|
||||
resourceService: false,
|
||||
eventService: false,
|
||||
sandboxEngine: false,
|
||||
lifecycleManager: false
|
||||
},
|
||||
@@ -96,7 +88,7 @@ export class SystemServiceIntegration {
|
||||
}
|
||||
|
||||
this.startTime = new Date()
|
||||
this.eventBus = new EventBuilderImpl<EventCommunicationEvents>()
|
||||
this.eventBus = new EventBuilderImpl<any>()
|
||||
|
||||
this.setupGlobalErrorHandling()
|
||||
}
|
||||
@@ -104,7 +96,7 @@ export class SystemServiceIntegration {
|
||||
/**
|
||||
* 初始化系统服务
|
||||
*/
|
||||
async initialize(): Promise<void> {
|
||||
private async initialize(): Promise<void> {
|
||||
if (this.initialized.value) {
|
||||
throw new Error('系统服务已初始化')
|
||||
}
|
||||
@@ -138,12 +130,6 @@ export class SystemServiceIntegration {
|
||||
this.systemStatus.running = true
|
||||
|
||||
console.log('系统服务初始化完成')
|
||||
|
||||
// 发送系统就绪事件
|
||||
this.eventService.sendMessage('system', 'system-ready', {
|
||||
timestamp: new Date(),
|
||||
services: Object.keys(this.systemStatus.servicesStatus)
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('系统服务初始化失败:', error)
|
||||
this.systemStatus.lastError = error instanceof Error ? error.message : String(error)
|
||||
@@ -174,14 +160,6 @@ export class SystemServiceIntegration {
|
||||
return this.resourceService
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取事件服务
|
||||
*/
|
||||
getEventService(): EventCommunicationService {
|
||||
this.checkInitialized()
|
||||
return this.eventService
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取沙箱引擎
|
||||
*/
|
||||
@@ -243,7 +221,7 @@ export class SystemServiceIntegration {
|
||||
/**
|
||||
* 关闭系统服务
|
||||
*/
|
||||
async shutdown(): Promise<void> {
|
||||
private async shutdown(): Promise<void> {
|
||||
console.log('关闭系统服务...')
|
||||
|
||||
this.running.value = false
|
||||
@@ -277,10 +255,6 @@ export class SystemServiceIntegration {
|
||||
this.sandboxEngine.destroy()
|
||||
}
|
||||
|
||||
if (this.eventService) {
|
||||
this.eventService.destroy()
|
||||
}
|
||||
|
||||
if (this.windowService) {
|
||||
// 关闭所有窗体
|
||||
const windows = this.windowService.getAllWindows()
|
||||
@@ -307,11 +281,6 @@ export class SystemServiceIntegration {
|
||||
this.resourceService = new ResourceService(this.eventBus)
|
||||
this.systemStatus.servicesStatus.resourceService = true
|
||||
|
||||
// 2. 初始化事件通信服务
|
||||
console.log('初始化事件通信服务...')
|
||||
this.eventService = new EventCommunicationService(this.eventBus)
|
||||
this.systemStatus.servicesStatus.eventService = true
|
||||
|
||||
// 3. 初始化窗体服务
|
||||
console.log('初始化窗体服务...')
|
||||
this.windowService = new WindowService(this.eventBus)
|
||||
@@ -319,7 +288,7 @@ export class SystemServiceIntegration {
|
||||
|
||||
// 4. 初始化沙箱引擎
|
||||
console.log('初始化沙箱引擎...')
|
||||
this.sandboxEngine = new ApplicationSandboxEngine(this.resourceService, this.eventService)
|
||||
this.sandboxEngine = new ApplicationSandboxEngine(this.resourceService)
|
||||
this.systemStatus.servicesStatus.sandboxEngine = true
|
||||
|
||||
// 5. 初始化生命周期管理器
|
||||
@@ -327,7 +296,6 @@ export class SystemServiceIntegration {
|
||||
this.lifecycleManager = new ApplicationLifecycleManager(
|
||||
this.windowService,
|
||||
this.resourceService,
|
||||
this.eventService,
|
||||
this.sandboxEngine
|
||||
)
|
||||
this.systemStatus.servicesStatus.lifecycleManager = true
|
||||
@@ -337,16 +305,6 @@ export class SystemServiceIntegration {
|
||||
* 设置服务间通信
|
||||
*/
|
||||
private setupServiceCommunication(): void {
|
||||
// 监听应用生命周期事件
|
||||
this.eventService.subscribe('system', 'app-lifecycle', (message) => {
|
||||
this.debugLog('[AppLifecycle] 应用生命周期事件:', message.payload)
|
||||
})
|
||||
|
||||
// 监听窗口状态变化事件
|
||||
this.eventService.subscribe('system', 'window-state-change', (message) => {
|
||||
this.debugLog('[WindowState] 窗口状态变化消息已处理:', message.payload)
|
||||
})
|
||||
|
||||
// 监听窗体状态变化(来自 WindowService 的 onStateChange 事件)
|
||||
this.eventBus.addEventListener(
|
||||
'onWindowStateChanged',
|
||||
@@ -354,12 +312,6 @@ export class SystemServiceIntegration {
|
||||
console.log(
|
||||
`[SystemIntegration] 接收到窗体状态变化事件: ${windowId} ${oldState} -> ${newState}`
|
||||
)
|
||||
this.eventService.sendMessage('system', 'window-state-change', {
|
||||
windowId,
|
||||
newState,
|
||||
oldState
|
||||
})
|
||||
console.log(`[SystemIntegration] 已发送 window-state-change 消息到事件通信服务`)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -384,13 +336,11 @@ export class SystemServiceIntegration {
|
||||
// 监听窗体数据更新事件
|
||||
this.eventBus.addEventListener('onWindowFormDataUpdate', (data: WindowFormDataUpdateParams) => {
|
||||
console.log(`[SystemIntegration] 接收到窗体数据更新事件:`, data)
|
||||
this.eventService.sendMessage('system', 'window-form-data-update', data)
|
||||
})
|
||||
|
||||
// 监听窗体调整尺寸开始事件
|
||||
this.eventBus.addEventListener('onWindowFormResizeStart', (windowId: string) => {
|
||||
console.log(`[SystemIntegration] 接收到窗体调整尺寸开始事件: ${windowId}`)
|
||||
this.eventService.sendMessage('system', 'window-form-resize-start', { windowId })
|
||||
})
|
||||
|
||||
// 监听窗体调整尺寸过程中事件
|
||||
@@ -401,18 +351,12 @@ export class SystemServiceIntegration {
|
||||
width,
|
||||
height
|
||||
})
|
||||
this.eventService.sendMessage('system', 'window-form-resizing', {
|
||||
windowId,
|
||||
width,
|
||||
height
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// 监听窗体调整尺寸结束事件
|
||||
this.eventBus.addEventListener('onWindowFormResizeEnd', (windowId: string) => {
|
||||
console.log(`[SystemIntegration] 接收到窗体调整尺寸结束事件: ${windowId}`)
|
||||
this.eventService.sendMessage('system', 'window-form-resize-end', { windowId })
|
||||
})
|
||||
}
|
||||
|
||||
@@ -677,35 +621,7 @@ export class SystemServiceIntegration {
|
||||
* 执行事件相关方法
|
||||
*/
|
||||
private async executeEventMethod(action: string, data: any, appId: string): Promise<any> {
|
||||
switch (action) {
|
||||
case 'emit':
|
||||
return this.eventService.sendMessage(appId, data.channel, data.data)
|
||||
|
||||
case 'on':
|
||||
return this.eventService.subscribe(appId, data.channel, (message) => {
|
||||
// 发送事件到应用
|
||||
const app = this.lifecycleManager.getApp(appId)
|
||||
if (app?.sandboxId) {
|
||||
this.sandboxEngine.sendMessage(app.sandboxId, {
|
||||
type: 'system:event',
|
||||
subscriptionId: data.subscriptionId,
|
||||
message
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
case 'off':
|
||||
return this.eventService.unsubscribe(data.subscriptionId)
|
||||
|
||||
case 'broadcast':
|
||||
return this.eventService.broadcast(appId, data.channel, data.data)
|
||||
|
||||
case 'sendTo':
|
||||
return this.eventService.sendCrossAppMessage(appId, data.targetAppId, data.data)
|
||||
|
||||
default:
|
||||
throw new Error(`未知的事件操作: ${action}`)
|
||||
}
|
||||
throw new Error('事件服务已被移除')
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -794,11 +710,16 @@ export class SystemServiceIntegration {
|
||||
* 开始自动清理
|
||||
*/
|
||||
private startAutoCleanup(): void {
|
||||
if (!this.config.cleanupInterval) return
|
||||
|
||||
this.cleanupInterval = setInterval(() => {
|
||||
this.debugLog('执行自动清理...')
|
||||
|
||||
// 清理事件服务
|
||||
this.eventService.cleanup()
|
||||
// 移除资源服务清理(方法不存在)
|
||||
// this.resourceService.cleanup()
|
||||
|
||||
// 移除事件服务清理
|
||||
// this.eventService.cleanup()
|
||||
|
||||
// 清理沙箱引擎缓存
|
||||
// this.sandboxEngine.cleanup()
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
<template>
|
||||
<div class="window-manager">
|
||||
<!-- 所有已打开的内置应用窗口 -->
|
||||
<teleport
|
||||
v-for="window in builtInWindows"
|
||||
:key="window.id"
|
||||
:to="`#app-container-${window.appId}`"
|
||||
>
|
||||
<component
|
||||
:is="window.component"
|
||||
:key="window.id"
|
||||
v-bind="window.props"
|
||||
@close="closeWindow(window.id)"
|
||||
/>
|
||||
<teleport v-for="window in builtInWindows" :key="window.id" :to="`#app-container-${window.appId}`">
|
||||
<component :is="window.component" :key="window.id" v-bind="window.props" @close="closeWindow(window.id)" />
|
||||
</teleport>
|
||||
</div>
|
||||
</template>
|
||||
@@ -84,7 +75,7 @@ const closeWindow = (windowId: string) => {
|
||||
const window = builtInWindows.value[index]
|
||||
builtInWindows.value.splice(index, 1)
|
||||
console.log(`[WindowManager] 关闭内置应用窗口: ${window.appId} (${windowId})`)
|
||||
|
||||
|
||||
// 通知系统服务关闭窗口
|
||||
if (systemService) {
|
||||
const windowService = systemService.getWindowService()
|
||||
@@ -103,37 +94,39 @@ const removeBuiltInWindow = (windowId: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 监听窗口事件
|
||||
let eventUnsubscriber: (() => void) | null = null
|
||||
// 移除事件监听相关代码
|
||||
// let eventUnsubscriber: (() => void) | null = null
|
||||
|
||||
onMounted(() => {
|
||||
if (systemService) {
|
||||
const eventService = systemService.getEventService()
|
||||
|
||||
// 监听内置应用窗口创建事件
|
||||
const subscriberId = eventService.subscribe('system', 'built-in-window-created', async (message) => {
|
||||
const { windowId, appId } = message.payload
|
||||
await addBuiltInWindow(windowId, appId)
|
||||
})
|
||||
|
||||
// 监听内置应用窗口关闭事件
|
||||
const closeSubscriberId = eventService.subscribe('system', 'built-in-window-closed', (message) => {
|
||||
const { windowId } = message.payload
|
||||
removeBuiltInWindow(windowId)
|
||||
})
|
||||
|
||||
eventUnsubscriber = () => {
|
||||
eventService.unsubscribe(subscriberId)
|
||||
eventService.unsubscribe(closeSubscriberId)
|
||||
}
|
||||
}
|
||||
})
|
||||
// onMounted(() => {
|
||||
// 移除事件服务相关代码
|
||||
// if (systemService) {
|
||||
// const eventService = systemService.getEventService()
|
||||
|
||||
onUnmounted(() => {
|
||||
if (eventUnsubscriber) {
|
||||
eventUnsubscriber()
|
||||
}
|
||||
})
|
||||
// // 监听内置应用窗口创建事件
|
||||
// const subscriberId = eventService.subscribe('system', 'built-in-window-created', async (message) => {
|
||||
// const { windowId, appId } = message.payload
|
||||
// await addBuiltInWindow(windowId, appId)
|
||||
// })
|
||||
|
||||
// // 监听内置应用窗口关闭事件
|
||||
// const closeSubscriberId = eventService.subscribe('system', 'built-in-window-closed', (message) => {
|
||||
// const { windowId } = message.payload
|
||||
// removeBuiltInWindow(windowId)
|
||||
// })
|
||||
|
||||
// eventUnsubscriber = () => {
|
||||
// eventService.unsubscribe(subscriberId)
|
||||
// eventService.unsubscribe(closeSubscriberId)
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
// onUnmounted(() => {
|
||||
// 移除事件服务相关代码
|
||||
// if (eventUnsubscriber) {
|
||||
// eventUnsubscriber()
|
||||
// }
|
||||
// })
|
||||
|
||||
// 暴露给全局使用
|
||||
defineExpose({
|
||||
|
||||
@@ -52,7 +52,6 @@ const systemStatus = ref<SystemStatus>({
|
||||
servicesStatus: {
|
||||
windowService: false,
|
||||
resourceService: false,
|
||||
eventService: false,
|
||||
sandboxEngine: false,
|
||||
lifecycleManager: false
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user