OL-LearnPlatform-Frontend/src/views/LearningPaths.vue
2025-08-10 22:49:43 +08:00

900 lines
22 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="learning-paths-page">
<!-- 页面标题区域 -->
<div class="page-header">
<div class="container">
<div class="header-content">
<h1 class="page-title">学习路径</h1>
<p class="page-subtitle">从易到难精编24类学习路径</p>
</div>
<div class="header-decoration">
<!-- 装饰图片暂时隐藏 -->
</div>
</div>
</div>
<!-- 主要内容区域 -->
<div class="main-content">
<div class="container">
<div class="content-layout">
<!-- 左侧筛选栏 -->
<div class="filter-sidebar">
<!-- 所有路径标题 - 无背景样式 -->
<h3 class="filter-title">所有路径</h3>
<div class="filter-section">
<div class="filter-options">
<div class="filter-option active" :class="{ active: selectedCategory === 'psychology' }"
@click="selectCategory('psychology')">
<img
:src="selectedCategory === 'psychology' ? '/public/images/Learning-paths/path-icon-active.png' : '/public/images/Learning-paths/path-icon.png'"
class="option-icon" />
<span class="option-text">数据心理学</span>
</div>
<div class="filter-option" :class="{ active: selectedCategory === 'ai' }" @click="selectCategory('ai')">
<img
:src="selectedCategory === 'ai' ? '/public/images/Learning-paths/path-icon-active.png' : '/public/images/Learning-paths/path-icon.png'"
class="option-icon" />
<span class="option-text">AI 时代生存指南从工程师到架构师...</span>
</div>
<div class="filter-option" :class="{ active: selectedCategory === 'english1' }"
@click="selectCategory('english1')">
<img
:src="selectedCategory === 'english1' ? '/public/images/Learning-paths/path-icon-active.png' : '/public/images/Learning-paths/path-icon.png'"
class="option-icon" />
<span class="option-text">新视野英语训练营</span>
</div>
<div class="filter-option" :class="{ active: selectedCategory === 'english2' }"
@click="selectCategory('english2')">
<img
:src="selectedCategory === 'english2' ? '/public/images/Learning-paths/path-icon-active.png' : '/public/images/Learning-paths/path-icon.png'"
class="option-icon" />
<span class="option-text">新视野英语训练营</span>
</div>
<div class="filter-option" :class="{ active: selectedCategory === 'learning' }"
@click="selectCategory('learning')">
<img
:src="selectedCategory === 'learning' ? '/public/images/Learning-paths/path-icon-active.png' : '/public/images/Learning-paths/path-icon.png'"
class="option-icon" />
<span class="option-text">科学学习方法 | 强化记忆</span>
</div>
<div class="filter-option" :class="{ active: selectedCategory === 'health' }"
@click="selectCategory('health')">
<img
:src="selectedCategory === 'health' ? '/public/images/Learning-paths/path-icon-active.png' : '/public/images/Learning-paths/path-icon.png'"
class="option-icon" />
<span class="option-text">运动与健康</span>
</div>
<div class="filter-option" :class="{ active: selectedCategory === 'education' }"
@click="selectCategory('education')">
<img
:src="selectedCategory === 'education' ? '/public/images/Learning-paths/path-icon-active.png' : '/public/images/Learning-paths/path-icon.png'"
class="option-icon" />
<span class="option-text">数师教育学科技能体系</span>
</div>
</div>
</div>
</div>
<!-- 右侧内容区域 -->
<div>
<!-- 课程标题 - 无背景样式 -->
<h3 class="content-title filter-title">课程</h3>
<div class="content-main">
<!-- 统计信息和筛选行 -->
<div class="content-header">
<div class="result-info">
<div class="stats-container">
<div class="stat-item">
<span class="stat-label"></span>
<span class="stat-number">34</span>
<span class="stat-label"></span>
</div>
<div class="stat-divider">|</div>
<div class="stat-item">
<span class="stat-number">567</span>
<span class="stat-label">课程</span>
</div>
<div class="stat-divider">|</div>
<div class="stat-item">
<span class="stat-number">1243</span>
<span class="stat-label">小时</span>
</div>
</div>
</div>
<div class="sort-options">
<span class="sort-label">难度等级</span>
<div class="custom-select">
<select v-model="sortBy" class="sort-select">
<option value="default">全部</option>
<option value="beginner">初级</option>
<option value="intermediate">中级</option>
<option value="advanced">高级</option>
</select>
<div class="select-arrow"></div>
</div>
</div>
</div>
<!-- 学习路径列表 -->
<div class="paths-list">
<div class="path-card" v-for="path in paginatedPaths" :key="path.id" @click="goToPath(path.id)">
<div class="card-image">
<div class="image-placeholder"></div>
<div class="card-badge">
<img src="/public/images/auth/stage.png" alt="阶段标识" class="badge-icon">
<span>{{ path.badge || '初阶' }}</span>
</div>
</div>
<div class="card-content">
<div class="title-container">
<h3 class="card-title">{{ path.title }}</h3>
<span class="student-count">{{ path.studentsCount }}</span>
</div>
<div class="card-meta">
<span class="meta-item">讲师:</span>
<span class="meta-item">代方枚</span>
<span class="meta-item">史雯</span>
</div>
<p class="card-description">{{ path.description }}</p>
<div class="card-footer">
<div class="card-stats">
<div class="stat-item">
<img src="/public/images/auth/chapter.png" alt="章节" class="stat-icon">
<span>共9章节54节</span>
</div>
<div class="stat-item">
<img src="/public/images/auth/watch.png" alt="时间" class="stat-icon">
<span>12小时43分钟</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div class="pagination">
<button class="page-btn no-border" :disabled="currentPage === 1" @click="goToPage(1)">
首页
</button>
<button class="page-btn no-border" :disabled="currentPage === 1" @click="goToPage(currentPage - 1)">
上一页
</button>
<template v-if="visiblePages[0] > 1">
<button class="page-btn">1</button>
<button class="page-btn ellipsis" disabled>...</button>
</template>
<button class="page-btn" :class="{ active: page === currentPage }" v-for="page in visiblePages"
:key="page" @click="goToPage(page)">
{{ page }}
</button>
<template v-if="visiblePages[visiblePages.length - 1] < totalPages">
<button class="page-btn ellipsis" disabled>...</button>
<button class="page-btn">{{ totalPages }}</button>
</template>
<button class="page-btn no-border" :disabled="currentPage === totalPages" @click="goToPage(currentPage + 1)">
下一页
</button>
<button class="page-btn no-border" :disabled="currentPage === totalPages" @click="goToPage(totalPages)">
尾页
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
// 筛选状态
const selectedCategory = ref('psychology')
const sortBy = ref('default')
// 分页状态
const currentPage = ref(1)
const pageSize = 8
// 学习路径数据
const learningPaths = ref([
{
id: 1,
title: '数据心理学的起源',
description: '本课程深度解析,让您一次性掌握数据分析的心理学基础。适合希望从心理学角度理解数据的学习者。这是一门综合性的课程,涵盖了数据分析的各个方面。通过本课程的学习,您将能够掌握数据分析的核心技能,并能够在实际工作中应用这些技能。',
image: 'https://images.unsplash.com/photo-1635070041078-e363dbe005cb?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
category: 'psychology',
level: '数学基础',
duration: '免费',
studentsCount: 1234,
lessonsCount: 24,
badge: '热门'
},
{
id: 2,
title: '数据心理学的发展历程',
description: '本课程深度解析,让您一次性掌握数据分析的心理学基础。适合希望从心理学角度理解数据的学习者。这是一门综合性的课程,涵盖了数据分析的各个方面。通过本课程的学习,您将能够掌握数据分析的核心技能。',
image: 'https://images.unsplash.com/photo-1509228468518-180dd4864904?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
category: 'psychology',
level: '数学基础',
duration: '免费',
studentsCount: 856,
lessonsCount: 30,
badge: '热门'
},
{
id: 3,
title: '研究情境性环境的影响',
description: '本课程深度解析,让您一次性掌握数据分析的心理学基础。适合希望从心理学角度理解数据的学习者。这是一门综合性的课程,涵盖了数据分析的各个方面。通过本课程的学习,您将能够掌握数据分析的核心技能。',
image: 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
category: 'psychology',
level: '数学基础',
duration: '免费',
studentsCount: 678,
lessonsCount: 36,
badge: '热门'
},
{
id: 4,
title: '研究学习环境设计和有效教学模式',
description: '本课程深度解析,让您一次性掌握数据分析的心理学基础。适合希望从心理学角度理解数据的学习者。这是一门综合性的课程,涵盖了数据分析的各个方面。通过本课程的学习,您将能够掌握数据分析的核心技能。',
image: 'https://images.unsplash.com/photo-1551650975-87deedd944c3?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
category: 'psychology',
level: '数学基础',
duration: '免费',
studentsCount: 945,
lessonsCount: 30,
badge: '热门'
},
{
id: 5,
title: '数据心理学的起源',
description: '本课程深度解析,让您一次性掌握数据分析的心理学基础。适合希望从心理学角度理解数据的学习者。这是一门综合性的课程,涵盖了数据分析的各个方面。通过本课程的学习,您将能够掌握数据分析的核心技能。',
image: 'https://images.unsplash.com/photo-1596495578065-6e0763fa1178?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&q=80',
category: 'psychology',
level: '数学基础',
duration: '免费',
studentsCount: 1567,
lessonsCount: 18,
badge: '热门'
},
])
// 计算属性
const filteredPaths = computed(() => {
let filtered = learningPaths.value
if (selectedCategory.value) {
filtered = filtered.filter(path => path.category === selectedCategory.value)
}
return filtered
})
const totalPages = computed(() => Math.ceil(filteredPaths.value.length / pageSize))
const paginatedPaths = computed(() => {
const start = (currentPage.value - 1) * pageSize
const end = start + pageSize
return filteredPaths.value.slice(start, end)
})
const visiblePages = computed(() => {
const pages = []
const total = totalPages.value
const current = currentPage.value
for (let i = Math.max(1, current - 2); i <= Math.min(total, current + 2); i++) {
pages.push(i)
}
return pages
})
// 方法
const selectCategory = (category: string) => {
selectedCategory.value = category
currentPage.value = 1
}
const goToPage = (page: number) => {
currentPage.value = page
}
const goToPath = (pathId: number) => {
router.push(`/learning-path/${pathId}`)
}
onMounted(() => {
// 页面加载时的初始化逻辑
console.log('学习路径页面加载完成')
})
</script>
<style scoped>
.learning-paths-page {
min-height: 100vh;
background-color: #f8f9fa;
}
/* 页面标题区域 */
.page-header {
position: relative;
overflow: hidden;
background-image: url('/images/Learning-paths/learning-paths-bg.png');
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.page-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(45deg, rgba(255, 255, 255, 0.1) 25%, transparent 25%),
linear-gradient(-45deg, rgba(255, 255, 255, 0.1) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, rgba(255, 255, 255, 0.1) 75%),
linear-gradient(-45deg, transparent 75%, rgba(255, 255, 255, 0.1) 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
opacity: 0.1;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
position: relative;
}
.header-content {
text-align: center;
color: #000;
}
.page-title {
font-size: 28px;
font-weight: 700;
margin: 40px 0 0 0;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.page-subtitle {
font-size: 14px;
font-weight: 400;
margin-bottom: 38px;
opacity: 0.9;
}
.header-decoration {
position: absolute;
right: 50px;
top: 50%;
transform: translateY(-50%);
}
.decoration-image {
width: 200px;
height: auto;
opacity: 0.8;
}
/* 主要内容区域 */
.main-content {
padding: 40px 0 80px;
background-color: rgb(246, 246, 246);
}
.content-layout {
display: flex;
gap: 30px;
align-items: flex-start;
}
/* 左侧筛选栏 */
.filter-sidebar {
width: 280px;
flex-shrink: 0;
}
.filter-section {
background: white;
border-radius: 8px;
padding: 10px 20px;
margin-bottom: 0;
box-shadow: none;
}
.filter-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin: 0 0 20px 0;
padding: 0;
/* 所有路径标题直接显示在灰色背景上,无背景样式 */
}
.filter-options {
display: flex;
flex-direction: column;
gap: 0;
}
.filter-option {
display: flex;
align-items: center;
padding: 10px 0;
border-radius: 0;
cursor: pointer;
transition: all 0.2s;
border: none;
border-bottom: 1px solid #e9ecef;
background: transparent;
}
.filter-option:last-child {
border-bottom: none;
}
.filter-option:hover {
background-color: transparent;
}
.filter-option.active .option-text {
color: #1976d2;
font-weight: 500;
}
.filter-option.active .blue-icon {
color: #1976d2;
}
.option-icon {
display: inline-block;
margin-right: 8px;
width: 16px;
height: 16px;
object-fit: contain;
flex-shrink: 0;
}
/* 移除不再需要的样式 */
.gray-icon {
color: #999;
}
.option-text {
font-size: 14px;
font-weight: 400;
color: #666;
line-height: 1.4;
word-wrap: break-word;
word-break: break-word;
flex: 1;
}
/* 右侧内容区域 */
.content-main {
flex: 1;
background-color: #fff;
padding: 20px 30px;
border-radius: 5px;
}
.content-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin: 0 0 20px 0;
padding: 0;
/* 课程标题直接显示在灰色背景上,无背景样式 */
}
.content-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
padding: 12px 16px;
width: 100%;
border-bottom: 1px solid #F4F4F4;
}
.result-info {
color: #333;
font-size: 14px;
}
.stats-container {
display: flex;
align-items: center;
}
.stat-item {
display: flex;
align-items: center;
margin-right: 12px;
}
.stat-number {
font-size: 16px;
font-weight: 600;
color: #2196f3;
margin-right: 4px;
}
.stat-label {
margin-right: 4px;
font-size: 14px;
color: #666;
}
.stat-divider {
color: #ddd;
margin: 0 6px;
}
.sort-options {
display: flex;
align-items: center;
gap: 10px;
}
.sort-label {
font-size: 14px;
color: #666;
font-weight: 500;
}
.custom-select {
position: relative;
}
.sort-select {
padding: 6px 32px 6px 12px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 14px;
background: white;
cursor: pointer;
color: #333;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
}
.select-arrow {
position: absolute;
top: 50%;
right: 12px;
transform: translateY(-50%);
color: #666;
pointer-events: none;
font-size: 10px;
}
/* 学习路径列表 */
.paths-list {
display: flex;
flex-direction: column;
gap: 16px;
margin-bottom: 40px;
}
.path-card {
background: white;
border-radius: 8px;
overflow: hidden;
transition: all 0.3s;
cursor: pointer;
display: flex;
min-height: 140px;
}
.path-card:hover {
transform: translateY(-3px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);
}
.card-image {
position: relative;
width: 240px;
flex-shrink: 0;
background: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
overflow: hidden;
}
.image-placeholder {
width: 100%;
height: 100%;
background: #e0e0e0;
display: flex;
align-items: center;
justify-content: center;
}
.card-badge {
position: absolute;
top: 8px;
right: 8px;
background: #6C6C6C;
color: white;
padding: 2px 4px;
border-radius: 3px;
font-size: 12px;
font-weight: 500;
display: flex;
align-items: center;
gap: 4px;
}
.badge-icon {
width: 14px;
height: 14px;
object-fit: contain;
}
.card-content {
padding: 16px 20px;
flex: 1;
display: flex;
flex-direction: column;
}
.title-container {
display: flex;
justify-content: space-between;
align-items: center;
margin: 0 0 8px 0;
}
.card-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin: 0;
line-height: 1.3;
}
.student-count {
font-size: 14px;
color: #999;
display: flex;
align-items: center;
}
.student-count:before {
content: '';
display: inline-block;
width: 16px;
height: 16px;
background-image: url('/public/images/auth/number.png');
background-size: contain;
background-repeat: no-repeat;
margin-right: 5px;
}
.card-meta {
display: flex;
gap: 8px;
margin-bottom: 10px;
}
.meta-item {
font-size: 12px;
color: #B4B4B4;
padding: 3px 0;
border-radius: 12px;
}
.card-description {
font-size: 14px;
color: #B4B4B4;
line-height: 1.5;
margin: 0 0 16px 0;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
flex: 1;
}
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: auto;
padding-top: 10px;
}
.card-stats {
display: flex;
gap: 16px;
}
.stat-item {
font-size: 13px;
color: #999;
display: flex;
align-items: center;
gap: 5px;
}
.stat-icon {
width: 16px;
height: 16px;
object-fit: contain;
vertical-align: middle;
}
.card-rating {
display: flex;
align-items: center;
gap: 4px;
}
.rating-icon {
font-size: 14px;
}
.rating-text {
font-size: 12px;
color: #666;
font-weight: 500;
}
/* 分页样式 */
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 4px;
margin-top: 40px;
}
.page-btn {
padding: 6px 12px;
border: 1px solid #ddd;
background: white;
color: #666;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
min-width: 36px;
}
.page-btn.ellipsis {
border: none;
cursor: default;
}
.page-btn.no-border {
border: none;
}
.page-btn.active {
background-color: #1e88e5;
color: white;
border-color: #1e88e5;
font-weight: 600;
}
.page-btn:hover:not(:disabled) {
border-color: #1e88e5;
color: #1e88e5;
background-color: #f5f9ff;
}
.page-btn.active {
background: #2196f3;
border-color: #2196f3;
color: white;
}
.page-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* 响应式设计 */
@media (max-width: 1024px) {
.content-layout {
flex-direction: column;
}
.filter-sidebar {
width: 100%;
}
.filter-options {
flex-direction: row;
flex-wrap: wrap;
gap: 12px;
}
.filter-option {
flex: 0 0 auto;
}
}
@media (max-width: 768px) {
.page-header {
padding: 40px 0;
}
.page-title {
font-size: 32px;
}
.page-subtitle {
font-size: 16px;
}
.header-decoration {
display: none;
}
.paths-list {
grid-template-columns: 1fr;
gap: 16px;
}
.content-header {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.sort-options {
align-self: flex-end;
}
}
@media (max-width: 480px) {
.container {
padding: 0 16px;
}
.main-content {
padding: 20px 0 40px;
}
.filter-section {
padding: 16px;
}
.card-content {
padding: 16px;
}
.pagination {
gap: 4px;
}
.page-btn {
padding: 6px 12px;
font-size: 12px;
min-width: 32px;
}
}
</style>