From 9655f4742a6e0ce7825f79797666a95b7546d0a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E6=A2=A6?= Date: Fri, 15 May 2026 14:38:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20homepage=E9=87=8C=E9=9D=A2=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E7=9B=B4=E6=92=AD=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/global.ts | 7 +++ src/hooks/useTeacherExplain.ts | 92 +++++++++++++++++++++++++++------- src/stores/user.ts | 8 +++ 3 files changed, 88 insertions(+), 19 deletions(-) diff --git a/src/api/global.ts b/src/api/global.ts index 7c24ab9..8d7491c 100644 --- a/src/api/global.ts +++ b/src/api/global.ts @@ -104,6 +104,13 @@ export interface UserData { workMode: number // 监控状态 0: 未开启 1: 已开启 monitorState: number + /** 老师投屏状态(getHomePage 返回) */ + screenShareState?: { + classId: string + ip: string | null + port: number | null + sharing: boolean + } isTeacher: boolean // 是否为可以接受任务的学员 isTaskStudent: boolean diff --git a/src/hooks/useTeacherExplain.ts b/src/hooks/useTeacherExplain.ts index 0fbdd9f..c0c5400 100644 --- a/src/hooks/useTeacherExplain.ts +++ b/src/hooks/useTeacherExplain.ts @@ -140,6 +140,77 @@ function startTeacherScreenSharePolling(classId: string) { }, TEACHER_SCREEN_SHARE_STATE_POLL_INTERVAL) } +function resolveTeacherScreenSharePollClassId(msg: Record): string { + if (msg.classId !== undefined && msg.classId !== null && String(msg.classId).trim() !== '') { + return String(msg.classId) + } + if (classInfo.value?.classId !== undefined && classInfo.value?.classId !== null) { + return String(classInfo.value.classId) + } + const userClassId = userStore().userInfo?.classId + if (userClassId !== undefined && userClassId !== null && String(userClassId).trim() !== '') { + return String(userClassId) + } + return '' +} + +/** 启动老师投屏直播(原生 + 状态轮询) */ +function applyTeacherScreenShareStart(msg: Record): boolean { + const host = msg.ip !== undefined && msg.ip !== null ? String(msg.ip) : '' + const port = Number(msg.port) + if (!host || !Number.isFinite(port)) return false + + const u = userStore().userInfo + $XXL.startTeacherShareScreen({ + host, + port, + name: String(u?.name || u?.nickName || ''), + id: String(u?.userId ?? ''), + }) + const pollClassId = resolveTeacherScreenSharePollClassId(msg) + if (pollClassId) startTeacherScreenSharePolling(pollClassId) + return true +} + +function stopTeacherScreenShareLive() { + stopTeacherScreenSharePolling() + $XXL.stopTeacherShareScreen() +} + +/** + * 同步 getHomePage 返回的 screenShareState + * sharing 为 true 时走直播逻辑,否则结束投屏 + */ +export const syncTeacherScreenShareState = async (state: unknown) => { + const stateInfo = state && typeof state === 'object' ? (state as Record) : {} + const screenShare = stateInfo.screenShareState + if (!screenShare || typeof screenShare !== 'object') return + + const share = screenShare as Record + if (share.sharing !== true) { + stopTeacherScreenShareLive() + return + } + + let msg = { ...share } + const host = msg.ip !== undefined && msg.ip !== null ? String(msg.ip) : '' + const port = Number(msg.port) + if (!host || !Number.isFinite(port)) { + const classId = resolveTeacherScreenSharePollClassId(msg) + if (!classId) return + try { + const res = await teacherScreenShareApi.state(classId) + if (!res?.sharing) return + msg = { ...msg, ip: res.ip, port: res.port, classId: res.classId ?? classId } + } catch (err) { + console.warn('[TeacherExplain] 首页投屏状态补全失败:', err) + return + } + } + + applyTeacherScreenShareStart(msg) +} + /** 老师投屏开始/结束:调原生桥接 */ function handleTeacherScreenShareMessage(data: TeacherWsMessage) { const root = resolveTeacherScreenSharePayload(data) @@ -148,29 +219,12 @@ function handleTeacherScreenShareMessage(data: TeacherWsMessage) { if (root.type === 'teacherScreenShareStart') { const msg = parseMsgObject(root.msg) if (!msg) return - const host = msg.ip !== undefined && msg.ip !== null ? String(msg.ip) : '' - const port = Number(msg.port) - if (!host || !Number.isFinite(port)) return - const u = userStore().userInfo - $XXL.startTeacherShareScreen({ - host, - port, - name: String(u?.name || u?.nickName || ''), - id: String(u?.userId ?? ''), - }) - const pollClassId = - msg.classId !== undefined && msg.classId !== null && String(msg.classId).trim() !== '' - ? String(msg.classId) - : classInfo.value?.classId !== undefined && classInfo.value?.classId !== null - ? String(classInfo.value.classId) - : '' - if (pollClassId) startTeacherScreenSharePolling(pollClassId) + applyTeacherScreenShareStart(msg) return } if (root.type === 'teacherScreenShareStop') { - stopTeacherScreenSharePolling() - $XXL.stopTeacherShareScreen() + stopTeacherScreenShareLive() } } diff --git a/src/stores/user.ts b/src/stores/user.ts index f7c72a7..f737b99 100644 --- a/src/stores/user.ts +++ b/src/stores/user.ts @@ -9,6 +9,7 @@ import type { School } from '@/api/studentManage' import { setCache, clearCache, getSN, getManufacturer, isStudyMachine, isWebEvn } from '@/utils' import { LogType, useLog, useSocket } from '@/hooks' import { syncClassMonitorState } from '@/hooks/useClassMonitor' +import { syncTeacherScreenShareState } from '@/hooks/useTeacherExplain' import { globalStore } from '@/stores' import { storeToRefs } from 'pinia' import obj from '@/utils/obj' @@ -107,6 +108,7 @@ export const userStore = defineStore('user', () => { const updateUserInfo = async (info: any) => { if (!info) return const hasMonitorState = Object.prototype.hasOwnProperty.call(info, 'monitorState') + const hasScreenShareState = Object.prototype.hasOwnProperty.call(info, 'screenShareState') info = { ...userInfo.value, ...info } if (typeof info.screenLock !== 'undefined') { @@ -121,6 +123,12 @@ export const userStore = defineStore('user', () => { syncClassMonitorState(info) } + if (hasScreenShareState) { + syncTeacherScreenShareState(info).catch(err => { + console.warn('[User] 同步投屏状态失败:', err) + }) + } + if (typeof info.schoolClass === 'string') { info.schoolClass = JSON.parse(info.schoolClass) if (info.schoolClass?.length) {