|
|
@@ -29,10 +29,6 @@
|
|
|
|
|
|
<!-- 课程部分 -->
|
|
|
<div class="lower-box" v-if="!showVideo">
|
|
|
- <!-- 左切换按钮 -->
|
|
|
- <div v-if="!showVideo && courseItems.length > 3" 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="content-box"
|
|
|
ref="contentBox"
|
|
|
@@ -44,9 +40,9 @@
|
|
|
>
|
|
|
<!-- 动态渲染课程内容 -->
|
|
|
<div
|
|
|
- v-for="(item) in courseItems"
|
|
|
+ v-for="(item, index) in courseItems"
|
|
|
:key="item.id"
|
|
|
- class="slide-item"
|
|
|
+ :class="['slide-item', { active: activeButton === index }]"
|
|
|
:style="courseItems.length <= 3 ? { float: 'left' } : {}"
|
|
|
@click="handleCourseItemClick(item)"
|
|
|
>
|
|
|
@@ -67,10 +63,22 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <!-- 右切换按钮 -->
|
|
|
- <div v-if="!showVideo && courseItems.length > 3" class="carousel-btn next-btn" @click="nextSlide" :class="{ 'disabled-btn': currentIndex >= courseItems.length - 3 }" :disabled="currentIndex >= courseItems.length - 3">
|
|
|
- <el-icon class="btn-icon" :class="{ 'disabled-icon': currentIndex >= courseItems.length - 3 }"><ArrowRightBold /></el-icon>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 底部切换按钮 -->
|
|
|
+ <div v-if="!showVideo && courseItems.length > 3" class="bottom-box">
|
|
|
+ <!-- 左切换按钮 -->
|
|
|
+ <div class="carousel-btn prev-btn" @click="prevSlide" :class="{ 'disabled-btn': activeButton === 0 }" :disabled="activeButton === 0">
|
|
|
+ <img :src="leftbtn" alt="左按钮" class="btn-image" :class="{ 'disabled-icon': activeButton === 0 }" />
|
|
|
+ </div>
|
|
|
+ <!-- 进度条滑块 -->
|
|
|
+ <div class="line-container" v-if="courseItems.length > 0">
|
|
|
+ <el-slider v-model="activeButton" :max="courseItems.length - 1" :step="1" :show-tooltip="false" />
|
|
|
+ </div>
|
|
|
+ <!-- 右切换按钮 -->
|
|
|
+ <div class="carousel-btn next-btn" @click="nextSlide" :class="{ 'disabled-btn': activeButton >= courseItems.length - 1 }" :disabled="activeButton >= courseItems.length - 1">
|
|
|
+ <img :src="rightbtn" alt="右按钮" class="btn-image" :class="{ 'disabled-icon': activeButton >= courseItems.length - 1 }" />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 视频和编程界面 -->
|
|
|
@@ -91,6 +99,9 @@ import { getBlocklyByTypeId } from '@/api/programming/index.js'
|
|
|
import explanation from '@/assets/programming/explanation.png'
|
|
|
import practice from '@/assets/programming/practice.png'
|
|
|
import summary from '@/assets/programming/summary.png'
|
|
|
+// 导入按钮图片
|
|
|
+import leftbtn from '@/assets/programming/leftbtn.png'
|
|
|
+import rightbtn from '@/assets/programming/rightbtn.png'
|
|
|
|
|
|
import Interface from './Interface.vue'
|
|
|
|
|
|
@@ -119,8 +130,8 @@ const courseItems = ref([])
|
|
|
const selectedCourseData = ref(null)
|
|
|
// 保存原始API返回的数据
|
|
|
const resData = ref([])
|
|
|
-// 轮播图当前索引
|
|
|
-const currentIndex = ref(0)
|
|
|
+// 当前激活的按钮索引
|
|
|
+const activeButton = ref(0)
|
|
|
|
|
|
// 拖动相关变量
|
|
|
const isDragging = ref(false)
|
|
|
@@ -142,33 +153,15 @@ const getStarCount = (contentType) => {
|
|
|
|
|
|
// 上一页
|
|
|
const prevSlide = () => {
|
|
|
- if (currentIndex.value > 0) {
|
|
|
- currentIndex.value = Math.max(0, currentIndex.value - 3)
|
|
|
- // 滚动到对应位置
|
|
|
- if (contentBox.value) {
|
|
|
- const itemWidth = rpxValue(140) + rpxValue(70); // 宽度+间距
|
|
|
- const scrollPosition = currentIndex.value * itemWidth;
|
|
|
- contentBox.value.scrollTo({
|
|
|
- left: scrollPosition,
|
|
|
- behavior: 'smooth'
|
|
|
- });
|
|
|
- }
|
|
|
+ if (activeButton.value > 0) {
|
|
|
+ activeButton.value -= 1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 下一页
|
|
|
const nextSlide = () => {
|
|
|
- if (currentIndex.value < courseItems.value.length - 3) {
|
|
|
- currentIndex.value = Math.min(courseItems.value.length - 3, currentIndex.value + 3)
|
|
|
- // 滚动到对应位置
|
|
|
- if (contentBox.value) {
|
|
|
- const itemWidth = rpxValue(140) + rpxValue(70); // 宽度+间距
|
|
|
- const scrollPosition = currentIndex.value * itemWidth;
|
|
|
- contentBox.value.scrollTo({
|
|
|
- left: scrollPosition,
|
|
|
- behavior: 'smooth'
|
|
|
- });
|
|
|
- }
|
|
|
+ if (activeButton.value < courseItems.value.length - 1) {
|
|
|
+ activeButton.value += 1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -219,26 +212,28 @@ const rpxValue = (px) => {
|
|
|
}
|
|
|
|
|
|
// 滚动事件处理函数
|
|
|
-const handleScroll = () => {
|
|
|
- if (contentBox.value && !isDragging.value) {
|
|
|
- // 检查是否滚动到最右侧
|
|
|
- const { scrollLeft, scrollWidth, clientWidth } = contentBox.value;
|
|
|
- // 当滚动到最右侧时,禁用右侧箭头
|
|
|
- if (scrollLeft + clientWidth >= scrollWidth - 10) {
|
|
|
- currentIndex.value = Math.max(0, courseItems.value.length - 3);
|
|
|
- } else {
|
|
|
- // 向右滑动,左侧按钮正常显示
|
|
|
- if (scrollLeft > 10) {
|
|
|
- // 根据滚动位置计算当前索引,但不限制为0
|
|
|
- const itemWidth = rpxValue(140) + rpxValue(70); // 宽度+间距
|
|
|
- const calculatedIndex = Math.round(scrollLeft / itemWidth);
|
|
|
- currentIndex.value = Math.max(0, Math.min(courseItems.value.length - 3, calculatedIndex));
|
|
|
- } else { // 滚动到最左侧
|
|
|
- currentIndex.value = 0;
|
|
|
- }
|
|
|
+const handleScroll = () => {}
|
|
|
+
|
|
|
+// 监听activeButton变化,自动滚动到中间位置
|
|
|
+watch(activeButton, (newIndex) => {
|
|
|
+ if (contentBox.value && newIndex !== -1) {
|
|
|
+ // 找到对应的课程卡片元素
|
|
|
+ const courseElement = contentBox.value.querySelector(`.slide-item:nth-child(${newIndex + 1})`);
|
|
|
+ if (courseElement) {
|
|
|
+ // 计算滚动位置,选中的卡片居中
|
|
|
+ const containerWidth = contentBox.value.clientWidth;
|
|
|
+ const elementLeft = courseElement.offsetLeft;
|
|
|
+ const elementWidth = courseElement.offsetWidth;
|
|
|
+ // 计算居中位置
|
|
|
+ const scrollPosition = elementLeft - (containerWidth / 2) + (elementWidth / 2);
|
|
|
+ // 平滑滚动到计算的位置
|
|
|
+ contentBox.value.scrollTo({
|
|
|
+ left: scrollPosition,
|
|
|
+ behavior: 'smooth'
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
+});
|
|
|
|
|
|
// 提取课程数据获取和处理逻辑为单独函数
|
|
|
const fetchCourseData = () => {
|
|
|
@@ -367,6 +362,7 @@ watch(showVideo, (newValue, oldValue) => {
|
|
|
.top-box {
|
|
|
height: 20%;
|
|
|
display: flex;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
.top-left-box,
|
|
|
@@ -454,12 +450,13 @@ watch(showVideo, (newValue, oldValue) => {
|
|
|
}
|
|
|
|
|
|
.lower-box {
|
|
|
- flex: 1;
|
|
|
+ height: 75%;
|
|
|
+ overflow: hidden; /* 溢出隐藏 */
|
|
|
+ position: relative;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
+ margin-top: rpx(-25);
|
|
|
}
|
|
|
|
|
|
.content-box {
|
|
|
@@ -500,15 +497,14 @@ watch(showVideo, (newValue, oldValue) => {
|
|
|
}
|
|
|
|
|
|
.slide-item {
|
|
|
- width: rpx(140); /* 设置固定宽度 */
|
|
|
- height: rpx(120); /* 高度设置 */
|
|
|
- margin: rpx(80) rpx(35);
|
|
|
- border-radius: rpx(40);
|
|
|
+ width: rpx(130); /* 设置固定宽度 */
|
|
|
+ height: rpx(110); /* 高度设置 */
|
|
|
+ margin: rpx(85) rpx(40);
|
|
|
+ border-radius: rpx(35);
|
|
|
background-color: rgba(255, 255, 255);
|
|
|
display: inline-flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
- box-shadow: 0 rpx(5) rpx(10) rgba(0, 0, 0, 0.2);
|
|
|
transition: transform 0.3s ease;
|
|
|
cursor: pointer;
|
|
|
z-index: 2; /* 内容在背景图上方 */
|
|
|
@@ -533,6 +529,20 @@ watch(showVideo, (newValue, oldValue) => {
|
|
|
transform: translateY(50%) scale(1.1);
|
|
|
z-index: 10;
|
|
|
}
|
|
|
+/* 默认选中第一个元素的样式 */
|
|
|
+/* 奇数项选中样式 */
|
|
|
+.slide-item:nth-child(odd).active {
|
|
|
+ transform: translateY(-50%) scale(1.1);
|
|
|
+ z-index: 10;
|
|
|
+ box-shadow: 0 rpx(8) rpx(15) rgba(0, 0, 0, 0.3);
|
|
|
+}
|
|
|
+
|
|
|
+/* 偶数项选中样式 */
|
|
|
+.slide-item:nth-child(even).active {
|
|
|
+ transform: translateY(50%) scale(1.1);
|
|
|
+ z-index: 10;
|
|
|
+ box-shadow: 0 rpx(8) rpx(15) rgba(0, 0, 0, 0.3);
|
|
|
+}
|
|
|
|
|
|
/* 内容样式 */
|
|
|
.box-content {
|
|
|
@@ -588,52 +598,117 @@ watch(showVideo, (newValue, oldValue) => {
|
|
|
|
|
|
/* 轮播图按钮样式 */
|
|
|
.carousel-btn {
|
|
|
- position: absolute;
|
|
|
- top: 50%;
|
|
|
- transform: translateY(-50%);
|
|
|
- width: rpx(30);
|
|
|
- height: rpx(30);
|
|
|
- background-color: rgba(255, 255, 255, 0.8);
|
|
|
+ width: rpx(40);
|
|
|
+ height: rpx(40);
|
|
|
border-radius: 50%;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
cursor: pointer;
|
|
|
- z-index: 100; /* 提高按钮层级确保始终在最上层 */
|
|
|
- box-shadow: 0 rpx(5) rpx(10) rgba(0, 0, 0, 0.2);
|
|
|
+ z-index: 10;
|
|
|
transition: all 0.3s ease;
|
|
|
- pointer-events: all;
|
|
|
}
|
|
|
|
|
|
.carousel-btn:hover:not(.disabled-btn) {
|
|
|
- background-color: rgba(255, 255, 255, 1);
|
|
|
- transform: translateY(-50%) scale(1.1);
|
|
|
+ transform: 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;
|
|
|
+ opacity: 0.5;
|
|
|
}
|
|
|
|
|
|
.prev-btn {
|
|
|
- left: 1%;
|
|
|
+ margin-right: rpx(5);
|
|
|
}
|
|
|
|
|
|
.next-btn {
|
|
|
- right: 1%;
|
|
|
+ margin-left: rpx(5);
|
|
|
}
|
|
|
|
|
|
.btn-icon {
|
|
|
font-size: rpx(15);
|
|
|
color: #333;
|
|
|
}
|
|
|
+
|
|
|
+/* 底部切换按钮样式 */
|
|
|
+.bottom-box {
|
|
|
+ height: 15%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.bottom-box .carousel-btn {
|
|
|
+ position: relative;
|
|
|
+ top: 0;
|
|
|
+ transform: none;
|
|
|
+ width: rpx(40);
|
|
|
+ height: rpx(40);
|
|
|
+ background-color: transparent;
|
|
|
+ box-shadow: none;
|
|
|
+}
|
|
|
+
|
|
|
+.bottom-box .carousel-btn:hover:not(.disabled-btn) {
|
|
|
+ transform: none;
|
|
|
+}
|
|
|
+
|
|
|
+.line-container {
|
|
|
+ width: 70%; /* 滑块宽度 */
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ /* el-slider滑块样式 */
|
|
|
+ :deep(.el-slider__runway) {
|
|
|
+ height: rpx(10); /* 滑块轨道高度 */
|
|
|
+ background-color: rgba(228, 227, 254,0.3); /* 轨道背景色 */
|
|
|
+ border-radius: rpx(10);
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-slider__bar) {
|
|
|
+ height: rpx(10); /* 滑块激活部分高度 */
|
|
|
+ background-color: #2598c2; /* 激活部分颜色为蓝色 */
|
|
|
+ border-radius: rpx(10);
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-slider__button) {
|
|
|
+ width: rpx(35);
|
|
|
+ height: rpx(35);
|
|
|
+ margin-top: rpx(-5);
|
|
|
+ margin-left: rpx(-10);
|
|
|
+ border: none; /* 移除边框 */
|
|
|
+ background-image: url('@/assets/programming/xiaozhi.png');
|
|
|
+ background-size: 100%; /* 背景图片完全覆盖按钮 */
|
|
|
+ background-position: center; /* 背景图片居中 */
|
|
|
+ background-repeat: no-repeat; /* 背景图片不重复 */
|
|
|
+ background-color: transparent; /* 透明背景 */
|
|
|
+ &:hover {
|
|
|
+ transform: scale(1.1); /* 悬停时放大效果 */
|
|
|
+ }
|
|
|
+ &.hover,
|
|
|
+ &:active {
|
|
|
+ transform: scale(1.1); /* 激活时放大效果 */
|
|
|
+ box-shadow: none; /* 移除阴影效果 */
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-slider__stop) {
|
|
|
+ width: rpx(10); /* 停止点大小 */
|
|
|
+ height: rpx(10); /* 停止点大小 */
|
|
|
+ background-color: white; /* 停止点颜色 */
|
|
|
+ opacity: 0.2;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.btn-image {
|
|
|
+ width: rpx(40);
|
|
|
+ height: rpx(40);
|
|
|
+ object-fit: contain;
|
|
|
+}
|
|
|
</style>
|