一、技术背景与鸿蒙生态全景

1.1 HarmonyOS 6.1.1 技术栈概览

HarmonyOS 6.1.1 作为华为全场景分布式操作系统的最新演进版本,在 ArkTS 语言运行时、ArkUI 声明式 UI 框架以及应用开发范式方面进行了深度优化。ArkTS 是在 TypeScript 基础上扩展而来的编程语言,它保留了 TypeScript 的静态类型检查能力,同时引入了针对鸿蒙声明式 UI 的语法增强,包括装饰器系统、状态管理模型、构建器函数等特性。

ArkTS 的核心设计哲学是"数据驱动视图"。开发者只需声明界面的结构与数据绑定关系,当数据发生变化时,框架会自动触发 UI 的增量更新,无需手动操作 DOM 或调用刷新接口。这一范式在 HarmonyOS API 24 中得到了进一步完善,渲染管线效率提升约 30%,状态更新批处理机制更加成熟。

在 HarmonyOS 6.1.1 中,ArkUI 组件库已经扩展到超过 80 个内置组件,涵盖基础布局(Column、Row、Stack、Flex、Grid)、容器组件(Scroll、List、Swiper)、交互组件(Button、TextInput、Slider、Toggle)、媒体组件(Image、Video、Text)以及高级组件(XComponent、Canvas 等)。这些组件通过统一的属性链式调用方式进行配置,形成了一种高度可读的声明式 DSL。

1.2 声明式 UI 范式的核心特征

鸿蒙的声明式 UI 范式与传统的命令式 UI 编程有着本质区别。在命令式范式下,开发者需要显式地创建控件、设置属性、添加到父容器、在数据变化时手动更新控件状态。而声明式范式下,开发者描述"界面应该是什么样子",框架负责将其映射到实际的渲染树,并在数据变化时自动完成差量更新。

ArkUI 声明式范式的技术基石包含三个层面。第一层是 @Entry@Component 装饰器,它们将一个 struct 结构体标记为可渲染的 UI 组件单元。第二层是状态管理装饰器体系,包括 @State@Prop@Link@Provide@Consume@Observed@ObjectLink 等,它们实现了组件间数据的响应式传递与同步。第三层是 @Builder@BuilderParam 装饰器,用于将可复用的 UI 片段封装为独立的方法,提升代码的可维护性与复用度。

1.3 ArkTS 语言关键特性

ArkTS 在 TypeScript 之上引入了若干重要的语言层面约束与增强。首先是禁用了 any 类型和动态类型推断,强制所有变量和参数必须有明确的静态类型声明。这一约束虽然增加了编码的严格性,但大幅提升了运行时性能和编译期错误检测能力。

其次是引入了 interface 作为纯数据类型定义的工具。在 ArkTS 中,interface 只能声明属性,不能包含方法实现。这与 TypeScript 中 interface 可以包含可选方法和默认实现有本质区别。这种设计简化了类型系统的复杂度,使编译器能进行更激进的优化。

第三,ArkTS 的泛型和数组操作与 TypeScript 高度兼容。开发者可以使用 CourseItem[] 这样的泛型数组类型,配合 ForEach 渲染组件进行列表数据的声明式渲染。ForEach 组件接受三个参数:数据源、子项渲染函数和键值生成函数,框架通过键值对比实现高效的 diff 算法。

1.4 HarmonyOS 6.1.1 新特性速览

HarmonyOS 6.1.1 在 API 24 层面带来了多项开发者可见的改进。动画系统方面,animateTo.animation 属性的曲线库新增了更多贝塞尔曲线预设,弹性动画和阻尼动画的支持更加完善。布局系统方面,layoutWeight 属性在 Flex/Column/Row 容器中的权重分配算法进行了优化,解决了某些边角情况下权重失效的问题。

弹窗与浮层方面,虽然 HarmonyOS 提供了 CustomDialogbindSheet 等系统级容器,但本应用采用了更为灵活的自定义浮层方案——通过 Stack 布局叠加 @Builder 构建的弹窗组件,配合 @State 布尔变量控制显示与隐藏。这种方案的优势在于完全可控的样式定制能力,不受系统弹窗容器的样式约束,缺点是需要手动处理遮罩点击关闭等交互逻辑。

1.5 业务背景:企业培训学院应用

在上述技术底座之上,本应用实现了一个面向企业内部的员工培训学习平台——“学堂Pro · 企业大学”。该应用涵盖课程浏览与报名、分类检索、考试管理、证书展示、学币积分商城、学习进度追踪以及个人中心等七大功能模块,构建了一套完整的企业人才发展数字化闭环。

应用采用底部双排 7 Tab 架构,主排包含首页、分类、考试、证书四个入口,副排包含商城、学习、我的三个入口。每个 Tab 页面内嵌多个功能区块和自定义弹窗交互,覆盖了报名选班、考试须知、学币兑换、学习提醒、昵称编辑、退课警示等六类弹窗场景。此外,应用还实现了自定义柱状图图表、课程卡按压动效、学习进度动画、证书卡光泽阴影等视觉效果。

HarmonyOS 6.1.1 系统层

ArkTS 语言运行时 API 24

ArkUI 声明式 UI 框架

组件库 Column/Row/Stack/Flex/Scroll

状态管理 @State/@Builder/@Component

动画系统 .animation/Curve

学堂Pro 企业培训应用

7 Tab 页面 + 6 弹窗 + 自定义图表

在这里插入图片描述

二、类型定义体系深度解析

2.1 CourseItem 课程数据模型

应用首先定义了课程项的数据模型。CourseItem interface 包含 12 个属性字段,覆盖了一门课程的全部展示维度。其中 id 为数值类型主键,用于 ForEach 的键值生成。free 为布尔类型,标记该课程是否可以免费学习或积分兑换。其余字段均为字符串类型,包括课程标题、封面 emoji 图标、讲师信息、章节数、学时、学员数、评分、价格和标签。

这种纯数据 interface 的设计是 ArkTS 的典型实践。由于 ArkTS 禁止在 interface 中定义方法体,所有数据加工逻辑都在组件内部通过函数完成。例如,课程封面使用 emoji 字符串而非图片 URL,这是一种轻量化的设计策略——在原型阶段避免引入图片资源和网络加载逻辑,同时保证跨设备的一致性渲染。

interface CourseItem {
  id: number
  title: string
  cover: string
  teacher: string
  chapter: string
  hours: string
  students: string
  score: string
  free: boolean
  tag: string
}

逐行来看:id: number 声明了一个数值类型的课程唯一标识符,在后续的 ForEach 渲染中,框架通过 c.id.toString() 将其转换为字符串作为 diff 键。title: string 存储课程完整标题,在卡片中使用 maxLines(2) 限制为两行显示。cover: string 存储封面 emoji,如 🚀 代表管理进阶课程,这种 emoji 编码方案在 ArkUI 的 Text 组件中原生支持渲染。

free: boolean 是一个关键的布尔标志位。在课程卡片渲染中,当 c.freetrue 时,价格显示为"积分兑换"且文字颜色为绿色 #43A047;为 false 时显示具体价格且颜色为橙色 #E65100。这种条件渲染是声明式 UI 的核心能力之一。

2.2 CourseCategory 分类模型

CourseCategory interface 定义了课程分类的数据结构。包含 idnameiconcount 四个字段。icon 同样使用 emoji 字符串(如 🚀💻),count 存储该分类下的课程数量描述(如"86门")。

在分类网格页面中,这组数据通过 ForEach(COURSE_CATEGORY, ...) 渲染为一个 4 列的网格布局。每个分类卡片的宽度设置为 '23%',配合外层容器的 padding 和卡片间的 margin(5),实现了自适应的四列网格效果。选中态通过 border 属性的动态切换实现——当 this.pickedCate === c.name 时,边框宽度变为 2、颜色为橙色 #E65100,否则边框宽度为 0。

