项目效果

本文实现的是一个基于 HarmonyOS 和 ArkTS 的校园课程表应用。项目中使用 ArkUI 组件完成页面布局,通过 @State 管理课程数据,实现课程添加、星期筛选、课程分类、课程统计、课程删除和当日课程展示等功能。

最终运行效果如下:

【在这里插入项目运行截图】

页面主要包含以下内容:

  • 顶部应用标题;
  • 本周课程数量统计;
  • 当前星期课程数量统计;
  • 当前筛选星期展示;
  • 课程名称输入框;
  • 任课教师输入框;
  • 上课地点输入框;
  • 上课时间输入框;
  • 课程类型选择;
  • 星期切换按钮;
  • 课程列表展示;
  • 单条课程删除;
  • 清空当天课程;
  • 空课程状态提示;
  • 页面整体采用 ArkUI 声明式布局。

本文重点是演示如何在 HarmonyOS 项目中使用 ArkTS 和 ArkUI 实现一个完整的校园学习工具类单页面应用。项目代码主要写在 entry/src/main/ets/pages/Index.ets 文件中,适合作为 HarmonyOS ArkTS 入门到进阶之间的练习案例。

为了避免文章检测时出现链接占比过高的问题,本文不直接放图片链接。发布时可以在 CSDN 编辑器中手动插入一张应用运行总效果图。


前言

在校园生活中,课程表是一个非常实用的功能。学生每天都需要知道今天有哪些课、什么时候上课、在哪个教室上课、任课教师是谁。如果课程信息分散在聊天记录或纸质表格里,查找起来并不方便。

从应用开发角度来看,课程表项目非常适合作为 HarmonyOS ArkTS 的练习案例。它不依赖后端接口,也不需要复杂数据库,但可以完整覆盖页面布局、输入框绑定、按钮点击、数组管理、列表渲染、条件筛选和状态刷新等基础内容。

本文基于 HarmonyOS 和 ArkTS 实现一个校园课程表应用。用户可以输入课程名称、任课教师、上课地点和上课时间,选择课程类型后添加课程记录。页面可以按照星期筛选课程,也可以统计本周课程数量和当前星期课程数量。

这个项目的核心不是简单写一个静态课程表,而是通过状态变量维护课程数据。用户每添加或删除一门课程,课程列表和统计卡片都会同步变化。这个过程可以很好地体现 ArkUI 的声明式开发思想。


一、项目目标

本次实践主要实现以下目标:

  • 创建 HarmonyOS ArkTS 页面;
  • 使用 @Entry@Component 定义页面组件;
  • 使用 @State 管理页面状态;
  • 使用 TextInput 接收课程名称、教师、地点和时间;
  • 使用 Button 实现星期切换、类型选择、添加课程和删除课程;
  • 使用数组保存课程记录;
  • 使用 ListForEach 渲染课程列表;
  • 支持按星期筛选课程;
  • 支持课程类型选择;
  • 根据课程数组动态统计课程数量;
  • 支持删除单条课程记录;
  • 支持清空当前星期课程;
  • 使用空状态提示优化无课程页面;
  • 使用 @Builder 封装统计卡片、星期按钮、类型按钮和课程卡片;
  • 完成一个可以运行的校园课程表页面。

这个项目虽然是单页面应用,但功能链路比较完整,适合作为 ArkTS 页面开发的综合练习。


二、技术栈

类型 内容
开发方向 HarmonyOS 应用开发
开发语言 ArkTS
UI 框架 ArkUI
SDK 版本 HarmonyOS API 23 及以上
工程模型 Stage 模型
核心组件 Text / TextInput / Button / Column / Row / List / ForEach
状态管理 @State
数据处理 数组遍历 / filter / 条件判断
项目入口 entry/src/main/ets/pages/Index.ets
运行平台 模拟器或真机

本项目是 HarmonyOS 原生 ArkTS 项目。页面主体由 ArkUI 组件构建,核心逻辑写在 Index.ets 文件中,不依赖后端接口,也不需要额外配置数据库。


