lex před 2 roky
rodič
revize
d55cfeb1d5
73 změnil soubory, kde provedl 4019 přidání a 26 odebrání
  1. 5 2
      package.json
  2. binární
      src/common/images/icon-audio.png
  3. binární
      src/common/images/icon-collect-active.png
  4. binární
      src/common/images/icon-collect-default.png
  5. binární
      src/common/images/icon-image.png
  6. binární
      src/common/images/icon-music.png
  7. binární
      src/common/images/icon-selected.png
  8. binární
      src/common/images/icon-video.png
  9. 144 0
      src/components/card-type/index.module.less
  10. 147 0
      src/components/card-type/index.tsx
  11. 28 9
      src/components/layout/layoutSilder.tsx
  12. 13 4
      src/router/routes/index.ts
  13. 14 1
      src/styles/index.less
  14. 20 0
      src/utils/contants.ts
  15. 18 0
      src/utils/searchArray.ts
  16. 111 0
      src/views/attend-class/component/audio-pay.tsx
  17. 37 0
      src/views/attend-class/component/audio.module.less
  18. 83 0
      src/views/attend-class/component/musicScore.module.less
  19. 102 0
      src/views/attend-class/component/musicScore.tsx
  20. 110 0
      src/views/attend-class/component/point.module.less
  21. 102 0
      src/views/attend-class/component/points.tsx
  22. 32 0
      src/views/attend-class/component/tool.module.less
  23. 39 0
      src/views/attend-class/component/tool.tsx
  24. 49 0
      src/views/attend-class/component/tools/pen.module.less
  25. 50 0
      src/views/attend-class/component/tools/pen.tsx
  26. 242 0
      src/views/attend-class/component/video-play.tsx
  27. 153 0
      src/views/attend-class/component/video.module.less
  28. 0 0
      src/views/attend-class/datas/data.json
  29. 11 0
      src/views/attend-class/image/back.svg
  30. 15 0
      src/views/attend-class/image/icon-arrow.svg
  31. 62 0
      src/views/attend-class/image/icon-assignHomework.svg
  32. 24 0
      src/views/attend-class/image/icon-dian.svg
  33. 42 0
      src/views/attend-class/image/icon-down-disabled.svg
  34. 42 0
      src/views/attend-class/image/icon-down.svg
  35. 19 0
      src/views/attend-class/image/icon-image-active.svg
  36. 19 0
      src/views/attend-class/image/icon-image.svg
  37. binární
      src/views/attend-class/image/icon-load.gif
  38. 23 0
      src/views/attend-class/image/icon-loop-active.svg
  39. 20 0
      src/views/attend-class/image/icon-loop.svg
  40. 42 0
      src/views/attend-class/image/icon-menu.svg
  41. binární
      src/views/attend-class/image/icon-more.png
  42. 28 0
      src/views/attend-class/image/icon-mulv.svg
  43. 12 0
      src/views/attend-class/image/icon-pause.svg
  44. binární
      src/views/attend-class/image/icon-pen.png
  45. 15 0
      src/views/attend-class/image/icon-play.svg
  46. 24 0
      src/views/attend-class/image/icon-point.svg
  47. 20 0
      src/views/attend-class/image/icon-song-active.svg
  48. 20 0
      src/views/attend-class/image/icon-song.svg
  49. 53 0
      src/views/attend-class/image/icon-start.svg
  50. 4 0
      src/views/attend-class/image/icon-touping.svg
  51. 42 0
      src/views/attend-class/image/icon-up-disabled.svg
  52. 42 0
      src/views/attend-class/image/icon-up.svg
  53. 9 0
      src/views/attend-class/image/icon-video-active.svg
  54. 9 0
      src/views/attend-class/image/icon-video.svg
  55. binární
      src/views/attend-class/image/icon-videobg.png
  56. 162 0
      src/views/attend-class/image/icon-whiteboard.svg
  57. 1 0
      src/views/attend-class/image/icon-zhibo.svg
  58. 360 0
      src/views/attend-class/index.module.less
  59. 693 0
      src/views/attend-class/index.tsx
  60. 5 0
      src/views/prepare-lessons/components/directory-main/index.module.less
  61. 1 1
      src/views/prepare-lessons/components/directory-main/index.tsx
  62. 118 0
      src/views/prepare-lessons/components/lesson-main/index.module.less
  63. 99 2
      src/views/prepare-lessons/components/lesson-main/index.tsx
  64. binární
      src/views/prepare-lessons/components/resource-main/images/icon-search.png
  65. 67 0
      src/views/prepare-lessons/components/resource-main/index.module.less
  66. 108 2
      src/views/prepare-lessons/components/resource-main/index.tsx
  67. binární
      src/views/prepare-lessons/images/icon-search.png
  68. 3 2
      src/views/prepare-lessons/index.module.less
  69. 23 3
      src/views/prepare-lessons/index.tsx
  70. 73 0
      src/views/prepare-lessons/model/attend-class/index.module.less
  71. 100 0
      src/views/prepare-lessons/model/attend-class/index.tsx
  72. 49 0
      src/views/prepare-lessons/model/resource-search-group/index.module.less
  73. 61 0
      src/views/prepare-lessons/model/resource-search-group/index.tsx

+ 5 - 2
package.json

