瀏覽代碼

Merge branch 'kajian_bo_yunjiaolian' into online

liushengqiang 2 年之前
父節點
當前提交
c42301b774

+ 1 - 1
src/views/courseList/index.tsx

@@ -106,7 +106,7 @@ export default defineComponent({
       postMessage({
         api: 'openWebView',
         content: {
-          url: `${location.origin}${location.pathname}/#/coursewarePlay?id=${item.lessonCoursewareDetailId}`,
+          url: `${location.origin}${location.pathname}#/coursewarePlay?id=${item.lessonCoursewareDetailId}&source=my-course`,
           orientation: 0,
           isHideTitle: true,
           statusBarTextColor: false,

+ 51 - 0
src/views/coursewarePlay/component/musicScore.module.less

@@ -5,10 +5,12 @@
   -webkit-overflow-scrolling: touch;
   overflow: scroll;
   .container {
+    position: relative;
     display: block;
     border: none;
     width: 100%;
     height: 100%;
+    z-index: 10;
   }
   .musicModel {
     position: absolute;
@@ -30,3 +32,52 @@
   justify-content: center;
   overflow: hidden;
 }
+
+.startBtn{
+  position: absolute;
+  left: 50%;
+  bottom: 6vh;
+  transform: translateX(-50%);
+  z-index: 11;
+  &:active{
+    opacity: .8;
+  }
+}
+.loading{
+  position: absolute;
+  left: 4%;
+  top: 50%;
+  margin-top: -15Px;
+}
+.skeletonWrap{
+  position: absolute;
+  left: 0;
+  top: 0;
+  right: 0;
+  height: 100%;
+  z-index: 1;
+  padding-top: 1.2rem;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  overflow: hidden;
+  background: #fff;
+  pointer-events: none;
+}
+.skeleton {
+  --van-skeleton-paragraph-height: 24px;
+  :global{
+    .van-skeleton__content{
+      .van-skeleton-paragraph{
+        margin: 12px auto;
+        width: 80% !important;
+        &:first-child{
+          width: 60% !important;
+        }
+        &:last-child{
+          width: 100% !important;
+        }
+      }
+    }
+  }
+}

+ 78 - 13
src/views/coursewarePlay/component/musicScore.tsx

@@ -2,6 +2,12 @@ import { defineComponent, ref, nextTick, onMounted, watch } from 'vue'
 import styles from './musicScore.module.less'
 import qs from 'query-string'
 import OEmpty from '@/components/o-empty'
+import iconStart from '../image/icon-start.svg'
+import { listenerMessage, postMessage } from '@/helpers/native-message'
+import { Loading, Skeleton } from 'vant'
+import { usePageVisibility } from '@vant/use'
+import { useRoute } from 'vue-router'
+import { browser } from '@/helpers/utils'
 
 export default defineComponent({
   name: 'musicScore',
@@ -9,11 +15,26 @@ export default defineComponent({
     music: {
       type: Object,
       default: () => {}
+    },
+    activeModel: {
+      type: Boolean
     }
   },
   emits: ['setIframe'],
   setup(props, { emit }) {
+    const browserInfo = browser()
+    const route = useRoute()
+    const isLoading = ref(false)
+    const pageVisibility = usePageVisibility()
+    /** 页面显示和隐藏 */
+    watch(pageVisibility, (value) => {
+      console.log("🚀 ~ value:", value)
+      if (value == 'hidden') {
+        isLoading.value = false
+      }
+    })
     const iframeRef = ref()
+    const isLoaded = ref(false)
     const renderError = ref(false)
     const renderSuccess = ref(false)
     const Authorization = sessionStorage.getItem('Authorization') || ''
@@ -45,23 +66,67 @@ export default defineComponent({
       }
     })
 
+    // 去云教练完整版
+    const gotoAccomany = () => {
+      if (isLoading.value) return
+      if (!browserInfo.ios){
+        isLoading.value = true
+      }
+      const parmas = qs.stringify({
+        id: props.music.content
+      })
+      let src = `${location.origin}/orchestra-music-score/?` + parmas
+      postMessage({
+        api: 'openAccompanyWebView',
+        content: {
+          url: src,
+          orientation: 0,
+          isHideTitle: true,
+          statusBarTextColor: false,
+          isOpenLight: true
+        }
+      }, () => {
+        if (browserInfo.ios){
+          isLoading.value = true
+        }
+      })
+    }
+    listenerMessage('webViewOnResume', () => {
+      isLoading.value = false
+    })
+
     return () => (
       <div class={styles.musicScore}>
-        {renderError.value ? (
-          <div class={styles.errorModel}>
-            <OEmpty type="network" tips="请检查网络环境" />
-          </div>
-        ) : (
-          <iframe
-            ref={iframeRef}
-            onLoad={(e: Event) => {
-              emit('setIframe', iframeRef.value)
+        <iframe
+          ref={iframeRef}
+          onLoad={(e: Event) => {
+            emit('setIframe', iframeRef.value)
+            isLoaded.value = true
+          }}
+          class={[styles.container, 'musicIframe']}
+          frameborder="0"
+          src={src}
+        ></iframe>
+        {route.query.source == 'my-course' && isLoaded.value && (
+          <div
+            style={{
+              display: props.activeModel ? '' : 'none'
             }}
-            class={[styles.container, 'musicIframe']}
-            frameborder="0"
-            src={src}
-          ></iframe>
+            class={styles.startBtn}
+            onClick={(e: Event) => {
+              e.stopPropagation()
+              gotoAccomany()
+            }}
+          >
+            <img src={iconStart} />
+            {/* {isLoading.value && (
+              <Loading class={styles.loading} color="rgba(63,134,237,1)" size={16} />
+            )} */}
+          </div>
         )}
+        {<div class={styles.skeletonWrap}>
+          <Skeleton class={styles.skeleton} row={8} />
+        </div>}
       </div>
     )
   }

+ 19 - 10
src/views/coursewarePlay/component/video-play.tsx

@@ -96,26 +96,27 @@ export default defineComponent({
 
     onMounted(() => {
       videoItem.value = new Plyr(videoRef.value, {
-        muted: true,
+        autoplay: true,
         controls: controls,
         autopause: true, // 一次只允许
         ratio: '16:9', // 强制所有视频的纵横比
         hideControls: false, // 在 2 秒没有鼠标或焦点移动、控制元素模糊(制表符退出)、播放开始或进入全屏时自动隐藏视频控件。只要移动鼠标、聚焦控制元素或暂停播放,控件就会立即重新出现。
         clickToPlay: false, // 单击(或点击)视频容器将切换播放/暂停
-        fullscreen: { enabled: false, fallback: false, iosNative: false}, // 不适用全屏
+        fullscreen: { enabled: false, fallback: false, iosNative: false } // 不适用全屏
       })
       if (videoItem.value) {
-        videoItem.value.once('loadedmetadata', (e: Event) => {
-          changePlayBtn('play')
-          // console.log('自动播放', item.value.autoPlay)
-          if (item.value.autoPlay && videoItem.value) {
-            videoItem.value.play()
-          }
-          emit('loadedmetadata', videoItem.value)
-        })
         videoItem.value.on('play', () => {
           if (videoItem.value) {
             videoItem.value.muted = false
+            videoItem.value.volume = 1
+          }
+            
+          // console.log('开始播放', item.value)
+          if (!item.value.autoPlay && !item.value.isprepare && videoItem.value) {
+            // 加载完成后,取消静音播放
+            
+            console.log(videoItem.value)
+            videoItem.value.pause()
           }
           changePlayBtn('')
           emit('togglePlay', videoItem.value?.paused)
@@ -128,6 +129,14 @@ export default defineComponent({
           emit('ended')
           changePlayBtn('play')
         })
+        videoItem.value.once('loadedmetadata', (e: Event) => {
+          changePlayBtn('play')
+          if (item.value.autoPlay && videoItem.value) {
+            videoItem.value.play()
+          }
+          emit('loadedmetadata', videoItem.value)
+        })
+
         nextTick(() => {
           onDefault()
         })

+ 45 - 0
src/views/coursewarePlay/image/icon-start.svg

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="121px" height="38px" viewBox="0 0 121 38" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>按钮/小按钮/橙</title>
+    <defs>
+        <linearGradient x1="15.3622126%" y1="46.5566937%" x2="89.7647319%" y2="53.6358332%" id="linearGradient-1">
+            <stop stop-color="#FF9C63" offset="0%"></stop>
+            <stop stop-color="#FF7144" offset="100%"></stop>
+        </linearGradient>
+        <text id="text-2" font-family="PingFangSC-Medium, PingFang SC" font-size="16" font-weight="400" fill="#FFFFFF">
+            <tspan x="49" y="24">去练习</tspan>
+        </text>
+        <filter x="-4.2%" y="-4.5%" width="108.3%" height="118.2%" filterUnits="objectBoundingBox" id="filter-3">
+            <feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0.941236855   0 0 0 0 0.448637032   0 0 0 0 0.244388325  0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+        <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-4">
+            <stop stop-color="#FCFFFF" offset="0%"></stop>
+            <stop stop-color="#FFE4D4" offset="100%"></stop>
+        </linearGradient>
+        <path d="M10,0 C15.5228475,0 20,4.4771525 20,10 C20,15.5228475 15.5228475,20 10,20 C4.4771525,20 0,15.5228475 0,10 C0,4.4771525 4.4771525,0 10,0 Z M15.8740909,4.13727273 C15.7362351,3.65455394 15.2331964,3.37495234 14.7504546,3.51272728 L10.205,4.81181817 C9.89624241,4.89978885 9.65677854,5.14409039 9.57499999,5.45454546 L9.54545455,5.45454546 L9.54590909,11.0254545 C8.4211929,10.2420678 6.92120134,10.2669785 5.82311793,11.0872797 C4.72503453,11.907581 4.27567032,13.3388975 4.70785225,14.6396281 C5.14003419,15.9403587 6.35662277,16.8181818 7.72727272,16.8181818 L7.83636363,16.8163636 C9.5501842,16.7575695 10.9091192,15.3511924 10.9090909,13.6363636 L10.9090909,8.24636364 L15.25,7.00636364 C15.6400039,6.89481163 15.9089107,6.53837105 15.9090909,6.13272727 L15.9090909,4.38727272 C15.9090909,4.30271722 15.8973272,4.21857285 15.8740909,4.13727273 Z" id="path-5"></path>
+        <filter x="-7.5%" y="-7.5%" width="115.0%" height="115.0%" filterUnits="objectBoundingBox" id="filter-6">
+            <feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="0.5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0.941236855   0 0 0 0 0.448637032   0 0 0 0 0.244388325  0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+    </defs>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="画板" transform="translate(-350.000000, -321.000000)">
+            <g id="编组-2" transform="translate(350.000000, 321.000000)">
+                <rect id="button-normal" fill="url(#linearGradient-1)" x="0" y="0" width="121" height="38" rx="19"></rect>
+                <g id="确定" fill-rule="nonzero" fill="#FFFFFF" fill-opacity="1">
+                    <use filter="url(#filter-3)" xlink:href="#text-2"></use>
+                    <use xlink:href="#text-2"></use>
+                </g>
+                <g id="编组" transform="translate(24.000000, 9.000000)" fill-rule="nonzero">
+                    <g id="形状结合">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-6)" xlink:href="#path-5"></use>
+                        <use fill="url(#linearGradient-4)" xlink:href="#path-5"></use>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 10 - 1
src/views/coursewarePlay/index.module.less

@@ -95,7 +95,7 @@
   width: 100%;
   height: 100%;
   background-color: #000;
-  transition-duration: .8s;
+  // transition-duration: .8s;
   transform-style: preserve-3d;
   transition-property: transform, opacity, height;
   backface-visibility: hidden;
@@ -104,6 +104,15 @@
   &.itemActive{
     z-index: 10;
   }
+  &.acitveAnimation{
+    transition-duration: .8s;
+  }
+  &.show{
+    display: block;
+  }
+  &.hide{
+    display: none;
+  }
   video {
     width: 100%;
     height: 100%;

+ 73 - 48
src/views/coursewarePlay/index.tsx

@@ -74,7 +74,7 @@ export default defineComponent({
       const activeItem = data.itemList[popupData.activeIndex]
       if (activeItem.type != 'VIDEO') return
       if (value == 'hidden') {
-        isPlay.value = !activeItem.videoEle.paused
+        isPlay.value = !activeItem.videoEle?.paused
         togglePlay(activeItem, false)
       } else {
         // 页面显示,并且
@@ -154,6 +154,7 @@ export default defineComponent({
     const activeData = reactive({
       nowTime: 0,
       model: true, // 遮罩
+      isAnimation: true, // 是否动画
       videoBtns: true, // 视频
       currentTime: 0,
       duration: 0,
@@ -199,7 +200,6 @@ export default defineComponent({
       const browserInfo = browser()
       for (let i = 0; i < data.knowledgePointList.length; i++) {
         const item = data.knowledgePointList[i]
-        const itemLength = item.materialList.length - 1
         for (let j = 0; j < item.materialList.length; j++) {
           const material = item.materialList[j]
           //请求本地缓存
@@ -216,7 +216,6 @@ export default defineComponent({
             iframeRef: null,
             videoEle: null,
             tabName: item.name,
-            isLast: j === itemLength, // 当前知识点
             autoPlay: false, //加载完成是否自动播放
             isprepare: false, // 视频是否加载完成
             isRender: false // 是否渲染了
@@ -233,9 +232,9 @@ export default defineComponent({
       popupData.tabActive = item.knowledgePointId
       popupData.itemActive = item.id
       popupData.itemName = item.name
-      // console.log('🚀 ~ list', list)
       nextTick(() => {
         data.itemList = list
+        checkedAnimation(popupData.activeIndex)
         postMessage({
           api: 'courseLoading',
           content: {
@@ -367,10 +366,6 @@ export default defineComponent({
       try {
         playRef.value?.handleOut()
       } catch (error) {}
-      if (route.query.source == 'my-course') {
-        router.back()
-        return
-      }
       postMessage({ api: 'goBack' })
     }
 
@@ -399,8 +394,8 @@ export default defineComponent({
       }
     }
     // 切换素材
-    const toggleMaterial = () => {
-      const index = data.itemList.findIndex((n: any) => n.id == popupData.itemActive)
+    const toggleMaterial = (itemActive: any) => {
+      const index = data.itemList.findIndex((n: any) => n.id == itemActive)
       if (index > -1) {
         handleSwipeChange(index)
       }
@@ -488,7 +483,7 @@ export default defineComponent({
           transform: 'translate3d(100%, 0, -800px) rotateY(120deg)'
         }
       },
-      //风车4
+      // 风车4
       {
         prev: {
           transform: 'translate3d(-50%, 50%, -800px) rotateZ(-14deg)',
@@ -499,7 +494,7 @@ export default defineComponent({
           opacity: 0
         }
       },
-      //翻页5
+      // 翻页5
       {
         prev: {
           transform: 'translateZ(-800px) rotate3d(0, -1, 0, 90deg)',
@@ -517,31 +512,63 @@ export default defineComponent({
     // 轮播切换
     const handleSwipeChange = (index: number) => {
       if (popupData.activeIndex == index) return
-      popupData.activeIndex = index
       handleStop()
       clearTimeout(acitveTimer.value)
-      acitveTimer.value = setTimeout(() => {
-        const item = data.itemList[index]
-        if (item) {
-          popupData.tabActive = item.knowledgePointId
-          popupData.itemActive = item.id
-          popupData.itemName = item.name
-          popupData.tabName = item.tabName
-          if (item.type == 'SONG') {
-            activeData.model = true
-          }
-          if (item.type === 'VIDEO') {
-            // 自动播放下一个视频
-            clearTimeout(activeData.timer)
-            closeToast()
-            item.autoPlay = true
-            nextTick(() => {
-              item.videoEle?.play()
-              console.error(item.videoEle)
-            })
+      const oldIndex = popupData.activeIndex
+      checkedAnimation(popupData.activeIndex, index)
+      popupData.activeIndex = index
+
+      acitveTimer.value = setTimeout(
+        () => {
+          const item = data.itemList[index]
+          if (item) {
+            popupData.tabActive = item.knowledgePointId
+            popupData.itemActive = item.id
+            popupData.itemName = item.name
+            popupData.tabName = item.tabName
+            if (item.type == 'SONG') {
+              activeData.model = true
+            }
+            if (item.type === 'VIDEO') {
+              // 自动播放下一个视频
+              clearTimeout(activeData.timer)
+              closeToast()
+              item.autoPlay = true
+              nextTick(() => {
+                item.videoEle?.play()
+              })
+            }
           }
+          requestAnimationFrame(() => {
+            const _effectIndex = effectIndex.value + 1
+            effectIndex.value = _effectIndex >= effects.length - 1 ? 0 : _effectIndex
+          })
+        },
+        activeData.isAnimation ? 800 : 0
+      )
+    }
+
+    /** 是否有转场动画 */
+    const checkedAnimation = (index: number, nextIndex?: number) => {
+      const item = data.itemList[index]
+      const nextItem = data.itemList[nextIndex!]
+      if (nextItem) {
+        if (nextItem.knowledgePointId != item.knowledgePointId) {
+          activeData.isAnimation = true
+          return
         }
-      }, 800)
+        const videoEle = item.videoEle
+        const nextVideo = nextItem.videoEle
+        if (videoEle && videoEle.duration < 8 && index < nextIndex!) {
+          activeData.isAnimation = false
+        } else if (nextVideo && nextVideo.duration < 8 && index > nextIndex!) {
+          activeData.isAnimation = false
+        } else {
+          activeData.isAnimation = true
+        }
+      } else {
+        activeData.isAnimation = item?.adviseStudyTimeSecond < 8 ? false : true
+      }
     }
 
     // 上一个知识点, 下一个知识点
@@ -579,13 +606,18 @@ export default defineComponent({
                 return isRender ? (
                   <div
                     key={'index' + mIndex}
-                    class={[styles.itemDiv, popupData.activeIndex === mIndex && styles.itemActive]}
+                    class={[
+                      styles.itemDiv,
+                      popupData.activeIndex === mIndex && styles.itemActive,
+                      activeData.isAnimation && styles.acitveAnimation,
+                      Math.abs(popupData.activeIndex - mIndex) < 2 ? styles.show : styles.hide
+                    ]}
                     style={
                       mIndex < popupData.activeIndex
                         ? effects[effectIndex.value].prev
                         : mIndex > popupData.activeIndex
                         ? effects[effectIndex.value].next
-                        : effects[effectIndex.value].current
+                        : {}
                     }
                     onClick={(e: Event) => {
                       e.stopPropagation()
@@ -646,6 +678,7 @@ export default defineComponent({
                       <img src={m.content} />
                     ) : (
                       <MusicScore
+                        activeModel={activeData.model}
                         data-vid={m.id}
                         music={m}
                         onSetIframe={(el: any) => {
@@ -739,18 +772,14 @@ export default defineComponent({
           <div
             class={styles.menu}
             onClick={() => {
-              if (showIndex.value > 0) {
-                effectIndex.value =
-                  effectIndex.value < effects.length - 1 ? effectIndex.value + 1 : 0
-              } else {
-                showIndex.value += 1
-              }
+              const _effectIndex = effectIndex.value + 1
+              effectIndex.value = _effectIndex >= effects.length - 1 ? 0 : _effectIndex
               setModelOpen()
             }}
           >
-            {popupData.tabName} {showIndex.value > 0 ? `动画${effectIndex.value}` : ''}
+            {popupData.tabName}
           </div>
-          {data.isCourse && <PlayRecordTime ref={playRef} list={data.itemList} />}
+          {data.isCourse && <PlayRecordTime ref={playRef} list={data.knowledgePointList} />}
         </div>
 
         <Popup
@@ -771,12 +800,8 @@ export default defineComponent({
             tabActive={popupData.tabActive}
             itemActive={popupData.itemActive}
             onHandleSelect={(res: any) => {
-              // console.log(res)
-              popupData.tabActive = res.tabActive
-              popupData.itemActive = res.itemActive
-              popupData.tabName = res.tabName
               popupData.open = false
-              toggleMaterial()
+              toggleMaterial(res.itemActive)
             }}
           />
         </Popup>

+ 1 - 1
src/views/coursewarePlay/playRecordTime.tsx

@@ -47,7 +47,7 @@ export default defineComponent({
     /** 建议学习总时长 */
     const total = computed(() => {
       const _total = props.list.reduce(
-        (_total: number, item: any) => _total + item.adviseStudyTimeSecond,
+        (_total: number, item: any) => _total + (item.totalMaterialTimeSecond || 0),
         0
       )
       return _total