当前位置: 首页 > news >正文

HarmonyOS APP<玩转React>开源教程十八:课程详情页面

第18次:课程详情页面

课程详情页面是用户学习的核心界面,需要清晰地展示课程内容、代码示例和关键要点。本次课程将学习如何渲染不同类型的内容段落。


学习目标

  • 掌握课程内容渲染方法
  • 学会实现 ContentSection 段落组件
  • 设计提示与警告样式
  • 实现完成按钮交互
  • 完成课程详情页面开发

18.1 课程内容结构

LessonContent 数据结构

interfaceLessonContent{sections:ContentSection[];// 内容段落codeExamples:CodeExample[];// 代码示例keyTakeaways:string[];// 关键要点}interfaceContentSection{type:'text'|'tip'|'warning'|'heading';title?:string;content:string;}interfaceCodeExample{title:string;code:string;language:string;explanation?:string;isEditable:boolean;}

内容类型说明

类型说明样式
text普通文本默认样式
heading标题加粗大字
tip提示信息蓝色背景
warning警告信息红色背景

18.2 页面头部设计

导航栏

Row(){// 返回按钮Text('←').fontSize(24).fontColor(this.isDarkMode?'#ffffff':'#1a1a2e').onClick(()=>router.back())// 标题Text(this.lesson?.title??'').fontSize(18).fontWeight(FontWeight.Medium).fontColor(this.isDarkMode?'#ffffff':'#1a1a2e').margin({left:16}).layoutWeight(1).maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis})// 收藏按钮Text(this.isBookmarked?'★':'☆').fontSize(24).fontColor(this.isBookmarked?'#fcc419':(this.isDarkMode?'#9ca3af':'#6c757d')).onClick(()=>this.toggleBookmark())}.width('100%').padding(16).backgroundColor(this.isDarkMode?'#282c34':'#ffffff')

18.3 ContentSection 段落渲染

段落组件实现

@BuilderSectionContent(section:ContentSection){Column(){// 标题(如果有)if(section.title){Text(section.title).fontSize(16).fontWeight(FontWeight.Bold).fontColor(this.isDarkMode?'#ffffff':'#1a1a2e').margin({bottom:8})}// 根据类型渲染不同样式if(section.type==='tip'){this.TipContent(section.content)}elseif(section.type==='warning'){this.WarningContent(section.content)}else{this.TextContent(section.content)}}.width('100%').padding({left:16,right:16,top:16}).alignItems(HorizontalAlign.Start)}

普通文本

@BuilderTextContent(content:string){Text(content).fontSize(14).fontColor(this.isDarkMode?'#e6e6e6':'#333333').lineHeight(22)}

18.4 提示与警告样式

提示样式(Tip)

@BuilderTipContent(content:string){Row(){Text('💡').fontSize(16)Text(content).fontSize(14).fontColor(this.isDarkMode?'#ffffff':'#1a1a2e').margin({left:8}).layoutWeight(1)}.width('100%').padding(12).backgroundColor(this.isDarkMode?'rgba(97,218,251,0.15)':'rgba(97,218,251,0.1)').borderRadius(8)}

警告样式(Warning)

@BuilderWarningContent(content:string){Row(){Text('⚠️').fontSize(16)Text(content).fontSize(14).fontColor(this.isDarkMode?'#ffffff':'#1a1a2e').margin({left:8}).layoutWeight(1)}.width('100%').padding(12).backgroundColor(this.isDarkMode?'rgba(255,107,107,0.15)':'rgba(255,107,107,0.1)').borderRadius(8)}

18.5 代码示例展示

代码示例区域

if(this.lesson.content.codeExamples.length>0){Text('代码示例').fontSize(18).fontWeight(FontWeight.Bold).fontColor(this.isDarkMode?'#ffffff':'#1a1a2e').padding({left:16,right:16,top:24}).width('100%')ForEach(this.lesson.content.codeExamples,(example:CodeExample)=>{Column(){// 代码块组件CodeBlock({code:example.code,language:example.language,title:example.title,explanation:example.explanation})// 在调试器中打开按钮if(example.isEditable){Button('🔧 在调试器中打开').fontSize(13).fontColor('#61DAFB').backgroundColor(this.isDarkMode?'#1e2127':'#f0f9ff').borderRadius(16).margin({top:8}).onClick(()=>{router.pushUrl({url:'pages/CodePlayground',params:{title:example.title,code:example.code,language:example.language,explanation:example.explanation}});})}}.margin({left:16,right:16,top:12}).alignItems(HorizontalAlign.End)})}

18.6 关键要点展示

要点列表

@BuilderKeyTakeawaysSection(){Column(){Text('📌 关键要点').fontSize(16).fontWeight(FontWeight.Bold).fontColor(this.isDarkMode?'#ffffff':'#1a1a2e').margin({bottom:12})ForEach(this.lesson?.content.keyTakeaways??[],(takeaway:string,index:number)=>{Row(){// 序号圆圈Text(`${index+1}`).fontSize(12).fontColor('#ffffff').backgroundColor('#61DAFB').width(20).height(20).textAlign(TextAlign.Center).borderRadius(10)// 要点内容Text(takeaway).fontSize(14).fontColor(this.isDarkMode?'#e6e6e6':'#333333').margin({left:12}).layoutWeight(1)}.width('100%').margin({bottom:8})})}.width('100%').padding(16).margin({left:16,right:16,top:24}).backgroundColor(this.isDarkMode?'#282c34':'#ffffff').borderRadius(12).alignItems(HorizontalAlign.Start)}

18.7 完成按钮交互

按钮设计

@BuilderCompleteButton(){Button(this.isCompleted?'✓ 已完成':'标记为完成').width('90%').height(48).fontSize(16).fontWeight(FontWeight.Medium).backgroundColor(this.isCompleted?'#51cf66':'#61DAFB').fontColor(this.isCompleted?'#ffffff':'#1a1a2e').borderRadius(24).margin({top:24}).onClick(()=>this.markComplete())}

完成逻辑

privateasyncmarkComplete():Promise<void>{if(this.lesson&&!this.isCompleted){// 标记课程完成awaitProgressService.markLessonComplete(this.lesson.id,this.moduleId);this.isCompleted=true;// 检查并颁发徽章constprogress=awaitProgressService.loadProgress();awaitBadgeService.checkAndAwardBadges(progress);}}

18.8 完整页面代码

/** * 课程详情页 */import{router}from'@kit.ArkUI';import{TutorialService}from'../services/TutorialService';import{ProgressService}from'../services/ProgressService';import{BookmarkService}from'../services/BookmarkService';import{BadgeService}from'../services/BadgeService';import{Lesson,ContentSection,CodeExample}from'../models/Models';import{CodeBlock}from'../components/CodeBlock';interfaceRouterParams{moduleId:string;lessonId:string;}@Entry@Componentstruct LessonDetail{@Statelesson:Lesson|undefined=undefined;@StatemoduleId:string='';@StateisBookmarked:boolean=false;@StateisCompleted:boolean=false;@StorageLink('isDarkMode')isDarkMode:boolean=false;aboutToAppear():void{constparams=router.getParams()asRouterParams;if(params?.moduleId&&params?.lessonId){this.moduleId=params.moduleId;this.lesson=TutorialService.getLessonById(params.moduleId,params.lessonId);this.isBookmarked=BookmarkService.isBookmarked(params.lessonId);this.checkCompletion();}}privateasynccheckCompletion():Promise<void>{constprogress=awaitProgressService.loadProgress();this.isCompleted=progress.completedLessons.includes(this.lesson?.id??'');}build(){Column(){// 头部导航this.HeaderSection()// 内容区域if(this.lesson){Scroll(){Column(){// 课程描述Text(this.lesson.description).fontSize(14).fontColor(this.isDarkMode?'#d1d5db':'#495057').padding({left:16,right:16,top:16}).width('100%')// 内容段落ForEach(this.lesson.content.sections,(section:ContentSection)=>{this.SectionContent(section)})// 代码示例this.CodeExamplesSection()// 关键要点if(this.lesson.content.keyTakeaways.length>0){this.KeyTakeawaysSection()}// 完成按钮this.CompleteButton()}.padding({bottom:40})}.layoutWeight(1).scrollBar(BarState.Off)}}.width('100%').height('100%').backgroundColor(this.isDarkMode?'#1a1a2e':'#f8f9fa')}// ... Builder 方法实现}

本次课程小结

通过本次课程,你已经:

✅ 掌握了课程内容渲染方法
✅ 学会了实现 ContentSection 段落组件
✅ 设计了提示与警告样式
✅ 实现了完成按钮交互
✅ 完成了课程详情页面开发


课后练习

  1. 添加目录导航:实现课程内容目录快速跳转

  2. 添加阅读进度:显示当前阅读位置百分比

  3. 添加字体设置:支持调整字体大小


下次预告

第19次:CodeBlock 代码块组件

我们将开发代码展示组件:

  • 代码展示设计
  • 语法高亮基础
  • 代码复制功能
  • Toast 提示反馈

实现专业的代码展示效果!

http://www.jsqmd.com/news/514194/

相关文章:

  • 基于L1范数、NS及MRTS剪枝算法的VGG16模型压缩与NIST测试报告
  • [具身智能-79]:ROS2的发布和订阅机制的工作原理、订阅过程、发布过程
  • 从原理到代码:一文搞懂Jaccard系数在YOLOv5中的应用
  • STM8 CAN硬件滤波器配置详解:标准帧与扩展帧位映射
  • 本地部署OpenClaw:5分钟搭个AI助理,到底值不值?
  • KeilC51与MDK537共存安装全攻略:从下载到切换使用(附资源包)
  • 如何通过SEC-Edgar实现美国上市公司财报的高效批量下载
  • 从Tor到QUIC:手把手教你用Wireshark和Scapy分析5种主流加密协议的流量指纹
  • Qwen3-32B助力研发提效:代码补全+技术文档问答的私有大模型落地案例
  • FPGA实战:手把手教你用Verilog实现一个AXI4-Full Master(附完整代码与仿真)
  • C++模板初阶知识库
  • [具身智能-80]:逆向运动学 (Inverse Kinematics, IK) 是计算关节角度以使机械臂末端到达指定位置和姿态的核心算法。
  • 智慧油田磕头机数据采集物联网解决方案
  • unity pc运行包导入glb
  • 基于改进Cuk电路的锂离子电池组均衡系统复现与仿真研究
  • ESP32驱动MAX31725高精度温度传感器实战指南
  • Qwen3-0.6B-FP8极速对话工具:AI编程辅助工具开发
  • 直播行业中的优秀人才容易得的心理疾病
  • 北京游2天1晚深度游
  • 酒吧点歌软件 | 一店一码・多店运营;多店独立后台、艺人管理、收益结算一体化。
  • GLM-OCR入门环境配置保姆级教程:Anaconda虚拟环境与依赖安装
  • 人工智能期末考试突击指南:华南理工大学研究生亲测有效的5个复习技巧
  • 提示词的时代快结束了,下一个是什么?
  • Ansible AWX保姆级安装教程:从Docker到Kubernetes的完整配置流程
  • STM8 CAN总线Bootloader设计与实现
  • 新概念英语第一册065_Not a baby
  • OFA-VE在金融领域的应用:票据识别与理解
  • 通义千问1.5-1.8B-Chat-GPTQ-Int4 WebUI快速部署教程:Python环境一键配置指南
  • Ghidra vs IDA:逆向工具对比与Java脚本开发指南
  • Qwen3-VL-8B功能体验:不只是OCR,实测它如何“读懂”图片里的故事