@@ -22,17 +22,20 @@
     "prepare": "husky install"
   },
   "dependencies": {
-    "@vant/use": "^1.5.1",
+    "@vant/use": "^1.5.2",
     "@vueuse/core": "^10.2.0",
     "clean-deep": "^3.4.0",
     "dayjs": "^1.11.7",
     "echarts": "^5.4.2",
     "numeral": "^2.0.6",
     "pinia": "^2.1.4",
+    "plyr": "^3.7.8",
+    "query-string": "^8.1.0",
     "umi-request": "^1.4.0",
     "vue": "^3.3.4",
     "vue-router": "^4.1.6",
-    "vue3-lottie": "^2.7.0"
+    "vue3-lottie": "^2.7.0",
+    "wavesurfer.js": "^7.0.0-beta.11"
   },
   "devDependencies": {
     "@babel/core": "^7.21.4",

binární
src/common/images/icon-audio.png


binární
src/common/images/icon-collect-active.png


binární
src/common/images/icon-collect-default.png


binární
src/common/images/icon-image.png


binární
src/common/images/icon-music.png


binární
src/common/images/icon-selected.png


binární
src/common/images/icon-video.png


+ 144 - 0
src/components/card-type/index.module.less

@@ -0,0 +1,144 @@
+:global {
+  .n-card--bordered {
+    border: 2px solid rgba(202, 228, 244, 1) !important;
+
+    &:hover {
+      border: 2px solid rgba(0, 122, 254, 1) !important;
+    }
+  }
+}
+
+.card-section {
+  position: relative;
+  box-sizing: border-box;
+  width: 300px;
+  height: 220px;
+  border-radius: 14px;
+  overflow: hidden;
+  background: linear-gradient(270deg, #DBF1FF 0%, #E7F9FF 100%);
+  display: inline-flex;
+
+  // 鼠标经过时样式
+  &:hover {
+    .image {
+      transform: scale(1.1);
+      transition: all .3s ease-in-out;
+    }
+
+    .addBtn {
+      display: block;
+      opacity: 1;
+      transition: all .3s ease-in-out;
+    }
+  }
+
+  &.isActive {
+    border: 2px solid rgba(0, 122, 254, 1) !important;
+  }
+
+  // 封面样式
+  .cover {
+    width: 100%;
+    height: 170px;
+
+    img {
+      height: inherit;
+    }
+  }
+
+  .image {
+    transition: all .3s ease-in-out;
+  }
+
+  // 封面样式
+  .cover {
+    width: 100%;
+    height: 170px;
+
+    img {
+      height: inherit;
+    }
+  }
+
+  .image {
+    transition: all .3s ease-in-out;
+  }
+
+  :global {
+    .n-card__footer {
+      padding: 10px 12px;
+    }
+  }
+
+  .footer {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+
+  }
+
+  .title {
+    display: flex;
+    align-items: center;
+
+    .titleType {
+      width: 36px;
+      height: 17px;
+    }
+
+    .titleContent {
+      padding-left: 6px;
+      font-size: 16px;
+      max-width: 180px;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      color: #131415;
+      font-weight: 600;
+    }
+  }
+
+  // 收藏按钮
+  .iconCollect {
+    width: 34px;
+    height: 34px;
+    background: url('../../common/images/icon-collect-default.png') no-repeat center;
+    background-size: contain;
+    position: absolute;
+    right: 12px;
+
+    &.isCollect {
+      cursor: pointer;
+    }
+
+    &.isActive {
+      background: url('../../common/images/icon-collect-active.png') no-repeat center;
+      background-size: contain;
+    }
+  }
+
+  // 精选
+  .iconSelected {
+    background: url('../../common/images/icon-selected.png') no-repeat center;
+    background-size: contain;
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    width: 58px;
+    height: 29px;
+  }
+
+  // 添加
+  .addBtn {
+    position: absolute;
+    top: 6px;
+    right: 6px;
+    font-size: 16px;
+    font-weight: 600;
+    height: 32px;
+    border-radius: 8px;
+    display: none;
+    opacity: 0;
+    transition: all .3s ease-in-out;
+  }
+}

+ 147 - 0
src/components/card-type/index.tsx

@@ -0,0 +1,147 @@
+import { PropType, defineComponent } from 'vue';
+import styles from './index.module.less';
+import { NButton, NCard, NImage } from 'naive-ui';
+import iconImage from '@common/images/icon-image.png';
+import iconVideo from '@common/images/icon-video.png';
+import iconAudio from '@common/images/icon-audio.png';
+import iconMusic from '@common/images/icon-music.png';
+
+type itemType = {
+  id: string | number;
+  type: 'IMG' | 'VIDEO' | 'AUDIO' | 'SONG';
+  url: string;
+  title: string;
+  isCollect: boolean;
+  isSelected: boolean;
+};
+
+export default defineComponent({
+  name: 'card-type',
+  props: {
+    // 是否是选中状态
+    isActive: {
+      type: Boolean,
+      default: false
+    },
+    // 是否可以收藏
+    isCollect: {
+      type: Boolean,
+      default: true
+    },
+    // 是否显示收藏
+    isShowCollect: {
+      type: Boolean,
+      default: true
+    },
+    // 是否显示添加按钮
+    isShowAdd: {
+      type: Boolean,
+      default: false
+    },
+    item: {
+      type: Object as PropType<itemType>,
+      default: () => ({})
+    }
+  },
+  /**
+   * @type {string} click 点击事件
+   * @type {string} collect 收藏
+   * @type {string} add 添加
+   */
+  emits: ['click', 'collect', 'add'],
+  setup(props, { emit }) {
+    const formatType = (type: string) => {
+      let typeImg = iconImage;
+      switch (type) {
+        case 'IMG':
+          typeImg = iconImage;
+          break;
+        case 'VIDEO':
+          typeImg = iconVideo;
+          break;
+        case 'AUDIO':
+          typeImg = iconAudio;
+          break;
+        case 'SONG':
+          typeImg = iconMusic;
+          break;
+      }
+      return typeImg;
+    };
+
+    return () => (
+      <div
+        onClick={() => emit('click', props.item)}
+        class={[styles['card-section']]}>
+        <NCard
+          class={[
+            styles['card-section'],
+            props.isActive ? styles.isActive : ''
+          ]}>
+          {{
+            cover: () =>
+              ['IMG', 'VIDEO', 'SONG'].includes(props.item.type) && (
+                <NImage
+                  class={[styles.cover, styles.image]}
+                  lazy
+                  previewDisabled
+                  objectFit="cover"
+                  src={props.item.url}
+                />
+              ),
+            footer: () => (
+              <div class={styles.footer}>
+                <div class={styles.title}>
+                  <NImage
+                    class={[styles.titleType]}
+                    src={formatType(props.item.type)}
+                    objectFit="cover"
+                  />
+                  <span class={styles.titleContent}>{props.item.title}</span>
+                </div>
+                {/* 收藏 */}
+                {props.isShowCollect && (
+                  <i
+                    onClick={(e: MouseEvent) => {
+                      e.stopPropagation();
+                      e.preventDefault();
+                      // 判断是否可以收藏
+                      if (props.isCollect) {
+                        console.log('Collect');
+                        emit('collect');
+                      }
+                    }}
+                    class={[
+                      styles.iconCollect,
+                      props.isCollect ? styles.isCollect : '',
+                      props.item.isCollect ? styles.isActive : ''
+                    ]}></i>
+                )}
+
+                {/* 精选 */}
+                {props.item.isSelected && (
+                  <span class={styles.iconSelected}></span>
+                )}
+
+                {/* 添加按钮 */}
+                {props.isShowAdd && (
+                  <NButton
+                    type="primary"
+                    class={styles.addBtn}
+                    onClick={(e: MouseEvent) => {
+                      e.stopPropagation();
+                      e.preventDefault();
+                      emit('add');
+                      console.log('add');
+                    }}>
+                    添加
+                  </NButton>
+                )}
+              </div>
+            )
+          }}
+        </NCard>
+      </div>
+    );
+  }
+});

+ 28 - 9
src/components/layout/layoutSilder.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, reactive } from 'vue';
+import { defineComponent, onMounted, reactive } from 'vue';
 import { NImage } from 'naive-ui';
 import styles from './index.module.less';
 import logo from './images/logo.png';
@@ -17,16 +17,21 @@ import setNormal from './images/setNormal.png';
 import studentIcon from './images/studentIcon.png';
 import studentNormal from './images/studentNormal.png';
 import SilderItem from './modals/silderItem';
+import { watch } from 'fs';
+import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
 export default defineComponent({
   name: 'layoutSilder',
   setup() {
+    const router = useRouter();
+    const route = useRoute();
     const itemInfoList = reactive([
       {
         activeIcon: indexIcon,
         name: '主页',
         normalIcon: indexNormal,
         isActive: true,
-        id: 1
+        id: 1,
+        path: '/'
       },
       {
         activeIcon: studentIcon,
@@ -47,7 +52,8 @@ export default defineComponent({
         name: '备课',
         normalIcon: palyNormal,
         isActive: false,
-        id: 4
+        id: 4,
+        path: '/prepare-lessons'
       },
       {
         activeIcon: kuIcon,
@@ -72,17 +78,32 @@ export default defineComponent({
       }
     ]);
     const checkNavBar = (item: any) => {
-      console.log('checkNavBar', item);
       itemInfoList.forEach((now: any) => {
         now.isActive = false;
+        if (now.id == item.id) {
+          now.isActive = true;
+          item.path && router.push(item.path);
+        }
       });
+    };
+
+    onBeforeRouteUpdate((to: any) => {
+      console.log(to, 'to');
+      checkPathChange(to.path);
+    });
+
+    const checkPathChange = (path: string) => {
       itemInfoList.forEach((now: any) => {
-        if (now.id == item.id) {
+        now.isActive = false;
+        if (now.path === path) {
           now.isActive = true;
         }
       });
-      console.log('checkNavBar', item);
     };
+
+    onMounted(() => {
+      checkPathChange(route.path);
+    });
     return () => (
       <>
         <div class={styles.silder}>
@@ -91,9 +112,7 @@ export default defineComponent({
           </div>
           <div class={styles.sliderList}>
             {itemInfoList.map((item: any) => (
-              <SilderItem onCheckNavBar={checkNavBar} item={item}>
-                {' '}
-              </SilderItem>
+              <SilderItem onCheckNavBar={checkNavBar} item={item} />
             ))}
           </div>
         </div>

+ 13 - 4
src/router/routes/index.ts

@@ -18,15 +18,24 @@ export const constantRoutes: any[] = [
         path: '',
         name: 'Home',
         component: () => import('@/views/home/index')
+      },
+      {
+        name: 'prepare-lessons',
+        path: '/prepare-lessons',
+        component: () => import('@/views/prepare-lessons/index'),
+        meta: {
+          title: '备课',
+          singleLayout: 'blank'
+        }
       }
     ]
   },
   {
-    name: 'prepare-lessons',
-    path: '/prepare-lessons',
-    component: () => import('@/views/prepare-lessons/index'),
+    name: 'attend-class',
+    path: '/attend-class',
+    component: () => import('@/views/attend-class/index'),
     meta: {
-      title: '备课',
+      title: '开始上课',
       singleLayout: 'blank'
     }
   },

+ 14 - 1
src/styles/index.less

@@ -17,6 +17,19 @@ body {
   background-color: #f1f5ff;
 }
 
+.n-base-selection,
+.n-input {
+  border-radius: 8px;
+  min-height: 40px;
+  height: 40px;
+  font-size: 15px;
+  --n-height: 40px !important;
+}
+
+.n-base-select-menu .n-base-select-option {
+  font-size: 15px;
+}
+
 @font-face {
   font-family: 'dotfont';
   /* Project id  */
@@ -59,4 +72,4 @@ body {
   min-width: 8px;
   border-radius: 6px;
   background-color: rgb(159, 159, 159);
-}
+}

+ 20 - 0
src/utils/contants.ts

@@ -0,0 +1,20 @@
+/**
+ * @description: 教材
+ */
+export const teaching = {
+  1: '人教版',
+  2: '声部训练',
+  3: '小曲目',
+  4: '考级曲目'
+};
+
+/**
+ * @description: 乐器
+ */
+export const instrument = {
+  1: '坚笛',
+  2: '排萧',
+  3: '口风琴',
+  4: '陶笛',
+  5: '葫芦丝'
+};

+ 18 - 0
src/utils/searchArray.ts

@@ -0,0 +1,18 @@
+import * as constant from './contants';
+
+export function getValueForKey(obj: any) {
+  const arr: any[] = [];
+  for (const k in obj) {
+    arr.push({
+      label: obj[k],
+      value: k
+    });
+  }
+  return arr;
+}
+
+// 教材
+export const teachingArray = getValueForKey(constant.teaching);
+
+// 乐器
+export const instrumentArray = getValueForKey(constant.instrument);

+ 111 - 0
src/views/attend-class/component/audio-pay.tsx

@@ -0,0 +1,111 @@
+import { defineComponent, onMounted, toRefs } from 'vue';
+import styles from './audio.module.less';
+import WaveSurfer from 'wavesurfer.js';
+import iconplay from '../image/icon-pause.svg';
+import iconpause from '../image/icon-play.svg';
+
+export default defineComponent({
+  name: 'audio-play',
+  props: {
+    item: {
+      type: Object,
+      default: () => {
+        return {};
+      }
+    },
+    isEmtry: {
+      type: Boolean,
+      default: false
+    }
+  },
+  setup(props) {
+    const { item, isEmtry } = toRefs(props);
+    const audioId = 'a' + +Date.now() + Math.floor(Math.random() * 100);
+    onMounted(() => {
+      const audio = new Audio();
+      audio.controls = true;
+      audio.style.width = '100%';
+      audio.className = styles.audio;
+      document.querySelector(`#${audioId}`)?.appendChild(audio);
+      const wavesurfer = WaveSurfer.create({
+        container: document.querySelector(`#${audioId}`) as HTMLElement,
+        waveColor: '#C5C5C5',
+        progressColor: '#02baff',
+        url: item.value.content + '?t=' + +new Date(),
+        cursorWidth: 0,
+        height: 160,
+        normalize: true,
+        // Set a bar width
+        barWidth: 6,
+        // Optionally, specify the spacing between bars
+        barGap: 12,
+        // And the bar radius
+        barRadius: 12,
+        autoScroll: true,
+        /** If autoScroll is enabled, keep the cursor in the center of the waveform during playback */
+        autoCenter: true,
+        hideScrollbar: false,
+        media: audio
+      });
+
+      wavesurfer.once('interaction', () => {
+        // wavesurfer.play();
+      });
+    });
+
+    return () => (
+      <div class={styles.audioWrap}>
+        <div class={styles.audioContainer}>
+          <div id={audioId}></div>
+        </div>
+
+        <div class={styles.controls}></div>
+        {/* <div
+          class="plyr__controls bottomFixed ${styles.controls}">
+          <div class="${styles.actions}">
+            <div class="${styles.actionWrap}">
+              <button id="${playBtnId}" class="${styles.actionBtn}">
+                <div
+                  class="van-loading van-loading--circular"
+                  aria-live="polite"
+                  aria-busy="true">
+                  <span
+                    class="van-loading__spinner van-loading__spinner--circular"
+                    style="color: rgb(255, 255, 255);">
+                    <svg class="van-loading__circular" viewBox="25 25 50 50">
+                      <circle cx="50" cy="50" r="20" fill="none"></circle>
+                    </svg>
+                  </span>
+                </div>
+                <img class="${styles.playIcon}" src="${iconplay}" />
+                <img class="${styles.playIcon}" src="${iconpause}" />
+              </button>
+            </div>
+            <div class="${styles.time}">
+              <div
+                class="plyr__time plyr__time--current"
+                aria-label="Current time">
+                00:00
+              </div>
+              <span class="${styles.line}">/</span>
+              <div
+                class="plyr__time plyr__time--duration"
+                aria-label="Duration">
+                00:00
+              </div>
+            </div>
+          </div>
+          <div class="${styles.slider}">
+            <div class="plyr__progress">
+              <input data-plyr="seek" type="range" min="0" max="100" step="0.01" value="0" aria-label="Seek">
+                        <progress class="plyr__progress__buffer" min="0" max="100" value="0">% buffered</progress>
+              <span role="tooltip" class="plyr__tooltip">
+                00:00
+              </span>
+            </div>
+          </div>
+        </div> */}
+      </div>
+    );
+  }
+});

+ 37 - 0
src/views/attend-class/component/audio.module.less

@@ -0,0 +1,37 @@
+.audioWrap {
+  width: 100%;
+  height: 100%;
+  background-color: #fff;
+}
+
+.audioContainer {
+  position: relative;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
+  padding: 0 240px;
+
+  &>div {
+    flex: 1;
+  }
+
+  .audio {
+    position: absolute;
+    top: 0;
+    opacity: 0;
+  }
+}
+
+.controls {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  width: 100%;
+  background: rgba(0, 0, 0, 0.6);
+  backdrop-filter: blur(26px);
+  height: 150px;
+  padding: 0 260px 0 200px !important;
+  transition: all 0.5s;
+}

+ 83 - 0
src/views/attend-class/component/musicScore.module.less

@@ -0,0 +1,83 @@
+.musicScore {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  -webkit-overflow-scrolling: touch;
+  overflow: scroll;
+  .container {
+    position: relative;
+    display: block;
+    border: none;
+    width: 100%;
+    height: 100%;
+    z-index: 10;
+  }
+  .musicModel {
+    position: absolute;
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+  }
+}
+.errorModel {
+  position: absolute;
+  left: 0;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  background: #000;
+  display: flex;
+  align-items: center;
+  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;
+        }
+      }
+    }
+  }
+}

+ 102 - 0
src/views/attend-class/component/musicScore.tsx

@@ -0,0 +1,102 @@
+import { defineComponent, ref, watch } from 'vue';
+import { NSkeleton } from 'naive-ui';
+import styles from './musicScore.module.less';
+import iconStart from '../image/icon-start.svg';
+import { listenerMessage, postMessage } from '@/helpers/native-message';
+import { usePageVisibility } from '@vant/use';
+import { browser } from '@/helpers/utils';
+
+export default defineComponent({
+  name: 'musicScore',
+  props: {
+    music: {
+      type: Object,
+      default: () => ({})
+    },
+    activeModel: {
+      type: Boolean
+    }
+  },
+  emits: ['setIframe'],
+  setup(props, { emit }) {
+    const browserInfo = browser();
+    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 src = `https://test.lexiaoya.cn/orchestra-music-score/?_t=1687590480955&id=11707&modelType=practice&modeType=json&Authorization=bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib2F1dGgyLXJlc291cmNlIl0sImNsaWVudFR5cGUiOiJCQUNLRU5EIiwidXNlcl9uYW1lIjoiMTgxNjI4NTU4MDAiLCJzY29wZSI6WyJhbGwiXSwidXNlcklkIjoiMTAwMDE0OSIsImF1dGhvcml0aWVzIjpbIjE4MTYyODU1ODAwIl0sImp0aSI6IjY0MzA2NjllLTE5NGItNDk3Yy1hMDQ5LWM4YWUxMGU0NDNiOCIsImNsaWVudF9pZCI6ImptZWR1LWJhY2tlbmQifQ.aeJ0o-lSfx1Ok-YptZuC-AUY6M7k3LK9rr0Bmx7sj81pPt2HDiDqeT2PuriYdJacxWnxboyhdG_lwU0QK-W-vON97c45NQpSEFLVpZ0m1xdIqwllwf20xhyj5YJwdOFUzxf1TNEfGsHZg66J7wEJQBSzlmQwcxmEE5lqLVD8o_3f2SBqnWCj9RqE4air7FUemllMnZiu8HsS-TKtLDaGa_XW8Yb_Zjzzz6r5UcYNI-C1uKPXg18o1dhHBJ8O-Pl0U8WivPRub_opvO2NSn5L9YtPt7Dd50UeSwaIOdMeCGdii1bg__h77Stek1_5IaZLYkoo2gvmUA-xk09TwCQBpA`;
+    const checkView = () => {
+      fetch(src)
+        .then(() => {
+          renderSuccess.value = true;
+          renderError.value = false;
+        })
+        .catch(() => {
+          renderError.value = true;
+        });
+    };
+    watch(props.music, () => {
+      if (renderSuccess.value) return;
+      renderError.value = false;
+      if (props.music.display) {
+        checkView();
+      }
+    });
+
+    // 去云教练完整版
+    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
+      const src = `https://test.lexiaoya.cn/orchestra-music-score/?_t=1687590480955&id=11707&modelType=practice&modeType=json&Authorization=bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsib2F1dGgyLXJlc291cmNlIl0sImNsaWVudFR5cGUiOiJCQUNLRU5EIiwidXNlcl9uYW1lIjoiMTgxNjI4NTU4MDAiLCJzY29wZSI6WyJhbGwiXSwidXNlcklkIjoiMTAwMDE0OSIsImF1dGhvcml0aWVzIjpbIjE4MTYyODU1ODAwIl0sImp0aSI6IjY0MzA2NjllLTE5NGItNDk3Yy1hMDQ5LWM4YWUxMGU0NDNiOCIsImNsaWVudF9pZCI6ImptZWR1LWJhY2tlbmQifQ.aeJ0o-lSfx1Ok-YptZuC-AUY6M7k3LK9rr0Bmx7sj81pPt2HDiDqeT2PuriYdJacxWnxboyhdG_lwU0QK-W-vON97c45NQpSEFLVpZ0m1xdIqwllwf20xhyj5YJwdOFUzxf1TNEfGsHZg66J7wEJQBSzlmQwcxmEE5lqLVD8o_3f2SBqnWCj9RqE4air7FUemllMnZiu8HsS-TKtLDaGa_XW8Yb_Zjzzz6r5UcYNI-C1uKPXg18o1dhHBJ8O-Pl0U8WivPRub_opvO2NSn5L9YtPt7Dd50UeSwaIOdMeCGdii1bg__h77Stek1_5IaZLYkoo2gvmUA-xk09TwCQBpA`;
+      postMessage(
+        {
+          api: 'openAccompanyWebView',
+          content: {
+            url: src,
+            orientation: 0,
+            isHideTitle: true,
+            statusBarTextColor: false,
+            isOpenLight: true
+          }
+        },
+        () => {
+          if (browserInfo.ios) {
+            isLoading.value = true;
+          }
+        }
+      );
+    };
+
+    return () => (
+      <div class={styles.musicScore}>
+        <iframe
+          ref={iframeRef}
+          onLoad={() => {
+            emit('setIframe', iframeRef.value);
+            isLoaded.value = true;
+          }}
+          class={[styles.container, 'musicIframe']}
+          frameborder="0"
+          src={src}></iframe>
+        <div class={styles.skeletonWrap}>
+          <NSkeleton text repeat={8} />
+        </div>
+      </div>
+    );
+  }
+});

+ 110 - 0
src/views/attend-class/component/point.module.less

@@ -0,0 +1,110 @@
+.container {
+  display: flex;
+  flex-direction: column;
+  min-width: 288px;
+  max-width: 288px;
+  height: 100vh;
+  color: #333;
+  font-size: 12px;
+  box-sizing: border-box;
+  background:#fff;
+}
+.pointHead {
+  display: flex;
+  align-items: center;
+  padding: 13px 10px 15px 15px;
+  flex-shrink: 0;
+  font-size: 14px;
+  img {
+    width: 16px;
+    height: 16px;
+    margin-right: 7px;
+  }
+}
+.content {
+  flex: 1;
+  overflow-y: auto;
+  padding: 0 20px;
+}
+.collapse {
+  &.childActive {
+    :global {
+      .van-cell {
+        color: #fff;
+      }
+    }
+    .arrow {
+      opacity: 1;
+    }
+  }
+  &:after {
+    display: none;
+    border-width: 0;
+  }
+
+  .borderTop {
+    border-top: 1px solid rgba(255, 255, 255, 0.2);
+  }
+  :global {
+    .van-cell {
+      background: transparent;
+      font-size: 14px;
+      color: rgba(255, 255, 255, 0.5);
+      padding: 6px 0;
+    }
+    .van-cell__title {
+      font-weight: 600;
+    }
+    .van-collapse-item__title--expanded {
+      color: #fff;
+    }
+    .van-collapse-item--border:after {
+      display: none;
+    }
+    .van-collapse-item__content {
+      background: transparent;
+      color: #fff;
+      font-size: 10px;
+      padding: 0 0 6px;
+    }
+  }
+  .arrow {
+    display: block;
+    width: 10px;
+    height: 10px;
+    margin-right: 6px;
+    transition: all 0.3s;
+    opacity: 0.5;
+  }
+}
+.item {
+  display: flex;
+  align-items: center;
+  justify-content: flex-start;
+  padding: 6px 11px;
+  border-radius: 6px;
+  font-size: 12px;
+  :global {
+    .van-icon {
+      display: none;
+    }
+    .van-image {
+      margin-right: 6px;
+      margin-top: -1px;
+      width: 15px;
+      height: 16px;
+    }
+  }
+}
+.name{
+  flex: 1;
+}
+.itemActive {
+  background: #E0E0E0;
+  color: var(--van-primary);
+  :global {
+    .van-icon {
+      display: block;
+    }
+  }
+}

+ 102 - 0
src/views/attend-class/component/points.tsx

@@ -0,0 +1,102 @@
+import { defineComponent, reactive, watch } from 'vue';
+import styles from './point.module.less';
+import iconMulv from '../image/icon-mulv.svg';
+import iconArrow from '../image/icon-arrow.svg';
+import iconZhibo from '../image/icon-load.gif';
+import iconImage from '../image/icon-image.svg';
+import iconImageActive from '../image/icon-image-active.svg';
+import iconVideo from '../image/icon-video.svg';
+import iconVideoActive from '../image/icon-video-active.svg';
+import iconSong from '../image/icon-song.svg';
+import iconSongActive from '../image/icon-song-active.svg';
+import { Collapse, Icon, Image } from 'vant';
+export default defineComponent({
+  name: 'points',
+  props: {
+    data: {
+      type: Array,
+      default: () => []
+    },
+    tabActive: {
+      type: String,
+      default: ''
+    },
+    itemActive: {
+      type: String,
+      default: ''
+    }
+  },
+  emits: ['handleSelect'],
+  setup(props, { emit }) {
+    const pointData = reactive({
+      active: props.tabActive[0] || '',
+      childActive: props.tabActive[1] || ''
+    });
+    watch(
+      () => props.tabActive,
+      () => {
+        pointData.active = props.tabActive[0] || '';
+        pointData.childActive = props.tabActive[1] || '';
+      }
+    );
+
+    console.log(
+      pointData.active,
+      'pointData.active',
+      props.data,
+      props.tabActive
+    );
+
+    // 获取对应图片
+    const getImage = (item: any) => {
+      if (item.type === 'VIDEO') {
+        return props.itemActive == item.id ? iconVideoActive : iconVideo;
+      } else if (['IMAGE', 'IMG'].includes(item.type)) {
+        return props.itemActive == item.id ? iconImageActive : iconImage;
+      } else if (item.type === 'SONG') {
+        return props.itemActive == item.id ? iconSongActive : iconSong;
+      } else {
+        return props.itemActive == item.id ? iconVideoActive : iconVideo;
+      }
+    };
+    return () => (
+      <div class={styles.container}>
+        <div class={styles.pointHead}>
+          <img src={iconMulv} />
+          课件列表
+        </div>
+        <div class={styles.content}>
+          <Collapse
+            class={styles.collapse}
+            modelValue={pointData.active}
+            onUpdate:modelValue={(val: any) => {
+              pointData.active = val;
+            }}
+            accordion>
+            {props.data.map((item: any, index: number) => {
+              return (
+                <div
+                  class={[
+                    styles.item,
+                    props.itemActive == item.id ? styles.itemActive : ''
+                  ]}
+                  onClick={() => {
+                    emit('handleSelect', {
+                      itemActive: item.id,
+                      tabName: item.name
+                    });
+                  }}>
+                  <Image src={getImage(item)} class={styles.itemImage} />
+                  <span class={["van-ellipsis", styles.name]}>
+                    {item.name}
+                  </span>
+                  <Icon name={iconZhibo} />
+                </div>
+              );
+            })}
+          </Collapse>
+        </div>
+      </div>
+    );
+  }
+});

+ 32 - 0
src/views/attend-class/component/tool.module.less

@@ -0,0 +1,32 @@
+.tool {
+    position: relative;
+    width: 220px;
+    box-sizing: border-box;
+    height: 100vh;
+    color: #fff;
+    font-size: 13px;
+    font-weight: 500;
+    line-height: 18px;
+    * {
+        box-sizing: border-box;
+    }
+}
+
+.title {
+    padding: 12px 18px;
+}
+.grid{
+    :global{
+        .van-grid-item__content{
+            background: transparent;
+            padding: var(--van-padding-xs) var(--van-padding-xs);
+        }
+        .van-grid-item__text{
+            color: inherit;
+            margin-top: 2px;
+        }
+        .van-grid-item__icon{
+            font-size: 22px;
+        }
+    }
+}

+ 39 - 0
src/views/attend-class/component/tool.tsx

@@ -0,0 +1,39 @@
+import { Grid, GridItem } from 'vant';
+import { defineComponent } from 'vue';
+import styles from './tool.module.less';
+import iconPen from '../image/icon-pen.png';
+
+export type ToolType = 'init' | 'pen';
+
+export type ToolItem = {
+  type: ToolType;
+  name: string;
+  icon: string;
+};
+
+export default defineComponent({
+  name: 'tool',
+  emits: ['handleTool'],
+  setup(props, { emit }) {
+    const tool: ToolItem[] = [
+      {
+        type: 'pen',
+        icon: iconPen,
+        name: '批注'
+      }
+    ];
+    return () => (
+      <div class={styles.tool}>
+        <div class={styles.title}>教学功能</div>
+        <Grid class={styles.grid} columnNum={3} border={false}>
+          {tool.map(item => (
+            <GridItem
+              icon={item.icon}
+              text={item.name}
+              onClick={() => emit('handleTool', item)}></GridItem>
+          ))}
+        </Grid>
+      </div>
+    );
+  }
+});

+ 49 - 0
src/views/attend-class/component/tools/pen.module.less

@@ -0,0 +1,49 @@
+.pen {
+  position: fixed;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  top: 0;
+  z-index: 11;
+}
+
+.open {
+  display: block;
+}
+
+.hide {
+  display: none;
+}
+
+.iframe {
+  display: block;
+  width: 100%;
+  height: 100%;
+  border: 0;
+}
+
+.dely {
+  opacity: 0;
+}
+
+.rightItem {
+  position: absolute;
+  right: 15Px;
+  bottom: 0;
+  bottom: constant(safe-area-inset-bottom);
+  bottom: env(safe-area-inset-bottom);
+  width: 50Px;
+  height: 54Px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.img {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  display: block;
+}

+ 50 - 0
src/views/attend-class/component/tools/pen.tsx

@@ -0,0 +1,50 @@
+import { defineComponent, toRefs, ref, reactive } from 'vue';
+import styles from './pen.module.less';
+
+export default defineComponent({
+  name: 'pen-page',
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    },
+    close: {
+      type: Function,
+      default: () => ({})
+    }
+  },
+  setup(props) {
+    const { show } = toRefs(props);
+    const firstRender = ref(true);
+    const src = /(localhost|192)/.test(location.host)
+      ? 'https://test.lexiaoya.cn/whiteboard-noCollab'
+      : `${location.origin}/whiteboard-noCollab`;
+
+    return () => (
+      <div
+        class={[
+          styles.pen,
+          firstRender.value ? styles.dely : '',
+          show.value ? styles.open : styles.hide
+        ]}>
+        <iframe
+          class={styles.iframe}
+          frameborder="0"
+          width="100vw"
+          height="100vh"
+          src={src}
+          onLoad={() => {
+            firstRender.value = false;
+          }}></iframe>
+        <div class={styles.rightItem} onClick={() => props.close()}>
+          <svg width="22px" height="20px" viewBox="0 0 22 20">
+            <path
+              transform="translate(-1.000000, -2.000000)"
+              fill="#FFFFFF"
+              d="M13,2 C13.5522847,2 14,2.44771525 14,3 C14,3.51283584 13.6139598,3.93550716 13.1166211,3.99327227 L13,4 L3,4 L3,20 L13,20 C13.5128358,20 13.9355072,20.3860402 13.9932723,20.8833789 L14,21 C14,21.5128358 13.6139598,21.9355072 13.1166211,21.9932723 L13,22 L2,22 C1.48716416,22 1.06449284,21.6139598 1.00672773,21.1166211 L1,21 L1,3 C1,2.48716416 1.38604019,2.06449284 1.88337887,2.00672773 L2,2 L13,2 Z M17.7071068,7.05025253 L21.9497475,11.2928932 L21.9497475,11.2928932 C22.3402718,11.6834175 22.3402718,12.3165825 21.9497475,12.7071068 L17.7071068,16.9497475 C17.3165825,17.3402718 16.6834175,17.3402718 16.2928932,16.9497475 C15.9023689,16.5592232 15.9023689,15.9260582 16.2928932,15.5355339 L18.828,12.999 L9.29368112,13 C8.74139637,13 8.29368112,12.5522847 8.29368112,12 C8.29368112,11.4871642 8.67972131,11.0644928 9.17706,11.0067277 L9.29368112,11 L18.827,10.999 L16.2928932,8.46446609 C15.9023689,8.0739418 15.9023689,7.44077682 16.2928932,7.05025253 C16.6834175,6.65972824 17.3165825,6.65972824 17.7071068,7.05025253 Z"></path>
+          </svg>
+        </div>
+      </div>
+    );
+  }
+});

+ 242 - 0
src/views/attend-class/component/video-play.tsx

@@ -0,0 +1,242 @@
+import { defineComponent, nextTick, onMounted, toRefs } from 'vue';
+import 'plyr/dist/plyr.css';
+import Plyr from 'plyr';
+import { ref } from 'vue';
+import styles from './video.module.less';
+// import iconLoop from '../image/icon-loop.svg';
+// import iconLoopActive from '../image/icon-loop-active.svg';
+import iconplay from '../image/icon-pause.svg';
+import iconpause from '../image/icon-play.svg';
+
+export default defineComponent({
+  name: 'video-play',
+  props: {
+    item: {
+      type: Object,
+      default: () => {
+        return {};
+      }
+    },
+    isEmtry: {
+      type: Boolean,
+      default: false
+    }
+  },
+  emits: ['loadedmetadata', 'togglePlay', 'ended', 'reset'],
+  setup(props, { emit, expose }) {
+    const { item, isEmtry } = toRefs(props);
+    const videoRef = ref();
+    const videoItem = ref<Plyr>();
+    const controlID = 'v' + Date.now() + Math.floor(Math.random() * 100);
+    const playBtnId = 'play' + Date.now() + Math.floor(Math.random() * 100);
+    const loopBtnId = 'loop' + Date.now() + Math.floor(Math.random() * 100);
+    const toggleHideControl = (isShow: false) => {
+      videoItem.value?.toggleControls(isShow);
+    };
+    const togglePlay = (e: Event) => {
+      e.stopPropagation();
+      videoItem.value?.togglePlay();
+    };
+    const toggleLoop = () => {
+      const loopBtn = document.getElementById(loopBtnId);
+      if (!loopBtn || !videoItem.value) return;
+      const isLoop = videoItem.value.loop;
+      if (isLoop) {
+        loopBtn.classList.remove(styles.active);
+      } else {
+        loopBtn.classList.add(styles.active);
+      }
+      videoItem.value.loop = !videoItem.value.loop;
+    };
+    const onDefault = () => {
+      document
+        .getElementById(controlID)
+        ?.addEventListener('click', (e: Event) => {
+          e.stopPropagation();
+          emit('reset');
+        });
+      document.getElementById(playBtnId)?.addEventListener('click', togglePlay);
+      document.getElementById(loopBtnId)?.addEventListener('click', toggleLoop);
+    };
+
+    const changePlayBtn = (code: string) => {
+      const playBtn = document.getElementById(playBtnId);
+      if (!playBtn) return;
+      if (code == 'play') {
+        playBtn.classList.remove(styles.btnPause);
+        playBtn.classList.add(styles.btnPlay);
+      } else {
+        playBtn.classList.remove(styles.btnPlay);
+        playBtn.classList.add(styles.btnPause);
+      }
+    };
+    const controls = `
+            <div id="${controlID}" class="plyr__controls bottomFixed ${styles.controls}">
+                <div class="${styles.actions}">
+                    <div class="${styles.actionWrap}">
+                        <button id="${playBtnId}" class="${styles.actionBtn}">
+                            <div class="van-loading van-loading--circular" aria-live="polite" aria-busy="true"><span class="van-loading__spinner van-loading__spinner--circular" style="color: rgb(255, 255, 255);"><svg class="van-loading__circular" viewBox="25 25 50 50"><circle cx="50" cy="50" r="20" fill="none"></circle></svg></span></div>
+                            <img class="${styles.playIcon}" src="${iconplay}" />
+                            <img class="${styles.playIcon}" src="${iconpause}" />
+                        </button>
+                    </div>
+                    <div class="${styles.time}">
+                        <div class="plyr__time plyr__time--current" aria-label="Current time">00:00</div><span class="${styles.line}">/</span>
+                        <div class="plyr__time plyr__time--duration" aria-label="Duration">00:00</div>
+                    </div>
+                </div>
+                <div class="${styles.slider}">
+                    <div class="plyr__progress">
+                        <input data-plyr="seek" type="range" min="0" max="100" step="0.01" value="0" aria-label="Seek">
+                        <progress class="plyr__progress__buffer" min="0" max="100" value="0">% buffered</progress>
+                        <span role="tooltip" class="plyr__tooltip">00:00</span>
+                    </div>
+
+                </div>
+            </div>`;
+
+    // <div class="plyr__controls__item plyr__volume">
+    //   <button
+    //     type="button"
+    //     class="plyr__control"
+    //     data-plyr="mute"
+    //     aria-pressed="false"
+    //     aria-haspopup="true"
+    //     aria-controls="plyr-mute-1764"
+    //     aria-expanded="true">
+    //     <svg class="icon--pressed" aria-hidden="true" focusable="false">
+    //       <use xlink:href="#plyr-muted"></use>
+    //     </svg>
+    //     <svg class="icon--not-pressed" aria-hidden="true" focusable="false">
+    //       <use xlink:href="#plyr-volume"></use>
+    //     </svg>
+    //     <span class="label--pressed plyr__tooltip">
+    //       <font style="vertical-align: inherit;">
+    //         <font style="vertical-align: inherit;">取消静音</font>
+    //       </font>
+    //     </span>
+    //     <span class="label--not-pressed plyr__tooltip">
+    //       <font style="vertical-align: inherit;">
+    //         <font style="vertical-align: inherit;">沉默的</font>
+    //       </font>
+    //     </span>
+    //   </button>
+    //   <input
+    //     data-plyr="volume"
+    //     type="range"
+    //     min="0"
+    //     max="1"
+    //     step="0.05"
+    //     value="1"
+    //     autocomplete="off"
+    //     role="slider"
+    //     aria-label="体积"
+    //     aria-valuemin="0"
+    //     aria-valuemax="100"
+    //     aria-valuenow="100"
+    //     id="plyr-volume-1764"
+    //     aria-valuetext="100.0%"
+    //     style="--value: 100%;"></input>
+    // </div>;
+
+    // <div class="plyr__menu__container" id="plyr-mute-1764">
+    //   <div style="">
+    //     <div id="plyr-mute-1764-home">
+    //       <div role="menu">
+    //         <button
+    //           data-plyr="mute"
+    //           type="button"
+    //           class="plyr__control plyr__control--forward"
+    //           role="menuitem"
+    //           aria-haspopup="true">
+    //           <span>
+    //             Captions<span class="plyr__menu__value">English</span>
+    //           </span>
+    //         </button>
+    //       </div>
+    //     </div>
+    //   </div>
+    // </div>;
+
+    //
+
+    // <div class="${styles.actionWrap}">
+    //             <button id="${playBtnId}" class="${styles.actionBtn}">
+    //                 <div class="van-loading van-loading--circular" aria-live="polite" aria-busy="true"><span class="van-loading__spinner van-loading__spinner--circular" style="color: rgb(255, 255, 255);"><svg class="van-loading__circular" viewBox="25 25 50 50"><circle cx="50" cy="50" r="20" fill="none"></circle></svg></span></div>
+    //                 <img class="${styles.playIcon}" src="${iconplay}" />
+    //                 <img class="${styles.playIcon}" src="${iconpause}" />
+    //             </button>
+    //             <button id="${loopBtnId}" class="${styles.actionBtn} ${styles.loopBtn}">
+    //                 <img class="loop" src="${iconLoop}" />
+    //                 <img class="loopActive" src="${iconLoopActive}" />
+    //             </button>
+    //         </div>
+    //         <div>${item.value.name}</div>
+
+    onMounted(() => {
+      videoItem.value = new Plyr(videoRef.value, {
+        autoplay: true,
+        controls: controls,
+        autopause: true, // 一次只允许
+        ratio: '16:9', // 强制所有视频的纵横比
+        hideControls: false, // 在 2 秒没有鼠标或焦点移动、控制元素模糊(制表符退出)、播放开始或进入全屏时自动隐藏视频控件。只要移动鼠标、聚焦控制元素或暂停播放,控件就会立即重新出现。
+        clickToPlay: false, // 单击(或点击)视频容器将切换播放/暂停
+        fullscreen: { enabled: false, fallback: false, iosNative: false } // 不适用全屏
+      });
+      if (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);
+        });
+        videoItem.value.on('pause', () => {
+          changePlayBtn('play');
+          emit('togglePlay', videoItem.value?.paused);
+        });
+        videoItem.value.on('ended', (e: Event) => {
+          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();
+        });
+      }
+    });
+    expose({
+      changePlayBtn,
+      toggleHideControl
+    });
+    return () => (
+      <div class={styles.videoWrap}>
+        <video
+          style={{ width: '100%', height: '100%' }}
+          src={isEmtry.value ? '' : item.value.content}
+          ref={videoRef}
+          playsinline="false"></video>
+      </div>
+    );
+  }
+});

+ 153 - 0
src/views/attend-class/component/video.module.less

@@ -0,0 +1,153 @@
+.videoWrap {
+  width: 100%;
+  height: 100%;
+  --plyr-color-main: #198CFE;
+  --plyr-range-track-height: 13px;
+  --plyr-tooltip-radius: 3px;
+  --plyr-range-thumb-height: 32px;
+
+
+  :global {
+    .plyr--video {
+      width: 100%;
+      height: 100%;
+    }
+
+    .plyr__time {
+      display: block !important;
+    }
+
+    .plyr__video-wrapper {
+      pointer-events: none;
+    }
+  }
+}
+
+:global(.bottomFixed).controls {
+  width: 100%;
+  // background: linear-gradient(0deg, rgba(0, 0, 0, 0.5), transparent);
+  background: rgba(0, 0, 0, 0.6);
+  backdrop-filter: blur(26px);
+  height: 150px;
+  padding: 0 260px 0 200px !important;
+  // flex-direction: column;
+  transition: all 0.5s;
+
+  .time {
+    display: flex;
+    justify-content: space-between;
+    color: #fff;
+    // font-size: 10px;
+    padding: 4px 0 4px 20px;
+    font-size: 24px;
+    font-weight: 600;
+    line-height: 33px;
+
+    .line {
+      font-size: 12px;
+    }
+
+    :global {
+      .plyr__time+.plyr__time:before {
+        content: '';
+        margin-right: 0;
+      }
+    }
+  }
+
+  .slider {
+    width: 100%;
+    padding: 0 20px 0 12px;
+
+    :global {
+      .van-slider__button {
+        background: var(--van-primary);
+      }
+
+      .van-loading {
+        width: 100%;
+        height: 100%;
+      }
+    }
+  }
+
+  .actions {
+    display: flex;
+    justify-content: space-between;
+    // width: 100%;
+    color: #fff;
+    font-size: 12px;
+    padding: 0 20px;
+    align-items: center;
+
+    .actionWrap {
+      display: flex;
+    }
+
+    .actionBtn {
+      display: flex;
+      width: 82px;
+      height: 82px;
+      padding: 4px 0;
+      background: transparent;
+    }
+
+    .actionBtn>img {
+      width: 100%;
+      height: 100%;
+    }
+
+    :global {
+      .van-loading__circular {
+        width: 100%;
+        height: 100%;
+      }
+    }
+
+    .playIcon {
+      display: none;
+    }
+
+    .btnPlay img:nth-child(2) {
+      display: block;
+    }
+
+    .btnPause img:nth-child(3) {
+      display: block;
+    }
+
+    .btnPlay,
+    .btnPause {
+      :global {
+        .van-loading {
+          display: none;
+        }
+      }
+    }
+
+    .loopBtn {
+      :global {
+        .loop {
+          display: block;
+        }
+
+        .loopActive {
+          display: none;
+        }
+      }
+    }
+
+    .loopBtn.active {
+      :global {
+        .loop {
+          display: none;
+        }
+
+        .loopActive {
+          display: block;
+        }
+      }
+    }
+
+  }
+}

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 0 - 0
src/views/attend-class/datas/data.json


+ 11 - 0
src/views/attend-class/image/back.svg

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="19px" height="19px" viewBox="0 0 19 19" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
+        <g id="课件播放(老师)" transform="translate(-40.000000, -13.000000)" stroke="#FFFFFF" stroke-width="2">
+            <g id="图标/通用/返回" transform="translate(40.000000, 13.000000)">
+                <polyline id="Stroke-1" points="13.5 18 5 9.5 13.5 1"></polyline>
+            </g>
+        </g>
+    </g>
+</svg>

+ 15 - 0
src/views/attend-class/image/icon-arrow.svg

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="课件播放目录(老师)" transform="translate(-570.000000, -213.000000)" fill="#FFFFFF">
+            <g id="编组" transform="translate(548.000000, 0.000000)">
+                <g id="编组-5" transform="translate(22.000000, 209.000000)">
+                    <g id="展开更多备份" transform="translate(6.000000, 10.000000) rotate(-90.000000) translate(-6.000000, -10.000000) translate(0.000000, 4.000000)">
+                        <path d="M6.85328183,4.39627936 L10.070225,9.66036817 C10.3582139,10.1316227 10.2096477,10.7471111 9.73839317,11.0351 C9.58138526,11.1310493 9.40094791,11.1818182 9.21694316,11.1818182 L2.78305684,11.1818182 C2.23077209,11.1818182 1.78305684,10.7341029 1.78305684,10.1818182 C1.78305684,9.99781343 1.83382572,9.81737609 1.92977501,9.66036817 L5.14671817,4.39627936 C5.43470705,3.92502482 6.05019547,3.77645865 6.52145001,4.06444754 C6.6568159,4.14717114 6.77055823,4.26091347 6.85328183,4.39627936 Z" id="展开更多" transform="translate(6.000000, 7.090909) rotate(-180.000000) translate(-6.000000, -7.090909) "></path>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 62 - 0
src/views/attend-class/image/icon-assignHomework.svg

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="187px" height="60px" viewBox="0 0 187 60" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <defs>
+        <linearGradient x1="1.51267887e-13%" y1="15.1852058%" x2="100%" y2="70.279218%" id="linearGradient-1">
+            <stop stop-color="#88E1FF" offset="0%"></stop>
+            <stop stop-color="#2C8DFF" offset="100%"></stop>
+        </linearGradient>
+        <circle id="path-2" cx="30" cy="30" r="30"></circle>
+        <filter x="-8.3%" y="-5.0%" width="116.7%" height="120.0%" filterUnits="objectBoundingBox" id="filter-3">
+            <feOffset dx="0" dy="4" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0.43649793   0 0 0 0 0.550193556   0 0 0 0 0.741508152  0 0 0 0.273082386 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+        <filter x="-10.0%" y="-6.7%" width="120.0%" height="123.3%" filterUnits="objectBoundingBox" id="filter-4">
+            <feGaussianBlur stdDeviation="3" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
+            <feOffset dx="0" dy="2" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 1   0 0 0 0 1   0 0 0 0 1  0 0 0 0.5 0" type="matrix" in="shadowInnerInner1" result="shadowMatrixInner1"></feColorMatrix>
+            <feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner2"></feGaussianBlur>
+            <feOffset dx="-2" dy="-6" in="shadowBlurInner2" result="shadowOffsetInner2"></feOffset>
+            <feComposite in="shadowOffsetInner2" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner2"></feComposite>
+            <feColorMatrix values="0 0 0 0 0.108247927   0 0 0 0 0.476908508   0 0 0 0 0.983016304  0 0 0 0.462194056 0" type="matrix" in="shadowInnerInner2" result="shadowMatrixInner2"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixInner1"></feMergeNode>
+                <feMergeNode in="shadowMatrixInner2"></feMergeNode>
+            </feMerge>
+        </filter>
+        <filter x="-104.3%" y="-87.6%" width="308.7%" height="275.2%" filterUnits="objectBoundingBox" id="filter-5">
+            <feOffset dx="2" dy="5" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="4" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0.127134146   0 0 0 0 0.509284371   0 0 0 0 1  0 0 0 1 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixOuter1"></feMergeNode>
+                <feMergeNode in="SourceGraphic"></feMergeNode>
+            </feMerge>
+        </filter>
+    </defs>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="50、上课页面" transform="translate(-40.000000, -40.000000)">
+            <g id="布置作业" transform="translate(40.000000, 40.000000)">
+                <g id="编组-11备份-2">
+                    <rect id="矩形" fill="#DCEDFF" x="20" y="6" width="167" height="48" rx="24"></rect>
+                    <g id="椭圆形">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-3)" xlink:href="#path-2"></use>
+                        <use fill="url(#linearGradient-1)" fill-rule="evenodd" xlink:href="#path-2"></use>
+                        <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
+                    </g>
+                    <text id="布置作业" font-family="STYuanti-SC-Bold, Yuanti SC" font-size="24" font-weight="bold" letter-spacing="0.857142857" fill="#0378EC">
+                        <tspan x="70" y="38">布置作业</tspan>
+                    </text>
+                </g>
+                <g id="编组-20备份" filter="url(#filter-5)" transform="translate(16.979034, 13.000000)" stroke="#FFFFFF" stroke-linecap="round" stroke-width="3.3">
+                    <path d="M11.0629156,2.92618064 L11.0629156,2.92618064 L22.3036849,2.92618064 C24.3566824,2.92618064 26.0209657,4.60205759 26.0209657,6.66935656 L26.0209657,27.2568241 C26.0209657,29.3241231 24.3566824,31 22.3036849,31 L3.71728082,31 C1.66428331,31 0,29.3241231 0,27.2568241 L0,12.842191 L0,12.842191" id="路径"></path>
+                    <line x1="13.2497405" y1="16.9630903" x2="20.3053273" y2="16.9630903" id="路径-12"></line>
+                    <line x1="7.43456164" y1="23.5136482" x2="20.3053273" y2="23.5136482" id="路径-12备份"></line>
+                    <line x1="-1.36638462e-13" y1="5.28647048e-13" x2="9.98808831" y2="12.9965999" id="路径-13"></line>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 24 - 0
src/views/attend-class/image/icon-dian.svg

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <defs>
+        <path d="M23.21608,16.0687402 L23.3033009,16.1464466 C23.6659306,16.5090763 23.6918327,17.0809217 23.3810072,17.4734394 L23.3033009,17.5606602 L17.6464466,23.2175144 C17.2838169,23.5801441 16.7119715,23.6060462 16.3194539,23.2952208 L16.232233,23.2175144 L12.6966991,19.6819805 C12.3061748,19.2914562 12.3061748,18.6582912 12.6966991,18.267767 C13.0593288,17.9051373 13.6311742,17.8792351 14.0236919,18.1900606 L14.1109127,18.267767 L16.9384817,21.0967502 L21.8890873,16.1464466 C22.251717,15.7838169 22.8235624,15.7579148 23.21608,16.0687402 Z M11,0 C14.8659932,0 18,3.13400675 18,7 C18,9.41274397 16.7793245,11.5403839 14.9218582,12.7990351 C15.9323681,13.229421 16.8689386,13.8268917 17.694678,14.5714883 C18.1048344,14.9413398 18.1375079,15.5736612 17.7676564,15.9838176 C17.3978048,16.393974 16.7654834,16.4266475 16.355327,16.0567959 C14.8954225,14.7403517 13.0066893,14 11,14 C6.581722,14 3,17.581722 3,22 C3,22.5522847 2.55228475,23 2,23 C1.44771525,23 1,22.5522847 1,22 C1,17.8693735 3.50442274,14.3236863 7.07765565,12.7985509 C5.22023262,11.5396119 4,9.41230625 4,7 C4,3.13400675 7.13400675,0 11,0 Z M11,2 C8.23857625,2 6,4.23857625 6,7 C6,9.76142375 8.23857625,12 11,12 C13.7614237,12 16,9.76142375 16,7 C16,4.23857625 13.7614237,2 11,2 Z" id="path-1"></path>
+        <filter x="-13.3%" y="-12.8%" width="126.6%" height="125.5%" filterUnits="objectBoundingBox" id="filter-2">
+            <feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.125792177 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(-740.000000, -151.000000)" fill-rule="nonzero">
+            <g id="编组-8" transform="translate(732.000000, 66.000000)">
+                <g id="编组-7" transform="translate(0.000000, 75.000000)">
+                    <g id="形状结合" transform="translate(8.000000, 10.000000)">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
+                        <use fill="#FFFFFF" xlink:href="#path-1"></use>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 42 - 0
src/views/attend-class/image/icon-down-disabled.svg

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="64px" height="66px" viewBox="0 0 64 66" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组 11</title>
+    <defs>
+        <linearGradient x1="1.51267887e-13%" y1="15.1852058%" x2="100%" y2="70.279218%" id="linearGradient-1">
+            <stop stop-color="#E4E4E4" offset="0%"></stop>
+            <stop stop-color="#AAAAAA" offset="100%"></stop>
+        </linearGradient>
+        <circle id="path-2" cx="30" cy="30" r="30"></circle>
+        <filter x="-8.3%" y="-5.0%" width="116.7%" height="120.0%" filterUnits="objectBoundingBox" id="filter-3">
+            <feOffset dx="0" dy="4" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0.808310688   0 0 0 0 0.808310688   0 0 0 0 0.808310688  0 0 0 0.126502404 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+        <filter x="-10.0%" y="-6.7%" width="120.0%" height="123.3%" filterUnits="objectBoundingBox" id="filter-4">
+            <feGaussianBlur stdDeviation="3" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
+            <feOffset dx="0" dy="2" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 1   0 0 0 0 1   0 0 0 0 1  0 0 0 0.5 0" type="matrix" in="shadowInnerInner1" result="shadowMatrixInner1"></feColorMatrix>
+            <feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner2"></feGaussianBlur>
+            <feOffset dx="-2" dy="-6" in="shadowBlurInner2" result="shadowOffsetInner2"></feOffset>
+            <feComposite in="shadowOffsetInner2" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner2"></feComposite>
+            <feColorMatrix values="0 0 0 0 0.466145833   0 0 0 0 0.466145833   0 0 0 0 0.466145833  0 0 0 0.462194056 0" type="matrix" in="shadowInnerInner2" result="shadowMatrixInner2"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixInner1"></feMergeNode>
+                <feMergeNode in="shadowMatrixInner2"></feMergeNode>
+            </feMerge>
+        </filter>
+    </defs>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="55、音频预览" transform="translate(-1818.000000, -975.000000)">
+            <g id="编组-11" transform="translate(1820.000000, 975.000000)">
+                <g id="椭圆形">
+                    <use fill="black" fill-opacity="1" filter="url(#filter-3)" xlink:href="#path-2"></use>
+                    <use fill="url(#linearGradient-1)" fill-rule="evenodd" xlink:href="#path-2"></use>
+                    <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
+                </g>
+                <polyline id="Stroke-1" stroke="#FFFFFF" stroke-width="5.65714286" stroke-linecap="round" stroke-linejoin="round" transform="translate(30.142857, 30.142857) rotate(-90.000000) translate(-30.142857, -30.142857) " points="37.4285714 44.2857143 22.8571429 30.1428571 37.4285714 16"></polyline>
+            </g>
+        </g>
+    </g>
+</svg>