2.3 ExamItem 考试数据模型

考试项的 ExamItem interface 包含 8 个字段。其中 state 字段是渲染逻辑的核心驱动字段,它决定了考试卡片的状态标签颜色和是否显示操作按钮。state 可能的值为"未开始"、“可补考”、“已结束”、"已通过"四种状态。

这种用字符串枚举值而非数字枚举来表示状态的做法,在数据可读性上有明显优势,但在类型安全性上不如枚举。ArkTS 同时支持 enuminterface 两种方式来管理状态值,开发者需要根据场景权衡。在本应用中,Tab 定义使用了 enum AcademyTab,而考试状态等业务数据使用了字符串常量,体现了灵活的设计取舍。

2.4 CertItem、StudyLog、BadgeItem、MallGoods、TeacherInfo

证书模型 CertItem 包含证书名称、图标、编号、颁发日期和有效期信息。其中 valid 字段存储"长期有效"或具体到期月份(如"2027-11"),在渲染时通过条件判断显示不同的有效期标识——绿色圆点代表长期有效,黄色圆点代表有到期日。

学习日志模型 StudyLog 包含星期标识和当日学习分钟数。minutes 字段是数值类型,用于后续柱状图的高度计算——通过 (s.minutes / 95 * 60).toFixed(0) + 'vp' 公式将分钟数映射为视觉高度值。95 是一周内最大学习分钟数(周六的 95 分钟),60 是柱状图最大高度基准值。

徽章模型 BadgeItemearned 布尔字段控制徽章的视觉表现——已获得的徽章使用完整不透明度和暖橙背景色,未获得的徽章使用 0.3 的透明度和灰色背景,实现了一种"解锁/未解锁"的游戏化视觉体验。

商城商品模型 MallGoods 包含商品名称、emoji 图标、学币价格、描述和是否热门的标志。hot: boolean 在商品列表中控制是否显示红色"热门"标签。

讲师模型 TeacherInfo 包含讲师姓名、头衔、图标和主讲课程,用于分类页面的明星讲师团横滑展示。

静态数据源

数据模型层

CourseItem 课程

CourseCategory 分类

ExamItem 考试

CertItem 证书

StudyLog 学习日志

BadgeItem 徽章

MallGoods 商城商品

TeacherInfo 讲师

COURSE_LIST 10门

COURSE_CATEGORY 8个

EXAM_LIST 6场

CERT_LIST 6张

STUDY_LOG 7天

BADGE_LIST 8枚

MALL_GOODS 6件

TEACHER_LIST 5位

在这里插入图片描述

三、静态数据源分析

3.1 课程列表 COURSE_LIST(10 门课程)

课程列表数据包含了 10 门精心设计的课程,覆盖管理进阶、沟通表达、数据分析、企业文化、办公自动化、设计思维、财务思维、信息安全、项目管理和 AI 提效等多个领域。每门课程的数据都经过精心编排,体现了真实企业培训场景的业务逻辑。

例如,第 3 门课程"数据思维:人人都能学会的数据分析"的 free 字段为 trueprice 为"积分兑换",这在前端渲染时会触发积分兑换的绿色标识。第 10 门课程"AI 提效:大模型办公实战指南"的 score 为 5.0——满分评分,tag 为"爆款",体现了当下 AI 技术在企业培训中的热度。

3.2 考试列表 EXAM_LIST(6 场考试)

考试列表涵盖了不同类型和状态的考试场景。第 1 场考试"2026 年度信息安全意识考试"状态为"未开始",在卡片上会显示橙色状态标签和"查看考试须知"按钮。第 2 场考试状态为"可补考",显示红色状态标签和"立即补考"按钮。第 3-6 场考试状态为"已结束"或"已通过",不显示操作按钮。

这种基于 state 字段的条件渲染逻辑是状态驱动 UI 的典型案例——同一个数据模型根据某个字段的值,渲染出完全不同的视觉表现和交互行为。

3.3 学习时长日志 STUDY_LOG(7 天数据)

学习时长数据包含周一至周日的每日学习分钟数。周六的 95 分钟是最高值,周日 70 分钟次之,工作日普遍在 25-62 分钟之间。这组数据不仅用于柱状图的可视化渲染,还用于计算"本周累计 375 分钟"和"超过 82% 的同事"等统计信息。

柱状图的实现完全基于 ArkUI 原生组件,没有使用任何第三方图表库。通过 Column 嵌套结构、动态高度绑定和 .animation 属性,实现了带有入场动画的柱状图效果。这是鸿蒙声明式 UI 强大表现力的一个缩影。

3.4 徽章列表 BADGE_LIST(8 枚徽章)

徽章列表包含 8 枚学习成就徽章,其中 4 枚已获得(连续学习 7 天、夜猫子学员、三好学生、考试达人),4 枚未解锁(知识分享家、学分王者、早起鸟儿、全能学员)。earned: boolean 字段驱动了徽章的视觉表现——已获得的徽章 opacity 为 1.0、背景色为暖橙 #FFF3E0;未获得的徽章 opacity 为 0.3、背景色为灰色 #F5F5F5,形成强烈的视觉对比。

四、Tab 导航体系与枚举定义

4.1 AcademyTab 枚举与 Tab 元数据

应用通过 enum AcademyTab 定义了 7 个 Tab 的索引值:HOME(0)、CATEGORY(1)、EXAM(2)、CERT(3)、MALL(4)、STUDY(5)、MINE(6)。枚举值从 0 开始递增,与两个 Tab 数组的索引形成精确的映射关系。

MAIN_TABS 数组包含主排 4 个 Tab 的元数据(图标 emoji 和标签文字),SUB_TABS 数组包含副排 3 个 Tab 的元数据。这种将 Tab 配置数据化的设计,使得 Tab 的渲染可以通过 ForEach 循环完成,避免了重复的声明式代码堆叠。

AcademyTabMeta interface 定义了 Tab 元数据结构,包含 iconlabel 两个字符串字段。这种"配置即数据"的设计模式,在需要动态调整 Tab 数量或内容时具有极高的灵活性——只需修改数组内容,无需改动渲染逻辑。

4.2 双排 Tab 架构设计理念

底部双排 Tab 是本应用在交互设计上的一个特色。传统移动应用通常采用单排底部 Tab(最多 5 个),超过 5 个时使用"更多"抽屉或顶部 Tab。本应用通过将 7 个 Tab 分为主排 4 个 + 副排 3 个,在不使用抽屉的情况下实现了 7 Tab 的直接展示。

主排和副排通过背景色区分——主排为纯白 #FFFFFF,副排为浅暖橙 #FFF3E0。主排的图标字号为 20,副排为 15,形成了视觉层级差异。选中态的统一表现是:图标下方文字变为橙色 #E65100、主排选中时文字加粗。

底部双排Tab

主排 #FFFFFF

副排 #FFF3E0

条件渲染

条件渲染

条件渲染

条件渲染

条件渲染

条件渲染

条件渲染

商城 idx+4=4

学习 idx+4=5

我的 idx+4=6

首页 idx=0

分类 idx=1

考试 idx=2

证书 idx=3

CourseHomeContent

CategoryGridContent

ExamCenterContent

CertWallContent

CoinMallContent

StudyTrackContent

AcademyMineContent

在这里插入图片描述

五、主入口组件 AcademyApp 深度剖析

5.1 组件声明与状态定义

AcademyApp 是应用的入口组件,通过 @Entry 装饰器标记为页面入口,通过 @Component 装饰器标记为自定义组件。

