本文详细介绍如何使用DevEco Studio和ArkTS语言开发一个功能完整的课程表手机应用,适合HarmonyOS开发新手学习。

一、项目简介

1.1 项目功能

本项目是一个基于HarmonyOS的课程表管理应用,主要功能包括:

  • 课程表展示:以周为单位显示课程安排,支持星期切换

  • 添加课程:填写课程名称、教室、备注等信息

  • 编辑课程:修改已有课程信息

  • 查看详情:点击课程卡片查看详细信息

  • 周数计算:自动计算当前学期周数

1.2 技术栈

  • 开发工具:DevEco Studio

  • 开发语言:ArkTS

1.3 项目源码

项目完整代码已上传至Gitee,欢迎大家下载使用。ScheduleAPP项目开发源码https://gitee.com/zhen-shi_1_0/schedule.git

二、开发环境搭建

DevEco Studio的下载及使用请查看下面这篇博文的前两节。零基础使用 Flutter 编译开发 鸿蒙 HarmonyOS 项目教程——搭建环境篇https://blog.csdn.net/2401_82544706/article/details/155164990?sharetype=blogdetail&sharerId=155164990&sharerefer=WAP&sharesource=2401_82544706

三、创建项目

3.1 新建项目

  1. 打开DevEco Studio,点击 FileNewCreate Project

  2. 选择模板:选择 Empty Ability(空白应用)

  3. 配置项目信息

    • Project name: ScheduleAPP

    • Bundle name: com.example.scheduleapp

    • Compile SDK: API 9或更高

    • Device type: Phone

        4. 点击 Finish 完成创建

3.2 项目结构说明

创建完成后,项目结构如下:

 ScheduleAPP/
 ├── AppScope/              # 应用级配置
 │   ├── app.json5         # 应用配置
 │   └── resources/        # 应用资源
 ├── entry/                 # 应用主模块
 │   ├── src/
 │   │   ├── main/
 │   │   │   ├── ets/      # 代码目录
 │   │   │   │   ├── entryability/  # 应用入口
 │   │   │   │   └── pages/         # 页面目录(我们要创建的地方)
 │   │   │   ├── resources/ # 资源目录
 │   │   │   └── module.json5      # 模块配置

四、核心功能实现

4.1 第一步:创建课程数据模型

entry/src/main/ets/pages/ 目录下创建 class 文件夹,然后创建 Course.ets 文件:

export class Course {
   public courseName: string = '';  // 课程名
   public classroom: string = '';   // 教室
   public remark: string = '';      // 备注(如老师)
   public index: number = 0;        // 课程索引(用于定位)
 ​
   constructor(courseName: string, classroom: string, remark: string, index: number) {
     this.courseName = courseName;
     this.classroom = classroom;
     this.remark = remark;
     this.index = index;
   }
 }

代码说明

  • 使用 class 定义课程数据类

  • 包含课程的基本信息:名称、教室、备注、索引

  • index 用于标识课程在课程表中的位置(0-59,表示60个时间槽)

4.2 第二步:实现主页面(Index.ets)

主页面是课程表的展示界面,包含周数显示、星期选择、课程网格等。

4.2.1 页面结构

创建 entry/src/main/ets/pages/Index.ets 文件:

import router from '@ohos.router';
 import { Course } from './class/Course'
 ​
 // 课程节数数组(1-15节)
 const content1: string[] = (() => {
   const arr: string[] = new Array(15).fill('');
   for (let i = 0; i < 15; i += 1) {
     arr[i] = i + 1 + '';
   }
   return arr;
 })()
 ​
 @Entry
 @Component
 struct TableHome {
   // 使用 @StorageLink 连接全局状态
   @StorageLink('name') name: string = '课程表1';
   @StorageLink('showDialog') showDialog: boolean = false;
   @StorageLink('showDetails') showDetails: boolean = false;
   @StorageLink('selectedCourse') selectedCourse: Course = new Course('', '', '', -1);
   @StorageLink('updatedCourse') updatedCourse: Course = new Course('', '', '', -1);
 ​
   // 课程数据数组(60个时间槽:12节×5天)
   @State content: Course[] = (() => {
     const arr: Course[] = new Array(60);
     for (let i = 0; i < 60; i++) {
       arr[i] = new Course('', '', '', i);
     }
     return arr;
   })()
 ​
   // 当前选中的星期(1-7,1=周一)
   @State currentWeekday: number = new Date().getDay() || 7;
 ​
   // 获取星期名称
   getWeekdayName(weekday: number): string {
     const names = ['', '周一', '周二', '周三', '周四', '周五', '周六', '周日'];
     return names[weekday] || '';
   }
 ​
   // 获取指定星期几的日期
   getWeekDate(dayOfWeek: number): string {
     let date = new Date();
     let targetDay = dayOfWeek - 1; // 转换为JS星期索引(0=周日)
     let diff = targetDay - date.getDay();
     date.setDate(date.getDate() + diff);
     let month = date.getMonth() + 1;
     let day = date.getDate();
     return `${month}/${day}`;
   }
 ​
   // 计算从学期开始到现在的周数
   getWeeksSinceStart(startYear: number, startMonth: number, startDay: number): number {
     let startDate = new Date(startYear, startMonth - 1, startDay);
     let currentDate = new Date();
     let timeDiff = currentDate.getTime() - startDate.getTime();
     let daysDiff = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
     let weeksDiff = Math.floor(daysDiff / 7) + 1;
     return weeksDiff;
   }
 ​
   aboutToAppear(): void {
     // 初始化全局状态
     AppStorage.SetOrCreate('updatedCourse', new Course('', '', '', -1));
     AppStorage.SetOrCreate('showDetails', false);
   }
 ​
   // 页面显示时同步更新
   onPageShow() {
     this.syncUpdatedCourseToLocal();
   }
 ​
   syncUpdatedCourseToLocal(): void {
     const uc = this.updatedCourse;
     if (uc.index >= 0 && uc.index < this.content.length) {
       this.content[uc.index] = new Course(
         uc.courseName,
         uc.classroom,
         uc.remark,
         uc.index
       );
     }
   }
 ​
   // 构建一行课程(5个时间段)
   @Builder
   hingeBody(contNumber: number) {
     Sidebar({ inputValue: content1[contNumber] })
     ForEach(this.content.slice(contNumber*5+1, contNumber*5+6), (item: Course) => {
       GridItemCase({ course: item });
     })
   }
 ​
   // 构建主体内容(包括午休、晚休)
   @Builder
   mainBody() {
     // 上午课程(1-4节)
     ForEach([0,1,2,3], (item: number)=> {
       this.hingeBody(item)
     })
     // 午休
     GridItem() {
       Text('午休');
     }
     .GridItemRestFn()
     // 下午课程(5-8节)
     ForEach([4,5,6,7], (index: number)=> {
       this.hingeBody(index)
     })
     // 晚休
     GridItem() {
       Text('晚休');
     }
     .GridItemRestFn()
     // 晚上课程(9-11节)
     ForEach([8,9,10], (index: number)=> {
       this.hingeBody(index)
     })
   }
 ​
   build() {
     Stack({ alignContent: Alignment.BottomStart }) {
       Column() {
         // 导航栏
         Row() {
           Image($r('app.media.chevron_left'))
             .height(40)
             .onClick(() => {
               router.back()
             })
             .margin({ right: 10 })
 ​
           if (this.showDialog) {
             Dialog()
           } else {
             Text(this.name)
               .fontSize(18)
               .fontWeight(FontWeight.Bold)
               .alignSelf(ItemAlign.Center)
               .onClick(() => {this.showDialog = true;})
           }
         }
         .width('100%')
         .height(60)
         .padding(5)
         .backgroundColor("#F2F2F4")
         .alignItems(VerticalAlign.Center)
 ​
         // 星期选择器
         Row() {
           Grid() {
             GridItem(){
               Column() {
                 Text(){
                   Span(this.getWeeksSinceStart(2025,9,8).toString())
                   Span('周')
                 }
                 .fontSize(12)
                 .fontWeight(FontWeight.Bolder)
                 Image($r('app.media.chevron_down'))
                   .height(20)
               }
             }
             .height('100%')
 ​
             // 星期按钮(周一到周日)
             ForEach([1,2,3,4,5,6,7],(weekday: number)=>{
               GridItem(){
                 Column() {
                   Text(this.getWeekdayName(weekday))
                     .fontSize(14)
                     .fontColor(this.currentWeekday === weekday ? '#FFFFFF' : '#666666')
                     .fontWeight(FontWeight.Bold)
                     .margin({ bottom: 2 })
                   Text(this.getWeekDate(weekday + 1))
                     .fontSize(10)
                 }
                 .justifyContent(FlexAlign.Center)
               }
               .height(40)
               .backgroundColor(this.currentWeekday === weekday ? '#007DFF' : '#F5F5F5')
               .borderRadius(8)
               .onClick(() => {
                 this.currentWeekday = weekday;
               })
             })
           }
           .columnsTemplate('13fr 18fr 18fr 18fr 18fr 18fr')
           .padding({ left: 8, right: 8, top: 8, bottom: 8 })
           .columnsGap(1)
         }
         .height('8%')
         .backgroundColor("#F2F2F4")
         .margin({bottom:2})
 ​
         // 课程表主体
         Grid() {
           this.mainBody()
         }
         .width('100%')
         .height('100%')
         .columnsTemplate('13fr 18fr 18fr 18fr 18fr 18fr')
         .scrollBar(BarState.Off)
         .edgeEffect(EdgeEffect.Spring)
       }
       .height('100%')
       .width('100%')
 ​
       // 课程详情弹窗
       if (this.showDetails) {
         Details()
       }
     }
     .height('100%')
     .width('100%')
   }
 }