+ 42 - 0
src/views/attend-class/image/icon-down.svg

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="64px" height="66px" viewBox="0 0 64 66" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组 11</title>
+    <defs>
+        <linearGradient x1="1.51267887e-13%" y1="15.1852058%" x2="100%" y2="70.279218%" id="linearGradient-1">
+            <stop stop-color="#88E1FF" offset="0%"></stop>
+            <stop stop-color="#2C8DFF" offset="100%"></stop>
+        </linearGradient>
+        <circle id="path-2" cx="30" cy="30" r="30"></circle>
+        <filter x="-8.3%" y="-5.0%" width="116.7%" height="120.0%" filterUnits="objectBoundingBox" id="filter-3">
+            <feOffset dx="0" dy="4" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0.43649793   0 0 0 0 0.550193556   0 0 0 0 0.741508152  0 0 0 0.273082386 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+        <filter x="-10.0%" y="-6.7%" width="120.0%" height="123.3%" filterUnits="objectBoundingBox" id="filter-4">
+            <feGaussianBlur stdDeviation="3" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
+            <feOffset dx="0" dy="2" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 1   0 0 0 0 1   0 0 0 0 1  0 0 0 0.5 0" type="matrix" in="shadowInnerInner1" result="shadowMatrixInner1"></feColorMatrix>
+            <feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner2"></feGaussianBlur>
+            <feOffset dx="-2" dy="-6" in="shadowBlurInner2" result="shadowOffsetInner2"></feOffset>
+            <feComposite in="shadowOffsetInner2" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner2"></feComposite>
+            <feColorMatrix values="0 0 0 0 0.108247927   0 0 0 0 0.476908508   0 0 0 0 0.983016304  0 0 0 0.462194056 0" type="matrix" in="shadowInnerInner2" result="shadowMatrixInner2"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixInner1"></feMergeNode>
+                <feMergeNode in="shadowMatrixInner2"></feMergeNode>
+            </feMerge>
+        </filter>
+    </defs>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="52、乐谱预览" transform="translate(-1818.000000, -980.000000)">
+            <g id="编组-11" transform="translate(1820.000000, 980.000000)">
+                <g id="椭圆形">
+                    <use fill="black" fill-opacity="1" filter="url(#filter-3)" xlink:href="#path-2"></use>
+                    <use fill="url(#linearGradient-1)" fill-rule="evenodd" xlink:href="#path-2"></use>
+                    <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
+                </g>
+                <polyline id="Stroke-1" stroke="#FFFFFF" stroke-width="5.65714286" stroke-linecap="round" stroke-linejoin="round" transform="translate(30.142857, 30.142857) rotate(-90.000000) translate(-30.142857, -30.142857) " points="37.4285714 44.2857143 22.8571429 30.1428571 37.4285714 16"></polyline>
+            </g>
+        </g>
+    </g>
+</svg>

