瀏覽代碼

添加播放

lex-xin 8 月之前
父節點
當前提交
5e5374f94e
共有 36 個文件被更改,包括 647 次插入109 次删除
  1. 5 0
      src/App.vue
  2. 8 0
      src/api/coursewarePlay.api.ts
  3. 3 0
      src/assets/variables.scss
  4. 8 0
      src/businessComponents/globalTools/globalTools.ts
  5. 107 0
      src/businessComponents/globalTools/globalTools.vue
  6. 2 0
      src/businessComponents/globalTools/index.ts
  7. 36 14
      src/components/ellipsisScroll/ellipsisScroll.vue
  8. 二進制
      src/img/coursewarePlay/book-cover-line.png
  9. 二進制
      src/img/coursewarePlay/goPracticeBtn.png
  10. 二進制
      src/img/coursewarePlay/jieshu.png
  11. 二進制
      src/img/coursewarePlay/kcml.png
  12. 二進制
      src/img/coursewarePlay/menu.png
  13. 二進制
      src/img/coursewarePlay/menuActive.png
  14. 二進制
      src/img/coursewarePlay/shang.png
  15. 二進制
      src/img/coursewarePlay/ts3.png
  16. 二進制
      src/img/coursewarePlay/ts4.png
  17. 二進制
      src/img/coursewarePlay/xia.png
  18. 二進制
      src/img/coursewarePlay/zhishidian.png
  19. 二進制
      src/img/layout/g-arrow-right.png
  20. 二進制
      src/img/layout/icon-note.png
  21. 二進制
      src/img/layout/icon-tool.png
  22. 二進制
      src/img/layout/icon-whiteboard.png
  23. 2 2
      src/views/cloudPractice/cloudPractice.tsx
  24. 86 0
      src/views/coursewarePlay/components/courseMenuCollapse/courseMenuCollapse.vue
  25. 2 0
      src/views/coursewarePlay/components/courseMenuCollapse/index.ts
  26. 4 4
      src/views/coursewarePlay/components/playRecordTime/playRecordTime.vue
  27. 127 0
      src/views/coursewarePlay/components/useCoursewarePlayTip/coursewarePlayTip.vue
  28. 39 0
      src/views/coursewarePlay/components/useCoursewarePlayTip/index.ts
  29. 178 63
      src/views/coursewarePlay/coursewarePlay.vue
  30. 1 1
      src/views/coursewarePlay/index.ts
  31. 二進制
      src/views/coursewarePlay/videoPlay/img/iconLoop.png
  32. 二進制
      src/views/coursewarePlay/videoPlay/img/iconLoopActive.png
  33. 二進制
      src/views/coursewarePlay/videoPlay/img/iconPause.png
  34. 二進制
      src/views/coursewarePlay/videoPlay/img/iconPlay.png
  35. 二進制
      src/views/coursewarePlay/videoPlay/img/iconSpeed.png
  36. 39 25
      src/views/coursewarePlay/videoPlay/videoPlay.vue

+ 5 - 0
src/App.vue

@@ -1,5 +1,10 @@
 <template>
    <router-view />
+   <globalTools />
 </template>
 
+<script lang="ts" setup>
+import globalTools from "@/businessComponents/globalTools"
+</script>
+
 <style lang="scss"></style>

+ 8 - 0
src/api/coursewarePlay.api.ts

@@ -31,3 +31,11 @@ export const checkWebCourse_gyt = (id: string) => {
       url: `/api-teacher/courseSchedule/checkWebCourse/` + id
    })
 }
+// 查询关联课程
+export const refLevel_gyt = (data: string) => {
+   return httpAxios_gyt.axioseRquest({
+      method: "post",
+      url: `/api-teacher/lessonCoursewareDetail/refLevel`,
+      params: data
+   })
+}

+ 3 - 0
src/assets/variables.scss

@@ -58,3 +58,6 @@ $box-shadow-lighter: var(--el-box-shadow-lighter);
 
 /* 透明度 */
 $opacity-hover: 0.8;
+
+/* 禁用 */
+$opacity-disabled: 0.4;

+ 8 - 0
src/businessComponents/globalTools/globalTools.ts

@@ -0,0 +1,8 @@
+import { ref } from "vue"
+
+/** 工具栏状态 */
+export const toolOpen = ref(false)
+// 批注
+export const penShow = ref(false)
+// 白板
+export const whitePenShow = ref(false)

+ 107 - 0
src/businessComponents/globalTools/globalTools.vue

