diff --git a/src/api/index.ts b/src/api/index.ts index 41502a8..d030f0f 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -19,6 +19,7 @@ export const API_ENDPOINTS = { // 认证相关 AUTH: { LOGIN: '/biz/user/login', + USER_INFO: '/biz/user/info', REGISTER: '/auth/register', LOGOUT: '/auth/logout', REFRESH: '/auth/refresh', diff --git a/src/api/modules/auth.ts b/src/api/modules/auth.ts index 8c16a8b..b6b9a03 100644 --- a/src/api/modules/auth.ts +++ b/src/api/modules/auth.ts @@ -8,6 +8,8 @@ import type { // BackendLoginResponse, RegisterRequest, UserProfile, + BackendUserInfo, + UserInfoResponse, } from '../types' /** @@ -227,11 +229,82 @@ export class AuthApi { return ApiRequest.post('/auth/refresh', { refreshToken }) } - // 获取当前用户信息 + // 获取当前用户信息 - 使用后端实际接口 + static async getUserInfo(): Promise { + const response = await ApiRequest.get('/biz/user/info') + + console.log('🔍 getUserInfo - 原始响应:', response) + + // 后端返回的格式是 { success, message, code, result: BackendUserInfo, timestamp } + // 而不是标准的 ApiResponse 格式 + if (response.data && response.data.result) { + return { + success: response.data.success || (response.data.code === 200 || response.data.code === 0), + message: response.data.message || '', + code: response.data.code, + result: response.data.result, + timestamp: response.data.timestamp || Date.now() + } + } else { + // 如果是标准ApiResponse格式,直接转换 + return { + success: response.code === 200 || response.code === 0, + message: response.message || '', + code: response.code, + result: response.data, + timestamp: Date.now() + } + } + } + + // 获取当前用户信息 - 兼容旧接口 static getCurrentUser(): Promise> { return ApiRequest.get('/users/info') } + // 将后端用户信息转换为前端User格式 + static convertBackendUserToUser(backendUser: BackendUserInfo): User { + const { baseInfo, roles, extendedInfo } = backendUser + + // 转换性别 + let gender: 'male' | 'female' | 'other' = 'other' + if (baseInfo.sex === 1) gender = 'male' + else if (baseInfo.sex === 2) gender = 'female' + + // 转换状态 (1表示正常/激活,0表示禁用) + const status = baseInfo.status === 1 ? 'active' : 'inactive' + + // 确定角色 + let role: 'student' | 'teacher' | 'admin' = 'student' + if (roles.includes('admin') || roles.includes('ADMIN')) { + role = 'admin' + } else if (roles.includes('teacher') || roles.includes('TEACHER')) { + role = 'teacher' + } + + return { + id: parseInt(baseInfo.id) || 0, + username: baseInfo.username, + email: baseInfo.email, + phone: baseInfo.phone, + nickname: baseInfo.realname || baseInfo.username, + avatar: baseInfo.avatar, + role, + status: status as 'active' | 'inactive' | 'banned', + createdAt: new Date().toISOString(), // 后端没有提供,使用当前时间 + updatedAt: new Date().toISOString(), // 后端没有提供,使用当前时间 + profile: { + realName: baseInfo.realname, + gender, + birthday: baseInfo.birthday, + bio: extendedInfo.tag, + location: extendedInfo.college, + website: '', + socialLinks: {} + } + } + } + // 更新用户资料 static updateProfile(data: Partial): Promise> { return ApiRequest.put('/auth/profile', data) diff --git a/src/api/modules/course.ts b/src/api/modules/course.ts index efc2396..c226973 100644 --- a/src/api/modules/course.ts +++ b/src/api/modules/course.ts @@ -17,8 +17,10 @@ import type { BackendCourseSection, BackendInstructor, BackendSectionVideo, + BackendComment, SectionVideo, VideoQuality, + CourseComment, Quiz, LearningProgress, SearchRequest, @@ -118,6 +120,7 @@ export class CourseApi { title: item.name || '', description: item.description || '', instructor: item.school || '未知讲师', + teacherList: item.teacherList || [], // 新增:传递讲师列表 duration: item.arrangement || '待定', level: this.mapDifficultyToLevel(item.difficulty), category: item.subject || '其他', @@ -948,6 +951,87 @@ export class CourseApi { return qualities } + // 获取课程评论列表 + static async getCourseComments(courseId: string): Promise> { + try { + console.log('🔍 获取课程评论数据,课程ID:', courseId) + console.log('🔍 API请求URL: /biz/comment/course/' + courseId + '/list') + + const response = await ApiRequest.get(`/biz/comment/course/${courseId}/list`) + console.log('🔍 评论API响应:', response) + + // 处理后端响应格式 + if (response.data && response.data.success && response.data.result) { + console.log('✅ 响应状态码:', response.data.code) + console.log('✅ 响应消息:', response.data.message) + console.log('✅ 原始评论数据:', response.data.result) + console.log('✅ 评论数据数量:', response.data.result.length || 0) + + // 适配数据格式 + const adaptedComments: CourseComment[] = response.data.result.map((comment: BackendComment) => ({ + id: comment.id, + userId: comment.userId, + userName: comment.userName || '匿名用户', + userAvatar: comment.userAvatar || '', + userTag: comment.userTag || '', + content: comment.content || '', + images: comment.imgs ? comment.imgs.split(',').filter(img => img.trim()) : [], // 图片URL逗号分隔 + isTop: comment.izTop === 1, // 1=置顶,0=普通 + likeCount: comment.likeCount || 0, + createTime: comment.createTime || '', + timeAgo: this.formatTimeAgo(comment.createTime) // 计算相对时间 + })) + + console.log('✅ 适配后的评论数据:', adaptedComments) + + return { + code: response.data.code, + message: response.data.message, + data: adaptedComments + } + } else { + console.warn('⚠️ API返回的数据结构不正确:', response.data) + return { + code: 500, + message: '数据格式错误', + data: [] + } + } + } catch (error) { + console.error('❌ 评论API调用失败:', error) + throw error + } + } + + // 格式化时间为相对时间显示 + private static formatTimeAgo(createTime: string): string { + if (!createTime) return '未知时间' + + try { + const now = new Date() + const commentTime = new Date(createTime) + const diffMs = now.getTime() - commentTime.getTime() + + const diffMinutes = Math.floor(diffMs / (1000 * 60)) + const diffHours = Math.floor(diffMs / (1000 * 60 * 60)) + const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)) + const diffWeeks = Math.floor(diffDays / 7) + const diffMonths = Math.floor(diffDays / 30) + + if (diffMinutes < 1) return '刚刚' + if (diffMinutes < 60) return `${diffMinutes}分钟前` + if (diffHours < 24) return `${diffHours}小时前` + if (diffDays < 7) return `${diffDays}天前` + if (diffWeeks < 4) return `${diffWeeks}周前` + if (diffMonths < 12) return `${diffMonths}个月前` + + return commentTime.toLocaleDateString() + } catch (error) { + console.warn('时间格式化失败:', error) + return createTime + } + } + } export default CourseApi diff --git a/src/api/types.ts b/src/api/types.ts index 7f4de62..97ad735 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -46,6 +46,39 @@ export interface UserProfile { } } +// 后端用户信息接口返回的数据结构 +export interface BackendUserInfo { + baseInfo: { + id: string + username: string + realname: string + avatar: string + phone: string + email: string + sex: number // 0: 未知, 1: 男, 2: 女 + birthday: string + status: number // 0: 正常, 1: 禁用 + } + roles: string[] + extendedInfo: { + major: string + college: string + education: string + title: string + tag: string + sortOrder: number + } +} + +// 用户信息接口响应类型 +export interface UserInfoResponse { + success: boolean + message: string + code: number + result: BackendUserInfo + timestamp: number +} + // 登录注册类型 export interface LoginRequest { email?: string @@ -107,6 +140,7 @@ export interface Course { requirements: string[] objectives: string[] instructor: Instructor + teacherList?: BackendInstructor[] // 新增讲师列表字段(从后端适配) status: 'draft' | 'published' | 'archived' isEnrolled?: boolean progress?: number @@ -279,6 +313,7 @@ export interface BackendCourseItem { createTime: string updateBy: string updateTime: string + teacherList: BackendInstructor[] // 新增讲师列表字段 } // 后端课程列表响应格式 @@ -415,6 +450,7 @@ export interface BackendInstructor { avatar: string title: string tag: string + sortOrder?: number // 排序字段,用于多讲师排序 } // 后端讲师列表响应格式 @@ -475,6 +511,49 @@ export interface SectionVideo { currentQuality: string // 当前选中的质量 } +// 后端评论数据结构 +export interface BackendComment { + id: string + userId: string + targetType: string + targetId: string + content: string + imgs: string + izTop: number // 是否置顶:0=否,1=是 + likeCount: number + createBy: string + createTime: string + updateBy: string + updateTime: string + userName: string + userAvatar: string + userTag: string +} + +// 后端评论列表响应格式 +export interface BackendCommentListResponse { + success: boolean + message: string + code: number + result: BackendComment[] + timestamp: number +} + +// 前端评论类型 +export interface CourseComment { + id: string + userId: string + userName: string + userAvatar: string + userTag: string + content: string + images: string[] // 评论图片列表 + isTop: boolean // 是否置顶 + likeCount: number + createTime: string + timeAgo: string // 相对时间显示(如"2天前") +} + // 前端章节列表响应格式 export interface CourseSectionListResponse { list: CourseSection[] diff --git a/src/components/auth/LoginModal.vue b/src/components/auth/LoginModal.vue index 8a7f194..728e043 100644 --- a/src/components/auth/LoginModal.vue +++ b/src/components/auth/LoginModal.vue @@ -121,21 +121,48 @@ const handleLogin = async () => { if (response.code === 200 || response.code === 0) { const { user, token, refreshToken } = response.data - // 保存用户信息和token到store - userStore.user = user + // 保存token到store和本地存储 userStore.token = token - - // 保存到本地存储 localStorage.setItem('X-Access-Token', token) localStorage.setItem('token', token) localStorage.setItem('refreshToken', refreshToken || '') - localStorage.setItem('user', JSON.stringify(user)) // 如果选择了记住我,设置更长的过期时间 if (loginForm.remember) { localStorage.setItem('rememberMe', 'true') } + try { + // 登录成功后立即调用用户信息接口获取完整的用户信息 + console.log('🔍 登录成功,正在获取用户信息...') + const userInfoResponse = await AuthApi.getUserInfo() + + if (userInfoResponse.success && userInfoResponse.result) { + // 将后端用户信息转换为前端格式 + const convertedUser = AuthApi.convertBackendUserToUser(userInfoResponse.result) + + console.log('🔍 转换后的用户信息:', convertedUser) + console.log('🔍 用户真实姓名:', convertedUser.profile?.realName) + console.log('🔍 用户头像:', convertedUser.avatar) + + // 保存转换后的用户信息 + userStore.user = convertedUser + localStorage.setItem('user', JSON.stringify(convertedUser)) + + console.log('✅ 用户信息获取成功并保存到store:', userStore.user) + } else { + // 如果获取用户信息失败,使用登录接口返回的基本用户信息 + console.warn('⚠️ 获取用户信息失败,使用登录返回的基本信息') + userStore.user = user + localStorage.setItem('user', JSON.stringify(user)) + } + } catch (userInfoError) { + // 如果获取用户信息失败,使用登录接口返回的基本用户信息 + console.warn('⚠️ 获取用户信息异常,使用登录返回的基本信息:', userInfoError) + userStore.user = user + localStorage.setItem('user', JSON.stringify(user)) + } + message.success('登录成功!') emit('success') closeModal() diff --git a/src/components/layout/AppHeader.vue b/src/components/layout/AppHeader.vue index 63e9dab..bb1b91d 100644 --- a/src/components/layout/AppHeader.vue +++ b/src/components/layout/AppHeader.vue @@ -101,8 +101,8 @@
@@ -120,7 +120,7 @@ @@ -3306,16 +3287,12 @@ onMounted(() => { } /* 讲师评论特殊样式 */ -.reply-item.instructor-reply {} - .reply-item.instructor-reply .reply-username { color: #666666; font-weight: 500; } /* 用户评论样式 */ -.reply-item.user-reply {} - .reply-item.user-reply .reply-username { color: #666666; font-weight: 500; @@ -3564,7 +3541,6 @@ onMounted(() => { padding-left: 24px; padding-right: 24px; } - /* 响应式设计 */ @media (max-width: 1399px) and (min-width: 1200px) { .container { diff --git a/src/views/CourseDetailEnrolled.vue b/src/views/CourseDetailEnrolled.vue index 775a685..303de62 100644 --- a/src/views/CourseDetailEnrolled.vue +++ b/src/views/CourseDetailEnrolled.vue @@ -184,168 +184,58 @@
-
-
- -
-
-
- {{ comment.username }} -
-
{{ comment.content }}
-
- - - -
- - -
-
- 回复 @{{ replyToUsername }} - -
-
- -
-
- - - -
-
- -
-
-
-
- - -
- -
-
- 讲师头像 -
-
-
-
- 张老师 - 讲师 -
-
感谢您的反馈!我们会继续优化课程内容,让学习体验更好。
-
- -
-
- - -
-
- 用户头像 -
-
-
-
- 李同学 - 学员 -
-
同意楼上的观点,这个课程确实很有帮助!
-
- -
-
-
-
+ +
+

正在加载评论...

-
-
- 张老师 -
-
-
- 张老师 -
-
这个课程内容很实用,讲解得很清楚,对初学者很有帮助!111
-
- 课程图片 - 课程图片 - 课程图片 - 课程图片 - 课程图片 - 课程图片 -
- +6 -
-
-
-
- 笔记 - 笔记 -
- 2025.07.23 16:28 - -
+ +
+

{{ commentsError }}

+ +
- -
-
- 回复 @{{ replyToUsername }} - + +
+
+
+ +
+
+
+ {{ comment.userName }} + {{ comment.userTag }} + {{ comment.timeAgo }}
-
- -
-
- - - -
-
- -
-
+
{{ comment.content }}
+ + +
+ +
+ +
+ + +
+ +
@@ -527,7 +417,7 @@ import { ref, onMounted, onUnmounted, computed, nextTick } from 'vue' import { useRoute, useRouter } from 'vue-router' import { useUserStore } from '@/stores/user' import { CourseApi } from '@/api/modules/course' -import type { Course, CourseSection, SectionVideo, VideoQuality } from '@/api/types' +import type { Course, CourseSection, SectionVideo, VideoQuality, CourseComment, Instructor } from '@/api/types' import SafeAvatar from '@/components/common/SafeAvatar.vue' import LearningProgressStats from '@/components/common/LearningProgressStats.vue' import NotesModal from '@/components/common/NotesModal.vue' @@ -555,6 +445,19 @@ const currentQuality = ref('360') // 默认360p const videoLoading = ref(false) const showQualityMenu = ref(false) +// 评论相关状态 +const comments = ref([]) +const commentsLoading = ref(false) +const commentsError = ref('') + +// 讲师相关状态 +const instructors = ref([]) +const instructorsLoading = ref(false) +const instructorsError = ref('') + +// 评论输入相关状态 +const newComment = ref('') + // 视频源配置 const VIDEO_CONFIG = { // 本地视频(当前使用) @@ -640,27 +543,7 @@ const activeTab = ref('intro') // 笔记弹窗相关 const showNotesModal = ref(false) -// 讲师数据 -const instructors = ref([ - { - id: 1, - name: '汪波', - title: '教授', - avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&auto=format&fit=crop&w=60&q=80' - }, - { - id: 2, - name: '汪波', - title: '教授', - avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&auto=format&fit=crop&w=60&q=80' - }, - { - id: 3, - name: '汪波', - title: '教授', - avatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b786?ixlib=rb-4.0.3&auto=format&fit=crop&w=60&q=80' - } -]) +// 讲师数据现在使用API数据 (instructors状态在上面已声明) // 计算属性 const totalLessons = computed(() => { @@ -690,126 +573,8 @@ const formatTotalDuration = () => { return `${hours}小时${minutes}分钟` } -const displayComments = ref([ - { - id: 1, - username: '学习者小王', - avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&auto=format&fit=crop&w=50&q=80', - time: '2天前', - content: '老师讲得很详细,从零基础到实际应用都有涉及,非常适合初学者!', - likes: 23, - type: 'comment' - }, - { - id: 2, - username: 'AI爱好者', - avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&auto=format&fit=crop&w=50&q=80', - time: '5天前', - content: '课程内容很实用,跟着做了几个项目,收获很大。推荐给想学AI的朋友们!', - likes: 18, - type: 'comment' - }, - { - id: 3, - username: '程序员小李', - avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&auto=format&fit=crop&w=50&q=80', - time: '1周前', - content: 'DeepSeek确实是个很强大的工具,通过这个课程学会了很多实用技巧。', - likes: 31, - type: 'comment' - } -]) - -// 新评论内容 -const newComment = ref('') - -// 自动调整textarea高度 -const adjustTextareaHeight = (event: Event) => { - const textarea = event.target as HTMLTextAreaElement - textarea.style.height = 'auto' - textarea.style.height = textarea.scrollHeight + 'px' -} - -// 点击textarea时调整高度 -const handleTextareaClick = (event: MouseEvent) => { - const textarea = event.target as HTMLTextAreaElement - // 如果当前高度是40px,则调整到60px - if (textarea.style.height === '40px' || textarea.style.height === '') { - textarea.style.height = '60px' - } -} - -// 提交评论函数 -const submitComment = () => { - if (newComment.value.trim()) { - const newCommentObj = { - id: Date.now(), - username: '当前用户', - avatar: 'https://via.placeholder.com/40x40/1890ff/ffffff?text=我', - time: '刚刚', - content: newComment.value, - likes: 0, - type: 'comment' - } - displayComments.value.unshift(newCommentObj) - newComment.value = '' - // 这里可以调用API提交评论 - console.log('评论已提交:', newCommentObj) - } -} - -// 回复相关函数 -const startReply = (commentId: number, username: string) => { - replyingTo.value = commentId - replyToUsername.value = username - replyText.value = '' -} - -const cancelReply = () => { - replyingTo.value = null - replyToUsername.value = '' - replyText.value = '' -} - -const submitReply = () => { - if (replyText.value.trim() && replyingTo.value) { - const newReplyObj = { - id: Date.now(), - username: '当前用户', - avatar: 'https://via.placeholder.com/40x40/1890ff/ffffff?text=我', - time: '刚刚', - content: replyText.value, - likes: 0 - } - - // 这里可以调用API提交回复 - console.log('回复已提交:', newReplyObj) - console.log('回复给评论ID:', replyingTo.value) - console.log('回复给用户:', replyToUsername.value) - - // 清空回复状态 - cancelReply() - } -} - -// 回复文本框高度调整 -const adjustReplyTextareaHeight = (event: Event) => { - const textarea = event.target as HTMLTextAreaElement - textarea.style.height = '40px' - textarea.style.height = textarea.scrollHeight + 'px' -} - -const handleReplyTextareaClick = (event: MouseEvent) => { - const textarea = event.target as HTMLTextAreaElement - if (textarea.style.height === '40px' || !textarea.style.height) { - textarea.style.height = '60px' - } -} - -// 回复相关数据 -const replyingTo = ref(null) -const replyText = ref('') -const replyToUsername = ref('') +// displayComments 现在使用真实的API数据 (comments状态) +// const displayComments = ref([]) // 已替换为comments状态 // 生成模拟章节数据(暂时禁用) const generateMockSections = (): CourseSection[] => { @@ -1277,6 +1042,27 @@ const saveNote = (content: string) => { // 这里可以添加保存笔记到服务器的逻辑 } +// 评论相关函数 +const adjustTextareaHeight = (event: Event) => { + const textarea = event.target as HTMLTextAreaElement + textarea.style.height = 'auto' + textarea.style.height = textarea.scrollHeight + 'px' +} + +const handleTextareaClick = () => { + // 处理评论输入框点击事件 + console.log('评论输入框被点击') +} + +const submitComment = () => { + if (!newComment.value.trim()) return + + console.log('提交评论:', newComment.value) + // 这里可以调用API提交评论 + // 提交成功后清空输入框 + newComment.value = '' +} + onMounted(async () => { console.log('已报名课程详情页加载完成,课程ID:', courseId.value) initializeEnrolledState() // 初始化已报名状态 @@ -1287,6 +1073,8 @@ onMounted(async () => { } loadCourseDetail() loadCourseSections() + loadCourseComments() // 启用评论接口调用 + loadCourseInstructors() // 启用讲师接口调用 }) // 组件卸载时清理播放器实例 @@ -1356,6 +1144,7 @@ onUnmounted(() => { .video-player { background: #000; overflow: hidden; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } .video-container { @@ -1558,6 +1347,34 @@ onUnmounted(() => { color: #999; } +/* 讲师加载状态 */ +.instructors-loading { + padding: 20px; + text-align: center; + color: #666; +} + +.instructors-error { + padding: 20px; + text-align: center; + color: #ff4d4f; +} + +.instructors-error .retry-btn { + margin-top: 10px; + padding: 6px 12px; + background: #1890ff; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 12px; +} + +.instructors-error .retry-btn:hover { + background: #40a9ff; +} + /* 分隔线样式 */ .course-info-divider { height: 1px; @@ -1679,7 +1496,7 @@ onUnmounted(() => { align-items: center; justify-content: space-between; padding: 0 0 10px 0; - /* background: #F5F7FA; */ + background: #F5F7FA; border-bottom: 1px solid #f0f0f0; } @@ -3343,4 +3160,68 @@ onUnmounted(() => { .reply-action-btn:hover { color: #1890ff; } + +/* 评论加载和错误状态 */ +.comments-loading, .comments-error, .no-comments { + padding: 40px 20px; + text-align: center; + color: #666; +} + +.comments-error { + color: #ff4d4f; +} + +.comments-error .retry-btn { + margin-top: 10px; + padding: 8px 16px; + background: #1890ff; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; +} + +.comments-error .retry-btn:hover { + background: #40a9ff; +} + +/* 评论用户标签 */ +.comment-tag { + display: inline-block; + padding: 2px 6px; + background: #f0f0f0; + color: #666; + font-size: 10px; + border-radius: 2px; + margin-left: 8px; +} + +/* 置顶评论样式 */ +.top-comment { + background: #fff7e6 !important; + color: #fa8c16 !important; + border: 1px solid #ffd591 !important; +} + +.top-comment .top { + font-weight: 500; +} + +/* 点赞按钮 */ +.like-btn { + display: flex; + align-items: center; + gap: 4px; +} + +.like-icon { + color: #999; + transition: color 0.2s; +} + +.like-btn:hover .like-icon { + color: #ff4d4f; +} diff --git a/src/views/Courses.vue b/src/views/Courses.vue index 4fdc1e3..6217654 100644 --- a/src/views/Courses.vue +++ b/src/views/Courses.vue @@ -107,7 +107,7 @@
@@ -385,6 +385,39 @@ const getCourseTitle = (course: Course) => { return course.title } +// 获取课程讲师名称的函数 +const getCourseInstructors = (course: Course) => { + // 检查是否有teacherList字段(从后端数据适配而来) + if (course.teacherList && Array.isArray(course.teacherList) && course.teacherList.length > 0) { + // 按sortOrder降序排列讲师(sortOrder越大越靠前) + const sortedTeachers = [...course.teacherList].sort((a, b) => { + const sortOrderA = a.sortOrder || 0 + const sortOrderB = b.sortOrder || 0 + return sortOrderB - sortOrderA // 降序排列 + }) + + // 提取所有讲师的名字,用逗号分隔 + const teacherNames = sortedTeachers.map(teacher => teacher.name).join('、') + + console.log('🔍 课程讲师信息:', { + courseTitle: course.title, + originalTeachers: course.teacherList, + sortedTeachers: sortedTeachers, + teacherNames: teacherNames + }) + + return teacherNames + } + + // 如果没有teacherList,检查instructor字段(兼容旧数据) + if (course.instructor && course.instructor.name) { + return course.instructor.name + } + + // 默认值 + return '暂无讲师信息' +} + // 跳转到课程详情页 const goToCourseDetail = (course: Course) => { router.push({ diff --git a/src/views/Login.vue b/src/views/Login.vue index 7a8adfa..b8d92f3 100644 --- a/src/views/Login.vue +++ b/src/views/Login.vue @@ -187,20 +187,48 @@ const handleSubmit = async () => { if (response.code === 200) { const { user, token, refreshToken } = response.data - // 保存用户信息和token到store - userStore.user = user + // 保存token到store和本地存储 userStore.token = token - - // 保存到本地存储 + localStorage.setItem('X-Access-Token', token) localStorage.setItem('token', token) localStorage.setItem('refreshToken', refreshToken) - localStorage.setItem('user', JSON.stringify(user)) // 如果选择了记住我,设置更长的过期时间 if (rememberMe.value) { localStorage.setItem('rememberMe', 'true') } + try { + // 登录成功后立即调用用户信息接口获取完整的用户信息 + console.log('🔍 登录成功,正在获取用户信息...') + const userInfoResponse = await AuthApi.getUserInfo() + + if (userInfoResponse.success && userInfoResponse.result) { + // 将后端用户信息转换为前端格式 + const convertedUser = AuthApi.convertBackendUserToUser(userInfoResponse.result) + + console.log('🔍 转换后的用户信息:', convertedUser) + console.log('🔍 用户真实姓名:', convertedUser.profile?.realName) + console.log('🔍 用户头像:', convertedUser.avatar) + + // 保存转换后的用户信息 + userStore.user = convertedUser + localStorage.setItem('user', JSON.stringify(convertedUser)) + + console.log('✅ 用户信息获取成功并保存到store:', userStore.user) + } else { + // 如果获取用户信息失败,使用登录接口返回的基本用户信息 + console.warn('⚠️ 获取用户信息失败,使用登录返回的基本信息') + userStore.user = user + localStorage.setItem('user', JSON.stringify(user)) + } + } catch (userInfoError) { + // 如果获取用户信息失败,使用登录接口返回的基本用户信息 + console.warn('⚠️ 获取用户信息异常,使用登录返回的基本信息:', userInfoError) + userStore.user = user + localStorage.setItem('user', JSON.stringify(user)) + } + message.success('登录成功!') // 登录成功后跳转到首页或之前的页面 diff --git a/src/views/Profile.vue b/src/views/Profile.vue index 117286b..0b2f2cc 100644 --- a/src/views/Profile.vue +++ b/src/views/Profile.vue @@ -12,9 +12,9 @@
- - {{ userStore.user?.nickname || userStore.user?.username || '用户名' }} + {{ userStore.user?.profile?.realName || userStore.user?.nickname || userStore.user?.username || '用户名' }}
@@ -22,81 +22,72 @@ -
- + 我的课程
-
- + 我的作业
-
- + 我的考试
-
- + 我的练习
-
- + 我的活动
-
- + 我的关注
-
- + 我的消息
-
- + 我的资料
-
- + 我的下载 @@ -247,7 +238,7 @@ fontSize: '14px' }"> {{ detailAssignment.status === '未完成' || detailAssignment.status === '待提交' ? '未完成' : - (detailAssignment.status === '已完成' ? '已完成' : '541人已完成') }} + (detailAssignment.status === '已完成' ? '已完成' : '541人已完成') }}
@@ -370,7 +361,7 @@ 上传作业
- 重新编辑 + 重新编辑
@@ -676,7 +667,7 @@
{{ activity.status === 'ongoing' ? '进行中' : '已结束' - }} + }}
@@ -863,7 +854,7 @@
@@ -986,7 +978,9 @@
@@ -1250,7 +1244,6 @@ const userStore = useUserStore() type TabType = 'courses' | 'homework' | 'exam' | 'practice' | 'activity' | 'follows' | 'message' | 'materials' | 'download' const activeTab = ref('courses') -const hoveredTab = ref(null) const activeCourseTab = ref('all') // 作业筛选状态 @@ -2493,11 +2486,6 @@ const handleDownloadTabChange = (tab: string) => { // 筛选后的下载文件 const filteredDownloadFiles = computed(() => { - // 如果在子目录中,显示空内容(因为目前没有子目录的文件数据) - if (isInSubDirectory.value) { - return [] - } - let files = downloadFiles.filter(file => file.category === activeDownloadTab.value) if (downloadFilter.type !== 'all') { @@ -2519,14 +2507,10 @@ const toggleFileMenu = (fileId: number) => { } const handleFileClick = (file: any) => { - if (file.type === 'folder') { - // 点击任何文件夹,进入子目录 + if (file.type === 'folder' && file.name === '图片') { + // 点击图片文件夹,进入子目录 isInSubDirectory.value = true - currentPath.value = ['课件', file.name] - message.info(`进入文件夹:${file.name}`) - } else { - // 点击文件,显示文件详情或下载 - message.info(`打开文件:${file.name}`) + currentPath.value = ['课件', '图片'] } } @@ -2552,9 +2536,12 @@ const getFileIcon = (fileId?: number) => { ] const index = (fileId || 0) % homeworkImages.length return homeworkImages[index] + } else if (isInSubDirectory.value) { + // 子目录使用子目录图标 + return 'https://lanhu-oss-2537-2.lanhuapp.com/SketchPngf45333052202c303acc2c06223c26b820d330459ce2d452a21a3132fbbeab442' } else { // 默认文件夹图标 - return '/images/profile/folder.png' + return 'https://lanhu-oss-2537-2.lanhuapp.com/SketchPng5548891b00234027dbe6dadafbd83596d616261421c0587a85652dc194b2d5ef' } } @@ -2943,8 +2930,9 @@ onActivated(() => { /* 去掉背景色 */ border-radius: 0.6vw; /* 12px转换为vw */ - margin: 2.55vh 0 0 0; + margin: 2.55vh 0; /* 去掉左右边距,因为父容器已经居中 */ + padding: 1.04vh 0; /* 20px 0转换 */ display: flex; flex-direction: column; @@ -2979,7 +2967,7 @@ onActivated(() => { /* 自适应高度 */ min-height: 3vh; /* 设置最小高度,让盒子更大 */ - margin: 1.5vh 0 0 0; + margin: 1.5vh; /* 减小间距:从2.34vh减少到1.5vh */ display: flex; align-items: center; @@ -4198,9 +4186,9 @@ onActivated(() => { } .course-name { - /* margin-top: 10px; */ - margin-left: 5px; - color: #497087; + margin-top: 10px; + margin-left: 83px; + color: #999999; } .course-name span { @@ -5756,7 +5744,7 @@ onActivated(() => { /* 0 0 16px 0转换 */ line-height: 1.4; padding: 0 1.04vw; - font-weight: 700; + /* 添加左右内边距 */ } .activity-details { @@ -6184,7 +6172,7 @@ onActivated(() => { /* height: 5.21vh; */ padding: 0.52vh 0.57vw; /* 100px转换为vh,进一步增加高度 */ - background: #F5F8FB; + background: url('https://lanhu-oss-2537-2.lanhuapp.com/SketchPng9491a7fe5bdac8e8a88de63907163bd6b8a259824f56a3c76784ba6cdc7bc32b') 100% no-repeat; background-size: 100% 100%; margin-top: 0.26vh; /* 5px转换为vh */ @@ -7070,7 +7058,6 @@ onActivated(() => { box-sizing: border-box; display: flex; align-items: center; - padding-left: 15px; } .password-form-input { @@ -7622,7 +7609,7 @@ onActivated(() => { /* 4px转换为vw,减小图标和文字间距 */ padding: 0.52vh 0.73vw; /* 10px 14px转换 */ - font-size: 12px; + font-size: 10px; /* 14px转换为vw */ font-family: Helvetica, 'Microsoft YaHei', Arial, sans-serif; color: #000; @@ -8079,7 +8066,7 @@ onActivated(() => { width: 80px; height: 23px; border: none; - font-size: 12px; + font-size: 10px; cursor: pointer; transition: all 0.3s ease; white-space: nowrap;