+ 19 - 0
src/views/attend-class/image/icon-image-active.svg

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="15px" height="16px" viewBox="0 0 15 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <g id="页面-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="课件播放(三层)" transform="translate(-306.000000, -298.000000)" fill="#FF8057" fill-rule="nonzero">
+            <g id="编组" transform="translate(306.000000, 0.000000)">
+                <g id="编组-8" transform="translate(0.000000, 93.500000)">
+                    <g id="编组-12" transform="translate(0.000000, 65.000000)">
+                        <g id="知识点目录/图片备份" transform="translate(0.000000, 140.000000)">
+                            <g id="编组" transform="translate(7.500000, 7.500000) scale(-1, 1) translate(-7.500000, -7.500000) translate(0.500000, 2.000000)">
+                                <path d="M12.6293706,0 C13.3863483,-1.39054547e-16 14,0.613651672 14,1.37062937 L14,1.37062937 L14,9.62937063 C14,10.3863483 13.3863483,11 12.6293706,11 L12.6293706,11 L1.37062937,11 C0.613651672,11 9.27030316e-17,10.3863483 0,9.62937063 L0,9.62937063 L0,1.37062937 C-9.27030316e-17,0.613651672 0.613651672,1.39054547e-16 1.37062937,0 L1.37062937,0 Z M9.22142099,4.12294163 C8.89196248,3.93663301 8.47385062,4.05267893 8.287542,4.38213744 L8.287542,4.38213744 L7.04470782,6.57990105 C6.98353166,6.68808179 6.89408921,6.77759053 6.78595381,6.8388468 C6.4566334,7.02539942 6.03843569,6.90966326 5.85188307,6.58034285 L5.85188307,6.58034285 L5.64657316,6.21791028 C5.58509397,6.10938138 5.49516084,6.01968934 5.3864673,5.95850172 C5.05664729,5.77283381 4.63876177,5.88969218 4.45309386,6.21951219 L4.45309386,6.21951219 L3.17297968,8.49350383 C3.11520712,8.59613088 3.08485695,8.71191444 3.08485695,8.82968531 C3.08485695,9.20817416 3.39168279,9.515 3.77017164,9.515 L3.77017164,9.515 L11.2094939,9.515 C11.3277631,9.515 11.4440194,9.4843926 11.5469559,9.42615461 C11.8763768,9.23977951 11.9923383,8.82164423 11.8059632,8.49222333 L11.8059632,8.49222333 L9.4805487,4.38201704 C9.41928897,4.27373951 9.32971088,4.18417951 9.22142099,4.12294163 Z M3.23076923,1.5125 C2.34230769,1.5125 1.61538462,2.255 1.61538462,3.1625 C1.61538462,4.07 2.34230769,4.8125 3.23076923,4.8125 C4.11923077,4.8125 4.84615385,4.07 4.84615385,3.1625 C4.84615385,2.255 4.11923077,1.5125 3.23076923,1.5125 Z" id="形状结合"></path>
+                            </g>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 19 - 0
src/views/attend-class/image/icon-image.svg

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="15px" height="16px" viewBox="0 0 15 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <g id="页面-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="课件播放(三层)" transform="translate(-306.000000, -268.000000)" fill="#FFFFFF" fill-rule="nonzero">
+            <g id="编组" transform="translate(306.000000, 0.000000)">
+                <g id="编组-8" transform="translate(0.000000, 93.500000)">
+                    <g id="编组-12" transform="translate(0.000000, 65.000000)">
+                        <g id="知识点目录/图片备份-4" transform="translate(0.000000, 110.000000)">
+                            <g id="编组" transform="translate(7.500000, 7.500000) scale(-1, 1) translate(-7.500000, -7.500000) translate(0.500000, 2.000000)">
+                                <path d="M12.6293706,0 C13.3863483,-1.39054547e-16 14,0.613651672 14,1.37062937 L14,1.37062937 L14,9.62937063 C14,10.3863483 13.3863483,11 12.6293706,11 L12.6293706,11 L1.37062937,11 C0.613651672,11 9.27030316e-17,10.3863483 0,9.62937063 L0,9.62937063 L0,1.37062937 C-9.27030316e-17,0.613651672 0.613651672,1.39054547e-16 1.37062937,0 L1.37062937,0 Z M9.22142099,4.12294163 C8.89196248,3.93663301 8.47385062,4.05267893 8.287542,4.38213744 L8.287542,4.38213744 L7.04470782,6.57990105 C6.98353166,6.68808179 6.89408921,6.77759053 6.78595381,6.8388468 C6.4566334,7.02539942 6.03843569,6.90966326 5.85188307,6.58034285 L5.85188307,6.58034285 L5.64657316,6.21791028 C5.58509397,6.10938138 5.49516084,6.01968934 5.3864673,5.95850172 C5.05664729,5.77283381 4.63876177,5.88969218 4.45309386,6.21951219 L4.45309386,6.21951219 L3.17297968,8.49350383 C3.11520712,8.59613088 3.08485695,8.71191444 3.08485695,8.82968531 C3.08485695,9.20817416 3.39168279,9.515 3.77017164,9.515 L3.77017164,9.515 L11.2094939,9.515 C11.3277631,9.515 11.4440194,9.4843926 11.5469559,9.42615461 C11.8763768,9.23977951 11.9923383,8.82164423 11.8059632,8.49222333 L11.8059632,8.49222333 L9.4805487,4.38201704 C9.41928897,4.27373951 9.32971088,4.18417951 9.22142099,4.12294163 Z M3.23076923,1.5125 C2.34230769,1.5125 1.61538462,2.255 1.61538462,3.1625 C1.61538462,4.07 2.34230769,4.8125 3.23076923,4.8125 C4.11923077,4.8125 4.84615385,4.07 4.84615385,3.1625 C4.84615385,2.255 4.11923077,1.5125 3.23076923,1.5125 Z" id="形状结合"></path>
+                            </g>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

binární
src/views/attend-class/image/icon-load.gif


+ 23 - 0
src/views/attend-class/image/icon-loop-active.svg

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <defs>
+        <path d="M5.00480947,15.25 C6.49624217,17.8332372 9.29235844,19.1935866 12.0802733,18.9792617 C12.1933752,19.672858 12.4261755,20.3264161 12.7562805,20.918295 C9.05647623,21.4111568 5.25493468,19.6832296 3.27275866,16.25 C2.99661629,15.7717074 3.16049144,15.160117 3.63878407,14.8839746 C4.11707669,14.6078322 4.7286671,14.7717074 5.00480947,15.25 Z M10.4132849,7.08904722 L10.5144958,7.14250707 L15.5144958,10.1425071 C16.123756,10.5080632 16.1595948,11.3606179 15.6220123,11.7834679 L15.5144958,11.8574929 L10.5144958,14.8574929 C9.88129747,15.2374119 9.08434351,14.8231057 9.00623111,14.1142925 L9,14 L9,8 C9,7.26157025 9.76529405,6.79134664 10.4132849,7.08904722 Z M19.7272413,6.75 C20.8183076,8.63978222 21.1863003,10.7486105 20.9170607,12.7561204 C20.3245684,12.4254359 19.6711964,12.1929305 18.9778301,12.0792818 C19.0927198,10.6209049 18.7822682,9.11325853 17.9951905,7.75 C15.9241227,4.16280532 11.3371947,2.93374166 7.75,5.00480947 C6.85788041,5.51987496 6.09738693,6.19866524 5.49784407,6.99904787 L7,7 C7.51283584,7 7.93550716,7.38604019 7.99327227,7.88337887 L8,8 C8,8.51283584 7.61395981,8.93550716 7.11662113,8.99327227 L7,9 L3,9 C2.48716416,9 2.06449284,8.61395981 2.00672773,8.11662113 L2,8 L2,4 C2,3.44771525 2.44771525,3 3,3 C3.51283584,3 3.93550716,3.38604019 3.99327227,3.88337887 L4,4 L3.99919549,5.66623947 C4.74283972,4.70951337 5.67119653,3.89560614 6.75,3.27275866 C11.2937799,0.649406102 17.1038888,2.20622008 19.7272413,6.75 Z M11.001,9.766 L11.001,12.233 L13.057,11 L11.001,9.766 Z" id="path-1"></path>
+        <filter x="-31.6%" y="-31.6%" width="163.2%" height="163.1%" filterUnits="objectBoundingBox" id="filter-2">
+            <feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.15 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(-42.000000, -289.000000)" fill-rule="nonzero">
+            <g id="循环备份" transform="translate(42.000000, 289.000000)">
+                <g id="形状结合">
+                    <use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
+                    <use fill="#FFFFFF" xlink:href="#path-1"></use>
+                </g>
+                <path d="M18,13 C20.7614237,13 23,15.2385763 23,18 C23,20.7614237 20.7614237,23 18,23 C15.2385763,23 13,20.7614237 13,18 C13,15.2385763 15.2385763,13 18,13 Z M20.9553274,16.1420886 C20.7524108,15.9832858 20.4506336,16.0042922 20.2548135,16.2001122 L20.2548135,16.2001122 L17.5814904,18.87328 L16.1273302,17.4183562 L16.0823515,17.3785153 C15.8794349,17.2197125 15.5776577,17.2407189 15.3818377,17.436539 C15.1709545,17.6474221 15.1628139,17.9811905 15.3636549,18.1820315 L15.3636549,18.1820315 L17.1819295,20.0003061 L17.2269082,20.0401471 C17.4298248,20.1989498 17.731602,20.1779434 17.9274221,19.9821234 L17.9274221,19.9821234 L20.9821234,16.9274221 L21.0242071,16.8804452 C21.1931032,16.669536 21.1868014,16.3684248 21.0003061,16.1819295 L21.0003061,16.1819295 Z" id="形状结合" fill="#FF8057"></path>
+            </g>
+        </g>
+    </g>
+</svg>

+ 20 - 0
src/views/attend-class/image/icon-loop.svg

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <defs>
+        <path d="M19.7272413,6.75 C22.3505939,11.2937799 20.7937799,17.1038888 16.25,19.7272413 C11.7062201,22.3505939 5.89611123,20.7937799 3.27275866,16.25 C2.99661629,15.7717074 3.16049144,15.160117 3.63878407,14.8839746 C4.11707669,14.6078322 4.7286671,14.7717074 5.00480947,15.25 C7.07587728,18.8371947 11.6628053,20.0662583 15.25,17.9951905 C18.8371947,15.9241227 20.0662583,11.3371947 17.9951905,7.75 C15.9241227,4.16280532 11.3371947,2.93374166 7.75,5.00480947 C6.85788041,5.51987496 6.09738693,6.19866524 5.49784407,6.99904787 L7,7 C7.51283584,7 7.93550716,7.38604019 7.99327227,7.88337887 L8,8 C8,8.51283584 7.61395981,8.93550716 7.11662113,8.99327227 L7,9 L3,9 C2.48716416,9 2.06449284,8.61395981 2.00672773,8.11662113 L2,8 L2,4 C2,3.44771525 2.44771525,3 3,3 C3.51283584,3 3.93550716,3.38604019 3.99327227,3.88337887 L4,4 L3.99919549,5.66623947 C4.74283972,4.70951337 5.67119653,3.89560614 6.75,3.27275866 C11.2937799,0.649406102 17.1038888,2.20622008 19.7272413,6.75 Z M9,8 C9,7.22270552 9.84797124,6.74259237 10.5144958,7.14250707 L10.5144958,7.14250707 L15.5144958,10.1425071 C16.1618347,10.5309105 16.1618347,11.4690895 15.5144958,11.8574929 L15.5144958,11.8574929 L10.5144958,14.8574929 C9.84797124,15.2574076 9,14.7772945 9,14 L9,14 Z M11.001,9.766 L11.001,12.233 L13.057,11 L11.001,9.766 Z" id="path-1"></path>
+        <filter x="-31.6%" y="-31.6%" width="163.2%" height="163.1%" filterUnits="objectBoundingBox" id="filter-2">
+            <feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.15 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(-42.000000, -246.000000)" fill-rule="nonzero">
+            <g id="形状结合" transform="translate(42.000000, 246.000000)">
+                <use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
+                <use fill="#FFFFFF" xlink:href="#path-1"></use>
+            </g>
+        </g>
+    </g>
+</svg>

+ 42 - 0
src/views/attend-class/image/icon-menu.svg

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="95px" height="107px" viewBox="0 0 95 107" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <defs>
+        <rect id="path-1" x="0" y="4.72906404" width="56.7487685" height="59.1133005" rx="9.45812808"></rect>
+        <filter x="-1.8%" y="-1.7%" width="103.5%" height="103.4%" filterUnits="objectBoundingBox" id="filter-2">
+            <feOffset dx="-2" dy="-2" in="SourceAlpha" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 0.918931159   0 0 0 0 0.883965387   0 0 0 0 0.832431161  0 0 0 1 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
+        </filter>
+        <path d="M18.9162562,0 L37.8325123,0 C41.7501972,1.68511374e-16 44.9261084,3.17591113 44.9261084,7.09359606 L44.9261084,11.8226601 C44.9261084,13.1285551 43.8674713,14.1871921 42.5615764,14.1871921 L14.1871921,14.1871921 C12.8812971,14.1871921 11.8226601,13.1285551 11.8226601,11.8226601 L11.8226601,7.09359606 C11.8226601,3.17591113 14.9985712,7.19667046e-16 18.9162562,0 Z" id="path-3"></path>
+        <filter x="-7.6%" y="-17.6%" width="121.1%" height="149.3%" filterUnits="objectBoundingBox" id="filter-4">
+            <feOffset dx="1" dy="1" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0.939254982   0 0 0 0 0.859970658   0 0 0 0 0.652735679  0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+        <filter x="-6.0%" y="-14.1%" width="118.1%" height="142.3%" filterUnits="objectBoundingBox" id="filter-5">
+            <feOffset dx="-2" dy="-2" in="SourceAlpha" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 1   0 0 0 0 0.780047784   0 0 0 0 0.293807165  0 0 0 1 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
+        </filter>
+    </defs>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="50、上课页面" transform="translate(-1825.000000, -487.000000)">
+            <g id="目录" transform="translate(1825.000000, 487.000000)">
+                <path d="M23.6453202,0 L94.5812808,0 L94.5812808,0 L94.5812808,106.403941 L23.6453202,106.403941 C10.5863704,106.403941 1.5992601e-15,95.8175704 0,82.7586207 L0,23.6453202 C-1.5992601e-15,10.5863704 10.5863704,5.95160383e-15 23.6453202,0 Z" id="矩形" fill-opacity="0.25" fill="#000000"></path>
+                <g id="编组-18" transform="translate(21.280788, 18.916256)">
+                    <g id="矩形">
+                        <use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-1"></use>
+                        <use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
+                    </g>
+                    <path d="M13.0049261,33.1034483 C14.9637686,33.1034483 16.5517241,31.5154927 16.5517241,29.5566502 C16.5517241,27.5978078 14.9637686,26.0098522 13.0049261,26.0098522 C11.0460836,26.0098522 9.45812808,27.5978078 9.45812808,29.5566502 C9.45812808,31.5154927 11.0460836,33.1034483 13.0049261,33.1034483 Z M13.0049261,47.2906404 C14.9637686,47.2906404 16.5517241,45.7026848 16.5517241,43.7438424 C16.5517241,41.7849999 14.9637686,40.1970443 13.0049261,40.1970443 C11.0460836,40.1970443 9.45812808,41.7849999 9.45812808,43.7438424 C9.45812808,45.7026848 11.0460836,47.2906404 13.0049261,47.2906404 Z M27.1921182,26.0098522 L43.7438424,26.0098522 C45.7026848,26.0098522 47.2906404,27.5978078 47.2906404,29.5566502 C47.2906404,31.5154927 45.7026848,33.1034483 43.7438424,33.1034483 L27.1921182,33.1034483 C25.2332758,33.1034483 23.6453202,31.5154927 23.6453202,29.5566502 C23.6453202,27.5978078 25.2332758,26.0098522 27.1921182,26.0098522 Z M27.1921182,40.1970443 L36.6502463,40.1970443 C38.6090888,40.1970443 40.1970443,41.7849999 40.1970443,43.7438424 C40.1970443,45.7026848 38.6090888,47.2906404 36.6502463,47.2906404 L27.1921182,47.2906404 C25.2332758,47.2906404 23.6453202,45.7026848 23.6453202,43.7438424 C23.6453202,41.7849999 25.2332758,40.1970443 27.1921182,40.1970443 Z" id="形状结合" fill="#FF9E31"></path>
+                    <g id="矩形">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-3"></use>
+                        <use fill="#FCDA67" fill-rule="evenodd" xlink:href="#path-3"></use>
+                        <use fill="black" fill-opacity="1" filter="url(#filter-5)" xlink:href="#path-3"></use>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

binární
src/views/attend-class/image/icon-more.png


