Kaynağa Gözat

Merge branch 'iteration-clssroom-work' into dev

lex 1 yıl önce
ebeveyn
işleme
a9f2ba6d1c
57 değiştirilmiş dosya ile 2589 ekleme ve 711 silme
  1. 1 19
      dev-dist/sw.js
  2. 1 1
      public/version.json
  3. 7 0
      src/api/user.ts
  4. 33 1
      src/store/modules/catchData.ts
  5. 11 0
      src/store/modules/users.ts
  6. 36 1
      src/views/attend-class/index.module.less
  7. 54 4
      src/views/attend-class/index.tsx
  8. 49 21
      src/views/attend-class/model/train-type/index.module.less
  9. 26 12
      src/views/attend-class/model/train-type/index.tsx
  10. 28 23
      src/views/home/modals/subject-modal/index.tsx
  11. BIN
      src/views/homework-record/images/icon-ing.png
  12. BIN
      src/views/homework-record/images/icon-over.png
  13. 59 14
      src/views/homework-record/index.module.less
  14. 138 60
      src/views/homework-record/index.tsx
  15. 2 3
      src/views/natural-resources/components/my-resources/upload-modal/index.tsx
  16. 147 12
      src/views/prepare-lessons/components/lesson-main/courseware-presets/index.module.less
  17. 258 260
      src/views/prepare-lessons/components/lesson-main/courseware-presets/index.tsx
  18. 80 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/related.module.less
  19. 51 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/related.tsx
  20. 145 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/index.module.less
  21. 146 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/index.tsx
  22. 51 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/item.tsx
  23. 56 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/resource-search-group/index.module.less
  24. 53 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/resource-search-group/index.tsx
  25. 29 0
      src/views/prepare-lessons/components/lesson-main/courseware/addCourseware.tsx
  26. 2 0
      src/views/prepare-lessons/components/lesson-main/index.module.less
  27. 71 81
      src/views/prepare-lessons/components/lesson-main/index.tsx
  28. 8 1
      src/views/prepare-lessons/components/lesson-main/train/assign-homework.tsx
  29. 1 1
      src/views/prepare-lessons/components/lesson-main/train/index.module.less
  30. 59 44
      src/views/prepare-lessons/components/lesson-main/train/index.tsx
  31. 5 2
      src/views/prepare-lessons/components/resource-main/components/select-music/index.tsx
  32. 2 2
      src/views/prepare-lessons/components/resource-main/index.tsx
  33. BIN
      src/views/prepare-lessons/images/active-arrow.png
  34. BIN
      src/views/prepare-lessons/images/addSource/icon8.png
  35. BIN
      src/views/prepare-lessons/images/default-arrow.png
  36. BIN
      src/views/prepare-lessons/images/icon-add.png
  37. BIN
      src/views/prepare-lessons/images/icon-look.png
  38. BIN
      src/views/prepare-lessons/images/icon-menu-active.png
  39. BIN
      src/views/prepare-lessons/images/icon-menu-default.png
  40. BIN
      src/views/prepare-lessons/images/icon-no-work.png
  41. BIN
      src/views/prepare-lessons/images/protocol/check-active.png
  42. BIN
      src/views/prepare-lessons/images/protocol/check-default.png
  43. BIN
      src/views/prepare-lessons/images/protocol/closeAble.png
  44. BIN
      src/views/prepare-lessons/images/protocol/dingPng.png
  45. BIN
      src/views/prepare-lessons/images/protocol/moveTop.png
  46. 174 0
      src/views/prepare-lessons/model/add-courseware-protocol/index.module.less
  47. 230 0
      src/views/prepare-lessons/model/add-courseware-protocol/index.tsx
  48. 20 0
      src/views/prepare-lessons/model/add-other-source/index.module.less
  49. 104 1
      src/views/prepare-lessons/model/add-other-source/index.tsx
  50. 273 52
      src/views/prepare-lessons/model/courseware-type/index.module.less
  51. 82 45
      src/views/prepare-lessons/model/courseware-type/index.tsx
  52. 15 14
      src/views/prepare-lessons/model/related-class/index.tsx
  53. 32 31
      src/views/prepare-lessons/model/subject-sync/index.tsx
  54. 19 1
      src/views/xiaoku-music/component/play-item/index.tsx
  55. 1 1
      src/views/xiaoku-music/index.module.less
  56. 28 2
      src/views/xiaoku-music/index.tsx
  57. 2 2
      vite.config.ts

+ 1 - 19
dev-dist/sw.js

@@ -92,19 +92,6 @@ define(['./workbox-5357ef54'], function (workbox) {
     ],
     {}
   );
-  workbox.precacheAndRoute(
-    [
-      {
-        url: 'registerSW.js',
-        revision: '3ca0b8505b4bec776b69afdba2768812'
-      },
-      {
-        url: 'index.html',
-        revision: '0.44rej4rp79g'
-      }
-    ],
-    {}
-  );
 
   /**
    * https://juejin.cn/post/6844903881189621767
@@ -122,12 +109,7 @@ define(['./workbox-5357ef54'], function (workbox) {
     workbox.strategies.networkFirst()
   );
   workbox.routing.registerRoute(
-<<<<<<< HEAD
-    new RegExp('.*.html|css|js|json'),
-    workbox.strategies.NetworkFirst()
-=======
-    new RegExp('.*.html|css|js'),
+    new RegExp('|js'),
     workbox.strategies.networkOnly()
->>>>>>> iteration-classroom-listen
   );
 });

+ 1 - 1
public/version.json

@@ -1 +1 @@
-{"version":1710926195492}
+{"version":1710926225646}

+ 7 - 0
src/api/user.ts

@@ -70,3 +70,10 @@ export const suggestMessageUnread = (params?: any) => {
 export const api_musicSheetDetail = (data: any) => {
   return request.get(`/edu-app/musicSheet/detail/${data}`);
 };
+
+/** 获取乐器列表 */
+export const api_musicalInstrumentList = (data: any) => {
+  return request.post('/edu-app/musicalInstrument/list', {
+    data
+  });
+};

+ 33 - 1
src/store/modules/catchData.ts

@@ -1,12 +1,18 @@
 import { defineStore } from 'pinia';
 import { store } from '@/store';
