diff --git a/schoolNewsWeb/src/apis/study/learning-record.ts b/schoolNewsWeb/src/apis/study/learning-record.ts index 90d1427..c7ad533 100644 --- a/schoolNewsWeb/src/apis/study/learning-record.ts +++ b/schoolNewsWeb/src/apis/study/learning-record.ts @@ -17,7 +17,7 @@ export const learningRecordApi = { * @returns Promise> */ async getRecordList(filter?: Partial): Promise> { - const response = await api.get('/study/learning-record/list', filter); + const response = await api.post('/study/records/list', filter); return response.data; }, @@ -27,45 +27,51 @@ export const learningRecordApi = { * @returns Promise> */ async createRecord(record: LearningRecord): Promise> { - const response = await api.post('/study/learning-record/create', record); + const response = await api.post('/study/records/record', record); return response.data; }, /** - * 更新学习记录 + * 更新学习记录(禁用 loading 动画,避免影响视频播放) * @param record 记录数据 * @returns Promise> */ - async updateRecord(record: LearningRecord): Promise> { - const response = await api.put('/study/learning-record/update', record); + async updateRecord(record: Partial): Promise> { + const response = await api.put('/study/records/record', record, { + showLoading: false // 禁用 loading 动画 + } as any); return response.data; }, /** - * 获取用户学习统计 - * @param userID 用户ID - * @param timeRange 时间范围 - * @returns Promise> + * 删除学习记录 + * @param recordId 记录ID + * @returns Promise> */ - async getUserLearningStatistics(userID: string, timeRange?: string): Promise> { - const response = await api.get('/study/learning-record/statistics', { - userID, - timeRange - }); + async deleteRecord(recordId: string): Promise> { + const response = await api.delete('/study/records/record', { ID: recordId }); return response.data; }, /** - * 获取学习时长图表数据 - * @param userID 用户ID - * @param timeRange 时间范围 - * @returns Promise> + * 标记学习完成(禁用 loading 动画,避免影响视频播放) + * @param record 记录数据 + * @returns Promise> */ - async getLearningDurationChart(userID: string, timeRange?: string): Promise> { - const response = await api.get('/study/learning-record/duration-chart', { - userID, - timeRange - }); + async markComplete(record: Partial): Promise> { + const response = await api.put('/study/records/complete', record, { + showLoading: false // 禁用 loading 动画 + } as any); return response.data; - } + }, + + /** + * 查询用户课程的学习记录 + * @param filter 过滤条件(包含 userID 和 resourceID) + * @returns Promise> + */ + async getCourseLearningRecord(filter: Partial): Promise> { + const response = await api.post('/study/records/course/records', filter); + return response.data; + }, }; diff --git a/schoolNewsWeb/src/apis/study/learning-task.ts b/schoolNewsWeb/src/apis/study/learning-task.ts index 5fc1ab7..76c6286 100644 --- a/schoolNewsWeb/src/apis/study/learning-task.ts +++ b/schoolNewsWeb/src/apis/study/learning-task.ts @@ -32,6 +32,16 @@ export const learningTaskApi = { return response.data; }, + /** + * 根据ID获取用户任务详情 + * @param taskID 任务ID + * @returns Promise> + */ + async getUserTask(taskID: string): Promise> { + const response = await api.get(`${this.learningTaskPrefix}/${taskID}/user`); + return response.data; + }, + /** * 获取任务分页列表 * @param pageParam 分页参数 diff --git a/schoolNewsWeb/src/apis/usercenter/collection.ts b/schoolNewsWeb/src/apis/usercenter/collection.ts index b126710..acf100e 100644 --- a/schoolNewsWeb/src/apis/usercenter/collection.ts +++ b/schoolNewsWeb/src/apis/usercenter/collection.ts @@ -11,6 +11,7 @@ import type { UserCollection, ResultDomain } from '@/types'; * 用户收藏API服务 */ export const userCollectionApi = { + baseUrl: '/usercenter/collections', /** * 获取用户收藏列表 * @param userID 用户ID @@ -18,7 +19,7 @@ export const userCollectionApi = { * @returns Promise> */ async getUserCollections(userID: string, collectionType?: number): Promise> { - const response = await api.get('/usercenter/collection/list', { + const response = await api.get(`${this.baseUrl}/list`, { userID, collectionType }); @@ -31,7 +32,7 @@ export const userCollectionApi = { * @returns Promise> */ async addCollection(collection: UserCollection): Promise> { - const response = await api.post('/usercenter/collection/add', collection); + const response = await api.post(`${this.baseUrl}/collect`, collection); return response.data; }, @@ -43,7 +44,7 @@ export const userCollectionApi = { * @returns Promise> */ async removeCollection(userID: string, collectionType: number, collectionID: string): Promise> { - const response = await api.delete('/usercenter/collection/remove', { + const response = await api.delete(`${this.baseUrl}/collect`, { userID, collectionType, collectionID @@ -58,9 +59,8 @@ export const userCollectionApi = { * @param collectionID 收藏对象ID * @returns Promise> */ - async isCollected(userID: string, collectionType: number, collectionID: string): Promise> { - const response = await api.get('/usercenter/collection/check', { - userID, + async isCollected(collectionType: number, collectionID: string): Promise> { + const response = await api.get(`${this.baseUrl}/check`, { collectionType, collectionID }); diff --git a/schoolNewsWeb/src/layouts/NavigationLayout.vue b/schoolNewsWeb/src/layouts/NavigationLayout.vue index 5753057..c4eaac9 100644 --- a/schoolNewsWeb/src/layouts/NavigationLayout.vue +++ b/schoolNewsWeb/src/layouts/NavigationLayout.vue @@ -146,7 +146,6 @@ watch( diff --git a/schoolNewsWeb/src/views/study-plan/CourseStudyView.vue b/schoolNewsWeb/src/views/study-plan/CourseStudyView.vue index ec6898d..1e90437 100644 --- a/schoolNewsWeb/src/views/study-plan/CourseStudyView.vue +++ b/schoolNewsWeb/src/views/study-plan/CourseStudyView.vue @@ -4,6 +4,7 @@ :course-id="courseId" :chapter-index="chapterIndex" :node-index="nodeIndex" + :back-button-text="taskId ? '返回任务详情' : '返回课程详情'" @back="handleBack" /> @@ -24,15 +25,27 @@ const route = useRoute(); const courseId = computed(() => route.query.courseId as string || ''); const chapterIndex = computed(() => parseInt(route.query.chapterIndex as string) || 0); const nodeIndex = computed(() => parseInt(route.query.nodeIndex as string) || 0); +const taskId = computed(() => route.query.taskId as string || ''); -// 返回到课程详情页 +// 返回到上一页 function handleBack() { - router.push({ - path: '/study-plan/course-detail', - query: { - courseId: courseId.value - } - }); + // 如果有 taskId,返回任务详情页 + if (taskId.value) { + router.push({ + path: '/study-plan/task-detail', + query: { + taskId: taskId.value + } + }); + } else { + // 否则返回课程详情页 + router.push({ + path: '/study-plan/course-detail', + query: { + courseId: courseId.value + } + }); + } } diff --git a/schoolNewsWeb/src/views/study-plan/LearningTaskDetailView.vue b/schoolNewsWeb/src/views/study-plan/LearningTaskDetailView.vue index d19f653..8ba956e 100644 --- a/schoolNewsWeb/src/views/study-plan/LearningTaskDetailView.vue +++ b/schoolNewsWeb/src/views/study-plan/LearningTaskDetailView.vue @@ -23,7 +23,7 @@ const taskId = computed(() => route.query.taskId as string || ''); // 返回任务列表 function handleBack() { - router.back(); + router.push('/study-plan/tasks'); } diff --git a/schoolNewsWeb/src/views/study-plan/StudyTasksView.vue b/schoolNewsWeb/src/views/study-plan/StudyTasksView.vue index 7145bac..8799937 100644 --- a/schoolNewsWeb/src/views/study-plan/StudyTasksView.vue +++ b/schoolNewsWeb/src/views/study-plan/StudyTasksView.vue @@ -138,8 +138,8 @@ const progressPercent = computed(() => { }); onMounted(() => { - loadUserProgress(); loadTaskList(); + loadUserProgress(); }); // 获取当前用户ID @@ -199,6 +199,7 @@ async function loadUserProgress() { const pending = (progressData.notStartTaskNum || 0) + (progressData.learningTaskNum || 0); // 设置统计数据 totalCount.value = progressData.totalTaskNum || 0; + completedCount.value = progressData.completedTaskNum || 0; pendingCount.value = pending; } } catch (error) { @@ -244,7 +245,7 @@ function getDeadlineStatus(task: LearningTask): { show: boolean; text: string; t if (task.status === 2) { // 已完成 - return { show: true, text: '已截止', type: 'success' }; + return { show: true, text: '已完成', type: 'success' }; } else if (diffDays < 0) { // 已过期 return { show: true, text: '已截止', type: 'info' }; diff --git a/schoolNewsWeb/src/views/task/LearingTaskDetail.vue b/schoolNewsWeb/src/views/task/LearingTaskDetail.vue index 600498d..7e32e35 100644 --- a/schoolNewsWeb/src/views/task/LearingTaskDetail.vue +++ b/schoolNewsWeb/src/views/task/LearingTaskDetail.vue @@ -156,26 +156,31 @@
- - - 完成于 {{ formatDateTime(course.completeTime) }} - - - {{ course.progress ? '学习中' : '未开始' }} - +
+ + {{ getItemStatusText(course.status) }} + + + + {{ formatDateTime(course.completeTime) }} + +
+
+ {{ course.progress }}% +
- {{ course.progress ? '继续学习' : '开始学习' }} + {{ course.status === 2 ? '已完成' : (course.status === 1 ? '继续学习' : '开始学习') }}
@@ -215,26 +220,31 @@
- - - 完成于 {{ formatDateTime(resource.completeTime) }} - - - {{ resource.progress ? '阅读中' : '未开始' }} - +
+ + {{ getItemStatusText(resource.status) }} + + + + {{ formatDateTime(resource.completeTime) }} + +
+
+ {{ resource.progress }}% +
- {{ resource.progress ? '继续阅读' : '开始阅读' }} + {{ resource.status === 2 ? '已完成' : (resource.status === 1 ? '继续阅读' : '开始阅读') }}
@@ -336,7 +346,7 @@ async function loadTaskDetail() { loading.value = true; try { - const res = await learningTaskApi.getTaskById(currentTaskId.value); + const res = await learningTaskApi.getUserTask(currentTaskId.value); if (res.success && res.data) { taskVO.value = res.data; @@ -392,7 +402,7 @@ function handleResourceClick(resource: TaskItemVO) { path: '/article/show', query: { articleId: resource.resourceID, - taskId: currentTaskId.value + taskId: currentTaskId.value // 传递 taskId } }); } @@ -444,14 +454,32 @@ function getTaskStatusType(status?: number): 'info' | 'success' | 'warning' | 'd } } -// 获取课程状态类型 -function getCourseStatusType(progress?: boolean): 'success' | 'info' { - return progress ? 'success' : 'info'; +// 获取学习项状态文本(课程/资源通用) +function getItemStatusText(status?: number): string { + switch (status) { + case 0: + return '未开始'; + case 1: + return '学习中'; + case 2: + return '已完成'; + default: + return '未知'; + } } -// 获取资源状态类型 -function getResourceStatusType(progress?: boolean): 'success' | 'info' { - return progress ? 'success' : 'info'; +// 获取学习项状态类型(课程/资源通用) +function getItemStatusType(status?: number): 'info' | 'warning' | 'success' { + switch (status) { + case 0: + return 'info'; + case 1: + return 'warning'; + case 2: + return 'success'; + default: + return 'info'; + } } @@ -463,10 +491,14 @@ function getResourceStatusType(progress?: boolean): 'success' | 'info' { } .back-header { + position: sticky; + top: 0; + z-index: 100; padding: 16px 24px; background: #fff; border-bottom: 1px solid #e4e7ed; margin-bottom: 0; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } .loading { @@ -755,17 +787,32 @@ function getResourceStatusType(progress?: boolean): 'success' | 'info' { .course-meta { display: flex; align-items: center; + justify-content: space-between; gap: 12px; - .complete-time { + .status-info { display: flex; align-items: center; - gap: 4px; - font-size: 13px; - color: #67c23a; + gap: 8px; - .el-icon { - font-size: 16px; + .complete-time { + display: flex; + align-items: center; + gap: 4px; + font-size: 13px; + color: #67c23a; + + .el-icon { + font-size: 16px; + } + } + } + + .progress-info { + .progress-text { + font-size: 14px; + font-weight: 600; + color: #409eff; } } } @@ -843,17 +890,32 @@ function getResourceStatusType(progress?: boolean): 'success' | 'info' { .resource-meta { display: flex; align-items: center; + justify-content: space-between; gap: 12px; - .complete-time { + .status-info { display: flex; align-items: center; - gap: 4px; - font-size: 13px; - color: #67c23a; + gap: 8px; - .el-icon { - font-size: 16px; + .complete-time { + display: flex; + align-items: center; + gap: 4px; + font-size: 13px; + color: #67c23a; + + .el-icon { + font-size: 16px; + } + } + } + + .progress-info { + .progress-text { + font-size: 14px; + font-weight: 600; + color: #409eff; } } } diff --git a/schoolNewsWeb/vite.config.js b/schoolNewsWeb/vite.config.js index 240b359..c56c5b8 100644 --- a/schoolNewsWeb/vite.config.js +++ b/schoolNewsWeb/vite.config.js @@ -27,7 +27,7 @@ export default defineConfig({ build: { outDir: 'dist', assetsDir: 'static', - sourcemap: false, + sourcemap: true, // 开启 source map 用于调试 chunkSizeWarningLimit: 1500, rollupOptions: { output: {