浏览代码

添加逻辑

lex 1 年之前
父节点
当前提交
aa89578a69

+ 14 - 2
src/components/card-preview/index.tsx

@@ -20,6 +20,11 @@ export default defineComponent({
     size: {
       type: String,
       default: 'default'
+    },
+    /** 是否下载 只支持 video audio */
+    isDownload: {
+      type: Boolean,
+      default: false
     }
   },
   emit: ['update:show'],
@@ -62,12 +67,19 @@ export default defineComponent({
           title={item.value.title}
           blockScroll={false}>
           {item.value.type === 'VIDEO' && (
-            <VideoModal poster={item.value.url} src={item.value.content} />
+            <VideoModal
+              title={item.value.title}
+              poster={item.value.url}
+              src={item.value.content}
+              isDownload={props.isDownload}
+            />
           )}
           {item.value.type === 'MUSIC' && (
             <MusicModal class={styles.musicPreview} item={item.value} />
           )}
-          {item.value.type === 'SONG' && <SongModal item={item.value} />}
+          {item.value.type === 'SONG' && (
+            <SongModal item={item.value} isDownload={props.isDownload} />
+          )}
           {item.value.type === 'PPT' && (
             <NSpin show={pptLoading.value}>
               <iframe

+ 37 - 2
src/components/card-preview/song-modal/index.tsx

@@ -3,9 +3,11 @@ import styles from './index.module.less';
 import iconplay from '@views/attend-class/image/icon-pause.png';
 import iconpause from '@views/attend-class/image/icon-play.png';
 import iconReplay from '@views/attend-class/image/icon-replay.png';
-import { NSlider } from 'naive-ui';
+import iconPreviewDownload from '@views/attend-class/image/icon-preivew-download.png';
+import { NSlider, useMessage } from 'naive-ui';
 import Vudio from 'vudio.js';
 import tickMp3 from '@views/attend-class/image/tick.mp3';
+import { saveAs } from 'file-saver';
 
 export default defineComponent({
   name: 'audio-play',
@@ -19,10 +21,14 @@ export default defineComponent({
     isEmtry: {
       type: Boolean,
       default: false
+    },
+    isDownload: {
+      type: Boolean,
+      default: false
     }
   },
-
   setup(props) {
+    const message = useMessage();
     const audioForms = reactive({
       paused: true,
       currentTimeNum: 0,
@@ -85,6 +91,27 @@ export default defineComponent({
     const onReplay = () => {
       if (!audio.value) return;
       audio.value.currentTime = 0;
+
+      console.log(props.item);
+    };
+
+    // 下载资源
+    const onDownload = () => {
+      if (!props.item.content) {
+        message.error('下载失败');
+        return;
+      }
+      const fileUrl = props.item.content;
+      const filename = props.item.title;
+      // 发起Fetch请求
+      fetch(fileUrl)
+        .then(response => response.blob())
+        .then(blob => {
+          saveAs(blob, filename || new Date().getTime() + '.mp3');
+        })
+        .catch(() => {
+          message.error('下载失败');
+        });
     };
 
     let vudio1 = null;
@@ -195,6 +222,14 @@ export default defineComponent({
               <button class={styles.iconReplay} onClick={onReplay}>
                 <img src={iconReplay} />
               </button>
+              {props.isDownload && (
+                <button
+                  class={styles.iconReplay}
+                  onClick={onDownload}
+                  style={{ marginLeft: '12px' }}>
+                  <img src={iconPreviewDownload} />
+                </button>
+              )}
             </div>
           </div>
         </div>

+ 38 - 1
src/components/card-preview/video-modal/index.tsx

@@ -8,7 +8,9 @@ import styles from './index.module.less';
 import iconplay from '@views/attend-class/image/icon-pause.png';
 import iconpause from '@views/attend-class/image/icon-play.png';
 import iconReplay from '@views/attend-class/image/icon-replay.png';
-import { NSlider } from 'naive-ui';
+import iconPreviewDownload from '@views/attend-class/image/icon-preivew-download.png';
+import { NSlider, useMessage } from 'naive-ui';
+import { saveAs } from 'file-saver';
 
 export default defineComponent({
   name: 'video-play',
@@ -17,6 +19,10 @@ export default defineComponent({
       type: String,
       default: ''
     },
+    title: {
+      type: String,
+      default: ''
+    },
     poster: {
       type: String,
       default: ''
@@ -24,10 +30,15 @@ export default defineComponent({
     isEmtry: {
       type: Boolean,
       default: false
+    },
+    isDownload: {
+      type: Boolean,
+      default: false
     }
   },
   emits: ['loadedmetadata', 'togglePlay', 'ended', 'reset'],
   setup(props, { emit, expose }) {
+    const message = useMessage();
     const { src, poster, isEmtry } = toRefs(props);
     const videoFroms = reactive({
       paused: true,
@@ -75,6 +86,24 @@ export default defineComponent({
       emit('togglePlay', videoFroms.paused);
     };
 
+    // 下载资源
+    const onDownload = () => {
+      if (!props.src) {
+        message.error('下载失败');
+        return;
+      }
+      const fileUrl = props.src;
+      // 发起Fetch请求
+      fetch(fileUrl)
+        .then(response => response.blob())
+        .then(blob => {
+          saveAs(blob, props.title || new Date().getTime() + '');
+        })
+        .catch(() => {
+          message.error('下载失败');
+        });
+    };
+
     onMounted(() => {
       videoItem.value = TCPlayer(videoID, {
         appID: '',
@@ -181,6 +210,14 @@ export default defineComponent({
               <button class={styles.iconReplay} onClick={onReplay}>
                 <img src={iconReplay} />
               </button>
+              {props.isDownload && (
+                <button
+                  class={styles.iconReplay}
+                  onClick={onDownload}
+                  style={{ marginLeft: '12px' }}>
+                  <img src={iconPreviewDownload} />
+                </button>
+              )}
             </div>
           </div>
         </div>

+ 2 - 0
src/shims-vue.d.ts

@@ -9,3 +9,5 @@ declare module 'vudio.js';
 declare module 'tim-profanity-filter-plugin';
 
 declare module 'tcplayer.js';
+
+declare module 'file-saver';

+ 9 - 0
src/utils/index.ts

@@ -364,3 +364,12 @@ export const exitFullscreen = () => {
     ? document.webkitExitFullscreen()
     : '';
 };
+
+/** 检测链接格式 */
+export function checkUrlType(urlType: string) {
+  const subfix = (urlType || '').split('.').pop();
+  if (subfix === 'wav' || subfix === 'mp3' || subfix === 'm4a') {
+    return 'audio';
+  }
+  return 'video';
+}

二进制
src/views/attend-class/image/icon-preivew-download.png


+ 67 - 20
src/views/attend-class/index.tsx

@@ -89,6 +89,7 @@ import bottomIconPre from './image/bottom_icon3.png';
 import bottomIconNext from './image/bottom_icon4.png';
 import rightHideIcon from './image/right_hide_icon.png';
 import SelectResources from '../prepare-lessons/model/select-resources';
+import { getStudentAfterWork } from '../studentList/api';
 
 export type ToolType = 'init' | 'pen' | 'whiteboard';
 export type ToolItem = {
@@ -188,6 +189,7 @@ export default defineComponent({
       removeVisiable: false,
       removeTitle: '',
       removeContent: '',
+      removeCourseStatus: false, // 是否布置作业
 
       selectResourceStatus: false,
       videoState: 'init' as 'init' | 'play',
@@ -259,7 +261,7 @@ export default defineComponent({
     const showModalTime = ref(false);
     // ifram事件处理
     const iframeHandle = (ev: MessageEvent) => {
-      console.log(ev.data?.api, ev.data, 'ev.data');
+      // console.log(ev.data?.api, ev.data, 'ev.data');
       if (ev.data?.api === 'headerTogge') {
         activeData.model =
           ev.data.show || (ev.data.playState == 'play' ? false : true);
@@ -858,6 +860,21 @@ export default defineComponent({
         // if (popupData.activeIndex === data.itemList.length - 1) return;
         setModalOpen();
         handlePreAndNext('down');
+      } else if (e.code === 'Space') {
+        // const activeItem = data.itemList[popupData.activeIndex];
+        // // // 暂停视频和曲谱的播放
+        // if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
+        //   activeItem.videoEle?.play();
+        // }
+        // if (activeItem.type === 'SONG' && activeItem.audioEle) {
+        //   activeItem.audioEle?.play();
+        // }
+        // if (activeItem.type === 'MUSIC') {
+        //   activeItem.iframeRef?.contentWindow?.postMessage(
+        //     { api: 'setPlayState' },
+        //     '*'
+        //   );
+        // }
       }
     });
 
@@ -1188,9 +1205,20 @@ export default defineComponent({
             data.removeTitle = '结束预览';
             data.removeContent = '请确认是否结束预览?';
           } else {
+            const res = await getStudentAfterWork({
+              courseScheduleId: data.classId,
+              page: 1,
+              rows: 99
+            });
+            if (res.data.rows && res.data.rows.length) {
+              data.removeContent = '请确认是否结束课程?';
+              data.removeCourseStatus = false;
+            } else {
+              data.removeContent = '本次课堂尚未布置作业,是否结束课程?';
+              data.removeCourseStatus = true;
+            }
             data.removeVisiable = true;
             data.removeTitle = '结束课程';
-            data.removeContent = '请确认是否结束课程?';
           }
           break;
         case 2:
@@ -1643,11 +1671,6 @@ export default defineComponent({
                 onClick={() => {
                   data.modelAttendStatus = false;
                   handleStop();
-                  // if (state.application) {
-                  //   emit('close');
-                  // } else {
-                  //   window.close();
-                  // }
                   data.modelAttendStatus = false;
                 }}>
                 暂不布置
@@ -1749,28 +1772,52 @@ export default defineComponent({
             <p>{data.removeContent}</p>
 
             <NSpace class={styles.btnGroupModal} justify="center">
-              <NButton round onClick={() => (data.removeVisiable = false)}>
-                取消
+              <NButton
+                round
+                onClick={() => {
+                  if (data.removeCourseStatus) {
+                    if (globalState.application) {
+                      document.exitFullscreen
+                        ? document.exitFullscreen()
+                        : document.mozCancelFullScreen
+                        ? document.mozCancelFullScreen()
+                        : document.webkitExitFullscreen
+                        ? document.webkitExitFullscreen()
+                        : '';
+                      emit('close');
+                    } else {
+                      window.close();
+                    }
+                  } else {
+                    data.removeVisiable = false;
+                  }
+                }}>
+                {data.removeCourseStatus ? '结束课程' : '取消'}
               </NButton>
               <NButton
                 round
                 type="primary"
                 onClick={() => {
                   //
-                  if (globalState.application) {
-                    document.exitFullscreen
-                      ? document.exitFullscreen()
-                      : document.mozCancelFullScreen
-                      ? document.mozCancelFullScreen()
-                      : document.webkitExitFullscreen
-                      ? document.webkitExitFullscreen()
-                      : '';
-                    emit('close');
+                  if (data.removeCourseStatus) {
+                    data.modelTrainStatus = true;
+                    data.removeVisiable = false;
                   } else {
-                    window.close();
+                    if (globalState.application) {
+                      document.exitFullscreen
+                        ? document.exitFullscreen()
+                        : document.mozCancelFullScreen
+                        ? document.mozCancelFullScreen()
+                        : document.webkitExitFullscreen
+                        ? document.webkitExitFullscreen()
+                        : '';
+                      emit('close');
+                    } else {
+                      window.close();
+                    }
                   }
                 }}>
-                确定
+                {data.removeCourseStatus ? '布置作业' : '确定'}
               </NButton>
             </NSpace>
           </div>

+ 5 - 0
src/views/attend-class/model/train-type/index.module.less

@@ -304,4 +304,9 @@
       }
     }
   }
+}
+
+.reportModel {
+  width: 1030px;
+  overflow: hidden;
 }

+ 64 - 7
src/views/attend-class/model/train-type/index.tsx

@@ -8,7 +8,8 @@ import {
   NTag,
   NModal,
   NProgress,
-  NTooltip
+  NTooltip,
+  useMessage
 } from 'naive-ui';
 import pTag from './images/p-tag.png';
 import eTag from './images/e-tag.png';
@@ -23,6 +24,8 @@ import eMessage from './images/e-message.png';
 import { useUserStore } from '/src/store/modules/users';
 import CardPreview from '/src/components/card-preview';
 import { vaildUrl } from '/src/utils/urlUtils';
+import { musicPracticeRecordDetail } from '/src/views/prepare-lessons/api';
+import { checkUrlType } from '/src/utils';
 type ItemType = {
   id: string | number;
   trainingType: 'PRACTICE' | 'EVALUATION';
@@ -32,6 +35,8 @@ type ItemType = {
   typeList: string[];
   allTimes?: number;
   trainingTimes?: number;
+  recordId?: string | number; // 评测记录编号
+  musicPracticeRecordId?: string | number;
 };
 
 export default defineComponent({
@@ -66,7 +71,7 @@ export default defineComponent({
   emits: ['click', 'delete', 'edit', 'offShelf'],
   setup(props, { emit }) {
     const userStore = useUserStore();
-
+    const message = useMessage();
     const removeVisiable = ref(false);
     const previewShow = ref(false);
     const preivewItem = ref({
@@ -82,13 +87,17 @@ export default defineComponent({
       // window.open(src, '_blank');
       // console.log(props.item, 'item');
       // console.log(props.item, '1212');
+      preivewItem.value = {
+        type: 'MUSIC',
+        content: props.item.musicId,
+        title: props.item.musicName
+      };
       previewShow.value = true;
     };
 
     const reportSrc = ref('');
     const detailVisiable = ref(false);
     const gotoRecode = (row: any) => {
-      console.log(row.id, 'gotoRecode');
       const tockn = userStore.getToken;
       reportSrc.value =
         vaildUrl() +
@@ -96,6 +105,39 @@ export default defineComponent({
       detailVisiable.value = true;
     };
 
+    const onBackLook = async () => {
+      try {
+        if (!props.item?.musicPracticeRecordId) {
+          message.error('暂无数据');
+          return;
+        }
+        const { data } = await musicPracticeRecordDetail({
+          id: props.item.musicPracticeRecordId
+        });
+        if (data.videoFilePath || data.recordFilePath) {
+          let lookTitle = '';
+          if (data.videoFilePath) {
+            lookTitle = checkUrlType(data.videoFilePath);
+          } else {
+            lookTitle = checkUrlType(data.recordFilePath);
+          }
+          const lookUrl = data.videoFilePath || data.recordFilePath;
+          preivewItem.value.content = lookUrl;
+          preivewItem.value.title = props.item.musicName;
+          if (lookTitle === 'video') {
+            preivewItem.value.type = 'VIDEO';
+          } else if (lookTitle === 'audio') {
+            preivewItem.value.type = 'SONG';
+          }
+          previewShow.value = true;
+        } else {
+          message.error('暂无数据');
+        }
+      } catch {
+        //
+      }
+    };
+
     const isPass = computed(() => {
       const item = props.item;
       if (item) {
@@ -115,6 +157,18 @@ export default defineComponent({
       }
     });
 
+    const isDownload = computed(() => {
+      if (
+        props.isDisabled &&
+        !props.isCLassWork &&
+        props.item.trainingType === 'EVALUATION'
+      ) {
+        return true;
+      } else {
+        return false;
+      }
+    });
+
     return () => (
       <div
         class={[
@@ -296,13 +350,13 @@ export default defineComponent({
           props.item.trainingType === 'EVALUATION' ? (
             <>
               <NSpace size={6}>
-                {/* <n-button
+                <n-button
                   quaternary
                   disabled={props.isDelete}
                   class={styles.operation}
                   onClick={(e: MouseEvent) => {
                     e.stopPropagation();
-                    removeVisiable.value = true;
+                    gotoRecode({ id: props.item.recordId });
                   }}>
                   <NTooltip showArrow={false}>
                     {{
@@ -317,7 +371,9 @@ export default defineComponent({
                   class={styles.operation}
                   onClick={(e: MouseEvent) => {
                     e.stopPropagation();
-                    onDetail();
+                    // onDetail();
+                    // musicPracticeRecordId;
+                    onBackLook();
                   }}>
                   <NTooltip showArrow={false}>
                     {{
@@ -325,7 +381,7 @@ export default defineComponent({
                       default: '查看回放'
                     }}
                   </NTooltip>
-                </n-button> */}
+                </n-button>
                 {/* <n-button
                   quaternary
                   disabled={props.isDelete}
@@ -389,6 +445,7 @@ export default defineComponent({
         <CardPreview
           v-model:show={previewShow.value}
           item={preivewItem.value}
+          isDownload={isDownload.value}
         />
 
         <NModal

+ 2 - 2
src/views/classList/modals/TrainingDetails.tsx

@@ -61,14 +61,14 @@ export default defineComponent({
           // `速度${configJson.evaluateSpeed}`,
           `${configJson.trainingTimes}分合格`
         ];
-        console.log('configJson.evaluateDifficult--', tList);
+        // console.log('configJson.evaluateDifficult--', tList);
       } else {
         tList = [
           `${configJson.practiceChapterBegin}-${configJson.practiceChapterEnd}小节`,
           `速度${configJson.practiceSpeed}`,
           `${configJson.trainingTimes}分钟`
         ];
-        console.log('configJson.evaluateDifficult', tList);
+        // console.log('configJson.evaluateDifficult', tList);
       }
       return tList;
     };

+ 2 - 2
src/views/classList/modals/classTrainingDetails.tsx

@@ -61,14 +61,14 @@ export default defineComponent({
           // `速度${configJson.evaluateSpeed}`,
           `${configJson.trainingTimes}分合格`
         ];
-        console.log('configJson.evaluateDifficult--', tList);
+        // console.log('configJson.evaluateDifficult--', tList);
       } else {
         tList = [
           `${configJson.practiceChapterBegin}-${configJson.practiceChapterEnd}小节`,
           `速度${configJson.practiceSpeed}`,
           `${configJson.trainingTimes}分钟`
         ];
-        console.log('configJson.evaluateDifficult', tList);
+        // console.log('configJson.evaluateDifficult', tList);
       }
       return tList;
     };

+ 9 - 0
src/views/prepare-lessons/api.ts

@@ -161,3 +161,12 @@ export const courseScheduleUpdate = (params?: any) => {
     data: params
   });
 };
+
+/**
+ * 评测详情
+ */
+export const musicPracticeRecordDetail = (params: any) => {
+  return request.get('/edu-app/musicPracticeRecord/detail/' + params.id, {
+    params: params
+  });
+};

+ 0 - 19
src/views/studentList/index.module.less

@@ -274,25 +274,6 @@
 .reportModel {
   width: 1030px;
   overflow: hidden;
-
-  //   /* 设置滚动条的宽度、颜色和轨道背景 */
-  //   iframe::-webkit-scrollbar {
-  //     width: 10px;
-  //   }
-
-  //   iframe::-webkit-scrollbar-thumb {
-  //     background-color: #888;
-  //   }
-
-  //   iframe::-webkit-scrollbar-track {
-  //     background-color: #f0f0f0;
-  //   }
-
-  //   /* Firefox */
-  //   iframe {
-  //     scrollbar-width: thin;
-  //     scrollbar-color: #888 #f0f0f0;
-  //   }
 }
 
 .addStudentWrap {

+ 3 - 3
src/views/studentList/modals/studentTraomomhDetails.tsx

@@ -52,19 +52,19 @@ export default defineComponent({
           // `速度${configJson.evaluateSpeed}`,
           `${configJson.trainingTimes}分合格`
         ];
-        console.log('configJson.evaluateDifficult--', tList);
+        // console.log('configJson.evaluateDifficult--', tList);
       } else {
         tList = [
           `${configJson.practiceChapterBegin}-${configJson.practiceChapterEnd}小节`,
           `速度${configJson.practiceSpeed}`,
           `${configJson.trainingTimes}分钟`
         ];
-        console.log('configJson.evaluateDifficult', tList);
+        // console.log('configJson.evaluateDifficult', tList);
       }
       return tList;
     };
     const getTrainingDetail = async (id: any) => {
-      console.log(id, 'getTrainingDetail');
+      // console.log(id, 'getTrainingDetail');
       try {
         const res = await getTrainingStudentDetail({
           studentLessonTrainingId: id