学习型应用的详情页不能只是一块更大的文本卡片。用户从“圆轨道”进入详情后,理想路径应该是:先理解概念,再看到适用公式,最后带着明确实验场景进入模拟页验证。如果详情页不知道当前知识点的稳定 ID,也不知道关联哪个公式和实验,那么“去做相关模拟”只能跳到默认场景,知识与实践之间仍然断开。

“天体运行模拟”的真实源码已经搭起基础链路:KnowledgeListPage.ets 内置 10 个知识点,点击时把标题、分类、摘要和正文通过 ArkUI router.pushUrl() 传给 KnowledgeDetailPage.ets;详情页在 aboutToAppear() 回读参数,渲染核心概念、学习要点、学习建议三张卡片,并提供一个进入 ExperimentSimPage 的按钮。

本文复核这条真实路由和 ArkUI 页面结构,并给出从“文本透传”升级到“知识点 ID 驱动”的设计,让概念、公式和实验共享关联元数据。当前源码没有公式入口,模拟按钮也没有携带 expId,因此它进入的是模拟页默认场景;文章不会把建议关联能力伪装成已实现功能。

知识点详情文章封面

唯一复核标记:KNOWLEDGE-ONE13-CONCEPT-FORMULA-SIM-20260726:路由只传知识点 ID,详情模型集中声明公式和实验关联,模拟入口必须携带明确场景参数。

验证基线与真实入口

本文面向 HarmonyOS 5.0 及以上版本。实际工程应用版本为 1.0.0targetSdkVersion6.0.2(22)compatibleSdkVersion6.0.1(21),入口模块支持 phonetablet2in1

知识点目录包含 10 条:

  • 基础认知 3 条:引力是什么、速度决定轨道、质量与半径。
  • 轨道探索 3 条:圆轨道、椭圆轨道、逃逸轨道。
  • 多体系统 2 条:三体问题、引力扰动。
  • 高级实验 2 条:黑洞吞噬、星系碰撞。

详情页接收 4 个可选字符串参数,渲染 3 张信息卡,提供 1 个模拟按钮。当前公式跳转数量为 0;模拟按钮传递实验 ID 数量为 0;详情页稳定知识点 ID 数量为 0。

一、当前列表到详情的路由契约

列表点击时执行:

router.pushUrl({
  url: 'views/learning/KnowledgeDetailPage',
  params: {
    title: kp.title,
    category: kp.category,
    summary: kp.summary,
    content: kp.content
  }
})

详情页定义:

interface KnowledgeDetailParams {
  title?: string
  category?: string
  summary?: string
  content?: string
}

这个方案实现快、页面独立,即使详情页没有访问知识目录,也能显示完整内容。它适合首版静态应用。

问题是路由承担了数据存储职责。摘要或正文越长,路由对象越大;列表和详情分别维护默认值,也可能出现同一知识点两份文本不一致。

二、aboutToAppear() 负责接收参数

源码:

aboutToAppear(): void {
  const params =
    router.getParams() as
      KnowledgeDetailParams | undefined

  if (params?.title) {
    this.title = params.title
  }
  if (params?.category) {
    this.category = params.category
  }
  if (params?.summary) {
    this.summary = params.summary
  }
  if (params?.content) {
    this.content = params.content
  }
}

四个字段都有页面默认值,所以没有参数时不会白屏。这是一种安全回退。

if (params?.content) 会忽略空字符串。如果业务允许内容为空,应该用 params?.content !== undefined 区分“未传”和“传入空值”。

三、页面状态为什么全部是字符串

详情页当前有:

@State title: string = '知识点'
@State category: string = '基础认知'
@State summary: string =
  '通过分类知识点,快速理解对应天体概念。'
@State content: string =
  '结合模拟现象、轨道图像和引力规律进行理解,' +
  '可以让知识点更容易记住。'

路由回读后更新 @State,ArkUI 自动重建相关 Text。对于单次加载,这是可用的。

当模型扩展公式、实验、插图和学习进度时,散落状态会越来越多。更稳的方式是维护一个可空详情对象,并显式处理加载成功与未找到状态。