+ 28 - 0
src/views/attend-class/image/icon-mulv.svg

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>课程目录</title>
+    <defs>
+        <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
+            <stop stop-color="#FFB790" offset="0%"></stop>
+            <stop stop-color="#FF8057" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="50%" y1="26.807622%" x2="50%" y2="100%" id="linearGradient-2">
+            <stop stop-color="#ACD7FF" offset="0%"></stop>
+            <stop stop-color="#FFB469" offset="100%"></stop>
+        </linearGradient>
+        <path d="M0,10 C0.045380368,6.68544475 0.62101804,3.94093846 2.30411553,2.30411553 C3.94093846,0.62101804 6.68544475,0.045380368 10,0 C13.3145553,0.045380368 16.0590615,0.62101804 17.6958845,2.30411553 C19.378982,3.94093846 19.9546196,6.68544475 20,10 C19.9546196,13.3145553 19.378982,16.0590615 17.6958845,17.6958845 C16.0590615,19.378982 13.3145553,19.9546196 10,20 C6.68544475,19.9546196 3.94093846,19.378982 2.30411553,17.6958845 C0.62101804,16.0590615 0.045380368,13.3145553 0,10 Z" id="path-3"></path>
+    </defs>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="课件列表" transform="translate(-542.000000, -16.000000)" fill-rule="nonzero">
+            <g id="编组" transform="translate(524.000000, 0.000000)">
+                <g id="课程目录" transform="translate(18.000000, 16.000000)">
+                    <g id="路径">
+                        <use fill="url(#linearGradient-1)" xlink:href="#path-3"></use>
+                        <use fill="url(#linearGradient-2)" xlink:href="#path-3"></use>
+                    </g>
+                    <path d="M14.0837809,7.49598367 L5.91643478,7.49598367 C5.51435536,7.49598367 5.18518519,7.14283647 5.18518519,6.7109548 C5.18518519,6.27930471 5.51413965,5.92592593 5.91643478,5.92592593 L14.0835652,5.92592593 C14.4856446,5.92592593 14.8148148,6.27907313 14.8148148,6.7109548 C14.8148148,7.14260489 14.4858604,7.49598367 14.0837809,7.49598367 L14.0837809,7.49598367 Z M14.0837809,11.1552835 L5.91643478,11.1552835 C5.51435536,11.1552835 5.18518519,10.8021363 5.18518519,10.3702546 C5.18518519,9.93860449 5.51413965,9.58522571 5.91643478,9.58522571 L14.0835652,9.58522571 C14.4856446,9.58522571 14.8148148,9.93837292 14.8148148,10.3702546 C14.8148148,10.8021363 14.4858604,11.1552835 14.0837809,11.1552835 L14.0837809,11.1552835 Z M9.11106797,14.8148148 L5.91643478,14.8148148 C5.51435536,14.8148148 5.18518519,14.4616676 5.18518519,14.0297859 C5.18518519,13.5981358 5.51413965,13.2447571 5.91643478,13.2447571 L9.11085226,13.2447571 C9.51293169,13.2447571 9.84210196,13.5979043 9.84210196,14.0297859 C9.84231757,14.4616676 9.51314739,14.8148148 9.11106797,14.8148148 L9.11106797,14.8148148 Z" id="形状" fill="#FFFFFF"></path>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 12 - 0
src/views/attend-class/image/icon-pause.svg

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="82px" height="82px" viewBox="0 0 82 82" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组 2</title>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="55、音频预览" transform="translate(-280.000000, -964.000000)">
+            <g id="编组-2" transform="translate(280.000000, 964.000000)">
+                <rect id="矩形" fill="#198CFE" x="0" y="0" width="82" height="82" rx="26"></rect>
+                <path d="M47.5235354,32.9254996 L57.0285389,47.7110606 C57.9244954,49.1047707 57.5209864,50.9609122 56.1272763,51.8568688 C55.6433843,52.1679422 55.0802584,52.3333333 54.5050035,52.3333333 L35.4949965,52.3333333 C33.8381422,52.3333333 32.4949965,50.9901876 32.4949965,49.3333333 C32.4949965,48.7580785 32.6603876,48.1949526 32.9714611,47.7110606 L42.4764646,32.9254996 C43.3724211,31.5317894 45.2285626,31.1282804 46.6222728,32.0242369 C46.9837807,32.2566349 47.2911375,32.5639916 47.5235354,32.9254996 Z" id="三角形" fill="#FFFFFF" transform="translate(45.000000, 40.666667) rotate(-270.000000) translate(-45.000000, -40.666667) "></path>
+            </g>
+        </g>
+    </g>
+</svg>

binární
src/views/attend-class/image/icon-pen.png


+ 15 - 0
src/views/attend-class/image/icon-play.svg

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="82px" height="82px" viewBox="0 0 82 82" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组 2</title>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="54、视频预览" transform="translate(-280.000000, -964.000000)">
+            <g id="编组-2" transform="translate(280.000000, 964.000000)">
+                <rect id="矩形" fill="#198CFE" x="0" y="0" width="82" height="82" rx="26"></rect>
+                <g id="编组-3" transform="translate(26.000000, 27.000000)" fill="#FFFFFF">
+                    <rect id="矩形" x="0" y="0" width="9" height="28" rx="4.5"></rect>
+                    <rect id="矩形备份-85" x="21" y="0" width="9" height="28" rx="4.5"></rect>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 24 - 0
src/views/attend-class/image/icon-point.svg

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <defs>
+        <path d="M17.9889353,7.03683462 C15.5779696,4.12723478 11.4245096,3.37164362 8.14331066,5.24573351 C4.86211176,7.1198234 3.40277969,11.0812175 4.68401757,14.6360727 C5.96525544,18.190928 9.6162626,20.3104404 13.338596,19.6602991 C17.0609294,19.0101577 19.7773154,15.7785188 19.7776894,11.9998178 C19.7776894,11.3862113 20.2751133,10.888785 20.8887168,10.888785 C21.5023203,10.888785 21.9997442,11.3862113 21.9997442,11.9998178 C21.9997099,16.8400427 18.5329414,20.9853151 13.7690477,21.8413977 C9.00515408,22.6974802 4.31207278,20.0185514 2.62687668,15.4811663 C0.941680576,10.9437811 2.7482686,5.85083589 6.91602308,3.38965046 C11.0837776,0.928465034 16.4159405,1.80576128 19.5754824,5.47250055 L20.1087755,4.94698208 C20.545968,4.51623039 21.2495741,4.52145337 21.6803238,4.95864794 C22.1110734,5.39584251 22.1058505,6.09945198 21.668658,6.5302037 L12.3204735,15.742887 C11.8856052,16.1721277 11.185738,16.1696459 10.7539248,15.7373318 L6.69200863,11.6742852 C6.27096012,11.2383394 6.2769817,10.5453817 6.70554253,10.1168189 C7.13410335,9.688256 7.82705768,9.68223439 8.26300138,10.1032849 L11.5460874,13.3863866 L17.9889353,7.03683462 L17.9889353,7.03683462 Z" id="path-1"></path>
+        <filter x="-15.0%" y="-15.0%" width="130.0%" height="130.0%" filterUnits="objectBoundingBox" id="filter-2">
+            <feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.125792177 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(-740.000000, -208.000000)" fill-rule="nonzero">
+            <g id="编组-8" transform="translate(732.000000, 66.000000)">
+                <g id="编组-7" transform="translate(0.000000, 75.000000)">
+                    <g id="路径" transform="translate(8.000000, 67.000000)">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use>
+                        <use fill="#FFFFFF" xlink:href="#path-1"></use>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 20 - 0
src/views/attend-class/image/icon-song-active.svg

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="15px" height="16px" viewBox="0 0 15 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <g id="页面-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="课件播放(三层)" transform="translate(-364.000000, -298.000000)" fill-rule="nonzero">
+            <g id="编组" transform="translate(306.000000, 0.000000)">
+                <g id="编组-8" transform="translate(0.000000, 93.500000)">
+                    <g id="编组-12" transform="translate(0.000000, 65.000000)">
+                        <g id="知识点目录/图片备份-3" transform="translate(58.000000, 140.000000)">
+                            <g id="编组" transform="translate(1.000000, 0.000000)">
+                                <path d="M9,0 L13,4 L13,13 C13,14.1 12.1,15 11,15 L2,15 C0.900000001,15 0,14.1 0,13 L0,2 C0,0.9 0.900000001,0 2,0 L9,0 Z M9.12286624,4.39507366 L9.05045121,4.40041053 L4.62790119,5.15709075 C4.43655776,5.19167453 4.28513683,5.3491615 4.28561715,5.52434895 L4.28561715,5.52434895 L4.23182216,9.53593936 C3.89020555,9.39268515 3.46572382,9.3940618 3.05944296,9.57324696 C2.32170786,9.89861591 1.93033971,10.6831644 2.18299927,11.3317032 L2.18299927,11.3317032 L2.20168239,11.3766901 C2.48234091,12.0123255 3.29959494,12.2398786 4.02247275,11.8921219 C4.56059052,11.6329597 4.90067764,11.1236487 4.92749856,10.6174852 L4.92749856,10.6174852 L4.97723571,6.93534117 C4.98142189,6.75800316 5.13114435,6.59642647 5.32418625,6.56593241 L5.32418625,6.56593241 L8.30000288,6.10080156 C8.30466938,6.098651 8.31400237,6.09434988 8.32036733,6.09628904 C8.51680615,6.07397444 8.6721386,6.20703285 8.66965088,6.38846059 L8.66965088,6.38846059 L8.5995415,9.09592309 C8.2484902,8.94608506 7.81030549,8.94997414 7.39554263,9.14388305 C6.66756943,9.47937063 6.29062966,10.2718872 6.5572887,10.9139742 C6.82394775,11.5560613 7.63229769,11.8023353 8.36323892,11.4606074 C8.94898573,11.1906651 9.30736799,10.629963 9.28887187,10.0889393 L9.28887187,10.0889393 L9.42646417,4.69000872 C9.42773374,4.6796787 9.42603527,4.67558898 9.42730484,4.66525896 C9.4140946,4.48619318 9.2481596,4.36776591 9.05045121,4.40041053 Z" id="形状结合" fill="#FF8057"></path>
+                                <path d="M9.00000001,0 L13,4 L10,4 C9.4,4 9.00000001,3.6 9.00000001,3 L9.00000001,0 Z" id="路径" fill="#CC4419" opacity="0.506022135" style="mix-blend-mode: multiply;"></path>
+                            </g>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 20 - 0
src/views/attend-class/image/icon-song.svg

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="15px" height="16px" viewBox="0 0 15 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <g id="页面-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="课件播放(三层)" transform="translate(-364.000000, -268.000000)" fill-rule="nonzero">
+            <g id="编组" transform="translate(306.000000, 0.000000)">
+                <g id="编组-8" transform="translate(0.000000, 93.500000)">
+                    <g id="编组-12" transform="translate(0.000000, 65.000000)">
+                        <g id="知识点目录/图片备份-6" transform="translate(58.000000, 110.000000)">
+                            <g id="编组" transform="translate(1.000000, 0.000000)">
+                                <path d="M9,0 L13,4 L13,13 C13,14.1 12.1,15 11,15 L2,15 C0.900000001,15 0,14.1 0,13 L0,2 C0,0.9 0.900000001,0 2,0 L9,0 Z M9.12286624,4.39507366 L9.05045121,4.40041053 L4.62790119,5.15709075 C4.43655776,5.19167453 4.28513683,5.3491615 4.28561715,5.52434895 L4.28561715,5.52434895 L4.23182216,9.53593936 C3.89020555,9.39268515 3.46572382,9.3940618 3.05944296,9.57324696 C2.32170786,9.89861591 1.93033971,10.6831644 2.18299927,11.3317032 L2.18299927,11.3317032 L2.20168239,11.3766901 C2.48234091,12.0123255 3.29959494,12.2398786 4.02247275,11.8921219 C4.56059052,11.6329597 4.90067764,11.1236487 4.92749856,10.6174852 L4.92749856,10.6174852 L4.97723571,6.93534117 C4.98142189,6.75800316 5.13114435,6.59642647 5.32418625,6.56593241 L5.32418625,6.56593241 L8.30000288,6.10080156 C8.30466938,6.098651 8.31400237,6.09434988 8.32036733,6.09628904 C8.51680615,6.07397444 8.6721386,6.20703285 8.66965088,6.38846059 L8.66965088,6.38846059 L8.5995415,9.09592309 C8.2484902,8.94608506 7.81030549,8.94997414 7.39554263,9.14388305 C6.66756943,9.47937063 6.29062966,10.2718872 6.5572887,10.9139742 C6.82394775,11.5560613 7.63229769,11.8023353 8.36323892,11.4606074 C8.94898573,11.1906651 9.30736799,10.629963 9.28887187,10.0889393 L9.28887187,10.0889393 L9.42646417,4.69000872 C9.42773374,4.6796787 9.42603527,4.67558898 9.42730484,4.66525896 C9.4140946,4.48619318 9.2481596,4.36776591 9.05045121,4.40041053 Z" id="形状结合" fill="#FFFFFF"></path>
+                                <path d="M9.00000001,0 L13,4 L10,4 C9.4,4 9.00000001,3.6 9.00000001,3 L9.00000001,0 Z" id="路径" fill="#B3B3B3" opacity="0.619"></path>
+                            </g>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 53 - 0
src/views/attend-class/image/icon-start.svg

@@ -0,0 +1,53 @@
+<?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>
+        <linearGradient x1="-67.2865798%" y1="54.9313571%" x2="100%" y2="50%" id="linearGradient-2">
+            <stop stop-color="#5BECFF" offset="0%"></stop>
+            <stop stop-color="#259CFE" offset="100%"></stop>
+        </linearGradient>
+        <rect id="path-3" x="0" y="0" width="121" height="38" rx="19"></rect>
+        <text id="text-4" 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-5">
+            <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.108514357   0 0 0 0 0.562412481   0 0 0 0 0.907525104  0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+        <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-6">
+            <stop stop-color="#FCFFFF" offset="0%"></stop>
+            <stop stop-color="#D4F1FF" 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-7"></path>
+        <filter x="-10.0%" y="-5.0%" width="120.0%" height="120.0%" filterUnits="objectBoundingBox" id="filter-8">
+            <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.108514357   0 0 0 0 0.562412481   0 0 0 0 0.907525104  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)">
+                <g id="button-normal">
+                    <use fill="url(#linearGradient-1)" xlink:href="#path-3"></use>
+                    <use fill="url(#linearGradient-2)" xlink:href="#path-3"></use>
+                </g>
+                <g id="确定" fill-rule="nonzero" fill="#FFFFFF" fill-opacity="1">
+                    <use filter="url(#filter-5)" xlink:href="#text-4"></use>
+                    <use xlink:href="#text-4"></use>
+                </g>
+                <g id="编组" transform="translate(24.000000, 9.000000)" fill-rule="nonzero">
+                    <g id="形状结合">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-8)" xlink:href="#path-7"></use>
+                        <use fill="url(#linearGradient-6)" xlink:href="#path-7"></use>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 4 - 0
src/views/attend-class/image/icon-touping.svg


+ 42 - 0
src/views/attend-class/image/icon-up-disabled.svg

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="64px" height="66px" viewBox="0 0 64 66" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组 11备份 2</title>
+    <defs>
+        <linearGradient x1="1.51267887e-13%" y1="15.1852058%" x2="100%" y2="70.279218%" id="linearGradient-1">
+            <stop stop-color="#E4E4E4" offset="0%"></stop>
+            <stop stop-color="#AAAAAA" offset="100%"></stop>
+        </linearGradient>
+        <circle id="path-2" cx="30" cy="30" r="30"></circle>
+        <filter x="-8.3%" y="-5.0%" width="116.7%" height="120.0%" filterUnits="objectBoundingBox" id="filter-3">
+            <feOffset dx="0" dy="4" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0.808310688   0 0 0 0 0.808310688   0 0 0 0 0.808310688  0 0 0 0.273082386 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+        <filter x="-10.0%" y="-6.7%" width="120.0%" height="123.3%" filterUnits="objectBoundingBox" id="filter-4">
+            <feGaussianBlur stdDeviation="3" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
+            <feOffset dx="0" dy="2" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 1   0 0 0 0 1   0 0 0 0 1  0 0 0 0.5 0" type="matrix" in="shadowInnerInner1" result="shadowMatrixInner1"></feColorMatrix>
+            <feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner2"></feGaussianBlur>
+            <feOffset dx="-2" dy="-6" in="shadowBlurInner2" result="shadowOffsetInner2"></feOffset>
+            <feComposite in="shadowOffsetInner2" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner2"></feComposite>
+            <feColorMatrix values="0 0 0 0 0.466145833   0 0 0 0 0.466145833   0 0 0 0 0.466145833  0 0 0 0.462194056 0" type="matrix" in="shadowInnerInner2" result="shadowMatrixInner2"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixInner1"></feMergeNode>
+                <feMergeNode in="shadowMatrixInner2"></feMergeNode>
+            </feMerge>
+        </filter>
+    </defs>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="52、乐谱预览" transform="translate(-1718.000000, -980.000000)">
+            <g id="编组-11备份-2" transform="translate(1720.000000, 980.000000)">
+                <g id="椭圆形">
+                    <use fill="black" fill-opacity="1" filter="url(#filter-3)" xlink:href="#path-2"></use>
+                    <use fill="url(#linearGradient-1)" fill-rule="evenodd" xlink:href="#path-2"></use>
+                    <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
+                </g>
+                <polyline id="Stroke-1" stroke="#FFFFFF" stroke-width="5.65714286" stroke-linecap="round" stroke-linejoin="round" transform="translate(30.142857, 29.142857) rotate(-270.000000) translate(-30.142857, -29.142857) " points="37.4285714 43.2857143 22.8571429 29.1428571 37.4285714 15"></polyline>
+            </g>
+        </g>
+    </g>
+</svg>

+ 42 - 0
src/views/attend-class/image/icon-up.svg

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="64px" height="66px" viewBox="0 0 64 66" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组 11备份 2</title>
+    <defs>
+        <linearGradient x1="1.51267887e-13%" y1="15.1852058%" x2="100%" y2="70.279218%" id="linearGradient-1">
+            <stop stop-color="#88E1FF" offset="0%"></stop>
+            <stop stop-color="#2C8DFF" offset="100%"></stop>
+        </linearGradient>
+        <circle id="path-2" cx="30" cy="30" r="30"></circle>
+        <filter x="-8.3%" y="-5.0%" width="116.7%" height="120.0%" filterUnits="objectBoundingBox" id="filter-3">
+            <feOffset dx="0" dy="4" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0.43649793   0 0 0 0 0.550193556   0 0 0 0 0.741508152  0 0 0 0.273082386 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+        <filter x="-10.0%" y="-6.7%" width="120.0%" height="123.3%" filterUnits="objectBoundingBox" id="filter-4">
+            <feGaussianBlur stdDeviation="3" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
+            <feOffset dx="0" dy="2" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 1   0 0 0 0 1   0 0 0 0 1  0 0 0 0.5 0" type="matrix" in="shadowInnerInner1" result="shadowMatrixInner1"></feColorMatrix>
+            <feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner2"></feGaussianBlur>
+            <feOffset dx="-2" dy="-6" in="shadowBlurInner2" result="shadowOffsetInner2"></feOffset>
+            <feComposite in="shadowOffsetInner2" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner2"></feComposite>
+            <feColorMatrix values="0 0 0 0 0.108247927   0 0 0 0 0.476908508   0 0 0 0 0.983016304  0 0 0 0.462194056 0" type="matrix" in="shadowInnerInner2" result="shadowMatrixInner2"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixInner1"></feMergeNode>
+                <feMergeNode in="shadowMatrixInner2"></feMergeNode>
+            </feMerge>
+        </filter>
+    </defs>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="53、图片预览" transform="translate(-1718.000000, -980.000000)">
+            <g id="编组-11备份-2" transform="translate(1720.000000, 980.000000)">
+                <g id="椭圆形">
+                    <use fill="black" fill-opacity="1" filter="url(#filter-3)" xlink:href="#path-2"></use>
+                    <use fill="url(#linearGradient-1)" fill-rule="evenodd" xlink:href="#path-2"></use>
+                    <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
+                </g>
+                <polyline id="Stroke-1" stroke="#FFFFFF" stroke-width="5.65714286" stroke-linecap="round" stroke-linejoin="round" transform="translate(30.142857, 29.142857) rotate(-270.000000) translate(-30.142857, -29.142857) " points="37.4285714 43.2857143 22.8571429 29.1428571 37.4285714 15"></polyline>
+            </g>
+        </g>
+    </g>
+</svg>

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 9 - 0
src/views/attend-class/image/icon-video-active.svg


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 9 - 0
src/views/attend-class/image/icon-video.svg


binární
src/views/attend-class/image/icon-videobg.png


