feat:讨论模式页面全部完成,bug修改,吴老师说的bug修改

This commit is contained in:
小张 2025-09-19 18:48:17 +08:00
parent 061a67a5e8
commit e687fa8ebd
4 changed files with 546 additions and 91 deletions

View File

@ -762,12 +762,28 @@ const routes: RouteRecordRaw[] = [
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior(_to, _from, savedPosition) {
scrollBehavior(to, from, savedPosition) {
console.log('🔍 scrollBehavior 触发:', {
to: to.path,
from: from.path,
savedPosition,
currentScrollY: window.scrollY
})
// 如果是浏览器前进/后退,使用保存的位置
if (savedPosition) {
console.log('📍 使用保存的位置:', savedPosition)
return savedPosition
} else {
return { top: 0 }
}
// 对于所有新的路由跳转,都滚动到顶部
console.log('⬆️ 准备滚动到顶部')
return new Promise((resolve) => {
setTimeout(() => {
console.log('⬆️ 执行滚动到顶部')
resolve({ top: 0, behavior: 'smooth' })
}, 100) // 延迟确保页面已渲染
})
}
})
@ -801,4 +817,47 @@ router.beforeEach((to, from, next) => {
})
})
// 路由跳转完成后的处理
router.afterEach((to, from) => {
console.log('🔍 router.afterEach 触发:', {
to: to.path,
from: from.path,
windowScrollY: window.scrollY,
documentScrollTop: document.documentElement.scrollTop,
bodyScrollTop: document.body.scrollTop
})
// 检查所有可能的滚动容器
setTimeout(() => {
console.log('🔍 检查滚动容器:')
console.log('- window.scrollY:', window.scrollY)
console.log('- document.documentElement.scrollTop:', document.documentElement.scrollTop)
console.log('- document.body.scrollTop:', document.body.scrollTop)
// 查找所有可能的滚动容器
const scrollableElements = document.querySelectorAll('.n-layout, .n-layout-content, .content, .app-layout')
scrollableElements.forEach((el, index) => {
console.log(`- 滚动容器 ${index}:`, el.className, 'scrollTop:', el.scrollTop)
})
// 强制滚动所有容器到顶部
console.log('⬆️ 强制滚动所有容器到顶部')
window.scrollTo(0, 0)
document.documentElement.scrollTop = 0
document.body.scrollTop = 0
scrollableElements.forEach((el) => {
el.scrollTop = 0
})
// 最后检查
setTimeout(() => {
console.log('⬆️ 最终检查所有滚动位置:')
console.log('- window.scrollY:', window.scrollY)
console.log('- document.documentElement.scrollTop:', document.documentElement.scrollTop)
console.log('- document.body.scrollTop:', document.body.scrollTop)
}, 100)
}, 100)
})
export default router

View File