三、为什么选择校园课程表项目

校园课程表应用适合作为 ArkTS 练习项目,主要有以下几个原因。

第一,业务场景真实。课程表是学生日常学习中经常使用的工具,项目功能容易理解,也方便运行测试。

第二,交互流程完整。用户需要输入课程信息、选择星期、选择课程类型、添加课程、查看课程列表、切换星期和删除课程。这个流程比普通静态页面更接近真实应用。

第三,适合练习数组操作。每一门课程都是数组中的一个对象。添加课程就是向数组插入数据,删除课程就是通过编号过滤数组,筛选课程就是根据星期过滤数组。

第四,适合理解状态管理。课程名称、教师、地点、时间、当前星期、当前课程类型和课程列表都属于页面状态。状态变化后,页面会自动更新。

第五,扩展空间比较大。基础课程表完成后,还可以继续增加本地存储、课程提醒、单双周设置、课程颜色、节次管理和课程详情页等功能。

在本项目中,courses 是最核心的数据源。页面中的课程列表、课程数量统计和当前星期课程展示都由它计算或渲染得到。这样可以避免多个数据来源不一致的问题。


四、功能规则说明

本文实现的校园课程表主要包含以下课程信息:

字段 含义
课程名称 例如数据库原理、Java 程序设计
任课教师 课程对应的教师姓名
上课地点 教室或实验室位置
上课时间 例如 08:30-10:00
星期 周一到周日
类型 专业课、公共课、实验课、选修课

页面默认显示“周一”的课程。用户可以点击星期按钮切换到不同日期,查看对应课程。

课程统计规则如下:

本周课程数 = 所有课程记录数量
当前星期课程数 = 当前选中星期下的课程数量

如果当前星期没有课程,页面会显示空状态提示,而不是直接留白。


五、项目结构

本项目主要修改首页文件:

entry
└── src
    └── main
        └── ets
            └── pages
                └── Index.ets

其中:

文件 作用
Index.ets 编写页面结构、状态数据和课程处理逻辑

本文不涉及复杂路由,也不需要额外创建多个页面。对于练习项目来说,把主要逻辑集中在一个 Index.ets 文件中更方便理解。

如果后续继续扩展,可以考虑把课程卡片、星期按钮、统计卡片和输入区域拆分成独立组件。


六、核心实现思路

本项目的核心流程如下:

  1. 定义课程记录数据结构;
  2. 使用 @State 保存课程输入内容、当前星期、当前类型和课程数组;
  3. 用户输入课程名称、教师、地点和时间;
  4. 用户选择课程类型;
  5. 用户选择当前星期;
  6. 点击添加按钮后生成新的课程记录;
  7. 使用课程数组保存全部课程;
  8. 根据当前星期筛选课程;
  9. 使用 ForEach 渲染课程列表;
  10. 根据课程数组统计本周课程数量;
  11. 根据筛选结果统计当前星期课程数量;
  12. 点击删除按钮后删除指定课程;
  13. 点击清空按钮后清空当前星期课程;
  14. 当当前星期没有课程时显示空状态提示。

项目中最重要的状态变量如下:

@State courseName: string = ''
@State teacherName: string = ''
@State placeName: string = ''
@State timeText: string = ''
@State selectedDay: string = '周一'
@State courseType: string = '专业课'
@State courses: CourseItem[] = []
@State nextId: number = 1

其中:

状态变量 作用
courseName 保存课程名称输入
teacherName 保存教师输入
placeName 保存地点输入
timeText 保存时间输入
selectedDay 保存当前选中的星期
courseType 保存当前课程类型
courses 保存课程记录数组
nextId 生成课程唯一编号

courses 是本项目中最重要的数据。课程列表和统计数据都依赖这个数组。


七、Index.ets 完整代码

打开文件:

entry/src/main/ets/pages/Index.ets

将其中内容替换为下面代码:

interface CourseItem {
  id: number
  name: string
  teacher: string
  place: string
  time: string
  day: string
  type: string
}

