修复小bug,剩余语音未测试
This commit is contained in:
parent
1a9423951c
commit
1dc2883922
|
|
@ -505,23 +505,25 @@ public class StudyScoreController extends BaseController
|
||||||
return "FALSE";
|
return "FALSE";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ 修复:去除所有空格,确保格式一致
|
||||||
|
String noSpaces = trimmed.replaceAll("\\s+", "");
|
||||||
|
|
||||||
// 处理选项前缀:去除"A. "、"B. "等前缀
|
// 处理选项前缀:去除"A. "、"B. "等前缀
|
||||||
// 支持格式:
|
// 支持格式:
|
||||||
// - "A. 选项内容" -> "A" 或 "选项内容"
|
// - "A.选项内容" -> "A" 或 "选项内容"(已去除空格)
|
||||||
// - "A.选项内容" -> "A" 或 "选项内容"
|
|
||||||
// - "A" -> "A"
|
// - "A" -> "A"
|
||||||
String normalized = trimmed;
|
String normalized = noSpaces;
|
||||||
|
|
||||||
// 如果以"字母. "或"字母."开头,提取字母部分
|
// 如果以"字母."开头,提取字母部分
|
||||||
if (trimmed.matches("^[A-F]\\.\\s*.*"))
|
if (noSpaces.matches("^[A-F]\\..*"))
|
||||||
{
|
{
|
||||||
// 提取字母(如 "A. 选项内容" -> "A")
|
// 提取字母(如 "A.选项内容" -> "A")
|
||||||
normalized = trimmed.substring(0, 1).toUpperCase();
|
normalized = noSpaces.substring(0, 1).toUpperCase();
|
||||||
}
|
}
|
||||||
else if (trimmed.length() == 1 && trimmed.matches("[A-Fa-f]"))
|
else if (noSpaces.length() == 1 && noSpaces.matches("[A-Fa-f]"))
|
||||||
{
|
{
|
||||||
// 单个字母(如 "A" -> "A")
|
// 单个字母(如 "A" -> "A")
|
||||||
normalized = trimmed.toUpperCase();
|
normalized = noSpaces.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
return normalized;
|
return normalized;
|
||||||
|
|
@ -540,9 +542,13 @@ public class StudyScoreController extends BaseController
|
||||||
*/
|
*/
|
||||||
private boolean compareMultipleAnswers(String studentAnswer, String correctAnswer, String optionsJson)
|
private boolean compareMultipleAnswers(String studentAnswer, String correctAnswer, String optionsJson)
|
||||||
{
|
{
|
||||||
|
// ✅ 修复:去除所有空格,确保格式一致(与保存题目时的规范化逻辑一致)
|
||||||
|
String normalizedStudentAnswer = studentAnswer.replaceAll("\\s+", "");
|
||||||
|
String normalizedCorrectAnswer = correctAnswer.replaceAll("\\s+", "");
|
||||||
|
|
||||||
// 使用逗号分隔(不使用空格、分号等,避免选项内容中的这些字符导致问题)
|
// 使用逗号分隔(不使用空格、分号等,避免选项内容中的这些字符导致问题)
|
||||||
String[] studentParts = studentAnswer.split(",");
|
String[] studentParts = normalizedStudentAnswer.split(",");
|
||||||
String[] correctParts = correctAnswer.split(",");
|
String[] correctParts = normalizedCorrectAnswer.split(",");
|
||||||
java.util.Set<String> studentSet = new java.util.HashSet<>();
|
java.util.Set<String> studentSet = new java.util.HashSet<>();
|
||||||
java.util.Set<String> correctSet = new java.util.HashSet<>();
|
java.util.Set<String> correctSet = new java.util.HashSet<>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,12 @@ public class StudyLearningRecord extends BaseEntity
|
||||||
/** 课程名称(不持久化,仅用于显示) */
|
/** 课程名称(不持久化,仅用于显示) */
|
||||||
private String courseName;
|
private String courseName;
|
||||||
|
|
||||||
|
/** 学生姓名(不持久化,仅用于搜索和显示) */
|
||||||
|
private String studentName;
|
||||||
|
|
||||||
|
/** 学生学号/信息编号(不持久化,仅用于搜索和显示) */
|
||||||
|
private String studentNo;
|
||||||
|
|
||||||
/** 学习进度(百分比,0-100) */
|
/** 学习进度(百分比,0-100) */
|
||||||
@Excel(name = "学习进度")
|
@Excel(name = "学习进度")
|
||||||
private BigDecimal progress;
|
private BigDecimal progress;
|
||||||
|
|
@ -172,6 +178,26 @@ public class StudyLearningRecord extends BaseEntity
|
||||||
this.courseName = courseName;
|
this.courseName = courseName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getStudentName()
|
||||||
|
{
|
||||||
|
return studentName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStudentName(String studentName)
|
||||||
|
{
|
||||||
|
this.studentName = studentName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStudentNo()
|
||||||
|
{
|
||||||
|
return studentNo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStudentNo(String studentNo)
|
||||||
|
{
|
||||||
|
this.studentNo = studentNo;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
|
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
||||||
@Override
|
@Override
|
||||||
public StudyLearningRecord selectLearningRecordById(Long id)
|
public StudyLearningRecord selectLearningRecordById(Long id)
|
||||||
{
|
{
|
||||||
|
// ✅ 直接返回数据库中的进度值,与APP端保持一致
|
||||||
return learningRecordMapper.selectLearningRecordById(id);
|
return learningRecordMapper.selectLearningRecordById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,15 +66,9 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
||||||
{
|
{
|
||||||
List<StudyLearningRecord> records = learningRecordMapper.selectLearningRecordList(learningRecord);
|
List<StudyLearningRecord> records = learningRecordMapper.selectLearningRecordList(learningRecord);
|
||||||
|
|
||||||
// 实时计算每条记录的进度(与前端保持一致)
|
// ✅ 直接返回数据库中的进度值,与APP端保持一致
|
||||||
for (StudyLearningRecord record : records)
|
// APP端会在学习时计算进度并上报保存,后台直接显示保存的值即可
|
||||||
{
|
logger.info("【管理端查询】查询到 {} 条学习记录,直接使用数据库中的进度值", records.size());
|
||||||
if (record.getStudentId() != null && record.getCourseId() != null)
|
|
||||||
{
|
|
||||||
BigDecimal realTimeProgress = calculateCourseProgressForDisplay(record.getStudentId(), record.getCourseId());
|
|
||||||
record.setProgress(realTimeProgress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return records;
|
return records;
|
||||||
}
|
}
|
||||||
|
|
@ -84,6 +79,8 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
||||||
*/
|
*/
|
||||||
private BigDecimal calculateCourseProgressForDisplay(Long studentId, Long courseId)
|
private BigDecimal calculateCourseProgressForDisplay(Long studentId, Long courseId)
|
||||||
{
|
{
|
||||||
|
logger.info("【进度计算】开始计算 - 学生ID={}, 课程ID={}", studentId, courseId);
|
||||||
|
|
||||||
// 查询课程包含的所有课件
|
// 查询课程包含的所有课件
|
||||||
StudyCourseware query = new StudyCourseware();
|
StudyCourseware query = new StudyCourseware();
|
||||||
query.setCourseId(courseId);
|
query.setCourseId(courseId);
|
||||||
|
|
@ -91,10 +88,12 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
||||||
|
|
||||||
if (allCoursewareList == null || allCoursewareList.isEmpty())
|
if (allCoursewareList == null || allCoursewareList.isEmpty())
|
||||||
{
|
{
|
||||||
|
logger.warn("【进度计算】课程{}没有课件", courseId);
|
||||||
return BigDecimal.ZERO;
|
return BigDecimal.ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
int totalCoursewareCount = allCoursewareList.size();
|
int totalCoursewareCount = allCoursewareList.size();
|
||||||
|
logger.info("【进度计算】课程共有{}个课件", totalCoursewareCount);
|
||||||
|
|
||||||
// 查询该学员对该课程的所有学习详情记录
|
// 查询该学员对该课程的所有学习详情记录
|
||||||
StudyLearningDetail detailQuery = new StudyLearningDetail();
|
StudyLearningDetail detailQuery = new StudyLearningDetail();
|
||||||
|
|
@ -136,6 +135,9 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
||||||
{
|
{
|
||||||
StudyLearningDetail detail = coursewareDetailMap.get(cw.getId());
|
StudyLearningDetail detail = coursewareDetailMap.get(cw.getId());
|
||||||
|
|
||||||
|
logger.info("【课件检查】课件ID={}, 类型={}, 标题={}, 配置时长={}秒",
|
||||||
|
cw.getId(), cw.getType(), cw.getTitle(), cw.getDuration());
|
||||||
|
|
||||||
if ("video".equals(cw.getType()))
|
if ("video".equals(cw.getType()))
|
||||||
{
|
{
|
||||||
// 视频:判断是否完成
|
// 视频:判断是否完成
|
||||||
|
|
@ -145,6 +147,9 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
||||||
int maxPosition = maxPositionMap.getOrDefault(cw.getId(), videoPosition);
|
int maxPosition = maxPositionMap.getOrDefault(cw.getId(), videoPosition);
|
||||||
int duration = (cw.getDuration() != null && cw.getDuration() > 0) ? cw.getDuration() : 0;
|
int duration = (cw.getDuration() != null && cw.getDuration() > 0) ? cw.getDuration() : 0;
|
||||||
|
|
||||||
|
logger.info("【视频课件】课件ID={}, 播放位置={}秒, 最大位置={}秒, 配置时长={}秒",
|
||||||
|
cw.getId(), videoPosition, maxPosition, duration);
|
||||||
|
|
||||||
// 推断真实时长
|
// 推断真实时长
|
||||||
int realDuration = duration > 0 ? duration : (maxPosition + 3);
|
int realDuration = duration > 0 ? duration : (maxPosition + 3);
|
||||||
|
|
||||||
|
|
@ -155,11 +160,19 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
||||||
if (videoPosition == maxPosition && videoPosition >= 3)
|
if (videoPosition == maxPosition && videoPosition >= 3)
|
||||||
{
|
{
|
||||||
isCompleted = true;
|
isCompleted = true;
|
||||||
|
logger.info("【完成判断】课件ID={} - 特殊判断通过(播放到最大位置{}秒)", cw.getId(), videoPosition);
|
||||||
}
|
}
|
||||||
// 常规判断:观看进度 >= 90%
|
// 常规判断:观看进度 >= 90%
|
||||||
else if (realDuration > 0 && videoPosition >= realDuration * 0.9)
|
else if (realDuration > 0 && videoPosition >= realDuration * 0.9)
|
||||||
{
|
{
|
||||||
isCompleted = true;
|
isCompleted = true;
|
||||||
|
logger.info("【完成判断】课件ID={} - 常规判断通过({}秒 >= {}秒的90%)",
|
||||||
|
cw.getId(), videoPosition, realDuration);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.info("【完成判断】课件ID={} - 未完成({}秒 < {}秒的90%)",
|
||||||
|
cw.getId(), videoPosition, realDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCompleted)
|
if (isCompleted)
|
||||||
|
|
@ -167,6 +180,11 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
||||||
completedCount++;
|
completedCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.info("【视频课件】课件ID={} - 无学习记录或videoPosition为null, detail={}",
|
||||||
|
cw.getId(), detail);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -174,6 +192,11 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
||||||
if (detail != null)
|
if (detail != null)
|
||||||
{
|
{
|
||||||
completedCount++;
|
completedCount++;
|
||||||
|
logger.info("【非视频课件】课件ID={} - 已完成", cw.getId());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.info("【非视频课件】课件ID={} - 无学习记录", cw.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -183,9 +206,15 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
||||||
{
|
{
|
||||||
double completionRate = (double) completedCount / totalCoursewareCount * 100;
|
double completionRate = (double) completedCount / totalCoursewareCount * 100;
|
||||||
BigDecimal progress = new BigDecimal(completionRate).setScale(2, BigDecimal.ROUND_HALF_UP);
|
BigDecimal progress = new BigDecimal(completionRate).setScale(2, BigDecimal.ROUND_HALF_UP);
|
||||||
return progress.compareTo(new BigDecimal(100)) > 0 ? new BigDecimal(100) : progress;
|
BigDecimal finalProgress = progress.compareTo(new BigDecimal(100)) > 0 ? new BigDecimal(100) : progress;
|
||||||
|
|
||||||
|
logger.info("【进度计算】完成 - 学生ID={}, 课程ID={}, 已完成{}/总共{} = {}%",
|
||||||
|
studentId, courseId, completedCount, totalCoursewareCount, finalProgress);
|
||||||
|
|
||||||
|
return finalProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.warn("【进度计算】总课件数为0 - 学生ID={}, 课程ID={}", studentId, courseId);
|
||||||
return BigDecimal.ZERO;
|
return BigDecimal.ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,6 +227,7 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
||||||
@Override
|
@Override
|
||||||
public List<StudyLearningRecord> selectLearningRecordListByStudentId(Long studentId)
|
public List<StudyLearningRecord> selectLearningRecordListByStudentId(Long studentId)
|
||||||
{
|
{
|
||||||
|
// ✅ 直接返回数据库中的进度值,与APP端保持一致
|
||||||
return learningRecordMapper.selectLearningRecordListByStudentId(studentId);
|
return learningRecordMapper.selectLearningRecordListByStudentId(studentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,6 +240,7 @@ public class StudyLearningRecordServiceImpl implements IStudyLearningRecordServi
|
||||||
@Override
|
@Override
|
||||||
public List<StudyLearningRecord> selectLearningRecordListByCourseId(Long courseId)
|
public List<StudyLearningRecord> selectLearningRecordListByCourseId(Long courseId)
|
||||||
{
|
{
|
||||||
|
// ✅ 直接返回数据库中的进度值,与APP端保持一致
|
||||||
return learningRecordMapper.selectLearningRecordListByCourseId(courseId);
|
return learningRecordMapper.selectLearningRecordListByCourseId(courseId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<result property="studentId" column="student_id" />
|
<result property="studentId" column="student_id" />
|
||||||
<result property="courseId" column="course_id" />
|
<result property="courseId" column="course_id" />
|
||||||
<result property="courseName" column="course_name" />
|
<result property="courseName" column="course_name" />
|
||||||
|
<result property="studentName" column="student_name" />
|
||||||
|
<result property="studentNo" column="student_no" />
|
||||||
<result property="progress" column="progress" />
|
<result property="progress" column="progress" />
|
||||||
<result property="totalDuration" column="total_duration" />
|
<result property="totalDuration" column="total_duration" />
|
||||||
<result property="learnCount" column="learn_count" />
|
<result property="learnCount" column="learn_count" />
|
||||||
|
|
@ -24,9 +26,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectLearningRecordVo">
|
<sql id="selectLearningRecordVo">
|
||||||
select lr.id, lr.student_id, lr.course_id, c.course_name, lr.progress, lr.total_duration, lr.learn_count, lr.video_progress, lr.video_total_duration, lr.last_learn_time, lr.last_video_position, lr.create_by, lr.create_time, lr.update_by, lr.update_time, lr.remark
|
select lr.id, lr.student_id, lr.course_id, c.course_name,
|
||||||
|
u.nick_name as student_name, u.user_id as student_no,
|
||||||
|
lr.progress, lr.total_duration, lr.learn_count, lr.video_progress,
|
||||||
|
lr.video_total_duration, lr.last_learn_time, lr.last_video_position,
|
||||||
|
lr.create_by, lr.create_time, lr.update_by, lr.update_time, lr.remark
|
||||||
from learning_record lr
|
from learning_record lr
|
||||||
left join course c on lr.course_id = c.id
|
left join course c on lr.course_id = c.id
|
||||||
|
left join sys_user u on lr.student_id = u.user_id
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectLearningRecordList" parameterType="StudyLearningRecord" resultMap="StudyLearningRecordResult">
|
<select id="selectLearningRecordList" parameterType="StudyLearningRecord" resultMap="StudyLearningRecordResult">
|
||||||
|
|
@ -38,6 +45,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
<if test="courseId != null">
|
<if test="courseId != null">
|
||||||
and lr.course_id = #{courseId}
|
and lr.course_id = #{courseId}
|
||||||
</if>
|
</if>
|
||||||
|
<if test="studentName != null and studentName != ''">
|
||||||
|
and u.nick_name like concat('%', #{studentName}, '%')
|
||||||
|
</if>
|
||||||
|
<if test="studentNo != null and studentNo != ''">
|
||||||
|
and u.user_id = #{studentNo}
|
||||||
|
</if>
|
||||||
<!-- 数据范围过滤 -->
|
<!-- 数据范围过滤 -->
|
||||||
${params.dataScope}
|
${params.dataScope}
|
||||||
</where>
|
</where>
|
||||||
|
|
@ -46,24 +59,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
|
|
||||||
<select id="selectLearningRecordById" parameterType="Long" resultMap="StudyLearningRecordResult">
|
<select id="selectLearningRecordById" parameterType="Long" resultMap="StudyLearningRecordResult">
|
||||||
<include refid="selectLearningRecordVo"/>
|
<include refid="selectLearningRecordVo"/>
|
||||||
where id = #{id}
|
where lr.id = #{id}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectLearningRecordByStudentAndCourse" resultMap="StudyLearningRecordResult">
|
<select id="selectLearningRecordByStudentAndCourse" resultMap="StudyLearningRecordResult">
|
||||||
<include refid="selectLearningRecordVo"/>
|
<include refid="selectLearningRecordVo"/>
|
||||||
where student_id = #{studentId} and course_id = #{courseId} limit 1
|
where lr.student_id = #{studentId} and lr.course_id = #{courseId} limit 1
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectLearningRecordListByStudentId" parameterType="Long" resultMap="StudyLearningRecordResult">
|
<select id="selectLearningRecordListByStudentId" parameterType="Long" resultMap="StudyLearningRecordResult">
|
||||||
<include refid="selectLearningRecordVo"/>
|
<include refid="selectLearningRecordVo"/>
|
||||||
where student_id = #{studentId}
|
where lr.student_id = #{studentId}
|
||||||
order by last_learn_time desc
|
order by lr.last_learn_time desc
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectLearningRecordListByCourseId" parameterType="Long" resultMap="StudyLearningRecordResult">
|
<select id="selectLearningRecordListByCourseId" parameterType="Long" resultMap="StudyLearningRecordResult">
|
||||||
<include refid="selectLearningRecordVo"/>
|
<include refid="selectLearningRecordVo"/>
|
||||||
where course_id = #{courseId}
|
where lr.course_id = #{courseId}
|
||||||
order by last_learn_time desc
|
order by lr.last_learn_time desc
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<insert id="insertLearningRecord" parameterType="StudyLearningRecord" useGeneratedKeys="true" keyProperty="id">
|
<insert id="insertLearningRecord" parameterType="StudyLearningRecord" useGeneratedKeys="true" keyProperty="id">
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="100px">
|
||||||
<el-form-item label="课程ID" prop="courseId">
|
<el-form-item label="罪犯姓名" prop="studentName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="queryParams.courseId"
|
v-model="queryParams.studentName"
|
||||||
placeholder="请输入课程ID"
|
placeholder="请输入罪犯姓名"
|
||||||
clearable
|
clearable
|
||||||
style="width: 240px"
|
style="width: 200px"
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="信息编号" prop="studentNo">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.studentNo"
|
||||||
|
placeholder="请输入信息编号"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
@keyup.enter.native="handleQuery"
|
@keyup.enter.native="handleQuery"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
@ -31,10 +40,12 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="recordList">
|
<el-table v-loading="loading" :data="recordList">
|
||||||
<el-table-column label="记录ID" align="center" prop="id" />
|
<el-table-column label="记录ID" align="center" prop="id" width="80" />
|
||||||
<el-table-column label="学员ID" align="center" prop="studentId" />
|
<el-table-column label="学员ID" align="center" prop="studentId" width="100" />
|
||||||
|
<el-table-column label="罪犯姓名" align="center" prop="studentName" width="120" />
|
||||||
|
<el-table-column label="信息编号" align="center" prop="studentNo" width="120" />
|
||||||
<el-table-column label="课程名称" align="center" prop="courseName" min-width="150" />
|
<el-table-column label="课程名称" align="center" prop="courseName" min-width="150" />
|
||||||
<el-table-column label="学习次数" align="center" prop="learnCount" />
|
<el-table-column label="学习次数" align="center" prop="learnCount" width="100" />
|
||||||
<el-table-column label="累计时长" align="center" prop="totalDuration">
|
<el-table-column label="累计时长" align="center" prop="totalDuration">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{ formatDuration(scope.row.totalDuration) }}</span>
|
<span>{{ formatDuration(scope.row.totalDuration) }}</span>
|
||||||
|
|
@ -86,7 +97,8 @@ export default {
|
||||||
queryParams: {
|
queryParams: {
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
courseId: null
|
studentName: null,
|
||||||
|
studentNo: null
|
||||||
},
|
},
|
||||||
// 刷新进度中
|
// 刷新进度中
|
||||||
refreshing: false
|
refreshing: false
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import config from '@/utils/config.js'
|
import config from '@/utils/config.js'
|
||||||
|
|
||||||
const APP_DEV_HOST = '192.168.137.1'
|
const APP_DEV_HOST = '192.168.1.80'
|
||||||
const APP_DEV_PORT = 30091
|
const APP_DEV_PORT = 30091
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -12,8 +12,8 @@
|
||||||
|
|
||||||
// App环境:配置服务器地址(生产环境使用生产服务器)
|
// App环境:配置服务器地址(生产环境使用生产服务器)
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
// 强制使用新的服务器地址 192.168.137.1
|
// 强制使用新的服务器地址 192.168.1.80
|
||||||
const PROD_HOST = '192.168.137.1'
|
const PROD_HOST = '192.168.1.80'
|
||||||
const PROD_PORT = 30091
|
const PROD_PORT = 30091
|
||||||
|
|
||||||
// 无条件强制更新配置(清除旧配置)
|
// 无条件强制更新配置(清除旧配置)
|
||||||
|
|
|
||||||
|
|
@ -392,8 +392,12 @@ export default {
|
||||||
answerArray.push(option)
|
answerArray.push(option)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$set(this.answers, questionId, answerArray.join(','))
|
// 修复:统一使用完整选项文本,并排序确保一致性
|
||||||
|
const sortedAnswer = answerArray.sort().join(',')
|
||||||
|
this.$set(this.answers, questionId, sortedAnswer)
|
||||||
this.saveAnswers()
|
this.saveAnswers()
|
||||||
|
|
||||||
|
console.log(`多选题答案更新 - 题目${questionId}:`, sortedAnswer)
|
||||||
},
|
},
|
||||||
|
|
||||||
isOptionSelected(questionId, option) {
|
isOptionSelected(questionId, option) {
|
||||||
|
|
@ -498,9 +502,15 @@ export default {
|
||||||
console.warn('题目数据不完整:', question)
|
console.warn('题目数据不完整:', question)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
const answer = this.answers[question.id] || ''
|
||||||
|
console.log(`题目${question.id} [${question.questionType}]:`, {
|
||||||
|
questionContent: question.questionContent,
|
||||||
|
userAnswer: answer,
|
||||||
|
correctAnswer: question.correctAnswer
|
||||||
|
})
|
||||||
return {
|
return {
|
||||||
questionId: question.id,
|
questionId: question.id,
|
||||||
answer: this.answers[question.id] || ''
|
answer: answer
|
||||||
}
|
}
|
||||||
}).filter(item => item !== null) // 过滤掉无效项
|
}).filter(item => item !== null) // 过滤掉无效项
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -264,10 +264,7 @@ export default {
|
||||||
}
|
}
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
// 新方案:使用服务器端识别,无需UTS插件
|
// 新方案:使用服务器端识别,无需UTS插件
|
||||||
speechRecorder.init()
|
this.initSpeechService()
|
||||||
this.statusText = '准备就绪'
|
|
||||||
this.isReady = true
|
|
||||||
console.log('[Speech] 使用服务器端语音识别')
|
|
||||||
// #endif
|
// #endif
|
||||||
// #ifndef APP-PLUS
|
// #ifndef APP-PLUS
|
||||||
this.statusText = '语音识别仅支持APP端'
|
this.statusText = '语音识别仅支持APP端'
|
||||||
|
|
|
||||||
|
|
@ -70,14 +70,23 @@ class SpeechRecorder {
|
||||||
stop() {
|
stop() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!this.isRecording) {
|
if (!this.isRecording) {
|
||||||
reject(new Error('未在录音中'))
|
// 如果已经停止了,但有临时文件,返回该文件
|
||||||
|
if (this.tempFilePath) {
|
||||||
|
resolve(this.tempFilePath)
|
||||||
|
} else {
|
||||||
|
reject(new Error('未在录音中'))
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.recorderManager.onStop((res) => {
|
// 注册一次性监听器
|
||||||
|
const onStopHandler = (res) => {
|
||||||
|
this.tempFilePath = res.tempFilePath
|
||||||
|
this.isRecording = false
|
||||||
resolve(res.tempFilePath)
|
resolve(res.tempFilePath)
|
||||||
})
|
}
|
||||||
|
|
||||||
|
this.recorderManager.onStop(onStopHandler)
|
||||||
this.recorderManager.stop()
|
this.recorderManager.stop()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -92,6 +101,8 @@ class SpeechRecorder {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// 获取服务器配置
|
// 获取服务器配置
|
||||||
const config = require('./config.js').default
|
const config = require('./config.js').default
|
||||||
|
// 开发环境可以用 localhost:5000 测试
|
||||||
|
// const serverUrl = 'http://localhost:5000' // Windows本地测试
|
||||||
const serverUrl = config.API_BASE_URL
|
const serverUrl = config.API_BASE_URL
|
||||||
|
|
||||||
uni.uploadFile({
|
uni.uploadFile({
|
||||||
|
|
@ -157,6 +168,42 @@ class SpeechRecorder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步评测方法(用于已停止录音的场景)
|
||||||
|
* @param {string} referenceText 参考文本
|
||||||
|
* @param {number} questionId 题目ID(可选)
|
||||||
|
* @returns {Promise<Object>} 评测结果
|
||||||
|
*/
|
||||||
|
async evaluateAsync(referenceText, questionId = null) {
|
||||||
|
try {
|
||||||
|
console.log('[语音评测] 开始异步评测', { referenceText, questionId, tempFilePath: this.tempFilePath })
|
||||||
|
|
||||||
|
// 检查是否有录音文件
|
||||||
|
if (!this.tempFilePath) {
|
||||||
|
throw new Error('没有可用的录音文件')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传并识别
|
||||||
|
const result = await this.uploadAndRecognize(this.tempFilePath, {
|
||||||
|
referenceText: referenceText,
|
||||||
|
questionId: questionId
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('[语音评测] 评测成功', result)
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
...result
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[语音评测] 评测失败', error)
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.message || '评测失败'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出单例
|
// 导出单例
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user