鸿蒙教育机构管理系统 课程排期与学员管理【华为云根技术】

举报
鱼弦 发表于 2026/01/08 11:13:40 2026/01/08
【摘要】 一 引言与技术背景教育机构的教务管理正从Excel/分散系统向移动化、智能化、跨设备协同升级。HarmonyOS 的分布式软总线、统一开发框架 ArkUI、原子化服务与本地化数据存储为排课冲突检测、学员全生命周期管理与多端一致体验提供了系统级能力支撑。行业痛点包括:课程排期效率低(教室/教师/时间冲突频发)、学员信息分散(报名/考勤/成绩割裂)、跨设备协同差(课表更新不同步)。鸿蒙的分布式与...

一 引言与技术背景
  • 教育机构的教务管理正从Excel/分散系统移动化、智能化、跨设备协同升级。HarmonyOS 的分布式软总线统一开发框架 ArkUI原子化服务本地化数据存储为排课冲突检测、学员全生命周期管理与多端一致体验提供了系统级能力支撑。
  • 行业痛点包括:课程排期效率低(教室/教师/时间冲突频发)、学员信息分散(报名/考勤/成绩割裂)、跨设备协同差(课表更新不同步)。鸿蒙的分布式与多端一致 UI 能有效缓解上述问题。
  • 高校与头部教培机构已加速鸿蒙原生应用落地,覆盖课程安排、考勤门禁、成绩查询等高频场景,并推出元服务实现“即点即用”,验证了鸿蒙在教育场景的可行性与成熟度。

二 应用使用场景
  • 课程排期管理(教师/教务端):在平板上拖拽式排课,系统自动检测教室/教师/时间冲突,排期完成后自动同步至学员手机端课表。
  • 学员信息管理(管理员端):集中管理报名、班级、考勤、成绩,支持离线查询按班级/课程筛选统计
  • 跨设备协同(学员/教师/智慧屏):教师在智慧屏展示课程大纲,学员用手机扫码加入互动;课表变更多端一致
  • 扩展:对接分布式数据服务实现多设备同步;将排期/学员查询拆分为原子化服务供按需调用。

三 核心特性与原理流程图
  • 核心特性
    • 冲突检测引擎:基于“教室+教师+时间”三维约束的重叠判断,保障排课合法性。
    • 本地优先:使用Preferences持久化课程/学员数据,保障离线可用与快速启动。
    • 多端一致:基于ArkUI声明式 UI,同一代码适配手机/平板/智慧屏
    • 可扩展分布式:可按需接入分布式数据服务原子化服务,实现多设备同步与功能解耦。
  • 原理流程图
    [UI 操作:新增/修改课程或学员]
              |
      [冲突检测:教室/教师/时间重叠]
              |
        [通过?] ——否——> [提示冲突]
              |
            是
              |
      [本地持久化:Preferences 写入]
              |
    [可选:分布式数据同步至其他设备]
              |
        [UI 刷新与通知]

四 环境准备
  • 开发工具:DevEco Studio NEXT(建议最新稳定版),选择ArkTSStage 模型
  • 目标设备:Phone/Tablet(建议 API ≥ 10),授予麦克风/网络/存储等必要权限(如需)。
  • 工程结构建议:
    entry/src/main/ets/
      entryability/
      pages/
        ScheduleManage.ets
        StudentManage.ets
      model/
        Course.ets
        Student.ets
      services/
        ScheduleService.ets
        StudentService.ets
      common/
        utils/
          DateUtil.ets
  • 本地存储选型:优先使用@ohos.data.preferences进行轻量级结构化存储(课程/学员列表 JSON 序列化)。

