Jelajahi Sumber

课程图片和PPT按钮样式

丸子 8 bulan lalu
induk
melakukan
b0e132bc06
5 mengubah file dengan 338 tambahan dan 136 penghapusan
  1. TEMPAT SAMPAH
      src/assets/icon/videoImage01.png
  2. TEMPAT SAMPAH
      src/assets/icon/videoImage02.png
  3. 194 28
      src/components/Image/ImageView.vue
  4. 142 106
      src/components/PPT/PptView.vue
  5. 2 2
      src/views/AIDevelop.vue

TEMPAT SAMPAH
src/assets/icon/videoImage01.png


TEMPAT SAMPAH
src/assets/icon/videoImage02.png


+ 194 - 28
src/components/Image/ImageView.vue

@@ -1,20 +1,112 @@
 <template>
   <div class="image-container">
-    <img :src="imagePath" :alt="altText" class="course-image" />
+    <div v-if="images.length > 0" class="carousel-container">
+      <!-- 轮播图容器 -->
+      <div class="carousel-wrapper" ref="carouselWrapper">
+        <transition name="carousel-transition" mode="out-in">
+          <img 
+            :key="currentIndex"
+            :src="images[currentIndex]" 
+            :alt="`${altText} ${currentIndex + 1}`" 
+            class="carousel-image" 
+          />
+        </transition>
+      </div>
+      
+      <!-- 轮播指示器 -->
+      <div class="carousel-indicators">
+        <span 
+          v-for="(item, index) in images"
+          :key="index"
+          :class="['indicator', { 'active': currentIndex === index }]"
+          @click="goToSlide(index)"
+        ></span>
+      </div>
+    </div>
+    <div v-else class="no-image">
+      <span>{{ altText }}</span>
+    </div>
+    
+    <!-- 轮播控制按钮 -->
+    <div v-if="images.length > 1" class="carousel-controls">
+      <button class="control-btn left-btn" @click="prevSlide">
+        <img :src="videoImage01" alt="上一张" />
+      </button>
+      <button class="control-btn right-btn" @click="nextSlide">
+        <img :src="videoImage02" alt="下一张" />
+      </button>
+    </div>
+    
   </div>
 </template>
 
 <script setup>
-import { defineProps, defineEmits } from 'vue'
+import { defineProps, computed, ref, onMounted, onUnmounted } from 'vue'
+import videoImage01 from '@/assets/icon/videoImage01.png'
+import videoImage02 from '@/assets/icon/videoImage02.png'
 
 // 定义props
 const props = defineProps({
   imagePath: { type: String, required: true },
-  altText: { type: String, default: '课程图片' }
+  altText: { type: String, default: '课程图片' },
+  autoPlay: { type: Boolean, default: true },
+  interval: { type: Number, default: 3000 }
 })
 
+// 计算属性:将逗号分隔的图片路径字符串转换为数组
+const images = computed(() => {
+  if (!props.imagePath) return []
+  // 分割逗号分隔的字符串,并去除空字符串和前后空格
+  return props.imagePath.split(',').map(path => path.trim()).filter(path => path)
+})
+
+// 轮播相关状态
+const currentIndex = ref(0)
+const carouselWrapper = ref(null)
+const autoplayTimer = ref(null)
+
+// 下一张图片
+const nextSlide = () => {
+  currentIndex.value = (currentIndex.value + 1) % images.value.length
+  resetAutoplay()
+}
 
+// 上一张图片
+const prevSlide = () => {
+  currentIndex.value = (currentIndex.value - 1 + images.value.length) % images.value.length
+  resetAutoplay()
+}
 
+// 跳转到指定图片
+const goToSlide = (index) => {
+  currentIndex.value = index
+  resetAutoplay()
+}
+
+// 重置自动播放计时器
+// const resetAutoplay = () => {
+//   if (!props.autoPlay) return
+  
+//   if (autoplayTimer.value) {
+//     clearInterval(autoplayTimer.value)
+//   }
+  
+//   autoplayTimer.value = setInterval(nextSlide, props.interval)
+// }
+
+// 自动播放设置
+// onMounted(() => {
+//   if (props.autoPlay) {
+//     resetAutoplay()
+//   }
+// })
+
+// 清理定时器
+onUnmounted(() => {
+  if (autoplayTimer.value) {
+    clearInterval(autoplayTimer.value)
+  }
+})
 </script>
 
 <style scoped lang="scss">
