鸿蒙原生应用实战(五):数据统计与个人中心——柱状图实现、统计计算与设置面板

鸿蒙原生应用实战(五):数据统计与个人中心——柱状图实现、统计计算与设置面板 鸿蒙原生应用实战五数据统计与个人中心——柱状图实现、统计计算与设置面板本文是系列终篇讲解快递追踪 App 中最后两个页面包裹统计PackageStatsPage和个人中心ProfilePage。涵盖纯 ArkTS 柱状图实现、统计指标计算、Toggle 设置、主题色体系等完整内容并总结全项目架构。一、PackageStatsPage — 包裹统计页1.1 功能需求统计页是 App 的数据分析中心需要展示总览卡片总包裹数、已签收数、平均时效月度趋势图每月收发数量 柱状条可视化快递公司分布各公司使用数量 横向柱状条1.2 数据模型interfaceMonthlyStats{month:string;// 月份标识如 2024-08count:number;// 当月包裹总数delivered:number;// 当月已签收数avgDays:number;// 当月平均时效天}interfaceCompanyStats{name:string;// 公司名count:number;// 使用次数color:string;// 柱状图颜色}注意CompanyStats的color是字符串类型如#FF4A90D9因为在 ArkTS 中动态颜色字符串可以通过fill()直接传入。1.3 数据初始化使用aboutToAppear生命周期进行数据初始化aboutToAppear():void{// 月度数据letm1:MonthlyStats{month:2024-08,count:3,delivered:3,avgDays:2.5};letm2:MonthlyStats{month:2024-09,count:5,delivered:4,avgDays:3.0};// ...this.monthlyData[m1,m2,m3,m4,m5,m6];// 公司分布letc1:CompanyStats{name:顺丰速运,count:8,color:#FF4A90D9};// ...this.companyStats[c1,c2,c3,c4,c5,c6];}为什么在aboutToAppear而不是build中初始化分离数据准备和 UI 构建数据初始化只执行一次而非每次渲染1.4 ArkTS 严格对象字面量规则在 ArkTS 严格模式下arkts-no-untyped-obj-literals不能直接写// ❌ 编译错误对象字面量必须有显式类型this.monthlyData[{month:2024-08,count:3,delivered:3,avgDays:2.5}];必须将每个对象字面量赋值给类型化变量后再引用// ✅ 正确做法提取为独立类型变量letm1:MonthlyStats{month:2024-08,count:3,delivered:3,avgDays:2.5};this.monthlyData[m1];这个规则初看繁琐但有助于提升代码可读性和类型安全。1.5 总览卡片Column(){Text(总览)Row(){Column(){Text(this.totalPackages.toString()).fontSize(32).fontWeight(FontWeight.Bold).fontColor($r(app.color.primary))Text(总包裹数)}Column(){Text(this.totalDelivered.toString()).fontSize(32).fontWeight(FontWeight.Bold).fontColor($r(app.color.status_delivered))Text(已签收)}Column(){Text(this.overallAvgDays天).fontSize(24).fontWeight(FontWeight.Bold).fontColor($r(app.color.rating_star))Text(平均时效)}}}三个指标三种颜色指标字体大小颜色含义总包裹数32fp主题蓝核心指标已签收数32fp签收绿正向指标平均时效24fp (稍小)星标黄参考指标1.6 计算属性gettergettotalPackages():number{letsum0;for(letmofthis.monthlyData){if(m)summ.count;}returnsum;}gettotalDelivered():number{letsum0;for(letmofthis.monthlyData){if(m)summ.delivered;}returnsum;}getoverallAvgDays():string{letsum0;letcount0;for(letmofthis.monthlyData){if(mm.delivered0){summ.avgDays*m.delivered;countm.delivered;}}returncount0?(sum/count).toFixed(1):0;}加权平均计算avgDays是按包裹数加权的即(avgDays1 * delivered1 avgDays2 * delivered2 ...) / totalDelivered这样更准确。二、纯 ArkTS 柱状图实现没有使用第三方图表库完全用 ArkTS 原生组件绘制。2.1 月度趋势柱状图ForEach(this.monthlyData,(month:MonthlyStats){Column(){// 行标签月份 数量Row(){Text(month.month).width(70)Blank()Text(month.count件)}// 柱状条Row(){Column(){Row(){Column().width((month.count/8)*100%)// ← 比例宽度.height(16).backgroundColor($r(app.color.primary)).borderRadius({topLeft:8,bottomLeft:8})}.width(100%).backgroundColor(#FFF0F0F0)// 灰色背景条.borderRadius(8)}.layoutWeight(1)}// 辅助信息Text(已签收: month.delivered | 平均 month.avgDays天)}},(month:MonthlyStats)month.month)实现原理设定最大值8数据中最大包裹数作为 100% 基准每个柱的宽度 (count / 8) * 100%灰色背景条#FFF0F0F0总是 100% 宽蓝色前景条按比例填充用borderRadius({ topLeft: 8, bottomLeft: 8 })让柱状条左侧圆角2.2 快递公司分布图ForEach(this.companyStats,(stat:CompanyStats){Row(){// 颜色圆点 公司名Circle().width(12).height(12).fill(stat.color)Text(stat.name).width(80).margin({left:8})// 柱状条Column(){Row(){Column().width((stat.count/8)*100%).height(14).backgroundColor(stat.color)// ← 使用公司专属颜色.borderRadius(7)}.width(100%).backgroundColor(#FFF0F0F0).borderRadius(7)}.layoutWeight(1)// 数量标签Text(stat.count件).width(40).textAlign(TextAlign.End)}},(stat:CompanyStats)stat.name)与月度趋势的不同柱状条使用公司专属颜色而非统一蓝色左侧多了颜色圆点 公司名右侧多了数量文字标签柱状条高度 14vp稍细2.3 柱状图比例计算的风险.width((stat.count/8)*100%)如果最大值8是硬编码的当数据变化比如某月有 10 个包裹柱状条会超出 100%。更健壮的方式// 计算实际最大值getmaxCount():number{letmax1;// 避免除以 0for(letmofthis.monthlyData){if(m.countmax)maxm.count;}returnmax;}// 动态计算宽度.width((month.count/this.maxCount)*100%)但为了示例简洁当前版本使用了固定最大值。三、ProfilePage — 个人中心3.1 功能需求用户信息卡片头像 昵称 追踪数量设置项通知开关、常用快递公司、关于包裹统计运输中 / 已签收 / 异常 数量快捷操作反馈、评分、分享、同步3.2 用户信息卡片Column(){Circle().width(64).height(64).fill($r(app.color.primary))Text(快递追踪用户)Text(已追踪 8 个包裹).fontSize($r(app.float.small_font_size)).fontColor($r(app.color.text_hint))}.alignItems(HorizontalAlign.Center)使用圆形头像 居中布局简洁的用户信息展示。3.3 设置项列表Column(){// 通知设置带 Toggle 开关Row(){Text($r(app.string.notification_setting))Blank()Toggle({type:ToggleType.Switch,isOn:this.notifyEnabled}).onChange((value:boolean){this.notifyEnabledvalue;})}.height(52)Divider().width(100%)// 常用快递公司带跳转箭头Row(){Text($r(app.string.courier_company))Blank()Text(顺丰、圆通、中通 ...).fontColor($r(app.color.text_hint))Text().fontColor($r(app.color.text_hint))}.height(52)Divider()// 关于Row(){Text($r(app.string.about_app))Blank()Text(v1.0.0).fontColor($r(app.color.text_hint))}.height(52)}设置项设计模式每行固定高度 52vp保证点击区域足够大Divider分隔每一项右侧统一使用text_hint灰色显示辅助信息箭头暗示可点击跳转3.4 包裹统计卡片Column(){Text(包裹统计).fontWeight(FontWeight.Medium)Row(){Column(){Text(3).fontColor($r(app.color.primary))Text(运输中)}Column(){Text(5).fontColor($r(app.color.status_delivered))Text(已签收)}Column(){Text(1).fontColor($r(app.color.status_exception))Text(异常)}}}与首页状态颜色保持一致运输中蓝色、已签收绿色、异常红色。三个统计值之和为 3519与首页数据对应。3.5 快捷操作区Row(){Column(){Text();Text(问题反馈)}Column(){Text(⭐);Text(评分)}Column(){Text();Text(分享)}Column(){Text();Text(同步)}}四宫格布局设计使用layoutWeight(1)均匀分布Emoji 图标 小字标签每个 Column 居中排列四列等宽视觉平衡四、颜色与主题体系总结4.1 全项目颜色映射资源名色值用途primary#FF4A90D9主题蓝标题、按钮、选中态background#FFF5F5F5页面背景card_bg#FFFFFF卡片背景text_primary#FF333333主文字text_secondary#FF666666次要文字text_hint#FF999999提示文字divider#FFE0E0E0分割线status_transit#FFFF8C00运输中橙色status_delivered#FF4CAF50已签收绿色status_exception#FFF44336异常红色rating_star#FFFFC107星标黄4.2 字号体系资源名值用途page_title_font_size22fp页面标题body_font_size16fp正文/列表标题small_font_size13fp次要信息badge_font_size11fp状态标签4.3 间距体系资源名值用途padding_small8vp小间距padding_medium16vp卡片内边距padding_large24vp大间距五、全项目架构总结5.1 页面关系图Index (首页) ├── → AddPackagePage (添加包裹) ├── → TrackDetailPage (物流详情带参数) ├── → SearchPage (搜索页) ├── → PackageStatsPage (统计页) └── → CompanyManagePage (公司管理) ProfilePage (个人中心) ├── 通知设置 (Toggle) ├── 常用公司 → CompanyManagePage └── 快捷操作 SearchPage (搜索) ├── TextInput 筛选标签 └── → TrackDetailPage (搜索结果点击) HistoryPage (历史记录) └── 清空 底部统计5.2 技术栈一览技术维度使用方案开发模型Stage 模型UI 语言ArkTSArkUI 声明式语法状态管理State 装饰器路由ohos.routerAPI 23资源管理$r() 引用 string/color/float构建工具Hvigor列表渲染List ForEach ListItem生命周期aboutToAppear / build5.3 关键设计决策纯原生组件零第三方依赖所有 UI 均使用 ArkUI 内置组件未引入 OHPM 第三方包资源集中管理颜色、字号、间距统一在element/JSON 中定义通过$r()引用TypeScript 严格模式适配对象字面量必须有显式类型数组必须可推断防御性编程路由参数判空、默认值兜底模拟数据驱动所有数据使用内联 mock 数据便于后续接入后端 API5.4 可扩展方向数据持久化当前数据在内存中关闭应用即丢失。可集成ohos.data.preferences或ohos.data.relationalStore实现持久化网络请求接入真实快递查询 API使用ohos.net.http发起 HTTP 请求推送通知集成通知服务包裹状态变化时推送多端适配当前仅适配 phone可扩展支持 tablet、wearable动画优化添加列表项入场动画、状态切换过渡动画六、写在最后6.1 本系列回顾五篇文章完整记录了一个鸿蒙原生应用从零到一的开发过程篇目核心内容技术要点第一篇项目初始化与工程架构Stage 模型、module.json5、路由注册第二篇首页与列表开发List ForEach、State、空状态第三篇表单交互与搜索筛选表单验证、多条件搜索、Toggle 开关第四篇物流时间线与历史记录时间线 UI、router 传参、Line 组件第五篇数据统计与个人中心柱状图、统计计算、设置面板6.2 开发心得ArkTS 声明式语法非常直观。写过 React/Vue 的开发者上手极快State 数据驱动 UI 的模式与前端框架一脉相承。鸿蒙的资源管理值得学习。通过$r()引用资源配合 JSON 配置文件实现了设计 token 的集中管理。修改一个全局颜色只需改一处。List 组件性能优秀。相比 Scroll ColumnList 的虚拟化机制在大列表场景下优势明显。API 版本的兼容性需要注意。不同 API 版本的模块导入路径不同如ohos.routervskit.AbilityKit开发前要确认目标 API 版本。严格模式是双刃剑。arkts-no-untyped-obj-literals等规则增加了代码量但提升了运行时稳定性值得接受。6.3 下一步鸿蒙生态正在快速发展API 版本持续迭代开发工具日趋完善。建议读者关注以下方向ArkTS 的新语法特性如Prop、Link双向绑定鸿蒙元服务Atomic Service开发分布式能力跨设备流转、数据同步鸿蒙原生三方库生态的建设进展项目源码快递追踪 AppPackageTracker开发环境DevEco Studio 6.x HarmonyOS API 23/24系列索引第一篇项目初始化与工程架构第二篇首页与列表开发实战第三篇表单交互与搜索筛选第四篇物流时间线与历史记录第五篇数据统计与个人中心本文·终篇感谢阅读欢迎在评论区交流鸿蒙开发经验。