学习课程记录
This commit is contained in:
@@ -443,7 +443,15 @@ public class SCCourseServiceImpl implements SCCourseService {
|
|||||||
@Override
|
@Override
|
||||||
public ResultDomain<TbCourse> incrementLearnCount(String courseID) {
|
public ResultDomain<TbCourse> incrementLearnCount(String courseID) {
|
||||||
// TODO: 实现增加课程学习人数
|
// TODO: 实现增加课程学习人数
|
||||||
return null;
|
ResultDomain<TbCourse> resultDomain = new ResultDomain<>();
|
||||||
|
int i = courseMapper.incrementLearnCount(courseID,1);
|
||||||
|
if (i > 0) {
|
||||||
|
TbCourse course = courseMapper.selectByCourseId(courseID);
|
||||||
|
resultDomain.success("开始学习", course);
|
||||||
|
return resultDomain;
|
||||||
|
}
|
||||||
|
resultDomain.fail("报名失败");
|
||||||
|
return resultDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ public class SCLearningRecordServiceImpl implements LearningRecordService {
|
|||||||
resultDomain.fail("资源ID不能为空");
|
resultDomain.fail("资源ID不能为空");
|
||||||
return resultDomain;
|
return resultDomain;
|
||||||
}
|
}
|
||||||
if (learningRecord.getResourceType() == 2 && (learningRecord.getCourseID() == null || learningRecord.getChapterID() == null || learningRecord.getNodeID() == null)) {
|
if (learningRecord.getResourceType() == 2 && learningRecord.getCourseID() == null) {
|
||||||
resultDomain.fail("课程信息不能为空");
|
resultDomain.fail("课程ID不能为空");
|
||||||
return resultDomain;
|
return resultDomain;
|
||||||
}
|
}
|
||||||
List<TbLearningRecord> records = learningRecordMapper.selectLearningRecords(learningRecord);
|
List<TbLearningRecord> records = learningRecordMapper.selectLearningRecords(learningRecord);
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ function getRankClass(index: number): string {
|
|||||||
|
|
||||||
.rankings-section {
|
.rankings-section {
|
||||||
.ranking-card {
|
.ranking-card {
|
||||||
|
height: 100%;
|
||||||
.card-header {
|
.card-header {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
|||||||
@@ -484,8 +484,9 @@ function initVideoListeners() {
|
|||||||
const videos = articleContent.querySelectorAll('video');
|
const videos = articleContent.querySelectorAll('video');
|
||||||
|
|
||||||
if (videos.length === 0) {
|
if (videos.length === 0) {
|
||||||
// 没有视频,默认阅读即完成
|
// 没有视频,默认阅读即完成(不emit事件,依赖父组件的滚动检测)
|
||||||
hasVideoCompleted.value = true;
|
hasVideoCompleted.value = true;
|
||||||
|
console.log('ℹ️ 文章中没有视频,完成条件:滚动到底部');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -312,11 +312,11 @@ async function handleCollect() {
|
|||||||
try {
|
try {
|
||||||
if (isCollected.value) {
|
if (isCollected.value) {
|
||||||
// 取消收藏
|
// 取消收藏
|
||||||
const res = await userCollectionApi.removeCollection(
|
const res = await userCollectionApi.removeCollection({
|
||||||
userInfo.value.id,
|
userID: userInfo.value.id,
|
||||||
CollectionType.COURSE,
|
collectionType: CollectionType.COURSE,
|
||||||
props.courseId
|
collectionID: props.courseId
|
||||||
);
|
});
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
isCollected.value = false;
|
isCollected.value = false;
|
||||||
ElMessage.success('已取消收藏');
|
ElMessage.success('已取消收藏');
|
||||||
@@ -360,7 +360,8 @@ async function handleStartLearning() {
|
|||||||
await learningRecordApi.createRecord({
|
await learningRecordApi.createRecord({
|
||||||
userID: userInfo.value.id,
|
userID: userInfo.value.id,
|
||||||
resourceType: 2, // 课程
|
resourceType: 2, // 课程
|
||||||
resourceID: props.courseId,
|
courseID: props.courseId, // 使用courseID而不是resourceID
|
||||||
|
resourceID: props.courseId, // 保留resourceID以兼容
|
||||||
progress: 0,
|
progress: 0,
|
||||||
duration: 0,
|
duration: 0,
|
||||||
isComplete: false
|
isComplete: false
|
||||||
|
|||||||
@@ -98,10 +98,10 @@
|
|||||||
|
|
||||||
<!-- 文章资源 -->
|
<!-- 文章资源 -->
|
||||||
<div v-if="currentNode.nodeType === 0 && articleData" class="article-content">
|
<div v-if="currentNode.nodeType === 0 && articleData" class="article-content">
|
||||||
<ArticleShowView
|
<ArticleShow
|
||||||
:as-dialog="false"
|
:as-dialog="false"
|
||||||
:article-data="articleData"
|
:article-data="articleData"
|
||||||
:category-list="[]"
|
:show-back-button="false"
|
||||||
@videos-completed="handleArticleVideosCompleted"
|
@videos-completed="handleArticleVideosCompleted"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -195,7 +195,7 @@ import {
|
|||||||
Download,
|
Download,
|
||||||
InfoFilled
|
InfoFilled
|
||||||
} from '@element-plus/icons-vue';
|
} from '@element-plus/icons-vue';
|
||||||
import { ArticleShowView } from '@/views/public/article';
|
import { ArticleShow } from '@/views/public/article';
|
||||||
import { courseApi } from '@/apis/study';
|
import { courseApi } from '@/apis/study';
|
||||||
import { learningRecordApi, learningHistoryApi } from '@/apis/study';
|
import { learningRecordApi, learningHistoryApi } from '@/apis/study';
|
||||||
import { resourceApi } from '@/apis/resource';
|
import { resourceApi } from '@/apis/resource';
|
||||||
@@ -516,6 +516,12 @@ async function loadNodeContent() {
|
|||||||
if (!activeChapters.value.includes(currentChapterIndex.value)) {
|
if (!activeChapters.value.includes(currentChapterIndex.value)) {
|
||||||
activeChapters.value.push(currentChapterIndex.value);
|
activeChapters.value.push(currentChapterIndex.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 等待DOM更新后,检查是否需要自动标记完成(针对内容太短没有滚动条的情况)
|
||||||
|
await nextTick();
|
||||||
|
setTimeout(() => {
|
||||||
|
checkAutoComplete();
|
||||||
|
}, 500); // 延迟500ms,等待ArticleShow初始化完成
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择节点
|
// 选择节点
|
||||||
@@ -706,6 +712,48 @@ function hasScrollbar(): boolean {
|
|||||||
return contentAreaRef.value.scrollHeight > contentAreaRef.value.clientHeight;
|
return contentAreaRef.value.scrollHeight > contentAreaRef.value.clientHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查是否需要自动标记完成(针对内容太短没有滚动条的情况)
|
||||||
|
async function checkAutoComplete() {
|
||||||
|
// 如果当前节点已完成,跳过
|
||||||
|
if (isCurrentNodeCompleted.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentNode.value) return;
|
||||||
|
|
||||||
|
// 对富文本类型(nodeType === 1)立即检查
|
||||||
|
if (currentNode.value.nodeType === 1) {
|
||||||
|
if (!hasScrollbar()) {
|
||||||
|
console.log('📄 富文本内容较短无需滚动,自动标记为完成');
|
||||||
|
const nodeKey = `${currentChapterIndex.value}-${currentNodeIndex.value}`;
|
||||||
|
await markNodeComplete(nodeKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对文章类型(nodeType === 0),延迟检查(等待ArticleShow初始化视频监听器)
|
||||||
|
// 如果文章有视频,会在视频播放完成时emit事件,不会走到这里
|
||||||
|
// 如果文章没有视频且没有滚动条,需要自动标记完成
|
||||||
|
if (currentNode.value.nodeType === 0) {
|
||||||
|
// 保存当前节点信息,避免延迟后节点已改变
|
||||||
|
const chapterIdx = currentChapterIndex.value;
|
||||||
|
const nodeIdx = currentNodeIndex.value;
|
||||||
|
const nodeKey = `${chapterIdx}-${nodeIdx}`;
|
||||||
|
|
||||||
|
// 延迟1秒再检查,给ArticleShow足够时间初始化
|
||||||
|
setTimeout(async () => {
|
||||||
|
// 再次检查是否已完成(可能已通过视频完成事件标记)
|
||||||
|
// 且确认当前还在同一个节点
|
||||||
|
if (!completedNodes.value.has(nodeKey) &&
|
||||||
|
currentChapterIndex.value === chapterIdx &&
|
||||||
|
currentNodeIndex.value === nodeIdx &&
|
||||||
|
!hasScrollbar()) {
|
||||||
|
console.log('📄 文章内容较短无需滚动且无视频,自动标记为完成');
|
||||||
|
await markNodeComplete(nodeKey);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 处理内容区域滚动
|
// 处理内容区域滚动
|
||||||
function handleContentScroll(event: Event) {
|
function handleContentScroll(event: Event) {
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
|
|||||||
Reference in New Issue
Block a user