diff --git a/entry/src/main/ets/pages/ClassLivePage.ets b/entry/src/main/ets/pages/ClassLivePage.ets index 3342f38..04c70df 100644 --- a/entry/src/main/ets/pages/ClassLivePage.ets +++ b/entry/src/main/ets/pages/ClassLivePage.ets @@ -5,11 +5,12 @@ import { router } from '@kit.ArkUI'; import promptAction from '@ohos.promptAction'; import settingsService, { SettingsModel, TextResources } from '../common/SettingsService'; -import { ClassRoomService, - ClassSessionModel, - MessageModel, - QuestionModel, - QuestionOption, +import { + ClassRoomService, + ClassSessionModel, + MessageModel, + QuestionModel, + QuestionOption, QuestionAnswer, WebSocketEventType, SenderRole, @@ -21,8 +22,8 @@ import http from '@ohos.net.http'; import hilog from '@ohos.hilog'; // 添加hilog和tianChannel常量 -const TIAN_CHANNEL_DOMAIN_ID = 0x00201; // 自定义域ID -const TIAN_CHANNEL_TAG = 'tianChannel'; // 自定义TAG用于筛选日志 +const TIAN_CHANNEL_DOMAIN_ID = 0x00201; // 自定义域ID +const TIAN_CHANNEL_TAG = 'tianChannel'; // 自定义TAG用于筛选日志 // 定义选项对象接口 interface ScrollOptions { @@ -89,100 +90,88 @@ enum ClassLiveMode { 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 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() { // 接收新消息 @@ -194,10 +183,10 @@ struct ClassLivePage { if (!existingMessage) { // 输出调试信息 console.info(`收到新消息事件: ID=${message.id}, 发送者=${message.senderName}, 内容=${message.content}`); - + // 使用新数组方式更新消息列表,确保 UI 刷新 this.messages = [...this.messages, message]; - + // 新消息到达时自动滚动到底部 this.scrollToLatestMessage(); } else { @@ -207,42 +196,42 @@ struct ClassLivePage { } }; 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; @@ -257,7 +246,7 @@ struct ClassLivePage { } as RouterUrlOptions); } }; - + const alertConfig: AlertDialogConfig = { title: '课堂已结束', message: '教师已结束当前课堂', @@ -268,7 +257,7 @@ struct ClassLivePage { }; this.classRoomService.addEventListener(WebSocketEventType.END_CLASS, this.endClassCallback); } - + // 创建问题编辑器内的选项 private addOption() { if (!this.questionOptions) { @@ -281,7 +270,7 @@ struct ClassLivePage { }; this.questionOptions.push(newOption); } - + // 更新选项内容 private updateOptionContent(index: number, content: string) { if (index >= 0 && index < this.questionOptions.length) { @@ -290,22 +279,22 @@ struct ClassLivePage { 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) { // 如果是最后一个选项,则正确选项为前一个 @@ -314,7 +303,7 @@ struct ClassLivePage { } } } - + // 计算选项计数和百分比 private getOptionStats(index: number): OptionStats { if (!this.currentQuestion || this.currentQuestion.answers.length === 0) { @@ -324,7 +313,7 @@ struct ClassLivePage { }; return emptyStats; } - + // 使用普通for循环来避免索引访问的问题 let optionCount = 0; for (let i = 0; i < this.currentQuestion.answers.length; i++) { @@ -333,17 +322,17 @@ struct ClassLivePage { 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) { @@ -354,7 +343,7 @@ struct ClassLivePage { }; return emptyStats; } - + // 使用普通for循环来避免索引访问的问题 let correctCount = 0; for (let i = 0; i < this.currentQuestion.answers.length; i++) { @@ -363,10 +352,10 @@ struct ClassLivePage { correctCount++; } } - + const totalCount = this.currentQuestion.answers.length; const percentage = totalCount > 0 ? Math.round((correctCount / totalCount) * 100) : 0; - + const stats: CorrectRateStats = { correctCount: correctCount, totalCount: totalCount, @@ -374,15 +363,15 @@ struct ClassLivePage { }; 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]; @@ -390,10 +379,10 @@ struct ClassLivePage { return answer; } } - + return null; } - + // 重置题目编辑器 private resetQuestionEditor() { this.questionTitle = ''; @@ -402,30 +391,30 @@ struct ClassLivePage { this.questionDuration = 30; this.errorMessage = ''; } - + // 发送消息 private async sendMessage() { hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] ======== 开始UI层消息发送 ========`); - + if (!this.messageText || this.messageText.trim() === '') { hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 消息内容为空,不发送`); return; } - + try { // 防止重复点击、重复发送 if (this.isLoading) { hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 正在加载中,防止重复发送`); return; } - + hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 设置加载状态为true`); this.isLoading = true; - + // 保存消息内容,因为后面会清空输入框 const messageContent = this.messageText.trim(); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 消息内容: ${messageContent}`); - + // 创建一个本地消息对象用于立即显示 hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 创建本地消息对象`); const localMessage = new MessageModel( @@ -434,36 +423,36 @@ struct ClassLivePage { this.mode === ClassLiveMode.TEACHER ? SenderRole.TEACHER : SenderRole.STUDENT, messageContent ); - + // 保存消息ID,以便后续可以在发送失败时移除 const localMessageId = localMessage.id; hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 本地消息ID: ${localMessageId}`); - + // 清空消息输入框 - 提前清空避免用户重复点击 hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 清空输入框`); this.messageText = ''; - + // 先在本地添加消息,让用户立即看到 hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 添加消息到本地列表`); console.info(`添加本地消息: ID=${localMessageId}, 内容=${messageContent}`); this.messages = [...this.messages, localMessage]; - + // 自动滚动到最新消息 hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 滚动到最新消息`); this.scrollToLatestMessage(); - + // 发送消息到服务器 hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 调用服务层发送消息`); try { const result = await this.classRoomService.sendMessage(messageContent); hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 服务层返回结果: ${result}`); - + if (!result) { // 如果发送失败,从消息列表中移除本地消息 hilog.error(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 消息发送失败,移除本地消息: ${localMessageId}`); console.error(`消息发送失败,移除本地消息: ${localMessageId}`); this.messages = this.messages.filter(msg => msg.id !== localMessageId); - + // 显示错误提示 hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 显示发送失败提示`); promptAction.showToast({ @@ -477,11 +466,11 @@ struct ClassLivePage { } catch (serviceError) { // 处理服务层异常 hilog.error(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 服务层异常: ${serviceError}`); - + // 如果发送失败,从消息列表中移除本地消息 hilog.error(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 由于服务层异常,移除本地消息: ${localMessageId}`); this.messages = this.messages.filter(msg => msg.id !== localMessageId); - + // 显示错误提示 promptAction.showToast({ message: '消息发送失败,请重试', @@ -492,10 +481,10 @@ struct ClassLivePage { // 处理错误 const errorMessage = error instanceof Error ? error.message : String(error); const errorStack = error instanceof Error ? error.stack : 'No stack trace'; - + hilog.error(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 顶层异常: ${errorMessage}`); hilog.error(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] 异常堆栈: ${errorStack}`); - + logManager.error(LogCategory.CLASS, `Send message error: ${errorMessage}`); promptAction.showToast({ message: '消息发送失败,请重试', @@ -507,18 +496,18 @@ struct ClassLivePage { hilog.info(TIAN_CHANNEL_DOMAIN_ID, TIAN_CHANNEL_TAG, `[UI] ======== 结束UI层消息发送 ========`); } } - + // 自动滚动到最新消息 private scrollToLatestMessage() { console.info(`准备滚动到最新消息,当前消息数量: ${this.messages.length}`); - + setTimeout(() => { if (this.messages.length > 0) { try { // 直接访问DOM上的messageList元素 // 注意:当前版本不直接支持选择器,将使用替代方式滚动 console.info(`尝试滚动到索引: ${this.messages.length - 1}`); - + // 由于直接DOM选择器的限制,实际实现可能需根据实际框架支持调整 console.info('滚动到最新消息完成'); } catch (error) { @@ -530,15 +519,15 @@ struct ClassLivePage { } }, 200); // 增加延迟确保DOM已更新 } - + // 开始倒计时 private startCountdown(seconds: number) { // 停止之前的计时器 this.stopCountdown(); - + // 设置初始倒计时 this.countdownSeconds = seconds; - + // 创建新计时器 - 使用明确的类型 this.countdownTimer = setInterval(() => { if (this.countdownSeconds > 0) { @@ -549,7 +538,7 @@ struct ClassLivePage { } }, 1000) as number; } - + // 停止倒计时 private stopCountdown() { if (this.countdownTimer !== -1) { @@ -557,21 +546,21 @@ struct ClassLivePage { 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, @@ -579,7 +568,7 @@ struct ClassLivePage { this.correctOption, this.questionDuration ); - + if (question) { // 清空题目编辑器 this.resetQuestionEditor(); @@ -596,22 +585,22 @@ struct ClassLivePage { 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; @@ -628,21 +617,22 @@ struct ClassLivePage { this.isLoading = false; } } - + // 结束课堂 private async endClass() { this.isLoading = true; - + try { const result = await this.classRoomService.endClassSession(); - + if (!result) { // 只在失败时显示错误提示,成功时不跳转,等待服务器通知 const primaryButton: DialogButton = { value: '确定', - action: () => {} + action: () => { + } }; - + const alertConfig: AlertDialogConfig = { title: '错误', message: '结束课堂失败,请稍后再试', @@ -655,9 +645,10 @@ struct ClassLivePage { // 显示错误提示 const primaryButton: DialogButton = { value: '确定', - action: () => {} + action: () => { + } }; - + const alertConfig: AlertDialogConfig = { title: '错误', message: '结束课堂失败,请稍后再试', @@ -670,7 +661,7 @@ struct ClassLivePage { this.isLoading = false; } } - + // 显示错误并返回 private showErrorAndReturn(message: string) { const primaryButton: DialogButton = { @@ -682,7 +673,7 @@ struct ClassLivePage { } as RouterUrlOptions); } }; - + const alertConfig: AlertDialogConfig = { title: '错误', message: message, @@ -690,7 +681,7 @@ struct ClassLivePage { }; AlertDialog.show(alertConfig); } - + // 格式化时间 private formatTime(timestamp: number): string { const date = new Date(timestamp); @@ -698,7 +689,7 @@ struct ClassLivePage { const minutes = date.getMinutes().toString().padStart(2, '0'); return `${hours}:${minutes}`; } - + // 查找组件树中指定ID的元素 - 简化实现 private findChildById(id: string): ListElement | null { // 由于框架限制,返回null以避免编译错误 @@ -706,34 +697,34 @@ struct ClassLivePage { console.info(`尝试查找元素: ${id}`); return null; } - + 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 = { @@ -742,12 +733,13 @@ struct ClassLivePage { this.endClass(); } }; - + const secondaryButton: DialogButton = { value: '取消', - action: () => {} + action: () => { + } }; - + const alertConfig: AlertDialogConfig = { title: '离开课堂', message: '确定要离开当前课堂吗?这将结束整个课堂。', @@ -765,12 +757,13 @@ struct ClassLivePage { } as RouterUrlOptions); } }; - + const secondaryButton: DialogButton = { value: '取消', - action: () => {} + action: () => { + } }; - + const alertConfig: AlertDialogConfig = { title: '离开课堂', message: '确定要离开当前课堂吗?', @@ -779,11 +772,11 @@ struct ClassLivePage { }; AlertDialog.show(alertConfig); } - + // 返回true表示已处理返回事件 return true; } - + build() { Stack({ alignContent: Alignment.TopStart }) { Column() { @@ -798,13 +791,13 @@ struct ClassLivePage { .onClick(() => { this.onBackPress(); }) - + Text(this.classSession?.className || '在线课堂') .fontSize(20) .fontWeight(FontWeight.Bold) .fontColor(Color.White) } - + Row() { // 添加连接检查按钮 Button("检查") @@ -813,12 +806,17 @@ struct ClassLivePage { .backgroundColor('rgba(255,255,255,0.2)') .borderRadius(16) .fontColor(Color.White) - .padding({ left: 12, right: 12, top: 6, bottom: 6 }) + .padding({ + left: 12, + right: 12, + top: 6, + bottom: 6 + }) .margin({ right: 8 }) .onClick(() => { this.checkConnectionStatus(); }) - + // 教师端显示结束课堂按钮 if (this.mode === ClassLiveMode.TEACHER) { Button("结束课堂") @@ -827,7 +825,12 @@ struct ClassLivePage { .backgroundColor('rgba(255,255,255,0.2)') .borderRadius(16) .fontColor(Color.White) - .padding({ left: 12, right: 12, top: 6, bottom: 6 }) + .padding({ + left: 12, + right: 12, + top: 6, + bottom: 6 + }) .onClick(() => { this.endClass(); }) @@ -839,7 +842,7 @@ struct ClassLivePage { .height(60) .justifyContent(FlexAlign.SpaceBetween) .padding({ left: 16, right: 16 }) - + // 使用Flex布局来确保页面元素适应不同屏幕尺寸 Flex({ direction: FlexDirection.Column }) { // 顶部固定区域(课堂信息和通知) @@ -849,11 +852,11 @@ struct ClassLivePage { 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) } @@ -862,7 +865,7 @@ struct ClassLivePage { .backgroundColor('#f5f5f5') .borderRadius(8) .margin({ bottom: 10 }) - + // 通知消息 if (this.currentQuestion) { Column() { @@ -870,11 +873,11 @@ struct ClassLivePage { .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) @@ -890,14 +893,14 @@ struct ClassLivePage { } } .width('100%') - + // 聊天历史记录区域(可伸缩) Column() { - Text("聊天记录") - .fontSize(18) - .fontWeight(FontWeight.Bold) - .margin({ bottom: 10 }) - + // Text("聊天记录") + // .fontSize(18) + // .fontWeight(FontWeight.Bold) + // .margin({ bottom: 10 }) + List({ space: 8 }) { ForEach(this.messages, (message: MessageModel, index) => { ListItem() { @@ -909,25 +912,26 @@ struct ClassLivePage { 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) + .alignSelf(message.senderId === settingsService.getCurrentAccount() ? ItemAlign.End : + ItemAlign.Start) } .padding(12) .backgroundColor(message.senderRole === SenderRole.TEACHER ? '#e1f3d8' : '#edf2fc') @@ -936,13 +940,14 @@ struct ClassLivePage { .width('85%') } .width('100%') - .justifyContent(message.senderId === settingsService.getCurrentAccount() ? FlexAlign.End : FlexAlign.Start) + .justifyContent(message.senderId === settingsService.getCurrentAccount() ? FlexAlign.End : + FlexAlign.Start) .margin({ top: 4, bottom: 4 }) } }, (message: MessageModel) => message.id) // 使用消息ID作为唯一键,避免重复渲染 } .width('100%') - .layoutWeight(1) + // .layoutWeight(1) .padding(10) .backgroundColor('#f9f9f9') .borderRadius(8) @@ -955,7 +960,7 @@ struct ClassLivePage { } .flexGrow(1) // 使聊天记录区域占用所有可用空间 .width('100%') - + // 底部固定区域 (错误消息、加载状态和输入框) Column() { // 错误消息和加载状态 @@ -967,14 +972,14 @@ struct ClassLivePage { .textAlign(TextAlign.Center) .margin({ top: 8, bottom: 8 }) } - + if (this.isLoading) { Row() { LoadingProgress() .width(24) .height(24) .margin({ right: 10 }) - + Text("处理中...") .fontSize(14) .fontColor('#666') @@ -983,7 +988,7 @@ struct ClassLivePage { .width('100%') .margin({ top: 8, bottom: 8 }) } - + // 消息输入区域 Row() { TextInput({ placeholder: '输入消息...', text: this.messageText }) @@ -995,7 +1000,7 @@ struct ClassLivePage { .onChange((value: string) => { this.messageText = value; }) - + Button("发送") .height(44) .width(80) @@ -1017,22 +1022,22 @@ struct ClassLivePage { } .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) { @@ -1048,7 +1053,7 @@ struct ClassLivePage { .shadow({ radius: 8, color: 'rgba(0, 0, 0, 0.2)' }) .onClick(() => { this.showQuestionEditor = true; - + // 初始化题目选项 if (this.questionOptions.length === 0) { this.addOption(); @@ -1056,8 +1061,9 @@ struct ClassLivePage { } }) } - - if (this.mode === ClassLiveMode.STUDENT && this.currentQuestion && !this.showAnswerDialog && !this.showQuestionStats) { + + if (this.mode === ClassLiveMode.STUDENT && this.currentQuestion && !this.showAnswerDialog && + !this.showQuestionStats) { Button({ type: ButtonType.Circle }) { Image($r('app.media.question')) .width(24) @@ -1078,7 +1084,7 @@ struct ClassLivePage { .width('100%') .height('100%') } - + // 题目编辑窗口 @Builder QuestionEditorDialog() { @@ -1090,9 +1096,9 @@ struct ClassLivePage { Text("发布题目") .fontSize(20) .fontWeight(FontWeight.Bold) - + Blank() - + Button({ type: ButtonType.Circle }) { Image($r('app.media.close')) .width(16) @@ -1107,14 +1113,14 @@ struct ClassLivePage { } .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) @@ -1128,16 +1134,16 @@ struct ClassLivePage { } .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) @@ -1151,7 +1157,7 @@ struct ClassLivePage { }) } .width('100%') - + // 选项列表 Column({ space: 10 }) { ForEach(this.questionOptions, (option: QuestionOption, index: number) => { @@ -1163,12 +1169,12 @@ struct ClassLivePage { 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) @@ -1179,7 +1185,7 @@ struct ClassLivePage { .onChange((value: string) => { this.updateOptionContent(index, value); }) - + // 删除选项按钮 if (this.questionOptions.length > 2) { Button({ type: ButtonType.Circle }) { @@ -1204,14 +1210,14 @@ struct ClassLivePage { } .width('100%') .margin({ bottom: 16 }) - + // 答题时间 Column({ space: 10 }) { Text("答题时间(秒)") .fontSize(16) .fontColor('#666666') .alignSelf(ItemAlign.Start) - + Row() { Slider({ value: this.questionDuration, @@ -1229,7 +1235,7 @@ struct ClassLivePage { .onChange((value: number) => { this.questionDuration = value; }) - + Text(this.questionDuration.toString()) .fontSize(16) .fontColor('#333') @@ -1241,7 +1247,7 @@ struct ClassLivePage { } .width('100%') .margin({ bottom: 20 }) - + // 错误信息 if (this.errorMessage) { Text(this.errorMessage) @@ -1251,7 +1257,7 @@ struct ClassLivePage { .textAlign(TextAlign.Center) .margin({ bottom: 16 }) } - + // 发布按钮 Button("发布题目") .width('100%') @@ -1280,7 +1286,7 @@ struct ClassLivePage { // 点击遮罩不关闭对话框 }) } - + // 答题窗口 @Builder QuestionAnswerDialog() { @@ -1292,9 +1298,9 @@ struct ClassLivePage { Text("答题") .fontSize(20) .fontWeight(FontWeight.Bold) - + Blank() - + Button({ type: ButtonType.Circle }) { Image($r('app.media.close')) .width(16) @@ -1309,14 +1315,14 @@ struct ClassLivePage { } .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) @@ -1327,7 +1333,7 @@ struct ClassLivePage { .width('100%') .alignItems(HorizontalAlign.Start) .margin({ bottom: 16 }) - + // 选项列表 Column({ space: 10 }) { if (this.currentQuestion) { @@ -1340,7 +1346,7 @@ struct ClassLivePage { this.selectedOption = index; } }) - + Text(`${String.fromCharCode(65 + index)}. ${option.content}`) .fontSize(16) .margin({ left: 10 }) @@ -1356,7 +1362,7 @@ struct ClassLivePage { } .width('100%') .margin({ bottom: 20 }) - + // 错误信息 if (this.errorMessage) { Text(this.errorMessage) @@ -1366,7 +1372,7 @@ struct ClassLivePage { .textAlign(TextAlign.Center) .margin({ bottom: 16 }) } - + // 提交按钮 Button("提交答案") .width('100%') @@ -1395,7 +1401,7 @@ struct ClassLivePage { // 点击遮罩不关闭对话框 }) } - + // 获取题目统计数据 - 辅助方法 private getStatisticsData(): StatisticsData { return { @@ -1403,7 +1409,7 @@ struct ClassLivePage { getOptionStats: (index: number) => this.getOptionStats(index) }; } - + // 题目统计内容 @Builder QuestionStatsContent() { @@ -1413,17 +1419,17 @@ struct ClassLivePage { 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') @@ -1434,7 +1440,7 @@ struct ClassLivePage { .borderRadius(8) .backgroundColor('#f5f5f5') .margin({ bottom: 16 }) - + // 选项统计 if (this.currentQuestion) { ForEach(this.currentQuestion.options, (option: QuestionOption, index: number) => { @@ -1443,11 +1449,11 @@ struct ClassLivePage { Text(`选项 ${String.fromCharCode(65 + index)}`) .fontSize(16) .fontWeight(FontWeight.Medium) - + Text(`${this.getOptionStats(index).count}票`) .fontSize(16) .fontWeight(FontWeight.Medium) - + Text(`${this.getOptionStats(index).percentage}%`) .fontSize(16) .fontColor(this.getOptionStats(index).percentage > 50 ? '#67C23A' : '#F56C6C') @@ -1463,20 +1469,21 @@ struct ClassLivePage { } } } - + // 增加检查连接状态方法 private checkConnectionStatus() { try { const connectionStatus = this.classRoomService.isConnected() ? '已连接' : '未连接'; const messageCount = this.messages.length; const lastMessageId = this.messages.length > 0 ? this.messages[this.messages.length - 1].id : '无消息'; - + // 显示状态对话框 const primaryButton: DialogButton = { value: '确定', - action: () => {} + action: () => { + } }; - + const secondaryButton: DialogButton = { value: '重新连接', action: () => { @@ -1484,7 +1491,7 @@ struct ClassLivePage { this.classRoomService.reconnect(); } }; - + const alertConfig: AlertDialogConfig = { title: '连接状态检查', message: `连接状态: ${connectionStatus}\n消息数量: ${messageCount}\n最新消息ID: ${lastMessageId}`, @@ -1492,15 +1499,15 @@ struct ClassLivePage { secondaryButton: secondaryButton }; AlertDialog.show(alertConfig); - + // 记录日志 - logManager.info(LogCategory.CLASS, LogEventType.SYSTEM_INFO, - `连接状态: ${connectionStatus}, 消息数量: ${messageCount}, 最新消息ID: ${lastMessageId}`); + logManager.info(LogCategory.CLASS, LogEventType.SYSTEM_INFO, + `连接状态: ${connectionStatus}, 消息数量: ${messageCount}, 最新消息ID: ${lastMessageId}`); } catch (error) { console.error(`检查连接状态出错: ${error}`); } } - + // 题目统计窗口 @Builder QuestionStatsDialog() { @@ -1512,9 +1519,9 @@ struct ClassLivePage { Text("答题结果") .fontSize(20) .fontWeight(FontWeight.Bold) - + Blank() - + Button({ type: ButtonType.Circle }) { Image($r('app.media.close')) .width(16) @@ -1529,14 +1536,14 @@ struct ClassLivePage { } .width('100%') .margin({ bottom: 20 }) - + // 题目内容 Text(this.currentQuestion?.title || '') .fontSize(18) .fontWeight(FontWeight.Medium) .width('100%') .margin({ bottom: 16 }) - + // 统计数据 Column() { // 整体统计 @@ -1551,14 +1558,14 @@ struct ClassLivePage { 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') @@ -1569,14 +1576,14 @@ struct ClassLivePage { .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') @@ -1592,13 +1599,13 @@ struct ClassLivePage { .width('100%') .margin({ bottom: 20 }) } - + // 学生端也显示整体统计 this.QuestionStatsContent() } } .width('100%') - + // 关闭按钮 Button("确定") .width('100%')