四、三张信息卡形成学习节奏

详情页依次调用:

this.InfoCard('核心概念', this.summary)
this.InfoCard('学习要点', this.content)
this.InfoCard(
  '学习建议',
  '先观察模拟现象,再对应引力公式或轨道规律进行解释。' +
  '调参时重点关注质量、速度、距离和轨迹变化。'
)

结构对应:

  1. 这是什么。
  2. 在模拟中看什么。
  3. 应该如何学习。

InfoCard 使用 @Builder 复用标题和正文布局,避免复制三套样式。计算和路由逻辑没有放进 Builder,保持了声明式 UI。

五、@Builder 适合纯展示组件

源码:

@Builder
InfoCard(title: string, body: string) {
  Column() {
    Text(title)
    Text(body)
      .lineHeight(22)
      .margin({ top: 8 })
  }
  .width('100%')
  .padding(16)
}

这个 Builder 没有状态写入、异步操作或复杂分支,非常适合复用。若卡片未来需要展开、复制或跳转,可以提取成 @Component,让交互状态由组件自己管理。

六、当前“相关模拟”其实是默认模拟

按钮只执行:

router.pushUrl({
  url: 'views/experiment/ExperimentSimPage'
})

模拟页没有收到 expIdexpName,会使用自己的默认值:

@State expId: string = 'stable_orbit'
@State title: string = '稳定双体系统'

因此,无论用户阅读“三体问题”还是“黑洞吞噬”,都进入稳定双体默认场景。按钮文案“相关模拟”与实际关联并不完全一致。

诚实的修复有两种:

  • 暂时改成“进入模拟器”,承认它是通用入口。
  • 建立知识点到实验 ID 的映射,真正实现相关跳转。

七、知识点 ID 应成为详情主键

列表中的每个知识点已经有稳定 ID:

basic_1
basic_2
basic_3
orbit_1
orbit_2
orbit_3
multi_1
multi_2
extreme_1
extreme_2

详情路由只需传:

interface KnowledgeDetailParams {
  knowledgeId?: KnowledgePointId
}
router.pushUrl({
  url: 'views/learning/KnowledgeDetailPage',
  params: {
    knowledgeId: kp.id
  }
})

详情页再从统一目录查找。标题或正文修改后,不必更新路由调用方。

八、统一知识目录避免页面重复定义

推荐:

export interface KnowledgePointDetail {
  id: KnowledgePointId
  title: string
  category: KnowledgeCategory
  summary: string
  content: string
  icon: Resource
  formulaIds: FormulaId[]
  experimentIds: ExperimentId[]
}

目录放在模型层:

export const KNOWLEDGE_POINTS:
  readonly KnowledgePointDetail[] = [
  // 10 real entries
]

列表页按分类过滤,详情页按 ID 查找,搜索和收藏也可以共享同一份内容。

九、分类字符串也要收紧

当前分类是普通字符串。可以定义:

export type KnowledgeCategory =
  | '基础认知'
  | '轨道探索'
  | '多体系统'
  | '高级实验'

这样目录中的拼写错误会在编译期暴露。列表页的未知分类回退逻辑也可以显式处理,而不是依赖任意字符串。

十、知识点与公式的关联方式

当前公式页包含万有引力、圆轨道速度、逃逸速度、动能、引力势能、质心和天文单位。知识点可以关联公式 ID:

type FormulaId =
  | 'universal_gravity'
  | 'circular_orbit_speed'
  | 'escape_speed'
  | 'kinetic_energy'
  | 'gravity_potential'
  | 'center_of_mass'
  | 'astronomical_unit'

例如:

{
  id: 'orbit_1',
  title: '圆轨道',
  formulaIds: [
    'universal_gravity',
    'circular_orbit_speed'
  ]
}

详情页可以显示“相关公式”区域,点击跳到公式详情或带筛选参数的公式页。

十一、知识点与实验的关联方式

实验目录已有:

stable_orbit
elliptic_escape
three_body
binary_star
black_hole
galaxy_collision

可建立明确映射:

知识点 推荐实验
引力是什么 stable_orbit
速度决定轨道 elliptic_escape
圆轨道 stable_orbit
椭圆轨道 elliptic_escape
逃逸轨道 elliptic_escape
三体问题 three_body
引力扰动 three_body
黑洞吞噬 black_hole
星系碰撞 galaxy_collision

“质量与半径”可进入自由宇宙或稳定双体,具体由产品定义。

知识点连接概念、公式和模拟的流程

十二、模拟跳转必须携带场景参数

真实关联后:

private openExperiment(
  experimentId: ExperimentId,
  experimentName: string
): void {
  router.pushUrl({
    url: 'views/experiment/ExperimentSimPage',
    params: {
      expId: experimentId,
      expName: experimentName
    }
  })
}

按钮文案也应具体:

用“三体扰动实验”验证

这比统一的“去做相关模拟”更可预测。若有多个实验,可以显示列表或默认主实验加更多入口。

十三、公式跳转也要传稳定 ID

不要把公式字符串通过路由透传。公式显示和单位精度可能变化,路由只传:

router.pushUrl({
  url: 'views/learning/FormulaPage',
  params: {
    formulaId: 'circular_orbit_speed'
  }
})

公式页加载后滚动或高亮对应项。若当前公式页尚不支持定位,可先跳到筛选分类,但按钮文案应与能力一致。

十四、处理知识点不存在状态

通过 ID 查找可能失败:

const point = KNOWLEDGE_POINTS
  .find((item: KnowledgePointDetail) =>
    item.id === params?.knowledgeId
  )

不要默默展示默认“知识点”,否则用户不知道链接失效。页面状态可以是:

type DetailState =
  | 'loading'
  | 'content'
  | 'notFound'

notFound 提供返回按钮和“回到知识列表”,不显示伪内容。

十五、路由参数要在页面重建后仍可恢复

传 ID 的优势是页面重建时可以重新加载;传完整内容则依赖路由对象仍然存在。若未来知识目录来自本地数据库或资源文件,ID 仍然是稳定契约。

对于纯静态目录,加载是同步的;对于异步仓库,页面需要 loading 和错误状态,但路由协议不变。

十六、学习进度应绑定知识点 ID

如果未来记录“已读”“收藏”“完成模拟”,应保存:

interface KnowledgeProgress {
  knowledgeId: KnowledgePointId
  openedAt: number
  completedAt?: number
  relatedExperimentDone: boolean
}

不能用标题做键,因为标题可能改文案。稳定 ID 还能支持内容版本迁移。

十七、内容版本帮助处理更新

建议知识点增加:

contentVersion: number

当正文或关联实验发生重大变化时,应用可决定:

  • 保留已读状态。
  • 重新标记“内容已更新”。
  • 不清除用户收藏。
  • 重新计算学习完成度。

当前源码没有学习进度和版本字段,这属于后续演进。

十八、内容与页面布局应分离

详情页现在只支持文本卡片。模型可以继续扩展:

type KnowledgeBlock =
  | { type: 'paragraph'; text: string }
  | { type: 'formula'; formulaId: FormulaId }
  | { type: 'tip'; text: string }
  | { type: 'experiment'; experimentId: ExperimentId }

这比直接保存 Markdown 或 HTML 更容易保持原生 ArkUI、一致主题和离线安全。页面按 block 类型选择组件,内容层不包含 UI 样式。

十九、Scroll 保证长内容可达

源码把正文放在纵向 Scroll

Scroll() {
  Column({ space: 16 }) {
    // cards and button
  }
}
.scrollable(ScrollDirection.Vertical)
.layoutWeight(1)

长标题在顶部使用单行省略,正文卡片没有固定高度,能够自然扩展。按钮下方还有 20vp margin。

需要实测系统字体放大后按钮和最后一张卡是否仍高于底部导航安全区。

二十、多设备详情布局