五 不同场景的代码实现
  • 场景一 课程排期管理(冲突检测 + 本地保存 + 列表展示)
    1. 数据模型 model/Course.ets
    export class Course {
      id: string = '';
      name: string = '';
      teacher: string = '';
      classroom: string = '';
      startTime: string = ''; // HH:mm
      endTime: string = '';   // HH:mm
      dayOfWeek: number = 0; // 0=周一,6=周日
    }
    
    export class Classroom {
      id: string = '';
      name: string = '';
    }
    1. 工具类 common/utils/DateUtil.ets
    // 将 HH:mm 转为分钟数
    export function timeToMinutes(t: string): number {
      const [h, m] = t.split(':').map(Number);
      return h * 60 + m;
    }
    
    // 判断时间区间是否重叠
    export function isTimeOverlap(
      s1: string, e1: string,
      s2: string, e2: string
    ): boolean {
      const start1 = timeToMinutes(s1);
      const end1 = timeToMinutes(e1);
      const start2 = timeToMinutes(s2);
      const end2 = timeToMinutes(e2);
      return !(end1 <= start2 || end2 <= start1);
    }
    1. 排期服务 services/ScheduleService.ets
    import preferences from '@ohos.data.preferences';
    import { BusinessError } from '@ohos.base';
    import { Course } from '../model/Course';
    import { isTimeOverlap } from '../common/utils/DateUtil';
    
    const STORE_KEY = 'courses';
    
    export class ScheduleService {
      private context: Context;
      private storage: preferences.Preferences | null = null;
    
      constructor(context: Context) {
        this.context = context;
      }
    
      async init(): Promise<void> {
        try {
          this.storage = await preferences.getPreferences(this.context, 'course_data');
        } catch (err) {
          console.error('Preferences init failed', err as BusinessError);
        }
      }
    
      async load(): Promise<Course[]> {
        if (!this.storage) await this.init();
        try {
          const json = await this.storage!.get(STORE_KEY, '[]') as string;
          return JSON.parse(json) as Course[];
        } catch (err) {
          console.error('Load courses failed', err as BusinessError);
          return [];
        }
      }
    
      async save(courses: Course[]): Promise<void> {
        if (!this.storage) await this.init();
        try {
          await this.storage!.put(STORE_KEY, JSON.stringify(courses));
          await this.storage!.flush();
        } catch (err) {
          console.error('Save courses failed', err as BusinessError);
        }
      }
    
      checkConflict(newCourse: Course, existing: Course): boolean {
        if (newCourse.id === existing.id) return false; // 自身忽略
        const overlap = isTimeOverlap(
          newCourse.startTime, newCourse.endTime,
          existing.startTime, existing.endTime
        );
        const sameRoom = newCourse.classroom === existing.classroom;
        const sameTeacher = newCourse.teacher === existing.teacher;
        return (sameRoom && overlap) || (sameTeacher && overlap);
      }
    
      async add(course: Course): Promise<boolean> {
        const list = await this.load();
        if (list.some(c => this.checkConflict(newCourse, c))) {
          return false; // 冲突
        }
        course.id = Date.now().toString();
        list.push(course);
        await this.save(list);
        return true;
      }
    
      async update(course: Course): Promise<boolean> {
        const list = await this.load();
        const idx = list.findIndex(c => c.id === course.id);
        if (idx === -1) return false;
        // 冲突检测排除自身
        const others = [...list];
        others.splice(idx, 1);
        if (others.some(c => this.checkConflict(course, c))) {
          return false;
        }
        list[idx] = course;
        await this.save(list);
        return true;
      }
    
      async delete(id: string): Promise<void> {
        const list = await this.load();
        const next = list.filter(c => c.id !== id);
        await this.save(next);
      }
    }
    1. 排期页面 pages/ScheduleManage.ets
    import { Course, Classroom } from '../model/Course';
    import { ScheduleService } from '../services/ScheduleService';
    import { timeToMinutes } from '../common/utils/DateUtil';
    import preferences from '@ohos.data.preferences';
    import { BusinessError } from '@ohos.base';
    import router from '@ohos.router';
    
    @Entry
    @Component
    struct ScheduleManage {
      @State courses: Course[] = [];
      @State classrooms: Classroom[] = [
        { id: 'A101', name: '教室A101' },
        { id: 'B202', name: '教室B202' }
      ];
      @State teachers: string[] = ['张老师', '李老师', '王老师'];
      @State showForm: boolean = false;
      @State editCourse: Course = new Course();
      @State dayOfWeekItems: string[] = ['周一','周二','周三','周四','周五','周六','周日'];
    
      private svc: ScheduleService = new ScheduleService(getContext(this) as common.UIAbilityContext);
    
      aboutToAppear() {
        this.loadCourses();
      }
    
      private async loadCourses() {
        this.courses = await this.svc.load();
      }
    
      private async onAdd() {
        this.editCourse = new Course();
        this.editCourse.dayOfWeek = 0;
        this.editCourse.startTime = '09:00';
        this.editCourse.endTime = '10:30';
        this.showForm = true;
      }
    
      private async onEdit(c: Course) {
        this.editCourse = { ...c };
        this.showForm = true;
      }
    
      private async onDelete(id: string) {
        await this.svc.delete(id);
        await this.loadCourses();
      }
    
      private async onSave() {
        if (!this.editCourse.name || !this.editCourse.teacher || !this.editCourse.classroom) {
          // 简单校验
          return;
        }
        let ok = false;
        if (this.editCourse.id) {
          ok = await this.svc.update(this.editCourse);
        } else {
          ok = await this.svc.add(this.editCourse);
        }
        if (ok) {
          this.showForm = false;
          await this.loadCourses();
        } else {
          // 冲突提示
          AlertDialog.show({
            title: '冲突提示',
            message: '该教室或教师在此时间段已被占用,请调整!',
            confirm: { value: '确定', action: () => {} }
          });
        }
      }
    
      private onCancel() {
        this.showForm = false;
      }
    
      build() {
        Column({ space: 12 }) {
          Row() {
            Text('课程排期管理')
              .fontSize(20)
              .fontWeight(FontWeight.Bold)
              .layoutWeight(1)
            Button('新增')
              .onClick(() => this.onAdd())
          }
          .width('100%')
          .padding({ left: 12, right: 12, top: 8 })
    
          List({ space: 8 }) {
            ForEach(this.courses, (item: Course) => {
              ListItem() {
                Row({ space: 8 }) {
                  Column({ space: 4 }) {
                    Text(item.name)
                      .fontSize(16)
                      .fontWeight(FontWeight.Medium)
                    Text(`${item.teacher} | ${item.classroom}`)
                      .fontSize(12)
                      .fontColor('#666')
                  }
                  .alignItems(HorizontalAlign.Start)
                  .layoutWeight(1)
    
                  Text(this.dayOfWeekItems[item.dayOfWeek])
                    .fontSize(12)
                    .backgroundColor('#EAF6FF')
                    .padding({ left: 6, right: 6, top: 2, bottom: 2 })
                    .borderRadius(4)
    
                  Text(`${item.startTime}-${item.endTime}`)
                    .fontSize(12)
                    .fontColor('#333')
    
                  Row({ space: 4 }) {
                    Button('编辑')
                      .fontSize(12)
                      .onClick(() => this.onEdit(item))
                    Button('删除')
                      .fontSize(12)
                      .backgroundColor('#FF6B6B')
                      .onClick(() => this.onDelete(item.id))
                  }
                }
                .width('100%')
                .padding(8)
                .backgroundColor('#FFF')
                .borderRadius(6)
              }
            }, (item: Course) => item.id)
          }
          .layoutWeight(1)
    
          if (this.showForm) {
            Column({ space: 12 }) {
              Text(this.editCourse.id ? '编辑课程' : '新增课程')
                .fontSize(16)
                .fontWeight(FontWeight.Medium)
    
              TextInput({ placeholder: '课程名称' })
                .onChange(v => this.editCourse.name = v)
    
              Select(this.teachers)
                .selected(this.teachers.indexOf(this.editCourse.teacher))
                .onSelect((idx: number) => {
                  this.editCourse.teacher = this.teachers[idx];
                })
                .width('100%')
    
              Select(this.classrooms.map(c => c.name))
                .selected(this.classrooms.findIndex(c => c.id === this.editCourse.classroom))
                .onSelect((idx: number) => {
                  this.editCourse.classroom = this.classrooms[idx].id;
                })
                .width('100%')
    
              Select(this.dayOfWeekItems)
                .selected(this.editCourse.dayOfWeek)
                .onSelect((idx: number) => {
                  this.editCourse.dayOfWeek = idx;
                })
                .width('100%')
    
              Row({ space: 8 }) {
                TextInput({ placeholder: '开始时间 HH:mm' })
                  .width('45%')
                  .onChange(v => this.editCourse.startTime = v)
                TextInput({ placeholder: '结束时间 HH:mm' })
                  .width('45%')
                  .onChange(v => this.editCourse.endTime = v)
              }
    
              Row({ space: 8 }) {
                Button('取消')
                  .onClick(() => this.onCancel())
                Button('保存')
                  .onClick(() => this.onSave())
              }
            }
            .width('100%')
            .padding(12)
            .backgroundColor('#F7F9FF')
            .borderRadius(8)
          }
        }
        .width('100%')
        .height('100%')
        .padding(12)
      }
    }
  • 场景二 学员信息管理(增删改查 + 按班级筛选)
    1. 数据模型 model/Student.ets
    export class Student {
      id: string = '';
      name: string = '';
      age: number = 0;
      phone: string = '';
      className: string = ''; // 班级
      enrolledCourses: string[] = []; // 已报名课程ID
    }
    1. 学员服务 services/StudentService.ets
    import preferences from '@ohos.data.preferences';
    import { BusinessError } from '@ohos.base';
    import { Student } from '../model/Student';
    
    const STORE_KEY = 'students';
    
    export class StudentService {
      private context: Context;
      private storage: preferences.Preferences | null = null;
    
      constructor(context: Context) {
        this.context = context;
      }
    
      async init(): Promise<void> {
        try {
          this.storage = await preferences.getPreferences(this.context, 'student_data');
        } catch (err) {
          console.error('Preferences init failed', err as BusinessError);
        }
      }
    
      async load(): Promise<Student[]> {
        if (!this.storage) await this.init();
        try {
          const json = await this.storage!.get(STORE_KEY, '[]') as string;
          return JSON.parse(json) as Student[];
        } catch (err) {
          console.error('Load students failed', err as BusinessError);
          return [];
        }
      }
    
      async save(list: Student[]): Promise<void> {
        if (!this.storage) await this.init();
        try {
          await this.storage!.put(STORE_KEY, JSON.stringify(list));
          await this.storage!.flush();
        } catch (err) {
          console.error('Save students failed', err as BusinessError);
        }
      }
    
      async add(s: Student): Promise<void> {
        const list = await this.load();
        s.id = Date.now().toString();
        list.push(s);
        await this.save(list);
      }
    
      async update(s: Student): Promise<void> {
        const list = await this.load();
        const idx = list.findIndex(x => x.id === s.id);
        if (idx !== -1) {
          list[idx] = s;
          await this.save(list);
        }
      }
    
      async delete(id: string): Promise<void> {
        const list = await this.load();
        const next = list.filter(x => x.id !== id);
        await this.save(next);
      }
    }
    1. 学员管理页面 pages/StudentManage.ets
    import { Student } from '../model/Student';
    import { StudentService } from '../services/StudentService';
    import { router } from '@ohos.router';
    
    @Entry
    @Component
    struct StudentManage {
      @State students: Student[] = [];
      @State filtered: Student[] = [];
      @State classes: string[] = ['全部'];
      @State selectedClass: string = '全部';
      @State showForm: boolean = false;
      @State editStudent: Student = new Student();
    
      private svc: StudentService = new StudentService(getContext(this) as common.UIAbilityContext);
    
      aboutToAppear() {
        this.loadStudents();
      }
    
      private async loadStudents() {
        this.students = await this.svc.load();
        this.buildClassOptions();
        this.applyFilter();
      }
    
      private buildClassOptions() {
        const set = new Set<string>();
        this.students.forEach(s => {
          if (s.className) set.add(s.className);
        });
        this.classes = ['全部', ...Array.from(set).sort()];
      }
    
      private applyFilter() {
        if (this.selectedClass === '全部') {
          this.filtered = this.students;
        } else {
          this.filtered = this.students.filter(s => s.className === this.selectedClass);
        }
      }
    
      private async onAdd() {
        this.editStudent = new Student();
        this.showForm = true;
      }
    
      private async onEdit(s: Student) {
        this.editStudent = { ...s };
        this
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。