@@ -0,0 +1,107 @@
+<!--
+* @FileDescription: 公用工具 - 【白板/批注】
+* @Author: 王新雷
+* @Date:2024年10月14日10:03:11
+-->
+<template>
+   <div class="globalTools">
+      <div :class="['iconTools', toolOpen ? 'hideTools' : '']" @click="openTool">
+         <img src="@/img/layout/icon-tool.png" />
+      </div>
+
+      <div :class="['expendTools', toolOpen ? 'showTools' : '']">
+         <img @click="openType('note')" src="@/img/layout/icon-note.png" />
+         <img @click="openType('whiteboard')" class="iconWhiteboard" src="@/img/layout/icon-whiteboard.png" />
+         <img @click="openTool" class="iconArrow" src="@/img/layout/g-arrow-right.png" />
+      </div>
+
+      <pen
+         :close="
+            () => {
+               penShow = false
+            }
+         "
+         v-model="penShow"
+      />
+      <pen
+         :is-white="true"
+         :close="
+            () => {
+               whitePenShow = false
+            }
+         "
+         v-model="whitePenShow"
+      />
+   </div>
+</template>
+
+<script setup lang="ts">
+import pen from "@/views/coursewarePlay/components/pen"
+import { toolOpen, whitePenShow, penShow } from "./globalTools"
+
+function openTool() {
+   toolOpen.value = !toolOpen.value
+}
+
+function openType(type: "note" | "whiteboard") {
+   console.log(type, "type")
+   if (type === "note") {
+      penShow.value = true
+   } else if (type === "whiteboard") {
+      whitePenShow.value = true
+   }
+}
+</script>
+
+<style lang="scss" scoped>
+.globalTools {
+   .iconTools,
+   .expendTools {
+      position: fixed;
+      right: 0;
+      top: 50%;
+      transform: translateY(-50%);
+      z-index: 99;
+      padding: 8px 14px 8px 16px;
+      background: rgba(0, 0, 0, 0.4);
+      border-radius: 200px 0px 0px 200px;
+      border: 2px solid rgba(255, 255, 255, 0.3);
+      border-right-width: 0;
+      cursor: pointer;
+      transition: transform 0.2s ease;
+      img {
+         width: 34px;
+         height: 34px;
+      }
+   }
+
+   .iconTools {
+      transition-delay: 0.2s;
+   }
+
+   .expendTools {
+      transform: translate(100%, -50%);
+      img {
+         cursor: pointer;
+      }
+      .iconWhiteboard {
+         margin: 0 30px;
+      }
+      .iconArrow {
+         width: 28px;
+         height: 28px;
+      }
+   }
+
+   .hideTools {
+      transition: transform 0.2s ease;
+      transform: translate(100%, -50%);
+   }
+
+   .showTools {
+      transform: translateY(-50%);
+      transition: transform 0.2s ease;
+      transition-delay: 0.2s;
+   }
+}
+</style>

+ 2 - 0
src/businessComponents/globalTools/index.ts

@@ -0,0 +1,2 @@
+import globalTools from "./globalTools.vue"
+export default globalTools

+ 36 - 14
src/components/ellipsisScroll/ellipsisScroll.vue

@@ -4,28 +4,37 @@
 * @Date:2024-03-25 11:50:36
 -->
 <template>
-   <div ref="ellipsisScrollDom" :class="{ isScroll: isScroll }" class="ellipsisScroll">
+   <div ref="ellipsisScrollDom" :class="[isScroll && 'isScroll', isScroll && props.autoScroll && 'autoScroll']" class="ellipsisScroll">
       {{ props.title }}
    </div>
 </template>
 
 <script setup lang="ts">
-import { ref, onMounted, onUnmounted } from "vue"
+import { ref, onMounted, onUnmounted, nextTick } from "vue"
 
 const props = defineProps<{
    title: string
+   autoScroll?: boolean
 }>()
 
 const ellipsisScrollDom = ref<HTMLElement>()
 const isScroll = ref(false)
 
 onMounted(() => {
-   ellipsisScrollDom.value?.addEventListener("mouseenter", handleIsScroll)
-   ellipsisScrollDom.value?.addEventListener("mouseleave", handleLeaveScroll)
+   if (props?.autoScroll) {
+      nextTick(() => {
+         handleAutoScroll(ellipsisScrollDom.value)
+      })
+   } else {
+      ellipsisScrollDom.value?.addEventListener("mouseenter", handleIsScroll)
+      ellipsisScrollDom.value?.addEventListener("mouseleave", handleLeaveScroll)
+   }
 })
 onUnmounted(() => {
-   ellipsisScrollDom.value?.removeEventListener("mouseenter", handleIsScroll)
-   ellipsisScrollDom.value?.removeEventListener("mouseleave", handleLeaveScroll)
+   if (!props?.autoScroll) {
+      ellipsisScrollDom.value?.removeEventListener("mouseenter", handleIsScroll)
+      ellipsisScrollDom.value?.removeEventListener("mouseleave", handleLeaveScroll)
+   }
 })
 
 let widthCalc = 0
@@ -38,6 +47,14 @@ function handleIsScroll(e: MouseEvent) {
       isScroll.value = false
    }
 }
+function handleAutoScroll(target: any) {
+   widthCalc = target.scrollWidth - target.clientWidth
+   if (widthCalc > 0) {
+      isScroll.value = true
+   } else {
+      isScroll.value = false
+   }
+}
 function handleLeaveScroll() {
    isScroll.value = false
 }
