|
|
@@ -21,33 +21,58 @@
|
|
|
</div>
|
|
|
|
|
|
<!-- 编程部分 -->
|
|
|
- <div
|
|
|
- class="middle-box"
|
|
|
- ref="middleBox"
|
|
|
- @mousedown="handleMouseDown"
|
|
|
- @mousemove="handleMouseMove"
|
|
|
- @mouseup="handleMouseUp"
|
|
|
- @mouseleave="handleMouseLeave"
|
|
|
- >
|
|
|
- <div
|
|
|
- v-for="(courseType, index) in specificCourses"
|
|
|
- :key="index"
|
|
|
- class="middle-inner-box"
|
|
|
- @click="goToProgrammingList(courseType, index)"
|
|
|
- >
|
|
|
- <div class="new-white-box" :class="{ 'active': activeButton === index }" @click="goToProgrammingList(courseType, index)">
|
|
|
- <div class="bg-image-container" :style="{ backgroundImage: `url(${courseType.bgImage})` }"></div>
|
|
|
- <div class="text-container">
|
|
|
- <div class="box-title">
|
|
|
- <span>{{ courseType.title }}</span>
|
|
|
+ <div class="middle-wrapper">
|
|
|
+ <!-- 左切换按钮 -->
|
|
|
+ <div class="carousel-btn prev-btn" @click="prevSlide" :class="{ 'disabled-btn': currentIndex === 0 }" :disabled="currentIndex === 0">
|
|
|
+ <el-icon class="btn-icon" :class="{ 'disabled-icon': currentIndex === 0 }"><ArrowLeftBold /></el-icon>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ class="middle-box"
|
|
|
+ ref="middleBox"
|
|
|
+ @mousedown="handleMouseDown"
|
|
|
+ @mousemove="handleMouseMove"
|
|
|
+ @mouseup="handleMouseUp"
|
|
|
+ @mouseleave="handleMouseLeave"
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ v-for="(courseType, index) in specificCourses"
|
|
|
+ :key="index"
|
|
|
+ class="middle-inner-box"
|
|
|
+ @click="goToProgrammingList(courseType, index)"
|
|
|
+ >
|
|
|
+ <div class="new-white-box" :class="{ 'active': activeButton === index }" @click="goToProgrammingList(courseType, index)">
|
|
|
+ <!-- 列表封面图 -->
|
|
|
+ <div class="bg-image-container" :style="{ backgroundImage: `url(${courseType.bgImage})` }">
|
|
|
+ <!-- 星星图标 -->
|
|
|
+ <div
|
|
|
+ v-for="starIndex in 5"
|
|
|
+ :key="starIndex"
|
|
|
+ class="star-icon"
|
|
|
+ :style="{
|
|
|
+ backgroundImage: `url(${starIndex <= courseType.progress ? star01Image : star02Image})`,
|
|
|
+ left: `${30 + (starIndex - 1) * 30}px`
|
|
|
+ }"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ <div class="text-container">
|
|
|
+ <div class="box-title">
|
|
|
+ <span>{{ courseType.title }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- unlock背景图盒子 -->
|
|
|
+ <div class="unlock-box" :style="{ backgroundImage: `url(${unlockImage})` }"></div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <!-- unlock背景图盒子 -->
|
|
|
- <div class="unlock-box" :style="{ backgroundImage: `url(${unlockImage})` }"></div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+
|
|
|
+ <!-- 右切换按钮 -->
|
|
|
+ <div class="carousel-btn next-btn" @click="nextSlide" :class="{ 'disabled-btn': currentIndex >= specificCourses.length - 3 }" :disabled="currentIndex >= specificCourses.length - 3">
|
|
|
+ <el-icon class="btn-icon" :class="{ 'disabled-icon': currentIndex >= specificCourses.length - 3 }"><ArrowRightBold /></el-icon>
|
|
|
</div>
|
|
|
|
|
|
+ </div>
|
|
|
+
|
|
|
<!-- 底部切换按钮 -->
|
|
|
<div class="bottom-box">
|
|
|
<div class="line-container">
|
|
|
@@ -68,13 +93,16 @@
|
|
|
<script setup>
|
|
|
import { ref, reactive, watch, onMounted, onUnmounted } from 'vue'
|
|
|
// 返回图标
|
|
|
-import { ArrowLeftBold } from '@element-plus/icons-vue';
|
|
|
+import { ArrowLeftBold, ArrowRightBold } from '@element-plus/icons-vue';
|
|
|
// 导入路由
|
|
|
import { useRouter, useRoute } from 'vue-router';
|
|
|
-// 导入主题列表接口
|
|
|
-import { TopicList } from '@/api/programming/index.js'
|
|
|
+// 获取类型列表
|
|
|
+import { getTypeByThemeId } from '@/api/programming/index.js'
|
|
|
// 导入图片
|
|
|
import unlockImage from '@/assets/programming/unlock.png'
|
|
|
+// 星星图片
|
|
|
+import star02Image from '@/assets/programming/star02.png'
|
|
|
+import star01Image from '@/assets/programming/star01.png'
|
|
|
|
|
|
// 获取路由实例
|
|
|
const router = useRouter()
|
|
|
@@ -95,11 +123,13 @@ const specificCourses = reactive([])
|
|
|
|
|
|
// 当前激活的按钮索引
|
|
|
const activeButton = ref(0)
|
|
|
+// 轮播图当前索引
|
|
|
+const currentIndex = ref(0)
|
|
|
|
|
|
-// 在获取到categoryId后再调用TopicList接口
|
|
|
+// 在获取到categoryId后再调用获取类型列表getTypeByThemeId接口
|
|
|
const fetchTopicList = () => {
|
|
|
if (categoryId.value) {
|
|
|
- TopicList(categoryId.value).then(res => {
|
|
|
+ getTypeByThemeId(categoryId.value).then(res => {
|
|
|
console.log(categoryId.value, res);
|
|
|
// 更新课程数据,使用接口返回的数据
|
|
|
if (res && res.data && Array.isArray(res.data)) {
|
|
|
@@ -109,7 +139,8 @@ const fetchTopicList = () => {
|
|
|
specificCourses.push({
|
|
|
id: item.id,
|
|
|
title: item.ctType,
|
|
|
- bgImage: item.ctTypeImage
|
|
|
+ bgImage: item.ctTypeImage,
|
|
|
+ progress: item.progress
|
|
|
});
|
|
|
});
|
|
|
// 更新圆形按钮数据
|
|
|
@@ -180,6 +211,36 @@ const handleMouseLeave = () => {
|
|
|
isDragging.value = false
|
|
|
}
|
|
|
|
|
|
+// 上一页
|
|
|
+const prevSlide = () => {
|
|
|
+ if (currentIndex.value > 0) {
|
|
|
+ currentIndex.value = Math.max(0, currentIndex.value - 3)
|
|
|
+ // 滚动到对应位置
|
|
|
+ if (middleBox.value) {
|
|
|
+ const scrollAmount = middleBox.value.clientWidth * 0.75
|
|
|
+ middleBox.value.scrollTo({
|
|
|
+ left: Math.max(0, middleBox.value.scrollLeft - scrollAmount),
|
|
|
+ behavior: 'smooth'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 下一页
|
|
|
+const nextSlide = () => {
|
|
|
+ if (currentIndex.value < specificCourses.length - 3) {
|
|
|
+ currentIndex.value = Math.min(specificCourses.length - 3, currentIndex.value + 3)
|
|
|
+ // 滚动到对应位置
|
|
|
+ if (middleBox.value) {
|
|
|
+ const scrollAmount = middleBox.value.clientWidth * 0.75
|
|
|
+ middleBox.value.scrollTo({
|
|
|
+ left: middleBox.value.scrollLeft + scrollAmount,
|
|
|
+ behavior: 'smooth'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// 获取middleBox元素的引用
|
|
|
const middleBox = ref(null)
|
|
|
|
|
|
@@ -193,7 +254,7 @@ onMounted(() => {
|
|
|
const id = route.query.categoryId
|
|
|
if (id) {
|
|
|
categoryId.value = id
|
|
|
- // 获取到categoryId后调用TopicList接口
|
|
|
+ // 获取到categoryId后调用getTypeByThemeId接口
|
|
|
fetchTopicList()
|
|
|
}
|
|
|
})
|
|
|
@@ -293,25 +354,37 @@ const goToProgrammingList = (courseType, index) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.middle-box {
|
|
|
+.middle-wrapper {
|
|
|
height: 60%;
|
|
|
+ overflow: hidden; /* 溢出隐藏 */
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.middle-box {
|
|
|
+ min-width: rpx(660); /* 固定最小宽度 */
|
|
|
+ height: 100%;
|
|
|
+ margin: 0 rpx(50);
|
|
|
overflow-x: auto; /* 水平滚动条 */
|
|
|
overflow-y: hidden; /* 取消上下滚动 */
|
|
|
white-space: nowrap; /* 防止元素换行 */
|
|
|
- padding: 0 rpx(20); /* 左右内边距 */
|
|
|
scroll-behavior: smooth; /* 平滑滚动效果 */
|
|
|
-webkit-overflow-scrolling: touch; /* 触摸设备支持 */
|
|
|
position: relative;
|
|
|
+ flex: 1;
|
|
|
}
|
|
|
|
|
|
.middle-inner-box{
|
|
|
- width: rpx(200); /* 设置固定宽度 */
|
|
|
+ width: rpx(130); /* 设置固定宽度 */
|
|
|
height: 100%; /* 设置固定高度 */
|
|
|
position: relative;
|
|
|
- cursor: grab; /* 显示可抓取图标 */
|
|
|
+ cursor: pointer; /* 显示可抓取图标 */
|
|
|
user-select: none; /* 禁止文本选择 */
|
|
|
display: inline-block; /* 水平排列 */
|
|
|
- margin-right: rpx(10);
|
|
|
+ margin-right: rpx(20);
|
|
|
+ margin-left: rpx(15);
|
|
|
vertical-align: middle; /* 垂直居中对齐 */
|
|
|
}
|
|
|
|
|
|
@@ -325,17 +398,6 @@ const goToProgrammingList = (courseType, index) => {
|
|
|
height: rpx(0);
|
|
|
}
|
|
|
|
|
|
-.middle-inner-box{
|
|
|
- width: rpx(130); /* 设置固定宽度 */
|
|
|
- height: 100%; /* 设置固定高度 */
|
|
|
- position: relative;
|
|
|
- cursor: pointer;
|
|
|
- display: inline-block; /* 水平排列 */
|
|
|
- margin-right: rpx(20);
|
|
|
- margin-left: rpx(20);
|
|
|
- vertical-align: middle; /* 垂直居中对齐 */
|
|
|
- touch-action: none; /* 防止触摸设备上的默认行为 */
|
|
|
-}
|
|
|
|
|
|
.new-white-box {
|
|
|
width: 100%;
|
|
|
@@ -370,11 +432,8 @@ const goToProgrammingList = (courseType, index) => {
|
|
|
transform: scale(1.1); /* 放大1.1倍 */
|
|
|
z-index: 10; /* 确保放大时在顶层显示 */
|
|
|
border: rpx(5) solid #D0D7F7; /* 边框效果 */
|
|
|
- box-shadow: 0 0 rpx(10) rgba(0, 0, 0, 0.5);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
/* 背景图容器 */
|
|
|
.bg-image-container {
|
|
|
width: 100%;
|
|
|
@@ -382,6 +441,19 @@ const goToProgrammingList = (courseType, index) => {
|
|
|
background-size: 105%;
|
|
|
background-position: center;
|
|
|
background-repeat: no-repeat;
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+
|
|
|
+/* star图标样式 */
|
|
|
+.star-icon {
|
|
|
+ position: absolute;
|
|
|
+ top: rpx(10);
|
|
|
+ width: rpx(20);
|
|
|
+ height: rpx(20);
|
|
|
+ background-size: contain;
|
|
|
+ background-position: center;
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ z-index: 1;
|
|
|
}
|
|
|
|
|
|
/* 文字容器 */
|
|
|
@@ -409,10 +481,9 @@ const goToProgrammingList = (courseType, index) => {
|
|
|
background-size: contain;
|
|
|
background-position: center;
|
|
|
background-repeat: no-repeat;
|
|
|
- // bottom: -rpx(20);
|
|
|
- left: 50%;
|
|
|
+ left: 51%;
|
|
|
transform: translateX(-50%);
|
|
|
- top: rpx(149);
|
|
|
+ top: rpx(148);
|
|
|
z-index: 5;
|
|
|
}
|
|
|
.bottom-box {
|
|
|
@@ -423,7 +494,7 @@ const goToProgrammingList = (courseType, index) => {
|
|
|
}
|
|
|
|
|
|
.line-container {
|
|
|
- width: 80%;
|
|
|
+ width: 65%;
|
|
|
position: relative;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
@@ -439,8 +510,8 @@ const goToProgrammingList = (courseType, index) => {
|
|
|
}
|
|
|
|
|
|
.circle-button {
|
|
|
- width: rpx(10);
|
|
|
- height: rpx(10);
|
|
|
+ width: rpx(7);
|
|
|
+ height: rpx(7);
|
|
|
border-radius: 50%;
|
|
|
background-color: rgba(255, 255, 255);
|
|
|
border: rpx(1) solid #00c1fc;
|
|
|
@@ -463,4 +534,54 @@ const goToProgrammingList = (courseType, index) => {
|
|
|
box-shadow: 0 0 rpx(10) rgba(0, 0, 0, 0.5);
|
|
|
}
|
|
|
|
|
|
+/* 轮播图按钮样式 */
|
|
|
+.carousel-btn {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ width: rpx(30);
|
|
|
+ height: rpx(30);
|
|
|
+ background-color: rgba(255, 255, 255, 0.8);
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
+ z-index: 10;
|
|
|
+ box-shadow: 0 rpx(5) rpx(10) rgba(0, 0, 0, 0.2);
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.carousel-btn:hover:not(.disabled-btn) {
|
|
|
+ background-color: rgba(255, 255, 255, 1);
|
|
|
+ transform: translateY(-50%) scale(1.1);
|
|
|
+}
|
|
|
+
|
|
|
+.carousel-btn:disabled,
|
|
|
+.disabled-btn {
|
|
|
+ opacity: 0.5;
|
|
|
+ cursor: not-allowed;
|
|
|
+ background-color: rgba(200, 200, 200, 0.7);
|
|
|
+ box-shadow: none;
|
|
|
+}
|
|
|
+
|
|
|
+.disabled-icon {
|
|
|
+ color: #ccc !important;
|
|
|
+ filter: grayscale(100%);
|
|
|
+ opacity: 0.6;
|
|
|
+}
|
|
|
+
|
|
|
+.prev-btn {
|
|
|
+ left: 2%;
|
|
|
+}
|
|
|
+
|
|
|
+.next-btn {
|
|
|
+ right: 2%;
|
|
|
+}
|
|
|
+
|
|
|
+.btn-icon {
|
|
|
+ font-size: rpx(15);
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
</style>
|