@@ -30,39 +122,113 @@ const props = defineProps({
   display: flex;
   justify-content: center;
   align-items: center;
+  position: relative;
 }
-.course-image {
-  max-width: 70%;
-  max-height: rpx(289);
+
+.carousel-container {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  position: relative;
+}
+
+.carousel-wrapper {
+  width: 70%;
+  height: rpx(289);
+  overflow: hidden;
   border-radius: rpx(12);
-  object-fit: contain;
+  position: relative;
 }
-// 切换按钮
-.video-switch {
+
+.carousel-image {
   width: 100%;
+  height: 100%;
+  object-fit: cover;
+  border-radius: rpx(12);
+}
+
+// 轮播过渡动画
+.carousel-transition-enter-active,
+.carousel-transition-leave-active {
+  transition: all 0.2s ease;
+}
+
+.carousel-transition-enter-from {
+  opacity: 0;
+  transform: translateX(50px);
+}
+
+.carousel-transition-leave-to {
+  opacity: 0;
+  transform: translateX(-50px);
+}
+
+// 轮播指示器
+.carousel-indicators {
+  position: absolute;
+  bottom: rpx(10);
+  left: 50%;
+  transform: translateX(-50%);
   display: flex;
-  margin-top: rpx(5);
-  margin-bottom: rpx(15);
+  gap: rpx(8);
 }
-.caret-right,
-.caret-left {
-  width: rpx(50);
-  margin: auto;
+
+.indicator {
+  width: rpx(3);
+  height: rpx(3);
+  border-radius: 50%;
+  background-color: rgba(255, 255, 255, 0.5);
+  cursor: pointer;
+  transition: all 0.2s ease;
+}
+
+.indicator.active {
+  width: rpx(20);
+  border-radius: rpx(4);
+  background-color: white;
+}
+
+// 轮播控制按钮
+.carousel-controls {
+  position: absolute;
+  top: 50%;
+  left: 0;
+  right: 0;
+  transform: translateY(-50%);
   display: flex;
+  justify-content: space-between;
+  padding: 0 rpx(60);
+  pointer-events: none;
 }
-.caret-left ::v-deep(.el-button.is-round),
-.caret-right ::v-deep(.el-button.is-round) {
-  width: rpx(50);
-  height: rpx(15);
+
+.control-btn {
+  width: rpx(25);
+  height: rpx(25);
+  border-radius: 50%;
+  background: linear-gradient(to bottom, #fee78d, #ffd01a);
+  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+  border: none;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  cursor: pointer;
+  pointer-events: all;
+  transition: background-color 0.3s ease;
+}
+
+.control-btn:hover {
+  background-color: rgba(0, 0, 0, 0.7);
+}
+
+.control-btn img {
+  width: rpx(18);
+  height: rpx(18);
+}
+
+.no-image {
   color: white;
-  font-size: rpx(7);
-  border-radius: none;
-  border: 1px white solid;
-  background-color: rgb(255, 255, 255, 0.5);
-  box-shadow: 0 4px 8px rgba(202, 52, 52, 0.1);
-}
-.caret-right img,
-.caret-left img {
-  width: rpx(12);
+  font-size: rpx(10);
 }
 </style>

+ 142 - 106
src/components/PPT/PptView.vue

@@ -1,8 +1,10 @@
 <template>
-    <div class="box-ppt">
-      <div class="ppt-box">
+  <div class="box-ppt">
+    <div class="ppt-box">
+      <!-- 轮播图容器 -->
+      <div class="carousel-container">
         <!-- 添加翻页动画容器 -->
-        <div ref="pptContainer" class="ppt-container">
+        <div ref="pptContainer" class="carousel-wrapper">
           <VueOfficePptx
               ref="pptRef"
               :src="pptPath"
@@ -12,43 +14,39 @@
               :animation-speed="1.0"
           />
         </div>
+        
+        <!-- 轮播指示器 -->
+        <div v-if="totalPages > 1" class="carousel-indicators">
+          <span 
+            v-for="index in totalPages"
+            :key="index"
+            :class="['indicator', { 'active': currentPage === index }]"
+            @click="goToSlide(index)"
+          ></span>
+        </div>
+        
+        <!-- 轮播控制按钮 -->
+        <div v-if="totalPages > 1" class="carousel-controls">
+          <button class="control-btn left-btn" @click="prevPage" :disabled="currentPage <= 1">
+            <img :src="leftImg" alt="上一页" />
+          </button>
+          <button class="control-btn right-btn" @click="nextPage" :disabled="currentPage >= totalPages">
+            <img :src="rightImg" alt="下一页" />
+          </button>
+        </div>
       </div>
     </div>
-
-    <!-- 视频切换按钮 - 始终显示 -->
-    <div class="ppt-switch">
-      <div class="caret-left" @click="prevPage">
-        <el-button type="warning" round
-        :disabled="currentPage <= 1">
-        <img :src="leftImg" alt="Left" />上一页
-        </el-button>
-      </div>
-
-      <span class="page-info">{{ currentPage }} / {{ totalPages }}</span>
-
-      <div class="caret-right" @click="nextPage">
-        <el-button type="warning" round
-        :disabled="currentPage >= totalPages"
-        >下一页<img :src="rightImg" alt="Right" />
-        </el-button>
-      </div>
-
-    </div>
+  </div>
 </template>
 
 <script setup>
-import {
-  ref,
-  onMounted,
-  defineProps,
-} from 'vue'
-
+import { ref, onMounted, defineProps } from 'vue'
 import { ElMessage } from 'element-plus'
 // 导入图标
-import leftImg from '@/assets/icon/backward.png'
-import rightImg from '@/assets/icon/f-backward.png'
+import leftImg from '@/assets/icon/videoImage01.png'
+import rightImg from '@/assets/icon/videoImage02.png'
 // PPT
-import  VueOfficePptx  from '@vue-office/pptx'
+import VueOfficePptx from '@vue-office/pptx'
 
 // PPT翻页相关变量
 const pptRef = ref(null)
@@ -104,6 +102,12 @@ const prevPage = () => {
   }
 }
 
+// 跳转到指定页
+const goToSlide = (index) => {
+  currentPage.value = index;
+  scrollToPage(currentPage.value);
+}
+
 // 新增滚动到指定页方法
 const scrollToPage = (pageNum) => {
   if (pptContainer.value) {
@@ -118,7 +122,7 @@ const scrollToPage = (pageNum) => {
 // 初始化滚动位置
 onMounted(() => {
   // 获取容器元素
-  pptContainer.value = document.querySelector('.ppt-container');
+  pptContainer.value = document.querySelector('.carousel-wrapper');
   // 初始滚动到第一页
   scrollToPage(1);
 })
@@ -143,38 +147,123 @@ const handlePptError = (error) => {
   display: flex;
   justify-content: center;
   align-items: center;
-  .d-player-wrap {
-    height: rpx(289);
-    width: 68.5%;
-    border-radius: rpx(12);
-    object-fit: cover;
-  }
 }
-.ppt-box{
+
+.ppt-box {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.carousel-container {
   width: 100%;
-  height: rpx(200);
+  height: 100%;
   display: flex;
   justify-content: center;
   align-items: center;
+  position: relative;
+}
+
+.carousel-wrapper {
+  width: 70%;
+  height: rpx(290);
+  overflow: hidden;
+  border-radius: rpx(12);
+  position: relative;
+}
+
+// 轮播过渡动画
+.carousel-transition-enter-active,
+.carousel-transition-leave-active {
+  transition: all 0.2s ease;
+}
+
+.carousel-transition-enter-from {
+  opacity: 0;
+  transform: translateX(50px);
+}
+
+.carousel-transition-leave-to {
+  opacity: 0;
+  transform: translateX(-50px);
+}
+
+// 轮播指示器
+.carousel-indicators {
+  position: absolute;
+  bottom: rpx(10);
+  left: 50%;
+  transform: translateX(-50%);
+  display: flex;
+  gap: rpx(8);
 }
-// 添加翻页动画样式
-.page-transition-enter-active,
-.page-transition-leave-active {
-  transition: all .5s ease;
+
+.indicator {
+  width: rpx(3);
+  height: rpx(3);
+  border-radius: 50%;
+  background-color: rgba(255, 255, 255, 0.5);
+  cursor: pointer;
+  transition: all 0.2s ease;
 }
-.page-transition-enter-from {
-  opacity: .5;
-  transform: translateX(50px) scale(0.95);
+
+.indicator.active {
+  width: rpx(20);
+  border-radius: rpx(4);
+  background-color: white;
 }
-.page-transition-leave-to {
-  opacity: .5;
-  transform: translateX(-50px) scale(0.95);
+
+// 轮播控制按钮
+.carousel-controls {
+  position: absolute;
+  top: 50%;
+  left: 0;
+  right: 0;
+  transform: translateY(-50%);
+  display: flex;
+  justify-content: space-between;
+  padding: 0 rpx(60);
+  pointer-events: none;
 }
+
+.control-btn {
+  width: rpx(25);
+  height: rpx(25);
+  border-radius: 50%;
+  background: linear-gradient(to bottom, #fee78d, #ffd01a);
+  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+  border: none;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  cursor: pointer;
+  pointer-events: all;
+  transition: background-color 0.3s ease;
+}
+
+.control-btn:hover {
+  background-color: rgba(0, 0, 0, 0.7);
+}
+
+.control-btn:disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+}
+
+.control-btn img {
+  width: rpx(18);
+  height: rpx(18);
+}
+
 // 修改PPT内部预览容器样式
 ::v-deep .pptx-preview-wrapper {
   height: auto !important;
+  transition: transform 0.3s ease; /* 添加平滑过渡 */
 }
-.ppt-container ::v-deep(.pptx-preview-wrapper) {
+
+.carousel-wrapper ::v-deep(.pptx-preview-wrapper) {
   height: auto !important;
   // 滚动条整体样式
   &::-webkit-scrollbar {
@@ -193,59 +282,6 @@ const handlePptError = (error) => {
     background-color: rgba(143, 116, 255, 0.2); // 轨道颜色
     border-radius: rpx(4); // 轨道圆角
   }
-  // border-radius: rpx(12);
-}
-
-.ppt-switch {
-  width: 100%;
-  display: flex;
-  margin-top: rpx(5);
-  // margin-bottom: rpx(15);
-  justify-content: center;
-  align-items: center;
-}
-.caret-right,
-.caret-left {
-  width: rpx(50);
-   margin: 0 rpx(20);
-  display: flex;
-  justify-content: center;
-}
-.page-info{
-  color: #fff;
-  margin: 0 rpx(10);
-}
-
-.caret-left ::v-deep(.el-button.is-round),
-.caret-right ::v-deep(.el-button.is-round) {
-  width: rpx(50);
-  height: rpx(15);
-  color: white;
-  font-size: rpx(7);
-  border-radius: none;
-  border: 1px white solid;
-  background-color: rgb(255, 255, 255, 0.5);
-  box-shadow: 0 4px 8px rgba(202, 52, 52, 0.1);
-}
-
-.caret-right img,
-.caret-left img {
-  width: rpx(12);
-}
-
-.ppt-container {
-  overflow: hidden; /* 禁用滚动条 */
-  border-radius: rpx(12);
-  width: 70%;
-  position: relative;
-  height: calc(50vw * 0.75); /* 使用vw单位实现响应式高度 */
-  max-height:80vh; /* 限制最大高度 */
-}
-
-// 修改PPT内部预览容器样式
-::v-deep .pptx-preview-wrapper {
-  height: auto !important;
-  transition: transform 0.3s ease; /* 添加平滑过渡 */
 }
 
 // 确保每张幻灯片占满容器高度
@@ -254,4 +290,4 @@ const handlePptError = (error) => {
   width: 100% !important;
   box-sizing: border-box;
 }
-</style>
+</style>

+ 2 - 2
src/views/AIDevelop.vue

@@ -455,8 +455,8 @@ onMounted(async () => {
           courseTemp.courseContentType = 'ppt';
           courseTemp.pptPath = 'http://59.110.91.129:8088/admin-api/infra/file/29/get/20250820/ppt_1755654972861.pptx';
           // courseTemp.courseContentType = 'image';
-          // courseTemp.courseImagePath = 'http://59.110.91.129:8088/admin-api/infra/file/4/get/20250715/one_1752549934393.png';
-          // 可选:修改课程名称以便识别
+          // courseTemp.courseImagePath = 'http://59.110.91.129:8088/admin-api/infra/file/4/get/20250715/one_1752549934393.png,http://59.110.91.129:8088/admin-api/infra/file/29/get/20250722/666_1753151547130.png';
+          // // 可选:修改课程名称以便识别
           courseTemp.courseName = '测试';
         }