HarmonyOS 6.1 PC应用实战:键鼠协同与多窗体——电商后台管理系统“上车”
系列番外·生产力工具篇。车机篇发布后有开发者问“手机、车机都写了那传说中的鸿蒙PC呢咱们这个电商Demo能不能搞个PC版的后台管理系统” 问到点子上了随着HarmonyOS NEXT向PC端延伸一次开发多端部署的价值将被最大化。今天我们将电商Demo的“后台管理端”适配到PC平台。重点攻克三大PC开发独有的难关键鼠交互范式Hover、右键、拖拽、多窗体架构主窗口子窗口弹窗、响应式布局进阶从移动端单列到PC端多列侧边栏。全程基于API23含官方文档未涉及的PC端UX设计规范。一、前言为什么PC端是鸿蒙生态的“临门一脚”在移动端我们习惯了触摸交互和全屏应用。但在PC端用户的心智模型完全不同效率优先用户使用键盘快捷键CtrlC/V、鼠标精确点击、多窗口并排来提升效率。空间充裕PC拥有大屏幕1920x1080起可以容纳更多信息密度不再需要“隐藏式菜单”。多任务并行用户希望一边看数据报表一边回客服消息一边查商品库存。我们的电商Demo之前只有“C端用户版”手机App缺少“B端商家版”后台管理。今天我们利用ArkUI的跨平台能力在不重写业务逻辑的前提下快速构建一个运行在PC上的电商后台管理系统。这将充分展示ArkUI-X和HarmonyOS NEXT的威力一套代码跑在手机、车机和PC上。二、核心概念辨析移动端思维 vs PC端思维很多开发者直接把手机UI拉伸到PC屏幕上结果惨不忍睹。必须先转变思维维度移动端手机/车机PC端桌面/笔记本交互核心​触摸粗放键鼠精细​ 触摸二合一设备导航结构​底部Tab栏 页面栈顶部菜单栏​ 侧边导航栏 多标签页内容布局​单列流式布局多列栅格布局​ 分栏布局Master-Detail窗口形态​强制全屏自由窗口可缩放、最小化、最大化关键事件​onClick, onTouchonHover, onKeyEvent, onDrop, onDrag​核心认知PC端开发不仅仅是屏幕变大了而是交互逻辑的根本改变。我们需要为鼠标悬停Hover提供视觉反馈为键盘快捷键提供响应逻辑为窗口大小变化提供动态布局。三、代码实现从“手持电商”到“桌面后台”3.1 环境准备与配置新建PC模块在DevEco Studio中右键项目 - New - Module - PC Module。设备类型声明修改module.json5声明支持PC。{ module: { deviceTypes: [pc], // 声明为PC应用 abilities: [ { name: EntryAbility, window: { designWidth: 1920, // 设计稿基准宽度 autoDesignWidth: true, maxWindowRatio: 3.0, // 最大窗口比例 minWindowRatio: 0.5 // 最小窗口比例 } } ] } }资源目录在resources下新建layout-pc目录存放PC专属布局。3.2 构建PC端经典布局侧边栏主内容区创建entry_pc/src/main/ets/pages/AdminIndex.ets。采用经典的“主-详”布局。import { router } from kit.ArkUI import { CommonConstants } from ../common/Constants // 定义菜单项接口 interface MenuItem { id: string icon: Resource label: string route: string } Entry Component struct AdminIndex { State currentMenu: string dashboard State windowWidth: number 1920 // 监听窗口宽度 private menus: MenuItem[] [ { id: dashboard, icon: $r(app.media.ic_dashboard), label: 数据看板, route: pages/admin/Dashboard }, { id: products, icon: $r(app.media.ic_goods), label: 商品管理, route: pages/admin/ProductList }, { id: orders, icon: $r(app.media.ic_order), label: 订单管理, route: pages/admin/OrderList }, { id: settings, icon: $r(app.media.ic_settings), label: 系统设置, route: pages/admin/Settings } ] aboutToAppear(): void { // 监听窗口尺寸变化实现响应式布局 window.getLastWindow(getContext(this)).then((win) { win.on(windowSizeChange, (size) { this.windowWidth size.width }) }) } build() { Row() { // 左侧固定导航栏PC专属 Column() { // Logo Text(Shop Admin) .fontSize(24) .fontWeight(FontWeight.Bold) .fontColor(#0A59F7) .margin({ bottom: 40 }) // 菜单列表 List() { ForEach(this.menus, (menu: MenuItem) { Row() { Image(menu.icon) .width(24) .height(24) .fillColor(this.currentMenu menu.id ? #0A59F7 : #666) if (this.windowWidth 1200) { // 宽屏显示文字窄屏只显示图标 Text(menu.label) .fontSize(16) .fontColor(this.currentMenu menu.id ? #0A59F7 : #333) .margin({ left: 12 }) } } .width(100%) .padding(16) .backgroundColor(this.currentMenu menu.id ? #E8F0FE : Color.Transparent) .borderRadius(12) .onClick(() { this.currentMenu menu.id // PC端通常是单页应用SPA这里用路由替换内容区而不是打开新页面 router.replacePath({ url: menu.route }) }) .onHover((isHover) { // 鼠标悬停效果PC核心交互 if (isHover this.currentMenu ! menu.id) { // 可以在这里改变背景色提供反馈 } }) }) } .layoutWeight(1) // 底部用户信息 Row() { Image($r(app.media.avatar)) .width(40) .height(40) .borderRadius(20) if (this.windowWidth 1200) { Column() { Text(Admin) .fontSize(14) .fontWeight(FontWeight.Medium) Text(adminshop.com) .fontSize(12) .fontColor(#999) } .margin({ left: 12 }) .layoutWeight(1) } } .padding(16) } .width(this.windowWidth 1200 ? 240 : 80) // 响应式宽度 .height(100%) .backgroundColor(#F8F9FA) .padding(16) .transition(TransitionEffect.OPACITY.animation({ duration: 300 })) // 平滑过渡 // 右侧主内容区 Column() { // 顶部工具栏面包屑搜索快捷键提示 Row() { Text(this.menus.find(m m.id this.currentMenu)?.label || ) .fontSize(20) .fontWeight(FontWeight.Medium) Blank() TextInput({ placeholder: 搜索商品/订单... }) .width(300) .height(40) .backgroundColor(#F0F2F5) .borderRadius(20) .padding({ left: 16 }) Text(⌨️ CtrlK) .fontSize(12) .fontColor(#999) .margin({ left: 10 }) } .width(100%) .padding(16) .border({ width: { bottom: 1 }, color: #EEE }) // 内容区根据菜单切换 Stack() { // 这里根据currentMenu加载不同的Component // 例如Dashboard, ProductList等 this.ContentArea() } .layoutWeight(1) .backgroundColor(#FFFFFF) } .layoutWeight(1) .height(100%) } .width(100%) .height(100%) .onKeyEvent((event: KeyEvent) { // PC端键盘快捷键支持 if (event.type KeyType.Down) { // Ctrl K 聚焦搜索框 if (event.ctrlKey event.keyCode KeyCode.KEY_K) { // 实际开发中这里需要拿到TextInput的Controller并调用focus() promptAction.showToast({ message: 搜索快捷键触发 }) return true // 阻止事件冒泡 } // ESC 返回仪表盘 if (event.keyCode KeyCode.KEY_ESCAPE) { this.currentMenu dashboard return true } } return false }) } /** * 根据当前菜单渲染内容区 */ Builder ContentArea() { if (this.currentMenu products) { // 复用手机端的商品列表逻辑但改变UI布局 this.ProductManagementTable() } else { // 其他页面的占位符 Text(内容加载中...) .fontSize(16) .fontColor(#999) } } /** * PC端商品管理表格替代手机端的List */ Builder ProductManagementTable() { List() { // 表头 ListItem() { Row() { Text(商品名称).layoutWeight(3) Text(价格).layoutWeight(1) Text(库存).layoutWeight(1) Text(状态).layoutWeight(1) Text(操作).layoutWeight(2) } .padding(16) .backgroundColor(#F8F9FA) .fontWeight(FontWeight.Medium) } // 数据行 ForEach(this.getProducts(), (item: GoodsBean) { ListItem() { Row() { // 商品名图 Row() { Image(item.image).width(48).height(48).borderRadius(8).objectFit(ImageFit.Cover) Text(item.name).margin({ left: 12 }).maxLines(1) }.layoutWeight(3) Text(¥${item.price}).layoutWeight(1) Text(item.stock.toString()).layoutWeight(1) Text(item.status 1 ? 在售 : 下架) .layoutWeight(1) .fontColor(item.status 1 ? #4CAF50 : #F44336) // 操作按钮组 Row() { Button(编辑).height(32).backgroundColor(#E8F0FE).fontColor(#0A59F7) Button(下架).height(32).backgroundColor(#FFF3E0).fontColor(#FF9800).margin({ left: 8 }) }.layoutWeight(2) } .padding(16) .border({ width: { bottom: 1 }, color: #EEE }) .onHover((isHover) { // 鼠标悬停高亮行 // 实际开发中可以通过状态变量控制背景色 }) // PC端支持右键菜单 .onContextMenu((event: ContextMenuEvent) { this.showContextMenu(event.x, event.y, item) }) } }) } .sticky(StickyStyle.Header) // 表头吸顶 .width(100%) .height(100%) } /** * 显示右键菜单PC专属 */ showContextMenu(x: number, y: number, item: GoodsBean): void { ContextMenu.show({ x: x, y: y, menuItems: [ { title: 复制商品ID, icon: $r(app.media.ic_copy), action: () console.log(item.id) }, { title: 查看详情, icon: $r(app.media.ic_view), action: () router.pushUrl({ url: pages/admin/ProductDetail, params: { id: item.id } }) }, { divider: true }, { title: 删除商品, icon: $r(app.media.ic_delete), fontColor: #F44336, action: () this.deleteProduct(item.id) } ] }) } // 模拟数据 getProducts(): GoodsBean[] { return [ { id: 1, name: HarmonyOS 6.1 定制款手机, price: 5999, stock: 99, status: 1, image: phone.jpg }, { id: 2, name: 鸿蒙智行 车载充电器, price: 199, stock: 500, status: 1, image: charger.jpg }, { id: 3, name: 星闪无线鼠标, price: 299, stock: 0, status: 0, image: mouse.jpg } ] } deleteProduct(id: number): void { promptAction.showDialog({ title: 确认删除, message: 确定要删除商品ID: ${id} 吗, buttons: [{ text: 取消 }, { text: 删除, color: #F44336 }] }).then((result) { if (result.index 1) { // 执行删除逻辑 promptAction.showToast({ message: 删除成功 }) } }) } }3.3 关键PC交互实现上面的代码展示了三个关键的PC交互Hover悬停.onHover()。当鼠标移到列表行上时可以改变背景色给用户即时的视觉反馈。Context Menu右键菜单.onContextMenu()。PC用户习惯右键操作这里替代了手机上的长按菜单。Keyboard Shortcuts键盘快捷键.onKeyEvent()。监听CtrlK、ESC等组合键大幅提升操作效率。四、踩坑记录官方文档没写的5个PC坑窗口尺寸监听windowSizeChange回调非常频繁如果在回调里直接做重计算如重新构建整个UI树会导致界面卡顿。解决使用防抖Debounce或节流Throttle技术限制更新频率如200ms一次。焦点管理FocusPC端有“焦点”的概念。点击一个按钮后焦点可能还在按钮上此时按空格键会再次触发点击。在输入框失去焦点时需要手动管理焦点状态。拖拽文件上传PC端常见需求是拖拽文件到窗口上传。onDrop事件需要处理DragEvent从中提取文件的URIevent.items[0].uri然后通过文件管理API读取。官方文档对此流的介绍比较分散。多窗口层级PC端可能有多个窗口如主窗口、设置窗口、弹窗。promptAction.showDialog默认是模态的会阻塞主窗口。对于非阻塞通知应该使用Toast或自定义的Overlay。字体渲染PC端屏幕PPI差异巨大从普通显示器到4K屏。使用fp单位字体像素而不是px确保字体在不同DPI下物理大小一致。

相关新闻

STM32串口通信开发实战:从初始化到中断处理

STM32串口通信开发实战:从初始化到中断处理

1. STM32串口通信核心模块解析在STM32开发中,usart.c作为串口通信的核心驱动文件,承担着硬件初始化、数据收发和中断处理等关键功能。这个文件通常包含两个核心函数:uart_init()用于串口硬件初始化,USART1_IRQHandler()处理串口中…

2026/9/23 8:58:22 阅读更多 →
HarmonyOS 6.1 车机应用实战:把电商“搬”进智能座舱

HarmonyOS 6.1 车机应用实战:把电商“搬”进智能座舱

系列番外智慧出行篇。上篇聊完空间计算,评论区有车友问:“鸿蒙车机这么火,能不能把咱们的电商Demo适配到车机上?等红灯时逛个街?” 这需求太真实了!HarmonyOS智能座舱(HarmonySpace)…

2026/9/22 23:01:47 阅读更多 →
HarmonyOS 6.1 空间计算初探:把电商展厅搬进“虚实融合”新世界

HarmonyOS 6.1 空间计算初探:把电商展厅搬进“虚实融合”新世界

系列番外空间计算篇。终章发布后,有读者留言:“现在都在聊Vision Pro和空间计算,鸿蒙在这方面有布局吗?能不能把我们这个电商Demo弄成3D的?” 答案是肯定的。HarmonyOS NEXT的ArkUI 3D和Spatial Computing Kit正是为此…

2026/9/20 10:42:15 阅读更多 →

最新新闻

MySQL基础入门:从表结构到Python连接

MySQL基础入门:从表结构到Python连接

MySQL 是目前最流行的开源关系型数据库管理系统之一,凭借高性能、高可靠性和易用性,被广泛应用于各类 Web 应用、电商平台、内容管理系统以及数据分析场景。它支持标准的 SQL 语言,能够高效地存储、查询和管理结构化数据,同时提供…

2026/9/24 8:42:59 阅读更多 →
Keil MDK芯片包安装失败?三个隐藏设置与完整排查指南

Keil MDK芯片包安装失败?三个隐藏设置与完整排查指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 8:42:59 阅读更多 →
video-use Manim 技能 Camera  3D 参考实战:从 2D 相机运镜到 3D 场景、局部放大与线性变换

video-use Manim 技能 Camera 3D 参考实战:从 2D 相机运镜到 3D 场景、局部放大与线性变换

AI 技能/插件音视频视频处理人工智能 【免费下载链接】video-use Edit videos with coding agents 项目地址: https://gitcode.com/GitHub_Trending/vid/video-use 点击查看 免费下载 本指南以仓库中 Camera and 3D Reference 为骨架,系统讲解在 video-…

2026/9/24 8:42:58 阅读更多 →
Dopamine 断点续训基石:深入解析 get_latest_checkpoint_number 与离散域 Checkpointer 机制

Dopamine 断点续训基石:深入解析 get_latest_checkpoint_number 与离散域 Checkpointer 机制

机器学习深度学习 【免费下载链接】dopamine Dopamine is a research framework for fast prototyping of reinforcement learning algorithms. 项目地址: https://gitcode.com/gh_mirrors/do/dopamine 点击查看 免费下载 导读 dopamine.discrete_domains.checkp…

2026/9/24 8:42:58 阅读更多 →
高亲和力+低背景!揭秘诊断级单抗的筛选秘诀

高亲和力+低背景!揭秘诊断级单抗的筛选秘诀

单克隆抗体(Monoclonal Antibody,简称mAb)作为一种高特异性、高亲和力的生物大分子,已经在临床诊断、治疗及科研领域中发挥了举足轻重的作用。特别是在诊断领域,单抗因其独特的靶向性和高灵敏度,成为了许多…

2026/9/24 8:42:58 阅读更多 →
20页的复盘只动3页,AI改完其他页没乱

20页的复盘只动3页,AI改完其他页没乱

20页里只动3页 一位每天跟表格、文档打交道的人,手上刚做完一份月度复盘:一份数据表,加一份20页的汇报文件。开会前一天,他往表格里加了一张决策看板,又在汇报文件里挑出3页重排——其余17页,全都没动。 整…

2026/9/24 8:41:58 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/23 4:55:02 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/23 4:49:06 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/23 9:53:41 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/23 9:53:40 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/23 9:53:40 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/23 9:53:40 阅读更多 →