4.2.2 课程单元格组件

在主页面中添加课程单元格组件:

 @Component
 struct GridItemCase {
   @State isSelected: boolean = false;
   @Prop course: Course = new Course('','','',0);
   @State isCourse: boolean = false;
 ​
   constructor(courseProp: Course) {
     super();
     this.course = courseProp;
   }
 ​
   aboutToAppear(): void {
     // 判断是否有课程
     if (this.course.courseName === '') {
       this.isCourse = false;
     } else {
       this.isCourse = true;
     }
   }
 ​
   build() {
     GridItem(){
       Row(){
         Column(){
           if (this.isCourse) {
             // 显示课程信息
             Text(this.course.courseName)
               .fontSize(13)
               .fontWeight(FontWeight.Bold)
             Text(this.course.classroom)
               .fontSize(11)
               .fontColor("#d0d0d0")
             Text(this.course.remark)
               .fontSize(11)
               .fontColor("#d0d0d0")
           } else {
             // 显示加号(可添加课程)
             if (this.isSelected) {
               Text('+')
                 .fontSize(24)
                 .fontWeight(FontWeight.Bold)
                 .foregroundColor(Color.Black)
             }
           }
         }
         .width('100%')
         .height('100%')
         .justifyContent(FlexAlign.Center)
         .alignItems(HorizontalAlign.Center)
       }
       .onClick(()=>{
         if (this.isCourse) {
           // 点击已有课程,显示详情
           AppStorage.Set('selectedCourse', this.course)
           AppStorage.Set('showDetails', true)
         } else {
           // 点击空白区域,准备添加课程
           if (this.isSelected) {
             router.pushUrl({
               url: 'pages/AddCoursePage',
               params: this.course
             })
           }
           this.isSelected = !this.isSelected
           AppStorage.Set('showDetails', false)
         }
       })
       .height(90)
       .backgroundColor(this.isSelected ? "#f2f2f2" : Color.White)
     }
     .border({
       width: 1,
       color: "#F2F2F4",
       style: BorderStyle.Solid
     })
   }
 }
4.2.3 侧边栏组件(时间轴)
@Component
 struct Sidebar {
   @Prop inputValue: string = '';
 ​
   constructor(inputValue: string) {
     super();
     this.inputValue = inputValue;
   }
 ​
   build() {
     GridItem(){
       Column(){
         Text(this.inputValue)
           .fontSize(14)
           .fontWeight(FontWeight.Bold)
           .margin(3)
         Text("08:00")
           .fontSize(11)
           .fontColor("#d0d0d0")
         Text("08:45")
           .fontSize(11)
           .fontColor("#d0d0d0")
       }
       .width('100%')
       .height('100%')
       .justifyContent(FlexAlign.Center)
       .alignItems(HorizontalAlign.Center)
       .height(90)
     }
     .border({
       width: 1,
       color: "#F2F2F4",
       style: BorderStyle.Solid
     })
   }
 }