-import { getSubjectList, getSubjectList2, getCategories } from '@/api/user';
+import {
+  getSubjectList,
+  getSubjectList2,
+  getCategories,
+  api_musicalInstrumentList
+} from '@/api/user';
 
 export const useCatchStore = defineStore('catch-store', {
   state: () => ({
     bookVersionList: [] as any[], // 其它类型
     musicTypeList: [] as any[], // 乐谱分类
     subjectList: [] as any[], // 声部列表,
+    musicInstrumentList: [] as any[], // 乐器列表,
     subjectInstruemnts: [] as any[] // 乐器列表,
   }),
   getters: {
@@ -16,6 +22,9 @@ export const useCatchStore = defineStore('catch-store', {
     getMusicCategories(): any[] {
       return this.musicTypeList;
     },
+    getMusicInstruments(): any[] {
+      return this.musicInstrumentList;
+    },
     getAllMusicCategories(): any[] {
       return [
         {
@@ -62,6 +71,9 @@ export const useCatchStore = defineStore('catch-store', {
     setSubjectInstruemnts(subjects: any[]) {
       this.subjectInstruemnts = subjects;
     },
+    setMusicInstruments(instruments: any[]) {
+      this.musicInstrumentList = instruments;
+    },
     /**
      * 判断是否有声部数据,如不存在则获取声部列表
      * @returns Promise
@@ -148,6 +160,26 @@ export const useCatchStore = defineStore('catch-store', {
       } catch (e) {
         return Promise.reject(e);
       }
+    },
+    /**
+     * 获取乐器列表
+     * @returns Promise
+     */
+    async getMusicInstrument() {
+      try {
+        // 判断是否存在声部数据
+        if (this.getMusicInstruments && this.getMusicInstruments.length > 0) {
+          return Promise.resolve();
+        }
+        const { data } = await api_musicalInstrumentList({
+          enableFlag: true
+        });
+        console.log(data, 'data');
+        this.setMusicInstruments(data || []);
+        return Promise.resolve();
+      } catch (e) {
+        return Promise.reject(e);
+      }
     }
   }
 });

+ 11 - 0
src/store/modules/users.ts

@@ -8,6 +8,7 @@ export interface IUserState {
   token: string;
   imToken: string;
   username: string;
+  readCoursewareOpenAgreement: boolean;
   avatar: string;
   info: any;
   imUserInfo: any;
@@ -20,6 +21,7 @@ export const useUserStore = defineStore('user-store', {
     imToken: storage.get(IM_TOKEN, ''),
     username: '',
     avatar: '',
+    readCoursewareOpenAgreement: true, // 是否阅读协议 备课
     noReadCount: 0, // 未读数量
     info: storage.get(CURRENT_USER, {}),
     imUserInfo: {} // IM
@@ -45,6 +47,9 @@ export const useUserStore = defineStore('user-store', {
     },
     getImUserInfo(): any {
       return this.imUserInfo;
+    },
+    getReadCoursewareOpenAgreement(): boolean {
+      return this.readCoursewareOpenAgreement;
     }
   },
   actions: {
@@ -69,6 +74,9 @@ export const useUserStore = defineStore('user-store', {
     setImUserInfo(info: any) {
       this.imUserInfo = info;
     },
+    setReadCoursewareOpenAgreement(info: any) {
+      this.readCoursewareOpenAgreement = info;
+    },
     // 登录
     async login(userInfo: any) {
       try {
@@ -95,6 +103,9 @@ export const useUserStore = defineStore('user-store', {
             this.setUserInfo(result);
             this.setAvatar(result.account.avatar);
             this.setUsername(result.nickname);
+            this.setReadCoursewareOpenAgreement(
+              result.readCoursewareOpenAgreement
+            );
             resolve(true);
           })
           .catch((error: any) => {

+ 36 - 1
src/views/attend-class/index.module.less

@@ -459,7 +459,7 @@
 }
 
 .workContainer {
-  padding: 20px 40px 40px;
+  // padding: 20px 40px 40px;
 
   h2 {
     font-size: 26px;
@@ -868,4 +868,39 @@
 
 .selectClassModal {
   width: 1000px;
+}
+
+.workVisiable {
+  width: 1258px;
+}
+
+.workContainer {
+  display: flex;
+  align-items: center;
+
+  .workTrain {
+    flex: 1;
+    height: 75vh;
+
+    &>div {
+      padding-top: 15px;
+    }
+  }
+
+  :global {
+    .train-container {
+      // max-height: calc(var(--window-page-lesson-height) - 135px) !important;
+      max-height: calc(var(--window-page-lesson-height) - 100px) !important;
+
+      .train-listSection {
+        min-height: calc(var(--window-page-lesson-height) - 100px) !important;
+      }
+    }
+  }
+
+  .resourceMain {
+    flex: 0 0 360px;
+    height: 75vh;
+    box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1);
+  }
 }

+ 54 - 4
src/views/attend-class/index.tsx

@@ -85,6 +85,9 @@ import InstruemntDetail from '/src/views/prepare-lessons/model/source-instrument
 import TheotyDetail from '/src/views/prepare-lessons/model/source-knowledge/detail';
 import MusicDetail from '/src/views/prepare-lessons/model/source-music/detail';
 import ListenModal from '/src/components/card-preview/listen-modal';
+import Train from '../prepare-lessons/components/lesson-main/train';
+import ResourceMain from '../prepare-lessons/components/resource-main';
+import { useResizeObserver } from '@vueuse/core';
 
 export type ToolType = 'init' | 'pen' | 'whiteboard' | 'call';
 export type ToolItem = {
@@ -192,6 +195,7 @@ export default defineComponent({
       removeCourseStatus: false, // 是否布置作业
 
       teacherChapterName: '',
+      lessonPreTrainingId: null,
       selectResourceStatus: false,
       videoState: 'init' as 'init' | 'play',
       videoItemRef: null as any,
@@ -219,6 +223,8 @@ export default defineComponent({
         );
 
         data.teacherChapterName = res.data.name || '';
+        // 布置的作业编号
+        data.lessonPreTrainingId = res.data.lessonPreTrainingId;
         const tempRows = res.data.chapterKnowledgeList || [];
         const temp: any = [];
         const allItem: any = [];
@@ -326,7 +332,7 @@ export default defineComponent({
     onMounted(() => {
       // initMoveable();
       const query = route.query;
-      // console.log(query, props.preStudentNum, '学生人数');
+      console.log(query, props.preStudentNum, '学生人数');
       // 先取参数,
       data.type = props.type || (query.type as any);
       data.courseId = props.courseId || query.courseId;
@@ -1275,6 +1281,9 @@ export default defineComponent({
             data.modelAttendStatus = true;
           } else {
             data.modelTrainStatus = true;
+            nextTick(() => {
+              getModalHeight();
+            });
             data.modelAttendStatus = false;
           }
           break;
@@ -1389,6 +1398,17 @@ export default defineComponent({
       }
     };
 
+    const getModalHeight = () => {
+      const dom: any = document.querySelector('#model-homework-height');
+      if (dom) {
+        useResizeObserver(dom as HTMLElement, (entries: any) => {
+          const entry = entries[0];
+          const { height } = entry.contentRect;
+          dom.style.setProperty('--window-page-lesson-height', height + 'px');
+        });
+      }
+    };
+
     return () => (
       <div id="playContent" class={[styles.playContent, 'wrap']}>
         <div
@@ -1852,6 +1872,9 @@ export default defineComponent({
                 round
                 onClick={() => {
                   data.modelTrainStatus = true;
+                  nextTick(() => {
+                    getModalHeight();
+                  });
                   data.modelAttendStatus = false;
                 }}>
                 布置作业
@@ -1870,7 +1893,7 @@ export default defineComponent({
         </NModal>
 
         {/* 训练设置 */}
-        <NModal
+        {/* <NModal
           transformOrigin="center"
           v-model:show={data.modelTrainStatus}
           preset="card"
@@ -1884,6 +1907,32 @@ export default defineComponent({
             classGroupId={data.classGroupId}
             onClose={() => (data.modelTrainStatus = false)}
           />
+        </NModal> */}
+        <NModal
+          v-model:show={data.modelTrainStatus}
+          preset="card"
+          class={['modalTitle background', styles.workVisiable]}
+          title={'布置作业'}>
+          <div id="model-homework-height" class={styles.workContainer}>
+            <div class={styles.workTrain}>
+              <Train
+                cardType="homeworkRecord"
+                lessonPreTraining={{
+                  title: '',
+                  chapterId: data.courseId, // 课件编号
+                  id: data.lessonPreTrainingId // 作业编号
+                }}
+                courseScheduleId={data.classId}
+                onChange={(val: any) => {
+                  data.modelTrainStatus = val.status;
+                  // getCoursewareList();
+                }}
+              />
+            </div>
+            <div class={styles.resourceMain}>
+              <ResourceMain cardType="homerowk-record" />
+            </div>
+          </div>
         </NModal>
 
         <NModal
@@ -1973,6 +2022,9 @@ export default defineComponent({
                   if (data.removeCourseStatus) {
                     data.modelTrainStatus = true;
                     data.removeVisiable = false;
+                    nextTick(() => {
+                      getModalHeight();
+                    });
                   } else {
                     if (globalState.application) {
                       document.exitFullscreen
@@ -1997,5 +2049,3 @@ export default defineComponent({
     );
   }
 });
-
-// roll-call/index.html

+ 49 - 21
src/views/attend-class/model/train-type/index.module.less

@@ -71,32 +71,34 @@
     }
   }
 
-  .iconDelete {
-    position: absolute;
-    right: 0px;
-    top: 0px;
-    z-index: 11;
-    display: flex;
-    height: 30px;
-    padding: 0;
-
-    img {
-      height: 30px;
-      width: 30px;
-    }
-  }
+  // .iconDelete {
+  //   position: absolute;
+  //   right: 0px;
+  //   top: 0px;
+  //   z-index: 11;
+  //   display: flex;
+  //   height: 30px;
+  //   padding: 0;
+
+  //   img {
+  //     height: 30px;
+  //     width: 30px;
+  //   }
+  // }
 }
 
 .train-content {
-  height: 238px;
+  height: 140px;
   background: #ffffff;
   position: relative;
   overflow: hidden;
+  transition: all 0.2s ease;
 
   :global {
     .n-image {
       width: 100%;
       height: inherit;
+      transition: all 0.2s ease;
 
       img {
         width: 100%;
@@ -188,6 +190,32 @@
       visibility: visible;
       transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
     }
+
+    .iconDelete {
+      opacity: 1;
+      visibility: visible;
+      transition: all .2s ease;
+    }
+
+    :global {
+      .n-image {
+        transform: scale(1.05);
+        transition: all 0.2s ease;
+      }
+    }
+  }
+
+  .iconDelete {
+    position: absolute;
+    top: 8px;
+    right: 10px;
+    z-index: 98;
+    opacity: 0;
+    visibility: hidden;
+    transition: all .2s ease;
+    width: 27px;
+    height: 27px;
+    cursor: pointer;
   }
 }
 
@@ -195,7 +223,7 @@
   display: flex;
   align-items: center;
   justify-content: space-between;
-  padding: 14px 13px;
+  padding: 8px;
 
   .type {
     gap: 8px 10px !important;
@@ -216,12 +244,12 @@
 }
 
 .trainInfo {
-  max-width: 60%;
+  max-width: 82%;
 
   .trainName {
     display: flex;
     align-items: center;
-    font-size: max(15px, 13Px);
+    font-size: max(13px, 12Px);
     font-family: PingFangSC, PingFang SC;
     font-weight: 600;
     color: #131415;
@@ -233,7 +261,7 @@
     display: inline-block;
     background-color: #ccc;
     border-radius: 5px;
-    font-size: max(12px, 11Px);
+    font-size: max(12px, 10Px);
     font-weight: 600;
     color: #FFFFFF;
     height: 18Px;
@@ -254,14 +282,14 @@
     display: flex;
     align-items: center;
     padding-top: 5px;
-    font-size: 12Px;
+    font-size: 11Px;
     font-weight: 500;
     color: rgba(0, 0, 0, 0.5);
 
     :global {
       .n-divider {
         --n-color: rgba(0, 0, 0, 0.5) !important;
-        height: 12Px;
+        height: 11Px;
         margin: 0 5px !important;
       }
     }

+ 26 - 12
src/views/attend-class/model/train-type/index.tsx

@@ -12,9 +12,9 @@ import {
   useMessage,
   NDivider
 } from 'naive-ui';
-
+import iconDelete from '@/views/prepare-lessons/images/icon-delete-default.png';
 import iconEdit from './images/icon-edit.png';
-import iconDelete from './images/icon-delete2.png';
+// import iconDelete from './images/icon-delete2.png';
 import iconBackLook from './images/icon-back-look.png';
 import iconReport from './images/icon-report.png';
 import { useUserStore } from '/src/store/modules/users';
@@ -175,8 +175,16 @@ export default defineComponent({
           props.item.trainingType === 'EVALUATION' ? styles.evaluationType : ''
         ]}
         onClick={() => emit('click', props.item)}>
-        <div class={styles['train-content']}>
-          <NImage src={props.item.coverImg} previewDisabled objectFit="cover" />
+        <div
+          class={[
+            styles['train-content'],
+            props.isDisabled && !props.isCLassWork && styles.trainDisabled
+          ]}>
+          <NImage
+            src={props.item.coverImg}
+            previewDisabled
+            objectFit="contain"
+          />
           {props.isDisabled && !props.isCLassWork ? (
             <div class={styles.disPreview}>
               <NProgress
@@ -226,13 +234,21 @@ export default defineComponent({
                 {props.item.trainingType === 'EVALUATION' ? '分' : '分钟'}
               </p>
             </div>
-          ) : (
-            <div class={styles.preview} onClick={onDetail}>
+          ) : null}
+
+          {/* <div class={styles.preview} onClick={onDetail}>
               <NButton strong secondary class={styles.previewBtn}>
                 预览
               </NButton>
-            </div>
-          )}
+            </div> */}
+          <img
+            src={iconDelete}
+            class={styles.iconDelete}
+            onClick={(e: MouseEvent) => {
+              e.stopPropagation();
+              emit('delete', props.item);
+            }}
+          />
         </div>
         <div class={styles['train-footer']}>
           <div class={styles.trainInfo}>
@@ -273,8 +289,7 @@ export default defineComponent({
                   }}
                 </NTooltip>
               </n-button>
-              {/* {props.type === 'homework' && ( */}
-              <n-button
+              {/* <n-button
                 quaternary
                 disabled={!props.isDelete}
                 class={styles.operation}
@@ -290,8 +305,7 @@ export default defineComponent({
                     default: '删除'
                   }}
                 </NTooltip>
-              </n-button>
-              {/* )} */}
+              </n-button> */}
             </NSpace>
           )}
 

+ 28 - 23
src/views/home/modals/subject-modal/index.tsx

@@ -1,6 +1,6 @@
 import { defineComponent, onMounted, ref } from 'vue';
 import styles from './index.module.less';
-import { NButton, NSpace } from 'naive-ui';
+import { NButton, NScrollbar, NSpace } from 'naive-ui';
 import { useCatchStore } from '/src/store/modules/catchData';
 import iconSelect from '../../../prepare-lessons/images/icon-select.png';
 
@@ -21,8 +21,9 @@ export default defineComponent({
       emit('confirm', item);
     };
     onMounted(async () => {
-      await catchStore.getSubjects();
-      subjectList.value = catchStore.getSubjectList;
+      // await catchStore.getSubjects();
+      await catchStore.getMusicInstrument();
+      subjectList.value = catchStore.getMusicInstruments;
       if (subjectList.value.length > 0) {
         selectSubjectId.value = subjectList.value[0].id;
       }
@@ -34,28 +35,32 @@ export default defineComponent({
           <span>(勾选后则对应乐器下的课件内容将被当前课件内容全部替换)</span>
         </div> */}
 
-        <div class={styles.subjectList}>
-          {subjectList.value.map((subject: any) => (
-            <div
-              class={[
-                styles.subjectItem,
-                selectSubjectId.value === subject.id ? styles.subjectSelect : ''
-              ]}
-              onClick={() => {
-                selectSubjectId.value = subject.id;
-              }}>
-              <div class={styles.imgSection}>
-                <img src={subject.img} />
+        <NScrollbar style={{ maxHeight: '60vh' }}>
+          <div class={styles.subjectList}>
+            {subjectList.value.map((subject: any) => (
+              <div
+                class={[
+                  styles.subjectItem,
+                  selectSubjectId.value === subject.id
+                    ? styles.subjectSelect
+                    : ''
+                ]}
+                onClick={() => {
+                  selectSubjectId.value = subject.id;
+                }}>
+                <div class={styles.imgSection}>
+                  <img src={subject.img} />
 
-                {selectSubjectId.value === subject.id && (
-                  <img src={iconSelect} class={styles.iconSelect} />
-                )}
-              </div>
+                  {selectSubjectId.value === subject.id && (
+                    <img src={iconSelect} class={styles.iconSelect} />
+                  )}
+                </div>
 
-              <p class={styles.subjectName}>{subject.name}</p>
-            </div>
-          ))}
-        </div>
+                <p class={styles.subjectName}>{subject.name}</p>
+              </div>
+            ))}
+          </div>
+        </NScrollbar>
 
         <NSpace class={styles.btnGroupModal} justify="center">
           <NButton round onClick={() => emit('close')}>

BIN
src/views/homework-record/images/icon-ing.png


BIN
src/views/homework-record/images/icon-over.png


+ 59 - 14
src/views/homework-record/index.module.less

@@ -165,6 +165,7 @@
   background: #F7F9FF;
   border-radius: 13px;
   padding: 0 17px 17px;
+  position: relative;
 
   .header {
     display: flex;
@@ -206,21 +207,31 @@
       }
     }
 
+    .ing,
+    .over {
+      position: absolute;
+      top: 0;
+      right: 0;
+      width: 121px;
+      height: 38px;
+      display: inline-block;
+    }
+
     .ing {
-      font-weight: 500;
-      color: #1677FF;
+      background: url('./images/icon-ing.png') no-repeat center;
+      background-size: contain;
     }
 
     .over {
-      font-weight: 500;
-      color: #AAAAAA;
+      background: url('./images/icon-over.png') no-repeat center;
+      background-size: contain;
     }
   }
 
   .content {
-    display: flex;
-    justify-content: space-between;
-    align-items: flex-end;
+    // display: flex;
+    // justify-content: space-between;
+    // align-items: flex-end;
     padding: 13px;
     background: #FFFFFF;
     border-radius: 10px;
@@ -231,6 +242,40 @@
       font-weight: 600;
       color: #000000;
       padding-bottom: 5px;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+
+      :global {
+        .n-space {
+          flex-shrink: 0;
+        }
+      }
+
+      &>p {
+        display: flex;
+        align-items: center;
+      }
+
+      .CLASSWORK,
+      .HOMEWORK {
+        padding: 2px 8px 1px;
+        border-radius: 4px;
+        font-size: max(13px, 11Px);
+        line-height: 18px;
+        margin-right: 7px;
+      }
+
+      .CLASSWORK {
+        color: #E24F4F;
+        background: #FFD2D2;
+      }
+
+      .HOMEWORK {
+        color: #3F8ADF;
+        background: #D2E9FF;
+
+      }
     }
 
     .homeContent {
@@ -242,7 +287,7 @@
       align-items: flex-start;
 
       .pSection {
-        max-width: 500px;
+        max-width: 650px;
       }
 
       .p1,
@@ -288,14 +333,14 @@
     }
 
     .errorBtn {
-      min-width: 93px;
+      // min-width: 93px;
       // height: 30px;
-      --n-height: 36px !important;
+      --n-height: 24px !important;
       // background: #F94D50;
-      border-radius: 7px;
-      --n-font-size: max(13px, 12Px);
-      font-weight: 600;
-      color: #FFFFFF;
+      // border-radius: 7px;
+      --n-font-size: max(15px, 13Px);
+      font-weight: 500 !important;
+      // color: #FFFFFF;
       line-height: 18px;
     }
   }

+ 138 - 60
src/views/homework-record/index.tsx

@@ -24,12 +24,14 @@ import teacherIcon from '@components/layout/images/teacherIcon.png';
 import Pagination from '/src/components/pagination';
 import { api_trainingList, api_withdrawTraining } from './api';
 import TheEmpty from '/src/components/TheEmpty';
-import { getTimes } from '/src/utils';
+import { fscreen, getTimes } from '/src/utils';
 import dayjs from 'dayjs';
 import Train from '../prepare-lessons/components/lesson-main/train';
 import ResourceMain from '../prepare-lessons/components/resource-main';
 import { useResizeObserver } from '@vueuse/core';
 import { nextTick } from 'process';
+import PreviewWindow from '../preview-window';
+import { state as baseState } from '@/state';
 
 export const getCurrentMonth = () => {
   return [dayjs().startOf('month').valueOf(), dayjs().endOf('month').valueOf()];
@@ -44,6 +46,7 @@ export default defineComponent({
       searchForm: {
         keyword: null as any,
         currentClass: '',
+        homeworkType: '',
         currentGradeNum: '',
         subjectId: '',
         gradeYear: '',
@@ -65,7 +68,14 @@ export default defineComponent({
       activeRow: null as any,
       showaddClass: false,
       popSelectYearList: [] as any,
-      popSelectLevelList: [] as any
+      popSelectLevelList: [] as any,
+      previewModal: false,
+      previewParams: {
+        type: '',
+        courseId: '',
+        subjectId: '',
+        detailId: ''
+      } as any
     });
     const formRef = ref();
     const message = useMessage();
@@ -213,6 +223,43 @@ export default defineComponent({
       }
     };
 
+    // 预览上课
+    // courseId: '' as any, // 课件编号
+    //   subjectId: '' as any, // 声部编号
+    //   lessonCourseId: '' as any, // 教材编号
+    //   lessonCoursewareDetailId: '' as any, // 章节
+    //   detailId: '' as any, // 编号 - 课程编号
+    //   classGroupId: '' as any, // 上课时需要 班级编号
+    const onPreviewAttend = (item: any) => {
+      // 判断是否在应用里面
+      if (window.matchMedia('(display-mode: standalone)').matches) {
+        baseState.application = window.matchMedia(
+          '(display-mode: standalone)'
+        ).matches;
+        state.previewModal = true;
+        fscreen();
+        state.previewParams = {
+          type: 'preview',
+          courseId: item.chapterLessonCoursewareId,
+          subjectId: null,
+          detailId: item.lessonCoursewareKnowledgeDetailId,
+          lessonCourseId: item.lessonCoursewareDetailId
+        };
+      } else {
+        const { href } = router.resolve({
+          path: '/attend-class',
+          query: {
+            type: 'preview',
+            courseId: item.chapterLessonCoursewareId,
+            subjectId: null,
+            detailId: item.lessonCoursewareKnowledgeDetailId,
+            lessonCourseId: item.lessonCoursewareDetailId
+          }
+        });
+        window.open(href, +new Date() + '');
+      }
+    };
+
     onMounted(async () => {
       state.loading = true;
       await getYearList();
@@ -381,76 +428,100 @@ export default defineComponent({
                           布置时间:
                           {dayjs(item.createTime).format('YYYY-MM-DD HH:mm')}
                           <span> | </span>
-                          <span style={{ color: '#EA4132' }}>
+                          <span>
                             截止时间:
                             {dayjs(item.expireDate).format('YYYY-MM-DD HH:mm')}
                           </span>
                         </p>
                       </div>
                       <div class={item.status ? styles.over : styles.ing}>
-                        {item.status ? '已结束' : '进行中'}
+                        {/* {item.status ? '已结束' : '进行中'} */}
                       </div>
                     </div>
                     <div class={styles.content}>
-                      <div>
-                        <div class={styles.homeTitle}>{item.name}</div>
-                        <div class={styles.homeContent}>
-                          <span class={styles.title}>作业对象:</span>
-                          <span class={styles.text}>
-                            {item.homeworkObjName}
-                          </span>
-                        </div>
-                        <div class={[styles.homeContent, styles.homeworkText]}>
-                          <span class={styles.title}>作业内容:</span>
-                          <div class={styles.pSection}>
-                            {item.pTitle && (
-                              <p class={[styles.text, styles.p1]}>
-                                {item.pTitle}
-                              </p>
-                            )}
-                            {item.eTitle && (
-                              <p class={[styles.text, styles.p2]}>
-                                {item.eTitle}
-                              </p>
-                            )}
-                          </div>
-                        </div>
-                        <div class={styles.homeSubmit}>
-                          <span class={styles.title}>已提交:</span>
-                          <span class={styles.text}>
-                            {item.trainingNum || 0}/{item.expectNum || 0}人
-                          </span>
-                          <NDivider vertical />
-                          <span class={styles.title}>提交率:</span>
-                          <span class={styles.text}>
-                            {item.trainingRate || 0}%
-                          </span>
-                          <NDivider vertical />
-                          <span class={styles.title}>合格人数:</span>
-                          <span class={styles.text}>
-                            {item.standardNum || 0}人
-                          </span>
-                          <NDivider vertical />
-                          <span class={styles.title}>合格率:</span>
-                          <span class={styles.text}>
-                            {item.qualifiedRate || 0}%
+                      {/* <div> */}
+                      <div class={styles.homeTitle}>
+                        <p>
+                          <span class={styles[item.homeworkType]}>
+                            {item.homeworkType === 'CLASSWORK'
+                              ? '课后'
+                              : '课堂'}
                           </span>
+                          {item.name}
+                        </p>
+
+                        <NSpace>
+                          {item.chapterLessonCoursewareId && (
+                            <NButton
+                              class={styles.errorBtn}
+                              text
+                              color="#1677FF"
+                              onClick={(e: any) => {
+                                e.stopPropagation();
+                                // state.resetVisiable = true;
+                                // state.resetItem = item;
+                                onPreviewAttend(item);
+                              }}>
+                              查看课件
+                            </NButton>
+                          )}
+
+                          {!item.status && (
+                            <NButton
+                              class={styles.errorBtn}
+                              text
+                              color="#1677FF"
+                              onClick={(e: any) => {
+                                e.stopPropagation();
+                                state.resetVisiable = true;
+                                state.resetItem = item;
+                              }}>
+                              撤回
+                            </NButton>
+                          )}
+                        </NSpace>
+                      </div>
+                      <div class={styles.homeContent}>
+                        <span class={styles.title}>作业对象:</span>
+                        <span class={styles.text}>{item.homeworkObjName}</span>
+                      </div>
+                      <div class={[styles.homeContent, styles.homeworkText]}>
+                        <span class={styles.title}>作业内容:</span>
+                        <div class={styles.pSection}>
+                          {item.pTitle && (
+                            <p class={[styles.text, styles.p1]}>
+                              {item.pTitle}
+                            </p>
+                          )}
+                          {item.eTitle && (
+                            <p class={[styles.text, styles.p2]}>
+                              {item.eTitle}
+                            </p>
+                          )}
                         </div>
                       </div>
-
-                      {!item.status && (
-                        <NButton
-                          class={styles.errorBtn}
-                          type="error"
-                          color="#F94D50"
-                          onClick={(e: any) => {
-                            e.stopPropagation();
-                            state.resetVisiable = true;
-                            state.resetItem = item;
-                          }}>
-                          撤回
-                        </NButton>
-                      )}
+                      <div class={styles.homeSubmit}>
+                        <span class={styles.title}>已提交:</span>
+                        <span class={styles.text}>
+                          {item.trainingNum || 0}/{item.expectNum || 0}人
+                        </span>
+                        <NDivider vertical />
+                        <span class={styles.title}>提交率:</span>
+                        <span class={styles.text}>
+                          {item.trainingRate || 0}%
+                        </span>
+                        <NDivider vertical />
+                        <span class={styles.title}>合格人数:</span>
+                        <span class={styles.text}>
+                          {item.standardNum || 0}人
+                        </span>
+                        <NDivider vertical />
+                        <span class={styles.title}>合格率:</span>
+                        <span class={styles.text}>
+                          {item.qualifiedRate || 0}%
+                        </span>
+                      </div>
+                      {/* </div> */}
                     </div>
                   </div>
                 ))}
@@ -516,6 +587,13 @@ export default defineComponent({
             </div>
           </div>
         </NModal>
+
+        {/* 应用内预览或上课 */}
+        <PreviewWindow
+          v-model:show={state.previewModal}
+          type="attend"
+          params={state.previewParams}
+        />
       </div>
     );
   }

+ 2 - 3
src/views/natural-resources/components/my-resources/upload-modal/index.tsx

@@ -118,14 +118,13 @@ export default defineComponent({
               id: item.id || null
             });
           });
-          console.log(isUpdate.value, 'isUpdate.value', props.editStatus);
           if (isUpdate.value) {
             if (!props.editStatus) {
-              await materialUpdateAll(body);
+              const { data } = await materialUpdateAll(body);
               message.success('保存成功');
               uploadForms.list = [];
               emit('close', true);
-              emit('confirm');
+              emit('confirm', data);
             } else {
               emit('editAll', body);
             }

+ 147 - 12
src/views/prepare-lessons/components/lesson-main/courseware-presets/index.module.less

@@ -1,5 +1,24 @@
 .coursewarePresetsContainer {
-  padding-bottom: 20px;
+  // padding-bottom: 20px;
+  height: 100%;
+  display: flex;
+  // position: relative;
+  // overflow: hidden;
+
+  &.rightLineShow {
+    .presetsRight {
+      transition: all .1s ease;
+      opacity: 1;
+      position: relative;
+      transform: none;
+    }
+
+    .list {
+      .itemWrap {
+        width: calc(100% / 3);
+      }
+    }
+  }
 
   :global {
     .n-base-selection {
@@ -12,6 +31,68 @@
     .n-base-selection-input__content {
       font-size: max(15px, 13Px);
     }
+
+    .n-tabs-tab-pad {
+      width: 33px !important;
+    }
+
+    .n-tabs-nav {
+      padding-top: 20px;
+      padding-bottom: 10px;
+      padding-left: 30px;
+      padding-right: 30px;
+    }
+  }
+
+  .presetsLeft {
+    flex: 1;
+    transition: all .1s ease;
+  }
+
+  .presetsRight {
+    position: relative;
+    background: #FFFFFF;
+    box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.1);
+    border-radius: 0px 17px 17px 0px;
+    flex: 0 0 320px;
+
+    transition: all .1s ease;
+    position: absolute;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    transform: translateX(100%);
+
+
+    .presetsArrar {
+      position: absolute;
+      left: -23px;
+      top: 50%;
+      margin-top: -25px;
+      display: inline-block;
+      width: 23px;
+      height: 50px;
+      line-height: 50px;
+      cursor: pointer;
+      background-color: #F5F6FA;
+      border-radius: 10px 0 0 10px;
+      color: #ABAEB0;
+      text-align: center;
+
+      svg {
+        transform: rotate(180deg);
+      }
+
+      &:hover {
+        color: #198CFE;
+      }
+
+      &.presetsArrarActive {
+        svg {
+          transform: rotate(0deg);
+        }
+      }
+    }
   }
 }
 
@@ -21,7 +102,7 @@
 
 .coursewarePresets {
   max-height: calc(var(--window-page-lesson-height) - 100px);
-  padding: 0 20px 0px;
+  // padding: 0 20px 0px;
 }
 
 .btnSubjectList {
@@ -40,16 +121,28 @@
 
 .addBtn {
   // height: max(36px, 32Px) !important;
-  background: #198cfe !important;
+  background: linear-gradient(312deg, #1B7AF8 0%, #3CBBFF 100%) !important;
   border-radius: 7Px !important;
-  padding: 0 20Px !important;
-
-
+  padding: 0 16Px !important;
   border-radius: 8px;
   height: 38px;
-  font-size: max(18px, 13Px) !important;
+  font-size: max(15px, 12Px) !important;
   font-weight: 600 !important;
-
+  position: relative;
+  z-index: 9;
+
+  &::after {
+    content: '';
+    position: absolute;
+    left: 10%;
+    bottom: -1px;
+    display: inline-block;
+    width: 80%;
+    height: 8px;
+    background: linear-gradient(136deg, #31ABFF 0%, #1A7BF8 100%);
+    opacity: 0.6;
+    filter: blur(7px);
+  }
 }
 
 
@@ -142,17 +235,24 @@
   }
 }
 
+.listLoading {}
+
 .list {
   display: flex;
   flex-flow: row wrap;
   justify-content: flex-start;
-  padding: 12px 0 12px;
-  gap: 20px 0;
-  margin: 10px -10px 0;
-  min-height: 313px;
+  padding: 10px 20px 12px;
+  // padding: 0 20px 0px;
+  gap: 10px 0;
+  margin: 0 -10px 0;
+  min-height: calc(var(--window-page-lesson-height) - 100px);
   // height: 313px;
 
 
+  &.listEmpty {
+    align-items: center;
+  }
+
   &.listSame {
     margin-top: 0;
     padding-top: 0;
@@ -228,4 +328,39 @@
   width: 800px;
   border-radius: 16px;
   overflow: hidden;
+}
+
+.workVisiable {
+  width: 1258px;
+}
+
+.workContainer {
+  display: flex;
+  align-items: center;
+
+  .workTrain {
+    flex: 1;
+    height: 75vh;
+
+    &>div {
+      padding-top: 15px;
+    }
+  }
+
+  :global {
+    .train-container {
+      // max-height: calc(var(--window-page-lesson-height) - 135px) !important;
+      max-height: calc(var(--window-page-lesson-height) - 100px) !important;
+
+      .train-listSection {
+        min-height: calc(var(--window-page-lesson-height) - 100px) !important;
+      }
+    }
+  }
+
+  .resourceMain {
+    flex: 0 0 360px;
+    height: 75vh;
+    box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1);
+  }
 }

+ 258 - 260
src/views/prepare-lessons/components/lesson-main/courseware-presets/index.tsx

@@ -1,7 +1,15 @@
-import { defineComponent, onMounted, reactive, ref, watch } from 'vue';
+import {
+  defineComponent,
+  nextTick,
+  onMounted,
+  reactive,
+  ref,
+  watch
+} from 'vue';
 import styles from './index.module.less';
 import {
   NButton,
+  NTooltip,
   NCarousel,
   NIcon,
   NImage,
@@ -11,6 +19,8 @@ import {
   NSelect,
   NSpace,
   NSpin,
+  NTabPane,
+  NTabs,
   useMessage
 } from 'naive-ui';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
@@ -34,6 +44,9 @@ import { useRoute, useRouter } from 'vue-router';
 import TheMessageDialog from '/src/components/TheMessageDialog';
 import { eventGlobal, fscreen } from '/src/utils';
 import PreviewWindow from '/src/views/preview-window';
+import Related from './related';
+import Train from '../train';
+import ResourceMain from '../../resource-main';
 
 export default defineComponent({
   name: 'courseware-presets',
@@ -49,6 +62,8 @@ export default defineComponent({
 
     const forms = reactive({
       // 选取参数带的,后取缓存
+      leftWidth: '100%',
+      rightWidth: '0',
       messageLoading: false,
       subjectId: route.query.subjectId
         ? Number(route.query.subjectId)
@@ -63,6 +78,7 @@ export default defineComponent({
       openLoading: false,
       showRelatedClass: false,
       tableList: [] as any,
+      openTableShow: true, // 是否显示
       openTableList: [] as any,
       selectItem: {} as any,
       editTitleVisiable: false,
@@ -79,7 +95,8 @@ export default defineComponent({
         courseId: '',
         subjectId: '',
         detailId: ''
-      } as any
+      } as any,
+      workVisiable: false
     });
 
     const getCoursewareList = async () => {
@@ -100,13 +117,15 @@ export default defineComponent({
             item.chapterKnowledgeList[0]?.chapterKnowledgeMaterialList[0];
           tempList.push({
             id: item.id,
+            lessonPreTrainingId: item.lessonPreTrainingId,
             openFlag: item.openFlag,
             openFlagEnable: item.openFlagEnable,
             subjectNames: item.subjectNames,
             fromChapterLessonCoursewareId: item.fromChapterLessonCoursewareId,
             name: item.name,
             coverImg: firstItem?.bizInfo.coverImg,
-            type: firstItem?.bizInfo.type
+            type: firstItem?.bizInfo.type,
+            isNotWork: item.lessonPreTrainingNum <= 0 ? true : false // 是否布置作业
           });
         });
         forms.tableList = tempList;
@@ -116,60 +135,15 @@ export default defineComponent({
       forms.loading = false;
     };
 
-    const getOpenCoursewareList = async () => {
-      // 查询公开课件列表
-      forms.openLoading = true;
-      try {
-        // 判断是否有选择对应的课件 或声部
-        if (!prepareStore.getSelectKey) return (forms.openLoading = false);
-        const { data } = await api_queryOpenCoursewareByPage({
-          subjectId: prepareStore.getSubjectId,
-          coursewareDetailKnowledgeId: prepareStore.getSelectKey,
-          page: 1,
-          rows: 20
-        });
-        const result = data.rows || [];
-        const tempList: any = [];
-        result.forEach((item: any) => {
-          // const index = forms.tableList.findIndex(
-          //   (i: any) => i.fromChapterLessonCoursewareId === item.id
-          // );
-          const firstItem: any =
-            item.chapterKnowledgeList[0]?.chapterKnowledgeMaterialList[0];
-          tempList.push({
-            id: item.id,
-            openFlag: item.openFlag,
-            openFlagEnable: item.openFlagEnable,
-            subjectNames: item.subjectNames,
-            fromChapterLessonCoursewareId: item.fromChapterLessonCoursewareId,
-            name: item.name,
-            coverImg: firstItem?.bizInfo.coverImg,
-            type: firstItem?.bizInfo.type,
-            isAdd: item.addFlag
-          });
-        });
-
-        forms.openTableList = chunkArray(tempList, 4);
-      } catch {
-        //
-      }
-      forms.openLoading = false;
-    };
-
-    const chunkArray = (array: any, size: number) => {
-      const result = [];
-      for (let i = 0; i < array.length; i += size) {
-        result.push(array.slice(i, i + size));
-      }
-      return result;
-    };
-
     // 监听选择的key 左侧选择了其它的课
     watch(
       () => [prepareStore.getSelectKey, prepareStore.getSubjectId],
       async () => {
         await getCoursewareList();
-        await getOpenCoursewareList();
+        // await getOpenCoursewareList();
+        eventGlobal.emit('openCoursewareChanged');
+
+        subjectRef.value?.syncBarPosition();
       }
     );
 
@@ -244,45 +218,66 @@ export default defineComponent({
           'prepareLessonSubjectId',
           prepareStore.getSubjectId as any
         );
+
+        subjectRef.value?.syncBarPosition();
       }
     };
 
+    const subjectRef = ref();
     onMounted(async () => {
+      useResizeObserver(
+        document.querySelector('#presetsLeftRef') as HTMLElement,
+        (entries: any) => {
+          const entry = entries[0];
+          const { width } = entry.contentRect;
+          forms.leftWidth = width + 'px';
+        }
+      );
+      useResizeObserver(
+        document.querySelector('#presetsRightRef') as HTMLElement,
+        (entries: any) => {
+          const entry = entries[0];
+          const { width } = entry.contentRect;
+          forms.rightWidth = width + 'px';
+        }
+      );
+
       prepareStore.setClassGroupId(route.query.classGroupId as any);
       if (!prepareStore.getSubjectId) {
         // 获取教材分类列表
         checkSubjectIds();
       }
 
-      // useResizeObserver(
-      //   document.querySelector('#coursewarePresets') as HTMLElement,
-      //   (entries: any) => {
-      //     const entry = entries[0];
-      //     const { width } = entry.contentRect;
-      //     forms.bodyWidth = width + 'px';
-      //   }
-      // );
-
       await getCoursewareList();
-      await getOpenCoursewareList();
     });
 
-    // 重命名
-    const onEditTitleSubmit = async () => {
-      try {
-        await api_updateCoursewareInfo({
-          id: forms.selectItem.id,
-          name: forms.editTitle
+    const getModalHeight = () => {
+      const dom: any = document.querySelector('#model-homework-height');
+      if (dom) {
+        useResizeObserver(dom as HTMLElement, (entries: any) => {
+          const entry = entries[0];
+          const { height } = entry.contentRect;
+          dom.style.setProperty('--window-page-lesson-height', height + 'px');
         });
-        message.success('修改成功');
-        getCoursewareList();
-        // getOpenCoursewareList()
-        forms.editTitleVisiable = false;
-      } catch {
-        //
       }
     };
 
+    // 重命名
+    // const onEditTitleSubmit = async () => {
+    //   try {
+    //     await api_updateCoursewareInfo({
+    //       id: forms.selectItem.id,
+    //       name: forms.editTitle
+    //     });
+    //     message.success('修改成功');
+    //     getCoursewareList();
+    //     // getOpenCoursewareList()
+    //     forms.editTitleVisiable = false;
+    //   } catch {
+    //     //
+    //   }
+    // };
+
     // 删除
     const onRemove = async () => {
       forms.messageLoading = true;
@@ -292,7 +287,8 @@ export default defineComponent({
         });
         message.success('删除成功');
         getCoursewareList();
-        getOpenCoursewareList();
+        // getOpenCoursewareList();
+        eventGlobal.emit('openCoursewareChanged');
         forms.preRemoveVisiable = false;
       } catch {
         //
@@ -310,7 +306,8 @@ export default defineComponent({
         await api_addByOpenCourseware({ id: item.id });
         message.success('添加成功');
         getCoursewareList();
-        getOpenCoursewareList();
+        // getOpenCoursewareList();
+        eventGlobal.emit('openCoursewareChanged');
       } catch {
         //
       }
@@ -398,194 +395,167 @@ export default defineComponent({
       }
     };
 
-    const carouselRef = ref();
-    const onChangeSlide = (type: 'left' | 'right') => {
-      if (type === 'left') {
-        carouselRef.value?.prev();
-      } else if (type === 'right') {
-        carouselRef.value?.next();
-      }
-    };
+    // const carouselRef = ref();
+    // const onChangeSlide = (type: 'left' | 'right') => {
+    //   if (type === 'left') {
+    //     carouselRef.value?.prev();
+    //   } else if (type === 'right') {
+    //     carouselRef.value?.next();
+    //   }
+    // };
     return () => (
-      <div class={styles.coursewarePresetsContainer}>
-        <NScrollbar class={styles.coursewarePresets}>
-          <div class={styles.title} id="coursewarePresets">
-            <div class={styles.titleLeft}>
-              <i class={[styles.icon, styles.iconWork]}></i>
-              我的课件
-            </div>
-          </div>
-
-          <NSpace>
-            <NSelect
-              placeholder="选择声部"
-              class={styles.btnSubjectList}
-              options={[
-                { name: '全部声部', id: '' },
-                ...prepareStore.getSubjectList
-              ]}
-              labelField="name"
-              valueField="id"
-              value={prepareStore.getSubjectId}
-              onUpdate:value={(val: any) => {
-                prepareStore.setSubjectId(val);
-                // 保存
-                forms.subjectId = val;
-
-                if (!val) {
-                  sessionStorage.setItem(
-                    'prepareLessonCourseWareSubjectIsNull',
-                    val ? false : true
-                  );
-                }
-              }}
-            />
-            <NButton
-              class={styles.addBtn}
-              type="primary"
-              onClick={() => {
-                eventGlobal.emit('teacher-slideshow', true);
-                emit('change', {
-                  status: true,
-                  type: 'create'
-                });
-              }}>
-              <NImage
-                class={styles.addBtnIcon}
-                previewDisabled
-                src={add}></NImage>
-              创建课件
-            </NButton>
-          </NSpace>
-          <div style={{ overflow: 'hidden' }}>
-            <NSpin show={forms.loading}>
-              <div class={styles.list}>
-                {forms.tableList.map((item: any) => (
-                  <div class={[styles.itemWrap, styles.itemBlock, 'row-nav']}>
-                    <div class={styles.itemWrapBox}>
-                      <CoursewareType
-                        operate
-                        isEditName
-                        item={item}
-                        onClick={() => onPreviewAttend(item.id)}
-                        onEditName={() => {
-                          forms.selectItem = item;
-                          forms.editTitle = item.name;
-                          forms.editTitleVisiable = true;
-                        }}
-                        onEdit={() => {
-                          //
-                          eventGlobal.emit('teacher-slideshow', true);
-                          emit('change', {
-                            status: true,
-                            type: 'update',
-                            groupItem: { id: item.id }
-                          });
-                        }}
-                        onStartClass={() =>
-                          onStartClass(item, forms.classGroupId)
-                        }
-                        onDelete={() => {
-                          forms.selectItem = item;
-                          forms.preRemoveVisiable = true;
-                        }}
-                      />
+      <div
+        class={[
+          styles.coursewarePresetsContainer,
+          forms.openTableShow && styles.rightLineShow
+        ]}>
+        <div
+          class={styles.presetsLeft}
+          id="presetsLeftRef"
+          style={{ width: `calc(${forms.leftWidth} - ${forms.rightWidth})` }}>
+          <NTabs
+            ref={subjectRef}
+            defaultValue="subject"
+            paneClass={styles.paneTitle}
+            justifyContent="start"
+            paneWrapperClass={styles.paneWrapperContainer}
+            value={prepareStore.getSubjectId?.toString()}
+            onUpdate:value={val => {
+              prepareStore.setSubjectId(val);
+              // 保存
+              forms.subjectId = val;
+
+              if (!val) {
+                sessionStorage.setItem(
+                  'prepareLessonCourseWareSubjectIsNull',
+                  val ? 'false' : 'true'
+                );
+              }
+            }}
+            v-slots={{
+              suffix: () => (
+                <NButton
+                  class={styles.addBtn}
+                  type="primary"
+                  bordered={false}
+                  onClick={() => {
+                    eventGlobal.emit('teacher-slideshow', true);
+                    emit('change', {
+                      status: true,
+                      type: 'create'
+                    });
+                  }}>
+                  <NImage
+                    class={styles.addBtnIcon}
+                    previewDisabled
+                    src={add}></NImage>
+                  创建课件
+                </NButton>
+              )
+            }}>
+            {[{ name: '全部声部', id: '' }, ...prepareStore.getSubjectList].map(
+              (item: any) => (
+                <NTabPane
+                  name={`${item.id}`}
+                  tab={item.name}
+                  displayDirective="if"></NTabPane>
+              )
+            )}
+          </NTabs>
+          <NSpin show={forms.loading}>
+            <NScrollbar class={styles.coursewarePresets}>
+              <div style={{ overflow: 'hidden' }}>
+                <div
+                  class={[
+                    styles.list,
+                    !forms.loading &&
+                      forms.tableList.length <= 0 &&
+                      styles.listEmpty
+                  ]}>
+                  {forms.tableList.map((item: any) => (
+                    <div class={[styles.itemWrap, styles.itemBlock, 'row-nav']}>
+                      <div class={styles.itemWrapBox}>
+                        <CoursewareType
+                          operate
+                          isEditName
+                          item={item}
+                          onClick={() => onPreviewAttend(item.id)}
+                          // onEditName={() => {
+                          //   forms.selectItem = item;
+                          //   forms.editTitle = item.name;
+                          //   forms.editTitleVisiable = true;
+                          // }}
+                          onEdit={() => {
+                            //
+                            eventGlobal.emit('teacher-slideshow', true);
+                            emit('change', {
+                              status: true,
+                              type: 'update',
+                              groupItem: { id: item.id }
+                            });
+                          }}
+                          onStartClass={() =>
+                            onStartClass(item, forms.classGroupId)
+                          }
+                          onDelete={() => {
+                            forms.selectItem = item;
+                            forms.preRemoveVisiable = true;
+                          }}
+                          // 布置作业
+                          onWork={() => {
+                            forms.workVisiable = true;
+                            forms.selectItem = item;
+                            nextTick(() => {
+                              getModalHeight();
+                            });
+                          }}
+                        />
+                      </div>
                     </div>
-                  </div>
-                ))}
-                {!forms.loading && forms.tableList.length <= 0 && (
-                  <TheEmpty class={styles.empty1} description="暂无课件" />
-                )}
-              </div>
-            </NSpin>
-          </div>
-
-          {forms.openTableList.length > 0 && (
-            <>
-              <div class={[styles.title, styles.line]}>
-                <div class={styles.titleLeft}>
-                  <i class={[styles.icon, styles.iconCourseware]}></i>
-                  相关课件
-                  {forms.openTableList.length > 1 && (
-                    <span
-                      class={styles.more}
-                      onClick={() => (forms.showRelatedClass = true)}>
-                      查看更多
-                      {/* <NIcon size={15}>
-                        <svg
-                          xmlns="http://www.w3.org/2000/svg"
-                          viewBox="0 0 24 24">
-                          <path
-                            d="M8.59 16.59L13.17 12L8.59 7.41L10 6l6 6l-6 6l-1.41-1.41z"
-                            fill="currentColor"></path>
-                        </svg>
-                      </NIcon> */}
-                      <i class={styles.iconP}></i>
-                    </span>
+                  ))}
+                  {!forms.loading && forms.tableList.length <= 0 && (
+                    <TheEmpty class={styles.empty1} description="暂无课件" />
                   )}
                 </div>
-
-                {forms.openTableList.length > 1 && (
-                  <NSpace class={styles.swipeControll}>
-                    <div onClick={() => onChangeSlide('left')}>
-                      <NImage
-                        previewDisabled
-                        class={[
-                          styles.leftIcon,
-                          forms.carouselIndex === 0 && styles.disabled
-                        ]}
-                        src={iconSlideRight}
-                      />
-                    </div>
-                    <div onClick={() => onChangeSlide('right')}>
-                      <NImage
-                        class={
-                          forms.carouselIndex ==
-                            forms.openTableList.length - 1 && styles.disabled
-                        }
-                        previewDisabled
-                        src={iconSlideRight}
-                      />
-                    </div>
-                  </NSpace>
-                )}
               </div>
+            </NScrollbar>
+          </NSpin>
+        </div>
+
+        <div class={styles.presetsRight} id="presetsRightRef">
+          <NTooltip showArrow={false}>
+            {{
+              trigger: () => (
+                <div
+                  class={[
+                    styles.presetsArrar,
+                    !forms.openTableShow && styles.presetsArrarActive
+                  ]}
+                  onClick={() => (forms.openTableShow = !forms.openTableShow)}>
+                  <NIcon>
+                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
+                      <path
+                        d="M16.62 2.99a1.25 1.25 0 0 0-1.77 0L6.54 11.3a.996.996 0 0 0 0 1.41l8.31 8.31c.49.49 1.28.49 1.77 0s.49-1.28 0-1.77L9.38 12l7.25-7.25c.48-.48.48-1.28-.01-1.76z"
+                        fill="currentColor"></path>
+                    </svg>
+                  </NIcon>
+                </div>
+              ),
+              default: () => <div>{forms.openTableShow ? '收起' : '展开'}</div>
+            }}
+          </NTooltip>
 
-              <NSpin show={forms.openLoading} class={styles.openLoading}>
-                <NCarousel
-                  slidesPerView={1}
-                  loop={false}
-                  ref={carouselRef}
-                  // style={{ width: forms.bodyWidth }}
-                  v-model:currentIndex={forms.carouselIndex}>
-                  {forms.openTableList.map((item: any) => (
-                    <div class={[styles.list, styles.listSame]}>
-                      {item.map((child: any) => (
-                        <div
-                          class={[
-                            styles.itemWrap,
-                            styles.itemBlock,
-                            'row-nav'
-                          ]}>
-                          <div class={styles.itemWrapBox}>
-                            <CoursewareType
-                              isShowOpenFlag={false}
-                              isShowAdd
-                              isHoverShowAdd={false}
-                              item={child}
-                              onClick={() => onPreviewAttend(child.id)}
-                              onAdd={() => onAddCourseware(child)}
-                            />
-                          </div>
-                        </div>
-                      ))}
-                    </div>
-                  ))}
-                </NCarousel>
-              </NSpin>
-            </>
-          )}
-        </NScrollbar>
+          <Related
+            onMore={() => (forms.showRelatedClass = true)}
+            onAdd={(item: any) => {
+              onAddCourseware(item);
+            }}
+            onLook={(item: any) => {
+              onPreviewAttend(item.id);
+            }}
+          />
+        </div>
+        {/* )} */}
 
         <NModal
           v-model:show={forms.showRelatedClass}
@@ -608,7 +578,7 @@ export default defineComponent({
           />
         </NModal>
 
-        <NModal
+        {/* <NModal
           v-model:show={forms.editTitleVisiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable1]}
@@ -638,7 +608,7 @@ export default defineComponent({
               </NButton>
             </NSpace>
           </div>
-        </NModal>
+        </NModal> */}
 
         <NModal
           v-model:show={forms.preRemoveVisiable}
@@ -696,6 +666,34 @@ export default defineComponent({
             }}
           />
         </NModal>
+
+        <NModal
+          v-model:show={forms.workVisiable}
+          preset="card"
+          class={['modalTitle background', styles.workVisiable]}
+          title={
+            forms.selectItem.lessonPreTrainingId ? '编辑作业' : '创建作业'
+          }>
+          <div id="model-homework-height" class={styles.workContainer}>
+            <div class={styles.workTrain}>
+              <Train
+                cardType="prepare"
+                lessonPreTraining={{
+                  title: '',
+                  chapterId: forms.selectItem.id, // 课件编号
+                  id: forms.selectItem.lessonPreTrainingId // 作业编号
+                }}
+                onChange={(val: any) => {
+                  forms.workVisiable = val.status;
+                  getCoursewareList();
+                }}
+              />
+            </div>
+            <div class={styles.resourceMain}>
+              <ResourceMain cardType="prepare" />
+            </div>
+          </div>
+        </NModal>
       </div>
     );
   }

+ 80 - 0
src/views/prepare-lessons/components/lesson-main/courseware-presets/related.module.less

@@ -0,0 +1,80 @@
+.related {
+  height: 100%;
+
+  :global {
+    .n-tabs-tab-pad {
+      width: 24px !important;
+    }
+
+    .n-tabs-nav {
+      padding: 12px 20px 24px;
+      position: relative;
+    }
+
+    .n-tabs-tab {
+      color: #8B8D98;
+      font-size: max(18px, 13Px);
+      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;
+    }
+
+    .n-tabs-nav__suffix {
+      padding-left: 0 !important;
+    }
+
+    .n-tabs.n-tabs--top .n-tabs-nav-scroll-wrapper::after {
+      box-shadow: none !important;
+    }
+  }
+
+  .iconScreen {
+    margin-top: 6px;
+    position: absolute;
+    right: 20px;
+    font-size: 18px;
+    color: var(--n-color);
+    font-weight: bold;
+    width: 22px;
+    height: 22px;
+    cursor: pointer;
+  }
+
+  .homerowkTabs {
+    :global {
+      .n-tabs-tab-pad {
+        width: 15px !important;
+      }
+
+      .n-tabs-tab {
+        color: #8B8D98;
+        font-size: max(18px, 13Px);
+      }
+
+      .v-x-scroll {
+        padding-right: 40px;
+      }
+    }
+  }
+
+}

+ 51 - 0
src/views/prepare-lessons/components/lesson-main/courseware-presets/related.tsx

@@ -0,0 +1,51 @@
+import { defineComponent } from 'vue';
+import styles from './related.module.less';
+import { NTabPane, NTabs } from 'naive-ui';
+import SelectRelated from './select-related';
+
+export default defineComponent({
+  name: 'related-modal',
+  emits: ['more', 'add', 'look'],
+  setup(props, { emit }) {
+    return () => (
+      <div class={styles.related}>
+        <NTabs
+          animated
+          paneClass={styles.paneTitle}
+          paneWrapperClass={styles.paneWrapperContainer}
+          justifyContent="center">
+          {{
+            suffix: () => (
+              <div
+                class={styles.iconScreen}
+                onClick={() => {
+                  // forms.selectMusicStatus = true;
+                  // prepareStore.setSelectMusicStatus(true);
+                  emit('more');
+                }}>
+                <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="myMusic" tab="相关课件">
+                  {/* <SelectMusic cardType={props.cardType} type="myMusic" /> */}
+                  <SelectRelated
+                    onAdd={(item: any) => emit('add', item)}
+                    onLook={(item: any) => emit('look', item)}
+                  />
+                </NTabPane>
+              </>
+            )
+          }}
+        </NTabs>
+      </div>
+    );
+  }
+});

+ 145 - 0
src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/index.module.less

@@ -0,0 +1,145 @@
+.listContainer {
+  margin: 10px 0;
+  max-height: calc(var(--window-page-lesson-height) - 204px);
+  // overflow-x: auto;
+
+  &.listNoMusic {
+    max-height: calc(var(--window-page-lesson-height) - 150px);
+
+    .listSection {
+      min-height: calc(var(--window-page-lesson-height) - 150px);
+    }
+  }
+
+  .listSection {
+    min-height: calc(var(--window-page-lesson-height) - 204px);
+  }
+
+
+
+  .emptySection {
+    display: flex;
+    align-items: center;
+  }
+
+  .list {
+    padding: 10px 0;
+    text-align: center;
+
+    &>div {
+      margin-bottom: 20px;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+  }
+}
+
+.trainEditModal {
+  width: 494px;
+}
+
+.itemModal {
+  display: flex;
+  align-items: center;
+  margin: 0 17px 20px;
+
+  .itemCover {
+    position: relative;
+    width: 111px;
+    height: 63px;
+    margin-right: 10px;
+    border-radius: 7px;
+    border: 1px solid #EFF0F2;
+
+    :global {
+      .n-image {
+        width: 111px;
+        height: 63px;
+      }
+    }
+
+    img {
+      width: 100%;
+      height: 100%;
+    }
+
+    &:hover {
+      .function {
+        opacity: 1;
+        transition: opacity 0.2s ease;
+      }
+    }
+  }
+
+  .function {
+    position: absolute;
+    inset: 0;
+    background: rgba(0, 0, 0, .3);
+    display: flex;
+    align-items: center;
+    justify-content: space-evenly;
+    opacity: 0;
+    transition: opacity 0.2s ease;
+
+    .iconLook,
+    .iconAdd {
+      cursor: pointer;
+      width: 27px;
+      height: 27px;
+      border-radius: 50%;
+      background: rgba(0, 0, 0, .4);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+    }
+
+    .iconLook {
+      &::before {
+        display: inline-block;
+        content: '';
+        width: 15px;
+        height: 15px;
+        background: url('../../../../images/icon-look.png') no-repeat center;
+        background-size: contain;
+      }
+    }
+
+
+    .iconAdd {
+      &::before {
+        display: inline-block;
+        content: '';
+        width: 12px;
+        height: 12px;
+        background: url('../../../../images/icon-add.png') no-repeat center;
+        background-size: contain;
+      }
+    }
+  }
+
+  .itemContent {
+    max-width: 158px;
+    text-align: left;
+
+    .itemTitle {
+      font-weight: 600;
+      font-size: 12Px;
+      color: #000000;
+      line-height: 17px;
+    }
+
+    .itemSubject {
+      padding-top: 7px;
+      width: 100%;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      font-size: 11Px;
+      color: #777777;
+      line-height: 15px;
+    }
+  }
+}

+ 146 - 0
src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/index.tsx

@@ -0,0 +1,146 @@
+import { PropType, defineComponent, onMounted, reactive, watch } from 'vue';
+import ResourceSearchGroup from './resource-search-group';
+import { NModal, NScrollbar, NSpin } from 'naive-ui';
+import styles from './index.module.less';
+import TheEmpty from '/src/components/TheEmpty';
+import { useThrottleFn } from '@vueuse/core';
+import { usePrepareStore } from '/src/store/modules/prepareLessons';
+import { api_queryOpenCoursewareByPage } from '/src/views/prepare-lessons/api';
+import Item from './item';
+import { eventGlobal } from '/src/utils';
+
+export default defineComponent({
+  name: 'share-resources',
+  emits: ['look', 'add'],
+  setup(props, { emit }) {
+    const prepareStore = usePrepareStore();
+    const state = reactive({
+      loading: false,
+      finshed: false, // 是否加载完
+      pagination: {
+        page: 1,
+        rows: 10
+      },
+      searchGroup: {
+        keyword: ''
+      },
+      tableList: [] as any,
+      editStatus: false,
+      editItem: {} as any,
+      show: false,
+      item: {} as any
+    });
+    const getList = async () => {
+      try {
+        if (!prepareStore.getSelectKey) return;
+        if (state.pagination.page === 1) {
+          state.loading = true;
+        }
+        // 查询公开课件列表
+        const { data } = await api_queryOpenCoursewareByPage({
+          subjectId: prepareStore.getSubjectId,
+          coursewareDetailKnowledgeId: prepareStore.getSelectKey,
+          ...state.searchGroup,
+          ...state.pagination
+        });
+        const result = data.rows || [];
+        const tempList: any = [];
+        result.forEach((item: any) => {
+          // const index = forms.tableList.findIndex(
+          //   (i: any) => i.fromChapterLessonCoursewareId === item.id
+          // );
+          const firstItem: any =
+            item.chapterKnowledgeList[0]?.chapterKnowledgeMaterialList[0];
+          tempList.push({
+            id: item.id,
+            openFlag: item.openFlag,
+            openFlagEnable: item.openFlagEnable,
+            subjectNames: item.subjectNames,
+            fromChapterLessonCoursewareId: item.fromChapterLessonCoursewareId,
+            name: item.name,
+            coverImg: firstItem?.bizInfo.coverImg,
+            type: firstItem?.bizInfo.type,
+            isAdd: item.addFlag
+          });
+        });
+
+        state.loading = false;
+        state.tableList.push(...tempList);
+        // console.log(result, 'result', data);
+        state.finshed = data.pages <= data.current ? true : false;
+      } catch {
+        state.loading = false;
+      }
+    };
+
+    const onSearch = async (item: any) => {
+      state.pagination.page = 1;
+      state.tableList = [];
+      state.searchGroup = Object.assign(state.searchGroup, item);
+      getList();
+    };
+
+    // 声部变化时
+    // watch(
+    //   () => prepareStore.getSubjectId,
+    //   () => {
+    //     onSearch(state.searchGroup);
+    //   }
+    // );
+
+    const throttledFn = useThrottleFn(() => {
+      state.pagination.page = state.pagination.page + 1;
+      getList();
+    }, 500);
+
+    onMounted(() => {
+      getList();
+      eventGlobal.on('openCoursewareChanged', () =>
+        onSearch(state.searchGroup)
+      );
+    });
+    return () => (
+      <div>
+        <ResourceSearchGroup onSearch={(item: any) => onSearch(item)} />
+        <NScrollbar
+          class={[styles.listContainer, styles.listNoMusic]}
+          onScroll={(e: any) => {
+            const clientHeight = e.target?.clientHeight;
+            const scrollTop = e.target?.scrollTop;
+            const scrollHeight = e.target?.scrollHeight;
+            // 是否到底,是否加载完
+            if (
+              clientHeight + scrollTop + 20 >= scrollHeight &&
+              !state.finshed &&
+              !state.loading
+            ) {
+              throttledFn();
+            }
+          }}>
+          <NSpin show={state.loading} size={'small'}>
+            <div
+              class={[
+                styles.listSection,
+                !state.loading && state.tableList.length <= 0
+                  ? styles.emptySection
+                  : ''
+              ]}>
+              {state.tableList.length > 0 && (
+                <div class={styles.list}>
+                  {state.tableList.map((item: any) => (
+                    <Item
+                      item={item}
+                      onAdd={() => emit('add', item)}
+                      onLook={() => emit('look', item)}
+                    />
+                  ))}
+                </div>
+              )}
+              {!state.loading && state.tableList.length <= 0 && <TheEmpty />}
+            </div>
+          </NSpin>
+        </NScrollbar>
+      </div>
+    );
+  }
+});

+ 51 - 0
src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/item.tsx

@@ -0,0 +1,51 @@
+import { defineComponent } from 'vue';
+import styles from './index.module.less';
+import { NImage, NTooltip } from 'naive-ui';
+import TheNoticeBar from '/src/components/TheNoticeBar';
+
+export default defineComponent({
+  name: 'item-modal',
+  props: {
+    item: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  emits: ['look', 'add'],
+  setup(props, { emit }) {
+    return () => (
+      <div class={styles.itemModal}>
+        <div class={styles.itemCover}>
+          <NImage src={props.item.coverImg} objectFit="cover" previewDisabled />
+
+          <div class={styles.function}>
+            <NTooltip showArrow={false}>
+              {{
+                trigger: () => (
+                  <i class={styles.iconLook} onClick={() => emit('look')}></i>
+                ),
+                default: () => '预览课件'
+              }}
+            </NTooltip>
+            <NTooltip showArrow={false}>
+              {{
+                trigger: () => (
+                  <i class={styles.iconAdd} onClick={() => emit('add')}></i>
+                ),
+                default: () => '添加到我的课件'
+              }}
+            </NTooltip>
+          </div>
+        </div>
+
+        <div class={styles.itemContent}>
+          <p class={styles.itemTitle}>
+            <TheNoticeBar text={props.item.name} />
+          </p>
+
+          <div class={styles.itemSubject}>{props.item.subjectNames}</div>
+        </div>
+      </div>
+    );
+  }
+});

+ 56 - 0
src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/resource-search-group/index.module.less

@@ -0,0 +1,56 @@
+.searchGroup {
+  padding: 0 20px;
+
+  .searchSelect {
+    padding: 0 0 20px;
+    display: flex;
+    justify-content: flex-start;
+    gap: 0px 16px;
+  }
+
+  :global {
+
+    .n-base-selection,
+    .n-input {
+      border-radius: 8px;
+      min-height: 40px;
+      height: 40px;
+      font-size: max(15px, 12Px);
+      --n-height: 40px !important;
+    }
+  }
+}
+
+.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;
+      }
+    }
+  }
+}

+ 53 - 0
src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/resource-search-group/index.tsx

@@ -0,0 +1,53 @@
+import { PropType, defineComponent, onMounted, reactive } from 'vue';
+import styles from './index.module.less';
+import { NButton, NInput } from 'naive-ui';
+import { useCatchStore } from '/src/store/modules/catchData';
+import { useThrottleFn } from '@vueuse/core';
+
+export default defineComponent({
+  name: 'resource-search-group',
+  emits: ['search'],
+  setup(props, { emit }) {
+    const catchStore = useCatchStore();
+    const forms = reactive({
+      keyword: ''
+    });
+
+    const onSearch = () => {
+      emit('search', forms);
+    };
+
+    const throttledFn = useThrottleFn(() => onSearch(), 500);
+
+    // onMounted(async () => {});
+    return () => (
+      <>
+        <div class={styles.searchGroup}>
+          <NInput
+            type="text"
+            placeholder="请输入课件标题关键词"
+            clearable
+            v-model:value={forms.keyword}
+            class={styles.inputSearch}
+            onKeyup={(e: KeyboardEvent) => {
+              if (e.code === 'Enter') {
+                throttledFn();
+              }
+            }}
+            onClear={() => {
+              forms.keyword = '';
+              throttledFn();
+            }}>
+            {{
+              prefix: () => (
+                <span
+                  class={'icon-search-input'}
+                  onClick={() => throttledFn()}></span>
+              )
+            }}
+          </NInput>
+        </div>
+      </>
+    );
+  }
+});

+ 29 - 0
src/views/prepare-lessons/components/lesson-main/courseware/addCourseware.tsx

@@ -4,6 +4,7 @@ import {
   onMounted,
   onUnmounted,
   reactive,
+  ref,
   watch
 } from 'vue';
 import styles from './addCourseware.module.less';
@@ -50,6 +51,8 @@ import TheMessageDialog from '/src/components/TheMessageDialog';
 import AddItemModel from '../../../model/add-item-model';
 import AddOtherSource from '../../../model/add-other-source';
 import deepClone from '/src/helpers/deep-clone';
+import AddCoursewareProtocol from '../../../model/add-courseware-protocol';
+import { useUserStore } from '/src/store/modules/users';
 export default defineComponent({
   name: 'courseware-modal',
   props: {
@@ -61,6 +64,8 @@ export default defineComponent({
   emits: ['change'],
   setup(props, { emit }) {
     // const catchStore = useCatchStore();
+    const userStore = useUserStore();
+
     const prepareStore = usePrepareStore();
     // const route = useRoute();
     // const router = useRouter();
@@ -116,6 +121,7 @@ export default defineComponent({
       addOtherSource: false,
       addOtherIndex: 0 // 添加其它的索引
     });
+    const showModalMask = ref(false);
 
     // 获取列表
     const getList = async () => {
@@ -381,6 +387,11 @@ export default defineComponent({
           message.error('请至少添加一个资源');
           return;
         }
+
+        if (forms.openFlag && !userStore.getReadCoursewareOpenAgreement) {
+          showModalMask.value = true;
+          return;
+        }
         const resultStatus = await onSaveCourseWare();
 
         if (resultStatus) {
@@ -947,6 +958,24 @@ export default defineComponent({
             }}
           />
         </NModal>
+
+        <NModal v-model:show={showModalMask.value}>
+          <AddCoursewareProtocol
+            onClose={() => (showModalMask.value = false)}
+            onConfirm={async () => {
+              try {
+                const resultStatus = await onSaveCourseWare();
+                if (resultStatus) {
+                  userStore.setReadCoursewareOpenAgreement(true);
+                  emit('change', { status: false });
+                  eventGlobal.emit('teacher-slideshow', false);
+                }
+              } catch {
+                //
+              }
+            }}
+          />
+        </NModal>
       </div>
     );
   }

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

@@ -1,4 +1,6 @@
 .lesson-main {
+  height: 100%;
+
   :global {
     .n-tabs-tab-pad {
       width: 64px !important;

+ 71 - 81
src/views/prepare-lessons/components/lesson-main/index.tsx

@@ -32,36 +32,36 @@ export default defineComponent({
 
     return () => (
       <div class={styles['lesson-main']}>
-        <NTabs
-          ref={lessonMainRef}
-          defaultValue="courseware"
-          paneClass={styles.paneTitle}
-          justifyContent="center"
-          paneWrapperClass={styles.paneWrapperContainer}
-          value={prepareStore.getTabType}
-          onUpdate:value={(val: string) => {
-            prepareStore.setTabType(val);
-            // 重置编辑状态
-            prepareStore.setIsEditResource(false);
-            prepareStore.setIsEditTrain(false);
+        {state.editCoursewareShow ? (
+          <NTabs
+            ref={lessonMainRef}
+            defaultValue="courseware"
+            paneClass={styles.paneTitle}
+            justifyContent="center"
+            paneWrapperClass={styles.paneWrapperContainer}
+            value={prepareStore.getTabType}
+            onUpdate:value={(val: string) => {
+              prepareStore.setTabType(val);
+              // 重置编辑状态
+              prepareStore.setIsEditResource(false);
+              prepareStore.setIsEditTrain(false);
 
-            eventGlobal.emit('teacher-slideshow', false);
-            if (val !== 'train') {
-              state.editWorkShow = false;
-            }
-          }}>
-          {!state.editWorkShow && (
-            <NTabPane
-              name="courseware"
-              tab={
-                state.editCoursewareShow
-                  ? state.coursewareType === 'create'
-                    ? '创建课件'
-                    : '编辑课件'
-                  : '课件'
+              eventGlobal.emit('teacher-slideshow', false);
+              if (val !== 'train') {
+                state.editWorkShow = false;
               }
-              displayDirective="if">
-              {state.editCoursewareShow ? (
+            }}>
+            {!state.editWorkShow && (
+              <NTabPane
+                name="courseware"
+                tab={
+                  state.editCoursewareShow
+                    ? state.coursewareType === 'create'
+                      ? '创建课件'
+                      : '编辑课件'
+                    : '课件'
+                }
+                displayDirective="if">
                 <Courseware
                   groupItem={state.editCourseware}
                   onChange={(val: any) => {
@@ -72,64 +72,54 @@ export default defineComponent({
                     }
                   }}
                 />
+              </NTabPane>
+            )}
+          </NTabs>
+        ) : (
+          <CoursewarePresets
+            onChange={(val: any) => {
+              state.coursewareType = val.type;
+              state.editCoursewareShow = val.status;
+              prepareStore.setIsEditResource(val.status);
+              state.editCourseware = val.groupItem;
+            }}
+          />
+        )}
+
+        {/* {!state.editCoursewareShow && (
+          <NTabPane
+            name="train"
+            // tab="作业"
+            tab={state.editWorkShow ? '编辑作业' : '作业'}
+            displayDirective="if">
+            <div>
+              {state.editWorkShow ? (
+                <Train
+                  lessonPreTraining={state.editWork}
+                  onChange={(val: any) => {
+                    state.editWorkShow = val.status;
+
+                    if (!val.status) {
+                      eventGlobal.emit('teacher-slideshow', false);
+                    }
+                  }}
+                />
               ) : (
-                <CoursewarePresets
+                <TrainPresets
                   onChange={(val: any) => {
-                    state.coursewareType = val.type;
-                    state.editCoursewareShow = val.status;
-                    prepareStore.setIsEditResource(val.status);
-                    state.editCourseware = val.groupItem;
+                    state.editWorkShow = val.status;
+                    state.editWork = {
+                      ...val.lessonPreTraining,
+                      title:
+                        val.lessonPreTraining?.title ||
+                        prepareStore.getSelectName + '课后作业'
+                    };
                   }}
                 />
               )}
-            </NTabPane>
-          )}
-
-          {!state.editCoursewareShow && (
-            <NTabPane
-              name="train"
-              // tab="作业"
-              tab={state.editWorkShow ? '编辑作业' : '作业'}
-              displayDirective="if"
-              // v-slots={{
-              //   tab: () =>
-              //     state.editWorkShow ? (
-              //       <span id="lessons-4">编辑作业</span>
-              //     ) : (
-              //       <span id="lessons-4">作业</span>
-              //     )
-              // }}
-              // {...{ id: 'lessons-4' }}
-            >
-              <div>
-                {state.editWorkShow ? (
-                  <Train
-                    lessonPreTraining={state.editWork}
-                    onChange={(val: any) => {
-                      state.editWorkShow = val.status;
-
-                      if (!val.status) {
-                        eventGlobal.emit('teacher-slideshow', false);
-                      }
-                    }}
-                  />
-                ) : (
-                  <TrainPresets
-                    onChange={(val: any) => {
-                      state.editWorkShow = val.status;
-                      state.editWork = {
-                        ...val.lessonPreTraining,
-                        title:
-                          val.lessonPreTraining?.title ||
-                          prepareStore.getSelectName + '课后作业'
-                      };
-                    }}
-                  />
-                )}
-              </div>
-            </NTabPane>
-          )}
-        </NTabs>
+            </div>
+          </NTabPane>
+        )} */}
       </div>
     );
   }

+ 8 - 1
src/views/prepare-lessons/components/lesson-main/train/assign-homework.tsx

@@ -33,6 +33,11 @@ export default defineComponent({
       type: String,
       default: ''
     },
+    /** 课件编号 */
+    chapterLessonCoursewareId: {
+      type: String,
+      default: ''
+    },
     item: {
       type: Object,
       default: () => ({})
@@ -60,6 +65,7 @@ export default defineComponent({
       uploading: false,
       title: props.item.title,
       courseScheduleId: props.courseScheduleId || null,
+      chapterLessonCoursewareId: props.chapterLessonCoursewareId || null,
       gradeList: [] as any,
       classList: [] as any,
       currentGradeNum: null,
@@ -150,7 +156,8 @@ export default defineComponent({
               ? forms.classGroupId.join(',')
               : null,
             studentIds: null as any,
-            courseScheduleId: forms.courseScheduleId
+            courseScheduleId: forms.courseScheduleId,
+            chapterLessonCoursewareId: forms.chapterLessonCoursewareId // 课件编号
           };
           if (forms.homeworkObj === 'PERSON') {
             params.classGroupId = '';

+ 1 - 1
src/views/prepare-lessons/components/lesson-main/train/index.module.less

@@ -154,7 +154,7 @@
   gap: 20px 2%;
 
   &>div {
-    width: 49% !important;
+    width: 32% !important;
   }
 
   .itemBlock {

+ 59 - 44
src/views/prepare-lessons/components/lesson-main/train/index.tsx

@@ -47,7 +47,7 @@ export default defineComponent({
       default: () => ({})
     },
     cardType: {
-      type: String as PropType<'' | 'homeworkRecord'>,
+      type: String as PropType<'' | 'homeworkRecord' | 'prepare'>,
       default: ''
     },
     /** 编辑编号  - 目前从上传传 */
@@ -68,6 +68,7 @@ export default defineComponent({
   },
   emits: ['change'],
   setup(props, { emit }) {
+    console.log(props.courseScheduleId, 'courseScheduleId');
     const catchStore = useCatchStore();
     const prepareStore = usePrepareStore();
     const dialog = useDialog();
@@ -84,7 +85,6 @@ export default defineComponent({
       editStatus: false,
       editItem: {} as any,
       removeIds: [] as any, // 临时删除的编号
-
       removeVisiable1: false,
       preSaveVisiable: false
     });
@@ -94,13 +94,14 @@ export default defineComponent({
       forms.loadingStatus = true;
       try {
         // 判断是否有选择对应的课件
-        console.log(props.lessonPreTraining, 'props.lessonPreTraining');
+        // console.log(props.lessonPreTraining, 'props.lessonPreTraining');
         if (!props.lessonPreTraining?.id) return (forms.loadingStatus = false);
         const { data } = await lessonPreTrainingV2Detail({
           id: props.lessonPreTraining?.id
         });
         const tempRows = data.lessonPreTrainingDetails || [];
         const temp: any = [];
+        forms.title = data.title;
         tempRows.forEach((row: any) => {
           let tList: string[] = [];
           const configJson = row.trainingConfigJson;
@@ -140,13 +141,6 @@ export default defineComponent({
       forms.loadingStatus = false;
     };
 
-    // // 声部变化时
-    // watch(
-    //   () => prepareStore.getSubjectId,
-    //   () => {
-    //     getList();
-    //   }
-    // );
     // 监听选择的key 左侧选择了其它的课
     watch(
       () => prepareStore.getSelectKey,
@@ -159,7 +153,6 @@ export default defineComponent({
     );
     // 删除
     const onDelete = (item: any) => {
-      //
       forms.removeIds.push(item.id);
       const index = forms.trainList.findIndex((c: any) => c.id === item.id);
       forms.trainList.splice(index, 1);
@@ -203,9 +196,10 @@ export default defineComponent({
           id: props.lessonPreTraining?.id,
           coursewareKnowledgeDetailId:
             props.coursewareKnowledgeDetailId || prepareStore.getSelectKey,
-          lessonPreTrainingDetails
+          lessonPreTrainingDetails,
+          chapterLessonCoursewareId: props.lessonPreTraining?.chapterId
         });
-        message.success('保存预设成功');
+        message.success('保存成功');
         prepareStore.setIsEditTrain(false);
         forms.removeIds = [];
         // getList();
@@ -261,7 +255,7 @@ export default defineComponent({
                 // forms.drag = false;
                 prepareStore.setIsEditTrain(false);
                 forms.removeIds = [];
-                prepareStore.setTrainList([]);
+                // prepareStore.setTrainList([]);
                 // getList();
                 emit('change', { status: false });
               }}>
@@ -273,6 +267,10 @@ export default defineComponent({
                 type="primary"
                 disabled={forms.trainList.length <= 0}
                 onClick={() => {
+                  if (!forms.title) {
+                    message.error('请输入标题');
+                    return;
+                  }
                   let count = 0;
                   forms.trainList.forEach((item: any) => {
                     if (!item.removeFlag) {
@@ -292,11 +290,26 @@ export default defineComponent({
                 type="default"
                 disabled={forms.trainList.length <= 0}
                 onClick={() => {
+                  if (!forms.title) {
+                    message.error('请输入标题');
+                    return;
+                  }
+                  let count = 0;
+                  forms.trainList.forEach((item: any) => {
+                    if (!item.removeFlag) {
+                      count++;
+                    }
+                  });
+                  if (count <= 0) {
+                    message.error('作业内容不能为空');
+                    return;
+                  }
+
                   forms.preSaveVisiable = true;
                 }}
                 // loading={forms.preBtnLoading}
               >
-                保存预设
+                保存
               </NButton>
             )}
           </NSpace>
@@ -415,34 +428,35 @@ export default defineComponent({
           </NSpin>
         </NScrollbar>
 
-        {props.cardType !== 'homeworkRecord' && (
-          <div
-            class={[styles.btnGroup, styles.btnGroupClass]}
-            style={{ justifyContent: 'flex-end' }}>
-            <NSpace justify="end">
-              <NButton
-                type="primary"
-                // {...{ id: 'train-0' }}
-                // disabled={forms.drag}
-                disabled={forms.trainList.length <= 0}
-                onClick={() => {
-                  let count = 0;
-                  forms.trainList.forEach((item: any) => {
-                    if (!item.removeFlag) {
-                      count++;
+        {props.cardType !== 'homeworkRecord' &&
+          props.cardType !== 'prepare' && (
+            <div
+              class={[styles.btnGroup, styles.btnGroupClass]}
+              style={{ justifyContent: 'flex-end' }}>
+              <NSpace justify="end">
+                <NButton
+                  type="primary"
+                  // {...{ id: 'train-0' }}
+                  // disabled={forms.drag}
+                  disabled={forms.trainList.length <= 0}
+                  onClick={() => {
+                    let count = 0;
+                    forms.trainList.forEach((item: any) => {
+                      if (!item.removeFlag) {
+                        count++;
+                      }
+                    });
+                    if (count <= 0) {
+                      message.error('作业内容不能为空');
+                      return;
                     }
-                  });
-                  if (count <= 0) {
-                    message.error('作业内容不能为空');
-                    return;
-                  }
-                  forms.assignHomeworkStatus = true;
-                }}>
-                立即布置
-              </NButton>
-            </NSpace>
-          </div>
-        )}
+                    forms.assignHomeworkStatus = true;
+                  }}>
+                  立即布置
+                </NButton>
+              </NSpace>
+            </div>
+          )}
 
         {/* 编辑 */}
         <NModal
@@ -492,6 +506,7 @@ export default defineComponent({
             classGroupId={props.classGroupId}
             homeworkType={props.courseScheduleId ? 'CLASSWORK' : 'HOMEWORK'}
             courseScheduleId={props.courseScheduleId}
+            chapterLessonCoursewareId={props.lessonPreTraining.chapterId}
             item={{
               title: forms.title,
               lessonPreTrainingDetails: forms.trainList
@@ -546,10 +561,10 @@ export default defineComponent({
           v-model:show={forms.preSaveVisiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable1]}
-          title={'保存预设'}>
+          title={'保存'}>
           <TheMessageDialog
             content="是否保存当前页面编辑内容?"
-            cancelButtonText="不保存"
+            cancelButtonText="取消"
             confirmButtonText="保存"
             onClose={() => (forms.preSaveVisiable = false)}
             onConfirm={() => onPreSave()}

+ 5 - 2
src/views/prepare-lessons/components/resource-main/components/select-music/index.tsx

@@ -55,7 +55,7 @@ export default defineComponent({
     },
     /** 类型 */
     cardType: {
-      type: String as PropType<'' | 'homerowk-record'>,
+      type: String as PropType<'' | 'homerowk-record' | 'prepare'>,
       default: ''
     }
   },
@@ -85,7 +85,10 @@ export default defineComponent({
     });
     const getList = async () => {
       try {
-        if (!prepareStore.getSubjectId && props.cardType !== 'homerowk-record')
+        if (
+          !prepareStore.getSubjectId &&
+          !['homerowk-record', 'prepare'].includes(props.cardType)
+        )
           return;
         if (state.pagination.page === 1) {
           state.loading = true;

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

@@ -22,7 +22,7 @@ export default defineComponent({
   props: {
     /** 类型 */
     cardType: {
-      type: String as PropType<'' | 'homerowk-record'>,
+      type: String as PropType<'' | 'homerowk-record' | 'prepare'>,
       default: ''
     }
   },
@@ -92,7 +92,7 @@ export default defineComponent({
             : ''
         ]}>
         {prepareStore.getTabType === 'courseware' &&
-        props.cardType !== 'homerowk-record' ? (
+        !['homerowk-record', 'prepare'].includes(props.cardType) ? (
           <NTabs
             ref={tabRef}
             animated

BIN
src/views/prepare-lessons/images/active-arrow.png


BIN
src/views/prepare-lessons/images/addSource/icon8.png


BIN
src/views/prepare-lessons/images/default-arrow.png


BIN
src/views/prepare-lessons/images/icon-add.png


BIN
src/views/prepare-lessons/images/icon-look.png


BIN
src/views/prepare-lessons/images/icon-menu-active.png


BIN
src/views/prepare-lessons/images/icon-menu-default.png


BIN
src/views/prepare-lessons/images/icon-no-work.png


BIN
src/views/prepare-lessons/images/protocol/check-active.png


BIN
src/views/prepare-lessons/images/protocol/check-default.png


BIN
src/views/prepare-lessons/images/protocol/closeAble.png


BIN
src/views/prepare-lessons/images/protocol/dingPng.png


BIN
src/views/prepare-lessons/images/protocol/moveTop.png


+ 174 - 0
src/views/prepare-lessons/model/add-courseware-protocol/index.module.less

@@ -0,0 +1,174 @@
+.protocolModal {
+  position: relative;
+
+  .dingPng {
+    width: 162px;
+    height: 98px;
+    position: absolute;
+    left: 50%;
+    margin-left: -81px;
+    top: -49px;
+    z-index: 100;
+  }
+}
+
+.downMove {
+  width: 413px;
+  // height: 309px;
+  background: #FFFFFF;
+  border-radius: 16px;
+  position: relative;
+  padding: 0 30px 35px;
+  overflow: hidden;
+
+  .downMoveBg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 413px;
+    height: 101px;
+  }
+
+
+
+  .closeAble {
+    cursor: pointer;
+    width: 25px;
+    height: 25px;
+    position: absolute;
+    top: 18px;
+    right: 20px;
+  }
+
+  h2 {
+    font-size: 24px;
+    font-family: PingFangSC-Semibold, PingFang SC;
+    font-weight: 600;
+    color: #131415;
+    line-height: 33px;
+    text-align: center;
+    padding-top: 64px;
+    padding-bottom: 15px;
+  }
+
+  p {
+    font-size: 18px;
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #777777;
+    line-height: 30px;
+  }
+
+  .submitAppBtn,
+  .cancelBtn {
+    min-width: 130px;
+    height: 42px;
+    line-height: 42px;
+    border-radius: 24px;
+    border: none;
+  }
+
+  .submitAppBtn {
+    background: linear-gradient(305deg, #40C8FF 0%, #3192FF 100%)
+  }
+}
+
+.protocolGroup {
+  padding-top: 17px;
+  display: flex;
+  align-items: flex-start;
+  font-size: 13px;
+  color: #333333;
+  line-height: 20px;
+  cursor: pointer;
+
+  .checkbox {
+    margin-top: 6px;
+    margin-right: 5px;
+    width: 14Px;
+    height: 14Px;
+    display: inline-block;
+    background: url('../../images/protocol/check-default.png') no-repeat center;
+    background-size: contain;
+  }
+
+  .checkboxActive {
+    background: url('../../images/protocol/check-active.png') no-repeat center;
+    background-size: contain;
+  }
+
+  span {
+    color: #1677FF;
+  }
+}
+
+.protocolSection {
+  width: 610px;
+  background: #FFFFFF;
+  border-radius: 16px;
+  position: relative;
+  padding: 0 30px 35px;
+  overflow: hidden;
+
+  .downMoveBg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 101px;
+  }
+
+
+
+  .closeAble {
+    cursor: pointer;
+    width: 25px;
+    height: 25px;
+    position: absolute;
+    top: 18px;
+    right: 20px;
+  }
+
+  h2 {
+    font-size: 24px;
+    font-family: PingFangSC-Semibold, PingFang SC;
+    font-weight: 600;
+    color: #131415;
+    line-height: 33px;
+    text-align: center;
+    padding-top: 64px;
+    padding-bottom: 15px;
+  }
+
+  .protocolContent {
+    font-size: max(18px, 14Px);
+    line-height: 30px;
+    color: #777;
+    text-align: justify;
+
+    p {
+      font-weight: 600;
+      color: #333333;
+      padding-top: 20px;
+    }
+
+    .child {
+      padding-left: 20px;
+    }
+  }
+
+
+
+  .submitAppBtn,
+  .cancelBtn {
+    min-width: 150px;
+    height: 45px;
+    line-height: 45px;
+    border-radius: 24px;
+    border: none;
+  }
+
+  .submitAppBtn {
+    background: linear-gradient(305deg, #40C8FF 0%, #3192FF 100%)
+  }
+}

+ 230 - 0
src/views/prepare-lessons/model/add-courseware-protocol/index.tsx

@@ -0,0 +1,230 @@
+import { defineComponent, reactive } from 'vue';
+import styles from './index.module.less';
+import dingPng from '../../images/protocol/dingPng.png';
+import moveTop from '../../images/protocol/moveTop.png';
+import closeAble from '../../images/protocol/closeAble.png';
+import { NSpace, NButton, NScrollbar } from 'naive-ui';
+
+export default defineComponent({
+  name: 'add-courseware-protocol',
+  emits: ['close', 'confirm'],
+  setup(props, { emit }) {
+    const forms = reactive({
+      status: false,
+      protocolShow: false // 是否显示协议
+    });
+    return () => (
+      <div class={styles.protocolModal}>
+        <img src={dingPng} class={styles.dingPng} alt="" />
+        {!forms.protocolShow ? (
+          <div class={styles.downMove}>
+            <img src={moveTop} class={styles.downMoveBg} alt="" />
+            <img
+              src={closeAble}
+              class={styles.closeAble}
+              onClick={() => {
+                // showModalMask.value = false;
+                // checkAuthShow();
+                emit('close');
+              }}
+              alt=""
+            />
+            <h2>课件公开须知</h2>
+            <p>
+              公开课件后,您课件中的资源及作业都可被其他教师查看、使用,具体信息请查看服务协议
+            </p>
+
+            <div
+              class={styles.protocolGroup}
+              onClick={() => {
+                forms.status = !forms.status;
+              }}>
+              <i
+                class={[
+                  styles.checkbox,
+                  forms.status && styles.checkboxActive
+                ]}></i>
+              <p>
+                我已阅读并同意
+                <span
+                  onClick={(e: any) => {
+                    e.stopPropagation();
+                    forms.protocolShow = true;
+                  }}>
+                  《音乐数字课堂课件与作业公开服务协议》
+                </span>
+              </p>
+            </div>
+
+            <NSpace style={{ padding: '25px 0 0 0' }} justify="center">
+              <NButton class={styles.cancelBtn} round type="default">
+                取消
+              </NButton>
+              <NButton
+                class={styles.submitAppBtn}
+                bordered={false}
+                round
+                type="primary"
+                disabled={!forms.status}
+                onClick={() => {
+                  emit('confirm');
+                }}>
+                确定
+              </NButton>
+            </NSpace>
+          </div>
+        ) : (
+          <div class={styles.protocolSection}>
+            <img src={moveTop} class={styles.downMoveBg} alt="" />
+            <img
+              src={closeAble}
+              class={styles.closeAble}
+              onClick={() => {
+                // showModalMask.value = false;
+                // checkAuthShow();
+                emit('close');
+              }}
+              alt=""
+            />
+            <h2>《音乐数字课堂课件与作业公开服务协议》</h2>
+
+            <NScrollbar style={{ maxHeight: '45vh' }}>
+              <div class={styles.protocolContent}>
+                <p style={{ paddingTop: '0' }}>欢迎使用音乐数字课堂!</p>
+                <p>重要须知</p>
+                武汉酷乐秀网络科技有限公司(以下统称“平台方”,或称“我们”),在此特别提醒,为使用音乐数字课堂(包括手机端、网页端等,以下称“软件”或“平台”)与相关信息化教育产品、内容及服务,您务必仔细阅读、充分理解并遵守《音乐数字课堂课件与作业公开服务协议》(以下简称“本协议”)中各项条款,包括免除或者限制责任的条款,并选择接受或不接受。本协议所述服务条款构成您(个人或者单位、组织、团体等)使用平台方提供的相关服务(以下简称“该服务”)的先决条件。若您不接受本协议中所述服务条款或其后对协议条款的修改,您应不使用或主动取消平台方提供的相关服务。
+                您自愿使用平台方提供的网络存储空间进行培训课件与作业及相关智力成果的上传及传播服务。如果您未满18周岁,请在法定监护人的陪同下阅读本协议。
+                <br />
+                本协议是您(或称“用户”,指注册、登录、使用、浏览本服务的个人或组织)与平台方之间关于音乐数字课堂与相关信息化教育产品、内容及服务所订立的协议。
+                <br />
+                <p>1 权利声明</p>
+                音乐数字课堂由武汉酷乐秀网络科技有限公司(以下简称“酷乐秀”)独立开发,一切知识产权以及音乐数字课堂自有的所有信息内容包括但不限于:文字表述及其组合、图标、图饰、图像、图表、色彩、界面设计、版面框架、有关数据、附加程序、印刷材料或电子文档等均为酷乐秀所有,受著作权法和国际著作权条约以及其他知识产权法律法规的保护。
+                <br />
+                音乐数字课堂由武汉酷乐秀网络科技有限公司(以下简称“音乐数字课堂”)提供运营管理服务。
+                <br />
+                <p>2 服务说明 </p>
+                2.1
+                您可以通过音乐数字课堂主页【网址:https://kt.colexiu.com/classroom-admin/#/login】、手机客户端(及其他由音乐数字课堂运营的任何网站)、音乐数字课堂公众号,访问音乐数字课堂并使用相关服务。
+                <br />
+                2.2
+                您知悉并同意,在注册成为音乐数字课堂用户或使用本服务时,需提供一些必要信息,包括但不限于手机号码、头像等。若国家法律法规或政策有特殊规定的,平台方会根据相关特殊规定要求您提供相关信息,如真实的身份信息等,若您提供的信息不完整,您将无法使用本服务或本服务中的相关功能,由此为您带来不便或损失的,平台方完全免责。如个人信息发生变更,您有权随时与平台方联系并要求修改相关个人信息。在您向平台方提出前述删除请求前,平台方可在本协议约定范围内合理利用相关个人信息。
+                <br />
+                您应承诺合法使用平台方提供的服务,您使用该服务的行为应符合当地的法律法规及政策的要求,否则由此带来的责任由您自行承担。
+                <br />
+                <p>3 权利授予 </p>
+                3.1
+                您理解并同意,您将课件与作业及相关智力成果上传至音乐数字课堂,即视为您授予了平台方无期限限制的、全球范围内的、不可撤销的信息网络传播权和其他形式的使用权。为了更好地对课件与作业进行宣传和推广,平台方可以将上述权利转让或者转授予给其关联公司或者合作伙伴,而无须征得您的同意。您如果不同意上述条款,请不要将作品上传至音乐数字课堂。
+                <br />
+                3.2平台方仅对您提交的资料和信息进行审核,并不对您的主体资质和上传的课件与作业内容的真实性、正确性、合法性承担任何责任或提供任何担保。
+                <br />
+                3.3
+                如果任何第三方侵犯音乐数字课堂用户相关的权利,用户同意授权平台方或其指定的代理人代表平台方自身或用户对该第三方提出警告、投诉、发起行政执法、诉讼、进行上诉,或谈判和解,并且用户同意在平台方认为必要的情况下参与共同维权。
+                <br />
+                <p>4 权利限制 </p>
+                4.1
+                平台方通过音乐数字课堂所提供的服务,均限于在音乐数字课堂平台内使用,禁止任何以恶意破解等非法手段将音乐数字课堂内的服务与音乐数字课堂平台分离的行为,否则,由此引起的一切后果由行为人负责,平台方将保留依法追究行为人法律责任的权利。
+                <br />
+                4.2未经平台方书面同意,您不得将已上传至音乐数字课堂平台上的培训课件与作业的任何资料文件直接、间接或经修改后提供给任何第三方。
+                <br />
+                4.3
+                保留权利:本协议未明示许可的其他一切权利仍归平台方所有,您使用其他权利时必须获得平台方的书面同意。
+                <br />
+                4.4
+                除本协议有明确规定外,本协议并未对平台方其他服务规定相关的服务条款,对于这些服务可能有单独的服务条款加以规范,请您在使用有关服务时另行了解与确认。如您使用该服务,视为对相关服务条款的接受。
+                <br />
+                <p>5 用户使用须知</p>
+                5.1您承诺您具有履行本协议义务的权利能力及资格,因您自身职务或职业要求导致本协议无法履行或合同目的无法实现的,您应当及时通知平台方,并共同协商解决办法,以保证将事件影响性降到最低。
+                <br />
+                5.2您保证,您上传的课件与作业所涉的创意、脚本、资料、课件与作业等全部文件资料不违反国家相关法律法规、不侵犯第三方权益,且平台方按照约定行使授权内容的相关权利不会侵犯任何第三方的合法权益,也无须另行取得任何第三方的同意,更无须对任何第三方承担责任。如您违反前述保证,平台方有权采取包括但不限于删除相关课件与作业内容、中止或终止向您提供服务等措施。如您上传的课件与作业侵犯了第三方的合法权利,第三方向平台方提出举报,平台方有权删除相关的内容。当权利人发现音乐数字课堂上相关课件与作业侵犯其合法权益时,权利人可向平台方举报,平台方将根据中国法律法规和规范性文件的规定采取移除相关内容等合理措施。
+                <br />
+                5.3您保证上传的课件与作业中不含以下内容:
+                <br />
+                <div class={styles.child}>
+                  1.反对宪法所确定的基本原则;
+                  <br />
+                  2.危害国家安全,泄漏国家秘密,颠覆国家政权,破坏国家统一;
+                  <br />
+                  3.损害国家荣誉和利益; <br />
+                  4.煽动民族仇恨、民族歧视,破坏民族团结;
+                  <br />
+                  5.破坏国家宗教政策,宣扬邪教和封建迷信;
+                  <br />
+                  6.散布谣言,扰乱社会秩序,破坏社会稳定;
+                  <br />
+                  7.散布淫秽、色情、赌博、暴力、凶杀、恐怖或教唆犯罪;
+                  <br />
+                  8.侮辱或者诽谤他人,侵害他人合法权益;
+                  <br />
+                  9.含有法律、行政法规严令禁止的其他内容。
+                  <br />
+                </div>
+                5.4上述承诺与保证不受本协议期限的限制,您违反上述任一的保证与承诺的,平台方有权即时解除本协议并要求您承担由此产生的全部责任。
+                <br />
+                <p>6 隐私政策 </p>
+                6.1 目的
+                为改善平台方服务,提升用户体验,在您使用音乐数字课堂时,平台方可能对您使用音乐数字课堂的使用信息进行统计与分析,例如:平台方会根据您上传并通过审核的某单元的课件与作业数量显示您在该单元全国范围内的排名,同时显示您的头像、昵称信息及通过审核的课件与作业数量。平台方将采取必要的保护措施,以保护您的相关信息。您可以选择不提供或只提供部分信息,但平台方可能将无法为您提供完整的产品功能或服务。
+                <p>7 免责与责任限制</p>
+                7.1
+                音乐数字课堂不含有任何旨在破坏您计算机数据和获取您隐私信息的恶意代码。音乐数字课堂经过详细的测试,但不能保证与所有的软硬件系统完全兼容,不能保证音乐数字课堂完全没有错误。如果出现不兼容及软件错误的情况,您可拨打技术支持电话将情况报告平台方,以获得技术支持。如果无法解决兼容性问题,您可以删除音乐数字课堂。由此给您造成损失的,您同意平台方免责。
+                <br />
+                7.2
+                在适用法律允许的最大范围内,对因使用或不能使用音乐数字课堂所产生的损害及风险,包括但不限于直接或间接的个人损害、商业赢利的丧失、贸易中断、商业信息的丢失或任何其它经济损失,平台方不承担任何责任。
+                <br />
+                7.3
+                对于因电信系统或互联网网络故障、计算机故障或病毒、信息损坏或丢失、计算机系统问题或其它任何不可抗力原因而产生损失,平台方不承担任何责任。
+                <br />
+                7.4您知悉并同意,为实现部分软件功能,平台方有权将您在使用该服务过程当中产生的部分或全部数据信息,上传、保存至第三方云服务存储器,并根据您发出的功能指令需求,自行调取相关数据信息。因第三方云服务发生中断等功能障碍事故导致平台方无法正常提供服务的,平台方会尽其商业上的合理努力负责协调解决,但由此给您造成损失的,您同意平台方免责。
+                <br />
+                7.5
+                您违反本协议规定,对平台方造成损害的。平台方有权采取包括但不限于中断使用许可、停止提供服务、限制使用、法律追究等措施。
+                <br />
+                <p>8 法律及争议解决</p>
+                8.1 本协议适用中华人民共和国法律。
+                <br /> 8.2
+                因本协议引起的或与本协议有关的任何争议,各方应友好协商解决;协商不成的,任何一方均可将有关争议提交至平台方所在地有管辖权的人民法院管辖。
+                <br />
+                <p>9 其他条款</p>
+                9.1
+                如果本协议中的任何条款无论因何种原因完全或部分无效或不具有执行力,或违反任何适用的法律,则该条款被视为删除,但本协议的其余条款仍有效并且具有约束力。
+                <br />
+                9.2
+                如果本协议中的任何条款与音乐数字课堂中某具体软件的协议条款相矛盾的,以该具体软件的协议条款为准。
+                <br />
+                平台方有权随时根据有关法律、法规的变化以及公司经营状况和经营策略的调整等修改本协议。修改后的协议会在平台方指定网站上公布,并随附于新版音乐数字课堂。当发生有关争议时,以最新的协议文本为准。如果不同意改动的内容,您可以自行删除音乐数字课堂。如果您继续使用音乐数字课堂,则视为您接受本协议的变动。
+                <br />
+                至此,用户已经详细阅读并已理解本协议,并同意严格遵守各条款和条件。如果用户对本《协议》有任何疑问或者希望获得与本软件相关的任何信息,请按下列地址和方式与酷乐秀联系,酷乐秀将在15日内答复用户。
+                <br />
+                技术支持方:武汉酷乐秀网络科技有限公司
+                <br />
+                运营方:武汉酷乐秀网络科技有限公司
+              </div>
+            </NScrollbar>
+
+            <NSpace style={{ padding: '25px 0 0 0' }} justify="center">
+              <NButton
+                class={styles.cancelBtn}
+                round
+                type="default"
+                onClick={() => {
+                  forms.protocolShow = false;
+                }}>
+                取消授权
+              </NButton>
+              <NButton
+                class={styles.submitAppBtn}
+                bordered={false}
+                round
+                type="primary"
+                onClick={() => {
+                  forms.protocolShow = false;
+                  forms.status = true;
+                }}>
+                同意授权
+              </NButton>
+            </NSpace>
+          </div>
+        )}
+      </div>
+    );
+  }
+});

+ 20 - 0
src/views/prepare-lessons/model/add-other-source/index.module.less

@@ -70,4 +70,24 @@
 
 .subjectSyncModal {
   width: 1070px;
+}
+
+.attendClassModal {
+  width: 1100px;
+
+  :global {
+    .n-select-menu {
+      --n-height: calc(var(--n-option-height) * 5.6) !important;
+    }
+  }
+}
+
+.attendClassSaveModal {
+  width: 600px;
+
+  :global {
+    .n-select-menu {
+      --n-height: calc(var(--n-option-height) * 5.6) !important;
+    }
+  }
 }

+ 104 - 1
src/views/prepare-lessons/model/add-other-source/index.tsx

@@ -8,6 +8,7 @@ import icon4 from '../../images/addSource/icon4.png';
 import icon5 from '../../images/addSource/icon5.png';
 import icon6 from '../../images/addSource/icon6.png';
 import icon7 from '../../images/addSource/icon7.png';
+import icon8 from '../../images/addSource/icon8.png';
 import { useRouter } from 'vue-router';
 import SourceRhythm from '../source-rhythm';
 import SourceInstrument from '../source-instrument';
@@ -17,6 +18,10 @@ import SourceMusic from '../source-music';
 import { eventGlobal } from '/src/utils';
 import SubjectSync from '../subject-sync';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
+import UploadModal, {
+  formatUrlType
+} from '/src/views/natural-resources/components/my-resources/upload-modal';
+import SaveModal from '/src/views/natural-resources/components/my-resources/save-modal';
 
 export default defineComponent({
   name: 'add-other-source',
@@ -26,6 +31,11 @@ export default defineComponent({
     const router = useRouter();
     const sourceList = ref([
       {
+        image: icon8,
+        name: '上传资源',
+        index: 7
+      },
+      {
         image: icon1,
         name: '听音练习',
         index: 0
@@ -55,6 +65,7 @@ export default defineComponent({
         name: '乐理知识',
         index: 5
       }
+
       // {
       //   image: icon7,
       //   name: '制作曲谱',
@@ -67,8 +78,16 @@ export default defineComponent({
       theoryStatus: false, //
       musicStatus: false, //
       instrumentStatus: false, //
-      musicianStatus: false //
+      musicianStatus: false, //
+
+      uploadStatus: false,
+      saveStatus: false,
+      editStatus: false, // 是否编辑
+      editList: [] as any, // TOD
+      editIds: [] as any, // 编辑的
+      editOverIds: [] as any // 确认修改的数据
     });
+
     // LISTEN:听音,RHYTHM:节奏,THEORY:乐理知识,MUSIC_WIKI:曲目 INSTRUMENT:乐器 MUSICIAN:音乐家)
 
     const onDetail = (item: any) => {
@@ -98,6 +117,14 @@ export default defineComponent({
             router.push('/notation');
           });
           break;
+        case 7:
+          // eventGlobal.emit('pageBeforeLeave', () => {
+          //   // 直接跳转到制谱页面 (临时存储数据)
+          //   sessionStorage.setItem('notation-open-create', '1');
+          //   router.push('/notation');
+          // });
+          state.saveStatus = true;
+          break;
         default:
           break;
       }
@@ -304,6 +331,82 @@ export default defineComponent({
             }}
           />
         </NModal>
+
+        <NModal
+          v-model:show={state.uploadStatus}
+          preset="card"
+          showIcon={false}
+          class={['modalTitle background', styles.attendClassModal]}
+          title={state.editStatus ? '修改资源' : '上传资源'}
+          blockScroll={false}>
+          <UploadModal
+            editStatus={state.editStatus}
+            onClose={() => {
+              state.uploadStatus = false;
+            }}
+            onConfirm={(item: any) => {
+              state.editIds = [];
+              state.editList = [];
+              state.editOverIds = [];
+              state.saveStatus = false;
+
+              try {
+                state.musicStatus = false;
+                const value = item || [];
+                const temp: any[] = [];
+                value.forEach((item: any) => {
+                  temp.push({
+                    materialId: item.id,
+                    coverImg: item.coverImg,
+                    dataJson: null,
+                    title: item.name,
+                    isCollect: false,
+                    isSelected: false,
+                    content: item.content,
+                    type: item.type
+                  });
+                });
+                // state.listenStatus = false;
+                emit('comfirm', temp);
+                emit('close');
+              } catch {
+                //
+              }
+            }}
+            list={state.editList}
+          />
+        </NModal>
+
+        <NModal
+          v-model:show={state.saveStatus}
+          preset="card"
+          showIcon={false}
+          class={['modalTitle background', styles.attendClassSaveModal]}
+          title={'上传资源'}
+          blockScroll={false}>
+          <SaveModal
+            onClose={() => (state.saveStatus = false)}
+            onConfrim={(val: any) => {
+              const list = val || [];
+              const temp: any = [];
+              list.forEach((item: any) => {
+                temp.push({
+                  subjectIds: null,
+                  openFlag: false,
+                  coverImg: item.coverImg,
+                  title: item.name || '',
+                  type: formatUrlType(item.content),
+                  enableFlag: 1,
+                  content: item.content,
+                  id: null
+                });
+              });
+              state.editList = temp;
+              state.uploadStatus = true;
+              state.editStatus = false;
+            }}
+          />
+        </NModal>
       </>
     );
   }

+ 273 - 52
src/views/prepare-lessons/model/courseware-type/index.module.less

@@ -1,8 +1,15 @@
 .coursewareType {
   position: relative;
-  background: #F5F6FA;
   border-radius: 13px;
-  padding: 10px 10px 16px;
+  padding: 10px 10px 0;
+  transition: all .2s ease;
+
+  &:hover {
+    background: #FFFFFF;
+    box-shadow: 0 2px 15px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 15px;
+    transition: all .2s ease;
+  }
 
   &.coursewareTypeHover:hover {
     .addBtn {
@@ -53,13 +60,77 @@
     border-radius: 10px;
     overflow: hidden;
     background-color: #fff;
+    border: 1px solid #EFF0F2;
+    transition: all .2s ease;
 
-    .status {
+    &:hover {
+      :global {
+        .n-image {
+          transform: scale(1.05);
+          transition: all 0.2s ease;
+          // &:hover {
+          // }
+        }
+      }
+
+      .defaultLook {
+        opacity: 1;
+        visibility: visible;
+      }
+    }
+
+    .defaultLook {
       position: absolute;
+      opacity: 0;
+      visibility: hidden;
+      inset: 0;
+      background-color: rgba(0, 0, 0, 0.3);
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      cursor: pointer;
+      transition: all 0.2s ease;
+
+      &::before {
+        display: inline-block;
+        content: '';
+        width: 25px;
+        height: 25px;
+        background: url('../../images/icon-look.png') no-repeat center;
+        background-size: contain;
+      }
+    }
+
+    // 布置作业
+    &.isShowAdd:hover {
+      .defaultLook {
+        display: none;
+      }
+
+      .function {
+        opacity: 1;
+        visibility: visible;
+        transition: all .2s ease;
+      }
+    }
+
+    .iconNoWork {
+      position: absolute;
+      right: 0;
       top: 0;
-      left: 0;
+      display: inline-block;
+      width: 81px;
+      height: 22px;
+      background: url('../../images/icon-no-work.png') no-repeat center;
+      background-size: contain;
+    }
+
+    .status {
+      position: absolute;
+      top: 7px;
+      left: 7px;
       background: linear-gradient(226deg, #13BFFF 0%, #1183FF 100%);
-      border-radius: 4px 0px 10px 0px;
+      border-radius: 4px;
       font-weight: 600;
       font-size: max(12px, 11Px);
       color: #FFFFFF;
@@ -71,19 +142,7 @@
       .n-image {
         width: 100%;
         height: 100%;
-        transition: all .2s ease;
-
-        &:hover {
-          transform: scale(1.05);
-          transition: all 0.2s ease;
-
-          // &::after {
-          //   content: '';
-          //   position: absolute;
-          //   inset: 0;
-          //   background-color: rgba(0, 0, 0, 0.3);
-          // }
-        }
+        transition: all 0.2s ease;
       }
     }
 
@@ -197,74 +256,236 @@
   }
 }
 
+.operationBottom {
+  padding-bottom: 16px;
+
+  &:hover .footer {
+    opacity: 1;
+    visibility: visible;
+    transition: all .2s ease;
+  }
+}
+
+
 .footer {
+  opacity: 0;
+  visibility: hidden;
   display: flex;
   align-items: center;
   padding-top: 10px;
+  position: absolute;
+  left: 10px;
+  right: 10px;
+  bottom: 20px;
+  background-color: #fff;
+  transition: all .2s ease;
+
+  :global {
+    .n-space {
+      gap: 8px 10px !important;
+    }
+  }
 
   .actionClass {
     cursor: pointer;
     flex: 1;
     height: 30Px !important;
     line-height: 30Px;
-    background: #FFFFFF;
+    background: #198CFE;
     border-radius: 7px;
     text-align: center;
-    margin-right: 12Px;
+    margin-right: 10px;
     font-size: max(14px, 13Px);
-    color: #484F59;
+    color: #fff;
     font-weight: 600;
+    transition: all .2s ease;
 
     &:hover {
-      background-color: #198CFE;
-      color: #fff;
+      background-color: rgba(25, 140, 254, 0.8);
+      transition: all .2s ease;
+      // color: #fff;
+    }
+
+    &.actionWork {
+      margin-right: 0;
+      background: rgba(245, 246, 250, 1);
+      color: #484F59;
+      padding: 0 13px;
+
+      &:hover {
+        background-color: rgba(245, 246, 250, 1);
+        color: rgba(25, 140, 254, 1);
+      }
     }
   }
 
-  .optons {
-    width: 20Px;
-    height: 20Px;
-    background: #FFFFFF;
+  .menu {
+    height: 30Px !important;
+    line-height: 30Px;
+    width: 30Px !important;
+    background: rgba(245, 246, 250, 1);
     border-radius: 7px;
-    padding: 5px;
-    box-sizing: content-box;
-    cursor: pointer;
     display: flex;
     align-items: center;
     justify-content: center;
+    cursor: pointer;
 
-    img {
-      width: 20Px;
-      height: 20Px;
+    &::after {
+      content: '';
+      display: inline-block;
+      width: 13Px;
+      height: 10Px;
+      background: url('../../images/icon-menu-default.png') no-repeat center;
+      background-size: contain;
     }
 
-    .iconEdit,
-    .iconDelete {
-      display: inline-block;
-      width: 24px;
-      height: 24px;
+    &:hover {
+      &::after {
+        background: url('../../images/icon-menu-active.png') no-repeat center;
+        background-size: contain;
+      }
     }
+  }
 
-    .iconEdit {
-      background: url('../../images/icon-courseware-edit.png') no-repeat center;
-      background-size: contain;
+
+  // .optons {
+  //   width: 20Px;
+  //   height: 20Px;
+  //   background: #FFFFFF;
+  //   border-radius: 7px;
+  //   padding: 5px;
+  //   box-sizing: content-box;
+  //   cursor: pointer;
+  //   display: flex;
+  //   align-items: center;
+  //   justify-content: center;
+
+  //   img {
+  //     width: 20Px;
+  //     height: 20Px;
+  //   }
+
+  //   .iconEdit,
+  //   .iconDelete {
+  //     display: inline-block;
+  //     width: 24px;
+  //     height: 24px;
+  //   }
+
+  //   .iconEdit {
+  //     background: url('../../images/icon-courseware-edit.png') no-repeat center;
+  //     background-size: contain;
+  //   }
+
+  //   .iconDelete {
+  //     background: url('../../images/icon-courseware-delete.png') no-repeat center;
+  //     background-size: contain;
+  //   }
+
+  //   // &:hover {
+  //   //   .iconEdit {
+  //   //     background: url('../../images/icon-courseware-edit-active.png') no-repeat center;
+  //   //     background-size: contain;
+  //   //   }
+
+  //   //   .iconDelete {
+  //   //     background: url('../../images/icon-courseware-delete-active.png') no-repeat center;
+  //   //     background-size: contain;
+  //   //   }
+  //   // }
+  // }
+  .iconEdit,
+  .iconDelete {
+    display: inline-block;
+    width: 24px;
+    height: 24px;
+  }
+
+  .iconEdit {
+    background: url('../../images/icon-courseware-edit.png') no-repeat center;
+    background-size: contain;
+  }
+
+  .iconDelete {
+    background: url('../../images/icon-courseware-delete.png') no-repeat center;
+    background-size: contain;
+  }
+
+  .popoverItem {
+    display: flex;
+    align-items: center;
+    font-weight: 600;
+    font-size: max(13px, 12Px);
+    color: #484F59;
+    line-height: 18px;
+    border-radius: 8px;
+    padding: 8px 13px;
+    cursor: pointer;
+
+    i {
+      margin-right: 13px;
+    }
+
+    &:hover {
+      background: #F5F6FA;
+    }
+  }
+
+  :global {
+    .n-popover {
+      box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.1);
+      border-radius: 10px;
+      padding: 9px 7px !important;
     }
+  }
+}
+
+.function {
+  position: absolute;
+  inset: 0;
+  background: rgba(0, 0, 0, .3);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  opacity: 0;
+  visibility: hidden;
+  transition: opacity 0.2s ease;
+
+  .iconLook,
+  .iconAdd {
+    cursor: pointer;
+    width: 38px;
+    height: 38px;
+    border-radius: 50%;
+    background: rgba(0, 0, 0, .4);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin: 0 16px;
+
+  }
 
-    .iconDelete {
-      background: url('../../images/icon-courseware-delete.png') no-repeat center;
+  .iconLook {
+    &::before {
+      display: inline-block;
+      content: '';
+      width: 25px;
+      height: 25px;
+      background: url('../../images/icon-look.png') no-repeat center;
       background-size: contain;
     }
+  }
 
-    &:hover {
-      .iconEdit {
-        background: url('../../images/icon-courseware-edit-active.png') no-repeat center;
-        background-size: contain;
-      }
 
-      .iconDelete {
-        background: url('../../images/icon-courseware-delete-active.png') no-repeat center;
-        background-size: contain;
-      }
+  .iconAdd {
+    &::before {
+      display: inline-block;
+      content: '';
+      width: 18px;
+      height: 18px;
+      background: url('../../images/icon-add.png') no-repeat center;
+      background-size: contain;
     }
   }
+
 }

+ 82 - 45
src/views/prepare-lessons/model/courseware-type/index.tsx

@@ -1,9 +1,9 @@
 import { defineComponent } from 'vue';
 import styles from './index.module.less';
-import { NButton, NImage, NSpace, NTooltip } from 'naive-ui';
-import iconEdit from '../../images/icon-courseware-edit.png';
-import iconDelete from '../../images/icon-courseware-delete.png';
-import iconEditName from '../../images/icon-edit-name.png';
+import { NButton, NImage, NPopover, NSpace, NTooltip } from 'naive-ui';
+// import iconEdit from '../../images/icon-courseware-edit.png';
+// import iconDelete from '../../images/icon-courseware-delete.png';
+// import iconEditName from '../../images/icon-edit-name.png';
 
 export default defineComponent({
   name: 'courseware-type',
@@ -43,7 +43,16 @@ export default defineComponent({
     }
   },
   /** add */
-  emits: ['add', 'editName', 'edit', 'delete', 'startClass', 'click'],
+  emits: [
+    'add',
+    'editName',
+    'edit',
+    'delete',
+    'startClass',
+    'click',
+    'work',
+    'look'
+  ],
   setup(props, { emit }) {
     return () => (
       <div
@@ -53,27 +62,20 @@ export default defineComponent({
             ? styles.coursewareTypeHover
             : styles.coursewareTypeHocoursewareTypeShow
         ]}>
-        {props.isShowAdd && (
-          <NButton
-            type="primary"
-            class={[styles.addBtn]}
-            disabled={props.item.isAdd ? true : false}
-            onClick={(e: MouseEvent) => {
-              e.stopPropagation();
-              e.preventDefault();
-              emit('add');
-            }}>
-            {props.item.isAdd ? '已添加' : '添加'}
-          </NButton>
-        )}
         <div
-          class={[styles.cover, props.isShowPreviewBtn && styles.hideP]}
+          class={[
+            styles.cover,
+            props.isShowPreviewBtn && styles.hideP,
+            props.isShowAdd && styles.isShowAdd
+          ]}
           onClick={() => emit('click')}>
           {props.item.openFlag && props.isShowOpenFlag && (
             <span class={styles.status}>公开</span>
           )}
           <NImage objectFit="cover" previewDisabled src={props.item.coverImg} />
 
+          <i class={styles.defaultLook}></i>
+
           {props.isShowPreviewBtn && (
             <div class={styles.preview}>
               <NButton strong secondary class={styles.previewBtn}>
@@ -81,46 +83,81 @@ export default defineComponent({
               </NButton>
             </div>
           )}
-        </div>
-        <div class={styles.coursewareText}>
-          <div class={[styles.name, props.isEditName && styles.editName]}>
-            <span>{props.item.name}</span>
-            <i class={styles.iconEditName} onClick={() => emit('editName')}></i>
-          </div>
-          <div class={styles.subjectName}>{props.item.subjectNames}</div>
-        </div>
 
-        {props.operate && (
-          <div class={styles.footer}>
-            <div class={styles.actionClass} onClick={() => emit('startClass')}>
-              开始上课
-            </div>
-            <NSpace>
+          {/* 是否布置作业 */}
+          {props.item.isNotWork && <i class={styles.iconNoWork}></i>}
+          {/* 是否有添加逻辑 */}
+          {props.isShowAdd && (
+            <div class={styles.function}>
               <NTooltip showArrow={false}>
                 {{
                   trigger: () => (
-                    <div class={styles.optons} onClick={() => emit('edit')}>
-                      {/* <NImage src={iconEdit} previewDisabled></NImage> */}
-                      <i class={styles.iconEdit}></i>
-                    </div>
+                    <i class={styles.iconLook} onClick={() => emit('look')}></i>
                   ),
-                  default: () => <div>编辑</div>
+                  default: () => '预览课件'
                 }}
               </NTooltip>
               <NTooltip showArrow={false}>
                 {{
                   trigger: () => (
-                    <div class={styles.optons} onClick={() => emit('delete')}>
-                      {/* <NImage src={iconDelete} previewDisabled></NImage> */}
-                      <i class={styles.iconDelete}></i>
-                    </div>
+                    <i class={styles.iconAdd} onClick={() => emit('add')}></i>
                   ),
-                  default: () => <div>删除</div>
+                  default: () => '添加到我的课件'
                 }}
               </NTooltip>
-            </NSpace>
+            </div>
+          )}
+        </div>
+        <div class={styles.operationBottom}>
+          <div class={styles.coursewareText}>
+            <div class={[styles.name]}>
+              <span>{props.item.name}</span>
+              {/* , props.isEditName && styles.editName <i
+                class={styles.iconEditName}
+                onClick={() => emit('editName')}></i> */}
+            </div>
+            <div class={styles.subjectName}>{props.item.subjectNames}</div>
           </div>
-        )}
+
+          {props.operate && (
+            <div class={styles.footer}>
+              <div
+                class={styles.actionClass}
+                onClick={() => emit('startClass')}>
+                开始上课
+              </div>
+
+              <NSpace>
+                <div
+                  class={[styles.actionClass, styles.actionWork]}
+                  onClick={() => emit('work')}>
+                  作业
+                </div>
+                <NPopover trigger="hover" showArrow={false} to={false}>
+                  {{
+                    trigger: () => <div class={[styles.menu]}></div>,
+                    default: () => (
+                      <div class={styles.popoverList}>
+                        <div
+                          class={styles.popoverItem}
+                          onClick={() => emit('edit')}>
+                          <i class={styles.iconEdit}></i>
+                          <span>编辑课件</span>
+                        </div>
+                        <div
+                          class={styles.popoverItem}
+                          onClick={() => emit('delete')}>
+                          <i class={styles.iconDelete}></i>
+                          <span>删除课件</span>
+                        </div>
+                      </div>
+                    )
+                  }}
+                </NPopover>
+              </NSpace>
+            </div>
+          )}
+        </div>
       </div>
     );
   }

+ 15 - 14
src/views/prepare-lessons/model/related-class/index.tsx

@@ -69,7 +69,8 @@ export default defineComponent({
             name: item.name,
             coverImg: firstItem?.bizInfo.coverImg,
             type: firstItem?.bizInfo.type,
-            isAdd: item.addFlag
+            isAdd: item.addFlag,
+            isNotWork: item.lessonPreTrainingNum <= 0 ? true : false // 是否布置作业
           });
         });
 
@@ -83,18 +84,18 @@ export default defineComponent({
     };
 
     // 检测数据是否存在
-    watch(
-      () => props.tableList,
-      () => {
-        // fromChapterLessonCoursewareId;
-        forms.tableList.forEach((item: any) => {
-          const index = props.tableList.findIndex(
-            (i: any) => i.fromChapterLessonCoursewareId === item.id
-          );
-          item.isAdd = index !== -1 ? true : false;
-        });
-      }
-    );
+    // watch(
+    //   () => props.tableList,
+    //   () => {
+    //     // fromChapterLessonCoursewareId;
+    //     forms.tableList.forEach((item: any) => {
+    //       const index = props.tableList.findIndex(
+    //         (i: any) => i.fromChapterLessonCoursewareId === item.id
+    //       );
+    //       item.isAdd = index !== -1 ? true : false;
+    //     });
+    //   }
+    // );
 
     const throttleFn = useThrottleFn(() => {
       forms.tableList = [];
@@ -182,7 +183,7 @@ export default defineComponent({
                           onAdd={() => {
                             emit('add', item);
                           }}
-                          onClick={() => emit('click', item)}
+                          onLook={() => emit('click', item)}
                         />
                       </div>
                     </div>

+ 32 - 31
src/views/prepare-lessons/model/subject-sync/index.tsx

@@ -1,6 +1,6 @@
 import { defineComponent, onMounted, ref } from 'vue';
 import styles from './index.module.less';
-import { NButton, NSpace, useMessage } from 'naive-ui';
+import { NButton, NScrollbar, NSpace, useMessage } from 'naive-ui';
 import { useCatchStore } from '/src/store/modules/catchData';
 import iconSelect from '../../images/icon-select.png';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
@@ -80,9 +80,8 @@ export default defineComponent({
     };
     onMounted(async () => {
       // 获取教材分类列表
-      await catchStore.getSubjects();
-
-      subjectList.value = catchStore.getSubjectList;
+      await catchStore.getMusicInstrument();
+      subjectList.value = catchStore.getMusicInstruments;
       // const teachingSubjectList = prepareStore.getSubjectList; // 教材自带声部;
       // const tempSubjectList: any = [];
       // baseAllSubjectList.forEach((subject: any) => {
@@ -106,35 +105,37 @@ export default defineComponent({
           <span>(勾选后则对应乐器下的课件内容将被当前课件内容全部替换)</span>
         </div> */}
 
-        <div class={styles.subjectList}>
-          {subjectList.value.map((subject: any) => (
-            <div
-              class={[
-                styles.subjectItem,
-                selectSubjectIds.value.includes(subject.id)
-                  ? styles.subjectSelect
-                  : ''
-              ]}
-              onClick={() => {
-                if (selectSubjectIds.value.includes(subject.id)) {
-                  const index = selectSubjectIds.value.indexOf(subject.id);
-                  selectSubjectIds.value.splice(index, 1);
-                } else {
-                  selectSubjectIds.value.push(subject.id);
-                }
-              }}>
-              <div class={styles.imgSection}>
-                <img src={subject.img} />
+        <NScrollbar style={{ maxHeight: '60vh' }}>
+          <div class={styles.subjectList}>
+            {subjectList.value.map((subject: any) => (
+              <div
+                class={[
+                  styles.subjectItem,
+                  selectSubjectIds.value.includes(subject.id)
+                    ? styles.subjectSelect
+                    : ''
+                ]}
+                onClick={() => {
+                  if (selectSubjectIds.value.includes(subject.id)) {
+                    const index = selectSubjectIds.value.indexOf(subject.id);
+                    selectSubjectIds.value.splice(index, 1);
+                  } else {
+                    selectSubjectIds.value.push(subject.id);
+                  }
+                }}>
+                <div class={styles.imgSection}>
+                  <img src={subject.img} />
 
-                {selectSubjectIds.value.includes(subject.id) && (
-                  <img src={iconSelect} class={styles.iconSelect} />
-                )}
-              </div>
+                  {selectSubjectIds.value.includes(subject.id) && (
+                    <img src={iconSelect} class={styles.iconSelect} />
+                  )}
+                </div>
 
-              <p class={styles.subjectName}>{subject.name}</p>
-            </div>
-          ))}
-        </div>
+                <p class={styles.subjectName}>{subject.name}</p>
+              </div>
+            ))}
+          </div>
+        </NScrollbar>
 
         <NSpace class={styles.btnGroupModal} justify="center">
           <NButton round onClick={() => emit('close')}>

+ 19 - 1
src/views/xiaoku-music/component/play-item/index.tsx

@@ -56,6 +56,11 @@ export default defineComponent({
       if (props.playState === 'play') {
         audioRef.value.play();
       }
+
+      // 判断是否有链接
+      if (!props.item.audioFileUrl && !props.item.metronomeUrl) {
+        emit('change', 'pause');
+      }
     };
     /** 改变时间 */
     const handleChangeTime = (val: number) => {
@@ -80,13 +85,26 @@ export default defineComponent({
       () => props.playState,
       val => {
         if (val === 'play') {
-          audioRef.value.play();
+          audioRef.value.play().catch((err: any) => {
+            console.log(err, '22');
+            audioRef.value.play();
+          });
         } else {
           audioRef.value.pause();
         }
       }
     );
 
+    watch(
+      () => props.item,
+      () => {
+        // 判断是否有链接
+        if (!props.item.audioFileUrl && !props.item.metronomeUrl) {
+          emit('change', 'pause');
+        }
+      }
+    );
+
     return () => (
       <div
         class={[

+ 1 - 1
src/views/xiaoku-music/index.module.less

@@ -404,7 +404,7 @@
 
 .musicName {
   padding-top: 30px;
-  font-size: 24px;
+  font-size: 26px;
   font-weight: 500;
   color: #333;
   line-height: 24px;

+ 28 - 2
src/views/xiaoku-music/index.tsx

@@ -126,7 +126,7 @@ export default defineComponent({
         console.log(error);
       }
 
-      console.log(res, 'data', data.reshing, 'musicSubject');
+      // console.log(res, 'data', data.reshing, 'musicSubject');
       if (data.reshing) {
         data.list = [];
         data.reshing = false;
@@ -135,6 +135,8 @@ export default defineComponent({
       if (res?.code === 200 && Array.isArray(res?.data?.rows)) {
         data.list = [...data.list, ...res.data.rows];
         data.finshed = res.data.rows.length < forms.rows;
+      } else {
+        data.finshed = true;
       }
       if (data.list[data.listActive]) {
         getFavitor(data.list[data.listActive]);
@@ -471,6 +473,15 @@ export default defineComponent({
         musicIframeLoad();
       }
     );
+    // 合奏曲谱转换时,更新曲谱信息
+    watch(
+      () => data.showMusicImg,
+      () => {
+        if (isEnsemble.value) {
+          musicIframeLoad();
+        }
+      }
+    );
 
     const musicImg = computed(() => {
       let imgs: any = [];
@@ -651,6 +662,13 @@ export default defineComponent({
                               onClick={(e: Event) => {
                                 e.stopPropagation();
                                 handlePlay(item);
+                                if (
+                                  data.listActive === index &&
+                                  data.playState === 'play' &&
+                                  isEnsemble.value
+                                ) {
+                                  musicIframeLoad();
+                                }
                               }}>
                               试听
                               <img
@@ -672,6 +690,13 @@ export default defineComponent({
                               onClick={(e: Event) => {
                                 e.stopPropagation();
                                 handlePlay(item);
+                                if (
+                                  data.listActive === index &&
+                                  data.playState === 'play' &&
+                                  isEnsemble.value
+                                ) {
+                                  musicIframeLoad();
+                                }
                               }}>
                               试听
                               <img
@@ -806,7 +831,8 @@ export default defineComponent({
                       id="staffIframeRef"
                       style={{
                         width: '100%',
-                        height: '100%'
+                        height: '100%',
+                        paddingTop: '20px'
                         // opacity: loading.value ? 0 : 1
                       }}
                       src={data.iframeSrc}

+ 2 - 2
vite.config.ts

@@ -23,8 +23,8 @@ function resolve(dir: string) {
 }
 // https://vitejs.dev/config/
 // https://github.com/vitejs/vite/issues/1930 .env
-// const proxyUrl = 'https://dev.kt.colexiu.com/';
-const proxyUrl = 'https://test.kt.colexiu.com';
+const proxyUrl = 'https://dev.kt.colexiu.com/';
+// const proxyUrl = 'https://test.kt.colexiu.com';
 // const proxyUrl = 'http://192.168.3.14:7989';
 const now = new Date().getTime();
 export default defineConfig(() => {