298 lines
6.7 KiB
Markdown
298 lines
6.7 KiB
Markdown
![]() |
# 课程详情页报名状态功能更新说明
|
|||
|
|
|||
|
## 🎯 更新目标
|
|||
|
|
|||
|
实现课程详情页面的完整报名状态管理,包括:
|
|||
|
1. 判断登录和报名状态
|
|||
|
2. 未报名状态显示灰色不可点击样式
|
|||
|
3. 点击立即报名弹出确认提示框
|
|||
|
4. 确认后跳转到已报名的课程详情页面
|
|||
|
5. 更新右侧课程章节头部样式
|
|||
|
|
|||
|
## 📋 功能实现
|
|||
|
|
|||
|
### 1. 报名状态管理
|
|||
|
```javascript
|
|||
|
// 报名状态管理
|
|||
|
const isEnrolled = ref(false) // 用户是否已报名该课程
|
|||
|
const enrollmentLoading = ref(false) // 报名加载状态
|
|||
|
|
|||
|
// 计算用户是否已报名
|
|||
|
const isUserEnrolled = computed(() => {
|
|||
|
// 必须同时满足:用户已登录 AND 已报名该课程
|
|||
|
return userStore.isLoggedIn && isEnrolled.value
|
|||
|
})
|
|||
|
|
|||
|
// 报名确认弹窗
|
|||
|
const enrollConfirmVisible = ref(false)
|
|||
|
const enrollSuccessVisible = ref(false)
|
|||
|
```
|
|||
|
|
|||
|
### 2. 报名流程处理
|
|||
|
```javascript
|
|||
|
// 处理课程报名
|
|||
|
const handleEnrollCourse = () => {
|
|||
|
if (!userStore.isLoggedIn) {
|
|||
|
// 未登录,显示登录弹窗
|
|||
|
showLoginModal()
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
if (isEnrolled.value) {
|
|||
|
// 已报名,直接跳转到学习页面
|
|||
|
router.push(`/course/${courseId.value}/study`)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// 未报名,显示报名确认弹窗
|
|||
|
enrollConfirmVisible.value = true
|
|||
|
}
|
|||
|
|
|||
|
// 确认报名
|
|||
|
const confirmEnrollment = async () => {
|
|||
|
try {
|
|||
|
enrollmentLoading.value = true
|
|||
|
|
|||
|
// 模拟API调用
|
|||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
|||
|
|
|||
|
// 报名成功
|
|||
|
isEnrolled.value = true
|
|||
|
enrollConfirmVisible.value = false
|
|||
|
enrollSuccessVisible.value = true
|
|||
|
|
|||
|
// 2秒后自动跳转
|
|||
|
setTimeout(() => {
|
|||
|
enrollSuccessVisible.value = false
|
|||
|
router.push(`/course/${courseId.value}/study`)
|
|||
|
}, 2000)
|
|||
|
|
|||
|
} catch (error) {
|
|||
|
console.error('报名失败:', error)
|
|||
|
} finally {
|
|||
|
enrollmentLoading.value = false
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 3. 课程章节状态控制
|
|||
|
```html
|
|||
|
<div class="lesson-content"
|
|||
|
:class="{ 'unregistered': !isUserEnrolled }"
|
|||
|
@click="isUserEnrolled ? handleSectionClick(section) : handleUnregisteredClick(section)">
|
|||
|
|
|||
|
<div class="lesson-type-badge"
|
|||
|
:class="[getLessonTypeBadgeClass(section), { 'disabled': !isUserEnrolled }]">
|
|||
|
{{ getLessonTypeText(section) }}
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="lesson-info">
|
|||
|
<span class="lesson-title" :class="{ 'disabled': !isUserEnrolled }">
|
|||
|
{{ section.name }}
|
|||
|
</span>
|
|||
|
</div>
|
|||
|
|
|||
|
<div class="lesson-actions">
|
|||
|
<button class="lesson-action-btn"
|
|||
|
:class="{ 'disabled': !isUserEnrolled }"
|
|||
|
:disabled="!isUserEnrolled"
|
|||
|
@click.stop="isUserEnrolled ? handleSectionClick(section) : handleUnregisteredClick(section)">
|
|||
|
<!-- 图标 -->
|
|||
|
</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
```
|
|||
|
|
|||
|
### 4. 报名确认弹窗
|
|||
|
```html
|
|||
|
<!-- 报名确认弹窗 -->
|
|||
|
<div v-if="enrollConfirmVisible" class="modal-overlay" @click="cancelEnrollment">
|
|||
|
<div class="modal-content" @click.stop>
|
|||
|
<div class="modal-header">
|
|||
|
<h3>确认报名</h3>
|
|||
|
<button class="modal-close" @click="cancelEnrollment">×</button>
|
|||
|
</div>
|
|||
|
<div class="modal-body">
|
|||
|
<p>确定要报名《{{ course?.title }}》课程吗?</p>
|
|||
|
<p class="modal-tip">报名后您将获得完整的学习权限</p>
|
|||
|
</div>
|
|||
|
<div class="modal-footer">
|
|||
|
<button class="btn-cancel" @click="cancelEnrollment">取消</button>
|
|||
|
<button class="btn-confirm" @click="confirmEnrollment" :disabled="enrollmentLoading">
|
|||
|
{{ enrollmentLoading ? '报名中...' : '确认报名' }}
|
|||
|
</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- 报名成功弹窗 -->
|
|||
|
<div v-if="enrollSuccessVisible" class="modal-overlay">
|
|||
|
<div class="modal-content success-modal">
|
|||
|
<div class="success-icon">✓</div>
|
|||
|
<h3>报名成功!</h3>
|
|||
|
<p>正在跳转到学习页面...</p>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
```
|
|||
|
|
|||
|
## 🎨 样式更新
|
|||
|
|
|||
|
### 1. 课程章节头部样式
|
|||
|
根据图片标准更新:
|
|||
|
```html
|
|||
|
<div class="sections-header">
|
|||
|
<div class="header-left">
|
|||
|
<h3 class="sections-title">课程章节</h3>
|
|||
|
</div>
|
|||
|
<div class="header-right">
|
|||
|
<button class="sort-btn">
|
|||
|
<svg class="sort-icon">...</svg>
|
|||
|
<span class="sort-text">正序</span>
|
|||
|
</button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
```
|
|||
|
|
|||
|
```css
|
|||
|
.sections-header {
|
|||
|
display: flex;
|
|||
|
align-items: center;
|
|||
|
justify-content: space-between;
|
|||
|
padding: 16px 20px;
|
|||
|
background: white;
|
|||
|
border-bottom: 1px solid #f0f0f0;
|
|||
|
}
|
|||
|
|
|||
|
.sections-title {
|
|||
|
font-size: 18px;
|
|||
|
font-weight: 600;
|
|||
|
color: #333;
|
|||
|
margin: 0;
|
|||
|
}
|
|||
|
|
|||
|
.sort-btn {
|
|||
|
background: none;
|
|||
|
border: none;
|
|||
|
color: #999;
|
|||
|
padding: 6px 12px;
|
|||
|
border-radius: 4px;
|
|||
|
cursor: pointer;
|
|||
|
font-size: 14px;
|
|||
|
display: flex;
|
|||
|
align-items: center;
|
|||
|
gap: 6px;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 2. 未报名状态灰色样式
|
|||
|
```css
|
|||
|
/* 未报名状态的灰色样式 */
|
|||
|
.lesson-content.unregistered {
|
|||
|
cursor: not-allowed;
|
|||
|
}
|
|||
|
|
|||
|
.lesson-title.disabled {
|
|||
|
color: #999;
|
|||
|
}
|
|||
|
|
|||
|
.lesson-duration.disabled {
|
|||
|
color: #999;
|
|||
|
}
|
|||
|
|
|||
|
.lesson-type-badge.disabled {
|
|||
|
background: #d9d9d9 !important;
|
|||
|
color: #999 !important;
|
|||
|
}
|
|||
|
|
|||
|
.lesson-action-btn.disabled {
|
|||
|
cursor: not-allowed;
|
|||
|
opacity: 0.5;
|
|||
|
}
|
|||
|
|
|||
|
.lesson-action-btn.disabled svg {
|
|||
|
color: #d9d9d9 !important;
|
|||
|
}
|
|||
|
|
|||
|
.completion-icon.disabled {
|
|||
|
opacity: 0.5;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 3. 弹窗样式
|
|||
|
```css
|
|||
|
.modal-overlay {
|
|||
|
position: fixed;
|
|||
|
top: 0;
|
|||
|
left: 0;
|
|||
|
right: 0;
|
|||
|
bottom: 0;
|
|||
|
background: rgba(0, 0, 0, 0.5);
|
|||
|
display: flex;
|
|||
|
align-items: center;
|
|||
|
justify-content: center;
|
|||
|
z-index: 1000;
|
|||
|
}
|
|||
|
|
|||
|
.modal-content {
|
|||
|
background: white;
|
|||
|
border-radius: 8px;
|
|||
|
max-width: 400px;
|
|||
|
width: 90%;
|
|||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|||
|
}
|
|||
|
|
|||
|
.success-modal {
|
|||
|
text-align: center;
|
|||
|
padding: 40px 24px;
|
|||
|
}
|
|||
|
|
|||
|
.success-icon {
|
|||
|
width: 60px;
|
|||
|
height: 60px;
|
|||
|
background: #52c41a;
|
|||
|
border-radius: 50%;
|
|||
|
display: flex;
|
|||
|
align-items: center;
|
|||
|
justify-content: center;
|
|||
|
margin: 0 auto 20px;
|
|||
|
font-size: 30px;
|
|||
|
color: white;
|
|||
|
font-weight: bold;
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
## ✅ 功能流程
|
|||
|
|
|||
|
### 未登录用户
|
|||
|
1. 点击"立即报名" → 显示登录弹窗
|
|||
|
2. 登录成功后 → 显示报名确认弹窗
|
|||
|
3. 确认报名 → 显示报名成功提示 → 跳转学习页面
|
|||
|
|
|||
|
### 已登录未报名用户
|
|||
|
1. 点击"立即报名" → 显示报名确认弹窗
|
|||
|
2. 确认报名 → 显示报名成功提示 → 跳转学习页面
|
|||
|
3. 点击课程章节 → 显示报名确认弹窗
|
|||
|
|
|||
|
### 已报名用户
|
|||
|
1. 点击"立即报名" → 直接跳转学习页面
|
|||
|
2. 点击课程章节 → 正常进入学习
|
|||
|
|
|||
|
## 🎯 视觉效果
|
|||
|
|
|||
|
### 未报名状态
|
|||
|
- 课程章节显示为灰色
|
|||
|
- 所有按钮不可点击
|
|||
|
- 鼠标悬停显示禁用状态
|
|||
|
|
|||
|
### 已报名状态
|
|||
|
- 课程章节显示为彩色
|
|||
|
- 所有功能正常可用
|
|||
|
- 正常的交互反馈
|
|||
|
|
|||
|
### 章节头部
|
|||
|
- 简洁的"课程章节"标题
|
|||
|
- 右侧排序按钮带图标
|
|||
|
- 符合图片设计标准
|
|||
|
|
|||
|
现在课程详情页面具备完整的报名状态管理功能!🎉
|