diff --git a/src/api/global.ts b/src/api/global.ts index 8d7491c..ebfe748 100644 --- a/src/api/global.ts +++ b/src/api/global.ts @@ -111,6 +111,14 @@ export interface UserData { port: number | null sharing: boolean } + /** 学生课堂监控投屏状态(getHomePage 返回) */ + studentScreenShareState?: { + classId: string + ip: string | null + port: number | null + sharing: boolean + startedAt?: number + } isTeacher: boolean // 是否为可以接受任务的学员 isTaskStudent: boolean diff --git a/src/api/subject/studentScreenShare.ts b/src/api/subject/studentScreenShare.ts new file mode 100644 index 0000000..ecfd708 --- /dev/null +++ b/src/api/subject/studentScreenShare.ts @@ -0,0 +1,20 @@ +import { get } from '../request' + +export type StudentScreenShareState = { + classId: number + ip: string + lastHeartbeatAt: number + port: number + sharing: boolean + startedAt: number + ttlSeconds: number +} + +const studentScreenShareApi = { + /** 查询当前班级学生监控投屏状态 */ + state: async (classId: string) => { + return await get('/school/studentScreenShare/state', { classId }) + }, +} + +export default studentScreenShareApi diff --git a/src/hooks/useClassMonitor.ts b/src/hooks/useClassMonitor.ts index 34b2b63..b73faf6 100644 --- a/src/hooks/useClassMonitor.ts +++ b/src/hooks/useClassMonitor.ts @@ -1,3 +1,4 @@ +import studentScreenShareApi from '@/api/subject/studentScreenShare' import { $XXL } from '@/utils' import { userStore } from '@/stores' import { useTips } from './useTips' @@ -183,6 +184,63 @@ export const handleClassMonitorMessage = (data: anyObj) => { return false } +const resolveStudentScreenShareClassId = (msg: Record): string => { + if (msg.classId !== undefined && msg.classId !== null && String(msg.classId).trim() !== '') { + return String(msg.classId) + } + const userClassId = userStore().userInfo?.classId + if (userClassId !== undefined && userClassId !== null && String(userClassId).trim() !== '') { + return String(userClassId) + } + return '' +} + +/** + * 同步 getHomePage 返回的 studentScreenShareState + * sharing 为 true 时开启学生监控投屏,否则停止 + */ +export const syncStudentScreenShareState = async (state: unknown) => { + const stateInfo = state && typeof state === 'object' ? (state as Record) : {} + const screenShare = stateInfo.studentScreenShareState + if (!screenShare || typeof screenShare !== 'object') return + + const share = screenShare as Record + if (share.sharing !== true) { + stopClassMonitor(false) + return + } + + let msg: anyObj = { ...share } + const host = msg.ip !== undefined && msg.ip !== null ? String(msg.ip) : '' + const port = Number(msg.port) + if (!host || !Number.isFinite(port)) { + const classId = resolveStudentScreenShareClassId(msg) + if (!classId) return + try { + const res = await studentScreenShareApi.state(classId) + if (!res?.sharing) return + msg = { ...msg, ip: res.ip, port: res.port, classId: res.classId ?? classId } + } catch (err) { + monitorWarn('首页学生监控状态补全失败:', err) + return + } + } + + const sessionKey = getMonitorSessionKey(msg) + if (monitorState === 1 && sessionKey && monitorSessionId === sessionKey) { + applyStudentShareStart(msg) + return + } + + stopClassMonitor() + monitorSessionId = sessionKey + monitorState = 1 + if (!applyStudentShareStart(msg)) { + monitorState = 0 + monitorSessionId = '' + } +} + export const syncClassMonitorState = (state: unknown) => { const stateInfo = state && typeof state === 'object' ? (state as anyObj) : {} const nextState = Number(stateInfo.monitorState ?? state) diff --git a/src/stores/user.ts b/src/stores/user.ts index f737b99..445762f 100644 --- a/src/stores/user.ts +++ b/src/stores/user.ts @@ -8,7 +8,7 @@ import type { Option, UserData } from '@/api/global' 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 { syncClassMonitorState, syncStudentScreenShareState } from '@/hooks/useClassMonitor' import { syncTeacherScreenShareState } from '@/hooks/useTeacherExplain' import { globalStore } from '@/stores' import { storeToRefs } from 'pinia' @@ -108,6 +108,10 @@ export const userStore = defineStore('user', () => { const updateUserInfo = async (info: any) => { if (!info) return const hasMonitorState = Object.prototype.hasOwnProperty.call(info, 'monitorState') + const hasStudentScreenShareState = Object.prototype.hasOwnProperty.call( + info, + 'studentScreenShareState', + ) const hasScreenShareState = Object.prototype.hasOwnProperty.call(info, 'screenShareState') info = { ...userInfo.value, ...info } @@ -123,6 +127,12 @@ export const userStore = defineStore('user', () => { syncClassMonitorState(info) } + if (hasStudentScreenShareState) { + syncStudentScreenShareState(info).catch(err => { + console.warn('[User] 同步学生监控投屏状态失败:', err) + }) + } + if (hasScreenShareState) { syncTeacherScreenShareState(info).catch(err => { console.warn('[User] 同步投屏状态失败:', err)