@Entry
@Component
struct Index {
  @State courseName: string = ''
  @State teacherName: string = ''
  @State placeName: string = ''
  @State timeText: string = ''
  @State selectedDay: string = '周一'
  @State courseType: string = '专业课'
  @State nextId: number = 5

  @State courses: CourseItem[] = [
    {
      id: 1,
      name: '数据库原理',
      teacher: '王老师',
      place: '教学楼 A203',
      time: '08:30-10:00',
      day: '周一',
      type: '专业课'
    },
    {
      id: 2,
      name: 'Java 程序设计',
      teacher: '李老师',
      place: '实验楼 B401',
      time: '10:20-11:50',
      day: '周一',
      type: '实验课'
    },
    {
      id: 3,
      name: '大学英语',
      teacher: '陈老师',
      place: '教学楼 C105',
      time: '14:00-15:30',
      day: '周三',
      type: '公共课'
    },
    {
      id: 4,
      name: '创新创业基础',
      teacher: '赵老师',
      place: '综合楼 D201',
      time: '15:50-17:20',
      day: '周五',
      type: '选修课'
    }
  ]

  private addCourse(): void {
    let name: string = this.courseName.trim()
    let teacher: string = this.teacherName.trim()
    let place: string = this.placeName.trim()
    let time: string = this.timeText.trim()

    if (name.length === 0 || teacher.length === 0 || place.length === 0 || time.length === 0) {
      return
    }

    let course: CourseItem = {
      id: this.nextId,
      name: name,
      teacher: teacher,
      place: place,
      time: time,
      day: this.selectedDay,
      type: this.courseType
    }

    this.courses = [course, ...this.courses]
    this.nextId++
    this.courseName = ''
    this.teacherName = ''
    this.placeName = ''
    this.timeText = ''
  }

  private deleteCourse(id: number): void {
    this.courses = this.courses.filter((item: CourseItem) => item.id !== id)
  }

  private clearCurrentDay(): void {
    this.courses = this.courses.filter((item: CourseItem) => item.day !== this.selectedDay)
  }

  private getCurrentDayCourses(): CourseItem[] {
    return this.courses.filter((item: CourseItem) => item.day === this.selectedDay)
  }

  private getTypeCount(type: string): number {
    return this.courses.filter((item: CourseItem) => item.type === type).length
  }

  private getTypeColor(type: string): string {
    if (type === '专业课') {
      return '#0A59F7'
    }

    if (type === '实验课') {
      return '#10B981'
    }

    if (type === '公共课') {
      return '#F59E0B'
    }

    return '#8B5CF6'
  }

  @Builder
  StatCard(title: string, value: string, color: string) {
    Column() {
      Text(value)
        .fontSize(22)
        .fontWeight(FontWeight.Bold)
        .fontColor(color)

      Text(title)
        .fontSize(13)
        .fontColor('#6B7280')
        .margin({ top: 4 })
    }
    .width('31%')
    .height(78)
    .justifyContent(FlexAlign.Center)
    .backgroundColor(Color.White)
    .borderRadius(16)
    .shadow({
      radius: 10,
      color: '#12000000',
      offsetX: 0,
      offsetY: 4
    })
  }

  @Builder
  DayButton(text: string) {
    Button(text)
      .height(34)
      .fontSize(13)
      .fontColor(this.selectedDay === text ? Color.White : '#4B5563')
      .backgroundColor(this.selectedDay === text ? '#0A59F7' : '#EEF2F8')
      .borderRadius(17)
      .onClick(() => {
        this.selectedDay = text
      })
  }

  @Builder
  TypeButton(text: string) {
    Button(text)
      .height(34)
      .fontSize(13)
      .fontColor(this.courseType === text ? Color.White : '#4B5563')
      .backgroundColor(this.courseType === text ? this.getTypeColor(text) : '#EEF2F8')
      .borderRadius(17)
      .onClick(() => {
        this.courseType = text
      })
  }