@@ -49,20 +66,25 @@ function handleLeaveScroll() {
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
+   @keyframes roll {
+      0% {
+         transform: translateX(0);
+      }
+      100% {
+         transform: translateX(-100%);
+      }
+   }
    &.isScroll {
       &:hover {
          width: auto;
          overflow: initial;
          animation: 3s roll linear infinite normal;
       }
-      @keyframes roll {
-         0% {
-            transform: translateX(0);
-         }
-         100% {
-            transform: translateX(-100%);
-         }
-      }
+   }
+   &.autoScroll {
+      width: auto;
+      overflow: initial;
+      animation: 3s roll linear infinite normal;
    }
 }
 </style>

二進制
src/img/coursewarePlay/book-cover-line.png


二進制
src/img/coursewarePlay/goPracticeBtn.png


二進制
src/img/coursewarePlay/jieshu.png


二進制
src/img/coursewarePlay/kcml.png


二進制
src/img/coursewarePlay/menu.png


二進制
src/img/coursewarePlay/menuActive.png


二進制
src/img/coursewarePlay/shang.png


二進制
src/img/coursewarePlay/ts3.png


二進制
src/img/coursewarePlay/ts4.png


二進制
src/img/coursewarePlay/xia.png


二進制
src/img/coursewarePlay/zhishidian.png


二進制
src/img/layout/g-arrow-right.png


二進制
src/img/layout/icon-note.png


二進制
src/img/layout/icon-tool.png


二進制
src/img/layout/icon-whiteboard.png


+ 2 - 2
src/views/cloudPractice/cloudPractice.tsx

