HarmonyOS购物商城——个人中心数据驱动与路由分化
14个可点击区域13个都跳到MessagePage。用户点了收藏夹跳到MessagePage点了设置也跳到MessagePage点了待付款还是跳到MessagePage。所有入口殊途同归等于没有导航。这次优化的核心是两件事路由分化和参数驱动。每个菜单项跳转到真正有意义的目标页面目标页面通过路由参数接收上下文信息动态渲染内容。完整效果路由分化从统一占位到各走各路上一版的路由表菜单项跳转目标收藏夹MessagePage浏览记录MessagePage地址管理MessagePage优惠券MessagePage客服中心ServiceChat消息中心MessagePage设置MessagePage关于MessagePage8个入口7个跳MessagePage1个跳ServiceChat。MessagePage是个万能垃圾桶——不知道跳哪里就往里塞。新版的路由表菜单项跳转目标传入参数用户卡片ProfileEdit无全部 ›MessageDetailtitle我的订单, icon待付款MessageDetailtitle待付款, icon待发货MessageDetailtitle待发货, icon待收货MessageDetailtitle待收货, icon待评价MessageDetailtitle待评价, icon⭐售后MessageDetailtitle售后, icon收藏夹ProductList无浏览全部商品浏览记录ProductList无浏览全部商品地址管理MessageDetailtitle地址管理, sub管理您的收货地址优惠券MessageDetailtitle优惠券, sub暂无可用优惠券客服中心ServiceChat无消息中心MessagePage无设置MessageDetailtitle设置, sub账号安全·通用设置·隐私关于MessageDetailtitle关于购物商城, sub版本1.0.014个入口跳转到4个不同的目标ProfileEdit、MessageDetail、ProductList、ServiceChat。每个目标页面都有明确的职责。路由分化的逻辑分化不是随意的而是基于每个菜单项的实际功能用户卡片 → ProfileEdit用户想编辑个人信息跳转到专门的编辑页面。这是唯一一个跳转到真实功能页面的入口和ServiceChat并列说明ProfileEdit页面已经开发完成。收藏夹/浏览记录 → ProductList这两个功能的本质是浏览商品跳转到商品列表页是合理的。用户看到收藏的商品或浏览历史下一步很可能是再看看有没有想买的。ProductList已经支持接收搜索参数未来可以扩展为接收收藏列表或浏览历史的参数。订单相关 → MessageDetail五个订单状态待付款/待发货/待收货/待评价/售后和全部 ›“都跳转到MessageDetail。因为订单列表页面还没开发MessageDetail作为占位页面通过参数显示不同的标题和图标让用户感知到这些入口是不同的”。地址管理/优惠券/设置/关于 → MessageDetail同理这些页面还没开发用MessageDetail做占位。但每个入口传入不同的参数显示不同的内容。客服中心 → ServiceChat唯一跳转到真实功能页面的菜单项。消息中心 → MessagePage消息中心本身就是MessagePage跳转到自己——这个逻辑有点奇怪但因为MessagePage已经支持消息列表展示直接跳过去也能用。MessageDetail通用详情页的参数驱动数据模型State title: string State sub: string State time: string State icon: string State color: string #FF6B6B五个State变量对应路由传入的五个参数。页面创建时从路由参数中读取aboutToAppear(): void { const p router.getParams() as Recordstring, Object if (p) { if (p[title]) { this.title p[title] as string } if (p[sub]) { this.sub p[sub] as string } if (p[time]) { this.time p[time] as string } if (p[icon]) { this.icon p[icon] as string } if (p[color]) { this.color p[color] as string } } }每个参数都做了空值检查if (p[title])避免路由参数缺失时页面崩溃。as string类型断言是因为router.getParams()返回的是Recordstring, Object需要手动转换类型。页面渲染MessageDetail的渲染逻辑完全由参数驱动// Header标题 路由传入的title Text(this.title).fontSize(18).fontWeight(FontWeight.Bold) // 图标背景色 路由传入的color 18透明度 Column() { Text(this.icon).fontSize(28) }.width(52).height(52).borderRadius(16).backgroundColor(this.color 18) // 内容 路由传入的sub Text(this.sub).fontSize(15).fontColor(T1).lineHeight(26)图标背景色的this.color 18是把颜色值和透明度拼接。比如#FF475718#FF475718这是一个带透明度的ARGB颜色值。18是十六进制的24约15%的不透明度——很淡的背景色让图标浮在上面但不抢视觉焦点。为什么选 MessageDetail 做通用页选择MessageDetail而不是创建多个独立页面SettingsPage、AboutPage、AddressPage等有三个原因1. 这些页面功能相似。地址管理、优惠券、设置、关于——它们本质上都是展示一段信息没有复杂的交互逻辑。用一个通用页面就能覆盖。2. 减少页面数量。鸿蒙的路由系统每增加一个页面就会增加一个编译产物。页面越多包体积越大编译时间越长。用一个通用页面替代四个独立页面能显著减小包体积。3. 参数驱动的灵活性。通过路由参数传入不同的title/sub/icon/color同一个页面能渲染出完全不同的视觉效果。如果以后需要给某个页面加特殊逻辑比如设置页需要开关按钮可以在MessageDetail里加条件判断而不是新建页面。OrderIcon 的参数化上一版的 OrderIconBuilder OrderIcon(label: string, icon: string) { Column({ space: 6 }) { Text(icon).fontSize(24) Text(label).fontSize(11).fontColor(T2) }.layoutWeight(1).alignItems(HorizontalAlign.Center) .onClick(() { router.pushUrl({ url: pages/MessagePage }) }) }两个参数图标和文字。点击统一跳MessagePage。新版的 OrderIconBuilder OrderIcon(label: string, icon: string) { Column({ space: 6 }) { Text(icon).fontSize(24) Text(label).fontSize(11).fontColor(T2) }.layoutWeight(1).alignItems(HorizontalAlign.Center) .onClick(() { router.pushUrl({ url: pages/MessageDetail, params: { title: label, sub: 功能开发中敬请期待, time: , icon: icon, color: #FF4757 } as Recordstring,Object }) }) }点击时传入的参数动态引用了Builder的参数label和icon。每个订单状态跳转到MessageDetail时标题和图标都是自己对应的值——“待付款跳过去标题就是待付款”图标就是。这比上一版的统一跳MessagePage好在哪用户体验上点击待付款看到的页面标题是待付款点击待收货看到的页面标题是待收货。虽然内容都是功能开发中但标题的差异化让用户觉得这些入口确实指向不同的地方。菜单项的路由策略每个菜单项的路由选择都有考量收藏夹/浏览记录 → ProductListthis.Menu(收藏夹, ❤️, () { router.pushUrl({ url: pages/ProductList, params: {} as Recordstring,Object }) })没有传参数跳转到ProductList显示全部商品。这是最简单的实现——收藏夹本质上就是一个商品列表ProductList已经能渲染商品列表了。未来可以扩展传入{ filter: favorites }参数让ProductList只显示收藏的商品。地址管理 → MessageDetailthis.Menu(地址管理, , () { router.pushUrl({ url: pages/MessageDetail, params: { title: 地址管理, sub: 管理您的收货地址, time: , icon: , color: #5DADE2 } as Recordstring,Object }) })传入了完整的五个参数。color: #5DADE2是蓝色系和图标搭配。每个菜单项的color都不一样——优惠券用黄色#FECA57设置用灰色#888888关于用紫色#8A5DFA。颜色和图标语义匹配让用户即使不看文字也能通过颜色感知这是什么功能。关于 → MessageDetailthis.Menu(关于, ℹ️, () { router.pushUrl({ url: pages/MessageDetail, params: { title: 关于购物商城, sub: 版本 1.0.0 · HarmonyOS NEXT · 让购物更简单, time: , icon: ℹ️, color: #8A5DFA } as Recordstring,Object }) })关于页面通常展示版本信息和应用介绍。这里把版本号1.0.0和平台信息HarmonyOS NEXT直接写在sub参数里跳转到MessageDetail就能看到。如果以后版本号变了需要改这个参数——更好的做法是从AppInfo API动态获取版本号但原型阶段硬编码够用。订单标题行的全部 ›Text(全部 ›).fontSize(12).fontColor(T3) .onClick(() { router.pushUrl({ url: pages/MessageDetail, params: { title: 我的订单, sub: 您还没有订单快去购物吧, time: , icon: , color: #FF4757 } as Recordstring,Object }) })“全部 ›“点击后跳转到MessageDetail标题是我的订单”副标题是您还没有订单快去购物吧”。这是一个空状态提示——用户还没下过订单看到这个提示就知道这里暂时没有内容。路由参数的类型安全router.getParams() 的类型问题const p router.getParams() as Recordstring, Objectrouter.getParams()的返回类型是object | undefined需要用as断言成Recordstring, Object才能通过类型检查。这是鸿蒙路由API的设计问题——返回类型太宽泛开发者需要手动断言。params 的类型断言params: { title: label, sub: 功能开发中, ... } as Recordstring,Object传入params时也需要as Recordstring,Object断言。ArkUI的路由API对params类型有严格要求直接传对象会有类型错误。这两个类型断言是鸿蒙路由开发中的标准动作——每次用路由都要写。建议封装一个路由工具函数把类型断言内部化function navigateTo(url: string, params: Recordstring, Object {}) { router.pushUrl({ url, params: params as Recordstring, Object }) }调用时只需要navigateTo(pages/MessageDetail, { title: xxx })不需要每次写as Recordstring,Object。踩坑记录MessageDetail的图标背景色拼接。this.color 18把颜色值和透明度拼接成ARGB格式。但这个拼接有个前提color必须是6位十六进制格式比如#FF4757。如果color是3位缩写格式比如#F47拼接后会变成#F4718不是合法的颜色值。确保传入的color参数都是6位格式。空值检查的必要性。aboutToAppear里的if (p[title])检查是防御性编程。如果路由参数缺失比如直接打开MessageDetail页面而不传参数title会是空字符串页面标题区域会显示空白。有了空值检查至少不会崩溃。更好的做法是给每个参数设默认值this.title (p[title] as string) || 消息详情。OrderIcon的onClick不能直接用Builder参数。onClick(() { router.pushUrl({ params: { title: label } }) })里的label是Builder的参数在onClick的箭头函数里能正常访问——因为箭头函数捕获了外层作用域的变量。但如果用普通函数而不是箭头函数this指向会变label可能访问不到。始终用箭头函数是安全的选择。路由参数的大小写。传入params时用的是小写键名title、sub读取时也用小写p[title]。如果大小写不一致比如传Title读title会拿到undefined。鸿蒙路由参数的键名是区分大小写的这是容易忽略的细节。