OL-LearnPlatform-Frontend/src/views/TeacherDetail.vue

1071 lines
27 KiB
Vue
Raw Normal View History

2025-08-10 22:42:56 +08:00
<template>
<div class="courses-page">
<!-- 页面头部横幅 -->
<div class="page-header">
<div class="header-content">
<h1 class="page-title">粉丝</h1>
<p class="page-subtitle">7832</p>
</div>
<div class="header-content">
<h1 class="page-title">课程</h1>
<p class="page-subtitle">12</p>
</div>
<div class="header-content">
<h1 class="page-title">播放量</h1>
<p class="page-subtitle">12.3</p>
</div>
</div>
<div class="title-lecturer">
<img src="/images/Teachers/师资力量1.png" alt="">
<h3>汪波</h3>
<p>云南师范大学教授平台签约讲师</p>
2025-08-12 16:58:22 +08:00
<button :class="{ 'disabled': isFollowing }" @click="toggleFollow">
{{ isFollowing ? '已关注' : '+ 关注' }}
</button>
2025-08-10 22:42:56 +08:00
</div>
<!-- 主要内容区域 -->
<div class="main-content">
<div class="container">
<!-- 课程导航 -->
<div class="text-wrapper_8 flex-row">
<span class="text_24">全部课程</span>
<div class="sort-options">
<span class="text_25" :class="{ 'active': sortType === 'hot' }" @click="selectSortType('hot')">最热</span>
<span class="text_26" :class="{ 'active': sortType === 'new' }" @click="selectSortType('new')">最新</span>
</div>
</div>
<!-- 加载状态 -->
<div class="loading-state" v-if="loading">
<div class="loading-content">
<p>正在加载课程...</p>
</div>
</div>
<!-- 课程网格 -->
<div class="courses-grid" v-else-if="allCourses.length > 0">
<div class="course-card" v-for="course in allCourses" :key="course.id">
<div class="course-image">
<img :src="course.thumbnail" :alt="course.title" />
</div>
<div class="course-info">
<h3 class="course-title">{{ getCourseTitle(course) }}</h3>
<div class="course-meta">
<div class="course-chapters">
<img src="/images/Teachers/chapter.png" alt="课程章节" class="meta-icon">
<span>共9章54节</span>
</div>
<div class="course-duration">
<img src="/images/Teachers/duration.png" alt="课程时长" class="meta-icon">
<span>12小时43分钟</span>
</div>
</div>
<div class="course-footer">
<div class="course-stats">
<span class="course-students">讲师: 刘莹</span>
</div>
<button class="enroll-btn" @click="goToCourseDetail(course)">去学习</button>
</div>
</div>
</div>
</div>
<!-- 空状态 -->
<div class="empty-state" v-else>
<div class="empty-content">
<p>暂无符合条件的课程</p>
<button class="clear-filters-btn" @click="clearAllFilters">清除筛选条件</button>
</div>
</div>
<!-- 分页组件 -->
<div class="pagination">
<span class="pagination-info">{{ currentPageText }} / {{ totalPages }} ({{ totalItems }}条数据)</span>
<span class="pagination-link prev" @click="goToPrevPage" :class="{ disabled: currentPage === 1 }">上一页</span>
<!-- 第一页 -->
<button v-if="totalPages > 0" class="pagination-btn page-num" :class="{ active: currentPage === 1 }"
@click="goToPage(1)">1</button>
<!-- 左侧省略号 -->
<span v-if="currentPage > 4" class="pagination-dots">...</span>
<!-- 当前页附近的页码 -->
<template v-for="page in visiblePages" :key="page">
<button v-if="page !== 1 && page !== totalPages" class="pagination-btn page-num"
:class="{ active: currentPage === page }" @click="goToPage(page)">{{ page }}</button>
</template>
<!-- 右侧省略号 -->
<span v-if="currentPage < totalPages - 3" class="pagination-dots">...</span>
<!-- 最后一页 -->
<button v-if="totalPages > 1" class="pagination-btn page-num" :class="{ active: currentPage === totalPages }"
@click="goToPage(totalPages)">{{ totalPages }}</button>
<span class="pagination-link next" @click="goToNextPage"
:class="{ disabled: currentPage === totalPages }">下一页</span>
<span class="pagination-link last" @click="goToPage(totalPages)">尾页</span>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
2025-08-12 16:58:22 +08:00
// 关注状态
const isFollowing = ref(false)
// 切换关注状态
const toggleFollow = () => {
isFollowing.value = !isFollowing.value
}
2025-08-10 22:42:56 +08:00
import { useRouter } from 'vue-router'
import type { Course } from '@/api/types'
import { CourseApi } from '@/api'
import { useUserStore } from '@/stores/user'
// import { mockCourses } from '@/data/mockCourses'
2025-08-10 22:42:56 +08:00
const router = useRouter()
const userStore = useUserStore()
2025-08-10 22:42:56 +08:00
// 课程数据和加载状态
const courses = ref<Course[]>([])
const loading = ref(false)
const total = ref(0)
// 筛选和排序状态
const selectedSubject = ref('全部')
const selectedMajor = ref('全部')
const selectedDifficulty = ref('全部')
const sortType = ref('all') // all: 全部课程, hot: 最热, new: 最新
// 分页相关状态
const currentPage = ref(1)
const itemsPerPage = 20
const totalItems = computed(() => total.value)
const totalPages = computed(() => Math.ceil(totalItems.value / itemsPerPage))
2025-08-11 10:03:56 +08:00
// 已移除未使用的广告显隐逻辑,避免构建时报未使用错误
2025-08-10 22:42:56 +08:00
// 数字转中文
const numberToChinese = (num: number): string => {
const chineseNumbers = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十']
if (num <= 10) {
return chineseNumbers[num]
} else if (num < 20) {
return '十' + chineseNumbers[num - 10]
} else if (num < 100) {
const tens = Math.floor(num / 10)
const ones = num % 10
return chineseNumbers[tens] + '十' + (ones > 0 ? chineseNumbers[ones] : '')
}
return num.toString()
}
// 当前页面文本
const currentPageText = computed(() => {
if (currentPage.value === 1) {
return '首页'
} else {
return `${numberToChinese(currentPage.value)}`
}
})
// 可见的页码范围
const visiblePages = computed(() => {
const pages = []
const current = currentPage.value
const total = totalPages.value
// 显示当前页前后2页
const start = Math.max(2, current - 2)
const end = Math.min(total - 1, current + 2)
for (let i = start; i <= end; i++) {
pages.push(i)
}
return pages
})
// 加载课程数据(使用模拟数据)
const loadCourses = async () => {
try {
loading.value = true
// 模拟网络延迟
await new Promise(resolve => setTimeout(resolve, 500))
// 筛选逻辑
2025-08-19 19:04:11 +08:00
let filteredCourses: Course[] = [
{
id: '1',
title: '教育心理学基础课程',
description: '本课程深入讲解教育心理学的基本理论和实践应用,帮助学生理解学习过程中的心理机制。',
thumbnail: '/images/courses/course1.png',
price: 0,
currency: 'CNY',
rating: 4.8,
ratingCount: 125,
studentsCount: 1250,
duration: '12小时43分钟',
totalLessons: 54,
level: 'beginner',
language: 'zh-CN',
category: { id: 1, name: '教育培训', slug: 'education-training' },
tags: ['心理学', '教育', '基础'],
skills: ['心理分析', '教育理论'],
requirements: ['无特殊要求'],
objectives: ['掌握教育心理学基础理论'],
instructor: {
id: 1,
name: '汪波',
avatar: '/images/Teachers/师资力量1.png',
title: '云南师范大学教授',
bio: '资深教育心理学专家',
rating: 4.8,
studentsCount: 1250,
coursesCount: 6,
experience: '15年',
education: ['云南师范大学', '教育心理学博士'],
certifications: ['高级心理咨询师', '教育技术专家']
},
status: 'published',
createdAt: '2024-01-15T10:00:00Z',
updatedAt: '2024-01-15T10:00:00Z'
},
{
id: '2',
title: '现代教育技术应用',
description: '探索现代教育技术在课堂教学中的应用,包括多媒体教学、在线教育平台等。',
thumbnail: '/images/courses/course2.png',
price: 0,
currency: 'CNY',
rating: 4.6,
ratingCount: 89,
studentsCount: 890,
duration: '10小时20分钟',
totalLessons: 42,
level: 'intermediate',
language: 'zh-CN',
category: { id: 2, name: '技术应用', slug: 'tech-application' },
tags: ['教育技术', '多媒体', '在线教育'],
skills: ['多媒体制作', '在线教学'],
requirements: ['基础计算机操作'],
objectives: ['掌握现代教育技术应用'],
instructor: {
id: 1,
name: '汪波',
avatar: '/images/Teachers/师资力量1.png',
title: '云南师范大学教授',
bio: '资深教育技术专家',
rating: 4.6,
studentsCount: 890,
coursesCount: 6,
experience: '15年',
education: ['云南师范大学', '教育技术博士'],
certifications: ['高级教育技术专家', '多媒体制作师']
},
status: 'published',
createdAt: '2024-02-20T14:30:00Z',
updatedAt: '2024-02-20T14:30:00Z'
},
{
id: '3',
title: '课程设计与开发',
description: '学习如何设计和开发高质量的课程内容,包括教学目标制定、教学内容组织等。',
thumbnail: '/images/courses/course3.png',
price: 0,
currency: 'CNY',
rating: 4.7,
ratingCount: 67,
studentsCount: 567,
duration: '15小时30分钟',
totalLessons: 68,
level: 'advanced',
language: 'zh-CN',
category: { id: 3, name: '课程设计', slug: 'course-design' },
tags: ['课程设计', '教学开发', '教育'],
skills: ['课程规划', '教学设计'],
requirements: ['教育理论基础'],
objectives: ['掌握课程设计方法'],
instructor: {
id: 1,
name: '汪波',
avatar: '/images/Teachers/师资力量1.png',
title: '云南师范大学教授',
2025-08-19 19:47:12 +08:00
bio: '课程设计专家',
rating: 4.7,
studentsCount: 567,
coursesCount: 6,
experience: '15年',
education: ['云南师范大学', '课程设计博士'],
certifications: ['高级课程设计师', '教育专家']
2025-08-19 19:04:11 +08:00
},
status: 'published',
createdAt: '2024-03-10T09:15:00Z',
updatedAt: '2024-03-10T09:15:00Z'
},
{
id: '4',
title: '教育研究方法论',
description: '系统介绍教育研究的基本方法和技巧,培养学生进行教育研究的能力。',
thumbnail: '/images/courses/course4.png',
price: 0,
currency: 'CNY',
rating: 4.5,
ratingCount: 43,
studentsCount: 432,
duration: '8小时15分钟',
totalLessons: 36,
level: 'intermediate',
language: 'zh-CN',
category: { id: 4, name: '研究方法', slug: 'research-methods' },
tags: ['研究方法', '教育', '学术'],
skills: ['研究设计', '数据分析'],
requirements: ['统计学基础'],
objectives: ['掌握教育研究方法'],
instructor: {
id: 1,
name: '汪波',
avatar: '/images/Teachers/师资力量1.png',
title: '云南师范大学教授',
2025-08-19 19:47:12 +08:00
bio: '教育研究专家',
rating: 4.5,
studentsCount: 432,
coursesCount: 6,
experience: '15年',
education: ['云南师范大学', '教育研究博士'],
certifications: ['高级教育研究专家', '学术顾问']
2025-08-19 19:04:11 +08:00
},
status: 'published',
createdAt: '2024-01-25T16:45:00Z',
updatedAt: '2024-01-25T16:45:00Z'
},
{
id: '5',
title: '学生心理辅导技巧',
description: '学习如何对学生进行心理辅导,掌握基本的心理咨询和辅导技巧。',
thumbnail: '/images/courses/course5.png',
price: 0,
currency: 'CNY',
rating: 4.9,
ratingCount: 78,
studentsCount: 678,
duration: '6小时45分钟',
totalLessons: 28,
level: 'beginner',
language: 'zh-CN',
category: { id: 5, name: '心理辅导', slug: 'psychological-counseling' },
tags: ['心理辅导', '学生', '技巧'],
skills: ['心理咨询', '沟通技巧'],
requirements: ['心理学基础'],
objectives: ['掌握心理辅导技巧'],
instructor: {
id: 1,
name: '汪波',
avatar: '/images/Teachers/师资力量1.png',
title: '云南师范大学教授',
2025-08-19 19:47:12 +08:00
bio: '心理咨询专家',
rating: 4.9,
studentsCount: 678,
coursesCount: 6,
experience: '15年',
education: ['云南师范大学', '心理学博士'],
certifications: ['高级心理咨询师', '心理治疗师']
2025-08-19 19:04:11 +08:00
},
status: 'published',
createdAt: '2024-02-05T11:20:00Z',
updatedAt: '2024-02-05T11:20:00Z'
},
{
id: '6',
title: '教育评估与测量',
description: '学习教育评估的基本理论和方法,掌握教育测量的技术和工具。',
thumbnail: '/images/courses/course5.png',
price: 0,
currency: 'CNY',
rating: 4.4,
ratingCount: 34,
studentsCount: 345,
duration: '9小时20分钟',
totalLessons: 40,
level: 'advanced',
language: 'zh-CN',
category: { id: 6, name: '教育评估', slug: 'education-assessment' },
tags: ['教育评估', '测量', '技术'],
skills: ['评估设计', '测量技术'],
requirements: ['教育统计学'],
objectives: ['掌握教育评估方法'],
instructor: {
id: 1,
name: '汪波',
avatar: '/images/Teachers/师资力量1.png',
title: '云南师范大学教授',
2025-08-19 19:47:12 +08:00
bio: '教育评估专家',
rating: 4.4,
studentsCount: 345,
coursesCount: 6,
experience: '15年',
education: ['云南师范大学', '教育评估博士'],
certifications: ['高级教育评估师', '测量技术专家']
2025-08-19 19:04:11 +08:00
},
status: 'published',
createdAt: '2024-03-15T13:10:00Z',
updatedAt: '2024-03-15T13:10:00Z'
}
] // 使用模拟数据后续可以从API获取
2025-08-10 22:42:56 +08:00
// 按学科筛选
if (selectedSubject.value !== '全部') {
filteredCourses = filteredCourses.filter(course => {
switch (selectedSubject.value) {
case '计算机':
return course.category.name === '编程开发' || course.category.name === '前端开发' || course.category.name === '后端开发'
case '教育学':
return course.category.name === '教育培训'
default:
return true
}
})
}
// 排序逻辑
if (sortType.value === 'hot') {
// 按热度排序假设课程有studentsCount字段表示学生数量
filteredCourses.sort((a, b) => {
const studentsA = a.studentsCount || 0;
const studentsB = b.studentsCount || 0;
return studentsB - studentsA;
});
} else if (sortType.value === 'new') {
// 按最新排序假设课程有createdAt字段表示创建时间
filteredCourses.sort((a, b) => {
const dateA = new Date(a.createdAt || 0);
const dateB = new Date(b.createdAt || 0);
return dateB.getTime() - dateA.getTime();
});
} else {
// 默认排序按课程ID
filteredCourses.sort((a, b) => a.id.localeCompare(b.id));
2025-08-10 22:42:56 +08:00
}
// 按专业筛选
if (selectedMajor.value !== '全部') {
filteredCourses = filteredCourses.filter(course =>
course.title.includes(selectedMajor.value) ||
course.description.includes(selectedMajor.value) ||
course.tags?.some((tag: string) => tag.includes(selectedMajor.value))
2025-08-10 22:42:56 +08:00
)
}
// 按难度筛选
if (selectedDifficulty.value !== '全部') {
const difficultyMap: { [key: string]: string } = {
'初级': 'beginner',
'中级': 'intermediate',
'高级': 'advanced'
}
const targetLevel = difficultyMap[selectedDifficulty.value]
if (targetLevel) {
filteredCourses = filteredCourses.filter(course => course.level === targetLevel)
}
}
// 分页处理
total.value = filteredCourses.length
const startIndex = (currentPage.value - 1) * itemsPerPage
const endIndex = startIndex + itemsPerPage
courses.value = filteredCourses.slice(startIndex, endIndex)
console.log('课程加载成功:', courses.value.length, '条课程,总计:', total.value)
} catch (error) {
console.error('加载课程失败:', error)
courses.value = []
total.value = 0
} finally {
loading.value = false
}
}
// 根据学科获取分类ID这里需要根据实际后端分类来映射
// const getCategoryIdBySubject = (subject: string): number | undefined => {
// const categoryMap: Record<string, number> = {
// '必修课': 1,
// '高分课': 2,
// '名师课堂': 3,
// '训练营': 4,
// '无考试': 5,
// '专题讲座': 6
// }
// return categoryMap[subject]
// }
// 跳转到指定页面
const goToPage = (page: number) => {
if (page >= 1 && page <= totalPages.value) {
currentPage.value = page
loadCourses()
}
}
// 上一页
const goToPrevPage = () => {
if (currentPage.value > 1) {
goToPage(currentPage.value - 1)
}
}
// 下一页
const goToNextPage = () => {
if (currentPage.value < totalPages.value) {
goToPage(currentPage.value + 1)
}
}
// 清除所有筛选条件
const clearAllFilters = () => {
selectedSubject.value = '全部'
selectedMajor.value = '全部'
selectedDifficulty.value = '全部'
currentPage.value = 1
loadCourses()
}
2025-08-11 10:03:56 +08:00
// 已移除未使用的筛选函数,避免构建时报未使用错误
2025-08-10 22:42:56 +08:00
// 排序功能
const selectSortType = (type: string) => {
sortType.value = type
currentPage.value = 1 // 重置到第一页
loadCourses()
}
// 当前页显示的课程数据直接使用从API获取的数据
const allCourses = computed(() => {
return courses.value
})
// 获取课程标题的函数
const getCourseTitle = (course: Course) => {
return course.title
}
// 跳转到课程详情页
const goToCourseDetail = async (course: Course) => {
try {
// 检查用户是否已登录
if (!userStore.isLoggedIn) {
console.log('用户未登录跳转到AI伴学页面')
router.push(`/ai-companion?courseId=${course.id}`)
return
}
console.log('检查课程报名状态课程ID:', course.id)
// 调用报名状态检查接口
const response = await CourseApi.checkEnrollmentStatus(String(course.id))
if ((response.code === 0 || response.code === 200) && response.data) {
const isEnrolled = response.data.result
if (isEnrolled) {
// 已报名,跳转到已兑换页面
console.log('用户已报名,跳转到已兑换页面')
router.push(`/course/${course.id}/exchanged`)
} else {
// 未报名跳转到AI伴学页面
console.log('用户未报名跳转到AI伴学页面')
router.push(`/ai-companion?courseId=${course.id}`)
}
} else {
// 查询失败默认跳转到AI伴学页面
console.warn('查询报名状态失败跳转到AI伴学页面')
router.push(`/ai-companion?courseId=${course.id}`)
}
} catch (error) {
console.error('检查报名状态时发生错误:', error)
// 发生错误时默认跳转到AI伴学页面
router.push(`/ai-companion?courseId=${course.id}`)
}
2025-08-10 22:42:56 +08:00
}
// 组件挂载时加载数据
onMounted(() => {
loadCourses()
})
</script>
<style scoped>
.courses-page {
min-height: 100vh;
background: #fff;
}
.page-header {
background-image: url('/images/Teachers/teacher-bg.png');
background-size: cover;
background-position: center;
text-align: center;
color: #000;
position: relative;
overflow: hidden;
display: flex;
justify-content: flex-end;
align-items: center;
padding-right: 320px;
height: 165px;
}
.page-header::before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 300px;
height: 100%;
background: url('/images/header-decoration.png') no-repeat center right;
background-size: contain;
opacity: 0.3;
}
.header-content {
height: 40px;
padding: 0 20px;
position: relative;
z-index: 1;
border-right: 1px solid #D8D8D8;
}
/* 最后一个 */
.header-content:last-child {
border-right: none;
}
.page-title {
font-size: 16px;
margin: -10px 0 5px 0;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.page-subtitle {
font-size: 24px;
opacity: 0.9;
margin-bottom: 35px;
font-weight: 700;
}
.main-content {
padding: 40px 0;
}
.container {
max-width: 1420px;
margin: 0 auto;
padding: 0 0;
box-sizing: border-box;
}
.text-wrapper_8 {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.flex-row {
flex-direction: row;
}
.sort-options {
display: flex;
justify-content: flex-end;
}
.text_24 {
font-size: 24px;
color: #333;
margin-right: 0;
}
.text_25,
.text_26 {
margin-left: 30px;
cursor: pointer;
font-size: 14px;
padding-bottom: 5px;
position: relative;
color: #666;
}
.text_25.active,
.text_26.active {
color: #1890ff;
font-weight: 500;
}
.text_25.active::after,
.text_26.active::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 2px;
background-color: #1890ff;
}
.text_25,
.text_26 {
color: #666;
}
.text_25:hover,
.text_26:hover {
color: #1890ff;
}
.sort-tab.active {
color: #1890ff;
border-bottom-color: #1890ff;
font-weight: 500;
}
.courses-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 20px;
margin-bottom: 40px;
width: 100%;
box-sizing: border-box;
}
.course-card {
background: white;
border-radius: 2px;
overflow: hidden;
transition: transform 0.2s, box-shadow 0.2s;
cursor: pointer;
2025-08-19 19:04:11 +08:00
/* min-height: 350px; */
2025-08-14 17:32:28 +08:00
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
2025-08-10 22:42:56 +08:00
}
.course-image {
width: 100%;
height: 200px;
overflow: hidden;
border-radius: 5px 5px 0 0;
}
.course-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.course-info {
padding: 16px;
}
.course-title {
font-size: 16px;
font-weight: 500;
color: #333;
margin: 0 0 12px 0;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.course-meta {
display: flex;
justify-content: left;
gap: 10px;
font-size: 12px;
color: #999;
margin-bottom: 16px;
}
.course-duration {
color: #666;
}
.course-price {
color: #ff4d4f;
font-weight: 500;
}
.course-stats {
display: flex;
align-items: center;
}
.course-students {
color: #999;
font-size: 14px;
}
.course-footer {
display: flex;
justify-content: space-between;
align-items: center;
}
.course-chapters,
.course-duration {
display: flex;
align-items: center;
font-size: 14px;
color: #666;
}
.meta-icon {
width: 16px;
height: 16px;
margin-right: 4px;
vertical-align: middle;
}
.course-teacher {
font-size: 12px;
color: #999;
margin-bottom: 12px;
}
.course-tags {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.course-tag {
background: transparent;
color: #999;
padding: 0;
border-radius: 0;
font-size: 12px;
white-space: nowrap;
}
.enroll-btn {
background: #0286D5;
color: white;
border: none;
padding: 6px 10px;
border-radius: 20px;
cursor: pointer;
font-size: 12px;
transition: background 0.2s;
white-space: nowrap;
}
.enroll-btn:hover {
background: #40a9ff;
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
padding: 20px 0;
margin-top: 40px;
}
.pagination-info {
color: #666;
font-size: 14px;
margin-right: 20px;
}
.pagination-btn {
padding: 8px 12px;
border: 1px solid #d9d9d9;
background: white;
color: #666;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
min-width: 40px;
text-align: center;
margin: 0 6px;
}
.pagination-btn:hover {
border-color: #1890ff;
color: #1890ff;
}
.pagination-btn.active {
background: #1890ff;
border-color: #1890ff;
color: white;
}
/* 移除了 .pagination-btn.last 样式,因为现在尾页是文本链接 */
.pagination-btn:disabled {
background: #f5f5f5;
color: #ccc;
border-color: #e6e6e6;
cursor: not-allowed;
}
.pagination-btn:disabled:hover {
background: #f5f5f5;
color: #ccc;
border-color: #e6e6e6;
}
.pagination-link {
color: #666;
font-size: 14px;
cursor: pointer;
padding: 8px 12px;
margin: 0 6px;
transition: color 0.2s;
user-select: none;
}
.pagination-link:hover {
color: #1890ff;
}
.pagination-link.disabled {
color: #ccc;
cursor: not-allowed;
}
.pagination-link.disabled:hover {
color: #ccc;
}
.pagination-dots {
color: #999;
padding: 0 8px;
font-size: 14px;
margin: 0 6px;
}
.stats-grid {
display: flex;
align-items: center;
width: 100%;
margin: 0 auto;
padding: 0 247px;
}
.stat-item {
flex: 1;
display: flex;
align-items: center;
gap: 20px;
padding: 0 45px 0 25px;
}
.stat-item:last-child {
border-right: none;
}
.stat-item:last-child {
border-right: none;
}
.line {
width: 1px;
height: 50px;
background: #D0E8F5;
}
.stat-icon {
flex-shrink: 0;
width: 74px;
height: 74px;
display: flex;
align-items: center;
justify-content: center;
}
.stat-icon img {
width: 74px;
height: 74px;
object-fit: contain;
display: block;
}
.title-lecturer {
position: relative;
text-align: center;
}
.title-lecturer img {
position: absolute;
left: 50%;
top: -32%;
transform: translateX(-50%);
width: 128px;
height: 128px;
border-radius: 50%;
}
.title-lecturer h3 {
padding-top: 80px;
font-size: 24px;
font-weight: 600;
}
.title-lecturer p {
font-size: 14px;
color: #999;
margin-bottom: 20px;
}
.title-lecturer button {
width: 96px;
height: 40px;
background: #0088D1;
border: none;
border-radius: 5px;
font-size: 16px;
color: white;
}
.title-lecturer .disabled {
border: 1px solid #0088D1;
background-color: #E2F5FF;
color: #0088D1;
}
@media (max-width: 768px) {
.container {
margin: 0 20px;
}
}
@media (max-width: 480px) {
.courses-grid {
grid-template-columns: 1fr;
}
.container {
margin: 0 16px;
}
.filter-section {
padding: 16px;
}
}
</style>