一、前言很多刚开始学习 HarmonyOS 的同学都会遇到一个问题例如class LoginRepository { login(){ // 登录失败 // 想直接弹个Dialog } }结果发现Cannot find UIContext 或者 PromptAction is undefined为什么因为HarmonyOS 的所有弹窗本质都是依赖 UIContext。而Repository Manager Util Http SDK这些都属于逻辑层Non-UI它们根本不知道当前页面是谁 当前Window是谁 当前Ability是谁所以逻辑层不能直接弹窗。二、官方推荐方式官方已经说明PromptAction依赖 UI 执行上下文推荐UIContext.getPromptAction()而不是promptAction.showDialog()因为UIContext ↓ PromptAction ↓ Window ↓ Ability ↓ 当前页面这样生命周期才一致。三、为什么逻辑类没有UIContext例如Entry ↓ HomePage ↓ LoginPage ↓ Button ↓ onClick() ↓ LoginManager.login()LoginManagerclass LoginManager{ }这里this.getUIContext()根本不存在。因为只有Component 只有Ability 才拥有UIContext四、错误示例很多人会这样写import promptAction from ohos.promptAction class LoginManager{ login(){ promptAction.openCustomDialog() } }运行UIContext不存在 无法打开Dialog原因promptAction 没有Window 没有UI 没有Context五、正确思路整个工程应该变成UI ↓ 获得UIContext ↓ 保存UIContext ↓ DialogManager ↓ LoginManager ↓ 任何地方调用即Repository ↓ DialogManager.show() ↓ UIContext ↓ PromptAction六、第一步保存UIContext例如EntryAbility初始化onWindowStageCreate(windowStage: window.WindowStage) { windowStage.loadContent(pages/Index, () { window.getLastWindow(this.context).then(win{ let uiContext win.getUIContext() }) }) }然后保存AppStorage.setOrCreate( UIContext, uiContext )这样整个应用AppStorage ↓ UIContext任何地方都能拿到。七、封装ContextManager例如export class ContextManager{ static getUIContext():UIContext{ return AppStorage.getUIContext(UIContext)! } }以后ContextManager.getUIContext()即可。八、封装DialogManager推荐所有项目都建立common DialogManager.ts例如export class DialogManager{ }以后Toast Loading Dialog Sheet Popup 全部放这里统一管理。九、封装showDialog()例如static show(){ let ui ContextManager.getUIContext() ui.getPromptAction() }这里UIContext ↓ PromptAction ↓ Dialog即可打开弹窗。十、为什么推荐PromptActionHarmonyOS目前主要有两种弹窗方式CustomDialogController和PromptAction.openCustomDialog()其中官方更推荐基于UIContext PromptAction的方案因为它与页面生命周期解耦更适合工程化封装和在逻辑层统一调用。十一、企业级架构大型项目通常采用如下结构UI │ ├── Page ├── Dialog ├── Sheet └── Popup Business │ ├── LoginManager ├── UserManager ├── PayManager └── OrderManager Infrastructure │ ├── DialogManager ├── ToastManager ├── LoadingManager └── ContextManager业务层永远不关心弹窗如何实现LoginManager.login()失败DialogManager.showError(账号不存在)即可。十二、统一弹窗接口建议定义统一能力DialogManager.showInfo() DialogManager.showSuccess() DialogManager.showError() DialogManager.showWarning() DialogManager.showLoading() DialogManager.dismissLoading() DialogManager.showConfirm()业务代码变得非常简单await login() DialogManager.showSuccess(登录成功)十三、异步确认框例如const result await DialogManager.confirm(确定删除吗)返回true false业务代码if(result){ delete() }比传统回调更加清晰。十四、避免重复弹窗建议增加状态控制private static showing false打开前判断if(DialogManager.showing){ return }关闭DialogManager.showingfalse可以避免用户连续点击造成多个弹窗叠加。十五、弹窗队列企业项目经常会遇到登录失效 ↓ 网络异常 ↓ 版本更新 ↓ 支付结果多个弹窗同时出现。推荐维护Queue ↓ show() ↓ poll() ↓ dismiss() ↓ next()保证一次只显示一个Dialog用户体验更好。十六、生命周期处理页面关闭时Page disappear ↓ Dialog dismiss ↓ 释放 ComponentContent ↓ 释放引用避免Window 泄漏UIContext 持有旧页面重复创建 Builder特别是在使用openCustomDialog时应在关闭后及时释放对应资源。十七、适用场景这种全局弹窗方案尤其适用于Repository 登录失败提示网络请求异常Token 失效重新登录支付结果提示BLE 连接失败WebSocket 断开提示下载进度 Loading全局版本更新提示权限申请说明风险操作确认十八、最佳实践总结经过大量 HarmonyOS 项目实践推荐遵循以下原则不要在业务逻辑类直接依赖 UI 组件。统一保存并管理UIContext。优先使用UIContext.getPromptAction()实现弹窗。封装DialogManager所有弹窗统一入口。支持 Promise 化确认框提升业务代码可读性。增加弹窗队列和重复弹窗拦截机制。页面销毁时及时释放弹窗资源避免内存泄漏。这样可以实现真正的UI 与业务解耦逻辑层一行代码弹窗统一样式、统一动画、统一生命周期管理适用于中大型 HarmonyOS 企业项目。