@@ -858,8 +858,8 @@ export default defineComponent({
       // // 多个文件下载
       const downLoadMultiFile = (files: any, filesName: string) => {
          const zip = new JSZip()
-         const result = []
-         console.log(files)
+         // const result = []
+         // console.log(files)
          for (const i in files) {
             zip.file(files[i].name, files[i].url, { binary: true })
          }

+ 86 - 0
src/views/coursewarePlay/components/courseMenuCollapse/courseMenuCollapse.vue

@@ -0,0 +1,86 @@
+<template>
+   <div class="courseMenuCollapse">
+      <div class="courseMenuItem">
+         <div class="cover">
+            <img />
+            <div class="current">当前</div>
+         </div>
+         <div class="text">
+            <ellipsisScroll :autoScroll="true" :title="'长笛Level1长笛Level1'" />
+         </div>
+      </div>
+      <div class="courseMenuItem">
+         <div class="cover">
+            <img />
+            <div class="current">当前</div>
+         </div>
+         <div class="text">
+            <ellipsisScroll :title="'长笛Level1长笛Level1长笛Level1长笛Level1'" />
+         </div>
+      </div>
+      <div class="courseMenuItem">
+         <div class="cover">
+            <img />
+            <div class="current">当前</div>
+         </div>
+         <div class="text">长笛Level1</div>
+      </div>
+   </div>
+</template>
+
+<script lang="ts" setup>
+// import Ellipsis from "@/components/ellipsis"
+// import EllipsisScroll from "@/components/ellipsisScroll"
+</script>
+
+<style lang="scss" scoped>
+.courseMenuCollapse {
+   display: flex;
+   flex-wrap: wrap;
+
+   .courseMenuItem {
+      width: 89px;
+      margin-top: 24px;
+      margin-left: 24px;
+      box-sizing: content-box;
+      cursor: pointer;
+      .cover {
+         position: relative;
+         width: 100%;
+         height: 110px;
+         &::before {
+            position: absolute;
+            top: 0;
+            left: 0;
+            bottom: 0;
+            z-index: 9;
+            content: "";
+            width: 4px;
+            height: 100%;
+            background: url("@/img/coursewarePlay/book-cover-line.png");
+         }
+         .current {
+            position: absolute;
+            top: 4px;
+            left: 4px;
+            z-index: 9;
+            background: rgba(0, 0, 0, 0.5);
+            border-radius: 3px;
+            font-weight: 500;
+            font-size: 13px;
+            color: #ffffff;
+            line-height: 17px;
+            padding: 0 5px;
+         }
+      }
+      .text {
+         padding-top: 6px;
+         font-weight: 600;
+         font-size: 14px;
+         color: rgba(0, 0, 0, 0.5);
+         line-height: 20px;
+         overflow: hidden;
+      }
+   }
+}
+</style>

+ 2 - 0
src/views/coursewarePlay/components/courseMenuCollapse/index.ts

@@ -0,0 +1,2 @@
+import courseMenuCollapse from "./courseMenuCollapse.vue"
+export default courseMenuCollapse

+ 4 - 4
src/views/coursewarePlay/components/playRecordTime/playRecordTime.vue

@@ -56,12 +56,12 @@ function handleCoursewarePlayTime(id: string, time: number) {
 
 <style lang="scss" scoped>
 .playRecordTime {
-   margin-left: 20px;
+   // margin-left: 20px;
    display: flex;
    align-items: center;
-   padding: 14px 16px;
+   padding: 10px 16px;
    background: rgba(100, 100, 100, 0.5);
-   border-radius: 20px;
+   border-radius: 28px;
    .drop {
       margin-right: 12px;
       width: 8px;
@@ -85,7 +85,7 @@ function handleCoursewarePlayTime(id: string, time: number) {
    }
    .time {
       font-weight: 400;
-      font-size: 20px;
+      font-size: 18px;
       color: #ffffff;
    }
 }

+ 127 - 0
src/views/coursewarePlay/components/useCoursewarePlayTip/coursewarePlayTip.vue

@@ -0,0 +1,127 @@
+<!--
+* @FileDescription: 提示确认类型弹窗
+* @Author: 王新雷
+* @Date:2024年10月14日17:57:39
+-->
+<template>
+   <div class="dialogConfirm">
+      <div class="close" @click="close"></div>
+      <img class="headImg" v-if="props.modalData.headImg" :src="props.modalData.headImg" />
+      <div class="textCon">
+         <div class="text" v-html="props.modalData.text"></div>
+      </div>
+      <div v-if="filterBtnShow" class="dialogBtn">
+         <img v-if="props.modalData.btnShow[1]" @click="cancel" src="@/img/useDialogConfirm/cancel.png" />
+         <img v-if="props.modalData.btnShow[0]" @click="ok" src="@/img/useDialogConfirm/ok.png" />
+      </div>
+   </div>
+</template>
+
+<script setup lang="ts">
+import { computed } from "vue"
+
+const props = defineProps<{
+   modalData: {
+      text: string
+      btnShow: [boolean?, boolean?]
+      headImg?: string
+   }
+}>()
+
+const emits = defineEmits<{
+   (e: "onClose"): void
+   (e: "onCancel"): void
+   (e: "onOk"): void
+}>()
+
+const filterBtnShow = computed(() => {
+   const { btnShow } = props.modalData
+   return !!(btnShow[0] || btnShow[1])
+})
+function close() {
+   emits("onClose")
+}
+function cancel() {
+   emits("onCancel")
+}
+function ok() {
+   emits("onOk")
+}
+</script>
+
+<style lang="scss" scoped>
+.dialogConfirm {
+   padding-top: 36px;
+   padding-bottom: 22px;
+   width: 100%;
+   height: 100%;
+   display: flex;
+   flex-direction: column;
+   .close {
+      position: absolute;
+      top: -14px;
+      right: -16px;
+      width: 42px;
+      height: 44px;
+      cursor: pointer;
+      background: url("@/img/useDialogConfirm/close.png") no-repeat;
+      background-size: cover;
+      &:hover {
+         background: url("@/img/useDialogConfirm/closeHover.png") no-repeat;
+         background-size: cover;
+      }
+   }
+   .headImg {
+      width: 163px;
+      height: 51px;
+      position: absolute;
+      left: 50%;
+      top: -13px;
+      transform: translate(-50%, 0);
+   }
+   .textCon {
+      flex-grow: 1;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding: 0 18px 0 22px;
+      overflow: hidden;
+      .text {
+         font-size: 20px;
+         color: #a04d11;
+         line-height: 32px;
+      }
+   }
+   .dialogBtn {
+      flex-shrink: 0;
+      display: flex;
+      justify-content: center;
+      margin-top: 6px;
+      > img {
+         width: 170px;
+         height: 51px;
+         cursor: pointer;
+         margin-left: 12px;
+         &:hover {
+            opacity: $opacity-hover;
+         }
+      }
+   }
+}
+</style>
+<style lang="scss">
+.h-modalFrame.usecoursewarePlayTip {
+   /* prettier-ignore */
+   --modalFrameTitHeight: 0PX;
+   .modalFrameBox {
+      background: linear-gradient(180deg, #fdfef2 0%, #fdf6de 100%);
+      box-shadow: 0px 5px 0px 0px #ca8e2d, 0px 7px 10px 0px #6e6e6e;
+      border-radius: 20px;
+      border: 8px solid #ffdd98;
+      position: relative;
+      .modalFrameTitle {
+         display: none;
+      }
+   }
+}
+</style>

+ 39 - 0
src/views/coursewarePlay/components/useCoursewarePlayTip/index.ts

@@ -0,0 +1,39 @@
+/*
+   提示确认类型弹窗
+ */
+
+import modalFrame from "@/plugin/modalFrame"
+import coursewarePlayTip from "./coursewarePlayTip.vue"
+
+/**
+ * @param obj
+ *  text :文字
+ *  btnShow:控制确认按钮显示
+ *  headImg:头部图片
+ */
+type objType = {
+   text: string
+   btnShow?: [boolean?, boolean?]
+   headImg?: string
+   onCancel?: (...nargs: any[]) => void
+   onOk?: (...nargs: any[]) => void
+   onClose?: (...nargs: any[]) => void
+}
+export default ({ text, onCancel, onOk, onClose, btnShow, headImg }: objType) => {
+   modalFrame({
+      maskClose: true,
+      template: coursewarePlayTip,
+      width: 740,
+      height: 470,
+      btnShow: [],
+      modalData: {
+         text,
+         btnShow: btnShow || [true, true],
+         headImg
+      },
+      className: "usecoursewarePlayTip",
+      onCancel,
+      onOk,
+      onClose
+   })
+}

+ 178 - 63
src/views/coursewarePlay/coursewarePlay.vue

@@ -22,40 +22,47 @@
          </div>
       </div>
       <div class="leftTools posTools">
-         <div v-if="activeCoursewareIndex > 0" class="posBtn" @click="handleChangeCourseware(-1)">
-            <img src="@/img/coursewarePlay/shang.png" />
-            <div>上一个</div>
+         <div class="posBtn" @click="drawerMenuShow = true">
+            <img src="@/img/coursewarePlay/menu.png" />
+            <!-- <div>课程类型</div> -->
          </div>
-         <div v-if="activeCoursewareIndex < flattenCoursewareList.length - 1" class="posBtn" @click="handleChangeCourseware(1)">
-            <img src="@/img/coursewarePlay/xia.png" />
-            <div>下一个</div>
+         <div class="posBtn" @click="drawerShow = true">
+            <img src="@/img/coursewarePlay/zhishidian.png" />
+            <!-- <div>知识点</div> -->
          </div>
-      </div>
-      <div class="rightTools posTools">
          <div
-            class="posBtn"
+            :class="['posBtn', activeCoursewareIndex > 0 ? '' : 'disabled']"
             @click="
                () => {
-                  handleVideoPause()
-                  whitePenShow = true
+                  if (activeCoursewareIndex > 0) {
+                     handleChangeCourseware(-1)
+                  }
                }
             "
          >
-            <img src="@/img/coursewarePlay/baiban.png" />
-            <div>白板</div>
+            <img src="@/img/coursewarePlay/shang.png" />
+            <!-- <div>上一个</div> -->
          </div>
          <div
-            class="posBtn"
+            :class="['posBtn', activeCoursewareIndex < flattenCoursewareList.length - 1 ? '' : 'disabled']"
             @click="
                () => {
-                  handleVideoPause()
-                  penShow = true
+                  if (activeCoursewareIndex < flattenCoursewareList.length - 1) {
+                     handleChangeCourseware(1)
+                  }
                }
             "
          >
-            <img src="@/img/coursewarePlay/pizhu.png" />
-            <div>批注</div>
+            <img src="@/img/coursewarePlay/xia.png" />
+            <!-- <div>下一个</div> -->
          </div>
+
+         <!-- <div class="posBtn" @click="handleCoursewareEnd">
+            <img src="@/img/coursewarePlay/jieshu.png" />
+            <div>结束</div>
+         </div> -->
+      </div>
+      <!-- <div class="rightTools posTools">
          <div class="posBtn" @click="drawerShow = true">
             <img src="@/img/coursewarePlay/zhishidian.png" />
             <div>知识点</div>
@@ -64,7 +71,7 @@
             <img src="@/img/coursewarePlay/jieshu.png" />
             <div>结束</div>
          </div>
-      </div>
+      </div> -->
       <div
          v-if="activeCoursewareResourceId"
          @click="
@@ -78,14 +85,28 @@
       <div class="topTools">
          <div class="leftMenu">
             <img @click="handleGoBack" class="backImg" src="@/img/coursewarePlay/back.png" />
+
+            <div class="title-section">
+               <div class="title">{{ activeCourseware?.parentData.name || "" }}</div>
+               <div class="content">
+                  {{ activeCourseware?.name || "" }}
+                  <span v-if="!activeCourseware?.phaseGoals" @click="onTitleTip('phaseGoals', activeCourseware?.phaseGoals)">阶段目标</span>
+                  <span v-if="!activeCourseware?.checkItem" @click="onTitleTip('checkItem', activeCourseware?.checkItem)">检查事项</span>
+               </div>
+            </div>
+         </div>
+         <div class="midMenu">
             <playRecordTime
                v-if="route.query.modeId && coursewareTotalTime && userStoreHook.roles === 'GYT'"
                :modeId="route.query.modeId as string"
                :coursewareTotalTime="coursewareTotalTime"
             />
          </div>
-         <div class="midMenu">{{ activeCourseware?.parentData.name || "" }}</div>
-         <div class="rightMenu"></div>
+         <div class="rightMenu">
+            <div class="posCloseBtn" @click="handleCoursewareEnd">
+               <img src="@/img/coursewarePlay/jieshu.png" />
+            </div>
+         </div>
       </div>
       <el-drawer class="elDrawer" v-model="drawerShow" :show-close="false">
          <template #header="{ close }">
@@ -97,23 +118,16 @@
             <courseCollapse :activeCollapse="activeCourseware" :courseList="coursewareList" @handleClick="handleCourseClick" />
          </ElScrollbar>
       </el-drawer>
-      <pen
-         :close="
-            () => {
-               penShow = false
-            }
-         "
-         v-model="penShow"
-      />
-      <pen
-         :is-white="true"
-         :close="
-            () => {
-               whitePenShow = false
-            }
-         "
-         v-model="whitePenShow"
-      />
+      <el-drawer class="elDrawer elCourseMenu" v-model="drawerMenuShow" :show-close="false">
+         <template #header="{ close }">
+            <img class="directory" src="@/img/coursewarePlay/menuActive.png" />
+            <div class="tit">课程目录</div>
+            <img class="close" @click="close" src="@/img/coursewarePlay/close.png" />
+         </template>
+         <ElScrollbar class="elScrollbar">
+            <courseMenuCollapse :courseMenuList="coursewareMenuList" @handleClick="handleCourseMenuClick" />
+         </ElScrollbar>
+      </el-drawer>
       <practiceForm v-model="isPracticeShow" :practiceUrl="practiceUrl" @close="handlePracticeClose" />
    </div>
 </template>
@@ -121,31 +135,28 @@
 <script setup lang="ts">
 import videoPlay from "./videoPlay"
 import { getLessonCourseDetail_gym, getLessonCoursewareDetail_gyt, getLessonCourseDetail_klx } from "@/api/cloudTextbooks.api"
-import { checkWebCourse_gyt } from "@/api/coursewarePlay.api"
+import { checkWebCourse_gyt, refLevel_gyt } from "@/api/coursewarePlay.api"
 import { httpAjaxErrMsg, httpAjaxLoadingErrMsg } from "@/plugin/httpAjax"
 import userStore from "@/store/modules/user"
 import { useRoute, useRouter } from "vue-router"
 import { shallowRef, ref, computed, onUnmounted, onMounted, watch, nextTick } from "vue"
 import { ElMessageBox } from "element-plus"
 import courseCollapse from "./components/courseCollapse"
-import pen from "./components/pen"
+import courseMenuCollapse from "./components/courseMenuCollapse"
 import playRecordTime from "./components/playRecordTime"
 import practiceForm from "@/businessComponents/practiceForm"
-import useDialogConfirm from "@/hooks/useDialogConfirm"
 import { getRecentCourseSchedule_gym } from "@/api/homePage.api"
 import { getToken } from "@/libs/auth"
 import { URL_TEACH_GYT, URL_TEACH_GYM, URL_TEACH_KLX } from "@/config"
 import { handleFullscreen } from "@/libs/fullscreen"
+import useCoursewarePlayTip from "./components/useCoursewarePlayTip"
 
 const route = useRoute()
 const router = useRouter()
 const userStoreHook = userStore()
-// 批注
-const penShow = ref(false)
-// 白板
-const whitePenShow = ref(false)
 /* 获取资源 */
 const videoPlayDom = ref<InstanceType<typeof videoPlay>>()
+const coursewareMenuList = shallowRef<any[]>([]) // 课程类型
 const coursewareList = shallowRef<any[]>([]) // 知识点
 const flattenCoursewareList = shallowRef<any[]>([]) // 扁平化coursewareList
 // 选中的知识点
@@ -174,6 +185,7 @@ const songPlaySrc = computed<string>(() => {
 })
 const activeCoursewareIndex = ref(0)
 const drawerShow = ref(false)
+const drawerMenuShow = ref(false)
 // 课程总时间
 const coursewareTotalTime = ref(0)
 // 监控播放
@@ -230,6 +242,51 @@ function getCoursewareList() {
       }
    })
 }
+// getCoursewareMenuList()
+function getCoursewareMenuList() {
+   //  GYM,GYT,KLX 区分   查询接口
+   const LessonCoursewareMenuDetailApi = {
+      GYT: refLevel_gyt,
+      GYM: getLessonCourseDetail_gym,
+      KLX: getLessonCourseDetail_klx
+   }
+   httpAjaxErrMsg(LessonCoursewareMenuDetailApi[userStoreHook.roles!], {
+      lessonCoursewareDetailId: "1689564396456579073",
+      courseScheduleId: "1844948199117283329"
+   } as any).then(res => {
+      if (res.code === 200) {
+         const { lockFlag, knowledgePointList } = res.data || {}
+         if (lockFlag) {
+            ElMessageBox.alert("课件已锁定", "温馨提示", {
+               confirmButtonText: "退出",
+               type: "error"
+            })
+               .then(() => {
+                  handleGoBack()
+               })
+               .catch(() => {
+                  handleGoBack()
+               })
+            return
+         }
+         if ((knowledgePointList || []).length < 1) {
+            ElMessageBox.alert("没有找到课件", "温馨提示", {
+               confirmButtonText: "退出",
+               type: "error"
+            })
+               .then(() => {
+                  handleGoBack()
+               })
+               .catch(() => {
+                  handleGoBack()
+               })
+            return
+         }
+         // 处理返回的数据
+         handlePointList(knowledgePointList)
+      }
+   })
+}
 let flattenCoursewareListData: any = [] // 临时扁平化数据
 function handlePointList(pointList: any[]) {
    coursewareList.value = filterPointList(pointList)
@@ -276,6 +333,11 @@ function handleCourseClick(value: any) {
       return value.id === item.id && value.knowledgePointId === item.knowledgePointId
    })
 }
+
+function handleCourseMenuClick(value: any) {
+   console.log(value, "value")
+}
+
 /* 播放器相关 */
 // 视频播放或者暂停
 function handleVideoPlay() {
@@ -424,6 +486,14 @@ function handlePracticeClose() {
    isPracticeShow.value = false
    practiceUrl.value = ""
 }
+
+function onTitleTip(type: "phaseGoals" | "checkItem", text: string) {
+   useCoursewarePlayTip({
+      headImg: require(`@/img/coursewarePlay/${type ? "ts3" : "ts4"}.png`),
+      text,
+      btnShow: [false, false]
+   })
+}
 </script>
 
 <style lang="scss" scoped>
@@ -492,31 +562,65 @@ function handlePracticeClose() {
       top: 0;
       left: 0;
       width: 100%;
-      background: linear-gradient(180deg, rgba(0, 0, 0, 0.6), transparent);
+      background: linear-gradient(180deg, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0) 100%);
       transition: all 0.5s;
       display: flex;
-      align-items: center;
+      align-items: flex-start;
       justify-content: space-between;
-      padding: 20px 30px;
+      padding: 20px 30px 40px;
       .leftMenu {
          display: flex;
-         align-items: center;
+         align-items: flex-start;
          .backImg {
             cursor: pointer;
             width: 22px;
+            padding-top: 5px;
             &:hover {
                opacity: $opacity-hover;
             }
          }
+         .title-section {
+            font-weight: 500;
+            font-size: 22px;
+            color: #ffffff;
+            line-height: 30px;
+            padding-left: 20px;
+            .content {
+               padding-top: 6px;
+               font-weight: 500;
+               font-size: 18px;
+               color: #ffffff;
+               line-height: 26px;
+
+               span {
+                  background: rgba(0, 0, 0, 0.1);
+                  border-radius: 11px;
+                  border: 1px solid rgba(255, 255, 255, 0.7);
+                  font-size: 14px;
+                  color: #ffffff;
+                  line-height: 22px;
+                  padding: 1px 8px;
+                  margin-left: 6px;
+                  cursor: pointer;
+               }
+            }
+         }
       }
       .midMenu {
          position: absolute;
          left: 50%;
-         top: 50%;
-         transform: translate(-50%, -50%);
-         font-weight: 500;
-         font-size: 20px;
-         color: #ffffff;
+         top: 23px;
+         // transform: translate(-50%, -50%);
+         transform: translateX(-50%);
+      }
+      .rightMenu {
+         .posCloseBtn {
+            cursor: pointer;
+            img {
+               width: 34px;
+               height: 34px;
+            }
+         }
       }
    }
    .posTools {
@@ -525,15 +629,17 @@ function handlePracticeClose() {
       transform: translateY(-50%);
       transition: all 0.5s;
       &.leftTools {
-         left: 12px;
+         background: rgba(0, 0, 0, 0.4);
+         border-radius: 8px;
+         left: 32px;
       }
       &.rightTools {
          right: 12px;
       }
       .posBtn {
-         background: rgba(0, 0, 0, 0.3);
-         border-radius: 8px;
-         padding: 12px 6px;
+         // background: rgba(0, 0, 0, 0.3);
+         // border-radius: 8px;
+         padding: 20px 12px;
          font-weight: 500;
          font-size: 16px;
          color: #ffffff;
@@ -541,15 +647,13 @@ function handlePracticeClose() {
          flex-direction: column;
          align-items: center;
          cursor: pointer;
-         margin-bottom: 12px;
          &:hover {
             opacity: $opacity-hover;
          }
-         &:last-child {
-            margin-bottom: 0;
+         &.disabled {
+            opacity: $opacity-disabled;
          }
          > img {
-            margin-bottom: 5px;
             width: 34px;
             height: 34px;
          }
@@ -570,7 +674,7 @@ function handlePracticeClose() {
       }
    }
    &:deep(.elDrawer.el-drawer) {
-      width: 346px !important;
+      width: 348px !important;
       .el-drawer__header {
          height: 54px;
          background: #ededed;
@@ -612,5 +716,16 @@ function handlePracticeClose() {
          }
       }
    }
+
+   &:deep(.elCourseMenu.el-drawer) {
+      width: 363px !important;
+      .el-drawer__body {
+         & > .elScrollbar {
+            .el-scrollbar__view {
+               padding: 0;
+            }
+         }
+      }
+   }
 }
 </style>

+ 1 - 1
src/views/coursewarePlay/index.ts

@@ -1,2 +1,2 @@
 import coursewarePlay from "./coursewarePlay.vue"
-export default coursewarePlay
+export default coursewarePlay

二進制
src/views/coursewarePlay/videoPlay/img/iconLoop.png


二進制
src/views/coursewarePlay/videoPlay/img/iconLoopActive.png


二進制
src/views/coursewarePlay/videoPlay/img/iconPause.png


二進制
src/views/coursewarePlay/videoPlay/img/iconPlay.png


二進制
src/views/coursewarePlay/videoPlay/img/iconSpeed.png


+ 39 - 25
src/views/coursewarePlay/videoPlay/videoPlay.vue

@@ -14,24 +14,26 @@
    >
       <video class="videoPlayBox" :id="videoId" preload="auto" playsinline webkit-playsinline></video>
       <div class="videoController" @click.stop @touchstart.stop>
-         <div class="timeController">{{ `${formatTime(timeController.currentTime)} / ${formatTime(timeController.duration)}` }}</div>
-         <n-slider
-            class="sliderController"
-            :keyboard="false"
-            :value="timeController.currentTimeSilder"
-            :tooltip="isShowController"
-            :step="0.01"
-            @update:value="handleSilderChange"
-            :on-dragend="handleTimeChange"
-            :max="timeController.duration"
-            :format-tooltip="(value:number) => {
-                  return formatTime(value)
-            }"
-         />
+         <div class="sliderSection">
+            <div class="timeController">{{ `${formatTime(timeController.currentTime)} / ${formatTime(timeController.duration)}` }}</div>
+            <n-slider
+               class="sliderController"
+               :keyboard="false"
+               :value="timeController.currentTimeSilder"
+               :tooltip="isShowController"
+               :step="0.01"
+               @update:value="handleSilderChange"
+               :on-dragend="handleTimeChange"
+               :max="timeController.duration"
+               :format-tooltip="(value:number) => {
+                     return formatTime(value)
+               }"
+            />
+         </div>
          <div class="playController">
             <div class="leftPlayController">
                <img @click="handlePlay" :src="require(`./img/${playController.type === 'play' ? 'iconPause' : 'iconPlay'}.png`)" />
-               <img @click="handleLoop" :src="require(`./img/${playController.loop ? 'iconLoopActive' : 'iconLoop'}.png`)" />
+               <img @click="handleLoop" class="loopImg" :src="require(`./img/${playController.loop ? 'iconLoopActive' : 'iconLoop'}.png`)" />
                <img ref="btnSpendDom" src="./img//iconSpeed.png" />
                <el-popover
                   @show="handlePopoverTimeHide"
@@ -66,7 +68,7 @@
             </div>
             <div class="rightPlayController">
                <div class="videoName">
-                  <div>{{ videoName || "" }}</div>
+                  <!-- <div>{{ videoName || "" }}</div> -->
                </div>
             </div>
          </div>
@@ -327,8 +329,8 @@ defineExpose({
       width: 100%;
       left: 0;
       bottom: 0;
-      padding: 0 30px 15px;
-      background: linear-gradient(0deg, rgba(0, 0, 0, 0.5), transparent);
+      padding: 0 32px 22px;
+      background: linear-gradient(0deg, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0) 100%);
       color: #fff;
       transition: all 0.5s;
       // &:hover {   //取消鼠标移入不隐藏
@@ -336,20 +338,29 @@ defineExpose({
       //    opacity: initial !important;
       //    transform: initial !important;
       // }
+      .sliderSection {
+         display: flex;
+         align-items: center;
+
+         & > :deep(.sliderController.n-slider) {
+            --n-rail-color: rgba(255, 255, 255, 0.5) !important;
+            --n-fill-color: #ff8057 !important;
+            --n-fill-color-hover: #ff8057 !important;
+         }
+      }
       .timeController {
          font-weight: 500;
          font-size: 20px;
          color: #ffffff;
          line-height: 30px;
+         flex-shrink: 0;
+         margin-right: 16px;
       }
-      & > :deep(.sliderController.n-slider) {
-         --n-rail-color: #c9c9cb !important;
-         --n-fill-color: #ff8057 !important;
-         --n-fill-color-hover: #ff8057 !important;
-      }
+
       .playController {
          display: flex;
          justify-content: space-between;
+         padding-top: 10px;
          .leftPlayController {
             margin-left: -10px;
             display: flex;
@@ -357,7 +368,10 @@ defineExpose({
                cursor: pointer;
                width: 48px;
                height: 48px;
-               margin-right: 26px;
+               margin-right: 30px;
+            }
+            .loopImg {
+               width: 56px;
             }
             & > :deep(.palySpeedPopover.el-popover.el-popper) {
                min-width: initial;
@@ -388,7 +402,7 @@ defineExpose({
                      flex-grow: 1;
                      padding: 6px 0;
                      --n-rail-width-vertical: 5px !important;
-                     --n-rail-color: #c9c9cb !important;
+                     --n-rail-color: rgba(255, 255, 255, 0.5) !important;
                      --n-fill-color: #ff8057 !important;
                      --n-fill-color-hover: #ff8057 !important;
                      .thumb {