HarmonyOS旅行探索——美食详情页与参数传递的两种模式
美食详情页是从美食指南列表点进去的——用户在 CuisinePage 里点击某道美食,跳转到这个页面查看详细信息。从代码看,这个页面的布局很直观:顶部导航栏、渐变 Hero 区域(大号 emoji)、信息卡片(名称 + 城市 + 描述 + 价格)、品尝建议。但更有意思的是它的参数传递方式——和城市详情页完全不同。
完整效果


参数传递:全字段传递 vs ID 查询
城市详情页的跳转方式是传 ID:
// CuisinePage → CityDetail
router.pushUrl({
url: 'pages/CityDetail',
params: { 'cityId': city.id } as Record<string, Object>
})
美食详情页的跳转方式是传全字段:
// CuisinePage → FoodDetail
router.pushUrl({
url: 'pages/FoodDetail',
params: {
'name': f.name, 'emoji': f.emoji,
'city': f.city, 'desc': f.desc, 'price': f.price
} as Record<string, Object>
})
两种模式的区别:
| 维度 | CityDetail(ID 查询) | FoodDetail(全字段) |
|---|---|---|
| 路由参数 | 只传 cityId | 传 name/emoji/city/desc/price |
| 数据来源 | 详情页从 CITIES 数组查找 | 详情页直接用路由参数 |
| 额外查询 | 需要(aboutToAppear 里遍历数组) | 不需要 |
| 数据一致性 | 强(始终从数据源读取) | 弱(依赖传参时的数据快照) |
城市详情页用 ID 查询是因为城市数据量大(名称、国家、描述、景点数组、美食数组),全部通过路由传递不现实,而且城市数据可能被其他页面引用,从统一数据源读取更可靠。
美食详情页用全字段传递是因为美食数据量小(只有五个字段),而且美食只在 CuisinePage 里出现一次,不存在跨页面引用的问题。直接传字段比"传 ID → 查数组"少了一步查找,代码更简单。
参数接收
@State name: string = ''
@State emoji: string = ''
@State city: string = ''
@State desc: string = ''
@State price: string = ''
aboutToAppear(): void {
const p = router.getParams() as Record<string, Object>
if (p) {
if (p['name']) this.name = p['name'] as string
if (p['emoji']) this.emoji = p['emoji'] as string
if (p['city']) this.city = p['city'] as string
if (p['desc']) this.desc = p['desc'] as string
if (p['price']) this.price = p['price'] as string
}
}

五个 @State 变量分别接收五个路由参数。每个参数都做了 if (p['xxx']) 的存在性检查——如果调用方忘了传某个参数,对应变量保持空字符串,不会崩溃。
和城市详情页的 for 循环查找不同,这里只是简单的 if 判断 + 类型断言。代码量更多(五行 vs 三行),但逻辑更直白。
Hero 区域
Stack({ alignContent: Alignment.Center }) {
Column() {}.width('100%').height(200).borderRadius(20)
.linearGradient({ angle: 135, colors: [[A, 0], ['#FF9F43', 1]] })
Text(this.emoji).fontSize(80)
}
.width('100%').height(200).borderRadius(20)
.margin({ left: 16, right: 16, bottom: 16 })
和城市详情页的 Hero 结构完全一样——Stack 叠加渐变背景和 emoji。区别是城市的 Hero 有 180vp 高,美食的 Hero 有 200vp 高,美食的 emoji 也更大(80px vs 64px)。
美食详情页的 Hero 没有文字标题——emoji 就是全部的视觉内容。城市的 Hero 需要显示城市名、国家、描述、评分等信息,所以需要更多空间。美食详情页把这些信息放在了下方的 Info 卡片里。
信息卡片
Column() {
Text(this.name).fontSize(24).fontWeight(FontWeight.Bold).fontColor(T1)
.margin({ bottom: 8 })
Text('📍 ' + this.city).fontSize(14).fontColor(A).fontWeight(FontWeight.Medium)
.margin({ bottom: 12 })
Text(this.desc).fontSize(15).fontColor(T1).lineHeight(24)
.margin({ bottom: 16 })
Divider().strokeWidth(0.5).color('#F0EEF4').margin({ bottom: 14 })
Text('参考价格').fontSize(13).fontWeight(FontWeight.Bold).fontColor(T1)
.margin({ bottom: 4 })
Text(this.price).fontSize(20).fontWeight(FontWeight.Bold).fontColor(A)
}

