|
|
@@ -0,0 +1,659 @@
|
|
|
+<template>
|
|
|
+ <!-- 新增固定背景层 -->
|
|
|
+ <div class="fixed-grid-bg"></div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ style="height: calc(100vh - 80px); color: #71bbdb; margin-bottom: 0"
|
|
|
+ class="content-wrapper"
|
|
|
+ >
|
|
|
+ <PhotoChange></PhotoChange>
|
|
|
+ <div class="good-course-title">
|
|
|
+ <el-row class="dynamic-header">
|
|
|
+ <!-- 背景渐变层 -->
|
|
|
+ <div class="header-backdrop"></div>
|
|
|
+
|
|
|
+ <!-- 主标题 -->
|
|
|
+ <el-col :span="24" class="text-center">
|
|
|
+ <h2 class="gradient-heading">
|
|
|
+ <span class="gradient-text">SEE精选课程</span>
|
|
|
+ <div class="title-underline"></div>
|
|
|
+ </h2>
|
|
|
+
|
|
|
+ <!-- 动态副标题 -->
|
|
|
+ <div class="animated-subtitle">
|
|
|
+ <div class="subtitle-gradient">▼ 探索知识领域 ▼</div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="course-container">
|
|
|
+ <!-- 课程列表容器 使用 Element Plus 的布局组件 -->
|
|
|
+ <el-row :gutter="20" >
|
|
|
+ <!-- 循环渲染课程卡片 -->
|
|
|
+ <el-col
|
|
|
+ v-for="(course, index) in courses"
|
|
|
+ :key="index"
|
|
|
+ :xs="24"
|
|
|
+ :sm="12"
|
|
|
+ :lg="8"
|
|
|
+ >
|
|
|
+ <!-- 课程卡片容器 -->
|
|
|
+ <div
|
|
|
+ class="course-card"
|
|
|
+ :style="{
|
|
|
+ '--main-color': course.color,
|
|
|
+ '--secondary-color': course.secondaryColor
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <!-- 动态光效层 -->
|
|
|
+ <div class="hover-glow"></div>
|
|
|
+
|
|
|
+ <!-- 右上角科技图标 -->
|
|
|
+ <div class="tech-icon">
|
|
|
+ <!--<el-icon :size="30" :color="course.color">-->
|
|
|
+ <!--<component :is="course.icon" />-->
|
|
|
+ <!--</el-icon>-->
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 课程封面 -->
|
|
|
+ <div class="course-cover">
|
|
|
+ <div class="image-container">
|
|
|
+ <img
|
|
|
+ :src="course.cover"
|
|
|
+ alt="课程封面"
|
|
|
+ class="cover-image"
|
|
|
+ @load="handleImageLoad"
|
|
|
+ >
|
|
|
+ <div v-show="!isLoaded" class="image-slot">
|
|
|
+ Loading<span class="dot">...</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!--<el-image-->
|
|
|
+ <!-- :src="course.cover"-->
|
|
|
+ <!-- fit="cover"-->
|
|
|
+ <!-- class="cover-image"-->
|
|
|
+ <!-->-->
|
|
|
+ <!-- <template #placeholder>-->
|
|
|
+ <!-- <div class="image-slot">Loading<span class="dot">...</span></div>-->
|
|
|
+ <!-- </template>-->
|
|
|
+ <!--</el-image>-->
|
|
|
+ <div class="cover-overlay"></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 课程内容 -->
|
|
|
+ <div class="course-content">
|
|
|
+ <!-- 类别标签 -->
|
|
|
+ <div class="flex-between">
|
|
|
+ <el-tag
|
|
|
+ effect="dark"
|
|
|
+ :color="hexToRgba(course.color, 0.1)"
|
|
|
+ :style="{ color: course.color }"
|
|
|
+ >
|
|
|
+ {{ course.category }}
|
|
|
+ </el-tag>
|
|
|
+ <!-- 评分 -->
|
|
|
+ <div class="rating">
|
|
|
+ <el-rate
|
|
|
+ v-model="course.rating"
|
|
|
+ disabled
|
|
|
+ :colors="[course.color]"
|
|
|
+ />
|
|
|
+ <span class="rating-text">{{ course.rating.toFixed(1) }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 课程标题 -->
|
|
|
+ <h3 class="course-title">{{ course.title }}</h3>
|
|
|
+
|
|
|
+ <!-- 课程描述 -->
|
|
|
+ <p class="course-desc">{{ course.description }}</p>
|
|
|
+
|
|
|
+ <!-- 底部信息 -->
|
|
|
+ <div class="flex-between footer">
|
|
|
+ <!-- 讲师信息 -->
|
|
|
+ <div class="lecturer">
|
|
|
+ <el-avatar :size="36" :src="course.lecturer.avatar" />
|
|
|
+ <span class="lecturer-name">{{ course.lecturer.name }}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 开始学习按钮 -->
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ class="study-btn"
|
|
|
+ @click="showCourseDetail(course)"
|
|
|
+ >
|
|
|
+ 开始学习 →
|
|
|
+ <template #icon>
|
|
|
+ <el-icon><ArrowRight /></el-icon>
|
|
|
+ </template>
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+// import CarouselCard from "@/views/PortalPage/components/CarouselCard";
|
|
|
+import CourseCard from "@/components/CourseCard";
|
|
|
+import MyCourseCard from "@/views/PortalPage/components/MyCourseCard";
|
|
|
+import MyTestCard from "@/views/PortalPage/components/MyTestCard";
|
|
|
+import {ArrowRight, FolderOpened, Lock, MagicStick, SetUp, ShoppingCart} from "@element-plus/icons-vue";
|
|
|
+import TestCard from "@/components/TestCard";
|
|
|
+import PhotoChange from "@/views/PortalPage/components/PhotoChange";
|
|
|
+import {ref} from "vue";
|
|
|
+
|
|
|
+export default {
|
|
|
+ //数据
|
|
|
+ name: "PortalPage",
|
|
|
+ components: {
|
|
|
+ ArrowRight,
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ isExpanding: false,
|
|
|
+ activeTestPaneName: "eval",
|
|
|
+ exampleEvalTestList: [],
|
|
|
+ exampleCourseList: [],
|
|
|
+ showCourseList_current: 1,
|
|
|
+ exampleCoderTestList: [],
|
|
|
+ myCourseList: [],
|
|
|
+ myEvalExamList: [],
|
|
|
+ myCoderExamList: [],
|
|
|
+ courseRowHeight: 360,
|
|
|
+ testRowHeight: 300,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ isLogin() {
|
|
|
+ return this.$store.getters.isLogin;
|
|
|
+ },
|
|
|
+ isTeacher() {
|
|
|
+ return this.$store.getters.isTeacher;
|
|
|
+ },
|
|
|
+ isStudent() {
|
|
|
+ return this.$store.getters.isStudent;
|
|
|
+ },
|
|
|
+ isAdmin(){
|
|
|
+ return this.$store.state.user.role==="ADMIN";
|
|
|
+ },
|
|
|
+ myTestList() {
|
|
|
+ return []
|
|
|
+ .concat(this.myEvalExamList)
|
|
|
+ .concat(this.myCoderExamList)
|
|
|
+ .sort((a, b) => {
|
|
|
+ let a_time = a["startAt"] * 1000 || new Date(a.startTime).getTime();
|
|
|
+ let b_time = b["startAt"] * 1000 || new Date(b.startTime).getTime();
|
|
|
+ return a_time - b_time;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ showExampleCourseList() {
|
|
|
+ return this.exampleCourseList.slice((this.showCourseList_current - 1) * 6, this.showCourseList_current * 6);
|
|
|
+ },
|
|
|
+ showExampleCoderTestList() {
|
|
|
+ return this.exampleCoderTestList
|
|
|
+ // .filter(i => this.state(i) === "RUNNING")
|
|
|
+ .slice(0, 6);
|
|
|
+ },
|
|
|
+ showExampleEvalTestList() {
|
|
|
+ return this.exampleEvalTestList
|
|
|
+ // .filter(i => this.state(i) === "RUNNING")
|
|
|
+ .slice(0, 6);
|
|
|
+ },
|
|
|
+ },
|
|
|
+
|
|
|
+ //生命周期函数
|
|
|
+ created() {
|
|
|
+ window.addEventListener("scroll", this.handleResize, true);
|
|
|
+ },
|
|
|
+ beforeUnmount() {
|
|
|
+ window.removeEventListener("scroll", this.handleResize,);
|
|
|
+ },
|
|
|
+ async mounted() {
|
|
|
+ const that = this;
|
|
|
+
|
|
|
+ this.exampleCourseList = (await this.$apis.getExampleCourses()).data.data;
|
|
|
+ this.exampleEvalTestList = (await this.$apis.getRecommendEvalExams()).data.data;
|
|
|
+ this.exampleCoderTestList = (await this.$apis.getRecommendCoderExams({})).data.data;
|
|
|
+
|
|
|
+ if (this.isLogin && this.isStudent) {
|
|
|
+ const res = await this.$apis.getStudentCourses();
|
|
|
+ console.log(res);
|
|
|
+ this.myCourseList = (await this.$apis.getStudentCourses()).data.data;
|
|
|
+ this.myEvalExamList = (await this.$apis.getStudentEvalExams()).data.data.filter(i => this.state(i) === "RUNNING");
|
|
|
+ this.myCoderExamList = (await this.$apis.getStudentCoderExams({})).data.data.res.filter(i => this.state(i) === "RUNNING");
|
|
|
+ }
|
|
|
+
|
|
|
+ await this.$nextTick(() => that.handleResize());
|
|
|
+ },
|
|
|
+
|
|
|
+ //其他函数
|
|
|
+ methods: {
|
|
|
+ toAuth() {
|
|
|
+ this.$router.push("/auth");
|
|
|
+ },
|
|
|
+ toCourse() {
|
|
|
+ this.$router.push("/course");
|
|
|
+ },
|
|
|
+ toManage() {
|
|
|
+ this.$router.push("/manage");
|
|
|
+ },
|
|
|
+ toTest() {
|
|
|
+ this.$router.push("/test");
|
|
|
+ },
|
|
|
+ handleResize() {
|
|
|
+ if (this.$route.path !== "/portal") return;
|
|
|
+ let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
|
|
|
+ if (scrollTop > 100) {
|
|
|
+ this.courseRowHeight = 640;
|
|
|
+ this.isExpanding = true;
|
|
|
+ }
|
|
|
+ if (this.$refs.portalTestCardBodyMain) {
|
|
|
+ this.testRowHeight = Math.max(this.$refs.portalTestCardBodyMain.$el.clientHeight, 300);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ changeTestPane() {
|
|
|
+ const that = this;
|
|
|
+ this.$nextTick(() => that.handleResize());
|
|
|
+ },
|
|
|
+ state(testVO) {
|
|
|
+ const now = new Date();
|
|
|
+ if (testVO["examId"]) {
|
|
|
+ if (new Date(testVO.endTime) < now) {
|
|
|
+ return "FINISHED";
|
|
|
+ } else if (new Date(testVO.startTime) > now) {
|
|
|
+ return "NOT_STARTED";
|
|
|
+ } else {
|
|
|
+ return "RUNNING";
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (testVO.status === "CLOSED") {
|
|
|
+ return "CLOSED";
|
|
|
+ } else if (testVO["startAt"] * 1000 > now) {
|
|
|
+ return "NOT_STARTED";
|
|
|
+ } else if (testVO["endAt"] * 1000 < now) {
|
|
|
+ return "FINISHED";
|
|
|
+ } else {
|
|
|
+ return "RUNNING";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleCourseCurrentChange(index) {
|
|
|
+ this.showCourseList_current = index;
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+// 课程数据
|
|
|
+import {Monitor, TrendCharts} from "@element-plus/icons-vue";
|
|
|
+import {ElMessage} from "element-plus";
|
|
|
+import {onMounted} from "vue";
|
|
|
+import {useApis} from "../../api";
|
|
|
+
|
|
|
+const apis = useApis();
|
|
|
+
|
|
|
+const courses = ref([
|
|
|
+ {
|
|
|
+ title: "全栈工程师实战",
|
|
|
+ category: "🚩 编程开发",
|
|
|
+ color: "#f3a64a",
|
|
|
+ secondaryColor: "#71bbdb",
|
|
|
+ icon: MagicStick,
|
|
|
+ cover: "https://picsum.photos/800/450?random=1&coding,tech,neon",
|
|
|
+ rating: 4.9,
|
|
|
+ description: "Node.js + React 全栈开发技术栈,构建企业级应用,掌握前后端分离开发模式,学习数据库设计与优化。",
|
|
|
+ lecturer: {
|
|
|
+ name: "王涛导师",
|
|
|
+ avatar: "https://i.pravatar.cc/40?img=1",
|
|
|
+ bio: "资深全栈工程师,10年开发经验,专注于Web开发与架构设计。"
|
|
|
+ },
|
|
|
+ duration: "36小时",
|
|
|
+ lessons: 120,
|
|
|
+ level: "中级",
|
|
|
+ price: 299,
|
|
|
+ isFeatured: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "深度学习实战",
|
|
|
+ category: "🚀 人工智能",
|
|
|
+ color: "#71bbdb",
|
|
|
+ secondaryColor: "#50a879",
|
|
|
+ icon: Monitor,
|
|
|
+ cover: "https://picsum.photos/800/450?random=2&ai,tech,neon",
|
|
|
+ rating: 4.8,
|
|
|
+ description: "TensorFlow与PyTorch核心开发,构建智能推荐系统,学习神经网络、卷积神经网络和循环神经网络。",
|
|
|
+ lecturer: {
|
|
|
+ name: "李娜教授",
|
|
|
+ avatar: "https://i.pravatar.cc/40?img=2",
|
|
|
+ bio: "人工智能领域专家,专注于深度学习与计算机视觉。"
|
|
|
+ },
|
|
|
+ duration: "48小时",
|
|
|
+ lessons: 150,
|
|
|
+ level: "高级",
|
|
|
+ price: 399,
|
|
|
+ isFeatured: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "大数据分析",
|
|
|
+ category: "📊 数据科学",
|
|
|
+ color: "#50a879",
|
|
|
+ secondaryColor: "#f3a64a",
|
|
|
+ icon: TrendCharts,
|
|
|
+ cover: "https://picsum.photos/800/450?random=3&data,tech,neon",
|
|
|
+ rating: 4.7,
|
|
|
+ description: "使用Python进行数据清洗、分析和可视化,学习Pandas、NumPy、Matplotlib等工具,掌握大数据处理技巧。",
|
|
|
+ lecturer: {
|
|
|
+ name: "陈伟分析师",
|
|
|
+ avatar: "https://i.pravatar.cc/40?img=3",
|
|
|
+ bio: "数据科学专家,擅长数据分析与机器学习。"
|
|
|
+ },
|
|
|
+ duration: "30小时",
|
|
|
+ lessons: 100,
|
|
|
+ level: "中级",
|
|
|
+ price: 349,
|
|
|
+ isFeatured: false
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "前端开发进阶",
|
|
|
+ category: "🚩 编程开发",
|
|
|
+ color: "#f3a64a",
|
|
|
+ secondaryColor: "#71bbdb",
|
|
|
+ icon: MagicStick,
|
|
|
+ cover: "https://picsum.photos/800/450?random=4&frontend,tech,neon",
|
|
|
+ rating: 4.6,
|
|
|
+ description: "深入学习Vue.js和React框架,掌握组件化开发、状态管理和性能优化。",
|
|
|
+ lecturer: {
|
|
|
+ name: "张敏导师",
|
|
|
+ avatar: "https://i.pravatar.cc/40?img=4",
|
|
|
+ bio: "前端开发专家,专注于现代Web开发技术。"
|
|
|
+ },
|
|
|
+ duration: "24小时",
|
|
|
+ lessons: 80,
|
|
|
+ level: "中级",
|
|
|
+ price: 249,
|
|
|
+ isFeatured: false
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "机器学习入门",
|
|
|
+ category: "🚀 人工智能",
|
|
|
+ color: "#71bbdb",
|
|
|
+ secondaryColor: "#50a879",
|
|
|
+ icon: Monitor,
|
|
|
+ cover: "https://picsum.photos/800/450?random=5&ml,tech,neon",
|
|
|
+ rating: 4.5,
|
|
|
+ description: "从零开始学习机器学习基础,掌握常用算法和模型评估方法。",
|
|
|
+ lecturer: {
|
|
|
+ name: "刘强博士",
|
|
|
+ avatar: "https://i.pravatar.cc/40?img=5",
|
|
|
+ bio: "机器学习研究员,专注于算法研究与工程实践。"
|
|
|
+ },
|
|
|
+ duration: "20小时",
|
|
|
+ lessons: 60,
|
|
|
+ level: "初级",
|
|
|
+ price: 199,
|
|
|
+ isFeatured: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: "数据可视化",
|
|
|
+ category: "📊 数据科学",
|
|
|
+ color: "#50a879",
|
|
|
+ secondaryColor: "#f3a64a",
|
|
|
+ icon: TrendCharts,
|
|
|
+ cover: "https://picsum.photos/800/450?random=6&dataviz,tech,neon",
|
|
|
+ rating: 4.7,
|
|
|
+ description: "学习使用D3.js和ECharts创建交互式数据可视化图表,提升数据展示效果。",
|
|
|
+ lecturer: {
|
|
|
+ name: "赵琳分析师",
|
|
|
+ avatar: "https://i.pravatar.cc/40?img=6",
|
|
|
+ bio: "数据可视化专家,擅长将复杂数据转化为直观图表。"
|
|
|
+ },
|
|
|
+ duration: "18小时",
|
|
|
+ lessons: 50,
|
|
|
+ level: "初级",
|
|
|
+ price: 179,
|
|
|
+ isFeatured: false
|
|
|
+ }
|
|
|
+]);
|
|
|
+
|
|
|
+// 显示课程详情
|
|
|
+const showCourseDetail = (course) => {
|
|
|
+ // 使用 Element Plus 的消息框示例
|
|
|
+ ElMessage.success(`进入课程: ${course.title}`);
|
|
|
+};
|
|
|
+
|
|
|
+// 工具函数:HEX转RGBA
|
|
|
+const hexToRgba = (hex, alpha) => {
|
|
|
+ const r = parseInt(hex.slice(1, 3), 16);
|
|
|
+ const g = parseInt(hex.slice(3, 5), 16);
|
|
|
+ const b = parseInt(hex.slice(5, 7), 16);
|
|
|
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
|
+};
|
|
|
+
|
|
|
+let isLoaded = ref(false);
|
|
|
+
|
|
|
+const handleImageLoad = () => {
|
|
|
+ isLoaded.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+const fetchData = () => {
|
|
|
+ apis.getAllCourses()
|
|
|
+ .then(res => {
|
|
|
+ console.log("---------");
|
|
|
+ console.log(res);
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ fetchData();
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped src="./NewPortalPage.css"/>
|
|
|
+<style scoped>
|
|
|
+@import "https://unpkg.com/pattern.css";
|
|
|
+
|
|
|
+/* 新增固定背景样式 */
|
|
|
+.fixed-grid-bg {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100vw;
|
|
|
+ height: 100vh;
|
|
|
+ z-index: -1; /* 确保内容在上层 */
|
|
|
+ background-image:
|
|
|
+ linear-gradient(rgba(58, 55, 55, 0.3) 1px, transparent 1px),
|
|
|
+ linear-gradient(90deg, rgba(58, 55, 55, 0.3) 1px, transparent 1px);
|
|
|
+ background-size: 30px 30px; /* 网格尺寸 */
|
|
|
+ background-color: #f1f5f9;
|
|
|
+}
|
|
|
+
|
|
|
+/* 内容容器样式 */
|
|
|
+.content-wrapper {
|
|
|
+ position: relative;
|
|
|
+ z-index: 1; /* 确保内容在背景上层 */
|
|
|
+ background: rgba(255, 255, 255, 0); /* 半透明白色背景 */
|
|
|
+ /*backdrop-filter: blur(2px); 可选模糊效果 */
|
|
|
+}
|
|
|
+</style>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+/* 课程容器 */
|
|
|
+.course-container {
|
|
|
+ padding: 20px;
|
|
|
+ max-width: 1200px;
|
|
|
+ margin: 0 auto;
|
|
|
+}
|
|
|
+
|
|
|
+/* 课程卡片 */
|
|
|
+.course-card {
|
|
|
+ position: relative;
|
|
|
+ background: white;
|
|
|
+ border-radius: 1.5rem;
|
|
|
+ border: 2px solid rgba(var(--main-color), 0.1);
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ overflow: hidden;
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.course-card:hover {
|
|
|
+ border-color: rgba(var(--main-color), 0.3);
|
|
|
+ box-shadow: 0 10px 30px rgba(var(--main-color), 0.1);
|
|
|
+}
|
|
|
+
|
|
|
+/* 动态光效 */
|
|
|
+.hover-glow {
|
|
|
+ /* ...原有光效实现... */
|
|
|
+}
|
|
|
+
|
|
|
+/* 科技图标 */
|
|
|
+.tech-icon {
|
|
|
+ /* ...原有图标动画实现... */
|
|
|
+}
|
|
|
+
|
|
|
+/* 课程封面 */
|
|
|
+.course-cover {
|
|
|
+ position: relative;
|
|
|
+ height: 240px;
|
|
|
+ overflow: hidden;
|
|
|
+ border-radius: 1.5rem 1.5rem 0 0;
|
|
|
+}
|
|
|
+
|
|
|
+.cover-image {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ transition: transform 0.5s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.course-card:hover .cover-image {
|
|
|
+ transform: scale(1.1);
|
|
|
+}
|
|
|
+
|
|
|
+.cover-overlay {
|
|
|
+ /* ...原有渐变覆盖层实现... */
|
|
|
+}
|
|
|
+
|
|
|
+/* 课程内容 */
|
|
|
+.course-content {
|
|
|
+ padding: 1.5rem;
|
|
|
+}
|
|
|
+
|
|
|
+.flex-between {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+/* 课程标题 */
|
|
|
+.course-title {
|
|
|
+ font-size: 1.25rem;
|
|
|
+ font-weight: 700;
|
|
|
+ margin: 1rem 0;
|
|
|
+ color: #0a1929;
|
|
|
+ transition: color 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.course-card:hover .course-title {
|
|
|
+ color: var(--main-color);
|
|
|
+}
|
|
|
+
|
|
|
+/* 课程描述 */
|
|
|
+.course-desc {
|
|
|
+ color: #71bbdb;
|
|
|
+ line-height: 1.5;
|
|
|
+ margin: 0.75rem 0;
|
|
|
+ display: -webkit-box;
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
+ -webkit-line-clamp: 2;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+/* 底部信息 */
|
|
|
+.footer {
|
|
|
+ margin-top: 1.5rem;
|
|
|
+}
|
|
|
+
|
|
|
+.lecturer {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 0.75rem;
|
|
|
+}
|
|
|
+
|
|
|
+.lecturer-name {
|
|
|
+ font-size: 0.875rem;
|
|
|
+ color: #71bbdb;
|
|
|
+}
|
|
|
+
|
|
|
+/* 自定义按钮样式 */
|
|
|
+.study-btn {
|
|
|
+ background: linear-gradient(
|
|
|
+ to right,
|
|
|
+ var(--main-color),
|
|
|
+ var(--secondary-color)
|
|
|
+ );
|
|
|
+ border: 2px solid rgba(255, 255, 255, 0.2);
|
|
|
+ border-radius: 0.75rem;
|
|
|
+ color: white;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.study-btn:hover {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 5px 15px rgba(var(--main-color), 0.3);
|
|
|
+}
|
|
|
+
|
|
|
+/* 图片容器 */
|
|
|
+.image-container {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ height: 200px; /* 根据实际情况调整 */
|
|
|
+ overflow: hidden;
|
|
|
+ background-color: #f5f7fa; /* 保持与Element Plus一致的背景色 */
|
|
|
+ border-radius: 4px; /* 可选圆角 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 图片样式 */
|
|
|
+.cover-image {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover; /* 保持原有fit="cover"效果 */
|
|
|
+ //transition: opacity 0.3s;
|
|
|
+}
|
|
|
+
|
|
|
+/* 加载占位符 */
|
|
|
+.image-slot {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ color: #909399; /* Element Plus的次要文字颜色 */
|
|
|
+ font-size: 14px;
|
|
|
+ background: repeating-linear-gradient(
|
|
|
+ -45deg,
|
|
|
+ #f5f7fa,
|
|
|
+ #f5f7fa 10px,
|
|
|
+ #e9e9eb 10px,
|
|
|
+ #e9e9eb 20px
|
|
|
+ ); /* 添加加载条纹效果 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 加载动画 */
|
|
|
+.dot {
|
|
|
+ display: inline-block;
|
|
|
+ animation: dot-pulse 5s infinite;
|
|
|
+}
|
|
|
+
|
|
|
+@keyframes dot-pulse {
|
|
|
+ 0%, 100% { opacity: 0.3; }
|
|
|
+ 50% { opacity: 1; }
|
|
|
+}
|
|
|
+</style>
|