@Entry 装饰器的作用是告诉 ArkUI 框架:这个组件是一个页面的根节点,框架会为其创建独立的渲染页面和生命周期管理。一个 ArkTS 源文件中只能有一个 @Entry 组件。@Component 装饰器则标记一个 struct 为可复用的自定义组件,它可以在其他组件的 build() 方法中被引用。

@State currentTab: AcademyTab = AcademyTab.HOME 定义了组件的内部状态变量。@State 装饰器的作用是声明一个响应式的状态变量——当 currentTab 的值发生变化时,所有引用了该变量的 UI 描述都会自动重新渲染。这是 ArkUI 声明式范式中"数据驱动视图"的核心机制。

@State 的工作原理是:框架在组件初始化时为被装饰的变量建立依赖追踪。在 build() 方法执行期间,所有读取该变量的表达式都会被记录为该状态的依赖项。当状态被修改时(如 this.currentTab = AcademyTab.CATEGORY),框架会标记所有依赖项为"脏数据",在下一次渲染帧中重新执行对应的 UI 描述函数,生成新的虚拟节点树,并与旧树进行 diff,最终只更新发生变化的 DOM 节点。

5.2 build() 方法:三段式布局骨架

AcademyAppbuild() 方法构建了一个三段式的垂直布局结构。最外层是一个 Column 容器,依次排列顶部头部、内容区和底部双排 Tab。

ArkUI 知识点:Column 组件

Column 是 ArkUI 中最基础的垂直线性布局容器。它的作用是将子组件按照从上到下的方向依次排列。常用属性包括:alignItems(设置子组件在水平方向的对齐方式,可选值有 HorizontalAlign.StartHorizontalAlign.CenterHorizontalAlign.End)、justifyContent(设置子组件在垂直方向的分布方式,可选值有 FlexAlign.StartFlexAlign.CenterFlexAlign.EndFlexAlign.SpaceBetweenFlexAlign.SpaceAroundFlexAlign.SpaceEvenly)、layoutWeight(设置在父容器中的权重占比)。

在本应用中,Column 被广泛用于卡片内部的垂直内容排列。例如头部区域使用 Column 将标题和副标题垂直排列,课程卡片内部使用 Column 排列封面图标和标签。
在这里插入图片描述

5.3 顶部头部区域

头部区域使用 Row 作为水平容器,内部嵌套两个 Column。左侧 Column 显示应用标题"学堂Pro · 企业大学"和学习统计信息,右侧 Column 显示学币图标和数值。

ArkUI 知识点:Row 组件

Row 是 ArkUI 的水平线性布局容器,将子组件从左到右排列。常用属性包括:alignItems(垂直方向对齐)、justifyContent(水平方向分布)、layoutWeight(权重占比)。在 Row 中,layoutWeight(1) 表示该子组件占据剩余空间的比例。本应用中,头部左侧 Column 使用 layoutWeight(1) 撑满剩余空间,将右侧学币信息推到最右端。

头部使用了 linearGradient 属性实现线性渐变背景。linearGradient({ angle: 135, colors: [['#E65100', 0], ['#FB8C00', 1]] }) 表示从左上角(135 度)开始,从深橙 #E65100 渐变到亮橙 #FB8C00colors 数组的每个元素是一个二元组,第一个值是颜色值,第二个值是该颜色在渐变线上的位置(0-1)。

5.4 内容区条件渲染

内容区是一个 Column 容器,内部通过 if-else if-else 条件语句根据 this.currentTab 的值渲染对应的子组件。这种条件渲染模式是 ArkUI 声明式范式中处理页面切换的标准方案之一。

this.currentTabAcademyTab.HOME 时,渲染 CourseHomeContent() 组件。当为 CATEGORY 时,渲染 CategoryGridContent()。以此类推,共有 7 个条件分支。每个分支引用一个独立的 @Component 自定义组件,实现了页面逻辑的模块化分离。

内容区 Column 设置了 layoutWeight(1)width('100%'),使其占据头部和底部 Tab 之间的所有剩余空间。layoutWeight(1) 的含义是:在父容器 Column 中,该子组件占据其他兄弟组件(头部和底部 Tab)分配后剩余的全部空间。

5.5 底部双排 Tab 渲染

底部 Tab 区域是一个 Column 容器,内部嵌套两个 Row。第一个 Row 渲染副排 3 个 Tab,第二个 Row 渲染主排 4 个 Tab。

ArkUI 知识点:ForEach 组件

ForEach 是 ArkUI 中用于列表渲染的核心组件。它接受三个参数:数据源数组、子项渲染函数、键值生成函数。框架通过键值函数返回的字符串作为列表项的唯一标识,在数据更新时进行 diff 比对,只重新渲染发生变化的项,未变化的项保持复用。

在副排 Tab 的渲染中:ForEach(SUB_TABS, (t: AcademyTabMeta, idx: number) => { ... }, (t: AcademyTabMeta) => 'sub_' + t.label)。第一个参数 SUB_TABS 是数据源,第二个参数是子项渲染函数(接收数据项和索引),第三个参数返回 'sub_' + t.label 作为唯一键。主排 Tab 的键值使用 'main_' + t.label 前缀,确保与副排不冲突。

每个 Tab 项内部是一个 Column 容器,包含图标 Text 和标签 Text。选中态通过条件表达式动态设置:this.currentTab === idx + 4 ? '#E65100' : '#9E9E9E'。当当前 Tab 等于该 Tab 的索引值时,文字颜色为橙色,否则为灰色。主排选中时还增加了 FontWeight.Bold 加粗效果。

onClick 回调中将索引值转换为枚举类型:this.currentTab = (idx + 4) as AcademyTab。这里使用了 as 类型断言,将数值索引转换为 AcademyTab 枚举类型。由于 ArkTS 的枚举在运行时本质是数值,这种转换是安全的。

主排 Tab 的图标使用了 .animation({ duration: 200, curve: Curve.EaseOut }) 属性,为图标添加了 200 毫秒的缓出动画效果。Curve.EaseOut 是 ArkUI 内置的动画曲线之一,表示动画开始时快速变化,结束时逐渐减速。当 Tab 切换导致图标字号变化时,该动画会自动触发,产生平滑的过渡效果。
在这里插入图片描述

六、首页组件 CourseHomeContent 逐行精析

6.1 状态变量与弹窗控制

CourseHomeContent 组件管理了 5 个 @State 状态变量。showEnroll: boolean 控制报名弹窗的显示与隐藏。pickedCoursepickedPricepickedClass 分别存储用户在弹窗中选中的课程名称、价格和班级。cardPress: number 记录被按压的课程卡 ID,用于按压动画效果。

这种"多个布尔状态变量分别控制不同弹窗"的模式,在本应用的每个页面中都有体现。它的优点是逻辑清晰、状态独立。当 showEnrolltrue 时,在 Stack 布局中叠加遮罩层和报名弹窗;为 false 时不渲染这些层。

ArkUI 知识点:@Builder 装饰器

@Builder 装饰器用于将一个方法标记为"构建器函数"。被装饰的方法返回一段声明式 UI 描述,可以在 build() 方法中直接调用。它的核心价值在于将可复用的 UI 片段封装为独立的方法,避免代码重复。

本应用大量使用了 @Builder 来封装弹窗组件。例如 modalOverlay(onClose: () => void) 是一个通用的遮罩层构建器,接受一个关闭回调函数作为参数。enrollModal() 是报名弹窗的构建器。构建器可以被多次调用,每次调用都会生成一段独立的 UI 描述。

@Builder@Component 的区别在于:@Component 是独立的组件单元,有自己的状态和生命周期;而 @Builder 是当前组件内部的 UI 片段方法,共享当前组件的状态作用域。在本应用中,弹窗需要直接读写 showEnroll 等状态变量,因此使用 @Builder 比独立组件更方便。

6.2 modalOverlay 通用遮罩层