4.2.4 课程详情组件
 @Component
 struct Details {
   @StorageLink('selectedCourse') selectedCourse: Course = new Course('', '', '', -1)
 ​
   build() {
     Column() {
       // 标题栏
       Row() {
         Text('课程详情')
           .width('65%')
           .fontSize(20)
           .fontWeight(FontWeight.Bolder)
           .textAlign(TextAlign.End)
         Blank()
         Image($r('app.media.x_close'))
           .width(30)
           .onClick(() => {
             AppStorage.Set('showDetails', false)
           })
       }
       .padding({ top: 15, right: 15 })
       .width('100%')
 ​
       // 详情内容
       Column() {
         Row(){
           Circle()
             .width(10)
             .height(10)
             .borderRadius(5)
             .backgroundColor(Color.Black)
             .margin({ right: 10 })
           Text(this.selectedCourse.courseName)
             .fontSize(18)
             .textAlign(TextAlign.Start)
           Blank()
 ​
           Button('编辑')
             .size({width: 60, height: 30})
             .fontColor('#2f2f2f')
             .backgroundColor('#f5f5f5')
             .onClick(() => {
               router.pushUrl({
                 url: 'pages/AddCoursePage',
                 params: this.selectedCourse
               })
             })
         }
         .width('100%')
         .padding({ right: 15 })
         .margin({ bottom: 10 })
 ​
         if (this.selectedCourse.classroom !== ''){
           Text('教室:' + this.selectedCourse.classroom)
             .margin({ bottom: 5 })
             .width('100%')
         }
         if (this.selectedCourse.remark !== ''){
           Text('备注(如老师):' + this.selectedCourse.remark)
             .margin({ bottom: 5 })
             .width('100%')
         }
       }
       .width('90%')
       .margin({ top: 15 })
       .padding(20)
       .justifyContent(FlexAlign.Start)
       .backgroundColor(Color.White)
       .borderRadius(20)
     }
     .width('100%')
     .padding(12)
     .borderRadius({
       topLeft: 50,
       topRight: 50,
       bottomLeft: 20,
       bottomRight: 20
     })
     .backgroundColor('#f7f7f7')
     .justifyContent(FlexAlign.Center)
     .position({ bottom: 0 })
   }
 }
4.2.5 扩展方法

在文件末尾添加扩展方法:

// 扩展GridItem,用于午休/晚休样式
 @Extend(GridItem)
 function GridItemRestFn() {
   .width('100%')
   .backgroundColor("#F2F2F2")
   .columnStart(0)
   .columnEnd(5)
 }
 ​
 // 扩展Text,用于小字样式
 @Extend(Text)
 function TextFn() {
   .fontSize(11)
   .fontColor("#d0d0d0")
 }
 ​
 // 课程表名称编辑对话框
 @Preview
 @Component
 struct Dialog {
   build() {
     Row() {
       TextInput({
         placeholder: '请输入课程表名称'
       })
         .width('60%')
         .height(35)
         .onChange((value: string) => {
           newName = value;
         })
         .margin({ right: 15 })
       Button('确定')
         .height(35)
         .onClick(() => {
           if (newName === '') {
             newName = '课程表1'
           }
           AppStorage.Set('name', newName)
           AppStorage.Set('showDialog', false)
         })
     }
   }
 }
 ​
 let newName: string = '';

4.3 第三步:实现添加/编辑课程页面(AddCoursePage.ets)

创建 entry/src/main/ets/pages/AddCoursePage.ets 文件:

import router from '@ohos.router';
 import { Course } from './class/Course'
 ​
 @Entry
 @Component
 struct AddCoursePage {
   @StorageLink('updatedCourse') updatedCourse: Course = new Course('', '', '', -1);
 ​
   @State course: Course = new Course('','','',-1);
   @State timeSlotCount: number = 1; // 时间段数量
   private weekRange: string = '第9-18周';
   private backgroundColor1: ResourceColor = '#007DFF';
   @State isEditMode: boolean = false; // 是否为编辑模式
 ​
   aboutToAppear(): void {
     const params: Course = router.getParams() as Course;
     if (params && params.index >= 0) {
       this.course.index = params.index;
       if (params.courseName !== '') {
         this.course = params;
         this.isEditMode = true;
       }
     }
   }
 ​
   build() {
     Column() {
       // 导航栏
       Row() {
         Text('取消')
           .fontColor("#0075E6")
           .fontSize(16)
           .onClick(() => {
             router.back()
             AppStorage.Set('showDetails', false)
           })
         Text(this.isEditMode ? '编辑课程' : '新建课程')
           .fontSize(18)
           .fontWeight(FontWeight.Bold)
           .alignSelf(ItemAlign.Center)
         Text('完成')
           .fontColor("#0075E6")
           .fontSize(16)
           .onClick(() => {
             AppStorage.SetOrCreate('updatedCourse', this.course);
             AppStorage.Set('showDetails', false)
             router.back();
           })
       }
       .width('100%')
       .height(60)
       .padding(10)
       .backgroundColor(Color.White)
       .justifyContent(FlexAlign.SpaceBetween)
       .alignItems(VerticalAlign.Center)
 ​
       // 内容区域
       Scroll() {
         Column() {
           // 课程名输入
           Row() {
             Text('课程名')
               .fontSize(20)
               .fontWeight(FontWeight.Medium)
               .margin({ right: 10 })
             TextInput({
               placeholder:'必填',
               text:this.course.courseName
             })
               .layoutWeight(1)
               .backgroundColor(Color.White)
               .onChange((value: string) => {
                 this.course.courseName = value;
               })
           }
           .height(60)
           .margin({ top: 20, bottom: 15 })
           .borderRadius(10)
           .backgroundColor(Color.White)
           .padding(10)
           .width('90%')
 ​
           // 教室输入
           Row() {
             Text('教室')
               .fontSize(20)
               .fontWeight(FontWeight.Medium)
               .margin({ right: 10 })
             TextInput({
               placeholder: '非必填',
               text:this.course.classroom
             })
               .layoutWeight(1)
               .backgroundColor(Color.White)
               .onChange((value: string) => {
                 this.course.classroom = value;
               })
           }
           .width('90%')
           .height(60)
           .margin({ bottom: 15 })
           .borderRadius(10)
           .backgroundColor(Color.White)
           .padding(10)
 ​
           // 备注输入
           Row() {
             Text('备注(如老师)')
               .fontSize(20)
               .fontWeight(FontWeight.Medium)
               .margin({ right: 10 })
             TextInput({
               placeholder: '非必填',
               text:this.course.remark
             })
               .layoutWeight(1)
               .backgroundColor(Color.White)
               .onChange((value: string) => {
                 this.course.remark = value;
               })
           }
           .width('90%')
           .height(60)
           .margin({ bottom: 15 })
           .borderRadius(10)
           .backgroundColor(Color.White)
           .padding(10)
 ​
           // 时段选择(简化版,后续可扩展)
           Column() {
             Row() {
               Text('时段')
                 .fontSize(20)
                 .fontWeight(FontWeight.Medium)
                 .margin({ right: 10 })
               Blank()
               Row() {
                 Button(){
                   Text('-')
                     .fontSize(30)
                     .fontColor(Color.Black)
                 }
                 .width(40)
                 .height(40)
                 .type(ButtonType.Circle)
                 .backgroundColor("#F3F3F3")
                 .onClick(() => {
                   if (this.timeSlotCount > 1) {
                     this.timeSlotCount--;
                   }
                 })
                 Text(this.timeSlotCount.toString())
                   .textAlign(TextAlign.Center)
                   .fontSize(16)
                   .width(40)
                   .height(40)
 ​
                 Button(){
                   Text('+')
                     .fontSize(30)
                     .fontColor(Color.Black)
                 }
                 .width(40)
                 .height(40)
                 .type(ButtonType.Circle)
                 .backgroundColor("#F3F3F3")
                 .onClick(() => {
                   if (this.timeSlotCount < 3) {
                     this.timeSlotCount++;
                   }
                 })
               }
               .margin(10)
               .alignItems(VerticalAlign.Center)
               .justifyContent(FlexAlign.SpaceBetween)
             }
             .width('100%')
             .height(60)
           }
           .backgroundColor(Color.White)
           .borderRadius(10)
           .width('90%')
           .padding({ left: 10, right: 10 })
 ​
           // 上课周数
           Column() {
             Row() {
               Text('上课周数')
                 .fontSize(20)
                 .fontWeight(FontWeight.Medium)
               Blank()
               Text(this.weekRange)
                 .fontSize(14)
                 .fontColor(Color.Gray)
                 .margin(5)
               Image($r('app.media.chevron_right'))
                 .width(20)
                 .height(20)
             }
             .width('100%')
             .height(60)
             .alignItems(VerticalAlign.Center)
 ​
             // 课程背景色
             Row() {
               Text('课程背景色')
                 .fontSize(20)
                 .fontWeight(FontWeight.Medium)
               Blank()
               Circle()
                 .width(20)
                 .height(20)
                 .fill(this.backgroundColor1)
                 .margin(5)
               Image($r('app.media.chevron_right'))
                 .width(20)
                 .height(20)
             }
             .width('100%')
             .height(60)
             .alignItems(VerticalAlign.Center)
           }
           .width('90%')
           .margin(20)
           .borderRadius(10)
           .backgroundColor(Color.White)
           .padding({ left: 10, right: 10 })
         }
         .width('100%')
       }
     }
     .width('100%')
     .height('100%')
     .backgroundColor("#F2F2F4")
   }
 }