+ 162 - 0
src/views/attend-class/image/icon-whiteboard.svg

@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="70px" height="70px" viewBox="0 0 70 70" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>切片</title>
+    <defs>
+        <linearGradient x1="1.51267887e-13%" y1="15.1852058%" x2="100%" y2="70.279218%" id="linearGradient-1">
+            <stop stop-color="#A4A8FF" offset="0%"></stop>
+            <stop stop-color="#959AFF" offset="100%"></stop>
+        </linearGradient>
+        <circle id="path-2" cx="35" cy="35" r="35"></circle>
+        <filter x="-7.1%" y="-4.3%" width="114.3%" height="117.1%" filterUnits="objectBoundingBox" id="filter-3">
+            <feOffset dx="0" dy="4" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
+            <feColorMatrix values="0 0 0 0 0.438230942   0 0 0 0 0.43649793   0 0 0 0 0.741508152  0 0 0 0.273082386 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+        <filter x="-8.6%" y="-5.7%" width="117.1%" height="120.0%" filterUnits="objectBoundingBox" id="filter-4">
+            <feGaussianBlur stdDeviation="3" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
+            <feOffset dx="0" dy="2" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 1   0 0 0 0 1   0 0 0 0 1  0 0 0 0.5 0" type="matrix" in="shadowInnerInner1" result="shadowMatrixInner1"></feColorMatrix>
+            <feGaussianBlur stdDeviation="1" in="SourceAlpha" result="shadowBlurInner2"></feGaussianBlur>
+            <feOffset dx="-2" dy="-6" in="shadowBlurInner2" result="shadowOffsetInner2"></feOffset>
+            <feComposite in="shadowOffsetInner2" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner2"></feComposite>
+            <feColorMatrix values="0 0 0 0 0.529973323   0 0 0 0 0.550633836   0 0 0 0 1  0 0 0 1 0" type="matrix" in="shadowInnerInner2" result="shadowMatrixInner2"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixInner1"></feMergeNode>
+                <feMergeNode in="shadowMatrixInner2"></feMergeNode>
+            </feMerge>
+        </filter>
+        <path d="M11.7637958,36 L15.6907496,36 C16.2430344,36 16.6907496,36.4477153 16.6907496,37 C16.6907496,37.088882 16.6788998,37.1773673 16.6555134,37.2631174 L14.200968,46.2631174 C14.0823149,46.6981787 13.6871552,47 13.2362042,47 L9.30925039,47 C8.75696564,47 8.30925039,46.5522847 8.30925039,46 C8.30925039,45.911118 8.32110017,45.8226327 8.34448657,45.7368826 L10.799032,36.7368826 C10.9176851,36.3018213 11.3128448,36 11.7637958,36 Z" id="path-5"></path>
+        <filter x="-2.1%" y="-4.5%" width="115.3%" height="109.1%" filterUnits="objectBoundingBox" id="filter-6">
+            <feOffset dx="1" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feColorMatrix values="0 0 0 0 0.898039216   0 0 0 0 0.694117647   0 0 0 0 0.443137255  0 0 0 1 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
+        </filter>
+        <filter x="0.7%" y="-2.3%" width="109.8%" height="104.5%" filterUnits="objectBoundingBox" id="filter-7">
+            <feOffset dx="-0.5" dy="0" in="SourceAlpha" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 1   0 0 0 0 1   0 0 0 0 1  0 0 0 0.648983829 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
+        </filter>
+        <path d="M30.3092504,36 L34.2362042,36 C34.6871552,36 35.0823149,36.3018213 35.200968,36.7368826 L37.6555134,45.7368826 C37.8008292,46.2697069 37.4866914,46.8194481 36.953867,46.9647638 C36.8681169,46.9881502 36.7796316,47 36.6907496,47 L32.7637958,47 C32.3128448,47 31.9176851,46.6981787 31.799032,46.2631174 L29.3444866,37.2631174 C29.1991708,36.7302931 29.5133086,36.1805519 30.046133,36.0352362 C30.1318831,36.0118498 30.2203684,36 30.3092504,36 Z" id="path-8"></path>
+        <filter x="-13.2%" y="-4.5%" width="115.4%" height="109.1%" filterUnits="objectBoundingBox" id="filter-9">
+            <feOffset dx="-1" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
+            <feColorMatrix values="0 0 0 0 0.898039216   0 0 0 0 0.694117647   0 0 0 0 0.443137255  0 0 0 1 0" type="matrix" in="shadowOffsetOuter1"></feColorMatrix>
+        </filter>
+        <filter x="-10.5%" y="-2.3%" width="109.8%" height="104.5%" filterUnits="objectBoundingBox" id="filter-10">
+            <feOffset dx="0.5" dy="0" in="SourceAlpha" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 1   0 0 0 0 1   0 0 0 0 1  0 0 0 0.5 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
+        </filter>
+        <path d="M21.2297059,0 L24.7702941,0 C25.4853157,-1.31347324e-16 26.1009379,0.504689502 26.2411652,1.2058258 L27,5 L27,5 L19,5 L19.7588348,1.2058258 C19.8990621,0.504689502 20.5146843,5.75436534e-16 21.2297059,0 Z" id="path-11"></path>
+        <filter x="-3.1%" y="-5.0%" width="106.2%" height="110.0%" filterUnits="objectBoundingBox" id="filter-12">
+            <feOffset dx="-0.5" dy="0" in="SourceAlpha" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 1   0 0 0 0 1   0 0 0 0 1  0 0 0 0.648983829 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
+        </filter>
+        <path d="M7,4 L39,4 C41.7614237,4 44,6.23857625 44,9 L44,34 L44,34 L2,34 L2,9 C2,6.23857625 4.23857625,4 7,4 Z" id="path-13"></path>
+        <filter x="-6.0%" y="-8.3%" width="111.9%" height="116.7%" filterUnits="objectBoundingBox" id="filter-14">
+            <feGaussianBlur stdDeviation="1.5" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur>
+            <feOffset dx="0" dy="-2" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 0   0 0 0 0 0   0 0 0 0 0  0 0 0 0.08 0" type="matrix" in="shadowInnerInner1"></feColorMatrix>
+        </filter>
+        <rect id="path-15" x="7" y="9" width="25" height="11" rx="2"></rect>
+        <filter x="-8.0%" y="-9.1%" width="116.0%" height="136.4%" filterUnits="objectBoundingBox" id="filter-16">
+            <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.890196078   0 0 0 0 0.890196078   0 0 0 0 0.933333333  0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+        <filter x="-6.0%" y="-4.5%" width="112.0%" height="127.3%" filterUnits="objectBoundingBox" id="filter-17">
+            <feOffset dx="0" dy="-1" in="SourceAlpha" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 0.996078431   0 0 0 0 0.721568627   0 0 0 0 0.0352941176  0 0 0 1 0" type="matrix" in="shadowInnerInner1" result="shadowMatrixInner1"></feColorMatrix>
+            <feOffset dx="0" dy="0.5" in="SourceAlpha" result="shadowOffsetInner2"></feOffset>
+            <feComposite in="shadowOffsetInner2" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner2"></feComposite>
+            <feColorMatrix values="0 0 0 0 1   0 0 0 0 1   0 0 0 0 1  0 0 0 0.5 0" type="matrix" in="shadowInnerInner2" result="shadowMatrixInner2"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixInner1"></feMergeNode>
+                <feMergeNode in="shadowMatrixInner2"></feMergeNode>
+            </feMerge>
+        </filter>
+        <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-18">
+            <stop stop-color="#FF8632" offset="0%"></stop>
+            <stop stop-color="#FF5400" offset="100%"></stop>
+        </linearGradient>
+        <rect id="path-19" x="7" y="23.5" width="18" height="4" rx="2"></rect>
+        <filter x="-11.1%" y="-25.0%" width="122.2%" height="200.0%" filterUnits="objectBoundingBox" id="filter-20">
+            <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.890196078   0 0 0 0 0.890196078   0 0 0 0 0.933333333  0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
+        </filter>
+        <filter x="-6.9%" y="-6.2%" width="113.9%" height="162.5%" filterUnits="objectBoundingBox" id="filter-21">
+            <feOffset dx="0" dy="-0.5" in="SourceAlpha" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 0.898039216   0 0 0 0 0.184313725   0 0 0 0 0  0 0 0 1 0" type="matrix" in="shadowInnerInner1" result="shadowMatrixInner1"></feColorMatrix>
+            <feOffset dx="0" dy="0.5" in="SourceAlpha" result="shadowOffsetInner2"></feOffset>
+            <feComposite in="shadowOffsetInner2" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner2"></feComposite>
+            <feColorMatrix values="0 0 0 0 1   0 0 0 0 0.568627451   0 0 0 0 0.258823529  0 0 0 1 0" type="matrix" in="shadowInnerInner2" result="shadowMatrixInner2"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixInner1"></feMergeNode>
+                <feMergeNode in="shadowMatrixInner2"></feMergeNode>
+            </feMerge>
+        </filter>
+        <path d="M2.5,32 L43.5,32 C44.8807119,32 46,33.1192881 46,34.5 L46,36 C46,36.5522847 45.5522847,37 45,37 L1,37 C0.44771525,37 -5.9849844e-16,36.5522847 0,36 L0,34.5 C-1.69088438e-16,33.1192881 1.11928813,32 2.5,32 Z" id="path-22"></path>
+        <filter x="-0.5%" y="-5.0%" width="101.1%" height="110.0%" filterUnits="objectBoundingBox" id="filter-23">
+            <feOffset dx="0" dy="-0.5" in="SourceAlpha" result="shadowOffsetInner1"></feOffset>
+            <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite>
+            <feColorMatrix values="0 0 0 0 0.941176471   0 0 0 0 0.749019608   0 0 0 0 0.501960784  0 0 0 1 0" type="matrix" in="shadowInnerInner1" result="shadowMatrixInner1"></feColorMatrix>
+            <feOffset dx="0" dy="0.5" in="SourceAlpha" result="shadowOffsetInner2"></feOffset>
+            <feComposite in="shadowOffsetInner2" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner2"></feComposite>
+            <feColorMatrix values="0 0 0 0 1   0 0 0 0 1   0 0 0 0 1  0 0 0 0.5 0" type="matrix" in="shadowInnerInner2" result="shadowMatrixInner2"></feColorMatrix>
+            <feMerge>
+                <feMergeNode in="shadowMatrixInner1"></feMergeNode>
+                <feMergeNode in="shadowMatrixInner2"></feMergeNode>
+            </feMerge>
+        </filter>
+    </defs>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" opacity="0.5">
+        <g id="52、乐谱预览" transform="translate(-40.000000, -970.000000)">
+            <g id="画板" transform="translate(40.000000, 970.000000)">
+                <g id="椭圆形">
+                    <use fill="black" fill-opacity="1" filter="url(#filter-3)" xlink:href="#path-2"></use>
+                    <use fill="url(#linearGradient-1)" fill-rule="evenodd" xlink:href="#path-2"></use>
+                    <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-2"></use>
+                </g>
+                <g id="编组-17" transform="translate(12.000000, 12.000000)">
+                    <g id="矩形">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-6)" xlink:href="#path-5"></use>
+                        <use fill="#FDD99A" fill-rule="evenodd" xlink:href="#path-5"></use>
+                        <use fill="black" fill-opacity="1" filter="url(#filter-7)" xlink:href="#path-5"></use>
+                    </g>
+                    <g id="矩形备份-10">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-9)" xlink:href="#path-8"></use>
+                        <use fill="#FDD99A" fill-rule="evenodd" xlink:href="#path-8"></use>
+                        <use fill="black" fill-opacity="1" filter="url(#filter-10)" xlink:href="#path-8"></use>
+                    </g>
+                    <g id="矩形">
+                        <use fill="#FDD99A" fill-rule="evenodd" xlink:href="#path-11"></use>
+                        <use fill="black" fill-opacity="1" filter="url(#filter-12)" xlink:href="#path-11"></use>
+                    </g>
+                    <g id="矩形">
+                        <use fill="#FFFFFF" fill-rule="evenodd" xlink:href="#path-13"></use>
+                        <use fill="black" fill-opacity="1" filter="url(#filter-14)" xlink:href="#path-13"></use>
+                    </g>
+                    <g id="矩形">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-16)" xlink:href="#path-15"></use>
+                        <use fill="#FFD200" fill-rule="evenodd" xlink:href="#path-15"></use>
+                        <use fill="black" fill-opacity="1" filter="url(#filter-17)" xlink:href="#path-15"></use>
+                    </g>
+                    <rect id="矩形备份-9" fill="#E5E6EE" x="7" y="24" width="32" height="3" rx="1.5"></rect>
+                    <g id="矩形备份-9">
+                        <use fill="black" fill-opacity="1" filter="url(#filter-20)" xlink:href="#path-19"></use>
+                        <use fill="url(#linearGradient-18)" fill-rule="evenodd" xlink:href="#path-19"></use>
+                        <use fill="black" fill-opacity="1" filter="url(#filter-21)" xlink:href="#path-19"></use>
+                    </g>
+                    <g id="矩形">
+                        <use fill="#FFE0A1" fill-rule="evenodd" xlink:href="#path-22"></use>
+                        <use fill="black" fill-opacity="1" filter="url(#filter-23)" xlink:href="#path-22"></use>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 1 - 0
src/views/attend-class/image/icon-zhibo.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1673454396647" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1930" width="32" height="32" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M74.666667 853.333333c-17.066667 0-32-14.933333-32-32v-426.666666c0-17.066667 14.933333-32 32-32s32 14.933333 32 32v426.666666c0 17.066667-14.933333 32-32 32zM366.933333 853.333333c-17.066667 0-32-14.933333-32-32v-618.666666c0-17.066667 14.933333-32 32-32s32 14.933333 32 32v618.666666c0 17.066667-14.933333 32-32 32zM657.066667 853.333333c-17.066667 0-32-14.933333-32-32v-341.333333c0-17.066667 14.933333-32 32-32s32 14.933333 32 32v341.333333c0 17.066667-12.8 32-32 32zM949.333333 853.333333c-17.066667 0-32-14.933333-32-32v-512c0-17.066667 14.933333-32 32-32s32 14.933333 32 32v512c0 17.066667-14.933333 32-32 32z" fill="#FF8057" p-id="1931"></path></svg>

+ 360 - 0
src/views/attend-class/index.module.less

