10 KiB
DesktopContainer组件
**Referenced Files in This Document** - [DesktopContainer.vue](file://src/ui/desktop-container/DesktopContainer.vue) - [useDesktopContainerInit.ts](file://src/ui/desktop-container/useDesktopContainerInit.ts) - [App.vue](file://src/ui/App.vue) - [AppIcon.vue](file://src/ui/desktop-container/AppIcon.vue) - [IDesktopAppIcon.ts](file://src/ui/types/IDesktopAppIcon.ts) - [IGridTemplateParams.ts](file://src/ui/types/IGridTemplateParams.ts)目录
简介
DesktopContainer 是 Vue 桌面应用的核心容器组件,负责管理桌面图标的布局、状态和交互。该组件通过组合式函数 useDesktopContainerInit 实现了动态响应式网格系统,并结合 localStorage 提供图标位置的持久化存储能力。作为桌面环境的主视图容器,它与 App.vue 父组件构成清晰的数据流结构,为上层应用提供稳定可靠的桌面管理服务。
核心功能分析
DesktopContainer 组件承担着桌面环境的核心职责,主要包括:
- 初始化并维护一个基于 CSS Grid 的响应式布局系统
- 动态计算网格行列数及单元格实际尺寸以适应容器变化
- 管理所有桌面应用图标的元数据及其在网格中的坐标位置
- 通过本地存储实现用户自定义图标准置的持久化
- 提供标准化的应用启动接口(双击事件)
- 支持拖拽重排功能并与子组件
AppIcon协同工作
该组件的设计体现了关注点分离原则,将复杂的布局逻辑封装在独立的组合式函数中,保持了模板的简洁性和可维护性。
Section sources
响应式网格布局初始化
useDesktopContainerInit组合式函数
useDesktopContainerInit 函数是整个桌面布局系统的核心引擎,接收一个 CSS 选择器字符串 containerStr 作为参数,用于定位需要监控尺寸变化的 DOM 容器元素。
classDiagram
class IGridTemplateParams {
+cellExpectWidth : number
+cellExpectHeight : number
+cellRealWidth : number
+cellRealHeight : number
+gapX : number
+gapY : number
+colCount : number
+rowCount : number
}
class useDesktopContainerInit {
-container : HTMLElement
-gridTemplate : IGridTemplateParams
-ro : ResizeObserver
-appIconsRef : Ref~Array~
-exceedApp : Ref~Array~
+return gridStyle : ComputedRef
}
useDesktopContainerInit --> IGridTemplateParams : "uses"
Diagram sources
gridTemplate参数计算逻辑
gridTemplate 对象采用 reactive 声明为响应式数据,包含以下关键属性:
cellExpectWidth和cellExpectHeight:单元格期望尺寸(默认90x110px)gapX和gapY:行列间距(默认4px)colCount和rowCount:动态计算的总行列数
初始状态下,行列数设为1,随后由 ResizeObserver 根据容器实际尺寸重新计算。
gridStyle动态生成机制
通过 computed 属性 gridStyle 动态生成应用于容器的内联样式:
flowchart TD
Start([开始计算]) --> CalcColumns["构建 gridTemplateColumns<br/>repeat(colCount, minmax(cellExpectWidth + 'px', 1fr))"]
CalcColumns --> CalcRows["构建 gridTemplateRows<br/>repeat(rowCount, minmax(cellExpectHeight + 'px', 1fr))"]
CalcRows --> SetGap["设置 gap: gapY + 'px' gapX + 'px'"]
SetGap --> ReturnStyle["返回样式对象"]
ReturnStyle --> End([完成])
此计算属性确保了每当 gridTemplate 中的任何字段发生变化时,都能立即生成正确的 CSS Grid 样式规则。
Section sources
ResizeObserver尺寸监听机制
尺寸监听流程
ResizeObserver 被用来监听传入选择器所匹配容器的尺寸变化,其回调函数执行以下步骤:
sequenceDiagram
participant RO as ResizeObserver
participant CT as Container
participant GT as gridTemplate
RO->>CT : getBoundingClientRect()
CT-->>RO : 返回容器矩形信息
RO->>GT : 计算 colCount
RO->>GT : 计算 rowCount
RO->>GT : 计算 cellRealWidth
RO->>GT : 计算 cellRealHeight
Diagram sources
行列数计算公式
根据容器当前宽度和高度,使用如下数学公式计算最优行列分布:
colCount = floor((width + gapX) / (cellExpectWidth + gapX))
rowCount = floor((height + gapY) / (cellExpectHeight + gapY))
这种算法确保即使在存在间隙的情况下也能最大化利用可用空间。
实际单元格尺寸调整
考虑到间隙对总可用空间的影响,实际单元格尺寸通过以下方式精确计算:
const w = containerRect.width - (gapX * (colCount - 1))
const h = containerRect.height - (gapY * (rowCount - 1))
cellRealWidth = w / colCount
cellRealHeight = h / rowCount
最终结果保留两位小数,保证视觉上的平滑过渡。
Section sources
应用图标状态管理与持久化
appIconsRef状态管理
appIconsRef 是一个 ref 类型的响应式数组,存储所有桌面应用图标的配置信息。每个图标对象遵循 IDesktopAppIcon 接口规范:
erDiagram
IDesktopAppIcon {
string name PK
string icon
string path
int x
int y
}
Diagram sources
初始图标数据来源于两个渠道:
- 当前运行的应用进程列表(模拟为空数组)
localStorage中保存的历史图标位置信息
系统优先使用历史记录中的坐标,若无则按顺序自动分配。
localStorage持久化机制
通过 watch 监听器实现自动持久化:
flowchart LR
A[appIconsRef变化] --> B{触发watch}
B --> C[序列化为JSON字符串]
C --> D[存入localStorage]
D --> E[键名为'desktopAppIconInfo']
同时,在初始化时从 localStorage 读取已有数据,实现跨会话的状态恢复。
图标重排逻辑
当窗口大小导致网格行列数变化时,rearrangeIcons 函数会被调用,执行智能重排算法:
- 优先保留原有有效位置的图标
- 为移出可视区域的图标寻找新的空闲位置
- 若无足够空间,则将其加入
exceedApp隐藏列表
该机制确保用户体验的一致性,避免图标因窗口缩放而丢失。
Section sources
模板渲染与事件处理
v-for循环渲染机制
组件模板使用 v-for 指令遍历 appIconsRef 数组,为每个图标实例化一个 AppIcon 子组件:
<AppIcon
v-for="(appIcon, i) in appIconsRef"
:key="i"
:iconInfo="appIcon"
:gridTemplate="gridTemplate"
@dblclick="runApp(appIcon)"
/>
:key 使用索引值确保渲染性能,iconInfo 和 gridTemplate 作为 props 向下传递必要数据。
runApp双击事件扩展点
@dblclick 事件绑定到 runApp 方法,目前为空实现,作为未来功能扩展的预留接口:
const runApp = (appIcon: IDesktopAppIcon) => {}
此处可集成应用启动逻辑,如进程管理器调用、窗口创建等。
AppIcon组件协同
AppIcon 组件接收父级传递的 gridTemplate 参数,结合自身 x/y 坐标计算绝对位置:
grid-column: ${x}/${x + 1};
grid-row: ${y}/${y + 1};
并实现拖拽功能,在释放时根据鼠标位置更新坐标,形成闭环控制。
Section sources
与父组件的数据流关系
挂载关系分析
DesktopContainer 被直接嵌入 App.vue 的模板结构中,位于 .desktop-bg 容器内部:
graph TB
A[App.vue] --> B[desktop-root]
B --> C[desktop-bg]
C --> D[DesktopContainer]
B --> E[task-bar]
这种层级结构明确了其作为主内容区核心组件的地位。
数据流路径
数据流动遵循典型的 Vue 单向数据流模式:
flowchart LR
A[onMounted Hook] --> B[查询DOM容器]
B --> C[启动ResizeObserver]
C --> D[触发尺寸计算]
D --> E[更新gridTemplate]
E --> F[computed生成gridStyle]
F --> G[模板应用样式]
H[localStorage读取] --> I[初始化appIconsRef]
I --> J[渲染AppIcon列表]
J --> K[用户交互]
K --> L[更新appIconsRef]
L --> M[watch触发持久化]
整个过程无需向上通信,完全由组合式函数内部闭环处理。
Section sources
自定义容器集成示例
要在其他环境中复用 DesktopContainer,可通过修改 containerStr 参数指定不同的监听目标:
// 在自定义组件中使用
const { appIconsRef, gridStyle, gridTemplate } = useDesktopContainerInit('#custom-desktop-container')
对应的 HTML 结构需包含匹配的选择器:
<div id="custom-desktop-container" :style="gridStyle">
<!-- AppIcon will be rendered here -->
</div>
注意确保目标元素具有明确的尺寸(宽高),否则 ResizeObserver 无法正确计算布局参数。此外,建议保持原有的 CSS 类名以继承样式定义。
Section sources