ClassMG/entry/src/main/ets/pages/ClassPage.ets

893 lines
24 KiB
Plaintext

import { router } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { MonitorDataType } from './HomePage';
import settingsService, { SettingsModel, TextResources } from '../common/SettingsService';
import logManager, { LogCategory, LogEventType } from '../common/logtext';
import { DatabaseService } from '../common/DatabaseService';
import classRoomService, { ClassSessionModel, QuestionOption } from '../common/ClassRoomService';
// 用户角色类型
enum UserRole {
STUDENT = 'student',
TEACHER = 'teacher',
UNKNOWN = 'unknown'
}
@Entry
@Component
struct ClassPage {
@State currentClassInfo: ClassInfo = new ClassInfo('数学', '张老师', 'A101');
@State classData: ClassDataType = {
name: '',
time: '',
content: '',
status: false
};
// 新增状态
@State userRole: UserRole = UserRole.UNKNOWN;
@State classCode: string = '';
@State className: string = '';
@State showCreateClassDialog: boolean = false;
@State showJoinClassDialog: boolean = false;
@State errorMessage: string = '';
@State isLoading: boolean = false;
// 数据库服务
private dbService: DatabaseService = DatabaseService.getInstance();
// 从设置服务获取设置
@State settings: SettingsModel = settingsService.getSettings();
// 获取文本资源
@State texts: TextResources = settingsService.getTextResources();
aboutToAppear() {
// 注册语言变化的回调
settingsService.registerLanguageChangeCallback(() => {
this.texts = settingsService.getTextResources();
});
// 注册主题颜色变化的回调
settingsService.registerColorChangeCallback(() => {
this.settings = settingsService.getSettings();
});
// 检查用户角色
const currentAccount = settingsService.getCurrentAccount();
const category = this.dbService.getUserCategory(currentAccount);
if (category === 'student') {
this.userRole = UserRole.STUDENT;
} else if (category === 'teacher') {
this.userRole = UserRole.TEACHER;
} else {
this.userRole = UserRole.UNKNOWN;
}
// 同步设置到课堂服务
classRoomService.checkIsTeacher();
this.Log_Event('aboutToAppear');
}
build() {
Stack({ alignContent: Alignment.TopStart }) {
Column() {
// 顶部导航栏
Row() {
Text(this.texts.classPageTitle).fontSize(22).fontColor(Color.White).fontWeight(FontWeight.Bold)
}
.width('100%')
.backgroundColor(this.settings.themeColor)
.height(60)
.justifyContent(FlexAlign.Center)
.padding({ left: 20 })
// 页面内容区
Scroll() {
Column() {
// 根据用户角色显示不同界面
if (this.userRole === UserRole.STUDENT) {
this.StudentView()
} else if (this.userRole === UserRole.TEACHER) {
this.TeacherView()
} else {
this.UnknownRoleView()
}
}
.width('100%')
.padding({ left: 16, right: 16 })
}
.layoutWeight(1)
.scrollBar(BarState.Off)
// 底部导航栏
BottomNavigation({
activePage: 'class',
themeColor: this.settings.themeColor,
texts: this.texts
})
}
.width('100%')
.height('100%')
// 创建课堂对话框
if (this.showCreateClassDialog) {
this.CreateClassDialog()
}
// 加入课堂对话框
if (this.showJoinClassDialog) {
this.JoinClassDialog()
}
}
.width('100%')
.height('100%')
}
// 学生视图
@Builder
StudentView() {
Column() {
// 学生上课指引卡片
Column() {
Text("学生上课")
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 16 })
Text("请输入老师提供的课堂暗号进入课堂")
.fontSize(16)
.fontColor('#666')
.margin({ bottom: 20 })
// 暗号输入框
Column({ space: 8 }) {
Text("课堂暗号")
.fontSize(16)
.fontColor('#666666')
.alignSelf(ItemAlign.Start)
TextInput({ placeholder: '请输入4-6位数字暗号' })
.type(InputType.Number)
.maxLength(6)
.placeholderColor('#999999')
.placeholderFont({ size: 16 })
.width('100%')
.height(50)
.fontSize(16)
.fontColor('#333333')
.backgroundColor('#F5F5F5')
.borderRadius(8)
.padding({ left: 16, right: 16 })
.onChange((value: string) => {
this.classCode = value;
this.errorMessage = '';
})
}
.width('100%')
.margin({ bottom: 16 })
// 错误信息
if (this.errorMessage !== '') {
Text(this.errorMessage)
.fontSize(14)
.fontColor('#FF0000')
.width('100%')
.margin({ bottom: 16 })
}
// 加入课堂按钮
Button("加入课堂")
.width('100%')
.height(50)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.backgroundColor(this.settings.themeColor)
.borderRadius(8)
.fontColor(Color.White)
.enabled(!this.isLoading)
.opacity(this.isLoading ? 0.6 : 1)
.onClick(() => {
this.joinClass();
})
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(20)
.shadow({ radius: 6, color: '#eeeeee' })
.margin({ top: 20, bottom: 20 })
// 当前课程信息卡片(只在没有正在进行的课程时显示)
Column() {
Text("近期课程").fontSize(20).fontWeight(FontWeight.Bold).margin({ bottom: 16 })
Row() {
Column() {
Text(this.currentClassInfo.name)
.fontSize(18)
.fontWeight(FontWeight.Bold)
Text(`${this.texts.teacher}: ${this.currentClassInfo.teacher}`)
.fontSize(14)
.fontColor('#666')
.margin({ top: 8 })
Text(`${this.texts.classroom}: ${this.currentClassInfo.room}`)
.fontSize(14)
.fontColor('#666')
.margin({ top: 4 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
Column() {
Text("未开始")
.fontSize(14)
.fontColor('#999')
.backgroundColor('#f5f5f5')
.borderRadius(12)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
}
.justifyContent(FlexAlign.Center)
}
.width('100%')
.padding(16)
.borderRadius(8)
.backgroundColor('#f9f9f9')
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(20)
.shadow({ radius: 6, color: '#eeeeee' })
}
}
// 教师视图
@Builder
TeacherView() {
Column() {
// 教师上课指引卡片
Column() {
Text("教师上课")
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 16 })
Text("点击下方按钮创建新课堂")
.fontSize(16)
.fontColor('#666')
.margin({ bottom: 20 })
// 创建课堂按钮
Button("创建新课堂")
.width('100%')
.height(50)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.backgroundColor(this.settings.themeColor)
.borderRadius(8)
.fontColor(Color.White)
.enabled(!this.isLoading)
.opacity(this.isLoading ? 0.6 : 1)
.onClick(() => {
this.showCreateClassDialog = true;
})
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(20)
.shadow({ radius: 6, color: '#eeeeee' })
.margin({ top: 20, bottom: 20 })
// 历史课堂记录
Column() {
Text("历史课堂").fontSize(20).fontWeight(FontWeight.Bold).margin({ bottom: 16 })
// 示例历史课堂卡片
Row() {
Column() {
Text("高等数学")
.fontSize(18)
.fontWeight(FontWeight.Bold)
Text("课堂暗号: 123456")
.fontSize(14)
.fontColor('#666')
.margin({ top: 8 })
Text("2025-04-01 10:30")
.fontSize(14)
.fontColor('#666')
.margin({ top: 4 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
Column() {
Text("已结束")
.fontSize(14)
.fontColor('#999')
.backgroundColor('#f5f5f5')
.borderRadius(12)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
}
.justifyContent(FlexAlign.Center)
}
.width('100%')
.padding(16)
.borderRadius(8)
.backgroundColor('#f9f9f9')
.margin({ bottom: 12 })
// 示例历史课堂卡片
Row() {
Column() {
Text("线性代数")
.fontSize(18)
.fontWeight(FontWeight.Bold)
Text("课堂暗号: 654321")
.fontSize(14)
.fontColor('#666')
.margin({ top: 8 })
Text("2025-03-30 14:30")
.fontSize(14)
.fontColor('#666')
.margin({ top: 4 })
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
Column() {
Text("已结束")
.fontSize(14)
.fontColor('#999')
.backgroundColor('#f5f5f5')
.borderRadius(12)
.padding({ left: 12, right: 12, top: 6, bottom: 6 })
}
.justifyContent(FlexAlign.Center)
}
.width('100%')
.padding(16)
.borderRadius(8)
.backgroundColor('#f9f9f9')
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(20)
.shadow({ radius: 6, color: '#eeeeee' })
}
}
// 未知角色视图
@Builder
UnknownRoleView() {
Column() {
Text("用户角色未知")
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 16 })
Text("系统无法识别您的用户角色,请联系管理员或重新登录")
.fontSize(16)
.fontColor('#666')
.margin({ bottom: 20 })
Button("返回登录")
.width('60%')
.height(50)
.fontSize(18)
.fontWeight(FontWeight.Medium)
.backgroundColor(this.settings.themeColor)
.borderRadius(8)
.fontColor(Color.White)
.onClick(() => {
// 返回登录页
router.replaceUrl({
url: 'pages/login'
});
})
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(20)
.shadow({ radius: 6, color: '#eeeeee' })
.margin({ top: 20 })
}
// 创建课堂对话框
@Builder
CreateClassDialog() {
// 遮罩层
Column() {
// 对话框内容
Column() {
// 标题
Text("创建新课堂")
.fontSize(22)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 24 })
// 课堂名称
Column({ space: 8 }) {
Text("课堂名称")
.fontSize(16)
.fontColor('#666666')
.alignSelf(ItemAlign.Start)
TextInput({ placeholder: '请输入课堂名称' })
.maxLength(20)
.placeholderColor('#999999')
.placeholderFont({ size: 16 })
.width('100%')
.height(50)
.fontSize(16)
.fontColor('#333333')
.backgroundColor('#F5F5F5')
.borderRadius(8)
.padding({ left: 16, right: 16 })
.onChange((value: string) => {
this.className = value;
})
}
.width('100%')
.margin({ bottom: 16 })
// 课堂暗号
Column({ space: 8 }) {
Text("课堂暗号")
.fontSize(16)
.fontColor('#666666')
.alignSelf(ItemAlign.Start)
TextInput({ placeholder: '请输入4-6位数字暗号' })
.type(InputType.Number)
.maxLength(6)
.placeholderColor('#999999')
.placeholderFont({ size: 16 })
.width('100%')
.height(50)
.fontSize(16)
.fontColor('#333333')
.backgroundColor('#F5F5F5')
.borderRadius(8)
.padding({ left: 16, right: 16 })
.onChange((value: string) => {
this.classCode = value;
})
}
.width('100%')
.margin({ bottom: 16 })
// 错误信息
if (this.errorMessage !== '') {
Text(this.errorMessage)
.fontSize(14)
.fontColor('#FF0000')
.width('100%')
.margin({ bottom: 16 })
}
// 按钮组
Row({ space: 20 }) {
Button("取消")
.width('45%')
.height(45)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.backgroundColor('#f5f5f5')
.borderRadius(8)
.fontColor('#333')
.onClick(() => {
this.showCreateClassDialog = false;
this.errorMessage = '';
})
Button("创建")
.width('45%')
.height(45)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.backgroundColor(this.settings.themeColor)
.borderRadius(8)
.fontColor(Color.White)
.enabled(!this.isLoading)
.opacity(this.isLoading ? 0.6 : 1)
.onClick(() => {
this.createClass();
})
}
.width('100%')
}
.width('85%')
.backgroundColor(Color.White)
.borderRadius(16)
.padding(24)
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
.justifyContent(FlexAlign.Center)
.onClick(() => {
// 点击遮罩不关闭对话框
// 只能通过按钮关闭
})
}
// 加入课堂对话框
@Builder
JoinClassDialog() {
// 遮罩层
Column() {
// 对话框内容
Column() {
// 标题
Text("加入课堂")
.fontSize(22)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 24 })
// 课堂暗号
Column({ space: 8 }) {
Text("课堂暗号")
.fontSize(16)
.fontColor('#666666')
.alignSelf(ItemAlign.Start)
TextInput({ placeholder: '请输入4-6位数字暗号' })
.type(InputType.Number)
.maxLength(6)
.placeholderColor('#999999')
.placeholderFont({ size: 16 })
.width('100%')
.height(50)
.fontSize(16)
.fontColor('#333333')
.backgroundColor('#F5F5F5')
.borderRadius(8)
.padding({ left: 16, right: 16 })
.onChange((value: string) => {
this.classCode = value;
})
}
.width('100%')
.margin({ bottom: 16 })
// 错误信息
if (this.errorMessage !== '') {
Text(this.errorMessage)
.fontSize(14)
.fontColor('#FF0000')
.width('100%')
.margin({ bottom: 16 })
}
// 按钮组
Row({ space: 20 }) {
Button("取消")
.width('45%')
.height(45)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.backgroundColor('#f5f5f5')
.borderRadius(8)
.fontColor('#333')
.onClick(() => {
this.showJoinClassDialog = false;
this.errorMessage = '';
})
Button("加入")
.width('45%')
.height(45)
.fontSize(16)
.fontWeight(FontWeight.Medium)
.backgroundColor(this.settings.themeColor)
.borderRadius(8)
.fontColor(Color.White)
.enabled(!this.isLoading)
.opacity(this.isLoading ? 0.6 : 1)
.onClick(() => {
this.joinClass();
})
}
.width('100%')
}
.width('85%')
.backgroundColor(Color.White)
.borderRadius(16)
.padding(24)
}
.width('100%')
.height('100%')
.backgroundColor('rgba(0,0,0,0.5)')
.justifyContent(FlexAlign.Center)
.onClick(() => {
// 点击遮罩不关闭对话框
// 只能通过按钮关闭
})
}
// 创建课堂方法
private async createClass() {
// 参数验证
if (!this.className || this.className.trim() === '') {
this.errorMessage = '请输入课堂名称';
return;
}
if (!this.classCode || this.classCode.trim() === '') {
this.errorMessage = '请输入课堂暗号';
return;
}
if (!/^\d{4,6}$/.test(this.classCode)) {
this.errorMessage = '课堂暗号必须是4-6位数字';
return;
}
this.isLoading = true;
// 连接服务
await classRoomService.connect();
// 创建课堂
try {
const classSession = await classRoomService.createClassSession(this.className, this.classCode);
if (classSession) {
// 创建成功
this.showCreateClassDialog = false;
this.errorMessage = '';
// 跳转到教师上课页面
router.pushUrl({
url: 'pages/ClassLivePage',
params: {
mode: 'teacher'
}
});
} else {
this.errorMessage = '创建课堂失败,请稍后再试';
}
} catch (error) {
this.errorMessage = '创建过程中发生错误,请稍后再试';
logManager.error(LogCategory.CLASS, `Create class error: ${error}`);
} finally {
this.isLoading = false;
}
}
// 加入课堂方法
private async joinClass() {
// 参数验证
if (!this.classCode || this.classCode.trim() === '') {
this.errorMessage = '请输入课堂暗号';
return;
}
if (!/^\d{4,6}$/.test(this.classCode)) {
this.errorMessage = '课堂暗号必须是4-6位数字';
return;
}
this.isLoading = true;
// 连接服务
await classRoomService.connect();
// 加入课堂
try {
const result = await classRoomService.joinClassSession(this.classCode);
if (result) {
// 加入成功
this.showJoinClassDialog = false;
this.errorMessage = '';
// 跳转到学生上课页面
router.pushUrl({
url: 'pages/ClassLivePage',
params: {
mode: 'student'
}
});
} else {
this.errorMessage = '加入课堂失败,请检查暗号是否正确';
}
} catch (error) {
this.errorMessage = '加入过程中发生错误,请稍后再试';
logManager.error(LogCategory.CLASS, `Join class error: ${error}`);
} finally {
this.isLoading = false;
}
}
// 页面生命周期方法
onPageShow(): void {
this.Log_Event('onPageShow');
}
onPageHide(): void {
this.Log_Event('onPageHide');
}
onBackPress(): void {
this.Log_Event('onBackPress');
}
aboutToDisappear(): void {
this.Log_Event('aboutToDisappear');
}
// 日志记录方法
public Log_Event(eventName: string): void {
// 根据事件名称选择合适的日志事件类型
switch (eventName) {
case 'aboutToAppear':
logManager.info(LogCategory.CLASS, LogEventType.PAGE_APPEAR, 'ClassPage');
break;
case 'aboutToDisappear':
logManager.info(LogCategory.CLASS, LogEventType.PAGE_DISAPPEAR, 'ClassPage');
break;
case 'onPageShow':
logManager.info(LogCategory.CLASS, LogEventType.PAGE_SHOW, 'ClassPage');
break;
case 'onPageHide':
logManager.info(LogCategory.CLASS, LogEventType.PAGE_HIDE, 'ClassPage');
break;
case 'onBackPress':
logManager.info(LogCategory.CLASS, LogEventType.PAGE_BACK, 'ClassPage');
break;
default:
logManager.info(LogCategory.CLASS, LogEventType.SYSTEM_INFO, `ClassPage: ${eventName}`);
}
}
}
// Define a class for currentClassInfo
class ClassInfo {
name: string;
teacher: string;
room: string;
constructor(name: string, teacher: string, room: string) {
this.name = name;
this.teacher = teacher;
this.room = room;
}
}
// Define proper interfaces instead of using any
interface ClassDataType {
name: string;
time: string;
content: string;
status: boolean;
}
// 卡片组件
@Component
struct Card {
title: string = '';
data: ClassDataType | MonitorDataType = {} as ClassDataType;
themeColor: string = '#409eff';
build() {
Column() {
// 卡片标题
Row() {
Text(this.title)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#333')
}
.width('100%')
.padding({ top: 8, bottom: 16 })
// 图表内容
Column() {
Text('图表内容')
.fontSize(16)
.fontColor('#999')
.height(150)
.width('100%')
.textAlign(TextAlign.Center)
}
.backgroundColor('#f5f5f5')
.borderRadius(8)
.width('100%')
}
.width('100%')
.backgroundColor(Color.White)
.borderRadius(12)
.padding(16)
.shadow({ radius: 6, color: '#eeeeee' })
}
}
// 底部导航栏组件
@Component
struct BottomNavigation {
@Prop activePage: string = 'class';
@Prop themeColor: string;
@Prop texts: TextResources = {} as TextResources;
build() {
Row() {
// 首页按钮
Column() {
Image($r('app.media.home'))
.width(24)
.height(24)
.fillColor(this.activePage === 'home' ? this.themeColor : '#999')
Text(this.texts.home)
.fontSize(12)
.fontColor(this.activePage === 'home' ? this.themeColor : '#999')
.margin({ top: 4 })
}
.layoutWeight(1)
.onClick(() => {
if (this.activePage !== 'home') {
router.replaceUrl({
url: 'pages/HomePage',
params: {
direction: 'left' // 从上课页向左切换到首页
}
});
}
})
// 上课按钮
Column() {
Image($r('app.media.class'))
.width(24)
.height(24)
.fillColor(this.activePage === 'class' ? this.themeColor : '#999')
Text(this.texts.class)
.fontSize(12)
.fontColor(this.activePage === 'class' ? this.themeColor : '#999')
.margin({ top: 4 })
}
.layoutWeight(1)
.onClick(() => {
if (this.activePage !== 'class') {
router.replaceUrl({
url: 'pages/ClassPage'
});
}
})
// 设置按钮
Column() {
Image($r('app.media.settings'))
.width(24)
.height(24)
.fillColor(this.activePage === 'settings' ? this.themeColor : '#999')
Text(this.texts.settings)
.fontSize(12)
.fontColor(this.activePage === 'settings' ? this.themeColor : '#999')
.margin({ top: 4 })
}
.layoutWeight(1)
.onClick(() => {
if (this.activePage !== 'settings') {
router.replaceUrl({
url: 'pages/SettingsPage'
});
}
})
}
.width('100%')
.height(60)
.backgroundColor(Color.White)
.borderWidth({ top: 0.5 })
.borderColor('#eeeeee')
.padding({ top: 8, bottom: 8 })
}
}