Explorar el Código

课程根据类型显示图片视频PPT

丸子 hace 8 meses
padre
commit
ebcf9c88bb

+ 81 - 52
package-lock.json

@@ -11,6 +11,9 @@
         "@element-plus/icons-vue": "^2.3.1",
         "@microsoft/fetch-event-source": "^2.0.1",
         "@vitejs/plugin-legacy": "^7.0.1",
+        "@vue-office/docx": "^1.6.3",
+        "@vue-office/excel": "^1.7.14",
+        "@vue-office/pptx": "^1.0.1",
         "axios": "^1.10.0",
         "element-plus": "^2.10.2",
         "highlight.js": "^11.11.1",
@@ -21,6 +24,7 @@
         "router": "^2.2.0",
         "video.js": "^7.21.5",
         "vue": "^3.5.17",
+        "vue-demi": "^0.14.10",
         "vue-router": "^4.5.1",
         "vue-video-play": "^7.0.4",
         "vue-video-player": "^6.0.0",
@@ -2943,6 +2947,57 @@
         "vue": "^3.2.25"
       }
     },
+    "node_modules/@vue-office/docx": {
+      "version": "1.6.3",
+      "resolved": "https://registry.npmjs.org/@vue-office/docx/-/docx-1.6.3.tgz",
+      "integrity": "sha512-Cs+3CAaRBOWOiW4XAhTwwxJ0dy8cPIf6DqfNvYcD3YACiLwO4kuawLF2IAXxyijhbuOeoFsfvoVbOc16A/4bZA==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "@vue/composition-api": "^1.7.1",
+        "vue": "^2.0.0 || >=3.0.0",
+        "vue-demi": "^0.14.6"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vue-office/excel": {
+      "version": "1.7.14",
+      "resolved": "https://registry.npmjs.org/@vue-office/excel/-/excel-1.7.14.tgz",
+      "integrity": "sha512-pVUgt+emDQUnW7q22CfnQ+jl43mM/7IFwYzOg7lwOwPEbiVB4K4qEQf+y/bc4xGXz75w1/e3Kz3G6wAafmFBFg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "@vue/composition-api": "^1.7.1",
+        "vue": "^2.0.0 || >=3.0.0",
+        "vue-demi": "^0.14.6"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vue-office/pptx": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@vue-office/pptx/-/pptx-1.0.1.tgz",
+      "integrity": "sha512-+V7Kctzl6f6+Yk4NaD/wQGRIkqLWcowe0jEhPexWQb8Oilbzt1OyhWRWcMsxNDTdrgm6aMLP+0/tmw27cxddMg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "@vue/composition-api": "^1.7.1",
+        "vue": "^2.0.0 || >=3.0.0",
+        "vue-demi": "^0.14.6"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@vue/compiler-core": {
       "version": "3.5.17",
       "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.17.tgz",
@@ -3064,32 +3119,6 @@
         "url": "https://github.com/sponsors/antfu"
       }
     },
-    "node_modules/@vueuse/core/node_modules/vue-demi": {
-      "version": "0.14.10",
-      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
-      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
-      "hasInstallScript": true,
-      "license": "MIT",
-      "bin": {
-        "vue-demi-fix": "bin/vue-demi-fix.js",
-        "vue-demi-switch": "bin/vue-demi-switch.js"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antfu"
-      },
-      "peerDependencies": {
-        "@vue/composition-api": "^1.0.0-rc.1",
-        "vue": "^3.0.0-0 || ^2.6.0"
-      },
-      "peerDependenciesMeta": {
-        "@vue/composition-api": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/@vueuse/metadata": {
       "version": "9.13.0",
       "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz",
@@ -3111,32 +3140,6 @@
         "url": "https://github.com/sponsors/antfu"
       }
     },