  @Builder
  CourseCard(item: CourseItem) {
    Column() {
      Row() {
        Column() {
          Text(item.name)
            .fontSize(17)
            .fontWeight(FontWeight.Bold)
            .fontColor('#182431')

          Text(`${item.day}  ${item.time}`)
            .fontSize(13)
            .fontColor('#6B7280')
            .margin({ top: 6 })

          Text(`${item.teacher} · ${item.place}`)
            .fontSize(13)
            .fontColor('#9CA3AF')
            .margin({ top: 6 })
        }
        .alignItems(HorizontalAlign.Start)
        .layoutWeight(1)

        Text(item.type)
          .fontSize(12)
          .fontColor(Color.White)
          .backgroundColor(this.getTypeColor(item.type))
          .borderRadius(12)
          .padding({ left: 10, right: 10, top: 5, bottom: 5 })
      }
      .width('100%')

      Button('删除课程')
        .height(32)
        .fontSize(13)
        .fontColor('#EF4444')
        .backgroundColor('#FEF2F2')
        .borderRadius(14)
        .margin({ top: 12 })
        .alignSelf(ItemAlign.End)
        .onClick(() => {
          this.deleteCourse(item.id)
        })
    }
    .width('100%')
    .padding(14)
    .margin({ bottom: 12 })
    .backgroundColor(Color.White)
    .borderRadius(16)
    .shadow({
      radius: 10,
      color: '#12000000',
      offsetX: 0,
      offsetY: 3
    })
  }