@@ -0,0 +1,360 @@
+.playContent {
+  position: relative;
+  width: 100vw;
+  height: 100vh;
+  background-color: #000;
+  overflow: hidden;
+  --plyr-color-main: var(--van-primary);
+  --plyr-range-track-height: 3px;
+}
+
+.assignHomework {
+  position: absolute;
+  top: 40px;
+  left: 40px;
+  width: 187px;
+  height: 60px;
+  cursor: pointer;
+
+  img {
+    width: 100%;
+    height: 100%;
+  }
+}
+
+.coursewarePlay {
+  position: relative;
+  height: 100vh;
+  margin: 0 auto;
+  overflow: hidden;
+}
+
+.playModel {
+  position: absolute;
+  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;
+  top: 0;
+  left: 0;
+  right: 0;
+  z-index: 10;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  height: 40px;
+  background: linear-gradient(180deg, rgba(0, 0, 0, 0.6), transparent);
+  transition: transform 0.5s;
+  box-sizing: border-box;
+
+  div {
+    box-sizing: border-box;
+  }
+}
+
+.backBtn {
+  color: #fff;
+  height: 100%;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  z-index: 10;
+  padding: 0 15px;
+
+  :global {
+    .van-icon {
+      margin-right: 8px;
+    }
+  }
+}
+
+.headRight {
+  position: relative;
+  z-index: 10;
+  display: flex;
+  align-items: center;
+  margin-left: auto;
+  height: 100%;
+  padding-right: 15px;
+
+  .rightBtn {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    height: 100%;
+    padding: 0 10px;
+
+    img {
+      width: 22px;
+      height: 22px;
+      display: block;
+    }
+  }
+}
+
+.menu {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 12px;
+  color: #fff;
+}
+
+.tabsContent {
+  width: 100vw;
+  height: 100vh;
+
+  :global {
+    .van-tabs__wrap {
+      display: none !important;
+    }
+
+    .van-tabs__content {
+      width: 100%;
+      height: 100%;
+    }
+  }
+}
+
+.wraps {
+  width: 100%;
+  height: 100%;
+  transform-style: preserve-3d;
+  perspective: (32rem);
+  transition-timing-function: initial;
+}
+
+.itemDiv {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  background-color: #000;
+  transform-style: preserve-3d;
+  transition-property: transform, opacity, height;
+  backface-visibility: hidden;
+  overflow: hidden;
+  z-index: 1;
+
+  &.itemActive {
+    z-index: 10;
+  }
+
+  &.acitveAnimation {
+    transition-duration: .8s;
+  }
+
+  &.show {
+    display: block;
+  }
+
+  &.hide {
+    display: none;
+  }
+
+  video {
+    width: 100%;
+    height: 100%;
+  }
+
+  img {
+    display: block;
+    width: 100%;
+    height: 100%;
+    object-fit: contain;
+  }
+}
+
+.rightFixedBtns {
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  right: 0;
+  z-index: 10;
+}
+
+.fullBtn {
+  width: 95px;
+  height: 107px;
+  overflow: hidden;
+
+  img {
+    width: 100%;
+    height: 100%;
+  }
+
+  &:active {
+    opacity: .8;
+  }
+
+  &.btnsDisabled {
+    opacity: .3;
+    pointer-events: none;
+  }
+}
+
+
+:global {
+
+  .top-enter-active,
+  .top-leave-active {
+    transition: transform 0.5s;
+  }
+
+  .top-enter-from,
+  .top-leave-to {
+    transform: translateY(-100%);
+  }
+
+  .right-enter-active,
+  .right-leave-active {
+    transition: all 0.5s;
+  }
+
+  .right-enter-from,
+  .right-leave-to {
+    right: -60px;
+    opacity: 0;
+  }
+
+  .bottom-enter-active,
+  .bottom-leave-active {
+    transition: transform 0.5s;
+  }
+
+  .bottom-enter-from,
+  .bottom-leave-to {
+    transform: translateY(100%);
+  }
+}
+
+.loadWrap {
+  position: absolute;
+  left: 0;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  background: linear-gradient(45deg, #21232a, #111218);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.drawerContainer {
+  width: 360px !important;
+
+  :global {
+    .n-drawer-body-content-wrapper {
+      padding: 0px !important;
+      text-align: center;
+
+      &>div {
+        margin-bottom: 24px;
+      }
+    }
+
+    .n-drawer-header {
+      position: relative;
+      justify-content: center !important;
+      padding-top: 24px !important;
+      padding-bottom: 20px !important;
+      border-bottom: 0 !important;
+
+      .n-drawer-header__main {
+        position: relative;
+        z-index: 2;
+        font-size: 18px;
+        font-weight: 600;
+        color: #131415;
+
+        &::after {
+          position: absolute;
+          bottom: -4px;
+          left: 0;
+          z-index: -1;
+          content: ' ';
+          width: 100%;
+          display: inline-block;
+          height: 10px;
+          background: linear-gradient(90deg, #77BBFF 0%, rgba(163, 231, 255, 0.22) 100%);
+        }
+      }
+
+      .n-drawer-header__close {
+        position: absolute;
+        right: 26px;
+      }
+    }
+  }
+}
+
+.switchChangeSection {
+  position: absolute;
+  bottom: 35px;
+  right: 38px;
+  display: flex;
+  z-index: 199;
+  transition: all .3s ease-in-out;
+
+  .switchBtn {
+    width: 64px;
+    height: 66px;
+    cursor: pointer;
+
+    img {
+      transition: all .2s ease;
+      transform: scale(1);
+      width: inherit;
+      height: inherit;
+
+      &:active {
+        transform: scale(0.9);
+        transition: all .2s ease;
+      }
+    }
+
+    &+.switchBtn {
+      margin-left: 36px;
+    }
+  }
+}
+
+.switchDisplaySection {
+  position: absolute;
+  left: 40px;
+  bottom: 40px;
+  z-index: 199;
+  transition: all .5s;
+
+  .displayBtn {
+    width: 70px;
+    height: 70px;
+    cursor: pointer;
+
+    img {
+      width: inherit;
+      height: inherit;
+    }
+
+    &+.switchBtn {
+      margin-left: 40px;
+    }
+  }
+}
+
+.sectionAnimate {
+  opacity: 0;
+  pointer-events: none;
+  transform: translateY(100%);
+  transition: all .5s;
+}

+ 693 - 0
src/views/attend-class/index.tsx

@@ -0,0 +1,693 @@
+// import { Popup } from 'vant';
+import {
+  defineComponent,
+  onMounted,
+  reactive,
+  nextTick,
+  onUnmounted,
+  ref,
+  watch,
+  Transition
+} from 'vue';
+// import iconBack from './image/back.svg';
+import styles from './index.module.less';
+import 'plyr/dist/plyr.css';
+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 iconUp from './image/icon-up.svg';
+import iconUpDisabled from './image/icon-up-disabled.svg';
+import iconDown from './image/icon-down.svg';
+import iconDownDisabled from './image/icon-down-disabled.svg';
+import iconWhiteboard from './image/icon-whiteboard.svg';
+// import Points from './component/points';
+import iconAssignHomework from './image/icon-assignHomework.svg';
+import { Vue3Lottie } from 'vue3-lottie';
+import playLoadData from './datas/data.json';
+import { usePageVisibility } from '@vant/use';
+import VideoPlay from './component/video-play';
+import { useMessage, NDrawer, NDrawerContent } from 'naive-ui';
+import CardType from '@/components/card-type';
+import { ToolItem, ToolType } from './component/tool';
+import Pen from './component/tools/pen';
+import AudioPay from './component/audio-pay';
+
+export default defineComponent({
+  name: 'CoursewarePlay',
+  setup() {
+    const message = useMessage();
+    const pageVisibility = usePageVisibility();
+    const isPlay = ref(false);
+    /** 页面显示和隐藏 */
+    watch(pageVisibility, value => {
+      const activeItem = data.itemList[popupData.activeIndex];
+      if (activeItem.type != 'VIDEO') return;
+      if (value == 'hidden') {
+        isPlay.value = !activeItem.videoEle?.paused;
+        togglePlay(activeItem, false);
+      } else {
+        // 页面显示,并且
+        if (isPlay.value) togglePlay(activeItem, true);
+      }
+    });
+    /** 设置播放容器 16:9 */
+    const parentContainer = reactive({
+      width: '100vw'
+    });
+    const setContainer = () => {
+      const min = Math.min(screen.width, screen.height);
+      const max = Math.max(screen.width, screen.height);
+      const width = min * (16 / 9);
+      if (width > max) {
+        parentContainer.width = '100vw';
+        return;
+      } else {
+        parentContainer.width = width + 'px';
+      }
+    };
+    const handleInit = (type = 0) => {
+      //设置容器16:9
+      setContainer();
+    };
+    handleInit();
+    onUnmounted(() => {
+      handleInit(1);
+    });
+
+    // const route = useRoute();
+    // const router = useRouter();
+    // const headeRef = ref();
+    const data = reactive({
+      detail: null,
+      knowledgePointList: [] as any,
+      itemList: [] as any,
+      showHead: true,
+      isCourse: false,
+      isRecordPlay: false,
+      videoRefs: {} as any[]
+    });
+    const activeData = reactive({
+      isAutoPlay: true, // 是否自动播放
+      nowTime: 0,
+      model: true, // 遮罩
+      isAnimation: true, // 是否动画
+      videoBtns: true, // 视频
+      currentTime: 0,
+      duration: 0,
+      timer: null as any,
+      item: null as any
+    });
+    // const getTempList = async (materialList: any, name: any) => {
+    //   const list: any = [];
+    //   const browserInfo = browser();
+    //   for (let j = 0; j < materialList.length; j++) {
+    //     const material = materialList[j];
+
+    //     list.push({
+    //       ...material,
+    //       iframeRef: null,
+    //       videoEle: null,
+    //       tabName: name,
+    //       autoPlay: false, //加载完成是否自动播放
+    //       isprepare: false, // 视频是否加载完成
+    //       isRender: false // 是否渲染了
+    //     });
+    //   }
+    //   return list;
+    // };
+    const getDetail = async () => {
+      data.knowledgePointList = [
+        {
+          id: '5',
+          name: '歌曲表演 大鹿',
+          title: '歌曲表演 大鹿',
+          type: 'AUDIO',
+          content:
+            'https://cloud-coach.ks3-cn-beijing.ksyuncs.com/1686819360752.mp3',
+          url: 'https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/23cc71b5d7874dcf8752cd257483e687_mergeImage.png'
+        },
+        {
+          id: '1',
+          name: '歌曲表演 大鹿',
+          title: '歌曲表演 大鹿',
+          type: 'VIDEO',
+          content:
+            'https://courseware.lexiaoya.cn/%E5%BF%85%E5%AD%A6%E5%BF%85%E7%9C%8B-%E8%90%A8%E5%85%8B%E6%96%AF-1-C4-4.mp4',
+          url: 'https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/23cc71b5d7874dcf8752cd257483e687_mergeImage.png'
+        },
+        {
+          id: '2',
+          name: '知识 音的高低',
+          title: '知识 音的高低',
+          type: 'IMG',
+          content:
+            'https://gyt.ks3-cn-beijing.ksyuncs.com/courseware/1686815979899.png',
+          url: 'https://gyt.ks3-cn-beijing.ksyuncs.com/courseware/1686815979899.png'
+        },
+        {
+          id: '3',
+          name: '欣赏 永远在童话里',
+          title: '欣赏 永远在童话里',
+          type: 'IMG',
+          content:
+            'https://gyt.ks3-cn-beijing.ksyuncs.com/courseware/1686815979899.png',
+          url: 'https://gyt.ks3-cn-beijing.ksyuncs.com/courseware/1686815979899.png'
+        },
+        {
+          id: '4',
+          name: '唱歌 小红帽',
+          title: '唱歌 小红帽',
+          type: 'SONG',
+          content: '11707',
+          url: 'https://lanhu.oss-cn-beijing.aliyuncs.com/SketchPngd1f4e00a00bc8134db4ec43e51a66442f778756e2caf01d10a1ffdd51fc7c6cb'
+        }
+      ];
+      data.itemList = data.knowledgePointList.map((m: any) => {
+        return {
+          ...m,
+          iframeRef: null,
+          videoEle: null,
+          autoPlay: false, //加载完成是否自动播放
+          isprepare: false, // 视频是否加载完成
+          isRender: false // 是否渲染了
+        };
+      });
+    };
+
+    onMounted(() => {
+      getDetail();
+    });
+
+    // const playRef = ref();
+    // 返回
+    // const goback = () => {
+    //   try {
+    //     playRef.value?.handleOut();
+    //   } catch {
+    //     //
+    //   }
+    //   postMessage({ api: 'goBack' });
+    // };
+
+    const popupData = reactive({
+      open: false,
+      activeIndex: 0,
+      // tabActive: '',
+      // tabName: '',
+      // itemActive: '',
+      // itemName: ''
+      // guideOpen: false,
+      toolOpen: false // 工具弹窗控制
+    });
+
+    /**停止所有的播放 */
+    const handleStop = () => {
+      for (let i = 0; i < data.itemList.length; i++) {
+        const activeItem = data.itemList[i];
+        if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
+          activeItem.videoEle.stop();
+        }
+        // console.log('🚀 ~ activeItem:', activeItem)
+        // 停止曲谱的播放
+        if (activeItem.type === 'SONG') {
+          activeItem.iframeRef?.contentWindow?.postMessage(
+            { api: 'setPlayState' },
+            '*'
+          );
+        }
+      }
+    };
+    // 切换素材
+    const toggleMaterial = (itemActive: any) => {
+      const index = data.itemList.findIndex((n: any) => n.id == itemActive);
+      if (index > -1) {
+        handleSwipeChange(index);
+      }
+    };
+    /** 延迟收起模态框 */
+    const setModelOpen = () => {
+      clearTimeout(activeData.timer);
+      message.destroyAll();
+      activeData.timer = setTimeout(() => {
+        activeData.model = false;
+        Object.values(data.videoRefs).map((n: any) =>
+          n.toggleHideControl(false)
+        );
+      }, 4000);
+    };
+
+    /** 立即收起所有的模态框 */
+    const clearModel = () => {
+      clearTimeout(activeData.timer);
+      message.destroyAll();
+      activeData.model = false;
+      Object.values(data.videoRefs).map((n: any) => n.toggleHideControl(false));
+    };
+    const toggleModel = (type = true) => {
+      activeData.model = type;
+      Object.values(data.videoRefs).map((n: any) => n.toggleHideControl(type));
+    };
+
+    // 双击
+    const handleDbClick = (item: any) => {
+      if (item && item.type === 'VIDEO') {
+        const videoEle: HTMLVideoElement = item.videoEle;
+        if (videoEle) {
+          if (videoEle.paused) {
+            message.destroyAll();
+            videoEle.play();
+          } else {
+            message.warning('已暂停');
+            videoEle.pause();
+          }
+        }
+      }
+    };
+
+    // 切换播放
+    const togglePlay = (m: any, isPlay: boolean) => {
+      if (isPlay) {
+        m.videoEle?.play();
+      } else {
+        m.videoEle?.pause();
+      }
+    };
+
+    const showIndex = ref(-4);
+    const effectIndex = ref(3);
+    const effects = [
+      {
+        prev: {
+          transform: 'translate3d(0, 0, -800px) rotateX(180deg)'
+        },
+        next: {
+          transform: 'translate3d(0, 0, -800px) rotateX(-180deg)'
+        }
+      },
+      {
+        prev: {
+          transform: 'translate3d(-100%, 0, -800px)'
+        },
+        next: {
+          transform: 'translate3d(100%, 0, -800px)'
+        }
+      },
+      {
+        prev: {
+          transform: 'translate3d(-50%, 0, -800px) rotateY(80deg)'
+        },
+        next: {
+          transform: 'translate3d(50%, 0, -800px) rotateY(-80deg)'
+        }
+      },
+      {
+        prev: {
+          transform: 'translate3d(-100%, 0, -800px) rotateY(-120deg)'
+        },
+        next: {
+          transform: 'translate3d(100%, 0, -800px) rotateY(120deg)'
+        }
+      },
+      // 风车4
+      {
+        prev: {
+          transform: 'translate3d(-50%, 50%, -800px) rotateZ(-14deg)',
+          opacity: 0
+        },
+        next: {
+          transform: 'translate3d(50%, 50%, -800px) rotateZ(14deg)',
+          opacity: 0
+        }
+      },
+      // 翻页5
+      {
+        prev: {
+          transform: 'translateZ(-800px) rotate3d(0, -1, 0, 90deg)',
+          opacity: 0
+        },
+        next: {
+          transform: 'translateZ(-800px) rotate3d(0, 1, 0, 90deg)',
+          opacity: 0
+        },
+        current: { transitionDelay: '700ms' }
+      }
+    ];
+
+    const acitveTimer = ref();
+    // 轮播切换
+    const handleSwipeChange = (index: number) => {
+      // 如果是当前正在播放 或者是视频最后一个
+      if (popupData.activeIndex == index) return;
+      handleStop();
+      clearTimeout(acitveTimer.value);
+      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);
+              message.destroyAll();
+              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;
+        }
+        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;
+      }
+    };
+
+    // 上一个知识点, 下一个知识点
+    const handlePreAndNext = (type: string) => {
+      if (type === 'up') {
+        handleSwipeChange(popupData.activeIndex - 1);
+      } else {
+        handleSwipeChange(popupData.activeIndex + 1);
+      }
+    };
+
+    /** 弹窗关闭 */
+    const handleClosePopup = () => {
+      const item = data.itemList[popupData.activeIndex];
+      if (item?.type == 'VIDEO' && !item.videoEle?.paused) {
+        setModelOpen();
+      }
+    };
+
+    // 监听页面键盘事件 - 上下切换
+    document.body.addEventListener('keyup', (e: KeyboardEvent) => {
+      if (e.key === 'ArrowUp') {
+        if (popupData.activeIndex === 0) return;
+        handlePreAndNext('up');
+      } else if (e.key === 'ArrowDown') {
+        if (popupData.activeIndex === data.itemList.length - 1) return;
+        handlePreAndNext('down');
+      }
+    });
+
+    /** 教学数据 */
+    const studyData = reactive({
+      type: '' as ToolType,
+      penShow: false
+    });
+
+    /** 打开教学工具 */
+    const openStudyTool = (item: ToolItem) => {
+      const activeItem = data.itemList[popupData.activeIndex];
+      // 暂停视频和曲谱的播放
+      if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
+        activeItem.videoEle.pause();
+      }
+      if (activeItem.type === 'SONG') {
+        activeItem.iframeRef?.contentWindow?.postMessage(
+          { api: 'setPlayState' },
+          '*'
+        );
+      }
+      clearModel();
+      popupData.toolOpen = false;
+      studyData.type = item.type;
+
+      switch (item.type) {
+        case 'pen':
+          studyData.penShow = true;
+          break;
+      }
+    };
+
+    /** 关闭教学工具 */
+    const closeStudyTool = () => {
+      studyData.type = 'init';
+      toggleModel();
+    };
+
+    return () => (
+      <div id="playContent" class={styles.playContent}>
+        <div
+          onClick={() => {
+            clearTimeout(activeData.timer);
+            activeData.model = !activeData.model;
+            Object.values(data.videoRefs).map((n: any) =>
+              n.toggleHideControl(activeData.model)
+            );
+          }}>
+          <div
+            class={styles.coursewarePlay}
+            style={{ width: parentContainer.width }}
+            onClick={(e: Event) => {
+              e.stopPropagation();
+              setModelOpen();
+            }}>
+            <div class={styles.wraps}>
+              {data.itemList.map((m: any, mIndex: number) => {
+                const isRender =
+                  m.isRender || Math.abs(popupData.activeIndex - mIndex) < 2;
+                const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4;
+                if (isRender) {
+                  m.isRender = true;
+                }
+                return isRender ? (
+                  <div
+                    key={'index' + mIndex}
+                    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
+                        : {}
+                    }
+                    onClick={(e: Event) => {
+                      e.stopPropagation();
+                      clearTimeout(activeData.timer);
+                      if (Date.now() - activeData.nowTime < 300) {
+                        handleDbClick(m);
+                        return;
+                      }
+                      activeData.nowTime = Date.now();
+                      activeData.timer = setTimeout(() => {
+                        activeData.model = !activeData.model;
+                        Object.values(data.videoRefs).map((n: any) =>
+                          n.toggleHideControl(activeData.model)
+                        );
+                        if (activeData.model) {
+                          setModelOpen();
+                        }
+                      }, 300);
+                    }}>
+                    {m.type === 'VIDEO' ? (
+                      <>
+                        <VideoPlay
+                          ref={(v: any) => (data.videoRefs[mIndex] = v)}
+                          item={m}
+                          isEmtry={isEmtry}
+                          onLoadedmetadata={(videoItem: any) => {
+                            m.videoEle = videoItem;
+                            m.isprepare = true;
+                          }}
+                          onTogglePlay={(paused: boolean) => {
+                            m.autoPlay = false;
+                            if (paused || popupData.open) {
+                              clearTimeout(activeData.timer);
+                            } else {
+                              setModelOpen();
+                            }
+                          }}
+                          onEnded={() => {
+                            const _index = popupData.activeIndex + 1;
+                            if (_index < data.itemList.length) {
+                              handleSwipeChange(_index);
+                            }
+                          }}
+                          onReset={() => {
+                            if (!m.videoEle?.paused) {
+                              setModelOpen();
+                            }
+                          }}
+                        />
+                        <Transition name="van-fade">
+                          {!m.isprepare && (
+                            <div class={styles.loadWrap}>
+                              <Vue3Lottie
+                                animationData={playLoadData}></Vue3Lottie>
+                            </div>
+                          )}
+                        </Transition>
+                      </>
+                    ) : m.type === 'IMG' ? (
+                      <img src={m.content} />
+                    ) : m.type === 'AUDIO' ? (
+                      <AudioPay item={m} />
+                    ) : (
+                      <MusicScore
+                        activeModel={activeData.model}
+                        data-vid={m.id}
+                        music={m}
+                        onSetIframe={(el: any) => {
+                          m.iframeRef = el;
+                        }}
+                      />
+                    )}
+                  </div>
+                ) : null;
+              })}
+            </div>
+            <Transition name="right">
+              {activeData.model && (
+                <div
+                  class={styles.rightFixedBtns}
+                  onClick={(e: Event) => {
+                    e.stopPropagation();
+                    clearTimeout(activeData.timer);
+                  }}>
+                  <div
+                    class={[styles.fullBtn, styles.point]}
+                    onClick={() => (popupData.open = true)}>
+                    <img src={iconMenu} />
+                  </div>
+                </div>
+              )}
+            </Transition>
+          </div>
+        </div>
+
+        {/* <div
+          style={{ transform: activeData.model ? '' : 'translateY(-100%)' }}
+          class={styles.headerContainer}
+          ref={headeRef}>
+          <div class={styles.backBtn} onClick={() => goback()}>
+            <Icon name={iconBack} />
+            返回
+          </div>
+          <div class={styles.menu}>{popupData.itemName}</div>
+        </div> */}
+        {/* 布置作业按钮 */}
+        <div class={styles.assignHomework}>
+          <img src={iconAssignHomework} />
+        </div>
+
+        {/* 上下切换 */}
+        <div
+          class={[
+            styles.switchChangeSection,
+            activeData.model ? '' : styles.sectionAnimate
+          ]}>
+          <div
+            class={[styles.switchBtn]}
+            onClick={() => {
+              if (popupData.activeIndex === 0) return;
+              handlePreAndNext('up');
+            }}>
+            <img src={popupData.activeIndex === 0 ? iconUpDisabled : iconUp} />
+          </div>
+
+          <div
+            class={[styles.switchBtn]}
+            onClick={() => {
+              if (popupData.activeIndex === data.itemList.length - 1) return;
+              handlePreAndNext('down');
+            }}>
+            <img
+              src={
+                popupData.activeIndex === data.itemList.length - 1
+                  ? iconDownDisabled
+                  : iconDown
+              }
+            />
+          </div>
+        </div>
+
+        {/* 白板 */}
+        <div
+          class={[
+            styles.switchDisplaySection,
+            activeData.model ? '' : styles.sectionAnimate
+          ]}>
+          <div
+            class={styles.displayBtn}
+            onClick={() =>
+              openStudyTool({
+                type: 'pen',
+                icon: iconWhiteboard,
+                name: '白板'
+              })
+            }>
+            <img src={iconWhiteboard} />
+          </div>
+        </div>
+
+        <NDrawer
+          v-model:show={popupData.open}
+          class={styles.drawerContainer}
+          onAfterLeave={handleClosePopup}
+          showMask={false}>
+          <NDrawerContent title="资源列表" closable>
+            {data.knowledgePointList.map((item: any, index: number) => (
+              <CardType
+                item={item}
+                isActive={popupData.activeIndex === index}
+                isCollect={false}
+                onClick={(item: any) => {
+                  popupData.open = false;
+                  toggleMaterial(item.id);
+                }}
+              />
+            ))}
+          </NDrawerContent>
+        </NDrawer>
+
+        {studyData.penShow && (
+          <Pen show={studyData.type === 'pen'} close={() => closeStudyTool()} />
+        )}
+      </div>
+    );
+  }
+});

+ 5 - 0
src/views/prepare-lessons/components/directory-main/index.module.less

@@ -66,6 +66,7 @@
     white-space: nowrap;
     text-overflow: ellipsis;
     max-width: 280px !important;
+    color: rgba(0, 0, 0, .5);
 
     &.titleSelect {
       color: var(--n-color);
@@ -90,6 +91,10 @@
     width: 12px;
   }
 