-    "node_modules/@vueuse/shared/node_modules/vue-demi": {
-      "version": "0.14.10",
-      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
-      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
-      "hasInstallScript": true,
-      "license": "MIT",
-      "bin": {
-        "vue-demi-fix": "bin/vue-demi-fix.js",
-        "vue-demi-switch": "bin/vue-demi-switch.js"
-      },
-      "engines": {
-        "node": ">=12"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/antfu"
-      },
-      "peerDependencies": {
-        "@vue/composition-api": "^1.0.0-rc.1",
-        "vue": "^3.0.0-0 || ^2.6.0"
-      },
-      "peerDependenciesMeta": {
-        "@vue/composition-api": {
-          "optional": true
-        }
-      }
-    },
     "node_modules/@xmldom/xmldom": {
       "version": "0.8.10",
       "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
@@ -5975,6 +5978,32 @@
         }
       }
     },
+    "node_modules/vue-demi": {
+      "version": "0.14.10",
+      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+      "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/vue-eslint-parser": {
       "version": "10.2.0",
       "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz",

+ 4 - 0
package.json

@@ -12,6 +12,9 @@
     "@element-plus/icons-vue": "^2.3.1",
     "@microsoft/fetch-event-source": "^2.0.1",
     "@vitejs/plugin-legacy": "^7.0.1",
+    "@vue-office/docx": "^1.6.3",
+    "@vue-office/excel": "^1.7.14",
+    "@vue-office/pptx": "^1.0.1",
     "axios": "^1.10.0",
     "element-plus": "^2.10.2",
     "highlight.js": "^11.11.1",
@@ -22,6 +25,7 @@
     "router": "^2.2.0",
     "video.js": "^7.21.5",
     "vue": "^3.5.17",
+    "vue-demi": "^0.14.10",
     "vue-router": "^4.5.1",
     "vue-video-play": "^7.0.4",
     "vue-video-player": "^6.0.0",

+ 1 - 1
src/api/class.js

@@ -20,7 +20,7 @@ export function ClassOutline (id) {
 // 根据类型id获取课程列表
 export function ClassType (typeId) {
   return axios({
-    url: 'bjdxWeb/course/getCourseByTypeId?typeId=' + typeId ,
+    url: 'bjdxWeb/course/getCourseByTypeId?typeId=' + typeId,
     method: 'get'
   })
 }

+ 171 - 75
src/components/videopage/VideoPlayer.vue

@@ -1,39 +1,60 @@
 <template>
   <div class="video-container">
     <div class="box-video">
-      <video
-        class="full-box-video"
-        ref="videoRef"
-        :controls="true"
-        @timeupdate="handleTimeUpdate"
-        @seeked="handleSeeked"
-        @ended="handleVideoEnded"
-      ></video>
+      <!-- 根据contentType决定显示视频、图片、PPT -->
+       <!-- 视频 -->
+      <template v-if="contentType === 'video'">
+        <video
+          class="full-box-video"
+          ref="videoRef"
+          :controls="true"
+          @timeupdate="handleTimeUpdate"
+          @seeked="handleSeeked"
+          @ended="handleVideoEnded"
+        ></video>
+      </template>
+      <!-- 图片 -->
+      <template v-else-if="contentType === 'image'">
+        <div class="image-container">
+          <img :src="imagePath" alt="课程图片" class="course-image" />
+        </div>
+      </template>
+      <!-- PPT -->
+       <template v-else-if="contentType === 'ppt' || contentType === 'pptx'">
+          <div class="ppt-box">
+            <VueOfficePptx class="ppt-container" :src="pptPath" @error="handlePptError" @rendered="handlePptRendered" />
+          </div>
+       </template>
     </div>
 
-    <!-- 视频切换按钮 -->
+    <!-- 视频切换按钮 - 始终显示 -->
     <div class="video-switch">
       <div class="caret-left" @click="playPreviousVideo">
         <el-button type="warning" round>
-          <img :src="leftImg" alt="Left" />
-          上一节</el-button
+          <img :src="leftImg" alt="Left" />上一节</el-button
         >
       </div>
       <div class="caret-right" @click="playNextVideo">
         <el-button type="warning" round
-          >下一节
-          <img :src="rightImg" alt="Right" />
-        </el-button>
+          >下一节<img :src="rightImg" alt="Right" />
+        </el-button> 
       </div>
     </div>
   </div>
 </template>
 
 <script setup>
-import { ref, onMounted, onBeforeUnmount, defineProps, defineEmits, watch } from 'vue'
+import {
+  ref,
+  onMounted,
+  onBeforeUnmount,
+  defineProps,
+  defineEmits,
+  watch,
+  nextTick
+} from 'vue'
 
 import { videoPlay as Vue3VideoPlay } from 'vue3-video-play'
-// import 'vue3-video-play/dist/style.css'
 import Hls from 'hls.js'
 import { ElMessage } from 'element-plus'
 // 导入全局年级id
@@ -42,12 +63,18 @@ import { globalState } from '@/utils/globalState.js'
 import leftImg from '@/assets/icon/backward.png'
 import rightImg from '@/assets/icon/f-backward.png'
 import { saveRecord } from '@/api/personalized/index.js'
+// PPT
+import  VueOfficePptx  from '@vue-office/pptx'
+// import '@vue-office/pptx/lib/index.css'
 
 // 定义props
 const props = defineProps({
-  videoPath: { type: String, required: true },
+  contentType: { type: String, required: true }, // contentType类型
+  videoPath: { type: String }, // 变为可选
+  imagePath: { type: String }, // 图片路径
+  pptPath: { type: String }, // PPT路径
   courseId: { type: String, required: true },
-  typeId: { type: String, required: true }, // 添加typeId 
+  typeId: { type: String, required: true }, 
   courseConfigList: { type: Array, default: () => [] },
   allIndices: { type: Array, default: () => [] },
   currentIndex: { type: String, required: true }
@@ -74,7 +101,7 @@ const targetProgresses = [10, 50, 100]
 // 定义节流函数
 const throttle = (fn, delay) => {
   let lastCall = 0
-  return function(...args) {
+  return function (...args) {
     const now = Date.now()
     if (now - lastCall >= delay) {
       lastCall = now
@@ -87,11 +114,14 @@ const throttle = (fn, delay) => {
 const saveProgress = throttle(async (progress, currentTime) => {
   try {
     // 保存到localStorage,下次加载视频续播
-    localStorage.setItem(`videoProgress_${props.courseId}`, JSON.stringify({
-      progress: progress,
-      currentTime: currentTime,
-      timestamp: Date.now()
-    }))
+    localStorage.setItem(
+      `videoProgress_${props.courseId}`,
+      JSON.stringify({
+        progress: progress,
+        currentTime: currentTime,
+        timestamp: Date.now()
+      })
+    )
 
     // 保存视频进度接口
     await saveRecord({
@@ -108,11 +138,12 @@ const saveProgress = throttle(async (progress, currentTime) => {
 }, THROTTLE_TIME)
 
 // 处理视频时间更新事件
-const handleTimeUpdate = (ev) => {
+const handleTimeUpdate = ev => {
   if (!videoRef.value) return
   const currentTime = parseInt(ev.target.currentTime)
   const duration = videoRef.value.duration || 0
-  const progressPercentage = duration > 0 ? Math.round((currentTime / duration) * 100) : 0
+  const progressPercentage =
+    duration > 0 ? Math.round((currentTime / duration) * 100) : 0
 
   // 更新最后播放进度
   lastPlayProgress.value = progressPercentage
@@ -135,8 +166,8 @@ const handleTimeUpdate = (ev) => {
   // 触发父组件的时间更新事件
   emits('timeUpdate', { currentTime, progressPercentage })
 
-   if (!props.courseConfigList.length) return
-    props.courseConfigList.forEach(courseCofig => {
+  if (!props.courseConfigList.length) return
+  props.courseConfigList.forEach(courseCofig => {
     //暂停时间
     let time = courseCofig.ccTime
     // 检查是否到达时间点且还未暂停过
@@ -147,7 +178,11 @@ const handleTimeUpdate = (ev) => {
       // 只有当存在问题内容时才触发弹窗
       if (courseCofig.ccQuestContent) {
         // 触发父组件显示试题
-        emits('timeUpdate', { currentTime, progressPercentage, courseConfig: courseCofig })
+        emits('timeUpdate', {
+          currentTime,
+          progressPercentage,
+          courseConfig: courseCofig
+        })
       }
     }
   })
@@ -164,13 +199,26 @@ const handleVideoEnded = () => {
   if (!savedProgress.value.includes(100)) {
     saveProgress(100, videoRef.value.duration)
   }
-  emits('videoEnded')
+  // emits('videoEnded')
+}
+
+// 处理PPT渲染完成事件
+const handlePptRendered = () => {
+  console.log('PPT渲染完成')
+}
+// PPT错误处理事件
+const handlePptError = (error) => {
+  console.error('PPT加载错误:', error)
+  ElMessage.error('PPT加载失败,请检查文件路径或格式')
 }
 
 // 播放下一个视频
 const playNextVideo = () => {
   const currentIndexInList = props.allIndices.indexOf(props.currentIndex)
-  if (currentIndexInList !== -1 && currentIndexInList < props.allIndices.length - 1) {
+  if (
+    currentIndexInList !== -1 &&
+    currentIndexInList < props.allIndices.length - 1
+  ) {
     const nextIndex = props.allIndices[currentIndexInList + 1]
     emits('switchVideo', nextIndex)
     // 重置暂停索引
@@ -191,43 +239,47 @@ const playPreviousVideo = () => {
 
 // 初始化视频播放器
 const initVideoPlayer = () => {
-  if (!videoRef.value) {
-    console.error('视频元素未找到')
-    return
-  }
+  if (props.contentType !== 'video') return
+  // 使用nextTick确保DOM已经更新
+  nextTick(() => {
+    if (!videoRef.value) {
+      console.error('视频元素未找到')
+      return
+    }
 
-  // 清理之前的HLS实例
-  if (hlsRef.value) {
-    hlsRef.value.destroy()
-    hlsRef.value = null
-  }
+    // 清理之前的HLS实例
+    if (hlsRef.value) {
+      hlsRef.value.destroy()
+      hlsRef.value = null
+    }
 
-  // 检查视频路径是否是m3u8格式
-  if (props.videoPath && props.videoPath.toLowerCase().endsWith('.m3u8')) {
-    // 使用HLS播放
-    if (Hls.isSupported()) {
-      hlsRef.value = new Hls()
-      hlsRef.value.loadSource(props.videoPath)
-      hlsRef.value.attachMedia(videoRef.value)
-      hlsRef.value.on(Hls.Events.MANIFEST_PARSED, () => {
+    // 检查视频路径是否是m3u8格式
+    if (props.videoPath && props.videoPath.toLowerCase().endsWith('.m3u8')) {
+      // 使用HLS播放
+      if (Hls.isSupported()) {
+        hlsRef.value = new Hls()
+        hlsRef.value.loadSource(props.videoPath)
+        hlsRef.value.attachMedia(videoRef.value)
+        hlsRef.value.on(Hls.Events.MANIFEST_PARSED, () => {
+          tryPlayVideo()
+        })
+        hlsRef.value.on(Hls.Events.ERROR, (event, data) => {
+          console.error('HLS错误:', data)
+          ElMessage.error('视频加载失败,请稍后重试')
+        })
+      } else if (videoRef.value.canPlayType('application/vnd.apple.mpegurl')) {
+        // 对于不支持HLS但支持原生m3u8的浏览器
+        videoRef.value.src = props.videoPath
         tryPlayVideo()
-      })
-      hlsRef.value.on(Hls.Events.ERROR, (event, data) => {
-        console.error('HLS错误:', data)
-        ElMessage.error('视频加载失败,请稍后重试')
-      })
-    } else if (videoRef.value.canPlayType('application/vnd.apple.mpegurl')) {
-      // 对于不支持HLS但支持原生m3u8的浏览器
+      } else {
+        ElMessage.error('您的浏览器不支持播放m3u8格式视频')
+      }
+    } else {
+      // 普通视频播放
       videoRef.value.src = props.videoPath
       tryPlayVideo()
-    } else {
-      ElMessage.error('您的浏览器不支持播放m3u8格式视频')
     }
-  } else {
-    // 普通视频播放
-    videoRef.value.src = props.videoPath
-    tryPlayVideo()
-  }
+  })
 }
 
 // 尝试播放视频,处理浏览器自动播放限制
@@ -242,13 +294,6 @@ const tryPlayVideo = () => {
   setTimeout(() => {
     setLastPlayPosition()
   }, 1000)
-
-  // const playPromise = videoRef.value.play()
-  // if (playPromise !== undefined) {
-  //   playPromise.catch(error => {
-  //     console.error('视频播放失败,可能是浏览器自动播放限制:', error)
-  //   })
-  // }
 }
 
 // 在视频加载完成后设置上次播放进度
@@ -277,9 +322,12 @@ onMounted(() => {
   initVideoPlayer()
 })
 
-// 监听videoPath变化
-watch(() => props.videoPath, () => {
-  initVideoPlayer()
+// 监听contentType和videoPath变化
+watch([() => props.contentType, () => props.videoPath], () => {
+  // 当contentType变为video或videoPath变化时,重新初始化
+  if (props.contentType === 'video') {
+    initVideoPlayer()
+  }
 })
 
 // 组件卸载时
@@ -304,7 +352,7 @@ onBeforeUnmount(() => {
   display: flex;
   justify-content: center;
   align-items: center;
-  .d-player-wrap{
+  .d-player-wrap {
     height: rpx(289);
     width: 68.5%;
     border-radius: rpx(12);
@@ -313,9 +361,45 @@ onBeforeUnmount(() => {
 }
 .full-box-video {
   width: 70%;
+  height: 100%;
   object-fit: cover;
   border-radius: rpx(12);
 }
+.ppt-box{
+   width: 100%;
+  height: rpx(300);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.ppt-container {
+  width: 70%;
+  height: 100%;
+  border-radius: rpx(12);
+  overflow: hidden; 
+}
+
+.ppt-container ::v-deep(.pptx-preview-wrapper) {
+  // 滚动条整体样式
+  &::-webkit-scrollbar {
+    width: rpx(0); // 滚动条宽度
+    height: rpx(0);
+  }
+
+  // 滚动条滑块样式
+  &::-webkit-scrollbar-thumb {
+    background-color: #e2ddfc; // 滑块颜色
+    border-radius: rpx(4); // 滑块圆角
+  }
+
+  // 滚动条轨道样式
+  &::-webkit-scrollbar-track {
+    background-color: rgba(143, 116, 255, 0.2); // 轨道颜色
+    border-radius: rpx(4); // 轨道圆角
+  }
+  // border-radius: rpx(12);
+}
+
 /* 隐藏 Chrome 视频控件的渐变背景等默认样式 */
 video::-webkit-media-controls-panel {
   background: transparent !important; /* 去掉背景渐变,设为透明 */
@@ -327,7 +411,19 @@ video::-webkit-media-controls-panel {
   margin-top: rpx(5);
   margin-bottom: rpx(15);
 }
-
+.image-container {
+  width: 100%;
+  height: rpx(300);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.course-image {
+  max-width: 70%;
+  max-height: rpx(289);
+  border-radius: rpx(12);
+  object-fit: contain;
+}
 .caret-right,
 .caret-left {
   width: rpx(50);
@@ -351,4 +447,4 @@ video::-webkit-media-controls-panel {
 .caret-left img {
   width: rpx(12);
 }
-</style>
+</style>

+ 41 - 7
src/views/AIDevelop.vue

@@ -6,7 +6,7 @@
       class="icon-expand"
       :style="{
         backgroundColor: drawerVisible ? '#44449c' : '#7F70C840',
-        left: drawerVisible ? '18%' : '0'
+        left: drawerVisible ? '18%' : '0',
       }"
       @click="toggleDrawer"
     >
@@ -104,10 +104,12 @@
         <div class="small-title">
           <span>{{ course.courseName }}</span>
         </div>
-
-        <!-- 视频播放器组件 -->
+        <!-- 视频组件 -->
         <VideoPlayer
+          :contentType="course.courseContentType"
           :videoPath="course.courseVideoPath"
+          :imagePath="course.courseImagePath"
+          :pptPath="course.pptPath"
           :courseId="course.id || ''"
           :typeId="typeId"
           :courseConfigList="course.courseConfigList || []"
@@ -257,6 +259,16 @@ const handleVideoEnded = () => {
     )
   }
 
+  // 如果是图片类型,不需要自动播放下一个
+  if (course.value.courseContentType === 'video') {
+    const allIndices = flattenMenuItems()
+    const currentIndexInList = allIndices.indexOf(course.value.key)
+    if (currentIndexInList !== -1 && currentIndexInList < allIndices.length - 1) {
+      const nextIndex = allIndices[currentIndexInList + 1]
+      handleSelect(nextIndex)
+    }
+  }
+
   const allIndices = flattenMenuItems()
   const currentIndexInList = allIndices.indexOf(course.value.key)
   if (currentIndexInList !== -1 && currentIndexInList < allIndices.length - 1) {
@@ -382,7 +394,7 @@ const getAllCourseSections = () => {
 
 // 渲染 课程数据结构 以及 视频
 onMounted(async () => {
-  const typeIdParam = router.currentRoute.value.query.typeId
+   const typeIdParam = router.currentRoute.value.query.typeId
   if (typeIdParam) {
     typeId.value = typeIdParam
     try {
@@ -406,6 +418,14 @@ onMounted(async () => {
           title: courseTemp.courseName
         }
 
+        // 手动修改第一个课程为image类型用于测试
+        if (index === 0) {
+          courseTemp.courseContentType = 'ppt';
+          courseTemp.pptPath = 'http://59.110.91.129:8088/admin-api/infra/file/29/get/20250820/ppt_1755654972861.pptx';
+          // 可选:修改课程名称以便识别
+          courseTemp.courseName = '测试PPT';
+        }
+
         if (topName === courseTemp.courseLabel) {
           let topMenu = menuItems.value[menuItems.value.length - 1]
           let topMenuChildren = topMenu.children;
@@ -429,14 +449,12 @@ onMounted(async () => {
                 }
               ]
             }
-
             menuItems.value[menuItems.value.length-1] = menu
           }
         }else{
           menuItems.value.push(menu)
         }
         topName = courseTemp.courseLabel
-
         courseTemp['key'] = menuIndex
         videoPathMap.value[menuIndex] = courseTemp
 
@@ -511,7 +529,6 @@ $text-color: #483d8b; // 文本颜色:靛蓝色
   z-index: 9999;
   position: absolute;
   top: 50%;
-  left: 18%;
   transform: translateY(-50%);
   cursor: pointer; // 添加鼠标指针样式
   clip-path: polygon(0 0, 100% 15%, 100% 85%, 0 100%);
@@ -765,6 +782,23 @@ $text-color: #483d8b; // 文本颜色:靛蓝色
   font-size: rpx(10);
   justify-content: center; //使子元素水平居中
 }
+// 图片容器样式
+.image-container {
+  width: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: rpx(20) 0;
+}
+
+// 图片样式
+.course-image {
+  max-width: 70%;
+  max-height: rpx(400);
+  object-fit: contain;
+  border-radius: rpx(12);
+  box-shadow: 0 rpx(10) rpx(20) rgba(0, 0, 0, 0.1);
+}
 
 
 // 儿童风格试题弹框样式

+ 2 - 1
src/views/AIPainting.vue

@@ -436,6 +436,7 @@ const inProgressTimerFun = () => {
   width: rpx(135);
   height: 100%;
   background: linear-gradient(to bottom, #001169, #8a78d0);
+
 }
 .home-container {
   position: fixed;
@@ -457,7 +458,7 @@ const inProgressTimerFun = () => {
 .left-group {
   width: rpx(135);
   height: 100%;
-  background: linear-gradient(to bottom, #001169, #b4a8e1);
+  background: linear-gradient(to bottom, #001169, #8a78d0);
 }
 .mb-2 {
   color: black;

+ 3 - 2
src/views/personalized/Personalized.vue

@@ -296,7 +296,7 @@ onMounted(async()=>{
 .demo-progress {
   padding-left: rpx(10);
   padding-right: rpx(10);
-  padding-bottom: rpx(3);
+  padding-bottom: rpx(1);
 }
 
 .demo-progress .el-progress--line,
@@ -305,6 +305,7 @@ onMounted(async()=>{
 }
 
 ::v-deep(.el-progress-bar__outer) {
+  height: rpx(10);
   background-color: #fff;
 }
 
@@ -323,7 +324,7 @@ onMounted(async()=>{
 .progress-desc {
   font-size: rpx(8);
   color: black;
-  margin-bottom: rpx(0);
+  margin-bottom: rpx(3);
   text-align: left;
 }