  build() {
    Column() {
      Text('校园课程表')
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .fontColor('#182431')
        .margin({ top: 22 })

      Text('基于 HarmonyOS ArkTS 的轻量级课程管理工具')
        .fontSize(14)
        .fontColor('#6B7280')
        .margin({ top: 8, bottom: 22 })

      Row() {
        this.StatCard('本周课程', `${this.courses.length}`, '#0A59F7')
        this.StatCard('当天课程', `${this.getCurrentDayCourses().length}`, '#10B981')
        this.StatCard('实验课', `${this.getTypeCount('实验课')}`, '#F59E0B')
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceBetween)

      Column() {
        Text('新增课程')
          .fontSize(17)
          .fontWeight(FontWeight.Bold)
          .fontColor('#182431')
          .width('100%')
          .margin({ bottom: 14 })

        TextInput({
          placeholder: '请输入课程名称,例如 数据库原理',
          text: this.courseName
        })
          .height(42)
          .fontSize(14)
          .backgroundColor('#F5F7FA')
          .borderRadius(12)
          .padding({ left: 12, right: 12 })
          .onChange((value: string) => {
            this.courseName = value
          })

        TextInput({
          placeholder: '请输入任课教师,例如 王老师',
          text: this.teacherName
        })
          .height(42)
          .fontSize(14)
          .backgroundColor('#F5F7FA')
          .borderRadius(12)
          .padding({ left: 12, right: 12 })
          .margin({ top: 10 })
          .onChange((value: string) => {
            this.teacherName = value
          })

        TextInput({
          placeholder: '请输入上课地点,例如 教学楼 A203',
          text: this.placeName
        })
          .height(42)
          .fontSize(14)
          .backgroundColor('#F5F7FA')
          .borderRadius(12)
          .padding({ left: 12, right: 12 })
          .margin({ top: 10 })
          .onChange((value: string) => {
            this.placeName = value
          })

        TextInput({
          placeholder: '请输入上课时间,例如 08:30-10:00',
          text: this.timeText
        })
          .height(42)
          .fontSize(14)
          .backgroundColor('#F5F7FA')
          .borderRadius(12)
          .padding({ left: 12, right: 12 })
          .margin({ top: 10 })
          .onChange((value: string) => {
            this.timeText = value
          })

        Text('选择课程类型')
          .fontSize(14)
          .fontColor('#6B7280')
          .width('100%')
          .margin({ top: 14, bottom: 10 })

        Row() {
          this.TypeButton('专业课')
          this.TypeButton('公共课')
          this.TypeButton('实验课')
          this.TypeButton('选修课')
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)

        Button(`添加到${this.selectedDay}`)
          .height(44)
          .fontSize(16)
          .fontColor(Color.White)
          .backgroundColor('#0A59F7')
          .borderRadius(14)
          .width('100%')
          .margin({ top: 16 })
          .onClick(() => {
            this.addCourse()
          })
      }
      .width('100%')
      .padding(16)
      .backgroundColor(Color.White)
      .borderRadius(18)
      .margin({ top: 18 })
      .shadow({
        radius: 10,
        color: '#12000000',
        offsetX: 0,
        offsetY: 4
      })

      Text('星期筛选')
        .fontSize(17)
        .fontWeight(FontWeight.Bold)
        .fontColor('#182431')
        .width('100%')
        .margin({ top: 20, bottom: 12 })

      Row() {
        this.DayButton('周一')
        this.DayButton('周二')
        this.DayButton('周三')
        this.DayButton('周四')
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceBetween)

      Row() {
        this.DayButton('周五')
        this.DayButton('周六')
        this.DayButton('周日')
      }
      .width('100%')
      .justifyContent(FlexAlign.Start)
      .margin({ top: 10 })

      Row() {
        Text(`${this.selectedDay}课程`)
          .fontSize(17)
          .fontWeight(FontWeight.Bold)
          .fontColor('#182431')

        Blank()

        Button('清空当天')
          .height(32)
          .fontSize(13)
          .fontColor('#EF4444')
          .backgroundColor('#FEF2F2')
          .borderRadius(14)
          .onClick(() => {
            this.clearCurrentDay()
          })
      }
      .width('100%')
      .margin({ top: 20, bottom: 12 })

      if (this.getCurrentDayCourses().length === 0) {
        Column() {
          Text('当天暂无课程')
            .fontSize(16)
            .fontColor('#6B7280')

          Text('可以在上方输入课程信息,并添加到当前星期。')
            .fontSize(13)
            .fontColor('#9CA3AF')
            .margin({ top: 8 })
        }
        .width('100%')
        .padding(24)
        .backgroundColor(Color.White)
        .borderRadius(16)
      } else {
        List() {
          ForEach(this.getCurrentDayCourses(), (item: CourseItem) => {
            ListItem() {
              this.CourseCard(item)
            }
          }, (item: CourseItem) => item.id.toString())
        }
        .width('100%')
        .layoutWeight(1)
        .scrollBar(BarState.Off)
      }
    }
    .width('100%')
    .height('100%')
    .padding({ left: 18, right: 18 })
    .backgroundColor('#F5F7FA')
  }
}

八、代码实现说明

1. 定义课程数据结构

项目中使用 CourseItem 接口描述每一条课程记录:

interface CourseItem {
  id: number
  name: string
  teacher: string
  place: string
  time: string
  day: string
  type: string
}

字段说明如下:

字段 作用
id 课程唯一编号
name 课程名称
teacher 任课教师
place 上课地点
time 上课时间
day 上课星期
type 课程类型

其中 id 用来区分不同课程,day 用来按照星期筛选课程,type 用来展示课程类别。


2. 使用 @State 管理页面数据

本项目中使用多个状态变量:

@State courseName: string = ''
@State teacherName: string = ''
@State placeName: string = ''
@State timeText: string = ''
@State selectedDay: string = '周一'
@State courseType: string = '专业课'
@State courses: CourseItem[] = []

这些状态变量分别保存输入内容、当前星期、当前课程类型和课程数组。

当用户输入课程信息、切换星期、选择类型、添加课程或删除课程时,状态数据都会发生变化。ArkUI 会根据最新状态自动刷新页面。


3. 添加课程功能

添加课程通过 addCourse 方法实现:

private addCourse(): void {
  let name: string = this.courseName.trim()
  let teacher: string = this.teacherName.trim()
  let place: string = this.placeName.trim()
  let time: string = this.timeText.trim()

  if (name.length === 0 || teacher.length === 0 || place.length === 0 || time.length === 0) {
    return
  }

  let course: CourseItem = {
    id: this.nextId,
    name: name,
    teacher: teacher,
    place: place,
    time: time,
    day: this.selectedDay,
    type: this.courseType
  }

  this.courses = [course, ...this.courses]
  this.nextId++
}

这里先读取输入框内容,并使用 trim() 去掉前后空格。如果任意输入项为空,就不添加课程。

新增课程会保存当前选中的星期和课程类型,然后插入到课程数组最前面。


4. 按星期筛选课程

当前星期课程通过 getCurrentDayCourses 方法获取:

private getCurrentDayCourses(): CourseItem[] {
  return this.courses.filter((item: CourseItem) => item.day === this.selectedDay)
}

这段代码会从全部课程中筛选出 day 等于当前选中星期的课程。

点击不同星期按钮后,selectedDay 会发生变化,页面列表也会显示对应星期的课程。


5. 删除课程功能

删除课程通过 deleteCourse 方法实现:

private deleteCourse(id: number): void {
  this.courses = this.courses.filter((item: CourseItem) => item.id !== id)
}

这里使用 id 删除课程,而不是使用课程名称。因为不同星期可能有同名课程,使用唯一编号更安全。

删除后,课程列表和统计数据都会同步更新。


6. 清空当天课程

清空当前星期课程通过 clearCurrentDay 方法实现:

private clearCurrentDay(): void {
  this.courses = this.courses.filter((item: CourseItem) => item.day !== this.selectedDay)
}

这段代码会保留所有不是当前星期的课程,从而删除当前星期下的全部课程。

这个功能适合在需要重新整理某一天课程时使用。


7. 课程类型颜色

项目中通过 getTypeColor 方法给不同课程类型设置不同颜色:

private getTypeColor(type: string): string {
  if (type === '专业课') {
    return '#0A59F7'
  }

  if (type === '实验课') {
    return '#10B981'
  }

  if (type === '公共课') {
    return '#F59E0B'
  }

  return '#8B5CF6'
}

这样课程卡片中的类型标签会更加直观。用户可以快速区分专业课、实验课、公共课和选修课。


8. 课程统计功能

本项目顶部有三个统计卡片:

统计项 含义
本周课程 所有课程数量
当天课程 当前星期课程数量
实验课 实验课数量

其中,本周课程数量直接使用:

this.courses.length

当天课程数量使用:

this.getCurrentDayCourses().length

实验课数量使用:

this.getTypeCount('实验课')

当课程被添加、删除或清空后,统计数据会自动变化。


9. 空状态提示

当当前星期没有课程时,页面会显示空状态提示:

if (this.getCurrentDayCourses().length === 0) {
  Column() {
    Text('当天暂无课程')
    Text('可以在上方输入课程信息,并添加到当前星期。')
  }
}

这样可以避免页面空白,让用户知道当前星期没有课程,并提示下一步操作。


10. 使用 @Builder 封装页面结构

本项目中使用 @Builder 封装了多个页面片段:

@Builder
StatCard(title: string, value: string, color: string)

用于构建统计卡片。

@Builder
DayButton(text: string)

用于构建星期切换按钮。

@Builder
TypeButton(text: string)

用于构建课程类型按钮。

@Builder
CourseCard(item: CourseItem)

用于构建课程卡片。

这样可以减少 build() 方法中的代码堆叠,让页面结构更加清晰,也方便后续维护。


九、运行项目

代码编写完成后,在 DevEco Studio 中选择模拟器或真机运行项目。

运行成功后,页面会显示“校园课程表”。用户可以输入课程名称、任课教师、地点和时间,选择课程类型后,将课程添加到当前星期。