4.4 第四步:配置页面路由

编辑 entry/src/main/resources/base/profile/main_pages.json

{
  "src": [
    "pages/Index",
    "pages/AddCoursePage"
  ]
}

4.5 第五步:添加资源文件

4.5.1 添加图标资源

SVG图标下载地址:阿里巴巴矢量图标库

entry/src/main/resources/base/media/ 目录下添加以下SVG图标:

  • chevron_left.svg - 左箭头

  • chevron_right.svg - 右箭头

  • chevron_down.svg - 下箭头

  • x_close.svg - 关闭图标

4.5.2 字符串资源

编辑 entry/src/main/resources/base/element/string.json

{
  "string": [
    {
      "name": "app_name",
      "value": "课程表"
    },
    {
      "name": "module_desc",
      "value": "课程表模块"
    },
    {
      "name": "EntryAbility_desc",
      "value": "课程表应用入口"
    },
    {
      "name": "EntryAbility_label",
      "value": "课程表"
    }
  ]
}

五、核心知识点解析

5.1 状态管理

@State 装饰器

用于组件内部状态管理,状态变化会触发UI更新:

@State currentWeekday: number = 1;
@StorageLink 装饰器

连接全局AppStorage,实现跨组件状态共享:

@StorageLink('name') name: string = '课程表1';
AppStorage

全局状态存储,类似React的Context:

AppStorage.Set('name', '新课程表名');
AppStorage.Get('name');

5.2 组件通信

父子组件传参(@Prop)
// 父组件
GridItemCase({ course: item })

// 子组件
@Prop course: Course;
页面跳转传参
// 跳转
router.pushUrl({
  url: 'pages/AddCoursePage',
  params: this.course
})

// 接收参数
const params: Course = router.getParams() as Course;

5.3 布局组件

Grid(网格布局)

用于创建课程表网格:

Grid() {
  // 内容
}
.columnsTemplate('13fr 18fr 18fr 18fr 18fr 18fr')  // 6列,比例布局
Stack(层叠布局)

用于叠加详情弹窗:

Stack() {
  Column() { /* 主内容 */ }
  if (this.showDetails) {
    Details()  /* 详情弹窗 */
  }
}

5.4 生命周期

  • aboutToAppear(): 组件即将出现时调用

  • onPageShow(): 页面显示时调用

  • onPageHide(): 页面隐藏时调用

六、运行与测试

6.1 运行应用

  1. 连接设备或启动模拟器

  2. 点击 Run 按钮(绿色三角形)或按 Shift+F10

  3. 等待编译完成,应用自动安装运行

6.2 测试功能

  1. 查看课程表:启动后应看到空白课程表

  2. 添加课程

    • 点击空白单元格,出现"+"号

    • 再次点击进入添加页面

    • 填写课程信息,点击"完成"

  3. 查看详情:点击已有课程,查看详情弹窗

  4. 编辑课程:在详情页点击"编辑",修改信息

七、常见问题解决

7.1 编译错误

问题:找不到资源文件 $r('app.media.xxx')

解决:检查资源文件路径和名称是否正确

7.2 页面跳转失败

问题router.pushUrl 报错

解决:检查 main_pages.json 中是否注册了页面

7.3 状态更新不生效

问题:修改数据后UI不更新

解决:确保使用了 @State@StorageLink 装饰器

7.4 课程数据丢失

问题:应用重启后课程消失

解决:当前使用内存存储,需要添加持久化存储(后续可扩展)

八、学习资源

Logo

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

更多推荐