929 lines
20 KiB
Vue
929 lines
20 KiB
Vue
<template>
|
||
<div>
|
||
<!-- 课程筛选标签 -->
|
||
<div class="text-wrapper_1 flex-row">
|
||
<span
|
||
class="text_12"
|
||
:class="{ active: activeCourseTab === 'all' }"
|
||
@click="handleCourseTabChange('all')"
|
||
>全部课程</span
|
||
>
|
||
<span
|
||
class="text_13"
|
||
:class="{ active: activeCourseTab === 'learning' }"
|
||
@click="handleCourseTabChange('learning')"
|
||
>学习中</span
|
||
>
|
||
<span
|
||
class="text_14"
|
||
:class="{ active: activeCourseTab === 'completed' }"
|
||
@click="handleCourseTabChange('completed')"
|
||
>已完结</span
|
||
>
|
||
</div>
|
||
|
||
<!-- 分割线 -->
|
||
<div class="course-divider"></div>
|
||
|
||
<!-- 课程列表 -->
|
||
<div class="course-list">
|
||
<!-- 加载状态 -->
|
||
<div v-if="loading" class="loading-wrapper">
|
||
<n-spin size="medium" />
|
||
</div>
|
||
<!-- 课程卡片 -->
|
||
<div
|
||
v-else-if="filteredCourses.length > 0"
|
||
v-for="course in filteredCourses"
|
||
:key="course.id"
|
||
class="box_2 flex-row justify-between"
|
||
>
|
||
<div class="block_4 flex-col">
|
||
<div class="box_3 flex-row justify-between">
|
||
<div class="status-image-container">
|
||
<img
|
||
class="status-image"
|
||
referrerpolicy="no-referrer"
|
||
:src="
|
||
getCourseStatusClass(course) === 'learning'
|
||
? '/images/icon/learning.png'
|
||
: '/images/icon/finish.png'
|
||
"
|
||
:alt="getStatusText(course)"
|
||
/>
|
||
</div>
|
||
<img
|
||
class="thumbnail_4"
|
||
referrerpolicy="no-referrer"
|
||
:src="course.thumbnail"
|
||
:alt="course.name"
|
||
/>
|
||
<span :class="['status-text', getCourseStatusClass(course)]">{{
|
||
getStatusText(course)
|
||
}}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="block_5 flex-col">
|
||
<div class="group_6 flex-row">
|
||
<span class="text_16">{{ course.title }}</span>
|
||
|
||
<n-icon size="1.04vw" class="thumbnail_5">
|
||
<PersonOutline />
|
||
</n-icon>
|
||
<span class="text_17">{{ course.enrollmentCount || 0 }}</span>
|
||
</div>
|
||
<span class="text_18"
|
||
>讲师:{{
|
||
getInstructorNames(course.teacherList) || "暂无讲师"
|
||
}}</span
|
||
>
|
||
<span class="text_19">{{
|
||
formatDescription(course.description)
|
||
}}</span>
|
||
|
||
<div class="group_7 flex-row">
|
||
<img
|
||
class="thumbnail_6"
|
||
referrerpolicy="no-referrer"
|
||
src="/images/profile/11.png"
|
||
/>
|
||
<span class="text_20"
|
||
>共{{ course.chapterCount || 0 }}章{{
|
||
course.sectionCount || 0
|
||
}}节</span
|
||
>
|
||
<img
|
||
class="thumbnail_7"
|
||
referrerpolicy="no-referrer"
|
||
src="/images/profile/22.png"
|
||
/>
|
||
<span class="text_21"
|
||
>{{
|
||
course.startDate
|
||
? new Date(course.startDate).toLocaleDateString()
|
||
: "时间待定"
|
||
}}
|
||
-
|
||
{{
|
||
course.endDate
|
||
? new Date(course.endDate).toLocaleDateString()
|
||
: "时间待定"
|
||
}}</span
|
||
>
|
||
<!-- <img
|
||
class="thumbnail_8"
|
||
referrerpolicy="no-referrer"
|
||
src="/images/profile/33.png"
|
||
/>
|
||
<span class="text_22">{{ course.enrollmentCount }}</span> -->
|
||
<div
|
||
class="text-wrapper_2 flex-col"
|
||
@click="goToCourseDetail(course)"
|
||
>
|
||
<span class="text_23">{{
|
||
getCourseStatusClass(course) === "learning"
|
||
? "去学习"
|
||
: getCourseStatusClass(course) === "completed"
|
||
? "去复习"
|
||
: "去报名"
|
||
}}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- 空状态 -->
|
||
<div v-else class="empty-state">
|
||
<n-empty description="暂无课程数据"> </n-empty>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 分页器 -->
|
||
<div class="pagination-wrapper" v-if="totalPages > 1">
|
||
<div class="pagination">
|
||
<span
|
||
class="pagination-item nav-button"
|
||
:class="{ disabled: currentPage === 1 }"
|
||
@click="goToPage('first')"
|
||
>
|
||
首页
|
||
</span>
|
||
<span
|
||
class="pagination-item nav-button"
|
||
:class="{ disabled: currentPage === 1 }"
|
||
@click="goToPage('prev')"
|
||
>
|
||
上一页
|
||
</span>
|
||
|
||
<span
|
||
v-for="page in totalPages"
|
||
:key="page"
|
||
class="pagination-item page-number"
|
||
:class="{ active: page === currentPage }"
|
||
@click="goToPage(page)"
|
||
>
|
||
{{ page }}
|
||
</span>
|
||
|
||
<span
|
||
class="pagination-item nav-button"
|
||
:class="{ disabled: currentPage === totalPages }"
|
||
@click="goToPage('next')"
|
||
>
|
||
下一页
|
||
</span>
|
||
<span
|
||
class="pagination-item nav-button"
|
||
:class="{ disabled: currentPage === totalPages }"
|
||
@click="goToPage('last')"
|
||
>
|
||
尾页
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed, onMounted } from "vue";
|
||
// import { useI18n } from "vue-i18n";
|
||
import { useMessage } from "naive-ui";
|
||
import { useRouter } from "vue-router";
|
||
import { PersonOutline } from "@vicons/ionicons5";
|
||
|
||
import { CourseApi } from "@/api/modules/course";
|
||
import { TeachCourseApi } from "@/api/modules/teachCourse";
|
||
import { useUserStore } from "@/stores/user";
|
||
|
||
const userStore = useUserStore();
|
||
const router = useRouter();
|
||
|
||
// const { locale } = useI18n();
|
||
const message = useMessage();
|
||
|
||
// 定义章节接口
|
||
interface Section {
|
||
id: string;
|
||
name: string;
|
||
level: number; // 1为章,2为节
|
||
parentId?: string;
|
||
courseId: string;
|
||
sortOrder?: number;
|
||
}
|
||
|
||
// 定义教师接口
|
||
interface Teacher {
|
||
id: string;
|
||
name: string;
|
||
avatar: string;
|
||
title: string;
|
||
tag: string;
|
||
sortOrder: number;
|
||
}
|
||
|
||
// 定义课程接口
|
||
interface Course {
|
||
id: string;
|
||
name: string;
|
||
cover: string;
|
||
video: string;
|
||
school: string;
|
||
description: string;
|
||
type: number;
|
||
target: string;
|
||
difficulty: number;
|
||
subject: string;
|
||
outline: string;
|
||
prerequisite: string;
|
||
reference: string;
|
||
arrangement: string;
|
||
startTime: string;
|
||
endTime: string;
|
||
enrollCount: number;
|
||
maxEnroll: number;
|
||
status: number;
|
||
question: string;
|
||
izAi: number;
|
||
pauseExit: number;
|
||
allowSpeed: number;
|
||
showSubtitle: number;
|
||
publishStatus: number;
|
||
semester: string | null;
|
||
allowDownload: number;
|
||
createBy: string;
|
||
createTime: string;
|
||
updateBy: string;
|
||
updateTime: string;
|
||
teacherList: Teacher[];
|
||
isEnrolled: boolean;
|
||
// 新增章节信息
|
||
chapterCount?: number; // 章数
|
||
sectionCount?: number; // 节数
|
||
sections?: Section[]; // 章节列表
|
||
}
|
||
|
||
// 课程筛选状态
|
||
const activeCourseTab = ref("all");
|
||
|
||
// 分页相关状态
|
||
const currentPage = ref(1);
|
||
const pageSize = ref(10); // 每页显示5个课程
|
||
|
||
// 课程数据状态
|
||
const courses = ref<any[]>([]);
|
||
const loading = ref(false);
|
||
const total = ref(0);
|
||
|
||
// banner图片
|
||
// const bannerImage = computed(() => {
|
||
// return locale.value === "zh"
|
||
// ? "/banners/banner8.png"
|
||
// : "/banners/banner1-en.png";
|
||
// });
|
||
|
||
// const bannerAlt = computed(() => {
|
||
// return t('home.banner.alt')
|
||
// })
|
||
|
||
// 获取课程章节信息
|
||
const getCourseSections = async (courseId: string) => {
|
||
try {
|
||
const response = await TeachCourseApi.getCourseSections(courseId);
|
||
return response.data.result || [];
|
||
} catch (error) {
|
||
console.error(`获取课程${courseId}章节信息失败:`, error);
|
||
return [];
|
||
}
|
||
};
|
||
|
||
// 计算章节数量
|
||
const calculateChapterAndSectionCount = (sections: any) => {
|
||
const chapterCount = sections.filter(
|
||
(section: any) => section.level === 1
|
||
).length;
|
||
const sectionCount = sections.filter(
|
||
(section: any) => section.level === 2
|
||
).length;
|
||
return { chapterCount, sectionCount };
|
||
};
|
||
// 获取课程数据(一次性获取所有数据)
|
||
const fetchCourses = async () => {
|
||
try {
|
||
loading.value = true;
|
||
// 移除分页参数,一次性获取所有课程
|
||
const response = await CourseApi.getCourses({}, true);
|
||
console.log("Fetched courses:", response.data);
|
||
|
||
if (response.data && Array.isArray(response.data)) {
|
||
// 为每个课程获取章节信息
|
||
const coursesWithSections = await Promise.all(
|
||
response.data.map(async (course: any) => {
|
||
// 获取章节信息
|
||
const sections = await getCourseSections(course.id);
|
||
const { chapterCount, sectionCount } =
|
||
calculateChapterAndSectionCount(sections);
|
||
|
||
return {
|
||
...course,
|
||
sections,
|
||
chapterCount,
|
||
sectionCount,
|
||
};
|
||
})
|
||
);
|
||
|
||
courses.value = coursesWithSections;
|
||
total.value = coursesWithSections.length;
|
||
console.log("Courses with sections:", coursesWithSections);
|
||
} else {
|
||
courses.value = [];
|
||
total.value = 0;
|
||
}
|
||
} catch (error) {
|
||
message.error("获取课程数据失败,请稍后重试。");
|
||
console.error("Error fetching courses:", error);
|
||
courses.value = [];
|
||
total.value = 0;
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
};
|
||
|
||
// 获取筛选后的所有课程
|
||
const allFilteredCourses = computed(() => {
|
||
if (activeCourseTab.value === "learning") {
|
||
// 筛选学习中的课程
|
||
return courses.value.filter(
|
||
(course) => getCourseStatusClass(course) === "learning"
|
||
);
|
||
} else if (activeCourseTab.value === "completed") {
|
||
// 筛选已完结的课程(时间区间已结束)
|
||
return courses.value.filter(
|
||
(course) => getCourseStatusClass(course) === "completed"
|
||
);
|
||
}
|
||
return courses.value;
|
||
});
|
||
|
||
// 计算总页数(基于筛选后的数据)
|
||
const totalPages = computed(() => {
|
||
return Math.ceil(allFilteredCourses.value.length / pageSize.value);
|
||
});
|
||
|
||
// 当前页显示的课程(前端分页)
|
||
const filteredCourses = computed(() => {
|
||
const start = (currentPage.value - 1) * pageSize.value;
|
||
const end = start + pageSize.value;
|
||
return allFilteredCourses.value.slice(start, end);
|
||
});
|
||
|
||
// 获取状态文本
|
||
const getStatusText = (course: Course) => {
|
||
const now = new Date();
|
||
const endTime = new Date(course.endTime);
|
||
|
||
if (course.isEnrolled) {
|
||
if (endTime < now) {
|
||
return "已完结";
|
||
} else {
|
||
return "学习中";
|
||
}
|
||
}
|
||
return "未报名";
|
||
};
|
||
|
||
// 获取课程状态类名
|
||
const getCourseStatusClass = (course: Course) => {
|
||
const now = new Date();
|
||
const endTime = new Date(course.endTime);
|
||
|
||
if (course.isEnrolled) {
|
||
if (endTime < now) {
|
||
return "completed";
|
||
} else {
|
||
return "learning";
|
||
}
|
||
}
|
||
return "not-enrolled";
|
||
};
|
||
|
||
// 获取教师名称
|
||
const getInstructorNames = (teacherList: Teacher[]) => {
|
||
return teacherList.map((teacher) => teacher.name).join(" ");
|
||
};
|
||
|
||
// 格式化课程描述(移除HTML标签)
|
||
const formatDescription = (description: string) => {
|
||
return description.replace(/<[^>]*>/g, "").substring(0, 200) + "...";
|
||
};
|
||
|
||
// 跳转到课程详情页
|
||
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}`);
|
||
}
|
||
};
|
||
|
||
// 监听筛选变化,重置到第一页(不重新调用接口)
|
||
const handleCourseTabChange = (tab: string) => {
|
||
activeCourseTab.value = tab;
|
||
currentPage.value = 1; // 切换tab时重置到第一页
|
||
};
|
||
|
||
// 分页器方法(前端分页,不调用API)
|
||
const goToPage = (page: number | string) => {
|
||
let newPage = currentPage.value;
|
||
|
||
if (typeof page === "number") {
|
||
if (page >= 1 && page <= totalPages.value) {
|
||
newPage = page;
|
||
}
|
||
} else {
|
||
switch (page) {
|
||
case "first":
|
||
if (currentPage.value > 1) {
|
||
newPage = 1;
|
||
}
|
||
break;
|
||
case "prev":
|
||
if (currentPage.value > 1) {
|
||
newPage = currentPage.value - 1;
|
||
}
|
||
break;
|
||
case "next":
|
||
if (currentPage.value < totalPages.value) {
|
||
newPage = currentPage.value + 1;
|
||
}
|
||
break;
|
||
case "last":
|
||
if (currentPage.value < totalPages.value) {
|
||
newPage = totalPages.value;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (newPage !== currentPage.value) {
|
||
currentPage.value = newPage;
|
||
// 不再调用fetchCourses(),纯前端分页
|
||
}
|
||
};
|
||
|
||
onMounted(async () => {
|
||
await fetchCourses();
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.text-wrapper_1 {
|
||
display: flex;
|
||
gap: 2.08vw;
|
||
padding-bottom: 1.04vh;
|
||
}
|
||
|
||
.text_12,
|
||
.text_13,
|
||
.text_14,
|
||
.text_15 {
|
||
font-size: 0.94vw;
|
||
color: #000;
|
||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||
font-weight: normal;
|
||
cursor: pointer;
|
||
padding: 0.42vh 0;
|
||
transition: color 0.3s ease;
|
||
border-radius: 0.31vw;
|
||
}
|
||
|
||
.text_12.active,
|
||
.text_13.active,
|
||
.text_14.active,
|
||
.text_15.active {
|
||
color: rgba(2, 134, 206, 1);
|
||
}
|
||
|
||
.text_12:hover,
|
||
.text_13:hover,
|
||
.text_14:hover,
|
||
.text_15:hover {
|
||
color: rgba(2, 134, 206, 1);
|
||
}
|
||
|
||
.course-divider {
|
||
width: 100%;
|
||
height: 1.5px;
|
||
background: #e6e6e6;
|
||
margin-bottom: 1.67vh;
|
||
}
|
||
|
||
.course-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1.25vh;
|
||
}
|
||
|
||
.box_2 {
|
||
width: 100%;
|
||
min-height: 10.42vh;
|
||
background: rgba(255, 255, 255, 1);
|
||
border: none;
|
||
border-radius: 0.6vw;
|
||
padding: 1.04vh 1.04vw;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.box_2:hover {
|
||
box-shadow: 0 0.21vh 1.04vh rgba(0, 0, 0, 0.1);
|
||
transform: translateY(-0.1vh);
|
||
}
|
||
|
||
.block_4 {
|
||
margin-right: 1.04vw;
|
||
}
|
||
|
||
.box_3 {
|
||
width: 202px;
|
||
height: 156px;
|
||
position: relative;
|
||
border-radius: 5px;
|
||
overflow: hidden;
|
||
background: rgba(243, 243, 243, 1);
|
||
box-shadow: 0 0.1vh 0.31vh rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.thumbnail_4 {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
border-radius: 5px;
|
||
}
|
||
|
||
.status-image-container {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 10;
|
||
}
|
||
|
||
.status-image {
|
||
width: 66px;
|
||
height: 22px;
|
||
}
|
||
|
||
.status-text {
|
||
position: absolute;
|
||
top: 26vh;
|
||
right: 0.63vw;
|
||
color: #fff;
|
||
font-size: 0.6vw;
|
||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||
font-weight: normal;
|
||
z-index: 12;
|
||
padding: 0.21vh 0.52vw;
|
||
border-radius: 0.21vw;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
}
|
||
|
||
.status-text.learning {
|
||
background: rgba(2, 134, 206, 0.8);
|
||
}
|
||
|
||
.status-text.completed {
|
||
background: rgba(76, 175, 80, 0.8);
|
||
}
|
||
|
||
.status-text.not-enrolled {
|
||
background: rgba(158, 158, 158, 0.8);
|
||
}
|
||
|
||
.block_5 {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.group_6 {
|
||
align-items: center;
|
||
margin-bottom: 0.42vh;
|
||
}
|
||
|
||
.text_16 {
|
||
font-size: 1.04vw;
|
||
font-weight: bold;
|
||
color: #333;
|
||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||
flex: 1;
|
||
margin-right: 0.52vw;
|
||
}
|
||
|
||
.thumbnail_5 {
|
||
width: 1.04vw;
|
||
height: 1.04vw;
|
||
margin-right: 0.26vw;
|
||
}
|
||
|
||
.text_17 {
|
||
font-size: 0.78vw;
|
||
color: #666;
|
||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||
}
|
||
|
||
.text_18 {
|
||
font-size: 0.78vw;
|
||
color: #999;
|
||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||
margin-bottom: 0.42vh;
|
||
}
|
||
|
||
.text_19 {
|
||
font-size: 0.73vw;
|
||
color: #666;
|
||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||
margin-bottom: 0.63vh;
|
||
line-height: 1.4;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
flex: 1;
|
||
}
|
||
|
||
.group_7 {
|
||
align-items: center;
|
||
gap: 0.52vw;
|
||
}
|
||
|
||
.thumbnail_6,
|
||
.thumbnail_7,
|
||
.thumbnail_8 {
|
||
width: 0.83vw;
|
||
height: 0.83vw;
|
||
}
|
||
|
||
.text_20,
|
||
.text_21,
|
||
.text_22 {
|
||
font-size: 0.68vw;
|
||
color: #999;
|
||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||
}
|
||
|
||
.text-wrapper_2 {
|
||
margin-left: auto;
|
||
background: rgba(2, 134, 206, 1);
|
||
border-radius: 0.31vw;
|
||
padding: 0.31vh 0.78vw;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.text-wrapper_2:hover {
|
||
background: rgba(1, 120, 185, 1);
|
||
}
|
||
|
||
.text_23 {
|
||
font-size: 0.73vw;
|
||
color: #fff;
|
||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||
font-weight: normal;
|
||
}
|
||
|
||
.pagination-wrapper {
|
||
width: 100%;
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-top: 4.17vh;
|
||
padding: 1.04vh 0;
|
||
}
|
||
|
||
.pagination {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.52vw;
|
||
}
|
||
|
||
.pagination-item {
|
||
padding: 0.52vh 0.78vw;
|
||
font-size: 0.73vw;
|
||
color: #333;
|
||
cursor: pointer;
|
||
border-radius: 0.26vw;
|
||
transition: all 0.3s ease;
|
||
border: 1px solid #ddd;
|
||
background: #fff;
|
||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||
}
|
||
|
||
.pagination-item:hover {
|
||
color: rgba(2, 134, 206, 1);
|
||
border-color: rgba(2, 134, 206, 1);
|
||
}
|
||
|
||
.pagination-item.active {
|
||
background: rgba(2, 134, 206, 1);
|
||
color: #fff;
|
||
border-color: rgba(2, 134, 206, 1);
|
||
}
|
||
|
||
.pagination-item.disabled {
|
||
color: #ccc;
|
||
cursor: not-allowed;
|
||
border-color: #f0f0f0;
|
||
}
|
||
|
||
.pagination-item.disabled:hover {
|
||
color: #ccc;
|
||
border-color: #f0f0f0;
|
||
}
|
||
|
||
.nav-button {
|
||
padding: 0.52vh 1.04vw;
|
||
}
|
||
|
||
.page-number {
|
||
min-width: 2.08vw;
|
||
text-align: center;
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 1024px) {
|
||
.box_2 {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.block_4 {
|
||
width: 100%;
|
||
margin-right: 0;
|
||
margin-bottom: 0.83vh;
|
||
}
|
||
|
||
.box_3 {
|
||
width: 100%;
|
||
height: 18vh;
|
||
}
|
||
|
||
.text_16 {
|
||
font-size: 1.25vw;
|
||
}
|
||
|
||
.text_17,
|
||
.text_18,
|
||
.text_19 {
|
||
font-size: 1vw;
|
||
}
|
||
|
||
.text_20,
|
||
.text_21,
|
||
.text_22,
|
||
.text_23 {
|
||
font-size: 0.9vw;
|
||
}
|
||
|
||
.thumbnail_5,
|
||
.thumbnail_6,
|
||
.thumbnail_7,
|
||
.thumbnail_8 {
|
||
width: 1.25vw;
|
||
height: 1.25vw;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.text_12,
|
||
.text_13,
|
||
.text_14 {
|
||
font-size: 2.16px;
|
||
padding: 10px 0;
|
||
}
|
||
|
||
.course-divider {
|
||
width: 100%;
|
||
}
|
||
|
||
.pagination-wrapper {
|
||
margin-top: 6vh;
|
||
}
|
||
|
||
.pagination {
|
||
gap: 2vw;
|
||
}
|
||
|
||
.pagination-item {
|
||
font-size: 3vw;
|
||
padding: 1vh 2vw;
|
||
}
|
||
|
||
.nav-button {
|
||
padding: 1vh 3vw;
|
||
}
|
||
|
||
.page-number {
|
||
min-width: 8vw;
|
||
}
|
||
|
||
.box_2 {
|
||
padding: 0.83vh 0.83vw;
|
||
}
|
||
|
||
.block_4 {
|
||
margin-bottom: 0.63vh;
|
||
}
|
||
|
||
.box_3 {
|
||
height: 20vh;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.text_12,
|
||
.text_13,
|
||
.text_14 {
|
||
font-size: 0.83vw;
|
||
padding: 0.31vh 0.63vw;
|
||
}
|
||
|
||
.box_2 {
|
||
padding: 0.63vh 0.63vw;
|
||
}
|
||
|
||
.text_16 {
|
||
font-size: 0.83vw;
|
||
}
|
||
|
||
.text_19 {
|
||
font-size: 0.73vw;
|
||
}
|
||
}
|
||
|
||
/* 通用样式类 */
|
||
.flex-row {
|
||
display: flex;
|
||
flex-direction: row;
|
||
}
|
||
|
||
.flex-col {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.justify-between {
|
||
justify-content: space-between;
|
||
}
|
||
|
||
/* 加载状态样式 */
|
||
.loading-wrapper {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 2vh 0;
|
||
font-size: 0.83vw;
|
||
color: #666;
|
||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||
}
|
||
|
||
/* 空状态样式 */
|
||
.empty-state {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 4vh 0;
|
||
font-size: 0.94vw;
|
||
color: #999;
|
||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||
}
|
||
</style>
|