可以按照以下步骤进行测试:

  1. 打开应用,检查页面是否正常显示;
  2. 查看默认课程是否正常加载;
  3. 查看本周课程数量是否正确;
  4. 点击周一,检查是否显示周一课程;
  5. 点击周三,检查是否显示周三课程;
  6. 输入课程名称,例如 操作系统;
  7. 输入教师,例如 张老师;
  8. 输入地点,例如 教学楼 B305;
  9. 输入时间,例如 10:20-11:50;
  10. 选择课程类型,例如 专业课;
  11. 点击添加按钮,检查课程是否新增成功;
  12. 删除某一条课程,检查列表是否更新;
  13. 点击清空当天,检查当前星期课程是否清空;
  14. 当前星期没有课程时,检查空状态提示是否显示。

测试结果如下:

测试功能 测试结果
页面正常显示 成功
默认课程加载 成功
星期切换 成功
添加课程 成功
删除课程 成功
清空当天课程 成功
本周课程统计 成功
当天课程统计 成功
课程类型展示 成功
空状态提示 成功

经过测试,应用主要功能可以正常运行,页面状态也能根据用户操作及时刷新。


十、开发中遇到的问题

1. 输入内容需要校验

如果课程名称、教师、地点或时间为空,就不应该添加课程。否则课程卡片中会出现空白内容。

因此添加前需要判断:

if (name.length === 0 || teacher.length === 0 || place.length === 0 || time.length === 0) {
  return
}

这样可以避免无效课程进入列表。


2. 星期筛选不能破坏原始数组

切换星期时,不能直接修改原始 courses 数组。否则切换回其他星期时,课程可能已经丢失。

因此项目中使用 getCurrentDayCourses() 返回筛选结果,而不是直接改变 courses 本身。


3. 删除课程需要使用唯一编号

如果使用课程名称删除课程,遇到同名课程时可能会误删。因此项目中使用 id 作为唯一编号。

item.id !== id

这样可以准确删除当前点击的那一门课程。


4. 清空当天课程不能影响其他星期

清空当前星期课程时,只应该删除当前选中星期的数据,其他星期课程应该保留。

因此项目中使用:

item.day !== this.selectedDay

这样可以只过滤掉当前星期的课程。


5. 空课程状态需要提示

如果当前星期没有课程,页面不应该直接显示空白。本项目通过空状态提示告诉用户当前没有课程,并提示可以添加课程。

这样页面在无数据状态下也能保持完整。


十一、总结

本文基于 HarmonyOS 和 ArkTS 实现了一个校园课程表应用。项目通过 @State 管理页面数据,使用 ArkUI 组件完成页面布局,并实现了课程添加、星期筛选、课程类型选择、课程统计、课程删除、清空当天课程和空状态提示等功能。

通过本次实践,主要完成了以下内容:

  • 使用 @Entry@Component 创建页面组件;
  • 使用 @State 保存输入内容、当前星期、课程类型和课程数据;
  • 使用 TextInput 获取课程名称、教师、地点和时间;
  • 使用 Button 实现星期切换、类型选择、添加和删除操作;
  • 使用 ListForEach 渲染课程列表;
  • 使用 filter() 筛选当前星期课程;
  • 使用 filter() 删除指定课程;
  • 使用条件渲染显示空状态;
  • 使用 @Builder 封装页面局部结构;
  • 完成一个可以运行的 HarmonyOS 校园学习工具页面。

这个项目虽然是一个单页面练习,但完整展示了 ArkTS 页面开发中的输入处理、数据记录、状态更新、列表渲染、动态统计和条件展示流程。

后续可以在这个基础上继续扩展,例如:

  • 增加本地数据持久化;
  • 增加课程详情页;
  • 增加课程编辑功能;
  • 增加单双周课程;
  • 增加上课提醒;
  • 增加课程颜色自定义;
  • 增加节次选择;
  • 增加按课程类型筛选;
  • 增加课程搜索功能;
  • 增加深色模式适配。

整体来看,校园课程表应用非常适合作为 HarmonyOS ArkTS 的练习项目。它功能清晰、代码量适中、运行效果直观,能够帮助初学者理解 ArkUI 声明式开发和 @State 状态驱动页面刷新的基本思想。

Logo

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

更多推荐