import { router } from '@kit.ArkUI'; import promptAction from '@ohos.promptAction'; import settingsService, { SettingsModel, TextResources } from '../common/SettingsService'; import { ClassRoomService, ClassSessionModel, MessageModel, QuestionModel, QuestionOption, QuestionAnswer, WebSocketEventType, SenderRole, QuestionStatus, EventData } from '../common/ClassRoomService'; import logManager, { LogCategory, LogEventType } from '../common/logtext'; import http from '@ohos.net.http'; // 路由URL接口 interface RouterUrlOptions { url: string; params?: object; } // 路由参数接口 interface RouteParams { mode?: string; } // 选项统计接口 interface OptionStats { count: number; percentage: number; } // 正确率统计接口 interface CorrectRateStats { correctCount: number; totalCount: number; percentage: number; } // 统计数据接口 interface StatisticsData { correctRate: CorrectRateStats; getOptionStats: (index: number) => OptionStats; } // 对话框按钮定义 interface DialogButton { value: string; action: () => void; } // 对话框配置 interface AlertDialogConfig { title: string; message: string; primaryButton: DialogButton; secondaryButton?: DialogButton; } // 课堂界面模式 enum ClassLiveMode { TEACHER = 'teacher', STUDENT = 'student' } @Entry @Component struct ClassLivePage { // 从路由参数获取模式 @State mode: ClassLiveMode = ClassLiveMode.STUDENT; // 课堂会话 @State classSession: ClassSessionModel | null = null; // 消息列表 @State messages: MessageModel[] = []; // 当前消息 @State messageText: string = ''; // 当前题目 @State currentQuestion: QuestionModel | null = null; // 学生已选答案 @State selectedOption: number = -1; // 是否显示题目统计 @State showQuestionStats: boolean = false; // 是否显示题目编辑器 @State showQuestionEditor: boolean = false; // 是否显示答题窗口 @State showAnswerDialog: boolean = false; // 题目编辑相关状态 @State questionTitle: string = ''; @State questionOptions: QuestionOption[] = []; @State correctOption: number = 0; @State questionDuration: number = 30; // 错误信息 @State errorMessage: string = ''; // 是否加载中 @State isLoading: boolean = false; // 倒计时 @State countdownSeconds: number = 0; private countdownTimer: number = -1; // 从设置服务获取设置 @State settings: SettingsModel = settingsService.getSettings(); // 获取文本资源 @State texts: TextResources = settingsService.getTextResources(); // 事件回调函数引用 private messageCallback: (data: EventData) => void = () => {}; private questionCallback: (data: EventData) => void = () => {}; private endQuestionCallback: (data: EventData) => void = () => {}; private endClassCallback: (data: EventData) => void = () => {}; // 用于服务实例 private classRoomService: ClassRoomService = ClassRoomService.getInstance(); aboutToAppear() { // 注册语言变化的回调 settingsService.registerLanguageChangeCallback(() => { this.texts = settingsService.getTextResources(); }); // 注册主题颜色变化的回调 settingsService.registerColorChangeCallback(() => { this.settings = settingsService.getSettings(); }); // 获取路由参数中的模式 const params = router.getParams() as RouteParams; if (params && params.mode) { this.mode = params.mode as ClassLiveMode; } // 获取当前课堂会话 this.classSession = this.classRoomService.getCurrentSession(); if (!this.classSession) { // 如果没有课堂会话,返回上课页面 this.showErrorAndReturn('未找到课堂会话,请重新加入或创建课堂'); return; } // 获取当前消息列表 this.messages = this.classSession.messages || []; // 获取当前活动题目 this.currentQuestion = this.classRoomService.getCurrentQuestion(); // 注册事件监听 this.registerEventListeners(); logManager.info(LogCategory.CLASS, LogEventType.PAGE_APPEAR, 'ClassLivePage'); } // 注册事件监听 private registerEventListeners() { // 接收新消息 this.messageCallback = (data: EventData) => { const message = data as MessageModel; if (message && message.id) { // 检查消息是否已经存在(防止重复) const existingMessage = this.messages.find(m => m.id === message.id); if (!existingMessage) { this.messages.push(message); } } }; this.classRoomService.addEventListener(WebSocketEventType.SEND_MESSAGE, this.messageCallback); // 题目发布 this.questionCallback = (data: EventData) => { const question = data as QuestionModel; if (question && question.questionId) { this.currentQuestion = question; if (this.mode === ClassLiveMode.STUDENT) { // 学生模式,显示答题窗口 this.selectedOption = -1; this.showAnswerDialog = true; } // 设置倒计时 this.startCountdown(question.duration); } }; this.classRoomService.addEventListener(WebSocketEventType.PUBLISH_QUESTION, this.questionCallback); // 题目结束 this.endQuestionCallback = (data: EventData) => { const question = data as QuestionModel; if (question && question.questionId) { // 更新题目状态 this.currentQuestion = question; // 停止倒计时 this.stopCountdown(); // 显示题目统计 this.showAnswerDialog = false; this.showQuestionStats = true; } }; this.classRoomService.addEventListener(WebSocketEventType.END_QUESTION, this.endQuestionCallback); // 课堂结束 this.endClassCallback = (data: EventData) => { const session = data as ClassSessionModel; if (session && session.sessionId) { // 显示课堂结束提示 const primaryButton: DialogButton = { value: '确定', action: () => { // 返回上课页面 - 使用明确的导航而不是back() router.pushUrl({ url: 'pages/ClassPage' } as RouterUrlOptions); } }; const alertConfig: AlertDialogConfig = { title: '课堂已结束', message: '教师已结束当前课堂', primaryButton: primaryButton }; AlertDialog.show(alertConfig); } }; this.classRoomService.addEventListener(WebSocketEventType.END_CLASS, this.endClassCallback); } // 创建问题编辑器内的选项 private addOption() { if (!this.questionOptions) { this.questionOptions = []; } const newIndex = this.questionOptions.length; const newOption: QuestionOption = { index: newIndex, content: '' }; this.questionOptions.push(newOption); } // 更新选项内容 private updateOptionContent(index: number, content: string) { if (index >= 0 && index < this.questionOptions.length) { const options = [...this.questionOptions]; options[index].content = content; this.questionOptions = options; } } // 移除选项 private removeOption(index: number) { if (this.questionOptions.length <= 2) { this.errorMessage = '至少需要两个选项'; return; } // 移除选项 this.questionOptions = this.questionOptions.filter((_, i) => i !== index); // 重新编号 for (let i = 0; i < this.questionOptions.length; i++) { this.questionOptions[i].index = i; } // 如果正确选项是被删除的选项或之后的选项,需要调整 if (this.correctOption >= index) { // 如果是最后一个选项,则正确选项为前一个 if (this.correctOption >= this.questionOptions.length) { this.correctOption = this.questionOptions.length - 1; } } } // 计算选项计数和百分比 private getOptionStats(index: number): OptionStats { if (!this.currentQuestion || this.currentQuestion.answers.length === 0) { const emptyStats: OptionStats = { count: 0, percentage: 0 }; return emptyStats; } // 使用普通for循环来避免索引访问的问题 let optionCount = 0; for (let i = 0; i < this.currentQuestion.answers.length; i++) { const answer = this.currentQuestion.answers[i]; if (answer.selectedOption === index) { optionCount++; } } const totalCount = this.currentQuestion.answers.length; const percentage = totalCount > 0 ? Math.round((optionCount / totalCount) * 100) : 0; const stats: OptionStats = { count: optionCount, percentage: percentage }; return stats; } // 计算正确率 private getCorrectRate(): CorrectRateStats { if (!this.currentQuestion || this.currentQuestion.answers.length === 0) { const emptyStats: CorrectRateStats = { correctCount: 0, totalCount: 0, percentage: 0 }; return emptyStats; } // 使用普通for循环来避免索引访问的问题 let correctCount = 0; for (let i = 0; i < this.currentQuestion.answers.length; i++) { const answer = this.currentQuestion.answers[i]; if (answer.isCorrect) { correctCount++; } } const totalCount = this.currentQuestion.answers.length; const percentage = totalCount > 0 ? Math.round((correctCount / totalCount) * 100) : 0; const stats: CorrectRateStats = { correctCount: correctCount, totalCount: totalCount, percentage: percentage }; return stats; } // 获取当前用户的答案 private getMyAnswer(): QuestionAnswer | null { if (!this.currentQuestion) { return null; } const currentAccount = settingsService.getCurrentAccount(); // 使用for循环查找 for (let i = 0; i < this.currentQuestion.answers.length; i++) { const answer = this.currentQuestion.answers[i]; if (answer.studentAccount === currentAccount) { return answer; } } return null; } // 重置题目编辑器 private resetQuestionEditor() { this.questionTitle = ''; this.questionOptions = []; this.correctOption = 0; this.questionDuration = 30; this.errorMessage = ''; } // 发送消息 private async sendMessage() { if (!this.messageText || this.messageText.trim() === '') { return; } try { const result = await this.classRoomService.sendMessage(this.messageText); if (result) { // 清空消息 this.messageText = ''; } } catch (error) { // 处理错误 const errorMessage = error instanceof Error ? error.message : String(error); logManager.error(LogCategory.CLASS, `Send message error: ${errorMessage}`); } } // 开始倒计时 private startCountdown(seconds: number) { // 停止之前的计时器 this.stopCountdown(); // 设置初始倒计时 this.countdownSeconds = seconds; // 创建新计时器 - 使用明确的类型 this.countdownTimer = setInterval(() => { if (this.countdownSeconds > 0) { this.countdownSeconds--; } else { // 倒计时结束 this.stopCountdown(); } }, 1000) as number; // 直接转换为number,不用中间unknown } // 停止倒计时 private stopCountdown() { if (this.countdownTimer !== -1) { clearInterval(this.countdownTimer); this.countdownTimer = -1; } } // 发布题目 private async publishQuestion() { if (!this.questionTitle || this.questionTitle.trim() === '') { this.errorMessage = '请输入题目内容'; return; } if (this.questionOptions.length < 2) { this.errorMessage = '请至少添加两个选项'; return; } this.isLoading = true; try { const question = await this.classRoomService.publishQuestion( this.questionTitle, this.questionOptions, this.correctOption, this.questionDuration ); if (question) { // 清空题目编辑器 this.resetQuestionEditor(); // 关闭题目编辑器 this.showQuestionEditor = false; } else { this.errorMessage = '发布题目失败,请稍后再试'; } } catch (error) { this.errorMessage = '发布题目过程中发生错误'; const errorMessage = error instanceof Error ? error.message : String(error); logManager.error(LogCategory.CLASS, `Publish question error: ${errorMessage}`); } finally { this.isLoading = false; } } // 提交答案 private async submitAnswer() { if (this.selectedOption < 0 || !this.currentQuestion) { this.errorMessage = '请选择一个选项'; return; } this.isLoading = true; try { const result = await this.classRoomService.submitAnswer( this.currentQuestion.questionId, this.selectedOption ); if (result) { // 关闭答题窗口 this.showAnswerDialog = false; // 清空错误消息 this.errorMessage = ''; } else { this.errorMessage = '提交答案失败,请稍后再试'; } } catch (error) { this.errorMessage = '提交答案过程中发生错误'; const errorMessage = error instanceof Error ? error.message : String(error); logManager.error(LogCategory.CLASS, `Submit answer error: ${errorMessage}`); } finally { this.isLoading = false; } } // 结束课堂 private async endClass() { this.isLoading = true; try { const result = await this.classRoomService.endClassSession(); if (result) { // 返回上课页面 - 使用明确的导航而不是back() router.pushUrl({ url: 'pages/ClassPage' } as RouterUrlOptions); } else { // 显示错误提示 const primaryButton: DialogButton = { value: '确定', action: () => {} }; const alertConfig: AlertDialogConfig = { title: '错误', message: '结束课堂失败,请稍后再试', primaryButton: primaryButton }; AlertDialog.show(alertConfig); } } catch (error) { // 显示错误提示 const primaryButton: DialogButton = { value: '确定', action: () => {} }; const alertConfig: AlertDialogConfig = { title: '错误', message: '结束课堂失败,请稍后再试', primaryButton: primaryButton }; AlertDialog.show(alertConfig); const errorMessage = error instanceof Error ? error.message : String(error); logManager.error(LogCategory.CLASS, `End class error: ${errorMessage}`); } finally { this.isLoading = false; } } // 显示错误并返回 private showErrorAndReturn(message: string) { const primaryButton: DialogButton = { value: '确定', action: () => { // 使用明确的导航而不是back() router.pushUrl({ url: 'pages/ClassPage' } as RouterUrlOptions); } }; const alertConfig: AlertDialogConfig = { title: '错误', message: message, primaryButton: primaryButton }; AlertDialog.show(alertConfig); } // 格式化时间 private formatTime(timestamp: number): string { const date = new Date(timestamp); const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${hours}:${minutes}`; } aboutToDisappear() { // 移除事件监听 if (this.messageCallback) { this.classRoomService.removeEventListener(WebSocketEventType.SEND_MESSAGE, this.messageCallback); } if (this.questionCallback) { this.classRoomService.removeEventListener(WebSocketEventType.PUBLISH_QUESTION, this.questionCallback); } if (this.endQuestionCallback) { this.classRoomService.removeEventListener(WebSocketEventType.END_QUESTION, this.endQuestionCallback); } if (this.endClassCallback) { this.classRoomService.removeEventListener(WebSocketEventType.END_CLASS, this.endClassCallback); } // 停止倒计时 this.stopCountdown(); logManager.info(LogCategory.CLASS, LogEventType.PAGE_DISAPPEAR, 'ClassLivePage'); } onBackPress() { logManager.info(LogCategory.CLASS, LogEventType.PAGE_BACK, 'ClassLivePage'); // 拦截返回键,显示确认对话框 if (this.mode === ClassLiveMode.TEACHER) { const primaryButton: DialogButton = { value: '确定', action: () => { this.endClass(); } }; const secondaryButton: DialogButton = { value: '取消', action: () => {} }; const alertConfig: AlertDialogConfig = { title: '离开课堂', message: '确定要离开当前课堂吗?这将结束整个课堂。', primaryButton: primaryButton, secondaryButton: secondaryButton }; AlertDialog.show(alertConfig); } else { const primaryButton: DialogButton = { value: '确定', action: () => { // 使用明确的导航而不是back() router.pushUrl({ url: 'pages/ClassPage' } as RouterUrlOptions); } }; const secondaryButton: DialogButton = { value: '取消', action: () => {} }; const alertConfig: AlertDialogConfig = { title: '离开课堂', message: '确定要离开当前课堂吗?', primaryButton: primaryButton, secondaryButton: secondaryButton }; AlertDialog.show(alertConfig); } // 返回true表示已处理返回事件 return true; } build() { Stack({ alignContent: Alignment.TopStart }) { Column() { // 顶部导航栏 Row() { Row() { Image($r('app.media.back')) .width(24) .height(24) .fillColor(Color.White) .margin({ right: 16 }) .onClick(() => { this.onBackPress(); }) Text(this.classSession?.className || '在线课堂') .fontSize(20) .fontWeight(FontWeight.Bold) .fontColor(Color.White) } // 教师端显示结束课堂按钮 if (this.mode === ClassLiveMode.TEACHER) { Button("结束课堂") .fontSize(14) .fontWeight(FontWeight.Medium) .backgroundColor('rgba(255,255,255,0.2)') .borderRadius(16) .fontColor(Color.White) .padding({ left: 12, right: 12, top: 6, bottom: 6 }) .onClick(() => { this.endClass(); }) } } .width('100%') .backgroundColor(this.settings.themeColor) .height(60) .justifyContent(FlexAlign.SpaceBetween) .padding({ left: 16, right: 16 }) // 课堂内容区域,包含聊天功能 Column() { // 课堂信息区域 Column() { Text(`课堂名称: ${this.classSession?.className || '-'}`) .fontSize(16) .margin({ bottom: 5 }) Text(`授课教师: ${this.classSession?.teacherName || '-'}`) .fontSize(16) .margin({ bottom: 5 }) Text(`角色: ${this.mode === ClassLiveMode.TEACHER ? '教师' : '学生'}`) .fontSize(16) } .width('100%') .padding(10) .backgroundColor('#f5f5f5') .borderRadius(8) .margin({ top: 10, bottom: 10 }) // 通知消息 if (this.currentQuestion) { Column() { Text("当前有一个正在进行的题目") .fontSize(16) .fontWeight(FontWeight.Bold) .margin({ bottom: 10 }) Text(`题目: ${this.currentQuestion.title}`) .fontSize(16) .margin({ bottom: 10 }) if (this.countdownSeconds > 0) { Text(`剩余时间: ${this.countdownSeconds}秒`) .fontSize(16) .fontColor(this.countdownSeconds > 10 ? '#333' : '#F56C6C') .margin({ bottom: 10 }) } } .width('100%') .padding(16) .backgroundColor('#f0f0f0') .borderRadius(8) .margin({ bottom: 10 }) } // 聊天历史记录区域 Column() { Text("聊天记录") .fontSize(18) .fontWeight(FontWeight.Bold) .margin({ bottom: 10 }) List({ space: 8 }) { ForEach(this.messages, (message: MessageModel) => { ListItem() { Row() { // 消息气泡 Column() { // 发送者信息 Row() { Text(message.senderName) .fontSize(14) .fontWeight(FontWeight.Bold) Text(message.senderRole === SenderRole.TEACHER ? ' (教师)' : ' (学生)') .fontSize(12) .fontColor('#666') } .width('100%') .margin({ bottom: 4 }) // 消息内容 Text(message.content) .fontSize(16) .margin({ top: 4 }) // 时间 Text(this.formatTime(message.timestamp.getTime())) .fontSize(12) .fontColor('#999') .margin({ top: 4 }) .alignSelf(message.senderId === settingsService.getCurrentAccount() ? ItemAlign.End : ItemAlign.Start) } .padding(12) .backgroundColor(message.senderRole === SenderRole.TEACHER ? '#e1f3d8' : '#edf2fc') .borderRadius(8) .alignSelf(ItemAlign.Start) .width('85%') } .width('100%') .justifyContent(message.senderId === settingsService.getCurrentAccount() ? FlexAlign.End : FlexAlign.Start) .margin({ top: 4, bottom: 4 }) } }) } .width('100%') .layoutWeight(1) .padding(10) .backgroundColor('#f9f9f9') .borderRadius(8) .scrollBar(BarState.Auto) } .width('100%') .layoutWeight(1) .margin({ bottom: 10 }) // 消息输入区域 Row() { TextInput({ placeholder: '输入消息...', text: this.messageText }) .layoutWeight(1) .height(44) .borderRadius(22) .backgroundColor('#f5f5f5') .padding({ left: 20, right: 20 }) .onChange((value: string) => { this.messageText = value; }) Button("发送") .height(44) .width(80) .margin({ left: 10 }) .borderRadius(22) .backgroundColor(this.settings.themeColor) .onClick(() => { this.sendMessage(); }) } .width('100%') .margin({ top: 10 }) // 错误消息和加载状态 if (this.errorMessage) { Text(this.errorMessage) .fontSize(14) .fontColor('#F56C6C') .width('100%') .textAlign(TextAlign.Center) .margin({ top: 10 }) } if (this.isLoading) { Row() { LoadingProgress() .width(24) .height(24) .margin({ right: 10 }) Text("处理中...") .fontSize(14) .fontColor('#666') } .justifyContent(FlexAlign.Center) .width('100%') .margin({ top: 10 }) } } .width('100%') .height('100%') .padding(16) } .width('100%') .height('100%') // 题目编辑窗口 if (this.showQuestionEditor) { this.QuestionEditorDialog() } // 题目答题窗口 if (this.showAnswerDialog && this.currentQuestion) { this.QuestionAnswerDialog() } // 题目统计窗口 if (this.showQuestionStats && this.currentQuestion) { this.QuestionStatsDialog() } // 浮动操作按钮区域 Column() { if (this.mode === ClassLiveMode.TEACHER && !this.showQuestionEditor) { Button({ type: ButtonType.Circle }) { Image($r('app.media.add')) .width(24) .height(24) .fillColor(Color.White) } .width(56) .height(56) .backgroundColor(this.settings.themeColor) .shadow({ radius: 8, color: 'rgba(0, 0, 0, 0.2)' }) .onClick(() => { this.showQuestionEditor = true; // 初始化题目选项 if (this.questionOptions.length === 0) { this.addOption(); this.addOption(); } }) } if (this.mode === ClassLiveMode.STUDENT && this.currentQuestion && !this.showAnswerDialog && !this.showQuestionStats) { Button({ type: ButtonType.Circle }) { Image($r('app.media.question')) .width(24) .height(24) .fillColor(Color.White) } .width(56) .height(56) .backgroundColor(this.settings.themeColor) .shadow({ radius: 8, color: 'rgba(0, 0, 0, 0.2)' }) .onClick(() => { this.showAnswerDialog = true; }) } } .position({ x: 24, y: '85%' }) } .width('100%') .height('100%') } // 题目编辑窗口 @Builder QuestionEditorDialog() { Column() { // 对话框内容 Column() { // 标题 Row() { Text("发布题目") .fontSize(20) .fontWeight(FontWeight.Bold) Blank() Button({ type: ButtonType.Circle }) { Image($r('app.media.close')) .width(16) .height(16) } .width(32) .height(32) .backgroundColor('#f0f0f0') .onClick(() => { this.showQuestionEditor = false; }) } .width('100%') .margin({ bottom: 20 }) // 题目内容 Column({ space: 10 }) { Text("题目内容") .fontSize(16) .fontColor('#666666') .alignSelf(ItemAlign.Start) TextArea({ placeholder: '请输入题目内容', text: this.questionTitle }) .width('100%') .height(100) .fontSize(16) .backgroundColor('#F5F5F5') .borderRadius(8) .padding(10) .onChange((value: string) => { this.questionTitle = value; }) } .width('100%') .margin({ bottom: 16 }) // 选项部分 Column({ space: 10 }) { Row() { Text("选项") .fontSize(16) .fontColor('#666666') Blank() Button({ type: ButtonType.Circle }) { Image($r('app.media.add')) .width(16) .height(16) } .width(32) .height(32) .backgroundColor(this.settings.themeColor) .onClick(() => { this.addOption(); }) } .width('100%') // 选项列表 Column({ space: 10 }) { ForEach(this.questionOptions, (option: QuestionOption, index: number) => { Row() { Radio({ value: index.toString(), group: 'correctOption' }) .checked(index === this.correctOption) .onChange((isChecked: boolean) => { if (isChecked) { this.correctOption = index; } }) Text(`选项 ${String.fromCharCode(65 + index)}`) .fontSize(16) .fontWeight(FontWeight.Medium) .margin({ left: 8, right: 8 }) TextInput({ placeholder: '请输入选项内容', text: option.content }) .layoutWeight(1) .height(40) .fontSize(16) .backgroundColor('#F5F5F5') .borderRadius(8) .padding({ left: 10, right: 10 }) .onChange((value: string) => { this.updateOptionContent(index, value); }) // 删除选项按钮 if (this.questionOptions.length > 2) { Button({ type: ButtonType.Circle }) { Image($r('app.media.delete')) .width(16) .height(16) } .width(32) .height(32) .backgroundColor('#f5f5f5') .margin({ left: 8 }) .onClick(() => { this.removeOption(index); }) } } .width('100%') .alignItems(VerticalAlign.Center) }) } .width('100%') } .width('100%') .margin({ bottom: 16 }) // 答题时间 Column({ space: 10 }) { Text("答题时间(秒)") .fontSize(16) .fontColor('#666666') .alignSelf(ItemAlign.Start) Row() { Slider({ value: this.questionDuration, min: 10, max: 120, step: 5, style: SliderStyle.OutSet }) .layoutWeight(1) .blockColor(this.settings.themeColor) .trackColor('#E1E1E1') .selectedColor(this.settings.themeColor) .showSteps(true) .showTips(true) .onChange((value: number) => { this.questionDuration = value; }) Text(this.questionDuration.toString()) .fontSize(16) .fontColor('#333') .margin({ left: 16 }) .width(40) .textAlign(TextAlign.End) } .width('100%') } .width('100%') .margin({ bottom: 20 }) // 错误信息 if (this.errorMessage) { Text(this.errorMessage) .fontSize(14) .fontColor('#F56C6C') .width('100%') .textAlign(TextAlign.Center) .margin({ bottom: 16 }) } // 发布按钮 Button("发布题目") .width('100%') .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.publishQuestion(); }) } .width('90%') .backgroundColor(Color.White) .borderRadius(16) .padding(24) } .width('100%') .height('100%') .backgroundColor('rgba(0,0,0,0.5)') .justifyContent(FlexAlign.Center) .onClick(() => { // 点击遮罩不关闭对话框 }) } // 答题窗口 @Builder QuestionAnswerDialog() { Column() { // 对话框内容 Column() { // 标题 Row() { Text("答题") .fontSize(20) .fontWeight(FontWeight.Bold) Blank() Button({ type: ButtonType.Circle }) { Image($r('app.media.close')) .width(16) .height(16) } .width(32) .height(32) .backgroundColor('#f0f0f0') .onClick(() => { this.showAnswerDialog = false; }) } .width('100%') .margin({ bottom: 20 }) // 题目内容 Column() { Text(this.currentQuestion?.title || '') .fontSize(18) .fontWeight(FontWeight.Medium) .margin({ bottom: 10 }) if (this.countdownSeconds > 0) { Text(`剩余时间: ${this.countdownSeconds}秒`) .fontSize(16) .fontColor(this.countdownSeconds > 10 ? '#333' : '#F56C6C') .margin({ bottom: 20 }) } } .width('100%') .alignItems(HorizontalAlign.Start) .margin({ bottom: 16 }) // 选项列表 Column({ space: 10 }) { if (this.currentQuestion) { ForEach(this.currentQuestion.options, (option: QuestionOption, index: number) => { Row() { Radio({ value: index.toString(), group: 'answerOption' }) .checked(this.selectedOption === index) .onChange((isChecked: boolean) => { if (isChecked) { this.selectedOption = index; } }) Text(`${String.fromCharCode(65 + index)}. ${option.content}`) .fontSize(16) .margin({ left: 10 }) .layoutWeight(1) } .width('100%') .padding(10) .borderRadius(8) .backgroundColor(this.selectedOption === index ? '#f0f7ff' : '#f5f5f5') .alignItems(VerticalAlign.Center) }) } } .width('100%') .margin({ bottom: 20 }) // 错误信息 if (this.errorMessage) { Text(this.errorMessage) .fontSize(14) .fontColor('#F56C6C') .width('100%') .textAlign(TextAlign.Center) .margin({ bottom: 16 }) } // 提交按钮 Button("提交答案") .width('100%') .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.submitAnswer(); }) } .width('90%') .backgroundColor(Color.White) .borderRadius(16) .padding(24) } .width('100%') .height('100%') .backgroundColor('rgba(0,0,0,0.5)') .justifyContent(FlexAlign.Center) .onClick(() => { // 点击遮罩不关闭对话框 }) } // 获取题目统计数据 - 辅助方法 private getStatisticsData(): StatisticsData { return { correctRate: this.getCorrectRate(), getOptionStats: (index: number) => this.getOptionStats(index) }; } // 题目统计内容 @Builder QuestionStatsContent() { Column() { // 总体正确率 Row() { Text("参与人数: ") .fontSize(16) .fontWeight(FontWeight.Medium) Text(`${this.getCorrectRate().totalCount}人`) .fontSize(16) .fontWeight(FontWeight.Medium) Blank() Text("正确率: ") .fontSize(16) .fontWeight(FontWeight.Medium) Text(`${this.getCorrectRate().percentage}%`) .fontSize(16) .fontColor(this.getCorrectRate().percentage > 50 ? '#67C23A' : '#F56C6C') .fontWeight(FontWeight.Medium) } .width('100%') .padding(10) .borderRadius(8) .backgroundColor('#f5f5f5') .margin({ bottom: 16 }) // 选项统计 if (this.currentQuestion) { ForEach(this.currentQuestion.options, (option: QuestionOption, index: number) => { Column() { Row() { Text(`选项 ${String.fromCharCode(65 + index)}: ${option.content}`) .fontSize(16) .fontWeight(index === this.currentQuestion?.correctOption ? FontWeight.Bold : FontWeight.Normal) .fontColor(index === this.currentQuestion?.correctOption ? '#67C23A' : '#333') } .width('100%') .margin({ bottom: 4 }) Row() { // 进度条 Column() { Stack({ alignContent: Alignment.Start }) { Row() .width('100%') .height(20) .backgroundColor('#f0f0f0') .borderRadius(10) Row() .width(`${this.getOptionStats(index).percentage}%`) .height(20) .backgroundColor(index === this.currentQuestion?.correctOption ? '#67C23A' : '#409EFF') .borderRadius(10) } } .layoutWeight(1) // 百分比和人数 Text(`${this.getOptionStats(index).percentage}% (${this.getOptionStats(index).count}人)`) .fontSize(14) .fontColor('#666') .margin({ left: 8 }) .width(100) .textAlign(TextAlign.End) } .width('100%') } .width('100%') .margin({ bottom: 12 }) .padding(10) .borderRadius(8) .backgroundColor(index === this.currentQuestion?.correctOption ? '#f0f9eb' : '#f9f9f9') }) } } .width('100%') } // 题目统计窗口 @Builder QuestionStatsDialog() { Column() { // 对话框内容 Column() { // 标题 Row() { Text("答题结果") .fontSize(20) .fontWeight(FontWeight.Bold) Blank() Button({ type: ButtonType.Circle }) { Image($r('app.media.close')) .width(16) .height(16) } .width(32) .height(32) .backgroundColor('#f0f0f0') .onClick(() => { this.showQuestionStats = false; }) } .width('100%') .margin({ bottom: 20 }) // 题目内容 Text(this.currentQuestion?.title || '') .fontSize(18) .fontWeight(FontWeight.Medium) .width('100%') .margin({ bottom: 16 }) // 统计数据 Column() { // 整体统计 if (this.mode === ClassLiveMode.TEACHER) { this.QuestionStatsContent() } else { // 学生端显示自己的答案 if (this.getMyAnswer()) { Column() { // 学生答案 Row() { Text("我的答案: ") .fontSize(16) .fontWeight(FontWeight.Medium) Text(`${String.fromCharCode(65 + this.getMyAnswer()!.selectedOption)}`) .fontSize(16) .fontColor(this.getMyAnswer()!.isCorrect ? '#67C23A' : '#F56C6C') .fontWeight(FontWeight.Medium) Blank() Text(this.getMyAnswer()!.isCorrect ? '✓ 正确' : '✗ 错误') .fontSize(16) .fontColor(this.getMyAnswer()!.isCorrect ? '#67C23A' : '#F56C6C') .fontWeight(FontWeight.Medium) } .width('100%') .padding(10) .borderRadius(8) .backgroundColor(this.getMyAnswer()!.isCorrect ? '#f0f9eb' : '#fef0f0') .margin({ bottom: 20 }) // 正确答案 if (!this.getMyAnswer()!.isCorrect && this.currentQuestion) { Row() { Text("正确答案: ") .fontSize(16) .fontWeight(FontWeight.Medium) Text(`${String.fromCharCode(65 + this.currentQuestion.correctOption)}`) .fontSize(16) .fontColor('#67C23A') .fontWeight(FontWeight.Medium) } .width('100%') .padding(10) .borderRadius(8) .backgroundColor('#f0f9eb') .margin({ bottom: 20 }) } } .width('100%') .margin({ bottom: 20 }) } // 学生端也显示整体统计 this.QuestionStatsContent() } } .width('100%') // 关闭按钮 Button("确定") .width('100%') .height(45) .fontSize(16) .fontWeight(FontWeight.Medium) .backgroundColor(this.settings.themeColor) .borderRadius(8) .fontColor(Color.White) .onClick(() => { this.showQuestionStats = false; }) } .width('90%') .backgroundColor(Color.White) .borderRadius(16) .padding(24) } .width('100%') .height('100%') .backgroundColor('rgba(0,0,0,0.5)') .justifyContent(FlexAlign.Center) .onClick(() => { // 点击遮罩不关闭对话框 }) } }