@ -64,7 +64,7 @@
<!-- 面包屑导航 -->
<div class="breadcrumb-section">
<div class="breadcrumb">
<span class="breadcrumb-course">{{ course.name }}</span>
<span class="breadcrumb-course">{{ course?.title || '课程' }}</span>
<span class="breadcrumb-separator"> > </span>
<span class="breadcrumb-current">{{ practiceMode ? currentPracticeSection?.name || '练习' : '讨论' }}</span>
</div>
@ -338,59 +338,47 @@
<!-- 讨论模式界面 -->
<div v-if="discussionMode" class="discussion-section">
<div class="discussion-header">
<div class="discussion-title">
<h3>{{ currentDiscussionSection?.name }} - 讨论区</h3>
<p class="discussion-subtitle">如何理解机器学习与科研流程的关系如何理解机器学习与科研流程之间的关系各位同学大家好欢迎加入本学期的课程学习在学习过程中有任何问题思考不同见解都欢迎在讨论区积极交流期待回复老师的问题</p>
</div>
<div class="discussion-actions">
<button @click="exitDiscussion" class="exit-discussion-btn">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M12 4L4 12M4 4l8 8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
退出讨论
</button>
</div>
<!-- 讨论主容器 -->
<div class="discussion-container">
<!-- 讨论标题行 -->
<div class="discussion-title-row">
<h2 class="discussion-title">讨论</h2>
<span class="participation-status">未参与</span>
</div>
<!-- 讨论描述 -->
<div class="discussion-description">
如何理解学风与科研诚信如何理解优良学风与科研诚信之间的关系各位同学大家好欢迎加入本学期的课程学习在学习过程有任何问题困惑和不同见解都欢迎同学在讨论区积极交流踊跃回复老师留在讨论区的问题
</div>
<div class="discussion-content">
<!-- 评论统计 -->
<div class="discussion-stats">
<span class="comment-count">评论 ({{ discussionList.length }})</span>
<button class="sort-btn">
<svg width="16" height="16" viewBox="0 0 16 16" class="sort-icon">
<path d="M3 3h10M3 8h7M3 13h4" stroke="currentColor" stroke-width="1.5" fill="none" />
</svg>
<span class="sort-text">正序</span>
</button>
<span class="comment-count">评论 (1251)</span>
</div>
<!-- 发表评论 -->
<!-- 评论输入区域 -->
<div class="comment-input-section">
<div class="comment-input-wrapper">
<div class="user-avatar">
<img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&auto=format&fit=crop&w=40&q=80" alt="用户头像">
</div>
<div class="input-wrapper">
<textarea
v-model="newComment"
placeholder="发表你的想法下来吧..."
class="comment-textarea"
placeholder="请把你的想法写下来~"
class="comment-input"
rows="3"
></textarea>
<div class="comment-actions">
<div class="comment-tools">
<button class="tool-btn">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M8 2v12M2 8h12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
<div class="input-toolbar">
<div class="toolbar-left">
<button class="toolbar-btn">
<img src="/images/courses/expression.png" alt="表情" class="toolbar-icon" />
</button>
<button class="tool-btn">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<rect x="2" y="3" width="12" height="10" rx="2" stroke="currentColor" stroke-width="1.5"/>
<circle cx="6.5" cy="7.5" r="1.5" stroke="currentColor" stroke-width="1.5"/>
<path d="m10 10-1.5-1.5L6 11" stroke="currentColor" stroke-width="1.5"/>
</svg>
<button class="toolbar-btn">
<img src="/images/courses/Image.png" alt="图片" class="toolbar-icon" />
</button>
</div>
<button @click="submitDiscussionComment" class="submit-comment-btn" :disabled="!newComment.trim()">
<button @click="submitDiscussionComment" class="submit-btn" :disabled="!newComment.trim()">
发表
</button>
</div>
</div>
@ -400,27 +388,25 @@
<div class="discussion-list">
<div v-for="comment in discussionList" :key="comment.id" class="discussion-item">
<div class="comment-avatar">
<img :src="comment.avatar" :alt="comment.username" />
<img src="/images/activity/1.png" :alt="comment.username" />
</div>
<div class="comment-content">
<div class="comment-header">
<span class="comment-username">{{ comment.username }}</span>
<span class="comment-time">{{ comment.time }}</span>
<span class="comment-badge" v-if="comment.isTeacher">讲师</span>
</div>
<div class="comment-text">{{ comment.content }}</div>
<div class="comment-actions">
<button @click="likeComment(comment)" class="action-btn like-btn">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M8 14s6-4 6-8c0-2-1.5-3-3-3s-3 1-3 3c0-2-1.5-3-3-3s-3 1-3 3c0 4 6 8 6 8z" stroke="currentColor" stroke-width="1.5"/>
</svg>
{{ comment.likes || 0 }}
</button>
<button class="action-btn reply-btn">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M3 8h10M8 3l5 5-5 5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
回复
<div class="comment-images" v-if="comment.images">
<img v-for="(image, index) in comment.images" :key="index" :src="image" alt="评论图片" class="comment-image">
</div>
<div class="comment-footer">
<span class="comment-time">{{ comment.time }}</span>
<div class="discussion-comment-actions">
<button @click="likeComment(comment)" class="discussion-like-btn">
👍 {{ comment.likes || 0 }}
</button>
<span class="discussion-reply-text">回复</span>
</div>
</div>
</div>
</div>
@ -2443,11 +2429,7 @@ const currentPracticeQuestion = computed(() => {
return null
})
//
const startPractice = () => {
practiceStarted.value = true
console.log('✅ 练习已开始')
}
// 退
const exitPractice = () => {
@ -2544,12 +2526,7 @@ const nextPracticeQuestion = () => {
//
const getPracticeProgress = () => {
if (practiceQuestions.value.length === 0) return 0
const answered = getAnsweredCount()
return Math.round((answered / practiceQuestions.value.length) * 100)
}
const getCurrentPracticeScore = () => {
//
@ -2669,20 +2646,53 @@ const loadDiscussionData = async (section: CourseSection) => {
discussionList.value = [
{
id: 1,
username: '张同学',
avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&auto=format&fit=crop&w=60&q=80',
content: '如何理解机器学习与科研流程的关系?如何理解机器学习与科研流程之间的关系?各位同学大家好,欢迎加入本学期的课程学习,在学习过程中有任何问题思考不同见解,都欢迎在讨论区积极交流,期待回复老师的问题。',
time: '2023.07.23 16:25',
likes: 29,
username: '春暖花开',
avatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b786?ixlib=rb-4.0.3&auto=format&fit=crop&w=40&q=80',
content: '为了让科学教育有效,它必须符合科学的本质,而且应该让学生认识科学的本质,家庭和社会如何配合学校实现科学教育的目标。',
time: '2025.07.23 16:28',
likes: 23,
replies: []
},
{
id: 2,
username: '春暖花开',
avatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b786?ixlib=rb-4.0.3&auto=format&fit=crop&w=60&q=80',
avatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b786?ixlib=rb-4.0.3&auto=format&fit=crop&w=40&q=80',
content: '为了让科学教育有效,它必须符合科学的本质,而且应该让学生认识科学的本质,家庭和社会如何配合学校实现科学教育的目标。',
time: '2023.07.23 16:25',
likes: 24,
time: '2025.07.23 16:29',
likes: 23,
replies: []
},
{
id: 3,
username: '汪深',
avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-4.0.3&auto=format&fit=crop&w=40&q=80',
content: '这位同学的观点很好,希望能够继续深入讨论',
time: '2025.07.23 16:28',
likes: 0,
isTeacher: true,
replies: []
},
{
id: 4,
username: '春暖花开',
avatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b786?ixlib=rb-4.0.3&auto=format&fit=crop&w=40&q=80',
content: '为了让科学教育有效,它必须符合科学的本质,而且应该让学生认识科学的本质,家庭和社会如何配合学校实现科学教育的目标。',
time: '2025.07.23 16:28',
likes: 23,
images: [
'https://images.unsplash.com/photo-1581091226825-a6a2a5aee158?ixlib=rb-4.0.3&auto=format&fit=crop&w=200&q=80',
'https://images.unsplash.com/photo-1518186285589-2f7649de83e0?ixlib=rb-4.0.3&auto=format&fit=crop&w=200&q=80',
'https://images.unsplash.com/photo-1485827404703-89b55fcc595e?ixlib=rb-4.0.3&auto=format&fit=crop&w=200&q=80'
],
replies: []
},
{
id: 5,
username: '春暖花开',
avatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b786?ixlib=rb-4.0.3&auto=format&fit=crop&w=40&q=80',
content: '为了让科学教育有效,它必须符合科学的本质,而且应该让学生认识科学的本质,家庭和社会如何配合学校实现科学教育的目标。',
time: '2025.07.23 16:28',
likes: 23,
replies: []
}
]
@ -8283,15 +8293,16 @@ onActivated(() => {
line-height: 1.2;
}
/* 讨论模式样式 */
/* 讨论模式样式 - 按照原型图设计 */
.discussion-section {
background: white;
border-radius: 8px;
overflow: hidden;
width: 100%;
max-width: none;
margin: 0;
padding: 0;
width: 1246px;
min-height: 864px; /* 改为最小高度,支持自适应 */
height: auto; /* 自适应高度 */
background: rgba(255, 255, 255, 0.5);
border-radius: 2px;
border: 1px solid #E5E5E5;
padding: 24px;
margin: 0 auto;
}
.discussion-header {
@ -8346,8 +8357,8 @@ onActivated(() => {
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid #f0f0f0;
padding-bottom: 6px;
}
.comment-count {
@ -8527,6 +8538,381 @@ onActivated(() => {
color: white;
}
/* 讨论容器 */
.discussion-container {
width: 100%;
height: 100%;
padding-right: 48px; /* 增加右侧距离 */
}
/* 讨论标题行 */
.discussion-title-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
width: 100%; /* 确保占满容器宽度 */
}
.discussion-title {
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 16px;
color: #0088D1;
line-height: 22px;
text-align: left;
font-style: normal;
margin: 0;
flex-shrink: 0; /* 防止标题被压缩 */
}
.participation-status {
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 16px;
color: #0088D1;
line-height: 22px;
text-align: right;
font-style: normal;
flex-shrink: 0; /* 防止状态文字被压缩 */
}
/* 讨论描述 */
.discussion-description {
width: 100%;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 16px;
color: #000000;
line-height: 22px;
text-align: justify;
font-style: normal;
margin-bottom: 32px;
}
/* 评论统计 */
.discussion-stats {
margin-bottom: 14px;
}
.comment-count {
width: 85px;
height: 20px;
font-family: PingFangSC, PingFang SC;
font-weight: 500;
font-size: 14px;
color: #008BD7;
line-height: 20px;
text-align: left;
font-style: normal;
text-transform: none;
}
/* 评论输入区域 */
.comment-input-section {
display: flex;
gap: 12px;
margin-bottom: 24px;
background: transparent; /* 无背景,无边框 */
}
.user-avatar img {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
.input-wrapper {
flex: 1;
background: transparent;
}
.comment-input {
width: 100%;
height: 36px;
padding: 8px 12px;
border: 1px solid #E6E6E6;
border-radius: 2px;
outline: none;
resize: none;
font-family: PingFangSC, PingFang SC;
font-size: 14px;
color: #333;
background: transparent; /* 透明背景 */
box-sizing: border-box;
}
.comment-input::placeholder {
color: #BFBFBF;
}
.input-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 8px; /* 减少到8px */
margin-top: 8px;
}
.toolbar-left {
display: flex;
gap: 8px;
}
.toolbar-btn {
width: 24px;
height: 20px;
border-radius: 2px;
border: 1px solid #E6E6E6;
background: transparent; /* 透明背景 */
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
}
.toolbar-icon {
width: 16px; /* 调整图标大小 */
height: 16px;
/* 移除蓝色边框 */
}
.submit-btn {
width: 48px; /* 确保宽度为48px */
height: 24px;
background: #0088D1;
border-radius: 2px;
border: none;
cursor: pointer;
transition: background-color 0.3s;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #FFFFFF;
line-height: 20px;
text-align: center;
font-style: normal;
}
.submit-btn:hover {
background: #0077B8;
}
.submit-btn:disabled {
background: #D9D9D9;
cursor: not-allowed;
}
/* 评论列表 */
.discussion-list {
background: transparent;
}
.discussion-item {
display: flex;
gap: 12px;
margin-bottom: 16px; /* 减少间距 */
background: transparent;
padding: 0; /* 移除内边距 */
}
.discussion-item:last-child {
margin-bottom: 0;
}
.comment-avatar img {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
.comment-content {
flex: 1;
background: transparent;
}
.comment-header {
display: flex;
align-items: center;
gap: 12px; /* 用户名与讲师标签间距12px更紧凑 */
margin-bottom: 6px; /* 减少间距 */
justify-content: flex-start; /* 左对齐,让讲师标签紧跟用户名 */
}
.comment-username {
height: 20px;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #666666;
line-height: 20px;
text-align: left;
font-style: normal;
white-space: nowrap; /* 不换行 */
display: inline-block; /* 宽度自适应内容 */
}
.comment-badge {
background: #1890FF;
color: white;
padding: 2px 6px;
border-radius: 2px;
font-size: 10px;
line-height: 14px;
}
.comment-text {
width: 700px;
min-height: 20px; /* 改为最小高度,支持自适应 */
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 14px;
color: #333333;
line-height: 20px;
text-align: left;
font-style: normal;
margin-bottom: 8px; /* 减少间距 */
word-wrap: break-word; /* 支持换行 */
word-break: break-all; /* 强制换行 */
}
.comment-images {
display: flex;
gap: 8px;
margin-bottom: 12px;
}
.comment-image {
width: 80px;
height: 60px;
border-radius: 4px;
object-fit: cover;
}
.comment-footer {
display: flex;
align-items: center;
margin-top: 4px; /* 减少顶部间距 */
gap: 24px; /* 时间与操作按钮间距24px */
}
.comment-time {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 12px;
color: #999999;
line-height: 20px;
text-align: left;
font-style: normal;
text-transform: none;
white-space: nowrap; /* 确保时间一行展示 */
}
.comment-actions {
display: flex;
align-items: center;
gap: 24px; /* 点赞与回复间距24px */
}
/* 讨论区域专用的评论操作样式 */
.discussion-comment-actions {
display: flex;
align-items: center;
gap: 24px; /* 点赞与回复间距24px */
}
.like-btn {
background: transparent; /* 透明背景 */
border: none;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 12px;
color: #999999;
line-height: 20px;
text-align: left;
font-style: normal;
text-transform: none;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
white-space: nowrap; /* 确保一行展示 */
}
.like-btn:hover {
color: #1890FF;
}
.reply-text {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 12px;
color: #999999;
line-height: 20px;
text-align: left;
font-style: normal;
text-transform: none;
cursor: pointer;
padding: 0;
background: transparent; /* 透明背景 */
white-space: nowrap; /* 确保一行展示 */
}
.reply-text:hover {
color: #1890FF;
}
/* 讨论区域专用的点赞和回复按钮样式 */
.discussion-like-btn {
background: transparent !important; /* 强制透明背景 */
border: none;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 12px;
color: #999999;
line-height: 20px;
text-align: left;
font-style: normal;
text-transform: none;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
padding: 0;
white-space: nowrap; /* 确保一行展示 */
}
.discussion-like-btn:hover {
color: #1890FF;
}
.discussion-reply-text {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 12px;
color: #999999;
line-height: 20px;
text-align: left;
font-style: normal;
text-transform: none;
cursor: pointer;
padding: 0;
background: transparent !important; /* 强制透明背景 */
white-space: nowrap; /* 确保一行展示 */
}
.discussion-reply-text:hover {
color: #1890FF;
}
/* 新的答题卡样式 */
/* 答题信息样式 */
.answer-info {

View File

@ -503,10 +503,10 @@
<!-- 固定按钮组 -->
<div class="fixed-buttons-group" :class="{ 'show': showFixedButtons }">
<!-- <div class="fixed-button customer-btn" title="客服">
<div class="fixed-button customer-btn" title="客服">
<img src="/images/icon/customer.jpg" alt="客服" />
</div>
<div class="fixed-button phone-btn" title="电话">
<!-- <div class="fixed-button phone-btn" title="电话">
<img src="/images/icon/phone.jpg" alt="电话" />
</div> -->
<div class="fixed-button issue-btn" title="问题" @click="showIssueModal">
@ -615,6 +615,7 @@ const handleEnrollCourse = async (course: any, event?: Event) => {
//
if (course.isEnrolled) {
console.log('✅ 用户已报名,跳转到学习页面')
console.log('🔍 跳转前的滚动位置:', window.scrollY)
router.push(`/course/${course.id}/exchanged`)
return
}

View File

@ -1371,7 +1371,15 @@ const saveExam = async () => {
// ID
if (response.data) {
// result
if (typeof response.data === 'object' && (response.data as any).result) {
paperId = (response.data as any).result;
} else if (typeof response.data === 'string') {
//
paperId = response.data;
} else {
throw new Error('创建试卷失败,返回数据格式不正确');
}
} else {
throw new Error('创建试卷失败无法获取试卷ID');
}
@ -1724,13 +1732,14 @@ const saveExamQuestions = async (paperId: string) => {
//
const paperQuestionData = {
paperId: paperId,
paperId: String(paperId), // paperId
questionId: questionId,
orderNo: questionOrder,
score: subQuestion.score || 0
};
console.log('📝 关联题目到试卷:', paperQuestionData);
console.log('🔍 paperId类型:', typeof paperId, '值:', paperId);
const paperQuestionResponse = await ExamApi.addExamPaperQuestion(paperQuestionData);
console.log('✅ 题目关联成功:', paperQuestionResponse);