modalOverlay 是一个参数化构建器,接受一个 onClose 回调函数。它构建了一个全屏半透明黑色遮罩层,覆盖整个页面区域。

@Builder modalOverlay(onClose: () => void) {
  Column() {
    Column().width('100%').height('100%').backgroundColor('rgba(0,0,0,0.5)')
      .onClick(() => {
        onClose()
      })
  }
  .width('100%').height('100%').position({ x: 0, y: 0 }).zIndex(998)
}

在这里插入图片描述

外层 Column 通过 position({ x: 0, y: 0 })zIndex(998) 实现绝对定位和层级控制。position 属性将元素从文档流中脱离,固定在页面左上角。zIndex(998) 确保遮罩层位于内容层之上、弹窗层(zIndex 999)之下。

内层 Column 设置了 rgba(0,0,0,0.5) 的背景色——半透明黑色,遮罩下层内容但不完全遮挡。点击遮罩层触发 onClose() 回调,关闭弹窗。这种"点击遮罩关闭"是移动端弹窗的标准交互模式。

6.3 enrollModal 报名弹窗

报名弹窗是一个结构丰富的居中弹窗,包含课程信息展示、班级选择、费用明细和操作按钮四个区块。

弹窗外层 Column 使用 position({ y: '18%' }) 将弹窗定位在页面纵向 18% 的位置,zIndex(999) 确保在遮罩层之上。alignItems(HorizontalAlign.Center) 使内部内容水平居中。

弹窗主体使用 width('88%') 限制宽度为屏幕的 88%,backgroundColor('#FFFFFF') 白色背景,borderRadius(16) 圆角。这些样式属性共同构成了一个标准的卡片式弹窗容器。

班级选择区域使用 ForEach(['周六上午班', '周三晚间班', '录播自学班'], ...) 渲染三个班级选项。每个班级是一个 Column 容器,包含班级名称和剩余席位信息。选中态通过 backgroundColorfontColor 的条件表达式实现:选中时背景色为橙色 #E65100、文字白色;未选中时背景色为浅灰 #F5F5F5、文字深灰。

onClick 回调中 this.pickedClass = c 将选中的班级名赋值给状态变量,触发引用了该变量的 UI 重新渲染,实现选中态的即时切换。这是响应式状态更新的典型场景。

费用明细区域使用 Divider 组件分割不同费用项。Divider 是 ArkUI 的分割线组件,默认为水平方向的细线,通过 .color('#F5F5F5') 设置颜色。费用计算逻辑是硬编码的——学币抵扣固定为 ¥50,实付为 ¥249。在实际企业应用中,这些值应该根据 pickedPrice 动态计算,但本应用作为演示原型,采用了简化处理。

操作按钮区域包含"取消"和"立即报名"两个 Button 组件。Button 组件内部包裹了 Text 组件用于显示按钮文字。两个按钮通过 layoutWeight 分配空间比例——取消按钮 layoutWeight(1),报名按钮 layoutWeight(1.4),使报名按钮更宽,突出主操作。

6.4 build() 首页主体内容

首页主体使用 Stack 作为根容器,内部叠加滚动内容层和弹窗层。

ArkUI 知识点:Stack 布局

Stack 是 ArkUI 的层叠布局容器,子组件按照声明顺序从底层到顶层叠加。后声明的子组件覆盖先声明的子组件。常用属性包括 alignContent(设置子组件在 Stack 内的对齐方式,默认为 Alignment.Center)。

在本应用中,Stack 的核心用途是"弹窗叠加"。第一个子元素是 Scroll 滚动内容,第二个子元素是条件渲染的遮罩层和弹窗层。当弹窗显示时,弹窗层自然覆盖在滚动内容之上。这是 ArkUI 中实现自定义弹窗的推荐模式。

ArkUI 知识点:Scroll 组件

Scroll 是 ArkUI 的滚动容器组件,当子内容超出可视区域时提供滚动能力。常用属性包括 scrollable(设置滚动方向,默认为垂直 ScrollDirection.Vertical)、scrollBar(设置滚动条显示状态,BarState.Off 表示隐藏滚动条)、scrollBarColor(滚动条颜色)。

本应用中几乎所有页面都使用了 Scroll 作为内容容器,并统一设置 .scrollBar(BarState.Off) 隐藏滚动条,保持视觉简洁。Scroll 内部只能有一个直接子节点,因此通常嵌套一个 Column 来容纳多个子元素。

6.4.1 搜索栏

搜索栏是一个 Row 容器,包含放大镜 emoji 和占位文字。背景为白色、圆角 18、左右内边距 14。这是一个纯展示型搜索栏,没有实现实际的搜索功能——点击不会触发任何交互。在实际应用中,可以在此处添加 onClick 跳转到搜索页面。

6.4.2 滚动公告

公告栏使用暖橙背景 #FFF3E0,包含喇叭 emoji 和公告文字。公告文字使用 maxLines(1) 限制为一行,layoutWeight(1) 撑满剩余空间。这是一种常见的"通知条"组件设计模式。

6.4.3 课程卡片列表

课程卡片通过 ForEach(COURSE_LIST, (c: CourseItem) => { ... }, (c: CourseItem) => c.id.toString()) 渲染。每张卡片是一个 Column 容器,背景白色、圆角 14、内边距 12、外边距水平 12、顶部 8。

卡片内部首行是一个 Row,包含三列:课程封面 Column(72x72 固定尺寸)、课程信息 ColumnlayoutWeight(1) 撑满中间空间)、评分价格 Column(右对齐)。

课程封面 Column 使用 alignItems(HorizontalAlign.Center)justifyContent(FlexAlign.Center) 实现内容居中。justifyContentColumn 中控制子组件在垂直方向的分布——FlexAlign.Center 表示垂直居中。

卡片整体添加了 .animation({ duration: 150, curve: Curve.EaseOut }),配合 onClick 中的 this.cardPress = c.idsetTimeout 实现"按压回弹"效果:点击时记录被点击的课程 ID,150 毫秒后清除记录并弹出报名窗口。虽然 cardPress 状态变量没有直接绑定到卡片的视觉属性上(这是代码中的一种设计留白),但该模式展示了状态驱动的交互思路。

卡片底部区域使用 Divider 分隔,下方是推荐率和试看入口。试看入口使用条件颜色 #E65100 和背景 #FFF3E0,构成一个胶囊形的小按钮。

点击取消/遮罩

点击立即报名

切换班级

用户点击课程卡片

cardPress = c.id

记录 pickedCourse/ pickedPrice

setTimeout 150ms

cardPress = 0 清除按压态

showEnroll = true 显示弹窗

Stack叠加遮罩层 zIndex 998

Stack叠加报名弹窗 zIndex 999

用户选择班级

pickedClass 更新 选中态切换

用户操作

showEnroll = false 关闭弹窗

在这里插入图片描述

七、分类页组件 CategoryGridContent 逐行精析

7.1 分类网格与 Flex 布局

分类页使用 Flex({ wrap: FlexWrap.Wrap }) 作为网格容器。

ArkUI 知识点:Flex 组件与 FlexWrap

Flex 是 ArkUI 的弹性布局容器,类似于 CSS 的 Flexbox。它的核心优势在于灵活的换行能力——通过设置 wrap: FlexWrap.Wrap,当子元素在一行中放不下时会自动换到下一行。

Flex 的常用配置参数包括:direction(主轴方向,FlexDirection.RowFlexDirection.Column)、wrap(是否换行,FlexWrap.NoWrapFlexWrap.Wrap)、justifyContent(主轴对齐)、alignItems(交叉轴对齐)、alignContent(多行时的行间对齐)。

本应用使用 Flex({ wrap: FlexWrap.Wrap }) 实现 4 列网格。每个分类卡片宽度设为 '23%',4 个卡片占 92%,加上卡片间的 margin(5) 间距,恰好形成 4 列布局。当分类数量增加时,Flex 会自动换行。相比 Grid 组件,Flex 方案在动态列数场景下更灵活。