信息卡片分上下两部分,用 Divider 分隔:
上半部分:美食名称(大号黑色)、城市标签(橙色带定位图标)、描述文字(15px 行高 24px,保证长文本的可读性)。
下半部分:"参考价格"标签 + 价格数值(20px 橙色粗体)。价格和标签之间留了 4px 的间距,比其他元素的间距小,因为它们在语义上是紧密关联的。
lineHeight 的作用
Text(this.desc).fontSize(15).lineHeight(24) 设置了 24px 的行高。如果描述文字很长需要换行,24px 的行高比默认行高(大约 18-20px)更宽松,阅读起来更舒适。
在移动端 App 里,正文的行高通常是字号的 1.5-1.8 倍。这里 15 × 1.6 = 24,正好是 1.6 倍,是一个比较标准的正文行高。
品尝建议
Column() {
Text('品尝建议').fontSize(16).fontWeight(FontWeight.Bold).fontColor(T1)
.width('100%').margin({ bottom: 8 })
Text('📍 推荐在当地市场或老店品尝,味道最为正宗')
.fontSize(13).fontColor(T2).lineHeight(20).margin({ bottom: 8 })
Text('🍽 可作为正餐或小吃,根据个人口味选择配料')
.fontSize(13).fontColor(T2).lineHeight(20).margin({ bottom: 8 })
Text('📸 别忘了拍照记录,分享你的美食体验')
.fontSize(13).fontColor(T2).lineHeight(20)
}
品尝建议是硬编码的通用建议——每道美食看到的都是同样的三条。这在原型阶段是合理的,真正的 App 会根据每道美食的特点给出个性化建议(比如寿司建议蘸酱油,拉面建议先喝汤)。
每条建议前面加了 emoji 前缀(📍🍽📸),和美食列表的城市标签风格保持一致。fontSize(13) + lineHeight(20) 的组合比信息卡片的描述文字更紧凑(信息卡片是 fontSize(15) + lineHeight(24)),因为建议文字是辅助信息,不需要那么突出。
导航栏右侧的空操作
Text('❤️').fontSize(20)
右上角的爱心图标目前没有绑定任何 onClick 事件。和运动健康 App 里的"编辑"按钮、“保存"按钮一样,这是一个 UI 占位——告诉用户"这里以后可以收藏这道美食”,但实际功能还没实现。
如果以后要实现收藏功能,需要:
- 点击时把美食信息存入
UserDataService - 已收藏时爱心变红色(
fontColor('#FF4757')) - 再次点击取消收藏
但在当前版本里,这个交互还没有接入。
两种参数传递模式的选择标准
什么时候用 ID 查询,什么时候用全字段传递?可以总结一个简单的判断标准:
用 ID 查询:
- 数据量大(超过 5 个字段)
- 数据会被多个页面引用
- 数据可能被更新(需要从统一数据源读取最新值)
用全字段传递:
- 数据量小(5 个字段以内)
- 数据只在当前场景使用
- 数据是静态的(不会被其他页面修改)
美食详情页完美符合"全字段传递"的条件——五个字段、只在 CuisinePage 里跳转、数据是写死的 mock。城市详情页符合"ID 查询"的条件——字段多、景点和美食数组很长、城市数据被首页和美食页共同引用。
选择哪种模式不是技术偏好的问题,而是数据特征决定的。
更多推荐

所有评论(0)