feat: 监控和讲解增加心跳
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
This commit is contained in:
parent
228ee4ba68
commit
07ec772e9b
@ -144,6 +144,12 @@ class ClassroomMonitorService {
|
|||||||
status,
|
status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async heartbeat(classId: number | string) {
|
||||||
|
return await post(
|
||||||
|
`/school/classMonitor/heartbeat?classId=${encodeURIComponent(String(classId))}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const classroomMonitorService = new ClassroomMonitorService()
|
export const classroomMonitorService = new ClassroomMonitorService()
|
||||||
|
|||||||
@ -89,6 +89,13 @@ class TaskMgrService extends BasicService {
|
|||||||
return await post<any>(`/school/questionExplain/share`, data)
|
return await post<any>(`/school/questionExplain/share`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 题目讲解心跳
|
||||||
|
async questionExplainHeartbeat(classId: string) {
|
||||||
|
return await post<any>(
|
||||||
|
`/school/questionExplain/heartbeat?classId=${encodeURIComponent(classId)}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// 获取试卷详情
|
// 获取试卷详情
|
||||||
// async getpaperReleaseRecordDetail(recordId: any) {
|
// async getpaperReleaseRecordDetail(recordId: any) {
|
||||||
// return await get<any>(`/school/paperReleaseRecord/get/${recordId}`)
|
// return await get<any>(`/school/paperReleaseRecord/get/${recordId}`)
|
||||||
|
|||||||
@ -43,7 +43,9 @@ const studentSnapshotPreviewList = computed(() => {
|
|||||||
return studentSnapshots.value.map((item) => item.url).filter(Boolean)
|
return studentSnapshots.value.map((item) => item.url).filter(Boolean)
|
||||||
})
|
})
|
||||||
const SNAPSHOT_REFRESH_INTERVAL = 5000
|
const SNAPSHOT_REFRESH_INTERVAL = 5000
|
||||||
|
const MONITOR_HEARTBEAT_INTERVAL = 60 * 1000
|
||||||
let snapshotRefreshTimer: ReturnType<typeof setInterval> | undefined
|
let snapshotRefreshTimer: ReturnType<typeof setInterval> | undefined
|
||||||
|
let heartbeatTimer: ReturnType<typeof setInterval> | undefined
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadClassList()
|
loadClassList()
|
||||||
@ -51,6 +53,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
stopSnapshotAutoRefresh()
|
stopSnapshotAutoRefresh()
|
||||||
|
stopMonitorHeartbeat()
|
||||||
})
|
})
|
||||||
|
|
||||||
async function loadClassList() {
|
async function loadClassList() {
|
||||||
@ -64,6 +67,7 @@ async function loadClassList() {
|
|||||||
|
|
||||||
async function handleViewMonitor(item: ClassroomItem) {
|
async function handleViewMonitor(item: ClassroomItem) {
|
||||||
stopSnapshotAutoRefresh()
|
stopSnapshotAutoRefresh()
|
||||||
|
stopMonitorHeartbeat()
|
||||||
selectedClass.value = item
|
selectedClass.value = item
|
||||||
currentMonitor.value = undefined
|
currentMonitor.value = undefined
|
||||||
snapshots.value = []
|
snapshots.value = []
|
||||||
@ -79,8 +83,10 @@ async function loadMonitorDetail(classId = selectedClass.value?.id) {
|
|||||||
if (data?.monitoring) {
|
if (data?.monitoring) {
|
||||||
await loadSnapshots(classId)
|
await loadSnapshots(classId)
|
||||||
startSnapshotAutoRefresh()
|
startSnapshotAutoRefresh()
|
||||||
|
startMonitorHeartbeat()
|
||||||
} else {
|
} else {
|
||||||
stopSnapshotAutoRefresh()
|
stopSnapshotAutoRefresh()
|
||||||
|
stopMonitorHeartbeat()
|
||||||
snapshots.value = []
|
snapshots.value = []
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -130,12 +136,31 @@ function startSnapshotAutoRefresh() {
|
|||||||
}, SNAPSHOT_REFRESH_INTERVAL)
|
}, SNAPSHOT_REFRESH_INTERVAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startMonitorHeartbeat() {
|
||||||
|
if (heartbeatTimer) return
|
||||||
|
heartbeatTimer = setInterval(() => {
|
||||||
|
if (!isMonitoring.value || !selectedClass.value?.id) {
|
||||||
|
stopMonitorHeartbeat()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
void classroomMonitorService
|
||||||
|
.heartbeat(selectedClass.value.id)
|
||||||
|
.catch(() => undefined)
|
||||||
|
}, MONITOR_HEARTBEAT_INTERVAL)
|
||||||
|
}
|
||||||
|
|
||||||
function stopSnapshotAutoRefresh() {
|
function stopSnapshotAutoRefresh() {
|
||||||
if (!snapshotRefreshTimer) return
|
if (!snapshotRefreshTimer) return
|
||||||
clearInterval(snapshotRefreshTimer)
|
clearInterval(snapshotRefreshTimer)
|
||||||
snapshotRefreshTimer = undefined
|
snapshotRefreshTimer = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stopMonitorHeartbeat() {
|
||||||
|
if (!heartbeatTimer) return
|
||||||
|
clearInterval(heartbeatTimer)
|
||||||
|
heartbeatTimer = undefined
|
||||||
|
}
|
||||||
|
|
||||||
async function updateMonitorStatus(status: 0 | 1) {
|
async function updateMonitorStatus(status: 0 | 1) {
|
||||||
if (!selectedClass.value?.id) return
|
if (!selectedClass.value?.id) return
|
||||||
const option = status === 1 ? '开启' : '停止'
|
const option = status === 1 ? '开启' : '停止'
|
||||||
@ -151,6 +176,7 @@ async function updateMonitorStatus(status: 0 | 1) {
|
|||||||
|
|
||||||
function backToClassList() {
|
function backToClassList() {
|
||||||
stopSnapshotAutoRefresh()
|
stopSnapshotAutoRefresh()
|
||||||
|
stopMonitorHeartbeat()
|
||||||
selectedClass.value = undefined
|
selectedClass.value = undefined
|
||||||
currentMonitor.value = undefined
|
currentMonitor.value = undefined
|
||||||
snapshots.value = []
|
snapshots.value = []
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onActivated, onBeforeUnmount, onMounted, ref } from 'vue'
|
import {
|
||||||
|
computed,
|
||||||
|
onActivated,
|
||||||
|
onBeforeUnmount,
|
||||||
|
onDeactivated,
|
||||||
|
onMounted,
|
||||||
|
ref,
|
||||||
|
} from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
import { taskMgrService } from '#/api/service/question-bank/task-mgr'
|
import { taskMgrService } from '#/api/service/question-bank/task-mgr'
|
||||||
@ -80,6 +87,7 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onActivated(() => {
|
onActivated(() => {
|
||||||
|
stopQuestionExplainHeartbeat()
|
||||||
phase.value = 'detail'
|
phase.value = 'detail'
|
||||||
countdown.value = 3
|
countdown.value = 3
|
||||||
currentIndex.value = 0
|
currentIndex.value = 0
|
||||||
@ -92,6 +100,8 @@ type ExplainPhase = 'countdown' | 'detail' | 'explaining'
|
|||||||
const phase = ref<ExplainPhase>('detail')
|
const phase = ref<ExplainPhase>('detail')
|
||||||
const countdown = ref(3)
|
const countdown = ref(3)
|
||||||
let countdownTimer: null | ReturnType<typeof setInterval> = null
|
let countdownTimer: null | ReturnType<typeof setInterval> = null
|
||||||
|
const QUESTION_EXPLAIN_HEARTBEAT_INTERVAL = 60 * 1000
|
||||||
|
let questionExplainHeartbeatTimer: ReturnType<typeof setInterval> | undefined
|
||||||
|
|
||||||
function startCountdown() {
|
function startCountdown() {
|
||||||
document.documentElement.requestFullscreen?.().catch(() => {})
|
document.documentElement.requestFullscreen?.().catch(() => {})
|
||||||
@ -121,6 +131,11 @@ function clearCountdownTimer() {
|
|||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
clearCountdownTimer()
|
clearCountdownTimer()
|
||||||
|
stopQuestionExplainHeartbeat()
|
||||||
|
})
|
||||||
|
|
||||||
|
onDeactivated(() => {
|
||||||
|
stopQuestionExplainHeartbeat()
|
||||||
})
|
})
|
||||||
|
|
||||||
// ======================== 拖动分隔条 ========================
|
// ======================== 拖动分隔条 ========================
|
||||||
@ -260,12 +275,34 @@ async function enterExplaining() {
|
|||||||
collapseAnswerAnalysis()
|
collapseAnswerAnalysis()
|
||||||
phase.value = 'explaining'
|
phase.value = 'explaining'
|
||||||
currentIndex.value = 0
|
currentIndex.value = 0
|
||||||
await callExplainApi(1)
|
const started = await callExplainApi(1)
|
||||||
|
if (started) {
|
||||||
|
startQuestionExplainHeartbeat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startQuestionExplainHeartbeat() {
|
||||||
|
if (questionExplainHeartbeatTimer) return
|
||||||
|
questionExplainHeartbeatTimer = setInterval(() => {
|
||||||
|
if (phase.value !== 'explaining' || !classId) {
|
||||||
|
stopQuestionExplainHeartbeat()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
void taskMgrService
|
||||||
|
.questionExplainHeartbeat(classId)
|
||||||
|
.catch(() => undefined)
|
||||||
|
}, QUESTION_EXPLAIN_HEARTBEAT_INTERVAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopQuestionExplainHeartbeat() {
|
||||||
|
if (!questionExplainHeartbeatTimer) return
|
||||||
|
clearInterval(questionExplainHeartbeatTimer)
|
||||||
|
questionExplainHeartbeatTimer = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
async function callExplainApi(status: number, overrideQuestionId?: string) {
|
async function callExplainApi(status: number, overrideQuestionId?: string) {
|
||||||
const questionId = overrideQuestionId || getTitleKey(currentQuestion.value)
|
const questionId = overrideQuestionId || getTitleKey(currentQuestion.value)
|
||||||
if (!questionId) return
|
if (!questionId) return false
|
||||||
const recordId = resourceId
|
const recordId = resourceId
|
||||||
try {
|
try {
|
||||||
await taskMgrService.questionExplainShare({
|
await taskMgrService.questionExplainShare({
|
||||||
@ -274,8 +311,10 @@ async function callExplainApi(status: number, overrideQuestionId?: string) {
|
|||||||
recordId,
|
recordId,
|
||||||
status,
|
status,
|
||||||
})
|
})
|
||||||
|
return true
|
||||||
} catch {
|
} catch {
|
||||||
hud.error(status === 1 ? '开始讲解失败' : '结束讲解失败')
|
hud.error(status === 1 ? '开始讲解失败' : '结束讲解失败')
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,6 +346,7 @@ function endExplaining() {
|
|||||||
title: '提示',
|
title: '提示',
|
||||||
zIndex: 999_999,
|
zIndex: 999_999,
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
|
stopQuestionExplainHeartbeat()
|
||||||
const lastQuestion = paperDetail.value[paperDetail.value.length - 1]
|
const lastQuestion = paperDetail.value[paperDetail.value.length - 1]
|
||||||
const lastQuestionId = getTitleKey(lastQuestion)
|
const lastQuestionId = getTitleKey(lastQuestion)
|
||||||
await callExplainApi(0, lastQuestionId)
|
await callExplainApi(0, lastQuestionId)
|
||||||
|
|||||||
@ -34,7 +34,7 @@ export default defineConfig(async () => {
|
|||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://43.136.52.196:9053',
|
target: 'http://43.136.52.196:9053',
|
||||||
// target: 'https://admin.xuexiaole.com',
|
// target: 'https://admin.lingxixue.com',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user