Przeglądaj źródła

Merge branch 'master' of http://git.dayaedu.com/lex/orchestra-app

mo 2 lat temu
rodzic
commit
3cddf932d1

+ 1 - 1
index.html

@@ -34,7 +34,7 @@
   <meta name="apple-touch-fullscreen" content="yes" />
   <!-- windows phone 点击无高光 -->
   <meta name="msapplication-tap-highlight" content="no" />
-  <title>酷乐秀</title>
+  <title>管乐团</title>
   <script src="/flexible.js" charset="UTF-8"></script>
   <script type="text/javascript">
     window.paymentType = 'STUDENT'

+ 1 - 1
school.html

@@ -34,7 +34,7 @@
   <meta name="apple-touch-fullscreen" content="yes" />
   <!-- windows phone 点击无高光 -->
   <meta name="msapplication-tap-highlight" content="no" />
-  <title>酷乐秀</title>
+  <title>管乐团</title>
   <script src="/flexible.js" charset="UTF-8"></script>
   <script type="text/javascript">
     window.paymentType = 'SCHOOL'

+ 20 - 27
src/helpers/utils.ts

@@ -16,9 +16,7 @@ export const browser = () => {
     // ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
     android: u.indexOf('ORCHESTRAAPPA') > -1 || u.indexOf('Adr') > -1, //android终端
     iPhone: u.indexOf('ORCHESTRAAPPI') > -1, //是否为iPhone或者QQHD浏览器
-    isApp:
-      u.indexOf('ORCHESTRAAPPI') > -1 ||
-      u.indexOf('ORCHESTRAAPPA') > -1,
+    isApp: u.indexOf('ORCHESTRAAPPI') > -1 || u.indexOf('ORCHESTRAAPPA') > -1,
     isTeacher: u.indexOf('ORCHESTRATEACHER') > -1,
     isStudent: u.indexOf('ORCHESTRASTUDENT') > -1,
     isSchool: u.indexOf('ORCHESTRASCHOOL') > -1,
@@ -34,20 +32,18 @@ export const browser = () => {
 // 获取授权的code码
 export const getUrlCode = (name = 'code') => {
   // 截取url中的code方法
-  const url = location.search;
-  const theRequest: any = new Object();
-  if (url.indexOf("?") != -1) {
-    const str = url.substr(1);
-    const strs = str.split("&");
+  const url = location.search
+  const theRequest: any = new Object()
+  if (url.indexOf('?') != -1) {
+    const str = url.substr(1)
+    const strs = str.split('&')
     for (let i = 0; i < strs.length; i++) {
-      theRequest[strs[i].split("=")[0]] = strs[i].split("=")[1];
+      theRequest[strs[i].split('=')[0]] = strs[i].split('=')[1]
     }
   }
-  console.log(theRequest, 'theRequest');
-  return theRequest[name];
-
-};
-
+  console.log(theRequest, 'theRequest')
+  return theRequest[name]
+}
 
 export const getRandomKey = () => {
   const key = '' + new Date().getTime() + Math.floor(Math.random() * 1000000)
@@ -108,15 +104,7 @@ export const closeLoading = () => {
 
 export const getWeekCh = (week: number, type = 0) => {
   const template = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
-  const template2 = [
-    '星期天',
-    '星期一',
-    '星期二',
-    '星期三',
-    '星期四',
-    '星期五',
-    '星期六'
-  ]
+  const template2 = ['星期天', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
   return type ? template2[week] : template[week]
 }
 
@@ -130,9 +118,14 @@ export const numberFormat = (num: number, type?: string) => {
 export const moneyFormat = (value: number, format = '0,0.00') => {
   return numeral(value).format(format)
 }
-export const dateFormat = (
-  value: string | Date,
-  format = 'YYYY-MM-DD HH:mm:ss'
-) => {
+export const dateFormat = (value: string | Date, format = 'YYYY-MM-DD HH:mm:ss') => {
   return dayjs(value).format(format)
 }
+
+// 秒转分
+export const getSecondRPM = (second: number) => {
+  if (isNaN(second)) return '00:00'
+  const mm = Math.floor(second / 60).toString().padStart(2, '0')
+  const dd = Math.floor(second % 60).toString().padStart(2, '0')
+  return mm + ':' + dd
+}

+ 3 - 2
src/school/companion-teacher/companion-detail.tsx

@@ -24,6 +24,7 @@ import request from '@/helpers/request'
 import { state as baseState } from '@/state'
 import { useRoute, useRouter } from 'vue-router'
 import OEmpty from '@/components/o-empty'
+import ODialog from '@/components/o-dialog'
 
 export default defineComponent({
   name: 'companion-detail',
@@ -136,12 +137,12 @@ export default defineComponent({
               )
             }}
           </Cell>
-          <Cell>
+          <Cell center>
             {{
               title: () => (
                 <div class={styles.subjectContainer}>
                   <span>声部:</span>
-                  <div>
+                  <div style={{ display: 'flex', alignItems: 'center' }}>
                     {state.detail.subjectNames &&
                       state.detail.subjectNames.map((subject: any) => (
                         <Tag type="primary" class={styles.tagSubject}>

+ 2 - 2
src/school/companion-teacher/index.tsx

@@ -350,12 +350,12 @@ export default defineComponent({
                     )
                   }}
                 </Cell>
-                <Cell>
+                <Cell center>
                   {{
                     title: () => (
                       <div class={styles.subjectContainer}>
                         <span>声部:</span>
-                        <div>
+                        <div style={{ display: 'flex', alignItems: 'center' }}>
                           {item.subjectNames &&
                             item.subjectNames.length > 0 &&
                             item.subjectNames.map((subject: any) => (

+ 19 - 8
src/school/companion-teacher/unbind.tsx

@@ -75,18 +75,29 @@ export default defineComponent({
           message: '是否确认交接',
           showCancelButton: true
         }).then(async () => {
-          await request.post('/api-school/classGroup/handoverTeacher', {
+          const { data } = await request.post('/api-school/classGroup/handoverTeacher', {
             data: {
               teacherId: route.query.id,
               updateTeacherList: [...courseInfo]
             }
           })
-          setTimeout(() => {
-            showToast('交接成功')
-          }, 100)
-          setTimeout(() => {
-            router.replace('/companion-teacher')
-          }, 1000)
+
+          if (data.finish === true) {
+            setTimeout(() => {
+              showToast('交接成功')
+            }, 100)
+            setTimeout(() => {
+              router.replace('/companion-teacher')
+            }, 1000)
+          } else {
+            router.push({
+              path: '/course-preview',
+              query: {
+                cacheId: data.cacheId,
+                type: 'change'
+              }
+            })
+          }
         })
       } catch {
         //
@@ -144,7 +155,7 @@ export default defineComponent({
           </div>
         </OSticky>
 
-        <OPopup v-model:modelValue={state.teacherStatus}>
+        <OPopup v-model:modelValue={state.teacherStatus} destroy>
           <Teacher
             courseType={state.selectTeacher.courseType}
             teacherId={state.teacherId as any}

+ 2 - 2
src/school/mass-message/component/teacher-list/teacher-list.tsx

@@ -192,12 +192,12 @@ export default defineComponent({
                       )
                     }}
                   </Cell>
-                  <Cell>
+                  <Cell center>
                     {{
                       title: () => (
                         <div class={styles.subjectContainer}>
                           <span>声部:</span>
-                          <div>
+                          <div style={{ display: 'flex', alignItems: 'center' }}>
                             {item.subjectNames &&
                               item.subjectNames.map((subject: any) => (
                                 <Tag type="primary" class={styles.tagSubject}>

+ 125 - 4
src/school/orchestra/compontent/information.tsx

@@ -1,5 +1,21 @@
 import OSticky from '@/components/o-sticky'
-import { Button, DatePicker, Grid, GridItem, Icon, Image, List, Picker, Popover, Popup } from 'vant'
+import {
+  Button,
+  closeToast,
+  DatePicker,
+  Grid,
+  GridItem,
+  Icon,
+  Image,
+  List,
+  Picker,
+  Popover,
+  Popup,
+  showFailToast,
+  showLoadingToast,
+  showSuccessToast,
+  showToast
+} from 'vant'
 import { computed, defineComponent, nextTick, onMounted, reactive } from 'vue'
 import styles from './information.module.less'
 import iconSaveImage from '../images/icon-save-image.png'
@@ -10,6 +26,8 @@ import { useRoute } from 'vue-router'
 import { CountUp } from 'countup.js'
 import OEmpty from '@/components/o-empty'
 import dayjs from 'dayjs'
+import { promisefiyPostMessage, postMessage } from '@/helpers/native-message'
+import html2canvas from 'html2canvas'
 
 export default defineComponent({
   name: 'detail-information',
@@ -181,6 +199,109 @@ export default defineComponent({
       })
     }
 
+    const imgs = reactive({
+      saveLoading: false,
+      image: null as any,
+      shareLoading: false
+    })
+    const onSaveImg = async () => {
+      // 判断是否在保存中...
+      if (imgs.saveLoading) {
+        return
+      }
+      imgs.saveLoading = true
+      // 判断是否已经生成图片
+      if (imgs.image) {
+        saveImg()
+      } else {
+        const container: any = document.getElementById(`preview-container`)
+        html2canvas(container, {
+          allowTaint: true,
+          useCORS: true,
+          backgroundColor: null
+        })
+          .then(async (canvas) => {
+            const url = canvas.toDataURL('image/png')
+            imgs.image = url
+            saveImg()
+          })
+          .catch(() => {
+            closeToast()
+            imgs.saveLoading = false
+          })
+      }
+    }
+    const onShare = () => {
+      if (imgs.shareLoading) {
+        return
+      }
+      imgs.shareLoading = true
+      if (imgs.image) {
+        openShare()
+      } else {
+        const container: any = document.getElementById(`preview-container`)
+        html2canvas(container, {
+          allowTaint: true,
+          useCORS: true,
+          backgroundColor: null
+        })
+          .then(async (canvas) => {
+            const url = canvas.toDataURL('image/png')
+            imgs.image = url
+            openShare()
+          })
+          .catch(() => {
+            closeToast()
+            imgs.shareLoading = false
+          })
+      }
+    }
+    const openShare = () => {
+      const image = imgs.image
+      setTimeout(() => {
+        imgs.shareLoading = false
+      }, 100)
+      if (image) {
+        postMessage(
+          {
+            api: 'shareTripartite',
+            content: {
+              title: '',
+              desc: '',
+              image,
+              video: '',
+              type: 'image',
+              // button: ['copy']
+              shareType: 'wechat'
+            }
+          },
+          (res: any) => {
+            if (res && res.content) {
+              showToast(res.content.message || (res.content.status ? '分享成功' : '分享失败'))
+            }
+          }
+        )
+      }
+    }
+    const saveImg = async () => {
+      showLoadingToast({ message: '图片生成中...', forbidClick: true })
+      setTimeout(() => {
+        imgs.saveLoading = false
+      }, 100)
+      const res = await promisefiyPostMessage({
+        api: 'savePicture',
+        content: {
+          base64: imgs.image
+        }
+      })
+      if (res?.content?.status === 'success') {
+        showSuccessToast('保存成功')
+        state.showQrcode = false
+      } else {
+        showFailToast('保存失败')
+      }
+    }
+
     onMounted(() => {
       getDetails()
       getStatistics()
@@ -309,7 +430,7 @@ export default defineComponent({
           // safeAreaInsetBottom={true}
         >
           <div class={styles.codeContainer}>
-            <div class={styles.codeImg}>
+            <div class={styles.codeImg} id="preview-container">
               <div class={styles.codeContent}>
                 <h2 class={styles.codeTitle}>乐团报名</h2>
                 <div class={[styles.codeName, 'van-ellipsis']}>{state.orchestraInfo.name}</div>
@@ -336,13 +457,13 @@ export default defineComponent({
                 <i></i>分享方式
               </h3>
               <Grid columnNum={2} border={false}>
-                <GridItem>
+                <GridItem onClick={onSaveImg}>
                   {{
                     icon: () => <Image class={styles.shareImg} src={iconSaveImage} />,
                     text: () => <div class={styles.shareText}>保存图片</div>
                   }}
                 </GridItem>
-                <GridItem>
+                <GridItem onClick={onShare}>
                   {{
                     icon: () => <Image class={styles.shareImg} src={iconWechat} />,
                     text: () => <div class={styles.shareText}>微信</div>

+ 3 - 1
src/school/orchestra/create-orchestra/index.tsx

@@ -185,7 +185,9 @@ export default defineComponent({
           <Field
             label="乐团声部"
             readonly
-            placeholder="选择声部"
+            placeholder={
+              state.selectSubjects.length > 0 ? `已选${state.selectSubjects.length}个` : '选择声部'
+            }
             isLink
             inputAlign="right"
             onClick={() => (state.subjectStatus = true)}

+ 1 - 1
src/school/school-detail/eidt-school.tsx

@@ -142,7 +142,7 @@ export default defineComponent({
                 <Image
                   width={50}
                   height={50}
-                  fix="cover"
+                  fit="cover"
                   class={styles.schoolICon}
                   src={forms.logo ? forms.logo : logoIcon}
                 ></Image>

+ 5 - 1
src/school/train-planning/component/course-preview/index.tsx

@@ -156,7 +156,11 @@ export default defineComponent({
         }, 100)
         setTimeout(() => {
           state.isClick = false
-          postMessage({ api: 'back', content: {} })
+          if (route.query.type === 'change') {
+            router.replace('/companion-teacher')
+          } else {
+            postMessage({ api: 'back', content: {} })
+          }
         }, 1100)
       } catch {
         //

+ 0 - 1
src/views/coursewarePlay/component/points.tsx

@@ -35,7 +35,6 @@ export default defineComponent({
             class={styles.collapse}
             modelValue={pointData.active}
             onUpdate:modelValue={(val: any) => {
-              console.log(val)
               pointData.active = val
             }}
             accordion

+ 26 - 30
src/views/coursewarePlay/index.module.less

@@ -3,13 +3,14 @@
   height: 100vh;
   background-color: rgba(89, 98, 126, 0.2);
 }
-.playModel{
+.playModel {
   position: absolute;
-    left: 0;
-    top: 0;
-    right: 0;
-    bottom: 0;
-    box-shadow: inset 0px 0px 164px 0px rgba(0,0,0,1);
+  left: 0;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  box-shadow: inset 0px 0px 164px 0px rgba(0, 0, 0, 1);
+  pointer-events: none;
 }
 .headerContainer {
   position: fixed;
@@ -20,6 +21,7 @@
   padding: 10px;
   display: flex;
   align-items: center;
+  background: linear-gradient(180deg, rgba(0, 0, 0, .6), transparent);
 }
 .backBtn {
   position: absolute;
@@ -51,23 +53,14 @@
     }
   }
 }
-.videoItem {
+.itemDiv {
+  position: relative;
   width: 100%;
   height: 100%;
-  --plyr-color-main: var(--van-primary);
   video {
     width: 100%;
     height: 100%;
   }
-  :global {
-    .plyr {
-      height: 100%;
-    }
-  }
-}
-.imgItem {
-  width: 100%;
-  height: 100%;
   img {
     display: block;
     width: 100%;
@@ -75,10 +68,6 @@
     object-fit: contain;
   }
 }
-.songItem {
-  width: 100%;
-  height: 100%;
-}
 .rightFixedBtns {
   position: fixed;
   top: 50%;
@@ -94,12 +83,12 @@
     border-top-right-radius: 0;
   }
 }
-.leftFixedBtns{
+.leftFixedBtns {
   position: fixed;
   top: 50%;
   transform: translateY(-50%);
   left: 20px;
-  .prePoint{
+  .prePoint {
     margin-bottom: 8px;
   }
 }
@@ -117,26 +106,33 @@
     opacity: 0.8;
   }
 }
-.bottomFixedContainer{
-  position: fixed;
+.bottomFixedContainer {
+  position: absolute;
   left: 0;
   right: 0;
   bottom: 0;
-  .time{
+  z-index: 10;
+  background: linear-gradient(0deg, rgba(0, 0, 0, 0.5), transparent);
+  backdrop-filter: blur(2px);
+  .time {
     display: flex;
     justify-content: space-between;
     color: #fff;
     font-size: 10px;
     padding: 4px 10px;
   }
-  .actions{
+  .slider {
+    padding: 8px 10px;
+  }
+  .actions {
     display: flex;
     justify-content: space-between;
     color: #fff;
     font-size: 12px;
-    padding: 4px 10px 18px 10px;
-    :global{
-      .van-icon{
+    padding: 8px 10px;
+    align-items: center;
+    :global {
+      .van-icon {
         font-size: 20px;
         margin-right: 14px;
       }

+ 265 - 163
src/views/coursewarePlay/index.tsx

@@ -1,4 +1,15 @@
-import { Button, Icon, Popup, Slider, Swipe, SwipeItem, Tab, Tabs } from 'vant'
+import {
+  Button,
+  closeToast,
+  Icon,
+  Popup,
+  showToast,
+  Slider,
+  Swipe,
+  SwipeItem,
+  Tab,
+  Tabs
+} from 'vant'
 import {
   defineComponent,
   onMounted,
@@ -22,32 +33,40 @@ import MusicScore from './component/musicScore'
 import iconMenu from './image/icon-menu.svg'
 import iconDian from './image/icon-dian.svg'
 import iconPoint from './image/icon-point.svg'
+import iconLoop from './image/icon-loop.svg'
+import iconLoopActive from './image/icon-loop-active.svg'
+import iconplay from './image/icon-play.svg'
+import iconpause from './image/icon-pause.svg'
 import Points from './component/points'
+import { getSecondRPM } from '@/helpers/utils'
 
 export default defineComponent({
   name: 'CoursewarePlay',
   setup() {
-    const handleInit = () => {
+    const handleInit = (type = 0) => {
       postMessage({
         api: 'setRequestedOrientation',
         content: {
-          orientation: 0
+          orientation: type
         }
       })
       postMessage({
         api: 'setBarStatus',
         content: {
-          status: 0
+          status: type
         }
       })
       postMessage({
         api: 'setStatusBarVisibility',
         content: {
-          isVisibility: 0
+          isVisibility: type
         }
       })
     }
     handleInit()
+    onUnmounted(() => {
+      handleInit(1)
+    })
 
     const route = useRoute()
     const data = reactive({
@@ -55,33 +74,52 @@ export default defineComponent({
       active: '',
       itemActive: '',
       knowledgePointList: [] as any,
+      itemList: [] as any,
       showHead: true,
       players: [] as any
     })
     const activeData = reactive({
+      nowTime: 0,
       model: true, // 遮罩
-      currentTime: 10
+      videoBtns: true, // 视频
+      currentTime: 0,
+      duration: 0,
+      timer: null as any,
+      item: null as any
     })
-    const itemList = computed(() => {
+    const getItemList = () => {
       const list: any = []
       for (let i = 0; i < data.knowledgePointList.length; i++) {
         const item = data.knowledgePointList[i]
         for (let j = 0; j < item.materialList.length; j++) {
           const material = item.materialList[j]
           if (popupData.itemActive === '') {
-            popupData.tabActive = material.knowledgePointId
             popupData.tabName = item.name
+            popupData.tabActive = material.knowledgePointId
             popupData.itemActive = material.id
             popupData.itemName = material.name
+            popupData.activeIndex = 0
+          }
+          let videoItem = {}
+          if (material.type === 'VIDEO') {
+            videoItem = {
+              currentTime: 0,
+              duration: 0,
+              paused: true,
+              loop: false,
+              videoEle: null,
+              timer: null
+            }
           }
           list.push({
-            ...material
+            ...material,
+            ...videoItem
           })
         }
       }
-      // console.log('🚀 ~ list', list)
+      console.log('🚀 ~ list', list)
       return list
-    })
+    }
     const getDetail = async () => {
       try {
         const res: any = await request.get(
@@ -95,132 +133,77 @@ export default defineComponent({
             n.index = 0
             return n
           })
+          data.itemList = getItemList()
         }
       } catch (error) {}
     }
-    const videoInit = () => {
-      console.log(document.querySelectorAll('.player'))
-      data.players = Plyr.setup('.player', {
-        debug: false,
-        ratio: '16:9',
-        clickToPlay: true,
-        controls: [
-          'play-large',
-          'play',
-          'progress',
-          'current-time',
-          'duration',
-          'mute',
-          'volume',
-          'restart'
-        ]
-      })
-      data.players.forEach((p: Plyr) => {
-        // console.log(p)
-        p.on('play', () => {
-          console.log('开始播放了')
-          data.showHead = false
-        })
-        p.on('pause', () => {
-          console.log('暂停了')
-          data.showHead = true
-        })
-        p.on('controlsshown', () => {
-          console.log('显示控件')
-          data.showHead = true
-        })
-        p.on('controlshidden', () => {
-          console.log('控件隐藏')
-          data.showHead = false
-        })
-      })
-      console.log('🚀 ~ player', data.players)
-    }
-    const testFile = ref('')
     onMounted(() => {
-      // const o = new ActiveXObject("wscript.shell")
       getDetail()
-      postMessage(
-        {
-          api: 'getCourseFilePath',
-          content: {
-            url: 'https://daya.ks3-cn-beijing.ksyuncs.com/01/1672976208474.jpg',
-            materialId: '1610610237357969409',
-            updateTime: '2023-01-04 20:12:58',
-            type: 'VIDEO' // SONG VIDEO IMAGE
-          }
-        },
-        (res) => {
-          if (res?.content) {
-            testFile.value = 'customScheme://' + res.content.localPath
-            fetch(testFile.value)
-          }
-        }
-      )
-    })
-    listenerMessage('getCourseFilePath', (res) => {
-      if (res?.content) {
-        testFile.value = res.content.localPath
-      }
     })
     // 返回
     const goback = () => {
       // history.go(-1)
       postMessage({ api: 'back' })
     }
-    // 所有的切换
-    const handleChange = () => {
-      // console.log('切换了')
-      const iframes = document.querySelectorAll('.musicIframe')
-      Array.from(iframes).map((f: any) => {
-        f.contentWindow.postMessage({ api: 'setPlayState' }, '*')
-      })
-      data.players.forEach((p: any) => {
-        p.stop()
-      })
-    }
-    onUnmounted(() => {
-      postMessage({
-        api: 'setRequestedOrientation',
-        content: {
-          orientation: 1
-        }
-      })
-      postMessage({
-        api: 'setBarStatus',
-        content: {
-          status: 1
-        }
-      })
-      postMessage({
-        api: 'setStatusBarVisibility',
-        content: {
-          isVisibility: 1
-        }
-      })
-    })
 
+    const swipeRef = ref()
     const popupData = reactive({
       open: false,
-      activeIndex: 0,
+      activeIndex: -1,
       tabActive: '',
       tabName: '',
       itemActive: '',
       itemName: ''
     })
+    // 设置当前的激活状态
+    const setActiveData = () => {
+      handleStopVideo()
+    }
+    watch(() => popupData.activeIndex, setActiveData)
+
+    // 停止所有的播放
+    const handleStopVideo = () => {
+      data.itemList.forEach((m: any) => {
+        m.videoEle?.pause()
+      })
+    }
+    // 获取name
+    const setAllName = () => {
+      const item = data.itemList.find((n: any) => n.id == popupData.itemActive)
+      const tab = data.knowledgePointList.find((n: any) => n.id == popupData.tabActive)
+      if (item) {
+        popupData.itemName = item.name
+      }
+      if (tab) {
+        popupData.tabName = tab.name
+      }
+    }
     // 切换素材
     const toggleMaterial = () => {
-      const index = itemList.value.findIndex((n: any) => n.id == popupData.itemActive)
-      popupData.activeIndex = index
-      console.log('🚀 ~ popupData', popupData.activeIndex)
+      const index = data.itemList.findIndex((n: any) => n.id == popupData.itemActive)
+      if (index > -1) {
+        popupData.activeIndex = index
+        swipeRef.value?.swipeTo(index)
+        setAllName()
+      }
+    }
+    // 轮播切换
+    const handleSwipeChange = (val: any) => {
+      popupData.activeIndex = val
+      const item = data.itemList[val]
+      if (item) {
+        popupData.tabActive = item.knowledgePointId
+        popupData.itemActive = item.id
+        setAllName()
+      }
     }
     // 上一个知识点, 下一个知识点
     const handlePreAndNext = (type: string) => {
-      console.log(popupData.tabActive)
-      const tabIndex = data.knowledgePointList.findIndex((n: any) => n.id == popupData.tabActive)
-      const itemIndex = itemList.value.findIndex((n: any) => n.id == popupData.itemActive)
-      
-      console.log("🚀 ~ tab", tabIndex, itemIndex)
+      if (type === 'up') {
+        swipeRef.value?.prev()
+      } else {
+        swipeRef.value?.next()
+      }
     }
 
     // 去点名,签退
@@ -235,48 +218,191 @@ export default defineComponent({
         }
       })
     }
+
+    // 双击
+    const handleDbClick = (item: any) => {
+      // console.log(activeData.item)
+      if (item && item.type === 'VIDEO') {
+        const videoEle: HTMLVideoElement = document.querySelector(`[data-vid='${item.id}']`)!
+        if (videoEle) {
+          if (videoEle.paused) {
+            closeToast()
+            videoEle.play()
+          } else {
+            showToast('已暂停')
+            videoEle.pause()
+          }
+        }
+        item.timer = setTimeout(() => {
+          activeData.model = false
+        }, 3000)
+        console.dir(videoEle)
+      }
+    }
+
+    // 视频播放
+    const handleVideoPlay = (e: Event) => {
+      const videoEle = e.target! as unknown as HTMLVideoElement
+      // console.log(videoEle.paused, videoEle.currentTime, videoEle.duration)
+      if (videoEle.paused) return
+      activeData.currentTime = videoEle.currentTime
+      activeData.duration = videoEle.duration
+    }
+
+    // 调整播放进度
+    const handleChangeSlider = (val: any, m: any) => {
+      if (m?.videoEle) {
+        m.videoEle.currentTime = val
+      }
+    }
+
     return () => (
       <div class={styles.coursewarePlay}>
         <Swipe
           style={{ height: '100vh' }}
+          ref={swipeRef}
           showIndicators={false}
           loop={false}
-          initialSwipe={popupData.activeIndex}
           vertical
           lazyRender={true}
-          onChange={(val: any) => {
-            // item.index = val
-            // popupData.itemActive = `${item.id}-${item?.materialList?.[item.index]?.id}`
-          }}
+          onChange={handleSwipeChange}
         >
-          {itemList.value.map((m: any) => {
+          {data.itemList.map((m: any, mIndex: number) => {
             return (
               <SwipeItem>
-                {m.type === 'VIDEO' ? (
-                  <div class={styles.videoItem}>
-                    <video class="player">
-                      <source src={m.content} type="video/mp4" />
-                    </video>
-                  </div>
-                ) : m.type === 'IMG' ? (
-                  <div class={styles.imgItem}>
-                    <img src={m.content} />
-                  </div>
-                ) : (
-                  <div class={styles.songItem}>
-                    <MusicScore music={m} />
+                <>
+                  <div
+                    class={styles.itemDiv}
+                    onClick={() => {
+                      clearTimeout(activeData.timer)
+                      clearTimeout(m.timer)
+                      if (Date.now() - activeData.nowTime < 300) {
+                        handleDbClick(m)
+                        return
+                      }
+                      activeData.nowTime = Date.now()
+                      activeData.timer = setTimeout(() => {
+                        activeData.model = !activeData.model
+                      }, 300)
+                    }}
+                  >
+                    {m.type === 'VIDEO' ? (
+                      <>
+                        <video
+                          preload='auto'
+                          class="player"
+                          data-vid={m.id}
+                          src={m.content}
+                          loop={m.loop}
+                          onLoadedmetadata={(e: Event) => {
+                            const videoEle = e.target as unknown as HTMLVideoElement
+                            // console.log('video加载成功', videoEle)
+                            videoEle.currentTime = .5
+                            m.currentTime = videoEle.currentTime
+                            m.duration = videoEle.duration
+                            m.videoEle = videoEle
+                          }}
+                          onTimeupdate={(e: Event) => {
+                            const videoEle = e.target as unknown as HTMLVideoElement
+                            // console.log('video播放中')
+                            m.currentTime = videoEle.currentTime
+                          }}
+                          onPlay={() => {
+                            console.log('播放')
+                            m.paused = false
+                          }}
+                          onPause={() => {
+                            clearTimeout(m.timer)
+                            console.log('暂停')
+                            m.paused = true
+                          }}
+                        >
+                          <source src={m.content} type="video/mp4" />
+                        </video>
+                        <Transition name="bottom">
+                          {activeData.model && (
+                            <div class={styles.bottomFixedContainer}>
+                              <div class={styles.time}>
+                                <span>{getSecondRPM(m.currentTime)}</span>
+                                <span>{getSecondRPM(m.duration)}</span>
+                              </div>
+                              <div class={styles.slider}>
+                                <Slider
+                                  buttonSize={16}
+                                  step={0.01}
+                                  v-model:modelValue={m.currentTime}
+                                  onUpdate:modelValue={(val: any) => handleChangeSlider(val, m)}
+                                  min={0}
+                                  max={m.duration}
+                                />
+                              </div>
+
+                              <div class={styles.actions}>
+                                <div>
+                                  {m.paused ? (
+                                    <Icon
+                                      name={iconplay}
+                                      onClick={(e: Event) => {
+                                        e.stopPropagation()
+                                        clearTimeout(m.timer)
+                                        console.log('点击播放', m.videoEle)
+                                        m.videoEle?.play()
+                                        m.paused = false
+                                        m.timer = setTimeout(() => {
+                                          activeData.model = false
+                                        }, 3000)
+                                      }}
+                                    />
+                                  ) : (
+                                    <Icon
+                                      name={iconpause}
+                                      onClick={(e: Event) => {
+                                        e.stopPropagation()
+                                        console.log('点击暂停')
+                                        m.videoEle?.pause()
+                                        m.paused = true
+                                      }}
+                                    />
+                                  )}
+                                  {m.loop ? (
+                                    <Icon
+                                      name={iconLoopActive}
+                                      onClick={(e: Event) => {
+                                        e.stopPropagation()
+                                        m.loop = false
+                                      }}
+                                    />
+                                  ) : (
+                                    <Icon
+                                      name={iconLoop}
+                                      onClick={(e: Event) => {
+                                        e.stopPropagation()
+                                        m.loop = true
+                                      }}
+                                    />
+                                  )}
+                                </div>
+                                <div>{popupData.itemName}</div>
+                              </div>
+                            </div>
+                          )}
+                        </Transition>
+                      </>
+                    ) : m.type === 'IMG' ? (
+                      <img src={m.content} />
+                    ) : (
+                      <MusicScore music={m} />
+                    )}
+                    {/* <Transition name="van-fade">
+                      {activeData.model && <div class={styles.playModel}></div>}
+                    </Transition> */}
                   </div>
-                )}
+                </>
               </SwipeItem>
             )
           })}
         </Swipe>
-        <div
-          class={styles.playModel}
-          onClick={() => {
-            activeData.model = !activeData.model
-          }}
-        ></div>
+
         <Transition name="top">
           {activeData.model && (
             <div class={styles.headerContainer}>
@@ -336,30 +462,6 @@ export default defineComponent({
           )}
         </Transition>
 
-        <Transition name="bottom">
-          {activeData.model && (
-            <div class={styles.bottomFixedContainer}>
-              <div class={styles.time}>
-                <span>03:12</span>
-                <span>10:12</span>
-              </div>
-              <Slider
-                buttonSize={16}
-                style={{ margin: '8px 0' }}
-                v-model:modelValue={activeData.currentTime}
-                min={0}
-                max={100}
-              />
-              <div class={styles.actions}>
-                <div>
-                  <Icon name="play-circle" />
-                  <Icon name="pause-circle" />
-                </div>
-                <div>长笛的呼吸练习</div>
-              </div>
-            </div>
-          )}
-        </Transition>
         <Popup
           class={styles.popup}
           overlayClass={styles.overlayClass}

+ 7 - 3
src/views/exercise-record/exercis-detail.module.less

@@ -1,9 +1,13 @@
 @img: '@/school/images';
 
+.exercisContainer {
+  background: url('@{img}/detail-bg.png') top center/ cover no-repeat;
+  background-size: contain;
+}
 .topWrap {
-  height: 316px;
-  background: url('@{img}/detail-bg.png') center center/ cover;
-  margin-bottom: -90px;
+  // height: 316px;
+
+  // margin-bottom: -90px;
   .topInfo {
     padding: 34px 15px 30px;
     display: flex;

+ 83 - 77
src/views/exercise-record/exercis-detail.tsx

@@ -144,74 +144,75 @@ export default defineComponent({
     }
     return () => (
       <>
-        <div class={styles.topWrap}>
-          <OSticky position="top" background="#F8F8F8" onGetHeight={getHeight}>
-            <OHeader
-              border={false}
-              background={state.heightV > state.scrollTop ? 'transparent' : '#fff'}
-            >
-              {{
-                right: () => (
-                  <Icon
-                    name={questIcon}
-                    size={22}
-                    color="#333"
-                    onClick={() => {
-                      showTip.value = true
-                    }}
+        <div class={styles.exercisContainer}>
+          <div class={styles.topWrap}>
+            <OSticky position="top" background="#F8F8F8" onGetHeight={getHeight}>
+              <OHeader
+                border={false}
+                background={state.heightV > state.scrollTop ? 'transparent' : '#fff'}
+              >
+                {{
+                  right: () => (
+                    <Icon
+                      name={questIcon}
+                      size={22}
+                      color="#333"
+                      onClick={() => {
+                        showTip.value = true
+                      }}
+                    />
+                  )
+                }}
+              </OHeader>
+            </OSticky>
+            <div class={styles.topInfo}>
+              <div class={styles.topInfoLeft}>
+                <div class={styles.headWrap}>
+                  <Image
+                    src={infoDetail.value.avatar ? infoDetail.value.avatar : defaultIcon}
+                    fit="cover"
+                    width="68px"
+                    height="68px"
                   />
-                )
-              }}
-            </OHeader>
-          </OSticky>
-          <div class={styles.topInfo}>
-            <div class={styles.topInfoLeft}>
-              <div class={styles.headWrap}>
-                <Image
-                  src={infoDetail.value.avatar ? infoDetail.value.avatar : defaultIcon}
-                  fit="cover"
-                  width="68px"
-                  height="68px"
-                />
-              </div>
-              <div class={styles.infoMsg}>
-                <p>{infoDetail.value.nickname}</p>
-                <div class={styles.tag}>
-                  {infoDetail.value.subjectNames ? infoDetail.value.subjectNames : '暂无声部'}
+                </div>
+                <div class={styles.infoMsg}>
+                  <p>{infoDetail.value.nickname}</p>
+                  <div class={styles.tag}>
+                    {infoDetail.value.subjectNames ? infoDetail.value.subjectNames : '暂无声部'}
+                  </div>
                 </div>
               </div>
-            </div>
-            <div class={styles.topInfoRight}>
-              <div class={styles.infoDay}>
-                <p class={styles.infoDayMain}>
-                  {infoDetail.value.practiceDays ? infoDetail.value.practiceDays : 0}{' '}
-                  <span>天</span>
-                </p>
-                <p class={styles.infoDaysub}>练习天数</p>
-              </div>
-              <div class={styles.infoTime}>
-                <p class={styles.infoDayMain}>
-                  {infoDetail.value.practiceTimes ? infoDetail.value.practiceTimes : 0}{' '}
-                  <span>分钟</span>
-                </p>
-                <p class={styles.infoDaysub}>练习时长</p>
+              <div class={styles.topInfoRight}>
+                <div class={styles.infoDay}>
+                  <p class={styles.infoDayMain}>
+                    {infoDetail.value.practiceDays ? infoDetail.value.practiceDays : 0}{' '}
+                    <span>天</span>
+                  </p>
+                  <p class={styles.infoDaysub}>练习天数</p>
+                </div>
+                <div class={styles.infoTime}>
+                  <p class={styles.infoDayMain}>
+                    {infoDetail.value.practiceTimes ? infoDetail.value.practiceTimes : 0}{' '}
+                    <span>分钟</span>
+                  </p>
+                  <p class={styles.infoDaysub}>练习时长</p>
+                </div>
               </div>
             </div>
-          </div>
-          <div class={styles.chioseWrap}>
-            <div style={{ padding: '12px 13px', background: 'transparent' }}>
-              <div
-                class={styles.searchBand}
-                onClick={() => {
-                  state.showPopoverTime = true
-                }}
-              >
-                {forms.practiceMonthName}
-                <Icon name={state.showPopoverTime ? 'arrow-up' : 'arrow-down'} />
+            <div class={styles.chioseWrap}>
+              <div style={{ padding: '12px 13px', background: 'transparent' }}>
+                <div
+                  class={styles.searchBand}
+                  onClick={() => {
+                    state.showPopoverTime = true
+                  }}
+                >
+                  {forms.practiceMonthName}
+                  <Icon name={state.showPopoverTime ? 'arrow-up' : 'arrow-down'} />
+                </div>
               </div>
-            </div>
 
-            {/* <div style={{ padding: '12px 13px', background: 'transparent' }}>
+              {/* <div style={{ padding: '12px 13px', background: 'transparent' }}>
                 <Popover
                   v-model:show={state.showPopoverOrchestra}
                   actions={state.actions}
@@ -229,24 +230,29 @@ export default defineComponent({
                   }}
                 </Popover>
               </div> */}
+            </div>
           </div>
-        </div>
-        {showContact.value ? (
-          <PullRefresh v-model={refreshing.value} onRefresh={onRefresh} style="min-height: 100vh;">
-            <List
-              v-model:loading={loading.value}
-              finished={finished.value}
-              finished-text="没有更多了"
-              onLoad={getList}
+          {showContact.value ? (
+            <PullRefresh
+              v-model={refreshing.value}
+              onRefresh={onRefresh}
+              style="min-height: 100vh;"
             >
-              {list.value.map((item: any) => (
-                <DetailItem item={item} />
-              ))}
-            </List>
-          </PullRefresh>
-        ) : (
-          <OEmpty />
-        )}
+              <List
+                v-model:loading={loading.value}
+                finished={finished.value}
+                finished-text="没有更多了"
+                onLoad={getList}
+              >
+                {list.value.map((item: any) => (
+                  <DetailItem item={item} />
+                ))}
+              </List>
+            </PullRefresh>
+          ) : (
+            <OEmpty />
+          )}
+        </div>
 
         <Popup v-model:show={state.showPopoverTime} position="bottom" style="{ height: '30%' }">
           <DatePicker

+ 1 - 1
teacher.html

@@ -34,7 +34,7 @@
   <meta name="apple-touch-fullscreen" content="yes" />
   <!-- windows phone 点击无高光 -->
   <meta name="msapplication-tap-highlight" content="no" />
-  <title>酷乐秀</title>
+  <title>管乐团</title>
   <script src="/flexible.js" charset="UTF-8"></script>
   <script type="text/javascript">
     window.paymentType = 'TEACHER'