手机使用单列卡片是合理默认。平板与 2in1 可以:

  • 左侧显示概念与公式。
  • 右侧显示相关实验和学习建议。
  • 保持正文列最大宽度。
  • 支持鼠标和键盘聚焦。

不要把三个卡片横向拉成三列导致正文行宽过短。内容页应优先保障连续阅读。

知识点详情的四层职责

二十一、返回与深链路

顶部返回使用:

router.back()

从列表正常进入时没有问题。若未来支持外部深链直接打开详情,返回栈可能为空,需要提供回到学习首页的回退策略。

模拟结束后的返回路径也要设计:用户是回详情继续学习,还是回实验列表。可以在模拟路由携带 sourceKnowledgeId,但不要让模拟页依赖整个知识对象。

二十二、无障碍与触控

当前返回符号和按钮都是可点击元素。后续应验证:

  • 返回符号有明确无障碍说明。
  • 分类标签被朗读为分类。
  • 三张卡片按视觉顺序聚焦。
  • 相关公式和实验按钮朗读具体目标。
  • 64% 宽按钮在手机和大字体下文字不截断。

按钮文案具体化还能直接改善无障碍体验。

二十三、关键测试矩阵

场景 当前或增强后预期
不传参数进入详情 当前显示默认内容
从列表进入 标题、分类、摘要、正文一致
长标题 顶栏单行省略
点击返回 回到上一页
点击模拟 当前进入默认稳定双体
ID 模型增强后 详情按知识点 ID 加载
圆轨道详情 关联圆轨道速度与稳定双体
三体问题详情 关联质心或引力内容与三体实验
黑洞吞噬详情 进入 black_hole
未知 ID 显示 notFound,不伪造默认详情
大字体 卡片和按钮不重叠
平板/2in1 保持合适正文行宽

二十四、启动时关联校验

function validateKnowledgeLinks(
  points: readonly KnowledgePointDetail[],
  formulaIds: ReadonlySet<string>,
  experimentIds: ReadonlySet<string>
): string[] {
  const errors: string[] = []

  points.forEach((point: KnowledgePointDetail) => {
    point.formulaIds.forEach((id: FormulaId) => {
      if (!formulaIds.has(id)) {
        errors.push(
          `${point.id}: unknown formula ${id}`
        )
      }
    })

    point.experimentIds.forEach(
      (id: ExperimentId) => {
        if (!experimentIds.has(id)) {
          errors.push(
            `${point.id}: unknown experiment ${id}`
          )
        }
      }
    )
  })

  return errors
}

这样公式或实验改 ID 时,知识目录不会留下静默失效链接。

二十五、发布前检查

  • 详情路由优先传稳定知识点 ID。
  • 列表与详情读取同一知识目录。
  • 分类由联合类型约束。
  • 相关公式使用公式 ID。
  • 相关实验使用真实实验 ID。
  • 模拟按钮文案与实际目标一致。
  • 未知 ID 有 notFound 状态。
  • 默认内容不掩盖路由错误。
  • 内容版本与学习进度分离。
  • 长文、长标题和大字体可滚动。
  • 手机、平板、2in1 保持阅读行宽。
  • 返回和深链路都有明确目的地。

二十六、总结

现有 KnowledgeDetailPage.ets 已完成一条可复核的基础链路:知识列表透传四个文本参数,详情页在 aboutToAppear() 接收并更新状态,三个 InfoCard 组织概念、要点和建议,纵向 Scroll 保证内容可达,按钮进入模拟页。

要真正连接概念、公式和模拟,关键是把路由从“传完整文案”收紧为“传稳定 ID”,再由统一知识目录声明公式 ID 和实验 ID。这样“三体问题”才能进入三体实验,“圆轨道”才能定位圆轨道速度公式,页面文案变化也不会破坏路由。知识详情不再是孤立文本页,而会成为学习闭环的调度中心。

说明:本文基于真实 HarmonyOS/ArkTS 源码进行整理,部分文字与示例由 AI 辅助生成;所有现有能力与建议改造已明确区分。

Logo

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

更多推荐