feat(plan): 支持学案生成状态轮询与进度显示
- 新增接口检查学案是否正在生成,防止重复生成任务 - 使用 Redis 缓存标识学案生成状态,设置 12 分钟过期时间 - 生成学案时记录状态至 Redis,生成完成后自动清除 - Vue 学案列表新增学案生成进度条显示与已生成标签 - 新增组件事件监听生成成功,触发轮询检测学案状态 - 轮询间隔 10 秒,动态更新学案生成进度,最高至 95% - 路由离开与组件卸载时停止所有轮询,防止内存泄漏 - 优化学案生成逻辑,新增小测试卷自动关联及数据入库 - 更新配置文件模板路径,提高文档管理一致性
This commit is contained in:
@@ -1,10 +1,17 @@
|
|||||||
package com.yinlihupo.enlish.service.constant;
|
package com.yinlihupo.enlish.service.constant;
|
||||||
|
|
||||||
public interface LessonPlanConstant {
|
public class LessonPlanConstant {
|
||||||
|
|
||||||
String TITLE = "Title";
|
public static final String TITLE = "Title";
|
||||||
String PASSAGE = "ThePassage";
|
public static final String PASSAGE = "ThePassage";
|
||||||
String QUIZ = "Quiz";
|
public static final String QUIZ = "Quiz";
|
||||||
String ANSWER_KEY_EXPLANATION = "AnswerKey&Explanation";
|
public static final String ANSWER_KEY_EXPLANATION = "AnswerKey&Explanation";
|
||||||
String FULL_TRANSLATION = "FullTranslation";
|
public static final String FULL_TRANSLATION = "FullTranslation";
|
||||||
|
|
||||||
|
// 正在生成学案标识
|
||||||
|
public static final String GENERATING_PLAN = "GeneratingPlan";
|
||||||
|
|
||||||
|
public static String buildGeneratePlanContent(Integer studentId) {
|
||||||
|
return GENERATING_PLAN + ":" + studentId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.yinlihupo.enlish.service.controller;
|
package com.yinlihupo.enlish.service.controller;
|
||||||
|
|
||||||
|
import com.yinlihupo.enlish.service.constant.LessonPlanConstant;
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.LessonPlansDO;
|
import com.yinlihupo.enlish.service.domain.dataobject.LessonPlansDO;
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.VocabularyBankDO;
|
import com.yinlihupo.enlish.service.domain.dataobject.VocabularyBankDO;
|
||||||
import com.yinlihupo.enlish.service.model.vo.plan.*;
|
import com.yinlihupo.enlish.service.model.vo.plan.*;
|
||||||
@@ -13,6 +14,7 @@ import jakarta.annotation.Resource;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -30,6 +32,9 @@ public class LessonPlanController {
|
|||||||
@Resource
|
@Resource
|
||||||
private LessonPlansService lessonPlanService;
|
private LessonPlansService lessonPlanService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
@Resource(name = "taskExecutor")
|
@Resource(name = "taskExecutor")
|
||||||
private Executor taskExecutor;
|
private Executor taskExecutor;
|
||||||
@Resource
|
@Resource
|
||||||
@@ -47,6 +52,9 @@ public class LessonPlanController {
|
|||||||
Integer unitId = addLessonPlanReqVO.getUnitId();
|
Integer unitId = addLessonPlanReqVO.getUnitId();
|
||||||
Integer wordSize = addLessonPlanReqVO.getWordSize();
|
Integer wordSize = addLessonPlanReqVO.getWordSize();
|
||||||
try {
|
try {
|
||||||
|
if (redisTemplate.opsForValue().get(LessonPlanConstant.buildGeneratePlanContent(studentId)) != null) {
|
||||||
|
throw new RuntimeException("学案正常生成中");
|
||||||
|
}
|
||||||
taskExecutor.execute(() -> lessonPlanService.generateLessonPlans(studentId, unitId, wordSize));
|
taskExecutor.execute(() -> lessonPlanService.generateLessonPlans(studentId, unitId, wordSize));
|
||||||
return Response.success("生成学案成功,请等待 10 分钟");
|
return Response.success("生成学案成功,请等待 10 分钟");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -96,4 +104,17 @@ public class LessonPlanController {
|
|||||||
public void findPlanWordVoiceTTS(@RequestBody FindWordTTSVoiceReqVO findWordVoiceReqVO, HttpServletResponse response) {
|
public void findPlanWordVoiceTTS(@RequestBody FindWordTTSVoiceReqVO findWordVoiceReqVO, HttpServletResponse response) {
|
||||||
ttsUtil.generateWordVoice(findWordVoiceReqVO.getText(), response);
|
ttsUtil.generateWordVoice(findWordVoiceReqVO.getText(), response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("check")
|
||||||
|
@ApiOperationLog(description = "检测学案是否在生成")
|
||||||
|
public Response<String> checkLessonPlan(@RequestBody FindIsGeneratePlanReqVO findIsGeneratePlanReqVO) {
|
||||||
|
Integer studentId = findIsGeneratePlanReqVO.getStudentId();
|
||||||
|
String key = LessonPlanConstant.buildGeneratePlanContent(studentId);
|
||||||
|
if (redisTemplate.opsForValue().get(key) != null) {
|
||||||
|
return Response.fail();
|
||||||
|
}
|
||||||
|
return Response.success("学案生成完成");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.yinlihupo.enlish.service.model.vo.plan;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class FindIsGeneratePlanReqVO {
|
||||||
|
|
||||||
|
private Integer studentId;
|
||||||
|
}
|
||||||
@@ -12,12 +12,14 @@ import com.yinlihupo.framework.common.util.JsonUtils;
|
|||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -45,10 +47,17 @@ public class LessonPlansServiceImpl implements LessonPlansService {
|
|||||||
private StudentDOMapper studentDOMapper;
|
private StudentDOMapper studentDOMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private PlanExamDOMapper planExamDOMapper;
|
private PlanExamDOMapper planExamDOMapper;
|
||||||
|
@Resource
|
||||||
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generateLessonPlans(Integer studentId, Integer unitId, Integer wordSize) {
|
public void generateLessonPlans(Integer studentId, Integer unitId, Integer wordSize) {
|
||||||
|
String key = LessonPlanConstant.buildGeneratePlanContent(studentId);
|
||||||
|
redisTemplate.opsForValue().set(key, studentId);
|
||||||
|
redisTemplate.expire( key, 12, TimeUnit.MINUTES);
|
||||||
|
|
||||||
|
log.info("开始生成计划");
|
||||||
List<VocabularyBankDO> vocabularyBankDOS = vocabularyBankDOMapper.selectVocabularyBankDOAllByUnitId(unitId);
|
List<VocabularyBankDO> vocabularyBankDOS = vocabularyBankDOMapper.selectVocabularyBankDOAllByUnitId(unitId);
|
||||||
UnitDO unitDO = unitDOMapper.selectByPrimaryKey(unitId);
|
UnitDO unitDO = unitDOMapper.selectByPrimaryKey(unitId);
|
||||||
GradeUnitDO gradeUnitDO = gradeUnitDOMapper.selectByUnitId(unitId);
|
GradeUnitDO gradeUnitDO = gradeUnitDOMapper.selectByUnitId(unitId);
|
||||||
@@ -146,7 +155,7 @@ public class LessonPlansServiceImpl implements LessonPlansService {
|
|||||||
Map<String, Object> map = generateWeekendPlans(checkList, i + 6, gradeDO, unitDO, studentId);
|
Map<String, Object> map = generateWeekendPlans(checkList, i + 6, gradeDO, unitDO, studentId);
|
||||||
|
|
||||||
LessonPlansDO lessonPlansDO = LessonPlansDO.builder()
|
LessonPlansDO lessonPlansDO = LessonPlansDO.builder()
|
||||||
.title(map.get("title").toString())
|
.title(map.get("examStr").toString() + "复习")
|
||||||
.gradeId(gradeDO.getId().toString())
|
.gradeId(gradeDO.getId().toString())
|
||||||
.unitId(unitDO.getId())
|
.unitId(unitDO.getId())
|
||||||
.createdAt(LocalDateTime.now())
|
.createdAt(LocalDateTime.now())
|
||||||
@@ -154,6 +163,13 @@ public class LessonPlansServiceImpl implements LessonPlansService {
|
|||||||
.build();
|
.build();
|
||||||
lessonPlansDOMapper.insert(lessonPlansDO);
|
lessonPlansDOMapper.insert(lessonPlansDO);
|
||||||
|
|
||||||
|
Integer examId = (Integer) map.get("examId");
|
||||||
|
PlanExamDO planExamDO = PlanExamDO.builder()
|
||||||
|
.planId(lessonPlansDO.getId())
|
||||||
|
.examId(examId)
|
||||||
|
.build();
|
||||||
|
planExamDOMapper.insert(planExamDO);
|
||||||
|
|
||||||
StudentLessonPlansDO studentLessonPlansDO = StudentLessonPlansDO.builder()
|
StudentLessonPlansDO studentLessonPlansDO = StudentLessonPlansDO.builder()
|
||||||
.studentId(studentId)
|
.studentId(studentId)
|
||||||
.planId(lessonPlansDO.getId())
|
.planId(lessonPlansDO.getId())
|
||||||
@@ -177,13 +193,32 @@ public class LessonPlansServiceImpl implements LessonPlansService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Map<String, Object> generateWeekendPlans(List<VocabularyBankDO> checkList,
|
private Map<String, Object> generateWeekendPlans(List<VocabularyBankDO> words,
|
||||||
int day,
|
int day,
|
||||||
GradeDO gradeDO, UnitDO unitDO, Integer studentId) throws IOException {
|
GradeDO gradeDO, UnitDO unitDO, Integer studentId) throws IOException {
|
||||||
|
|
||||||
Map<String, Object> data = new HashMap<>();
|
Map<String, Object> data = new HashMap<>();
|
||||||
data.put("title", "第" + day + "天" + "复习" + gradeDO.getTitle() + unitDO.getTitle() + studentId);
|
words.forEach(word -> word.setDefinition(word.getDefinition().length() > 5 ? word.getDefinition().substring(0, 5) : word.getDefinition()));
|
||||||
data.put("checkList", checkList);
|
List<Integer> wordIds = words.stream().map(VocabularyBankDO::getId).toList();
|
||||||
|
StudentDO studentDO = studentDOMapper.selectStudentById(studentId);
|
||||||
|
|
||||||
|
String ExamTitle = gradeDO.getTitle() + unitDO.getTitle() + "教案小测第" + ExamWordsConstant.day2Chinese(day) + "天" + studentDO.getName();
|
||||||
|
ExamWordsDO examWordsDO = ExamWordsDO.builder()
|
||||||
|
.gradeId(gradeDO.getId())
|
||||||
|
.level(1)
|
||||||
|
.wordIds(wordIds)
|
||||||
|
.type(ExamWordsConstant.EXAM_TYPE_TEST)
|
||||||
|
.title(ExamTitle)
|
||||||
|
.createdAt(LocalDateTime.now())
|
||||||
|
.build();
|
||||||
|
examWordsDOMapper.insert(examWordsDO);
|
||||||
|
studentExamWordsDOMapper.insertStudentsExam(studentId, examWordsDO.getId());
|
||||||
|
|
||||||
|
data.put("examId", examWordsDO.getId());
|
||||||
|
data.put("studentId", studentId);
|
||||||
|
data.put("studentStr", studentDO.getName());
|
||||||
|
data.put("examStr", ExamTitle);
|
||||||
|
data.put("words", words);
|
||||||
// LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
|
// LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
|
||||||
// Configure config = Configure.builder()
|
// Configure config = Configure.builder()
|
||||||
// .bind("checkList", policy)
|
// .bind("checkList", policy)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ templates:
|
|||||||
data: C:\project\tess
|
data: C:\project\tess
|
||||||
plan:
|
plan:
|
||||||
weekday: C:\project\java\enlish_edu\enlish\enlish-service\src\main\resources\templates\tem_study_plan_v5.docx
|
weekday: C:\project\java\enlish_edu\enlish\enlish-service\src\main\resources\templates\tem_study_plan_v5.docx
|
||||||
weekend: C:\project\java\enlish_edu\enlish\enlish-service\src\main\resources\templates\study_plan_review_v1.docx
|
weekend: C:\project\java\enlish_edu\enlish\enlish-service\src\main\resources\templates\study_plan_review_v2.docx
|
||||||
plan_day: 7
|
plan_day: 7
|
||||||
tmp:
|
tmp:
|
||||||
png: C:\project\java\enlish_edu\enlish\enlish-service\src\main\resources\tmp\png\
|
png: C:\project\java\enlish_edu\enlish\enlish-service\src\main\resources\tmp\png\
|
||||||
|
|||||||
Binary file not shown.
@@ -51,6 +51,12 @@ export function getLessonPlanWords(planId) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function checkIsGenerated(studentId) {
|
||||||
|
return axios.post('plan/check', {
|
||||||
|
studentId: studentId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const resolveBlob = (res, fileName) => {
|
const resolveBlob = (res, fileName) => {
|
||||||
// 创建 Blob 对象,可以指定 type,也可以让浏览器自动推断
|
// 创建 Blob 对象,可以指定 type,也可以让浏览器自动推断
|
||||||
const blob = new Blob([res], { type: 'application/octet-stream' });
|
const blob = new Blob([res], { type: 'application/octet-stream' });
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const props = defineProps({
|
|||||||
modelValue: { type: Boolean, default: false },
|
modelValue: { type: Boolean, default: false },
|
||||||
studentId: { type: [Number, String], required: true }
|
studentId: { type: [Number, String], required: true }
|
||||||
})
|
})
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue', 'success'])
|
||||||
|
|
||||||
const visible = computed({
|
const visible = computed({
|
||||||
get: () => props.modelValue,
|
get: () => props.modelValue,
|
||||||
@@ -71,6 +71,7 @@ async function handleGenerate() {
|
|||||||
const d = res?.data
|
const d = res?.data
|
||||||
if (d.success) {
|
if (d.success) {
|
||||||
ElMessage.success('生成学案任务已提交,请等待十分钟')
|
ElMessage.success('生成学案任务已提交,请等待十分钟')
|
||||||
|
emit('success', { studentId: Number(props.studentId) })
|
||||||
visible.value = false
|
visible.value = false
|
||||||
} else {
|
} else {
|
||||||
showMessage(d.message || '生成学案失败,请联系管理员', 'error')
|
showMessage(d.message || '生成学案失败,请联系管理员', 'error')
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
<div class="p-3">
|
<div class="p-3">
|
||||||
<div class="text-sm font-semibold mb-2">学案</div>
|
<div class="text-sm font-semibold mb-2">学案</div>
|
||||||
<el-table :data="row.plans || []" size="small" border>
|
<el-table :data="row.plans || []" size="small" border>
|
||||||
<el-table-column prop="id" label="计划ID" width="100" />
|
|
||||||
<el-table-column prop="title" label="标题" min-width="280" />
|
<el-table-column prop="title" label="标题" min-width="280" />
|
||||||
<el-table-column label="状态" width="120">
|
<el-table-column label="状态" width="120">
|
||||||
<template #default="{ row: plan }">
|
<template #default="{ row: plan }">
|
||||||
@@ -50,7 +49,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="id" label="学生ID" width="100" />
|
|
||||||
<el-table-column prop="name" label="姓名" min-width="120" />
|
<el-table-column prop="name" label="姓名" min-width="120" />
|
||||||
<el-table-column prop="className" label="班级" min-width="120" />
|
<el-table-column prop="className" label="班级" min-width="120" />
|
||||||
<el-table-column prop="gradeName" label="年级" min-width="120" />
|
<el-table-column prop="gradeName" label="年级" min-width="120" />
|
||||||
|
|||||||
@@ -59,6 +59,18 @@
|
|||||||
<el-table-column prop="name" label="姓名" min-width="120" />
|
<el-table-column prop="name" label="姓名" min-width="120" />
|
||||||
<el-table-column prop="className" label="班级" min-width="120" />
|
<el-table-column prop="className" label="班级" min-width="120" />
|
||||||
<el-table-column prop="gradeName" label="年级" min-width="120" />
|
<el-table-column prop="gradeName" label="年级" min-width="120" />
|
||||||
|
<el-table-column prop="phone" label="学案" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<template v-if="generatingPercents[row.id] !== undefined">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<el-progress :percentage="generatingPercents[row.id]" :stroke-width="8" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-tag type="info" effect="plain">已生成</el-tag>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="180" fixed="right">
|
<el-table-column label="操作" width="180" fixed="right">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button type="primary" size="small" @click.stop="onViewStudent(row)">详情</el-button>
|
<el-button type="primary" size="small" @click.stop="onViewStudent(row)">详情</el-button>
|
||||||
@@ -85,6 +97,7 @@
|
|||||||
<LessonPlanDialog
|
<LessonPlanDialog
|
||||||
v-model="showLessonPlanDialog"
|
v-model="showLessonPlanDialog"
|
||||||
:student-id="selectedStudentIds[0]"
|
:student-id="selectedStudentIds[0]"
|
||||||
|
@success="onLessonPlanGenerateSuccess"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -110,7 +123,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import Header from '@/layouts/components/Header.vue'
|
import Header from '@/layouts/components/Header.vue'
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted, onUnmounted } from 'vue'
|
||||||
import { getClassList, deleteClass } from '@/api/class'
|
import { getClassList, deleteClass } from '@/api/class'
|
||||||
import { getGradeList, deleteGrade } from '@/api/grade'
|
import { getGradeList, deleteGrade } from '@/api/grade'
|
||||||
import { getStudentList, deleteStudent } from '@/api/student'
|
import { getStudentList, deleteStudent } from '@/api/student'
|
||||||
@@ -121,8 +134,9 @@ import AddStudentDialog from '@/layouts/components/AddStudentDialog.vue'
|
|||||||
import LessonPlanDialog from '@/layouts/components/LessonPlanDialog.vue'
|
import LessonPlanDialog from '@/layouts/components/LessonPlanDialog.vue'
|
||||||
import { getUnitList, deleteUnit } from '@/api/unit'
|
import { getUnitList, deleteUnit } from '@/api/unit'
|
||||||
import AddUnitDialog from '@/layouts/components/AddUnitDialog.vue'
|
import AddUnitDialog from '@/layouts/components/AddUnitDialog.vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter, onBeforeRouteLeave } from 'vue-router'
|
||||||
import { ElMessageBox } from 'element-plus'
|
import { ElMessageBox } from 'element-plus'
|
||||||
|
import { checkIsGenerated } from '@/api/plan'
|
||||||
|
|
||||||
const classes = ref([])
|
const classes = ref([])
|
||||||
const pageNo = ref(1)
|
const pageNo = ref(1)
|
||||||
@@ -155,6 +169,8 @@ const selectedStudentIds = ref([])
|
|||||||
const showGenerateDialog = ref(false)
|
const showGenerateDialog = ref(false)
|
||||||
const showAddStudentDialog = ref(false)
|
const showAddStudentDialog = ref(false)
|
||||||
const showLessonPlanDialog = ref(false)
|
const showLessonPlanDialog = ref(false)
|
||||||
|
const generatingPercents = ref({})
|
||||||
|
const pollingTimers = {}
|
||||||
|
|
||||||
const units = ref([])
|
const units = ref([])
|
||||||
const unitPageNo = ref(1)
|
const unitPageNo = ref(1)
|
||||||
@@ -209,6 +225,7 @@ async function fetchStudents() {
|
|||||||
studentTotalCount.value = d.totalCount || 0
|
studentTotalCount.value = d.totalCount || 0
|
||||||
studentPageNo.value = d.pageNo || studentPageNo.value
|
studentPageNo.value = d.pageNo || studentPageNo.value
|
||||||
studentPageSize.value = d.pageSize || studentPageSize.value
|
studentPageSize.value = d.pageSize || studentPageSize.value
|
||||||
|
ensurePollingForCurrentStudents()
|
||||||
} finally {
|
} finally {
|
||||||
studentLoading.value = false
|
studentLoading.value = false
|
||||||
}
|
}
|
||||||
@@ -263,6 +280,57 @@ function onGradeRowClick(row) {
|
|||||||
studentPageNo.value = 1
|
studentPageNo.value = 1
|
||||||
fetchStudents()
|
fetchStudents()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startLessonPlanPolling(studentId) {
|
||||||
|
if (!studentId) return
|
||||||
|
if (pollingTimers[studentId]) return
|
||||||
|
pollingTimers[studentId] = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const res = await checkIsGenerated(studentId)
|
||||||
|
const d = res?.data
|
||||||
|
const ok = d?.success === false || d?.success === false || d === false
|
||||||
|
console.log(ok)
|
||||||
|
if (ok) {
|
||||||
|
console.log('ok', d)
|
||||||
|
const p = Number(generatingPercents.value[studentId]) || 1
|
||||||
|
generatingPercents.value[studentId] = Math.min(p + 5, 95)
|
||||||
|
} else {
|
||||||
|
if (generatingPercents.value[studentId] !== undefined) {
|
||||||
|
delete generatingPercents.value[studentId]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
const p = Number(generatingPercents.value[studentId]) || 1
|
||||||
|
generatingPercents.value[studentId] = Math.min(p + 3, 95)
|
||||||
|
}
|
||||||
|
}, 10000)
|
||||||
|
}
|
||||||
|
function onLessonPlanGenerateSuccess(payload) {
|
||||||
|
const sid = payload?.studentId || selectedStudentIds.value?.[0]
|
||||||
|
startLessonPlanPolling(sid)
|
||||||
|
}
|
||||||
|
function ensurePollingForCurrentStudents() {
|
||||||
|
(students.value || []).forEach(s => startLessonPlanPolling(s.id))
|
||||||
|
}
|
||||||
|
function stopPolling(studentId) {
|
||||||
|
const t = pollingTimers[studentId]
|
||||||
|
if (t) {
|
||||||
|
clearInterval(t)
|
||||||
|
delete pollingTimers[studentId]
|
||||||
|
}
|
||||||
|
if (generatingPercents.value[studentId] !== undefined) {
|
||||||
|
delete generatingPercents.value[studentId]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function stopAllPolling() {
|
||||||
|
Object.keys(pollingTimers).forEach(id => stopPolling(id))
|
||||||
|
}
|
||||||
|
onUnmounted(() => {
|
||||||
|
stopAllPolling()
|
||||||
|
})
|
||||||
|
onBeforeRouteLeave(() => {
|
||||||
|
stopAllPolling()
|
||||||
|
})
|
||||||
async function onDeleteStudent(row) {
|
async function onDeleteStudent(row) {
|
||||||
try {
|
try {
|
||||||
await ElMessageBox.confirm('确认删除该学生?', '提示', {
|
await ElMessageBox.confirm('确认删除该学生?', '提示', {
|
||||||
|
|||||||
Reference in New Issue
Block a user