# P0-8: UIManager 需求详细文档 ## 对齐说明 本文档以 `docs/requirements/p0-requirements-set.md` 为准。P0 UIManager 支持可配置层级和层内 LIFO 栈,但不做完整导航路由系统。`UIPanelAttribute` 明确包含 `path` 参数。 ## 目标 提供 MVVM Panel 生命周期管理、运行时层级注册、层内栈式打开关闭、手写 Bind/Unbind 和基础缓存/销毁策略。 ## 层级注册 ```csharp public sealed class UIManager { public void RegisterLayer( string name, Canvas canvas, int sortOrder, PanelStrategy strategy = PanelStrategy.Destroy); } public enum PanelStrategy { Destroy, Cache } ``` 规则: - P0 不强制固定 `Bottom/Middle/Top`。 - 推荐默认层级为 `background / hud / popup / dialog / system`。 - 项目可只注册部分层级。 - 未注册层级打开 Panel 时抛异常。 ## Panel Attribute ```csharp [AttributeUsage(AttributeTargets.Class)] public sealed class UIPanelAttribute : Attribute { public string Layer { get; } public string Path { get; } public PanelStrategy? OverrideStrategy { get; } public UIPanelAttribute(string layer); public UIPanelAttribute(string layer, string path); public UIPanelAttribute(string layer, PanelStrategy overrideStrategy); public UIPanelAttribute(string layer, string path, PanelStrategy overrideStrategy); } ``` 规则: - `Layer` 必填。 - `Path` 可选;未填时按命名约定推导。 - `OverrideStrategy` 可选,优先于层级默认策略。 - 说明:C# Attribute 构造参数不支持 `Nullable`,因此 `OverrideStrategy` 属性保持 nullable,但通过重载表达“未指定”和“指定覆盖策略”。 ## Panel 基类 ```csharp public abstract class UIPanelBase : MonoBehaviour { protected TViewModel ViewModel { get; private set; } protected CompositeDisposable Disposables { get; } = new(); public void Bind(TViewModel viewModel) { ViewModel = viewModel; OnBind(viewModel); } public virtual void Unbind() { Disposables.Clear(); } protected abstract void OnBind(TViewModel viewModel); } ``` ## 打开与关闭接口 ```csharp public sealed class UIManager { public Task OpenAsync( TViewModel viewModel, CancellationToken cancellationToken) where TPanel : UIPanelBase; public void Close(); public void CloseLayer(string layerName); public void CloseAll(); public bool IsOpen(); } ``` ## 生命周期 ```text OpenAsync: 1. 读取 UIPanelAttribute 2. 校验层级已注册 3. 解析资源路径 4. IResourceService.LoadAsync 5. Instantiate 到目标 Canvas 6. Bind(viewModel) 7. SetActive(true) 8. 压入层内栈 Close: 1. 校验目标 Panel 是所在层级栈顶 2. 从栈弹出 3. Unbind() 4. Destroy 策略:Destroy GameObject 并释放资源 handle 5. Cache 策略:SetActive(false),保留实例和资源 handle ``` 路径约定: ```text ShopPanel -> UI/Shop/Prefab ConfirmPanel -> UI/Confirm/Prefab MainHudPanel -> UI/MainHud/Prefab ``` ## 暂不做 - 自动 UI 绑定。 - 导航路由系统。 - 跨 Feature 页面恢复。 - Panel 预加载。 - SafeArea。 - 多语言 UI 切换。 - 红点/通知系统。 - 复杂转场动画。 ## 验收标准 | # | 标准 | 通过条件 | |---|------|---------| | 1 | 注册层级 | RegisterLayer 后 Panel 可挂载到对应 Canvas | | 2 | Attribute 层级 | Panel 通过 UIPanelAttribute 找到层级 | | 3 | path 参数 | Attribute 指定 path 时按指定路径加载 | | 4 | 路径约定 | 未指定 path 时按命名约定加载 | | 5 | Open | 异步加载 Prefab、实例化、Bind、入栈 | | 6 | Close | 栈顶 Panel Unbind 并按策略关闭 | | 7 | LIFO | 关闭非栈顶 Panel 抛异常 | | 8 | Cache | Cache Panel 再次打开时复用并重新 Bind | | 9 | Destroy | Destroy Panel 关闭后释放资源 handle | | 10 | CloseLayer | 只关闭指定层级 | | 11 | CloseAll | 关闭所有层级 | | 12 | Bind 失败 | 清理已创建对象和资源 handle 后抛异常 | ## 依赖关系 ```text UIManager ├── IResourceService ├── R3 ├── Unity Canvas / GameObject / MonoBehaviour └── CancellationToken ```