一张 skecth-notes 风格的框架型信息图,主题是 HarmonyOS7 账号安全页的信息层

前言

订单状态时间线看起来只是几条记录,实际承担的是用户对订单进度的信任感。我做这类页面时,会先盯住三个问题:当前走到哪一步、有没有异常、用户下一步能做什么

这篇单独聊 物流详情 这个场景。重点不是堆 API,而是把订单节点、异常提示和刷新反馈放进一个真实页面里,看 ArkUI 布局怎么和 ArkTS 状态配合。

为什么这个问题经常被写乱

订单状态时间线 这类内容很容易被写成“代码能跑就算讲完了”,但对初学者来说,这恰恰是最不够的地方。真正让人卡住的,往往不是某个组件名记不住,而是不知道这段代码为什么要这样拆、状态为什么要这样放、以后需求变化时应该从哪里改。

所以这篇文章不只想给你一个能跑的例子,更想把背后的判断过程讲清楚。你只要把这个判断过程吃透,后面自己改页面、补需求、查问题时,心里会稳很多。

我会这样落到真实订单页

订单时间线不要只显示两行文案,真实业务里至少要处理 当前节点、异常节点、客服电话入口、刷新状态。我的做法是先把状态定义清楚,再让 UI 根据状态决定颜色和操作。

实操步骤
  1. 先和后端约定节点字段:statustimeoperatorremark,不要让前端从一段描述里拆信息。
  2. 把节点状态收敛成 done/current/warn/wait 四类,UI 不直接认识后端枚举。
  3. 当前节点放在第一屏可见位置,异常节点给出可点击的处理建议。
  4. 刷新按钮只改加载态和节点列表,不要顺手改订单基础信息。
节点状态 视觉处理 用户动作
done 深色标题、浅色说明 只读
current 品牌色圆点、展开备注 查看详情
warn 红色提示、显示处理按钮 联系客服
wait 灰色弱化 无操作

时间线页面最怕“所有节点长得一样”。用户真正关心的是:现在到哪了、哪里卡住了、下一步要做什么。

interface OrderStep {
  id: number
  title: string
  time: string
  remark: string
  state: string
}

@Entry
@Component
struct OrderTimelinePage {
  @State refreshing: boolean = false
  @State steps: OrderStep[] = [
    { id: 1, title: '已签收', time: '07-11 16:20', remark: '本人签收,服务已完成', state: 'current' },
    { id: 2, title: '派送中', time: '07-11 09:15', remark: '骑手正在前往收货地址', state: 'done' },
    { id: 3, title: '运输异常', time: '07-10 21:40', remark: '中转站延迟,已重新分拣', state: 'warn' },
    { id: 4, title: '商家已发货', time: '07-10 18:05', remark: '包裹离开发货仓', state: 'done' }
  ]

  private stepColor(state: string): string {
    if (state === 'current') {
      return '#0A59F7'
    }
    if (state === 'warn') {
      return '#D92D20'
    }
    return '#8A8F99'
  }

  build() {
    Column({ space: 14 }) {
      Row() {
        Text('订单跟踪').fontSize(22).fontWeight(FontWeight.Bold)
        Blank()
        Button(this.refreshing ? '刷新中' : '刷新')
          .enabled(!this.refreshing)
          .onClick(() => {
            this.refreshing = true
            this.steps[0].remark = '刚刚同步,物流状态无变化'
            this.refreshing = false
          })
      }.width('100%')

      List({ space: 0 }) {
        ForEach(this.steps, (step: OrderStep, index: number) => {
          ListItem() {
            Row({ space: 12 }) {
              Column() {
                Circle({ width: 12, height: 12 }).fill(this.stepColor(step.state))
                if (index < this.steps.length - 1) {
                  Divider().vertical(true).height(58).color('#DADDE3')
                }
              }.width(20)

              Column({ space: 6 }) {
                Text(step.title).fontSize(16).fontWeight(step.state === 'current' ? FontWeight.Bold : FontWeight.Medium)
                Text(step.time).fontSize(12).fontColor('#8A8F99')
                Text(step.remark).fontSize(13).fontColor(this.stepColor(step.state))
                if (step.state === 'warn') {
                  Button('联系客服处理').height(30).fontSize(12)
                }
              }.alignItems(HorizontalAlign.Start).layoutWeight(1)
            }.padding({ top: 10, bottom: 10 })
          }
        }, (step: OrderStep) => step.id.toString())
      }.width('100%')
    }.padding(16).backgroundColor('#F6F7FB').height('100%')
  }
}
把关键代码一段段拆开
  • OrderStep.state 是前端视图状态,避免 UI 直接绑定复杂后端码值。
  • stepColor() 把颜色规则集中起来,后面改主题时不用翻每个 Text
  • ForEach 的 key 使用 id,物流节点刷新或插入时不容易出现状态复用问题。
  • 刷新按钮用 refreshing 控制禁用态,能挡住连续点击造成的重复请求。
时间线真正要讲清楚什么

订单时间线不是把节点倒序排出来就结束了。用户打开这个页面,是想确认订单有没有正常推进,所以当前节点要最醒目,异常节点要给出解释和下一步动作,已经完成的节点只需要稳定记录。

示例里的 state 是一个前端视图状态,它把后端各种物流码收敛成页面能理解的几类。这样 UI 不需要到处判断复杂码值,只要根据 currentwarndone 去决定颜色、按钮和说明文案。

写在最后

如果后续接真实接口,刷新时建议整体替换 steps 数组,而不是只修改数组里某个对象字段。时间线经常会插入新节点或调整顺序,稳定的业务 id 和明确的数组更新,会让页面刷新更可靠。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