每个分类卡片是一个 Column,宽度 '23%'、高度 84,包含分类 emoji 图标(fontSize 26)、分类名称和课程数量。选中态通过 border 的动态切换实现。border({ width: this.pickedCate === c.name ? 2 : 0, color: '#E65100' }) 在选中时显示 2 像素橙色边框,未选中时边框宽度为 0。

7.2 学习路径推荐

学习路径区域是一个 Column 容器,标题"推荐学习路径"下方使用 ForEach 渲染 3 条学习路径。每条路径是一个 Row,包含路径图标 emoji、路径描述文字(layoutWeight(1) 撑满)和"查看"入口。

路径文字使用 lineHeight(15) 设置行高,提升多行文字的可读性。行之间使用 Divider 分隔,每行的 padding 设置为上下 11,保证足够的视觉间距。

7.3 明星讲师团横滑

讲师区域使用嵌套的 Scroll 实现横向滑动。外层 Scroll 是页面的纵向滚动容器,内层 Scroll 设置 scrollable(ScrollDirection.Horizontal) 为横向滚动。

ScrollDirection.Horizontal 是 ArkUI 中设置滚动方向的关键属性。当 Scroll 的直接子节点是 Row 时,配合横向滚动方向,可以实现水平方向的滑动列表效果。.scrollBar(BarState.Off) 隐藏横向滚动条,保持视觉简洁。

每位讲师卡片宽度 84,包含头像 emoji(56x56 圆形背景)、姓名、头衔和"查看主页"入口。讲师卡片背景白色、圆角 12,通过 margin({ left: 6, right: 6 }) 设置卡片间距。

八、考试页组件 ExamCenterContent 逐行精析

8.1 待考提醒卡

考试页顶部是一个渐变背景的待考提醒卡。使用 linearGradient({ angle: 135, colors: [['#E65100', 0], ['#FB8C00', 1]] }) 创建与头部一致的暖橙渐变。卡片内包含闹钟 emoji、提醒文字和"去备考"按钮。

"去备考"按钮是一个 Text 组件,背景白色、圆角 12、文字橙色。它使用 Text 而非 Button 来实现按钮效果——在 ArkUI 中,任何组件都可以通过 onClick 添加点击交互,Button 只是预设了某些样式的便捷组件。

8.2 考试列表条件渲染

考试列表通过 ForEach(EXAM_LIST, ...) 渲染。每张考试卡片的状态标签颜色通过嵌套三元表达式实现:

.fontColor(e.state === '未开始' ? '#FB8C00' : e.state === '可补考' ? '#F44336' : '#43A047')
.backgroundColor(e.state === '未开始' ? '#FFF3E0' : e.state === '可补考' ? '#FFEBEE' : '#E8F5E9')