+  &.childItem .title {
+    color: #131415;
+  }
+
   &.childSelect {
     background: #F5F6FA;
 

+ 1 - 1
src/views/prepare-lessons/components/directory-main/index.tsx

@@ -111,7 +111,7 @@ export default defineComponent({
 
     setTimeout(() => {
       show.value = false;
-    }, 500);
+    }, 100);
     return () => (
       <div class={styles.directoryList}>
         <div class={styles['select-directory']}>

+ 118 - 0
src/views/prepare-lessons/components/lesson-main/index.module.less

@@ -0,0 +1,118 @@
+.lesson-main {
+  :global {
+    .n-tabs-tab-pad {
+      width: 64px !important;
+    }
+
+    .n-tabs-nav {
+      padding: 12px 20px 20px;
+    }
+
+    .n-tabs-tab {
+      color: #8B8D98;
+      font-size: 20px;
+      padding-top: 12px;
+      padding-bottom: 8px;
+      line-height: 28px;
+
+      &.n-tabs-tab--active {
+        font-weight: 600 !important;
+        color: #131415 !important;
+      }
+    }
+
+    .n-tabs-tab__label {
+      z-index: 10;
+    }
+
+    .n-tabs-bar {
+      height: 10px;
+      background: linear-gradient(90deg, #77BBFF 0%, rgba(163, 231, 255, 0.22) 100%);
+      z-index: 0;
+      bottom: 8px;
+    }
+
+    .n-tab-pane {
+      padding-top: 4px !important;
+      // padding-left: 22px !important;
+      // padding-right: 22px !important;
+    }
+  }
+}
+
+.btnGroup {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding-left: 22px !important;
+  padding-right: 22px !important;
+
+  :global {
+    .n-button {
+      border-radius: 8px;
+      height: 38px;
+      font-size: 17px;
+      font-weight: 600 !important;
+      padding: 0 27px;
+    }
+
+    .n-button--default-type {
+      background: #E8F4FF;
+      color: #0378EC;
+
+      &:not(.n-button--disabled):hover {
+        background: #E8F4FF;
+      }
+
+      .n-button__border {
+        border: 1px solid #198CFE;
+      }
+    }
+  }
+}
+
+.listContainer {
+  margin-top: 24px;
+  padding: 0 22px 0;
+  // // 52 + 28 + 38
+  max-height: calc(var(--window-page-lesson-height) - 146px - 24px);
+}
+
+.list {
+  display: flex;
+  flex-flow: row wrap;
+  justify-content: flex-start;
+  gap: 20px;
+}
+
+.attendClassModal {
+  width: 800px;
+  border-radius: 16px;
+  overflow: hidden;
+
+  :global {
+    .n-card-header {
+      position: relative;
+      padding: 20px 18px;
+      text-align: center;
+      background: #F5F6FA;
+      font-size: 22px;
+      font-weight: 600;
+      color: #131415;
+      line-height: 30px;
+    }
+
+    .n-card-header__close {
+      position: absolute;
+      right: 18px;
+    }
+
+    .n-card__content {
+      padding: 0;
+    }
+
+    .n-base-select-menu .n-base-select-option {
+      font-size: 18px !important;
+    }
+  }
+}

+ 99 - 2
src/views/prepare-lessons/components/lesson-main/index.tsx

@@ -1,8 +1,105 @@
-import { defineComponent } from 'vue';
+import { defineComponent, reactive } from 'vue';
+import styles from './index.module.less';
+import { NButton, NModal, NScrollbar, NSpace, NTabPane, NTabs } from 'naive-ui';
+import CardType from '@/components/card-type';
+import AttendClass from '../../model/attend-class';
 
 export default defineComponent({
   name: 'lesson-main',
   setup() {
-    return () => <>目录</>;
+    const forms = reactive({
+      list: [
+        {
+          id: 1,
+          type: 'IMG',
+          url: 'https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/24a8551d69b245e0aec1c8613bf50d46_mergeImage.png',
+          title: '夏日曲演奏',
+          isCollect: false, // 是否收藏
+          isSelected: false // 是否精选
+        },
+        {
+          id: 2,
+          type: 'IMG',
+          url: 'https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/24a8551d69b245e0aec1c8613bf50d46_mergeImage.png',
+          title: '欢乐颂',
+          isCollect: true, // 是否收藏
+          isSelected: true // 是否精选
+        },
+        {
+          id: 3,
+          type: 'IMG',
+          url: 'https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/24a8551d69b245e0aec1c8613bf50d46_mergeImage.png',
+          title: '夏日曲演奏',
+          isCollect: false, // 是否收藏
+          isSelected: true // 是否精选
+        },
+        {
+          id: 4,
+          type: 'IMG',
+          url: 'https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/24a8551d69b245e0aec1c8613bf50d46_mergeImage.png',
+          title: '欢乐颂',
+          isCollect: true, // 是否收藏
+          isSelected: false // 是否精选
+        }
+      ],
+      showAttendClass: false // 开始上课
+    });
+    return () => (
+      <div class={styles['lesson-main']}>
+        <NTabs
+          animated
+          defaultValue="courseware"
+          paneClass={styles.paneTitle}
+          justifyContent="center"
+          paneWrapperClass={styles.paneWrapperContainer}>
+          <NTabPane name="courseware" tab="课件">
+            <div class={styles.btnGroup}>
+              <NSpace>
+                <NButton type="default">编辑</NButton>
+              </NSpace>
+
+              <NSpace>
+                <NButton type="default">预览</NButton>
+                <NButton
+                  type="primary"
+                  onClick={() => (forms.showAttendClass = true)}>
+                  开始上课
+                </NButton>
+              </NSpace>
+            </div>
+
+            <NScrollbar class={styles.listContainer}>
+              <div class={styles.list}>
+                {forms.list.map((item: any) => (
+                  <CardType isShowCollect={false} item={item} />
+                ))}
+              </div>
+            </NScrollbar>
+          </NTabPane>
+          <NTabPane name="train" tab="训练">
+            <div class={styles.btnGroup}>
+              <NSpace>
+                <NButton type="default">添加训练</NButton>
+                <NButton type="default">编辑</NButton>
+              </NSpace>
+
+              <NSpace>
+                <NButton type="primary">布置训练</NButton>
+              </NSpace>
+            </div>
+          </NTabPane>
+        </NTabs>
+
+        <NModal
+          v-model:show={forms.showAttendClass}
+          preset="card"
+          showIcon={false}
+          class={styles.attendClassModal}
+          title={'选择班级'}
+          blockScroll={false}>
+          <AttendClass onClose={() => (forms.showAttendClass = false)} />
+        </NModal>
+      </div>
+    );
   }
 });

binární
src/views/prepare-lessons/components/resource-main/images/icon-search.png


+ 67 - 0
src/views/prepare-lessons/components/resource-main/index.module.less

@@ -0,0 +1,67 @@
+.resource-main {
+  height: 100%;
+
+  :global {
+    .n-tabs-tab-pad {
+      width: 24px !important;
+    }
+
+    .n-tabs-nav {
+      padding: 12px 20px 24px;
+    }
+
+    .n-tabs-tab {
+      color: #8B8D98;
+      font-size: 16px;
+      padding-top: 12px;
+      padding-bottom: 6px;
+      line-height: 22px;
+
+      &.n-tabs-tab--active {
+        font-weight: 600 !important;
+        color: #131415 !important;
+      }
+    }
+
+    .n-tabs-tab__label {
+      z-index: 10;
+    }
+
+    .n-tabs-bar {
+      height: 10px;
+      background: linear-gradient(90deg, #77BBFF 0%, rgba(163, 231, 255, 0.22) 100%);
+      z-index: 0;
+      bottom: 6px;
+    }
+
+    .n-tab-pane {
+      padding-top: 0 !important;
+    }
+  }
+
+  .iconScreen {
+    font-size: 18px;
+    color: var(--n-color);
+    font-weight: bold;
+    width: 18px;
+    height: 18px;
+    cursor: pointer;
+  }
+}
+
+.listContainer {
+  margin: 20px 0;
+  max-height: calc(var(--window-page-lesson-height) - 224px - 40px);
+
+  .list {
+    text-align: center;
+
+    &>div {
+      margin-bottom: 20px;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+  }
+}

+ 108 - 2
src/views/prepare-lessons/components/resource-main/index.tsx

@@ -1,8 +1,114 @@
-import { defineComponent } from 'vue';
+import { defineComponent, onMounted, reactive } from 'vue';
+import styles from './index.module.less';
+import { NTabs, NTabPane, NScrollbar } from 'naive-ui';
+import ResourceSearchGroup from '../../model/resource-search-group';
+import CardType from '@/components/card-type';
+import { useElementSize } from '@vueuse/core';
+import { useRect } from '@vant/use';
 
 export default defineComponent({
   name: 'resource-main',
+  props: {
+    selectionHeight: {
+      type: String,
+      default: '100%'
+    }
+  },
   setup() {
-    return () => <>目录</>;
+    const forms = reactive({
+      list: [
+        {
+          id: 1,
+          type: 'IMG',
+          url: 'https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/24a8551d69b245e0aec1c8613bf50d46_mergeImage.png',
+          title: '夏日曲演奏',
+          isCollect: false, // 是否收藏
+          isSelected: false // 是否精选
+        },
+        {
+          id: 2,
+          type: 'IMG',
+          url: 'https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/24a8551d69b245e0aec1c8613bf50d46_mergeImage.png',
+          title: '欢乐颂',
+          isCollect: true, // 是否收藏
+          isSelected: true // 是否精选
+        },
+        {
+          id: 3,
+          type: 'IMG',
+          url: 'https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/24a8551d69b245e0aec1c8613bf50d46_mergeImage.png',
+          title: '夏日曲演奏',
+          isCollect: false, // 是否收藏
+          isSelected: true // 是否精选
+        },
+        {
+          id: 4,
+          type: 'IMG',
+          url: 'https://lanhu-dds-backend.oss-cn-beijing.aliyuncs.com/merge_image/imgs/24a8551d69b245e0aec1c8613bf50d46_mergeImage.png',
+          title: '欢乐颂',
+          isCollect: true, // 是否收藏
+          isSelected: false // 是否精选
+        }
+      ],
+      height: '100%' as any
+    });
+
+    return () => (
+      <div class={styles['resource-main']}>
+        <NTabs
+          animated
+          defaultValue="shareResources"
+          paneClass={styles.paneTitle}
+          paneWrapperClass={styles.paneWrapperContainer}>
+          {{
+            suffix: () => (
+              <div class={styles.iconScreen}>
+                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+                  <g fill="none">
+                    <path
+                      d="M5 6a1 1 0 0 1 1-1h2a1 1 0 0 0 0-2H6a3 3 0 0 0-3 3v2a1 1 0 0 0 2 0V6zm0 12a1 1 0 0 0 1 1h2a1 1 0 1 1 0 2H6a3 3 0 0 1-3-3v-2a1 1 0 1 1 2 0v2zM18 5a1 1 0 0 1 1 1v2a1 1 0 1 0 2 0V6a3 3 0 0 0-3-3h-2a1 1 0 1 0 0 2h2zm1 13a1 1 0 0 1-1 1h-2a1 1 0 1 0 0 2h2a3 3 0 0 0 3-3v-2a1 1 0 1 0-2 0v2z"
+                      fill="#198CFE"></path>
+                  </g>
+                </svg>
+              </div>
+            ),
+            default: () => (
+              <>
+                <NTabPane name="shareResources" tab="共享资源">
+                  <ResourceSearchGroup />
+                  <NScrollbar class={styles.listContainer}>
+                    <div class={styles.list}>
+                      {forms.list.map((item: any) => (
+                        <CardType isShowAdd item={item} />
+                      ))}
+                    </div>
+                  </NScrollbar>
+                </NTabPane>
+                <NTabPane name="myResources" tab="我的资源">
+                  <ResourceSearchGroup />
+                  <NScrollbar class={styles.listContainer}>
+                    <div class={styles.list}>
+                      {forms.list.map((item: any) => (
+                        <CardType isShowAdd item={item} />
+                      ))}
+                    </div>
+                  </NScrollbar>
+                </NTabPane>
+                <NTabPane name="myCollect" tab="我的收藏">
+                  <ResourceSearchGroup />
+                  <NScrollbar class={styles.listContainer}>
+                    <div class={styles.list}>
+                      {forms.list.map((item: any) => (
+                        <CardType isShowAdd item={item} />
+                      ))}
+                    </div>
+                  </NScrollbar>
+                </NTabPane>
+              </>
+            )
+          }}
+        </NTabs>
+      </div>
+    );
   }
 });

binární
src/views/prepare-lessons/images/icon-search.png


+ 3 - 2
src/views/prepare-lessons/index.module.less

@@ -1,7 +1,8 @@
 .prepareLessons {
   display: flex;
-  padding: 32px;
-  height: 100vh;
+  // padding: 32px;
+  padding-bottom: 32px;
+  height: calc(100vh - 96px);
 
 
   .directoryMain,

+ 23 - 3
src/views/prepare-lessons/index.tsx

@@ -1,16 +1,36 @@
-import { defineComponent } from 'vue';
+import { defineComponent, nextTick, onMounted, ref } from 'vue';
 import styles from './index.module.less';
 import DirectoryList from './components/directory-main';
 import LessonMain from './components/lesson-main';
 import ResourceMain from './components/resource-main';
+import { useElementSize, useResizeObserver } from '@vueuse/core';
+import { useRect } from '@vant/use';
 
 export default defineComponent({
   name: 'prepare-lessons',
   setup() {
+    const directroyRef = ref();
+    // const
+    onMounted(() => {
+      // console.log(document.querySelector('#resourceRef')?.clientHeight);
+
+      useResizeObserver(
+        document.querySelector('#resourceRef') as HTMLElement,
+        (entries: any) => {
+          const entry = entries[0];
+          const { height } = entry.contentRect;
+          // document.body.setAttribute
+          document.documentElement.style.setProperty(
+            '--window-page-lesson-height',
+            height + 'px'
+          );
+        }
+      );
+    });
     return () => (
       <div class={styles.prepareLessons}>
         {/* 左侧目录 */}
-        <div class={styles.directoryMain}>
+        <div class={styles.directoryMain} ref={directroyRef.value}>
           <DirectoryList />
         </div>
         {/* 中间排课 */}
@@ -18,7 +38,7 @@ export default defineComponent({
           <LessonMain />
         </div>
         {/* 资源 */}
-        <div class={styles.resourceMain}>
+        <div class={styles.resourceMain} id="resourceRef">
           <ResourceMain />
         </div>
       </div>

+ 73 - 0
src/views/prepare-lessons/model/attend-class/index.module.less

@@ -0,0 +1,73 @@
+.attendClass {
+  margin: 32px 0;
+  padding: 0 40px;
+}
+
+.attendClassSearch {
+  width: 100%;
+  display: flex;
+  align-items: center;
+  gap: 0 24px;
+  margin-bottom: 28px;
+
+  :global {
+
+    .n-base-selection,
+    .n-input {
+      height: 52px;
+      min-height: 52px;
+      --n-height: 52px !important;
+      font-size: 18px;
+    }
+  }
+
+  .iconSearch {
+    width: 16px;
+    height: 17px;
+  }
+}
+
+.thingItem {
+  background: #F5F6FA;
+  border-radius: 12px;
+  margin-bottom: 16px;
+  padding: 24px;
+  cursor: pointer;
+  transition: background-color 0.2s linear;
+
+  &:hover {
+    background: #ecedf4;
+    transition: background-color 0.2s linear;
+  }
+
+  :global {
+    .n-thing-header {
+      margin-bottom: 0 !important;
+    }
+  }
+
+  .title {
+    display: flex;
+    align-items: center;
+    font-size: 20px !important;
+    font-weight: 600 !important;
+    color: #131415 !important;
+    line-height: 28px;
+
+    &::before {
+      content: ' ';
+      display: inline-block;
+      width: 10px;
+      height: 10px;
+      border-radius: 50%;
+      background: #198CFE;
+      margin-right: 12px;
+    }
+  }
+
+  .content {
+    margin-top: 12px !important;
+    font-size: 16px;
+    color: #777777;
+  }
+}

+ 100 - 0
src/views/prepare-lessons/model/attend-class/index.tsx

@@ -0,0 +1,100 @@
+import { defineComponent } from 'vue';
+import styles from './index.module.less';
+import { NInput, NScrollbar, NSelect, NThing } from 'naive-ui';
+import iconSearch from '../../images/icon-search.png';
+
+export default defineComponent({
+  name: 'attend-class',
+  emits: ['close'],
+  setup(props, { emit }) {
+    const onAttendClass = (i: any) => {
+      emit('close');
+      window.open(window.location.origin + '/attend-class');
+    };
+    return () => (
+      <div class={styles.attendClass}>
+        <div class={styles.attendClassSearch}>
+          <NInput placeholder="请输入班级名称" clearable>
+            {{
+              prefix: () => <img src={iconSearch} class={styles.iconSearch} />
+            }}
+          </NInput>
+          <NSelect
+            placeholder="年级"
+            clearable
+            options={[
+              {
+                label: '一年级',
+                value: '1'
+              },
+              {
+                label: '二年级',
+                value: '2'
+              },
+              {
+                label: '三年级',
+                value: '3'
+              },
+              {
+                label: '四年级',
+                value: '4'
+              },
+              {
+                label: '五年级',
+                value: '5'
+              },
+              {
+                label: '六年级',
+                value: '6'
+              }
+            ]}
+          />
+          <NSelect
+            placeholder="班级"
+            clearable
+            options={[
+              {
+                label: '一班',
+                value: '1'
+              },
+              {
+                label: '二班',
+                value: '2'
+              },
+              {
+                label: '三班',
+                value: '3'
+              },
+              {
+                label: '四班',
+                value: '4'
+              },
+              {
+                label: '五班',
+                value: '5'
+              }
+            ]}
+          />
+        </div>
+        <NScrollbar style={{ 'max-height': '65vh' }}>
+          {[1, 2, 3, 4, 5, 6, 7].map(i => (
+            <div onClick={() => onAttendClass(i)}>
+              <NThing class={styles.thingItem}>
+                {{
+                  header: () => (
+                    <div class={styles.title}>一年级{i}班 23人</div>
+                  ),
+                  default: () => (
+                    <div class={styles.content}>
+                      人教版二年级上册 | 第一单元 |【歌表演】我和我的祖国
+                    </div>
+                  )
+                }}
+              </NThing>
+            </div>
+          ))}
+        </NScrollbar>
+      </div>
+    );
+  }
+});

+ 49 - 0
src/views/prepare-lessons/model/resource-search-group/index.module.less

@@ -0,0 +1,49 @@
+.searchGroup {
+  padding: 0 20px;
+
+  .searchSelect {
+    padding: 20px 0;
+    display: flex;
+    justify-content: flex-start;
+    gap: 0px 16px;
+  }
+
+  .iconSearch {
+    width: 18px;
+    height: 19px;
+  }
+}
+
+.inputSearch {
+  :global {
+    .n-input-wrapper {
+      padding-left: 12px;
+      padding-right: 4px;
+    }
+  }
+
+  .searchBtn {
+    height: 34px;
+    border-radius: 8px;
+    font-size: 15px;
+    font-weight: 500;
+  }
+}
+
+.btnType {
+  gap: 0px 6px !important;
+
+  :global {
+    .n-button {
+      height: 28px;
+      padding: 0 13px;
+      font-size: 15px;
+      color: rgba(0, 0, 0, .6);
+
+      &.n-button--primary-type {
+        font-weight: bold;
+        color: #fff;
+      }
+    }
+  }
+}

+ 61 - 0
src/views/prepare-lessons/model/resource-search-group/index.tsx

@@ -0,0 +1,61 @@
+import { defineComponent, reactive } from 'vue';
+import styles from './index.module.less';
+import { NButton, NInput, NSelect, NSpace } from 'naive-ui';
+import { instrumentArray, teachingArray } from '@/utils/searchArray';
+import iconSearch from '../../components/resource-main/images/icon-search.png';
+
+export default defineComponent({
+  name: 'resource-search-group',
+  setup() {
+    const forms = reactive({
+      search: null
+    });
+    return () => (
+      <>
+        <div class={styles.searchGroup}>
+          <NSpace size="small" class={styles.btnType}>
+            <NButton type="primary" round size="small">
+              全部
+            </NButton>
+            <NButton secondary round size="small">
+              乐谱
+            </NButton>
+            <NButton secondary round size="small">
+              图片
+            </NButton>
+            <NButton secondary round size="small">
+              音频
+            </NButton>
+            <NButton secondary round size="small">
+              视频
+            </NButton>
+          </NSpace>
+
+          <div class={styles.searchSelect}>
+            <NSelect placeholder="教材" options={teachingArray} clearable />
+            <NSelect placeholder="乐器" options={instrumentArray} clearable />
+          </div>
+
+          <NInput
+            type="text"
+            placeholder="请输入搜索关键词"
+            clearable
+            v-model:value={forms.search}
+            class={styles.inputSearch}>
+            {{
+              prefix: () => <img src={iconSearch} class={styles.iconSearch} />,
+              suffix: () => (
+                <NButton
+                  type="primary"
+                  class={styles.searchBtn}
+                  disabled={forms.search ? false : true}>
+                  搜索
+                </NButton>
+              )
+            }}
+          </NInput>
+        </div>
+      </>
+    );
+  }
+});

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů