浏览代码

Merge remote-tracking branch 'origin/wanzi'

liyanbo 4 月之前
父节点
当前提交
12b8096b72

+ 78 - 106
src/views/programming/ProgrammingCourset.vue

@@ -12,11 +12,11 @@
         </div>
       </div>
       <!-- 标题 -->
-      <div class="top-center-box">
-        <div class="top-center-inner-box" style="background-image: url('./src/assets/programming/list_title.png');">
-          <span>{{ pageTitle }}</span>
-        </div>
+    <div class="top-center-box" v-if="!showVideo">
+      <div class="top-center-inner-box" style="background-image: url('./src/assets/programming/list_title.png');">
+        <span>{{ pageTitle }}</span>
       </div>
+    </div>
       <!-- 课程提示 -->
       <div class="top-right-box">
         <div class="top-right-inner-box">
@@ -32,18 +32,14 @@
           <el-icon class="btn-icon" :class="{ 'disabled-icon': currentIndex === 0 }"><ArrowLeftBold /></el-icon>
         </div>
         <div 
-          class="content-box"
-          ref="contentBox"
-          @mousedown="handleMouseDown"
-          @mousemove="handleMouseMove"
-          @mouseup="handleMouseUp"
-          @mouseleave="handleMouseLeave"
-          @touchstart="handleTouchStart"
-          @touchmove="handleTouchMove"
-          @touchend="handleTouchEnd"
-          @touchcancel="handleTouchEnd"
-          @scroll="handleScroll"
-        >
+            class="content-box"
+            ref="contentBox"
+            @mousedown="handleMouseDown"
+            @mousemove="handleMouseMove"
+            @mouseup="handleMouseUp"
+            @mouseleave="handleMouseLeave"
+            @scroll="handleScroll"
+          >
           <!-- 动态渲染课程内容 -->
           <div 
             v-for="(item) in courseItems" 
@@ -127,8 +123,6 @@ const isDragging = ref(false)
 const startX = ref(0)
 const scrollLeft = ref(0)
 const hasMoved = ref(false) // 标记是否发生了移动
-const touchStartX = ref(0)
-const touchStartTime = ref(0)
 
 // 获取contentBox元素的引用
 const contentBox = ref(null)
@@ -144,23 +138,33 @@ const getStarCount = (contentType) => {
 
 // 上一页
 const prevSlide = () => {
-  if (contentBox.value) {
-    const scrollAmount = contentBox.value.clientWidth * 0.75
-    contentBox.value.scrollTo({
-      left: Math.max(0, contentBox.value.scrollLeft - scrollAmount),
-      behavior: 'smooth'
-    })
+  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'
+      });
+    }
   }
 }
 
 // 下一页
 const nextSlide = () => {
-  if (contentBox.value) {
-    const scrollAmount = contentBox.value.clientWidth * 0.75
-    contentBox.value.scrollTo({
-      left: contentBox.value.scrollLeft + scrollAmount,
-      behavior: 'smooth'
-    })
+  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'
+      });
+    }
   }
 }
 
@@ -191,8 +195,7 @@ const handleMouseMove = (e) => {
 }
 
 // 鼠标松开事件处理函数
-const handleMouseUp = (e) => {
-  e.stopPropagation()
+const handleMouseUp = () => {
   isDragging.value = false
   // 延迟重置hasMoved,确保点击事件可以正常判断
   setTimeout(() => {
@@ -206,70 +209,33 @@ const handleMouseLeave = () => {
   hasMoved.value = false
 }
 
-// 触摸开始事件处理函数(支持移动设备)
-const handleTouchStart = (e) => {
-  const touch = e.touches[0]
-  isDragging.value = true
-  hasMoved.value = false
-  touchStartX.value = touch.clientX
-  touchStartTime.value = Date.now()
-  scrollLeft.value = contentBox.value.scrollLeft
-  e.stopPropagation()
-}
-
-// 触摸移动事件处理函数
-const handleTouchMove = (e) => {
-  if (!isDragging.value) return
-  
-  const touch = e.touches[0]
-  const diffX = touchStartX.value - touch.clientX
-  
-  // 判断是否水平滑动
-  if (Math.abs(diffX) > 5) {
-    hasMoved.value = true
-    // 阻止默认行为和冒泡
-    e.preventDefault()
-    e.stopPropagation()
-    // 设置滚动位置
-    contentBox.value.scrollLeft = scrollLeft.value + diffX
-  }
-}
-
-// 触摸结束事件处理函数(支持移动设备)
-const handleTouchEnd = (e) => {
-  e.stopPropagation()
-  isDragging.value = false
-  // 延迟重置hasMoved
-  setTimeout(() => {
-    hasMoved.value = false
-  }, 100)
-}
-
-// 节流函数 - 限制函数在一段时间内只能执行一次
-const throttle = (func, limit) => {
-  let inThrottle
-  return function() {
-    const args = arguments
-    const context = this
-    if (!inThrottle) {
-      func.apply(context, args)
-      inThrottle = true
-      setTimeout(() => {
-        inThrottle = false
-      }, limit)
+// rpx转换为像素值的辅助函数
+const rpxValue = (px) => {
+  return (px / 750) * window.innerWidth;
+}
+
+// 滚动事件处理函数
+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;
+      }
     }
   }
 }
 
-// 优化的滚动事件处理函数 
-let animationFrameId = null
-const handleScroll = throttle(() => {
-  // 取消之前的动画帧请求
-  if (animationFrameId) {
-    cancelAnimationFrame(animationFrameId)
-  }
-}, 16) // 约60fps的频率
-
 // 组件挂载时获取路由参数设置标题
 onMounted(() => {
   // 检查路由参数中是否有courseTitle
@@ -391,18 +357,24 @@ const goBackIndex = () => {
   width: 100%;
   height: 100%;
   background-size: 70%; 
-  background-position: 50% 80%;
-  background-repeat: no-repeat;
-  display: flex; 
-  align-items: center; 
-  justify-content: center; 
-      cursor: pointer;
-
-  span{
-      font-size: rpx(15);
-      color: white;
+    background-repeat: no-repeat;
+    background-position: center center; /* 背景图垂直水平居中 */
+    display: flex; 
+    align-items: center; /* 垂直居中 */
+    justify-content: center; /* 水平居中 */
+    cursor: pointer;
+    position: relative; /* 相对定位 */
+   span{
+        font-size: rpx(16);
+        color: white;
+        position: relative;
+        z-index: 1; /* 确保文字在背景图上方 */
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        padding-bottom: rpx(5);
+    }
   }
-}
 
 .top-left-inner-box{
   display: flex;
@@ -469,7 +441,7 @@ const goBackIndex = () => {
   padding: 0 rpx(30);
   /* 设置背景图  */
   background-image: url('@/assets/programming/track01.png');
-  background-size: rpx(1360) rpx(350); /* 使用固定宽度,背景图大小始终一致 */
+  background-size: rpx(1360) rpx(350); /* 使用固定宽度,背景图大小一致 */
   background-position: left calc(-1 * rpx(50));
   background-repeat: no-repeat;
   background-attachment: local; /* 背景图跟内容一起滚动 */
@@ -517,12 +489,12 @@ const goBackIndex = () => {
 /* 鼠标悬停放大效果 - 在保持原有垂直位置的基础上放大 */
 .slide-item:nth-child(odd):hover {
   transform: translateY(-50%) scale(1.1);
-  z-index: 10; /* 确保放大时在顶层显示 */
+  z-index: 10;
 }
 
 .slide-item:nth-child(even):hover {
   transform: translateY(50%) scale(1.1);
-  z-index: 10; /* 确保放大时在顶层显示 */
+  z-index: 10;
 }
 
 /* 内容样式 */

+ 51 - 18
src/views/programming/ProgrammingGame.vue

@@ -34,6 +34,7 @@
             @mousemove="handleMouseMove"
             @mouseup="handleMouseUp"
             @mouseleave="handleMouseLeave"
+            @scroll="handleScroll"
           >
             <div 
               v-for="(course, index) in courseCategories" 
@@ -116,6 +117,10 @@ getThemeList().then(res => {
 // 自动滚动到中间位置
 watch(activeButton, (newIndex) => {
   if (middleBox.value) {
+    const itemWidth = rpxValue(200) + rpxValue(20);
+    const calculatedIndex = Math.round(newIndex / 3) * 3;
+    currentIndex.value = Math.max(0, Math.min(courseCategories.length - 3, calculatedIndex));
+    
     // 找到对应的课程卡片元素
     const courseElement = middleBox.value.querySelector(`.middle-inner-box:nth-child(${newIndex + 1})`);
     if (courseElement) {
@@ -161,9 +166,31 @@ const handleMouseUp = () => {
 }
 
 // 鼠标离开事件处理函数
-const handleMouseLeave = () => {
-  isDragging.value = false
-}
+  const handleMouseLeave = () => {
+    isDragging.value = false
+  }
+  
+  // 滚动事件处理函数
+  const handleScroll = () => {
+    if (middleBox.value && !isDragging.value) {
+      // 检查是否滚动到最右侧
+      const { scrollLeft, scrollWidth, clientWidth } = middleBox.value;
+      // 当滚动到最右侧时,禁用右侧箭头
+      if (scrollLeft + clientWidth >= scrollWidth - 10) {
+        currentIndex.value = Math.max(0, courseCategories.length - 3);
+      } else {
+        // 向右滑动,左侧按钮正常显示
+        if (scrollLeft > 10) {
+          // 根据滚动位置计算当前索引,但不限制为0
+          const itemWidth = rpxValue(200) + rpxValue(20);
+          const calculatedIndex = Math.round(scrollLeft / itemWidth);
+          currentIndex.value = Math.max(0, Math.min(courseCategories.length - 3, calculatedIndex));
+        } else { // 滚动到最左侧
+          currentIndex.value = 0;
+        }
+      }
+    }
+  }
 
 // 获取middleBox元素的引用
 const middleBox = ref(null)
@@ -274,15 +301,22 @@ const goToHomePage = () => {
 }
 .top-center-inner-box{
     background-size: 70%; 
-    background-position: 50% 80%;
     background-repeat: no-repeat;
-    display: flex; /* 使用flex布局 */
+    background-position: center center; /* 背景图垂直水平居中 */
+    display: flex;
     align-items: center; /* 垂直居中 */
     justify-content: center; /* 水平居中 */
-      cursor: pointer;
+    cursor: pointer;
+    position: relative; /* 相对定位 */
     span{
-        font-size: rpx(17);
+        font-size: rpx(16);
         color: white;
+        position: relative;
+        z-index: 1; /* 确保文字在背景图上方 */
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        padding-bottom: rpx(5);
     }
   }
 .top-left-inner-box{
@@ -328,31 +362,29 @@ const goToHomePage = () => {
   width: rpx(200); /* 设置固定宽度 */
   height: 100%; /* 设置固定高度 */
   position: relative;
-      cursor: pointer;
+  cursor: pointer;
   user-select: none; /* 禁止文本选择 */
-  display: inline-block; /* 水平排列 */
-  vertical-align: middle; /* 垂直居中对齐 */
-  margin-right: rpx(15); /* 增加卡片之间的间距 */
+  display: inline-flex; /* 使用flex布局 */
+  align-items: center; /* 垂直居中 */
+  justify-content: center; /* 水平居中 */
 }
-
 /* 鼠标按下时的光标样式 */
 .middle-inner-box:active {
   cursor: grabbing;
 }
-
 /* 隐藏滚动条 */
 .middle-box::-webkit-scrollbar {
   height: rpx(0);
 }
-
-
 .child-box-with-bg {
-  width: 100%; 
-  height: 100%;
+  width: rpx(200); 
+  height: rpx(210);
   background-size: 100%; 
   background-position: center; 
   background-repeat: no-repeat;
   transition: all 0.3s ease; /* 过渡效果 */
+  margin: auto;
+  position: relative; /* 相对定位 */
 }
 
 /* 鼠标悬浮时的放大效果 */
@@ -371,13 +403,14 @@ const goToHomePage = () => {
 /* 标题样式 */
 .box-title {
   position: absolute;
-  top: 15%; /* 百分比定位,相对于容器 */
+  top: 14.29%; /* 使用百分比定位,相对于容器高度 */
   left: 0;
   right: 0;
   color: white;
   text-align: center;
   font-size: rpx(18); 
   letter-spacing: rpx(6); /* 增加字体间距 */
+  transform: translateZ(1px);
 }
 
 

+ 59 - 88
src/views/programming/ProgrammingList.vue

@@ -31,13 +31,10 @@
               class="middle-box"
               ref="middleBox"
               @mousedown="handleMouseDown"
-              @mousemove="handleMouseMove"
-              @mouseup="handleMouseUp"
-              @mouseleave="handleMouseLeave"
-              @touchstart="handleTouchStart"
-              @touchmove="handleTouchMove"
-              @touchend="handleTouchEnd"
-              @touchcancel="handleTouchEnd"
+            @mousemove="handleMouseMove"
+            @mouseup="handleMouseUp"
+            @mouseleave="handleMouseLeave"
+            @scroll="handleScroll"
             >
               <div
                 v-for="(courseType, index) in specificCourses"
@@ -183,11 +180,17 @@ const circleButtons = reactive(specificCourses.map((_, index) => ({ text: String
 // 自动滚动到中间位置
 watch(activeButton, (newIndex) => {
   if (middleBox.value && newIndex !== -1) {
+    const containerWidth = middleBox.value.clientWidth;
+    const calculatedIndex = Math.max(0, Math.min(
+      specificCourses.length - 3, 
+      Math.round(newIndex / 3) * 3
+    ));
+    currentIndex.value = calculatedIndex;
+    
     // 找到对应的课程卡片元素
     const courseElement = middleBox.value.querySelector(`.middle-inner-box:nth-child(${newIndex + 1})`);
     if (courseElement) {
       // 计算滚动位置,选中的卡片居中
-      const containerWidth = middleBox.value.clientWidth;
       const elementLeft = courseElement.offsetLeft;
       const elementWidth = courseElement.offsetWidth;
       // 计算居中位置
@@ -205,97 +208,60 @@ watch(activeButton, (newIndex) => {
 const isDragging = ref(false)
 const startX = ref(0)
 const scrollLeft = ref(0)
-const hasMoved = ref(false) // 标记是否发生了移动
-const touchStartX = ref(0)
-const touchStartTime = ref(0)
 
 // 鼠标按下事件处理函数
 const handleMouseDown = (e) => {
   isDragging.value = true
-  hasMoved.value = false
   startX.value = e.pageX - middleBox.value.offsetLeft
   scrollLeft.value = middleBox.value.scrollLeft
-  e.stopPropagation() // 阻止事件冒泡
 }
 
 // 鼠标移动事件处理函数
 const handleMouseMove = (e) => {
   if (!isDragging.value) return
-  
-  // 标记已发生移动
-  hasMoved.value = true
-  
-  // 阻止默认行为和冒泡
   e.preventDefault()
-  e.stopPropagation()
-  
   const x = e.pageX - middleBox.value.offsetLeft
   const walk = (x - startX.value) * 2 // 滚动速度
   middleBox.value.scrollLeft = scrollLeft.value - walk
 }
 
 // 鼠标松开事件处理函数
-const handleMouseUp = (e) => {
-  e.stopPropagation()
+const handleMouseUp = () => {
   isDragging.value = false
-  // 延迟重置hasMoved,确保点击事件可以正常判断
-  setTimeout(() => {
-    hasMoved.value = false
-  }, 100)
 }
 
 // 鼠标离开事件处理函数
 const handleMouseLeave = () => {
   isDragging.value = false
-  hasMoved.value = false
-}
-
-// 触摸开始事件处理函数(支持移动设备)
-const handleTouchStart = (e) => {
-  const touch = e.touches[0]
-  isDragging.value = true
-  hasMoved.value = false
-  touchStartX.value = touch.clientX
-  touchStartTime.value = Date.now()
-  scrollLeft.value = middleBox.value.scrollLeft
-  e.stopPropagation()
 }
 
-// 触摸移动事件处理函数(支持移动设备)
-const handleTouchMove = (e) => {
-  if (!isDragging.value) return
-  
-  const touch = e.touches[0]
-  const diffX = touchStartX.value - touch.clientX
-  
-  // 判断是否为有效的水平滑动
-  if (Math.abs(diffX) > 5) {
-    hasMoved.value = true
-    
-    // 阻止默认行为和冒泡
-    e.preventDefault()
-    e.stopPropagation()
-    
-    // 设置滚动位置
-    middleBox.value.scrollLeft = scrollLeft.value + diffX
+// 滚动事件处理函数
+const handleScroll = () => {
+  if (middleBox.value && !isDragging.value) {
+    // 检查是否滚动到最右侧
+    const { scrollLeft, scrollWidth, clientWidth } = middleBox.value;
+    // 当滚动到最右侧时,禁用右侧箭头
+    if (scrollLeft + clientWidth >= scrollWidth - 10) {
+      currentIndex.value = Math.max(0, specificCourses.length - 3);
+    } else {
+      // 向右滑动,左侧按钮正常显示
+      if (scrollLeft > 10) {
+        // 根据滚动位置计算当前索引,但不限制为0
+        currentIndex.value = Math.max(0, Math.min(
+          specificCourses.length - 3, 
+          Math.round(scrollLeft / (clientWidth * 0.75))
+        ));
+      } else { // 滚动到最左侧
+        currentIndex.value = 0;
+      }
+    }
   }
 }
 
-// 触摸结束事件处理函数(支持移动设备)
-const handleTouchEnd = (e) => {
-  e.stopPropagation()
-  isDragging.value = false
-  // 延迟重置hasMoved
-  setTimeout(() => {
-    hasMoved.value = false
-  }, 100)
-}
+
 
 // 处理项目点击事件
 const handleItemClick = (courseType, index) => {
-  // 如果在拖动过程中,则不触发点击事件
-  if (hasMoved.value) return
-  
   // 调用原始的goToProgrammingList函数
   goToProgrammingList(courseType, index)
 }
@@ -444,15 +410,23 @@ const disableBlockly = (blocklyId = categoryId.value) => {
   height: 100%;
 }
 .top-center-inner-box{
-    background-size: 70%;
-    background-position: 50% 80%;
+    background-size: 70%; 
     background-repeat: no-repeat;
-    display: flex; /* 使用flex布局 */
+    background-position: center center; /* 背景图垂直水平居中 */
+    display: flex;
     align-items: center; /* 垂直居中 */
     justify-content: center; /* 水平居中 */
+    cursor: pointer;
+    position: relative; /* 相对定位 */
     span{
-        font-size: rpx(17);
+        font-size: rpx(16);
         color: white;
+        position: relative;
+        z-index: 1; /* 确保文字在背景图上方 */
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        padding-bottom: rpx(5);
     }
   }
 .top-left-inner-box{
@@ -473,7 +447,7 @@ const disableBlockly = (blocklyId = categoryId.value) => {
 }
 
 .middle-wrapper {
-  height: 60%;
+  height: 80%;
   overflow: hidden; /* 溢出隐藏 */
   position: relative;
   display: flex;
@@ -492,23 +466,18 @@ const disableBlockly = (blocklyId = categoryId.value) => {
   -webkit-overflow-scrolling: touch; /* 触摸设备支持 */
   position: relative;
   flex: 1;
-  cursor: grab; /* 显示可抓取图标 */
-}
-
-/* 鼠标按下时的光标样式 */
-.middle-box:active {
-  cursor: grabbing;
 }
 
 .middle-inner-box{
-  width: rpx(130); /* 设置固定宽度 */
+  width: rpx(140); /* 设置固定宽度 */
   height: 100%; /* 设置固定高度 */
   position: relative;
-  cursor: pointer; /* 显示可抓取图标 */
+  cursor: pointer;
   user-select: none; /* 禁止文本选择 */
-  display: inline-block; /* 水平排列 */
-  margin-right: rpx(20);
-  margin-left: rpx(15);
+  display: inline-flex;
+  align-items: center; /* 垂直居中 */
+  justify-content: center; /* 水平居中 */
+  margin-left: rpx(30);
   vertical-align: middle; /* 垂直居中对齐 */
 }
 
@@ -530,8 +499,7 @@ const disableBlockly = (blocklyId = categoryId.value) => {
 
 .new-white-box {
   width: 100%;
-  height: 65%;
-  margin-top: rpx(20);
+  height: rpx(150);
   background-color: white;
   border-radius: rpx(15);
   display: flex;
@@ -600,9 +568,10 @@ const disableBlockly = (blocklyId = categoryId.value) => {
 
 /* 背景图容器 */
 .bg-image-container {
-  width: 100%;
-  height: 85%;
-  background-size: 105%; 
+  width: rpx(140);
+  height: rpx(150);
+  margin-top: rpx(5);
+  background-size: 100%; 
   background-position: center;
   background-repeat: no-repeat;
   position: relative;
@@ -618,6 +587,8 @@ const disableBlockly = (blocklyId = categoryId.value) => {
   background-position: center;
   background-repeat: no-repeat;
   z-index: 1;
+  transform-origin: left;
+  transform: scale(0.91);
 }
 
 /* 文字容器 */