在这里插入图片描述
这种链式三元表达式在 ArkUI 的条件渲染中非常常见。"未开始"为橙色系(#FB8C00 文字 + #FFF3E0 背景),"可补考"为红色系(#F44336 文字 + #FFEBEE 背景),"已通过"为绿色系(#43A047 文字 + #E8F5E9 背景)。这种颜色编码方案遵循了通用的状态色彩语义——橙=提醒、红=紧急/失败、绿=成功/通过。

操作按钮使用条件渲染 if (e.state === '未开始' || e.state === '可补考') 来控制显示。只有处于活跃状态的考试才显示操作按钮,已结束或已通过的考试不显示按钮。按钮文字也是条件化的:e.state === '未开始' ? '查看考试须知' : '立即补考'

8.3 noticeModal 考试须知弹窗

考试须知弹窗与报名弹窗结构类似,但内容不同。弹窗顶部展示一个大号 emoji 图标(fontSize 30),然后是标题"考试须知"和考试名称。

须知规则通过 ForEach 渲染一个字符串数组。每条规则是一个 Row,包含橙色圆点 · 和规则文字。规则区域使用 backgroundColor('#FFF8F0') 浅暖橙背景,与弹窗主体白色形成层次区分。

弹窗底部同样使用双按钮布局——“稍后再考”(灰色)和"开始考试"(橙色),通过 layoutWeight 控制宽度比例。

8.4 错题本模块

错题本区域是一个独立的 Column 卡片,包含标题、错题列表和统计信息。错题列表通过 ForEach 渲染一个字符串数组,每条错题包含错误 emoji、题目描述(maxLines(1) 限制一行)和"重练"入口。

底部统计文字"共 23 道错题已加入复习计划"使用 fontSize(9) 和灰色 #9E9E9E,作为辅助信息展示。

onClick

showNotice=true

点击遮罩/稍后再考

点击开始考试

考试完成

onClick

showNotice=true

无操作按钮

无操作按钮

未开始

可补考

已结束

已通过

点击查看须知

显示须知弹窗

关闭弹窗

开始考试

点击立即补考

九、证书页组件 CertWallContent 逐行精析

9.1 证书统计栏

证书页顶部是一个四列统计栏,使用 Row 容器。每列之间使用 Column().width(1).height(30).backgroundColor('#E0E0E0') 作为竖向分割线——这是一个宽 1 像素、高 30 像素的细线,用于视觉分隔。

四列分别显示:持有证书数(6)、长期有效数(4)、即将续期数(2)、累计学分(120)。每列内的数字使用 fontSize(20)FontWeight.Bold 突出显示,标签文字使用 fontSize(9) 和灰色。

这种"分割线 + 统计栏"的设计模式在企业应用中非常常见。ArkUI 没有专门的分割线竖向组件,通过 Column 的固定宽度和高度模拟竖线,是一种常见的实现技巧。

9.2 证书卡片横滑与光泽效果

证书卡片使用嵌套 Scroll 实现横向滑动。每张证书是一个 150 宽的 Column,包含证书主体和有效期标识。

证书主体使用 linearGradient 创建三段渐变效果:[['#E65100', 0], ['#FF9800', 0.7], ['#FFB74D', 1]]。三段渐变从深橙到亮橙再到浅金橙,模拟了实体证书的烫金光泽质感。.shadow({ radius: 8, color: 'rgba(230,81,0,0.3)', offsetY: 4 }) 添加了带 Y 轴偏移的阴影,使证书卡片产生悬浮感。

ArkUI 知识点:shadow 属性

shadow 是 ArkUI 组件的阴影属性,接受一个对象参数:{ radius: number, color: string, offsetX?: number, offsetY?: number }radius 控制阴影模糊半径,color 设置阴影颜色(支持 rgba 格式),offsetX/offsetY 设置阴影的偏移方向。

在本应用中,证书卡片使用 offsetY: 4 使阴影向下偏移,产生"上方有光源"的视觉暗示。阴影颜色 rgba(230,81,0,0.3) 是橙色半透明,与证书的暖橙色调一致,避免黑色阴影的突兀感。

有效期标识使用条件判断:c.valid === '长期有效' ? '🟢 ' : '🟡 '。长期有效的证书显示绿色圆点,有到期日的证书显示黄色圆点,直观传达证书的有效状态。

9.3 学分规则说明

学分规则区域使用 ForEach 渲染规则数组,与考试须知的结构类似。每条规则是一个 Row,包含橙色圆点和规则文字。规则涵盖了必修课结业(10 学分)、选修课结业(6 学分)、认证考试通过(20 学分)和年度达标线(100 学分)。

十、商城页组件 CoinMallContent 逐行精析

10.1 学币卡片

商城页顶部是学币余额卡片,使用与头部一致的暖橙渐变背景。卡片左侧显示"我的学币"标签和余额数字(fontSize 30、加粗),右侧显示本周获取量和"本周 +180"和来源说明"学习时长折算"。

10.2 获取方式标签栏

获取方式区域使用 ForEach 渲染四个学币获取途径标签:“每日登录 +5”、“学满 30 分钟 +20”、“课程结业 +50”、“考试合格 +30”。每个标签是独立的 Text 组件,背景浅橙 #FFF3E0、文字橙色、圆角 10、右侧 margin 8。这是一种横向排列的标签条设计。

10.3 商品列表与兑换弹窗

商品列表通过 ForEach(MALL_GOODS, ...) 渲染。每件商品是一个 Row,包含商品图标(48x48 圆形背景)、商品信息(名称、描述、学币价格)和兑换按钮。

兑换按钮的 onClick 中更新三个状态变量:this.goodsName = g.namethis.goodsCoin = g.coinsthis.showExchange = true。这种"先记录选中数据,再显示弹窗"的模式,确保弹窗能展示正确的商品信息。

热门商品通过 if (g.hot) 条件渲染红色"热门"标签。这是一种条件化辅助标识的实现方式。

exchangeModal 兑换确认弹窗

兑换弹窗展示了学币换算的完整明细:当前学币(1,260)、消耗学币(动态绑定 this.goodsCoin)、兑换后剩余(1260 - this.goodsCoin 动态计算)。这种实时计算展示,让用户在确认兑换前清楚了解余额变化。

弹窗中的数字显示使用了 (1260 - this.goodsCoin).toString() + ' 学币' 的字符串拼接表达式。在 ArkUI 的声明式 UI 中,Text 组件的内容可以是任意字符串表达式,框架会在状态变化时自动重新求值并更新显示。

取消/遮罩

确认兑换

商城商品列表

用户点击兑换

记录 goodsName/ goodsCoin

showExchange = true

遮罩层 + 兑换弹窗

展示换算明细

当前学币 1260

消耗 goodsCoin 动态绑定

剩余 1260-goodsCoin 实时计算

用户操作

showExchange = false

十一、学习页组件 StudyTrackContent 逐行精析

11.1 进度条动画特效

学习页的"在学课程"区域展示了 3 门正在学习的课程,每门课程都有一个进度条。进度条的实现是本应用中技术含量最高的部分之一。

进度条外层是一个 Row,包含进度轨道(layoutWeight(1) 撑满)和百分比文字。进度轨道是一个 Row,背景色为浅橙 #FFF3E0、圆角 3、高度 5。内部嵌套一个 Column,宽度通过条件表达式动态计算:

.width(this.progressAnim ? (c.id === 1 ? '68%' : c.id === 2 ? '35%' : '82%') : '0%')

progressAnimtrue 时,进度条宽度为对应的百分比;为 false 时宽度为 '0%'.animation({ duration: 800, curve: Curve.EaseOut }) 为宽度变化添加了 800 毫秒的缓出动画。

progressAnim 在组件的 onAppear 生命周期回调中被设置为 true

.onAppear(() => {
  setTimeout(() => {
    this.progressAnim = true
  }, 150)
})

onAppear 是 ArkUI 组件的生命周期回调,在组件首次渲染完成后触发。通过 setTimeout 延迟 150 毫秒后设置 progressAnim = true,触发进度条从 0% 到目标百分比的动画过渡。这 150 毫秒的延迟确保了初始渲染(0% 状态)已完成,然后再触发动画。

ArkUI 知识点:onAppear 生命周期

onAppear 是 ArkUI 组件的一个生命周期回调,在组件挂载到渲染树并完成首次渲染后触发。它类似于 React 的 componentDidMount 或 Vue 的 mounted。常用于执行需要在组件可见后才能进行的操作,如启动动画、发起网络请求等。

本应用在 onAppear 中延迟启动进度条动画,是一种常见的"入场动画"实现模式。延迟的目的是确保初始状态(0% 宽度)已经被渲染到屏幕上,然后再改变状态触发动画,否则动画可能不会生效。

11.2 本周学习时长自定义柱状图

柱状图是本应用中最具技术亮点的自定义可视化组件。完全基于 ArkUI 原生组件实现,没有使用 Canvas 或第三方图表库。

柱状图外层是一个 Row 容器,设置 .alignItems(VerticalAlign.Bottom) 使子元素底部对齐,.height(94) 固定高度。通过 ForEach(STUDY_LOG, ...) 渲染 7 个柱子。

每个柱子是一个 ColumnlayoutWeight(1) 等分宽度,包含数值标签、柱体和星期标签三部分。柱体是一个 Column,宽度 16,高度通过动态计算:

.height(this.progressAnim ? (s.minutes / 95 * 60).toFixed(0) + 'vp' : '1vp')

progressAnimtrue 时,高度为 (s.minutes / 95 * 60).toFixed(0) + 'vp'。其中 95 是一周内最大学习分钟数(周六),60 是柱状图最大高度基准。这个公式将分钟数映射为 vp 单位的视觉高度。.toFixed(0) 将浮点数转为整数字符串,+ 'vp' 拼接为 ArkUI 支持的长度单位字符串。

柱体颜色使用条件表达式:s.minutes >= 60 ? '#E65100' : '#FFB74D'。学习时长达到 60 分钟及以上的柱子使用深橙色,不足 60 分钟的使用浅金色,实现数据的高低区分。

柱体添加了 .animation({ duration: 700, curve: Curve.EaseOut }),当高度从 1vp 变化到目标值时,产生 700 毫秒的生长动画效果。borderRadius({ topLeft: 4, topRight: 4 }) 为柱体顶部添加圆角,产生更精致的视觉效果。

ArkUI 知识点:vp 单位与布局尺寸

vp(virtual pixel)是 ArkUI 的虚拟像素单位,它会根据屏幕密度自动缩放。在 API 24 中,vp 是最常用的布局尺寸单位。除了 vp,还有 fp(font pixel,用于字体大小,受系统字号设置影响)和 px(物理像素,不推荐直接使用)。

在本应用中,尺寸值大部分使用百分比字符串(如 '100%''23%')和数字(默认单位为 vp)。柱状图的高度计算结果需要拼接 'vp' 后缀,因为 .height() 方法接受字符串类型时需要包含单位标识。

11.3 学习徽章墙

徽章墙使用 Flex({ wrap: FlexWrap.Wrap }) 实现 4 列网格布局,与分类页面的网格方案一致。每个徽章是一个 Column,宽度 '23%',包含徽章 emoji(fontSize 26)、徽章名称和获得状态。

已获得徽章的 opacity 为 1.0、背景色为暖橙 #FFF3E0;未获得徽章的 opacity 为 0.3、背景色为灰色 #F5F5F5opacity 属性控制组件的整体透明度,0.3 的透明度使未获得徽章呈现"锁定"的灰暗效果。

11.4 remindModal 底部抽屉弹窗

学习提醒弹窗是一个底部抽屉式弹窗,与前面的居中弹窗不同。它使用 position({ x: 0, y: '52%' }) 定位在页面下半部分,borderRadius({ topLeft: 22, topRight: 22 }) 只设置顶部圆角,模拟从底部滑出的抽屉效果。

弹窗内容包含提醒频率选择和提醒时段选择两组选项。频率选项通过 ForEach(['仅工作日', '每天', '每周三次'], ...) 渲染,时段选项通过 ForEach(['08:00', '12:30', '20:00', '21:30'], ...) 渲染。

选项的选中态通过 fontColorbackgroundColor 的条件表达式实现。点击选项更新 remindFreqremindTime 状态变量,触发选中态的即时切换。底部还有一段提示文字"开启提醒后,学习坚持率平均提升 2.4 倍",以及"取消"和"保存设置"双按钮。

学习页 onAppear

setTimeout 150ms

progressAnim = true

进度条: 0% -> 68%/35%/82%

柱状图: 1vp -> 目标高度

animation 800ms EaseOut

animation 700ms EaseOut

动画完成 进度条稳定

动画完成 柱状图稳定

十二、我的页组件 AcademyMineContent 逐行精析

12.1 个人信息卡

我的页顶部是个人信息卡,使用 Row 布局。左侧是一个大号 emoji 头像(fontSize 42),右侧是 Column 包含昵称、职位信息和标签栏。

昵称通过 this.nickName 状态变量绑定显示,可以通过编辑昵称弹窗修改。职位信息"研发部 · 前端工程师 · Lv.5 学习达人"是硬编码字符串。标签栏包含"187 天"和"96.5 小时"两个学习统计标签,以及一个铅笔 emoji 作为编辑入口。

编辑入口的 onClick 设置 this.showNick = true,弹出昵称编辑弹窗。

12.2 学习数据统计栏

统计栏与证书页的结构一致,使用 Row + 竖线分割的四列布局。四列分别显示:结业课程数(14)、持有证书数(6)、年度学分(120)、徽章数(4)。数字使用不同的颜色编码——结业课程为橙色 #E65100、证书为黑色 #212121、学分为绿色 #43A047、徽章为亮橙 #FB8C00

12.3 课程退订入口与 dropModal 退课警示弹窗

退课入口使用红色系配色——"管理"按钮背景为浅红 #FFEBEE、文字红色 #F44336,与页面的暖橙主题形成对比,传达"危险操作"的视觉暗示。

退课警示弹窗 dropModal 是一个警示型弹窗。弹窗顶部是大号退门 emoji(fontSize 34),然后是警示标题"确认退出「管理者进阶」课程?“和详细说明"当前进度 68%,学习记录将保留 90 天,已消耗的学币不予退还。”

弹窗底部使用双按钮——“继续学习”(灰色)和"确认退课"(红色 #F44336)。退课按钮使用红色背景而非橙色,进一步强化危险操作的视觉警示。这是状态色彩语义在企业应用中的重要实践。

12.4 nickModal 昵称编辑弹窗

昵称编辑弹窗包含一个 TextInput 组件。

ArkUI 知识点:TextInput 组件

TextInput 是 ArkUI 的文本输入组件,用于接收用户的单行文本输入。常用属性包括:text(设置初始文本内容,支持双向绑定)、placeholder(占位提示文字)、type(输入类型,如 InputType.NormalInputType.PasswordInputType.Number)、maxLength(最大输入长度)。

本应用中,TextInput({ text: this.nickName }) 将初始值绑定到状态变量。.onChange((v: string) => { this.nickName = v }) 监听文本变化事件,实时更新状态变量。这种"初始值绑定 + onChange 手动同步"的模式是 ArkTS 中实现受控输入的标准方式。

弹窗的 TextInput 设置了 fontSize(13)height(38)backgroundColor('#F5F5F5')borderRadius(10),构成一个简洁的输入框样式。

12.5 功能列表

功能列表通过 ForEach 渲染一个字符串数组:['学习档案导出', '年度学习报告', '我的听课笔记', '企业课程收藏', '设置与隐私']。同时使用第二个数组 ['📥', '📊', '📝', '⭐', '🔒'] 提供每项的 emoji 图标。

ForEach 的子项渲染函数接收两个参数:(s: string, i: number),其中 i 是索引。通过 ['📥', '📊', '📝', '⭐', '🔒'][i] 根据索引取对应的 emoji 图标。这种"双数组按索引关联"的模式在 ArkTS 中是一种常见的数据组织方式。

列表项之间使用 Divider 分隔,但最后一项不显示分割线(通过 if (i < 4) 条件控制)。分割线使用 .margin({ left: 44 }) 设置左侧缩进,使分割线不从头开始,而是从图标右侧开始——这是一种精致的列表视觉设计。

十三、组件依赖与架构关系

静态数据源

共享Builder

Tab页面层 7个自定义组件

入口层

条件渲染

条件渲染

条件渲染

条件渲染

条件渲染

条件渲染

条件渲染

引用

内嵌Builder

内嵌Builder

引用

引用

引用

内嵌Builder

内嵌Builder

引用

引用

内嵌Builder

内嵌Builder

引用

引用

引用

内嵌Builder

内嵌Builder

内嵌Builder

内嵌Builder

内嵌Builder

AcademyApp @Entry

currentTab @State

CourseHomeContent

CategoryGridContent

ExamCenterContent

CertWallContent

CoinMallContent

StudyTrackContent

AcademyMineContent

modalOverlay 通用遮罩

COURSE_LIST

COURSE_CATEGORY

EXAM_LIST

CERT_LIST

STUDY_LOG

BADGE_LIST

MALL_GOODS

TEACHER_LIST

enrollModal

noticeModal

exchangeModal

remindModal

nickModal

dropModal

十四、数据流向与状态更新机制

框架自动响应

用户交互触发状态更新

组件状态初始化

静态数据初始化

onClick

onClick

setTimeout

onClick

onClick

setTimeout

onChange

条件渲染

Stack叠加

animation

条件样式

Text绑定

8个const数组 在模块加载时初始化

AcademyApp.currentTab = HOME

CourseHomeContent.showEnroll = false

StudyTrackContent.progressAnim = false

AcademyMineContent.nickName = 学无止境的明远

点击Tab

currentTab = 新值

点击课程卡片

cardPress/pickedCourse赋值

showEnroll = true

选择班级

pickedClass = 新值

点击兑换

goodsName/goodsCoin/showExchange赋值

onAppear

progressAnim = true

编辑昵称

nickName = 新值

currentTab变化

切换页面组件

showEnroll变化

弹窗显示/隐藏

progressAnim变化

进度条/柱状图动画

pickedClass变化

选中态切换

nickName变化

昵称即时更新

十五、渲染流程与生命周期

是 StudyTrackContent

否 其他页面

页面加载

模块初始化 const数组创建

AcademyApp @Entry实例化

currentTab = HOME 初始状态

build执行 声明UI树

条件渲染 CourseHomeContent

CourseHomeContent.build执行

Scroll内Column构建

ForEach COURSE_LIST 10个子项

ForEach子项build执行 x10

首次渲染完成 屏幕显示

onAppear触发

是否有onAppear

setTimeout 150ms

progressAnim = true

框架diff检测到状态变化

重新执行依赖progressAnim的UI描述

animation属性触发过渡动画

800ms/700ms动画播放

动画结束 最终状态稳定

等待用户交互

十六、弹窗交互体系统一分析

16.1 六类弹窗的架构共性

本应用实现了 6 类弹窗,虽然功能各异,但架构设计高度统一。每类弹窗都遵循相同的模式:一个 @State boolean 控制变量、一个 modalOverlay 遮罩构建器、一个内容构建器、在 Stack 中条件渲染。

弹窗 所属组件 控制变量 定位方式 视觉风格
报名课程 CourseHomeContent showEnroll position y:18% 居中卡片
考试须知 ExamCenterContent showNotice position y:20% 居中卡片
学币兑换 CoinMallContent showExchange position y:26% 居中卡片
学习提醒 StudyTrackContent showRemind position y:52% 底部抽屉
编辑昵称 AcademyMineContent showNick position y:30% 居中小表单
退课警示 AcademyMineContent showDrop position y:28% 居中警示卡

这种统一的弹窗架构模式有以下优势。第一,代码结构一致性高,开发者熟悉一种弹窗的实现后可以快速理解其他弹窗。第二,遮罩层逻辑完全复用——modalOverlay 构建器在所有组件中都有定义(虽然是重复定义而非全局共享,但逻辑完全一致)。第三,关闭逻辑统一——点击遮罩或点击取消按钮都将控制变量设为 false

16.2 弹窗显示与关闭的状态流转

弹窗的显示和关闭完全由 @State 布尔变量驱动。当用户执行触发操作(如点击课程卡片、点击兑换按钮)时,onClick 回调将控制变量设为 true,框架检测到状态变化后重新执行 build() 方法中引用该变量的条件分支,在 Stack 中渲染出遮罩层和弹窗内容。

关闭弹窗有两种方式。第一种是点击遮罩层,触发 modalOverlayonClick 回调,调用传入的 onClose 函数,将控制变量设为 false。第二种是点击弹窗内的取消/确认按钮,直接在 onClick 中设置 this.showXxx = false。两种方式都会触发条件分支重新求值为 false,框架移除遮罩和弹窗的虚拟节点。

16.3 弹窗层级与定位策略

所有弹窗使用 position 属性进行绝对定位,脱离文档流。遮罩层使用 position({ x: 0, y: 0 }) 覆盖全屏,zIndex(998)。弹窗内容使用 position({ y: '18%' })position({ y: '52%' }) 的不同 Y 值定位在不同高度。

居中弹窗的 Y 值在 18%-30% 之间,使弹窗位于屏幕上半部分,符合移动端居中弹窗的常见定位。底部抽屉弹窗的 Y 值为 52%,使弹窗从屏幕中部开始延伸到底部,模拟从底部滑出的效果。

弹窗容器的 alignItems(HorizontalAlign.Center) 确保弹窗内容水平居中。宽度通过百分比控制——报名弹窗 88%、考试须知 84%、兑换弹窗 86%、昵称弹窗 76%、退课弹窗 80%。不同的宽度对应不同的内容量,报名弹窗内容最丰富所以最宽。

弹窗关闭流程

弹窗显示流程

用户触发操作 onClick

设置状态变量 = true

框架检测到状态变化

重新执行build条件分支

Stack中渲染遮罩层 zIndex 998

Stack中渲染弹窗内容 zIndex 999

用户点击遮罩/取消/确认

设置状态变量 = false

框架检测到状态变化

重新执行build条件分支

条件为false 移除遮罩和弹窗虚拟节点

底层内容恢复可见和可交互

十七、ArkUI 组件技术知识深度穿插

17.1 Text 组件深度解析

Text 组件是 ArkUI 中最基础的文本展示组件,在本应用中被大量使用。它不仅用于纯文本展示,还通过 emoji 字符串实现了图标展示的替代方案。

Text 的常用属性包括:fontSize(字号,数字或 fp 单位字符串)、fontWeight(字重,FontWeight.NormalFontWeight.Bolder)、fontColor(文字颜色,支持十六进制和 rgba 格式)、maxLines(最大行数,超出部分截断)、lineHeight(行高)、textAlign(文本对齐方式,TextAlign.StartTextAlign.CenterTextAlign.End)、textOverflow(溢出处理方式,如 TextOverflow.Ellipsis 省略号)。

在本应用中,Text 的一个重要用法是替代图标。所有 emoji 图标(如 🚀🔍📋)都通过 Text 组件渲染。fontSize 属性同时控制了 emoji 的大小——例如课程封面的 Text(c.cover).fontSize(30) 使 emoji 以 30vp 的大小显示。

另一个重要用法是作为可点击元素。在 ArkUI 中,Text 组件可以通过 .onClick() 添加点击事件,实现类似按钮的效果。本应用中的"试看"、“去备考”、“续学”、"兑换"等操作入口,都是 Text 组件配合背景色和圆角实现的胶囊按钮。

17.2 Button 组件与自定义按钮

Button 组件在 ArkUI 中有两种使用方式。第一种是直接使用文字内容:Button('立即报名')。第二种是使用子组件构建器:Button() { Text('立即报名') }

本应用统一使用第二种方式——Button 内部包裹 Text 组件。这种方式的优势在于可以对按钮文字进行更精细的样式控制(字号、颜色、字重等),而第一种方式中文字样式需要通过 Button 的属性设置。

Button 的常用属性包括 type(按钮类型,ButtonType.Capsule 胶囊形、ButtonType.Circle 圆形、ButtonType.Normal 普通)、backgroundColor(背景色)、borderRadius(圆角)、height(高度)。本应用中按钮统一使用 backgroundColor + borderRadius 自定义样式,没有使用 ButtonType 预设类型。

17.3 layoutWeight 权重布局深度

layoutWeight 是 ArkUI 线性布局容器(RowColumn)中子组件的权重属性。它决定了子组件在容器剩余空间中的分配比例。

在本应用中,layoutWeight 的使用场景非常丰富。头部区域左侧 Column 使用 layoutWeight(1) 撑满水平空间,将右侧学币信息推到最右端。课程卡片中间信息区使用 layoutWeight(1) 撑满封面和评分之间的空间。弹窗按钮使用 layoutWeight(1)layoutWeight(1.4) 的比例分配,使主操作按钮更宽。

layoutWeight 的工作原理是:容器在排列完所有固定尺寸(如 width(72))的子组件后,将剩余空间按照各子组件的 layoutWeight 值的比例进行分配。如果一个子组件的 layoutWeight 为 1,其他子组件没有设置 layoutWeight,则该子组件占据全部剩余空间。

17.4 linearGradient 渐变背景

linearGradient 是 ArkUI 的线性渐变背景属性,接受一个配置对象:{ angle: number, colors: Array }angle 是渐变角度(0-360 度),colors 是颜色站点数组。

本应用使用了两种渐变方案。两段渐变 [['#E65100', 0], ['#FB8C00', 1]] 用于头部、待考提醒和学币卡片,从深橙到亮橙。三段渐变 [['#E65100', 0], ['#FF9800', 0.7], ['#FFB74D', 1]] 用于证书卡片,增加了一个中间色站点,产生更丰富的光泽层次。

angle: 135 表示渐变方向从左上角到右下角。在 ArkUI 中,0 度表示从下到上,90 度表示从左到右,180 度表示从上到下,270 度表示从右到左,135 度是左上到右下的对角线方向。

17.5 animation 属性与动画系统

.animation() 是 ArkUI 组件的动画属性,用于为组件的属性变化添加过渡动画。它接受一个配置对象:{ duration: number, curve?: Curve, delay?: number, iterations?: number }

duration 是动画时长(毫秒)。curve 是动画曲线,ArkUI 内置了多种曲线:Curve.Linear(匀速)、Curve.EaseIn(先慢后快)、Curve.EaseOut(先快后慢)、Curve.EaseInOut(慢-快-慢)、Curve.FastOutSlowIn(快速开始缓慢结束)等。

本应用中使用了三种动画场景。Tab 图标的 200ms EaseOut 动画,用于 Tab 切换时图标的过渡。课程卡片的 150ms EaseOut 动画,用于按压效果。进度条和柱状图的 800ms/700ms EaseOut 动画,用于入场生长效果。

tag: string

    this.modalOverlay(() => {
      this.showDrop = false
    })
    this.dropModal()
  }
}
.width('100%').height('100%')

}
}


---
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/e4b1ad1186f343a0ba75ed58e9f6e066.png#pic_center)

### 28.8 总结:

应用的数据流形成了一个完整的闭环:静态数据源(const 数组)在模块加载时初始化 -> 组件创建时读取数据源渲染初始 UI -> 用户交互触发状态变量更新 -> 框架检测状态变化并重新执行依赖的 UI 描述 -> diff 算法比对新旧虚拟节点树 -> 执行最小化 DOM 更新 -> 屏幕渲染更新后的 UI。

这个闭环中,开发者只需要编写数据源定义和 UI 声明,框架负责中间所有的 diff 和渲染管线工作。这是声明式 UI 范式的核心价值——将开发者从繁琐的 DOM 操作中解放出来,专注于业务逻辑和 UI 设计本身。


Logo

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

更多推荐