瀏覽代碼

Merge branch 'iteration-clssroom-work' into online

lex 1 年之前
父節點
當前提交
102d196898
共有 83 個文件被更改,包括 3407 次插入956 次删除
  1. 17 7
      dev-dist/sw.js
  2. 1 1
      public/version.json
  3. 7 0
      src/api/user.ts
  4. 二進制
      src/components/TheEmpty/images/nomore.png
  5. 3 2
      src/components/TheEmpty/index.module.less
  6. 7 1
      src/components/card-preview/index.tsx
  7. 42 0
      src/components/card-preview/listen-modal/index.module.less
  8. 64 0
      src/components/card-preview/listen-modal/index.tsx
  9. 19 2
      src/main.ts
  10. 2 4
      src/router/router-guards.ts
  11. 33 1
      src/store/modules/catchData.ts
  12. 11 0
      src/store/modules/users.ts
  13. 1 0
      src/store/mutation-types.ts
  14. 8 1
      src/utils/request.ts
  15. 9 8
      src/utils/urlUtils.ts
  16. 4 5
      src/views/attend-class/component/rhythm-modal/index.tsx
  17. 37 2
      src/views/attend-class/index.module.less
  18. 90 5
      src/views/attend-class/index.tsx
  19. 1 1
      src/views/attend-class/model/class-work/index.tsx
  20. 7 3
      src/views/attend-class/model/select-class/index.module.less
  21. 40 2
      src/views/attend-class/model/select-class/index.tsx
  22. 二進制
      src/views/attend-class/model/train-type/images/icon-edit-active.png
  23. 二進制
      src/views/attend-class/model/train-type/images/icon-edit.png
  24. 87 23
      src/views/attend-class/model/train-type/index.module.less
  25. 39 16
      src/views/attend-class/model/train-type/index.tsx
  26. 4 4
      src/views/classList/contants.ts
  27. 1 1
      src/views/home/index.tsx
  28. 1 1
      src/views/home/index2.module.less
  29. 1 1
      src/views/home/modals/class-modal/index.module.less
  30. 28 23
      src/views/home/modals/subject-modal/index.tsx
  31. 二進制
      src/views/homework-record/images/icon-ing.png
  32. 二進制
      src/views/homework-record/images/icon-over.png
  33. 62 14
      src/views/homework-record/index.module.less
  34. 176 63
      src/views/homework-record/index.tsx
  35. 10 4
      src/views/natural-resources/components/my-resources/upload-modal/index.module.less
  36. 3 4
      src/views/natural-resources/components/my-resources/upload-modal/index.tsx
  37. 159 14
      src/views/prepare-lessons/components/lesson-main/courseware-presets/index.module.less
  38. 301 267
      src/views/prepare-lessons/components/lesson-main/courseware-presets/index.tsx
  39. 80 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/related.module.less
  40. 51 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/related.tsx
  41. 151 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/index.module.less
  42. 161 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/index.tsx
  43. 51 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/item.tsx
  44. 56 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/resource-search-group/index.module.less
  45. 53 0
      src/views/prepare-lessons/components/lesson-main/courseware-presets/select-related/resource-search-group/index.tsx
  46. 0 5
      src/views/prepare-lessons/components/lesson-main/courseware/addCourseware.module.less
  47. 145 86
      src/views/prepare-lessons/components/lesson-main/courseware/addCourseware.tsx
  48. 2 0
      src/views/prepare-lessons/components/lesson-main/index.module.less
  49. 85 81
      src/views/prepare-lessons/components/lesson-main/index.tsx
  50. 10 2
      src/views/prepare-lessons/components/lesson-main/train/assign-homework.tsx
  51. 3 3
      src/views/prepare-lessons/components/lesson-main/train/assign-student/index.module.less
  52. 21 5
      src/views/prepare-lessons/components/lesson-main/train/assign-student/index.tsx
  53. 9 8
      src/views/prepare-lessons/components/lesson-main/train/index.module.less
  54. 67 60
      src/views/prepare-lessons/components/lesson-main/train/index.tsx
  55. 22 19
      src/views/prepare-lessons/components/resource-main/components/select-music/index.tsx
  56. 2 2
      src/views/prepare-lessons/components/resource-main/index.tsx
  57. 二進制
      src/views/prepare-lessons/images/active-arrow.png
  58. 二進制
      src/views/prepare-lessons/images/addSource/icon8.png
  59. 二進制
      src/views/prepare-lessons/images/default-arrow.png
  60. 二進制
      src/views/prepare-lessons/images/icon-add.png
  61. 二進制
      src/views/prepare-lessons/images/icon-look.png
  62. 二進制
      src/views/prepare-lessons/images/icon-menu-active.png
  63. 二進制
      src/views/prepare-lessons/images/icon-menu-default.png
  64. 二進制
      src/views/prepare-lessons/images/icon-no-work.png
  65. 二進制
      src/views/prepare-lessons/images/protocol/check-active.png
  66. 二進制
      src/views/prepare-lessons/images/protocol/check-default.png
  67. 二進制
      src/views/prepare-lessons/images/protocol/closeAble.png
  68. 二進制
      src/views/prepare-lessons/images/protocol/dingPng.png
  69. 二進制
      src/views/prepare-lessons/images/protocol/moveTop.png
  70. 180 0
      src/views/prepare-lessons/model/add-courseware-protocol/index.module.less
  71. 240 0
      src/views/prepare-lessons/model/add-courseware-protocol/index.tsx
  72. 24 0
      src/views/prepare-lessons/model/add-other-source/index.module.less
  73. 157 11
      src/views/prepare-lessons/model/add-other-source/index.tsx
  74. 277 52
      src/views/prepare-lessons/model/courseware-type/index.module.less
  75. 88 47
      src/views/prepare-lessons/model/courseware-type/index.tsx
  76. 6 5
      src/views/prepare-lessons/model/related-class/index.module.less
  77. 45 37
      src/views/prepare-lessons/model/related-class/index.tsx
  78. 1 1
      src/views/prepare-lessons/model/subject-sync/index.module.less
  79. 95 46
      src/views/prepare-lessons/model/subject-sync/index.tsx
  80. 19 1
      src/views/xiaoku-music/component/play-item/index.tsx
  81. 1 1
      src/views/xiaoku-music/index.module.less
  82. 28 2
      src/views/xiaoku-music/index.tsx
  83. 2 2
      vite.config.ts

+ 17 - 7
dev-dist/sw.js

@@ -22,7 +22,7 @@ if (!self.define) {
   const singleRequire = (uri, parentUri) => {
     uri = new URL(uri + ".js", parentUri).href;
     return registry[uri] || (
-      
+
         new Promise(resolve => {
           if ("document" in self) {
             const script = document.createElement("script");
@@ -35,7 +35,7 @@ if (!self.define) {
             resolve();
           }
         })
-      
+
       .then(() => {
         let promise = registry[uri];
         if (!promise) {
@@ -85,8 +85,18 @@ define(['./workbox-5357ef54'], (function (workbox) { 'use strict';
     "revision": "0.k9m6fls6u18"
   }], {});
   workbox.cleanupOutdatedCaches();
-  workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
-    allowlist: [/^\/$/]
-  }));
-
-}));
+  workbox.registerRoute(
+    new workbox.NavigationRoute(workbox.createHandlerBoundToURL('index.html'), {
+      allowlist: [/^\/$/]
+    }),
+    workbox.strategies.networkOnly()
+  );
+  workbox.routing.registerRoute(
+    new RegExp('.*.html|css'),
+    workbox.strategies.networkFirst()
+  );
+  workbox.routing.registerRoute(
+    new RegExp('|js'),
+    workbox.strategies.networkOnly()
+  );
+});

+ 1 - 1
public/version.json

@@ -1 +1 @@
-{"version":1711027985265}
+{"version":1711031360080}

+ 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
+  });
+};

二進制
src/components/TheEmpty/images/nomore.png


+ 3 - 2
src/components/TheEmpty/index.module.less

@@ -10,11 +10,12 @@
     .n-empty__icon {
       width: 100%;
       height: 100%;
+      text-align: center;
     }
   }
 
   .emptyImg {
-    width: 240px;
-    height: 240px;
+    width: 196px;
+    height: 196px;
   }
 }

+ 7 - 1
src/components/card-preview/index.tsx

@@ -9,6 +9,7 @@ import RhythmModal from './rhythm-modal';
 import InstruemntDetail from '/src/views/prepare-lessons/model/source-instrument/detail';
 import TheoryDetail from '/src/views/prepare-lessons/model/source-knowledge/detail';
 import MusicDetail from '/src/views/prepare-lessons/model/source-music/detail';
+import ListenModal from './listen-modal';
 
 export default defineComponent({
   name: 'card-preview',
@@ -104,6 +105,10 @@ export default defineComponent({
             <RhythmModal class={styles.musicPreview} item={item.value} />
           )}
 
+          {item.value.type === 'LISTEN' && (
+            <ListenModal class={styles.musicPreview} item={item.value} />
+          )}
+
           {(item.value.type === 'INSTRUMENT' ||
             item.value.type === 'MUSICIAN') && (
             <div class={styles.instrumentGroup}>
@@ -151,7 +156,8 @@ export default defineComponent({
             'INSTRUMENT',
             'THEORY',
             'MUSICIAN',
-            'MUSIC_WIKI'
+            'MUSIC_WIKI',
+            'LISTEN'
           ].includes(item.value.type) && <TheEmpty />}
         </NModal>
       </>

+ 42 - 0
src/components/card-preview/listen-modal/index.module.less

@@ -0,0 +1,42 @@
+// .musicScore {
+//   width: 100%;
+//   height: 100%;
+
+//   iframe {
+//     width: inherit;
+//     height: inherit;
+
+//     :global {
+//       .headTopBackBtn {
+//         display: none;
+//       }
+//     }
+//   }
+// }
+
+.musicScore {
+  position: relative;
+  width: 100%;
+  min-height: 518px;
+  height: 100%;
+  -webkit-overflow-scrolling: touch;
+  // overflow: scroll;
+
+  .container {
+    position: relative;
+    display: block;
+    border: none;
+    width: 100%;
+    min-height: 518px;
+    height: 100%;
+    z-index: 10;
+  }
+
+  .musicModel {
+    position: absolute;
+    left: 0;
+    top: 0;
+    right: 0;
+    bottom: 0;
+  }
+}

+ 64 - 0
src/components/card-preview/listen-modal/index.tsx

@@ -0,0 +1,64 @@
+import { defineComponent, ref, watch } from 'vue';
+import styles from './index.module.less';
+import { useUserStore } from '/src/store/modules/users';
+import { vaildMusicScoreUrl } from '/src/utils/urlUtils';
+
+export default defineComponent({
+  name: 'song-modal',
+  props: {
+    item: {
+      type: Object,
+      default: () => ({})
+    },
+    activeStatus: {
+      type: Boolean,
+      default: false
+    }
+  },
+  emits: ['setIframe'],
+  setup(props, { emit }) {
+    const userStore = useUserStore();
+    const iframeRef = ref();
+    const isLoaded = ref(false);
+
+    const src = `${vaildMusicScoreUrl()}/instrument?v=${new Date().getTime()}/#/view-figner?Authorization=${
+      userStore.getToken
+    }&code=${props.item.content}&platform=pc&type=listenMode&linkSource=class`;
+    // if (/(localhost|192)/.test(location.host)) {
+    //   src = `http://localhost:3001/instrument.html#/view-figner?Authorization=${userStore.getToken}&code=${props.item.content}&platform=pc&type=listenMode`;
+    // }
+    // if (props.item.dataJson) {
+    //   src += '&dataJson=' + props.item.dataJson;
+    // }
+
+    watch(
+      () => props.activeStatus,
+      () => {
+        if (!props.activeStatus) {
+          iframeRef.value?.contentWindow?.postMessage(
+            {
+              api: 'setPlayState',
+              data: {
+                code: props.item.content
+              }
+            },
+            '*'
+          );
+        }
+      }
+    );
+    return () => (
+      <div class={styles.musicScore}>
+        <iframe
+          ref={iframeRef}
+          onLoad={() => {
+            emit('setIframe', iframeRef.value);
+            isLoaded.value = true;
+          }}
+          class={[styles.container, 'musicIframe']}
+          frameborder="0"
+          src={src}></iframe>
+      </div>
+    );
+  }
+});

+ 19 - 2
src/main.ts

@@ -1,14 +1,32 @@
 import { createApp } from 'vue';
 import App from './App';
-import router, { setupRouter } from './router/index';
+import router, { parseQuery, setupRouter } from './router/index';
 import dayjs from 'dayjs';
 import { setupNaive } from './plugins';
 import { setupStore } from './store';
 import 'dayjs/locale/zh-cn';
 import './styles/index.less';
 import './utils/rem';
+import { storage } from './utils/storage';
+import { ACCESS_TOKEN_ADMIN } from './store/mutation-types';
 
 async function setupApp() {
+  // 处理token
+  const authLoadNum = sessionStorage.getItem('authLoadNum');
+
+  if (location.search && authLoadNum !== '1') {
+    const parse = location.search.substring(1, location.search.length);
+    const result = parseQuery(parse);
+    if (result.Authorization) {
+      const ex = 7 * 24 * 60 * 60 * 1000;
+      storage.set(ACCESS_TOKEN_ADMIN, result.Authorization, ex);
+      sessionStorage.setItem('authLoadNum', '1');
+      sessionStorage.setItem('authSource', result.source?.toString() || '');
+    }
+    console.log(result, 'result');
+  } else {
+    sessionStorage.removeItem('authLoadNum');
+  }
   // app loading
   // const appLoading = createApp(AppLoading);
   // appLoading.mount('#appLoading');
@@ -24,7 +42,6 @@ async function setupApp() {
   dayjs.locale('zh-ch');
 
   // app.use(router);
-
   await router.isReady();
 
   // mount app

+ 2 - 4
src/router/router-guards.ts

@@ -26,16 +26,14 @@ const isChrome = () => {
 export function createRouterGuards(router: Router) {
   const userStore = useUserStore();
   router.beforeEach(async (to, from, next) => {
-    // console.log(to, '修改标题');
-    // console.log('access token');
     if (to.path === '/attend-class') {
       let title = to.meta.title;
       if (to.query.type === 'preview') {
         title = '预览课件';
       }
-      document.title = title ? title : ('音乐数字课堂' as string);
+      (document as any).title = title ? title : ('音乐数字课堂' as string);
     } else {
-      document.title = to.meta.title
+      (document as any).title = to.meta.title
         ? to.meta.title
         : ('音乐数字课堂' as string);
     }

+ 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) => {

+ 1 - 0
src/store/mutation-types.ts

@@ -1,4 +1,5 @@
 export const ACCESS_TOKEN = 'ACCESS-TOKEN-TEACHER'; // 用户token
+export const ACCESS_TOKEN_ADMIN = 'ACCESS-TOKEN-TEACHER--ADMIN'; // 用户token
 export const IM_TOKEN = 'IM-TOKEN'; //
 export const CURRENT_USER = 'CURRENT-USER'; // 当前用户信息
 export const TABS_ROUTES = 'TABS-ROUTES'; // 标签页

+ 8 - 1
src/utils/request.ts

@@ -3,6 +3,7 @@ import cleanDeep from 'clean-deep';
 import { useUserStore } from '../store/modules/users';
 import router from '@/router';
 import { eventGlobal } from '.';
+import { storage } from './storage';
 
 export interface SearchInitParams {
   rows?: string | number;
@@ -26,7 +27,13 @@ request.interceptors.request.use(
     }
 
     const userStore = useUserStore();
-    const Authorization = userStore.getToken || '';
+
+    let Authorization = userStore.getToken || '';
+    // 为了处理课堂乐器后台预览课件的功能
+    const authSource = sessionStorage.getItem('authSource');
+    if (authSource === 'admin' && storage.get('authorization')) {
+      Authorization = storage.get('authorization');
+    }
     const authHeaders: any = {};
     // if (
     //   userStore.getUserInfo &&

+ 9 - 8
src/utils/urlUtils.ts

@@ -1,14 +1,14 @@
 export function vaildUrl() {
   const url: string = window.location.href;
   let returnUrl = '';
-  if (/192/.test(url) || /localhost/.test(url)) {
-    //本地环境
-    returnUrl = 'https://test.kt.colexiu.com';
-  } else if (/test/.test(url)) {
+  if (/test/.test(url)) {
     // dev 环境
     returnUrl = 'https://test.kt.colexiu.com';
   } else if (/dev/.test(url)) {
     returnUrl = 'https://dev.kt.colexiu.com';
+  } else if (/localhost/.test(url)) {
+    //   //本地环境
+    returnUrl = 'https://test.kt.colexiu.com';
   } else {
     // 默认dev环境
     returnUrl = 'https://kt.colexiu.com';
@@ -19,14 +19,15 @@ export function vaildUrl() {
 export function vaildMusicScoreUrl() {
   const url: string = window.location.href;
   let returnUrl = '';
-  if (/192/.test(url) || /localhost/.test(url)) {
-    //本地环境
-    returnUrl = 'https://test.kt.colexiu.com';
-  } else if (/test/.test(url)) {
+
+  if (/test/.test(url)) {
     // dev 环境
     returnUrl = 'https://test.kt.colexiu.com';
   } else if (/dev/.test(url)) {
     returnUrl = 'https://dev.kt.colexiu.com';
+  } else if (/localhost/.test(url)) {
+    //   //本地环境
+    returnUrl = 'https://test.kt.colexiu.com';
   } else {
     returnUrl = 'https://mec.colexiu.com';
   }

+ 4 - 5
src/views/attend-class/component/rhythm-modal/index.tsx

@@ -20,14 +20,13 @@ export default defineComponent({
     const userStore = useUserStore();
     const iframeRef = ref();
     const isLoaded = ref(false);
-    const origin = /(localhost|192)/.test(location.host)
-      ? 'http://localhost:9002'
-      : location.origin;
-    let src = `${origin}/classroom-app/#/tempo-practice?v=${+new Date()}&platform=modal&dataJson=${
+    let src = `${
+      location.origin
+    }/classroom-app/#/tempo-practice?v=${+new Date()}&platform=modal&dataJson=${
       props.item.dataJson
     }&Authorization=${userStore.getToken}&win=pc`;
     if (/(localhost|192)/.test(location.host)) {
-      src = `${origin}/#/tempo-practice?v=${+new Date()}&platform=modal&dataJson=${
+      src = `http://localhost:9002/#/tempo-practice?v=${+new Date()}&platform=modal&dataJson=${
         props.item.dataJson
       }&Authorization=${userStore.getToken}&win=pc`;
     }

+ 37 - 2
src/views/attend-class/index.module.less

@@ -136,7 +136,7 @@
 .wraps {
   width: 100%;
   height: 100%;
-  transform-style: preserve-3d;
+  // transform-style: preserve-3d;
   perspective: (32rem);
   transition-timing-function: initial;
 }
@@ -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);
+  }
 }

+ 90 - 5
src/views/attend-class/index.tsx

@@ -84,6 +84,12 @@ import RhythmModal from './component/rhythm-modal';
 import InstruemntDetail from '/src/views/prepare-lessons/model/source-instrument/detail';
 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';
+import { storage } from '/src/utils/storage';
+import { ACCESS_TOKEN_ADMIN } from '/src/store/mutation-types';
 
 export type ToolType = 'init' | 'pen' | 'whiteboard' | 'call';
 export type ToolItem = {
@@ -168,6 +174,7 @@ export default defineComponent({
       subjectId: '' as any, // 声部编号
       lessonCourseId: '' as any, // 教材编号
       lessonCoursewareDetailId: '' as any, // 章节
+      lessonCoursewareSubjectList: [] as any, // 教材上的声部
       detailId: '' as any, // 编号 - 课程编号
       classGroupId: '' as any, // 上课时需要 班级编号
       classId: '' as any, // 上课编号
@@ -191,6 +198,7 @@ export default defineComponent({
       removeCourseStatus: false, // 是否布置作业
 
       teacherChapterName: '',
+      lessonPreTrainingId: null,
       selectResourceStatus: false,
       videoState: 'init' as 'init' | 'play',
       videoItemRef: null as any,
@@ -218,6 +226,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 = [];
@@ -341,7 +351,6 @@ export default defineComponent({
       if (data.type === 'preview') {
         rightList.splice(6, 1);
       }
-
       rollCallStudentList();
     });
 
@@ -419,6 +428,7 @@ export default defineComponent({
 
         const ids = formatParentId(data.detailId, popupData.chapterDetails);
         data.lessonCoursewareDetailId = ids[0];
+        data.lessonCoursewareSubjectList = res.data.subjectList || [];
       } catch {
         //
       }
@@ -1275,6 +1285,9 @@ export default defineComponent({
             data.modelAttendStatus = true;
           } else {
             data.modelTrainStatus = true;
+            nextTick(() => {
+              getModalHeight();
+            });
             data.modelAttendStatus = false;
           }
           break;
@@ -1389,6 +1402,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
@@ -1455,6 +1479,14 @@ export default defineComponent({
                   // if (isRender) {
                   //   m.isRender = true;
                   // }
+
+                  // const isRender =
+                  //   m.isRender || Math.abs(popupData.activeIndex - mIndex) < 2;
+                  // const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4;
+                  // if (isRender) {
+                  //   m.isRender = true;
+                  // }
+
                   return isRender ? (
                     <div
                       key={'index' + mIndex}
@@ -1587,6 +1619,15 @@ export default defineComponent({
                             m.iframeRef = el;
                           }}
                         />
+                      ) : m.type === 'LISTEN' ? (
+                        <ListenModal
+                          item={m}
+                          data-vid={m.id}
+                          activeStatus={popupData.activeIndex === mIndex}
+                          onSetIframe={(el: any) => {
+                            m.iframeRef = el;
+                          }}
+                        />
                       ) : m.type === 'INSTRUMENT' || m.type === 'MUSICIAN' ? (
                         <InstruemntDetail
                           type="preview"
@@ -1657,7 +1698,7 @@ export default defineComponent({
                   trigger: () => <img src={item.icon} />,
                   default: (
                     <>
-                      {index === 0 && data.type === 'preview'
+                      {index === rightList.length - 2 && data.type === 'preview'
                         ? item.name2
                         : item.name}
                     </>
@@ -1794,6 +1835,7 @@ export default defineComponent({
             classId={data.classId}
             courseId={popupData.courseId}
             subjectId={data.subjectId}
+            lessonCoursewareSubjectList={data.lessonCoursewareSubjectList}
             onConfirm={async (val: any) => {
               popupData.chapterLoading = true;
               try {
@@ -1835,6 +1877,9 @@ export default defineComponent({
                 round
                 onClick={() => {
                   data.modelTrainStatus = true;
+                  nextTick(() => {
+                    getModalHeight();
+                  });
                   data.modelAttendStatus = false;
                 }}>
                 布置作业
@@ -1853,7 +1898,7 @@ export default defineComponent({
         </NModal>
 
         {/* 训练设置 */}
-        <NModal
+        {/* <NModal
           transformOrigin="center"
           v-model:show={data.modelTrainStatus}
           preset="card"
@@ -1867,6 +1912,35 @@ 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: data.lessonPreTrainingId
+                    ? ''
+                    : data.teacherChapterName + '-课后作业',
+                  chapterId: data.courseId, // 课件编号
+                  id: data.lessonPreTrainingId // 作业编号
+                }}
+                classGroupId={data.classGroupId}
+                courseScheduleId={data.classId}
+                onChange={(val: any) => {
+                  data.modelTrainStatus = val.status;
+                  // getCoursewareList();
+                }}
+              />
+            </div>
+            <div class={styles.resourceMain}>
+              <ResourceMain cardType="homerowk-record" />
+            </div>
+          </div>
         </NModal>
 
         <NModal
@@ -1956,6 +2030,9 @@ export default defineComponent({
                   if (data.removeCourseStatus) {
                     data.modelTrainStatus = true;
                     data.removeVisiable = false;
+                    nextTick(() => {
+                      getModalHeight();
+                    });
                   } else {
                     if (globalState.application) {
                       document.exitFullscreen
@@ -1968,6 +2045,16 @@ export default defineComponent({
                       emit('close');
                     } else {
                       window.close();
+
+                      if (route.query.source === 'admin') {
+                        storage.remove(ACCESS_TOKEN_ADMIN);
+                        window.parent.postMessage(
+                          {
+                            api: 'iframe_exit'
+                          },
+                          '*'
+                        );
+                      }
                     }
                   }
                 }}>
@@ -1980,5 +2067,3 @@ export default defineComponent({
     );
   }
 });
-
-// roll-call/index.html

+ 1 - 1
src/views/attend-class/model/class-work/index.tsx

@@ -296,7 +296,7 @@ export default defineComponent({
                 lessonPreTraining={{
                   title:
                     (props.activeName || dayjs().format('YYYY年MM月DD日')) +
-                    '-课作业',
+                    '-课作业',
                   id: forms.selectItem.id
                 }}
                 // cardType={'homeworkRecord'}

+ 7 - 3
src/views/attend-class/model/select-class/index.module.less

@@ -1,18 +1,22 @@
 .selectClass {
-  padding: 27px 0;
+  padding: 12px 0 27px;
 }
 
 .selectClassScroll {
   min-height: 60vh;
   max-height: 60vh;
-  padding: 0 33px
+
+}
+
+.searchGroup {
+  padding: 12px 43px;
 }
 
 .list {
   display: flex;
   flex-flow: row wrap;
   justify-content: flex-start;
-  padding: 0 0 12px;
+  padding: 14px 33px 12px;
   gap: 20px 0;
   margin: 0 -10px 0;
   min-height: 300px;

+ 40 - 2
src/views/attend-class/model/select-class/index.tsx

@@ -1,7 +1,7 @@
 import { PropType, defineComponent, onMounted, reactive } from 'vue';
 import styles from './index.module.less';
 import { teacherChapterLessonCoursewareList } from '/src/views/prepare-lessons/api';
-import { NScrollbar, NSpin } from 'naive-ui';
+import { NInput, NScrollbar, NSelect, NSpin } from 'naive-ui';
 import CoursewareType from '/src/views/prepare-lessons/model/courseware-type';
 import TheEmpty from '/src/components/TheEmpty';
 
@@ -16,6 +16,10 @@ export default defineComponent({
       type: String,
       default: ''
     },
+    lessonCoursewareSubjectList: {
+      type: Array,
+      default: () => []
+    },
     classId: {
       type: String,
       default: ''
@@ -25,15 +29,18 @@ export default defineComponent({
   setup(props, { emit }) {
     const forms = reactive({
       loading: false,
+      subjectId: props.subjectId ? Number(props.subjectId) : '',
       tableList: [] as any
     });
+
     const getDetail = async () => {
       forms.loading = true;
       try {
         // 如果是上课则查询班级声部,预览查询全部
+        // console.log(props.classId, props.subjectId);
         const { data } = await teacherChapterLessonCoursewareList({
           coursewareDetailKnowledgeId: props.courseId,
-          subjectId: props.classId ? props.subjectId : null
+          subjectId: props.classId ? forms.subjectId : null
         });
 
         if (!Array.isArray(data)) {
@@ -62,10 +69,41 @@ export default defineComponent({
     };
 
     onMounted(() => {
+      if (props.subjectId) {
+        const s = Number(props.subjectId);
+        let isE = false;
+        props.lessonCoursewareSubjectList.forEach((item: any) => {
+          if (item.id === s) {
+            isE = true;
+          }
+        });
+        if (!isE) {
+          forms.subjectId = '';
+        }
+      }
       getDetail();
     });
     return () => (
       <div class={styles.selectClass}>
+        {props.classId && (
+          <div class={styles.searchGroup}>
+            <NSelect
+              options={[
+                { id: '', name: '全部声部' },
+                ...(props.lessonCoursewareSubjectList as any)
+              ]}
+              valueField="id"
+              labelField="name"
+              style={{ width: '220px' }}
+              placeholder={'请选择声部'}
+              v-model:value={forms.subjectId}
+              onUpdate:value={() => {
+                getDetail();
+              }}
+            />
+          </div>
+        )}
+
         <NSpin show={forms.loading}>
           <NScrollbar class={styles.selectClassScroll}>
             <div

二進制
src/views/attend-class/model/train-type/images/icon-edit-active.png


二進制
src/views/attend-class/model/train-type/images/icon-edit.png


+ 87 - 23
src/views/attend-class/model/train-type/index.module.less

@@ -6,6 +6,7 @@
   position: relative;
   overflow: hidden;
   border: 1px solid #CAE4F4;
+  transition: all .2s ease;
 
   .overflowBg {
     position: absolute;
@@ -17,6 +18,24 @@
     padding: 20px;
     cursor: move;
   }
+
+  &:hover {
+    transition: all .2s ease;
+    border-color: #007afe;
+    transform: scale(1.01);
+
+    .iconDelete {
+      opacity: 1;
+      visibility: visible;
+      transition: all .2s ease;
+    }
+
+    .trainDisabled {
+      .iconDelete {
+        display: none;
+      }
+    }
+  }
 }
 
 .train-header {
@@ -71,32 +90,40 @@
     }
   }
 
-  .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;
+  cursor: pointer;
+
+  &.trainDisabled {
+    cursor: default;
+    height: 238px;
+  }
 
   :global {
     .n-image {
       width: 100%;
       height: inherit;
+      // transition: all 0.2s ease;
 
       img {
         width: 100%;
@@ -188,6 +215,26 @@
       visibility: visible;
       transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
     }
+
+    // :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,12 +242,16 @@
   display: flex;
   align-items: center;
   justify-content: space-between;
-  padding: 14px 13px;
+  padding: 8px;
 
   .type {
     gap: 8px 10px !important;
   }
 
+  .title-text {
+    width: 150px;
+  }
+
 
   .operation {
     display: flex;
@@ -209,19 +260,32 @@
     border-radius: 50%;
 
     img {
-      height: 34px;
+      height: 36px;
+      width: 36px;
+    }
+
+    .iconEdit {
+      display: inline-block;
       width: 34px;
+      height: 34px;
+      background: url('./images/icon-edit.png') no-repeat center;
+      background-size: contain;
+
+      &:hover {
+        background: url('./images/icon-edit-active.png') no-repeat center;
+        background-size: contain;
+      }
     }
   }
 }
 
 .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 +297,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 +318,14 @@
     display: flex;
     align-items: center;
     padding-top: 5px;
-    font-size: 12Px;
-    font-weight: 500;
+    font-size: 11Px;
+    font-weight: 400;
     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;
       }
     }

+ 39 - 16
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';
@@ -22,6 +22,7 @@ import CardPreview from '/src/components/card-preview';
 import { vaildMusicScoreUrl, vaildUrl } from '/src/utils/urlUtils';
 import { musicPracticeRecordDetail } from '/src/views/prepare-lessons/api';
 import { checkUrlType } from '/src/utils';
+import TheNoticeBar from '/src/components/TheNoticeBar';
 type ItemType = {
   id: string | number;
   trainingType: 'PRACTICE' | 'EVALUATION';
@@ -175,8 +176,22 @@ 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
+          ]}
+          onClick={() => {
+            if (props.isDisabled && !props.isCLassWork) {
+              return;
+            }
+            onDetail();
+          }}>
+          <NImage
+            src={props.item.coverImg}
+            previewDisabled
+            objectFit="contain"
+          />
           {props.isDisabled && !props.isCLassWork ? (
             <div class={styles.disPreview}>
               <NProgress
@@ -226,13 +241,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}>
@@ -240,9 +263,10 @@ export default defineComponent({
               <span class={[styles.type, styles[props.item.trainingType]]}>
                 {props.item.trainingType === 'EVALUATION' ? '评测' : '练习'}
               </span>
-              <NEllipsis class={styles['title-text']} tooltip={false}>
-                {props.item.musicName}
-              </NEllipsis>
+              {/* <NEllipsis tooltip={false}>{props.item.musicName}</NEllipsis> */}
+              <div class={styles['title-text']}>
+                <TheNoticeBar text={props.item.musicName} />
+              </div>
             </div>
             <div class={styles.tagList}>
               {props.item.typeList?.map((type: string, index: number) => (
@@ -268,13 +292,13 @@ export default defineComponent({
                 }}>
                 <NTooltip showArrow={false}>
                   {{
-                    trigger: () => <img src={iconEdit} />,
+                    trigger: () => <i class={styles.iconEdit}></i>,
+                    // <img src={iconEdit} />
                     default: '编辑'
                   }}
                 </NTooltip>
               </n-button>
-              {/* {props.type === 'homework' && ( */}
-              <n-button
+              {/* <n-button
                 quaternary
                 disabled={!props.isDelete}
                 class={styles.operation}
@@ -290,8 +314,7 @@ export default defineComponent({
                     default: '删除'
                   }}
                 </NTooltip>
-              </n-button>
-              {/* )} */}
+              </n-button> */}
             </NSpace>
           )}
 

+ 4 - 4
src/views/classList/contants.ts

@@ -88,18 +88,18 @@ export const classArray = [
 export const getgradeNumList = () => {
   const userInfo = useUserStore();
   let gradeNumList = [];
-  if (userInfo.getUserInfo.schoolInfos[0].gradeYear == 'THREE_YEAR_SYSTEM') {
+  if (userInfo.getUserInfo.schoolInfos[0]?.gradeYear == 'THREE_YEAR_SYSTEM') {
     gradeNumList = threeYearSystem;
   } else if (
-    userInfo.getUserInfo.schoolInfos[0].gradeYear == 'FORE_YEAR_SYSTEM'
+    userInfo.getUserInfo.schoolInfos[0]?.gradeYear == 'FORE_YEAR_SYSTEM'
   ) {
     gradeNumList = foreYearSystem;
   } else if (
-    userInfo.getUserInfo.schoolInfos[0].gradeYear == 'FIVE_YEAR_SYSTEM'
+    userInfo.getUserInfo.schoolInfos[0]?.gradeYear == 'FIVE_YEAR_SYSTEM'
   ) {
     gradeNumList = fiveYearSystem;
   } else if (
-    userInfo.getUserInfo.schoolInfos[0].gradeYear == 'SIX_YEAR_SYSTEM'
+    userInfo.getUserInfo.schoolInfos[0]?.gradeYear == 'SIX_YEAR_SYSTEM'
   ) {
     gradeNumList = sixYearSystem;
   } else {

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

@@ -280,7 +280,7 @@ export default defineComponent({
           v-model:show={forms.subjectSyncVisiable}
           preset="card"
           class={['modalTitle background', styles.subjectSyncModal]}
-          title={'请选择声部'}>
+          title={'选择乐器'}>
           <SubjectModal
             onClose={() => (forms.subjectSyncVisiable = false)}
             onConfirm={async (item: any) => {

+ 1 - 1
src/views/home/index2.module.less

@@ -671,7 +671,7 @@
   }
 
   .n-empty {
-    --n-font-size: max(16px, 14Px) !important;
+    --n-font-size: max(16px, 12Px) !important;
   }
 }
 

+ 1 - 1
src/views/home/modals/class-modal/index.module.less

@@ -53,7 +53,7 @@
   }
 
   .n-empty {
-    --n-font-size: max(16px, 14Px) !important;
+    --n-font-size: max(16px, 12Px) !important;
   }
 }
 

+ 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')}>

二進制
src/views/homework-record/images/icon-ing.png


二進制
src/views/homework-record/images/icon-over.png


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

@@ -165,6 +165,9 @@
   background: #F7F9FF;
   border-radius: 13px;
   padding: 0 17px 17px;
+  position: relative;
+  display: flex;
+  flex-direction: column;
 
   .header {
     display: flex;
@@ -206,21 +209,32 @@
       }
     }
 
+    .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;
+    flex: 1;
     padding: 13px;
     background: #FFFFFF;
     border-radius: 10px;
@@ -231,6 +245,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 +290,7 @@
       align-items: flex-start;
 
       .pSection {
-        max-width: 500px;
+        max-width: 650px;
       }
 
       .p1,
@@ -288,14 +336,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;
     }
   }

+ 176 - 63
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();
@@ -135,15 +145,34 @@ export default defineComponent({
           ) {
             item.studentLessonTrainingDetails.forEach((child: any) => {
               if (child.trainingType === 'PRACTICE' && child.musicName) {
-                pTitle += pTitle ? '、' + child.musicName : child.musicName;
+                pTitle += pTitle
+                  ? '、《' + child.musicName + '》'
+                  : '练习曲目《' + child.musicName + '》';
               }
               if (child.trainingType === 'EVALUATION' && child.musicName) {
-                eTitle += eTitle ? '、' + child.musicName : child.musicName;
+                eTitle += eTitle
+                  ? '、《' + child.musicName + '》'
+                  : '评测曲目《' + child.musicName + '》';
               }
             });
           }
           item.pTitle = pTitle;
           item.eTitle = eTitle;
+
+          let studentName = item.homeworkObjName;
+          if (item.homeworkObjName) {
+            const tempObjName = item.homeworkObjName.split(',');
+            if (tempObjName.length > 5) {
+              studentName =
+                tempObjName.slice(0, 5).join('、') +
+                `...等${tempObjName.length}${
+                  item.homeworkObj === 'CLASS' ? '个班级' : '人'
+                }`;
+            } else {
+              studentName = tempObjName.join('、');
+            }
+          }
+          item.studentName = studentName;
         });
         state.tableList = res.data.rows;
         state.pagination.pageTotal = res.data.total;
@@ -213,6 +242,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.lessonCoursewareId
+        };
+      } else {
+        const { href } = router.resolve({
+          path: '/attend-class',
+          query: {
+            type: 'preview',
+            courseId: item.chapterLessonCoursewareId,
+            subjectId: null,
+            detailId: item.lessonCoursewareKnowledgeDetailId,
+            lessonCourseId: item.lessonCoursewareId
+          }
+        });
+        window.open(href, +new Date() + '');
+      }
+    };
+
     onMounted(async () => {
       state.loading = true;
       await getYearList();
@@ -237,6 +303,22 @@ export default defineComponent({
               <CSelect
                 {...({
                   options: [
+                    { id: '', name: '全部类型' },
+                    { id: 'CLASSWORK', name: '课外作业' },
+                    { id: 'HOMEWORK', name: '课后作业' }
+                  ],
+                  placeholder: '选择类型',
+                  clearable: true,
+                  inline: true,
+                  labelField: 'name',
+                  valueField: 'id'
+                } as any)}
+                v-model:value={state.searchForm.homeworkType}></CSelect>
+            </NFormItem>
+            <NFormItem>
+              <CSelect
+                {...({
+                  options: [
                     { id: '', name: '作业对象' },
                     { id: 'PERSON', name: '个人' },
                     { id: 'CLASS', name: '班级' }
@@ -381,76 +463,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.studentName}</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>
                 ))}
@@ -502,7 +608,7 @@ export default defineComponent({
             <div class={styles.workTrain}>
               <Train
                 lessonPreTraining={{
-                  title: dayjs().format('YYYY年MM月DD日') + '-课作业'
+                  title: dayjs().format('YYYY年MM月DD日') + '-课作业'
                 }}
                 cardType={'homeworkRecord'}
                 onChange={(val: any) => {
@@ -516,6 +622,13 @@ export default defineComponent({
             </div>
           </div>
         </NModal>
+
+        {/* 应用内预览或上课 */}
+        <PreviewWindow
+          v-model:show={state.previewModal}
+          type="attend"
+          params={state.previewParams}
+        />
       </div>
     );
   }

+ 10 - 4
src/views/natural-resources/components/my-resources/upload-modal/index.module.less

@@ -38,8 +38,8 @@
       position: absolute;
       top: 6px;
       left: 6px;
-      width: 54px;
-      height: 26px;
+      width: 22px;
+      height: 22px;
       z-index: 99;
     }
 
@@ -59,18 +59,24 @@
       position: absolute;
       bottom: 0;
       left: 0;
-      font-size: 12px;
+      font-size: 12Px;
       font-weight: 600;
       color: #FFFFFF;
       height: 43px;
-      background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.7) 100%);
+      // background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.7) 100%);
       width: 100%;
       border-radius: 0 0 10px 10px;
       justify-content: flex-end;
       padding-right: 12px;
       z-index: 99;
+      background: rgba(0, 0, 0, 0.3);
+      backdrop-filter: blur(1px);
 
       :global {
+        .n-switch {
+          margin-left: 8px;
+        }
+
         .n-switch__rail {
           background-color: #ccc;
         }

+ 3 - 4
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);
             }
@@ -221,7 +220,7 @@ export default defineComponent({
                     />
 
                     <div class={styles.commonType}>
-                      是否公开
+                      是否公开
                       <NSwitch
                         size="small"
                         v-model:value={item.openFlag}

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

@@ -1,5 +1,25 @@
 .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);
+    //     width: 336px;
+    //   }
+    // }
+  }
 
   :global {
     .n-base-selection {
@@ -12,6 +32,69 @@
     .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 340px;
+
+    transition: all .1s ease;
+    position: absolute;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    transform: translateX(100%);
+
+
+    .presetsArrar {
+      position: absolute;
+      left: -23px;
+      top: 50%;
+      margin-top: -30px;
+      width: 23px;
+      height: 50px;
+      line-height: 50px;
+      cursor: pointer;
+      background-color: #F5F6FA;
+      border-radius: 10px 0 0 10px;
+      color: #ABAEB0;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      svg {
+        transform: rotate(180deg);
+      }
+
+      &:hover {
+        color: #198CFE;
+      }
+
+      &.presetsArrarActive {
+        svg {
+          transform: rotate(0deg);
+        }
+      }
+    }
   }
 }
 
@@ -21,7 +104,8 @@
 
 .coursewarePresets {
   max-height: calc(var(--window-page-lesson-height) - 100px);
-  padding: 0 20px 0px;
+  min-height: calc(var(--window-page-lesson-height) - 100px);
+  // padding: 0 20px 0px;
 }
 
 .btnSubjectList {
@@ -33,23 +117,41 @@
 }
 
 .addBtnIcon {
-  width: 13px !important;
-  height: 14px !important;
+  width: 18px !important;
+  height: 18px !important;
   margin-right: 8px;
 }
 
 .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;
+  height: max(38px, 32Px);
   font-size: max(18px, 13Px) !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);
+  }
 
+  &:hover {
+    // &::after {
+    background: linear-gradient(135deg, #62C7FF 0%, #4995FA 100%) !important;
+    // }
+  }
 }
 
 
@@ -142,17 +244,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;
   // height: 313px;
 
 
+  &.listEmpty {
+    min-height: calc(var(--window-page-lesson-height) - 100px);
+    align-items: center;
+  }
+
   &.listSame {
     margin-top: 0;
     padding-top: 0;
@@ -163,7 +272,8 @@
   }
 
   .itemWrap {
-    width: calc(100% / 4);
+    // width: calc(100% / 4);
+    width: 336px;
     // padding-bottom: calc(100% / 3 * 0.73333);
     // position: relative;
 
@@ -228,4 +338,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);
+  }
 }

+ 301 - 267
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,9 +44,18 @@ 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',
+  props: {
+    addParam: {
+      type: Object,
+      default: () => ({})
+    }
+  },
   emits: ['change'],
   setup(props, { emit }) {
     const prepareStore = usePrepareStore();
@@ -49,6 +68,8 @@ export default defineComponent({
 
     const forms = reactive({
       // 选取参数带的,后取缓存
+      leftWidth: '100%',
+      rightWidth: '0',
       messageLoading: false,
       subjectId: route.query.subjectId
         ? Number(route.query.subjectId)
@@ -63,12 +84,14 @@ export default defineComponent({
       openLoading: false,
       showRelatedClass: false,
       tableList: [] as any,
+      openTableShow: true, // 是否显示
       openTableList: [] as any,
       selectItem: {} as any,
       editTitleVisiable: false,
       editTitle: null,
       editBtnLoading: false,
       preRemoveVisiable: false,
+      addVisiable: false, // 是否有添加的课件
       carouselIndex: 0,
       showAttendClass: false,
       attendClassType: 'change', //
@@ -79,7 +102,8 @@ export default defineComponent({
         courseId: '',
         subjectId: '',
         detailId: ''
-      } as any
+      } as any,
+      workVisiable: false
     });
 
     const getCoursewareList = async () => {
@@ -87,6 +111,7 @@ export default defineComponent({
       try {
         // 判断是否有选择对应的课件 或声部
         if (!prepareStore.getSelectKey) return (forms.loading = false);
+
         const { data } = await teacherChapterLessonCoursewareList({
           subjectId: prepareStore.getSubjectId,
           coursewareDetailKnowledgeId: prepareStore.getSelectKey
@@ -100,13 +125,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 +143,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 () => {
+        eventGlobal.emit('openCoursewareChanged');
         await getCoursewareList();
-        await getOpenCoursewareList();
+        // await getOpenCoursewareList();
+
+        subjectRef.value?.syncBarPosition();
       }
     );
 
@@ -244,44 +226,59 @@ 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();
+
+      // console.log(props.addParam, 'addCourseware');
+      if (props.addParam.isAdd) {
+        forms.addVisiable = true;
+      }
     });
 
     // 重命名
-    const onEditTitleSubmit = async () => {
-      try {
-        await api_updateCoursewareInfo({
-          id: forms.selectItem.id,
-          name: forms.editTitle
-        });
-        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 () => {
@@ -292,7 +289,8 @@ export default defineComponent({
         });
         message.success('删除成功');
         getCoursewareList();
-        getOpenCoursewareList();
+        // getOpenCoursewareList();
+        eventGlobal.emit('openCoursewareChanged');
         forms.preRemoveVisiable = false;
       } catch {
         //
@@ -310,7 +308,8 @@ export default defineComponent({
         await api_addByOpenCourseware({ id: item.id });
         message.success('添加成功');
         getCoursewareList();
-        getOpenCoursewareList();
+        // getOpenCoursewareList();
+        eventGlobal.emit('openCoursewareChanged');
       } catch {
         //
       }
@@ -350,14 +349,18 @@ export default defineComponent({
       }
     };
 
-    const onStartClass = async (item: any, classGroupId: any) => {
+    const onStartClass = async (
+      item: any,
+      classGroupId: any,
+      subjectId?: any
+    ) => {
       if (classGroupId) {
         // 开始上课
         const res = await courseScheduleStart({
           lessonCoursewareKnowledgeDetailId: prepareStore.selectKey,
           classGroupId: classGroupId,
-          useChapterLessonCoursewareId: item.id,
-          subjectId: prepareStore.getSubjectId
+          useChapterLessonCoursewareId: item.id
+          // subjectId: prepareStore.getSubjectId
         });
         if (window.matchMedia('(display-mode: standalone)').matches) {
           state.application = window.matchMedia(
@@ -369,7 +372,7 @@ export default defineComponent({
             type: 'class',
             classGroupId: classGroupId,
             courseId: item.id,
-            subjectId: prepareStore.getSubjectId,
+            subjectId: subjectId || route.query.subjectId,
             detailId: prepareStore.getSelectKey,
             classId: res.data,
             lessonCourseId: prepareStore.getBaseCourseware.id,
@@ -398,194 +401,171 @@ 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=""
+            paneClass={styles.paneTitle}
+            justifyContent="start"
+            paneWrapperClass={styles.paneWrapperContainer}
+            value={
+              prepareStore.getSubjectId
+                ? 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;
+                          }}
+                        />
+                      </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 +588,7 @@ export default defineComponent({
           />
         </NModal>
 
-        <NModal
+        {/* <NModal
           v-model:show={forms.editTitleVisiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable1]}
@@ -638,7 +618,7 @@ export default defineComponent({
               </NButton>
             </NSpace>
           </div>
-        </NModal>
+        </NModal> */}
 
         <NModal
           v-model:show={forms.preRemoveVisiable}
@@ -655,6 +635,28 @@ export default defineComponent({
           />
         </NModal>
 
+        <NModal
+          v-model:show={forms.addVisiable}
+          preset="card"
+          class={['modalTitle', styles.removeVisiable1]}
+          title={'保存成功'}>
+          <TheMessageDialog
+            content={`<p style="text-align: left;">【${props.addParam.name}】暂未设置课件作业,是否现在去设置课件作业</p>`}
+            cancelButtonText="稍后设置"
+            confirmButtonText="立即设置"
+            // loading={forms.messageLoading}
+            onClose={() => (forms.addVisiable = false)}
+            onConfirm={() => {
+              forms.addVisiable = false;
+              forms.workVisiable = true;
+              forms.selectItem = {
+                id: props.addParam.id,
+                name: props.addParam.name
+              };
+            }}
+          />
+        </NModal>
+
         {/* 应用内预览或上课 */}
         <PreviewWindow
           v-model:show={forms.previewModal}
@@ -692,10 +694,42 @@ export default defineComponent({
               }
             }}
             onConfirm={async (item: any) => {
-              onStartClass(forms.attendClassItem, item.classGroupId);
+              onStartClass(
+                forms.attendClassItem,
+                item.classGroupId,
+                item.subjectId
+              );
             }}
           />
         </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: forms.selectItem.name + '-课后作业',
+                  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>
+    );
+  }
+});

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

@@ -0,0 +1,151 @@
+.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: 118px;
+    height: 66px;
+    margin-right: 10px;
+    border-radius: 12px;
+    border: 1px solid #EFF0F2;
+    overflow: hidden;
+
+    :global {
+      .n-image {
+        width: 118px;
+        height: 66px;
+      }
+    }
+
+    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;
+
+      &:hover {
+        color: #1677FF;
+        cursor: pointer;
+      }
+    }
+
+    .itemSubject {
+      padding-top: 7px;
+      width: 100%;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      font-size: 11Px;
+      color: #777777;
+      line-height: 15px;
+    }
+  }
+}

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

@@ -0,0 +1,161 @@
+import {
+  PropType,
+  defineComponent,
+  onMounted,
+  onUnmounted,
+  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
+        });
+        if (state.pagination.page === 1 && state.tableList.length > 0) {
+          state.tableList = [];
+        }
+        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);
+
+    const eventChanged = () => {
+      onSearch(state.searchGroup);
+    };
+    onMounted(() => {
+      getList();
+      eventGlobal.on('openCoursewareChanged', eventChanged);
+    });
+    onUnmounted(() => {
+      eventGlobal.off('openCoursewareChanged', eventChanged);
+    });
+    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} onClick={() => emit('look')}>
+            <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>
+      </>
+    );
+  }
+});

+ 0 - 5
src/views/prepare-lessons/components/lesson-main/courseware/addCourseware.module.less

@@ -471,11 +471,6 @@
   }
 }
 
-
-.subjectSyncModal {
-  width: 1070px;
-}
-
 .addOtherSource {
   width: 726px;
 }

+ 145 - 86
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';
@@ -25,31 +26,33 @@ import {
 import CardType from '/src/components/card-type';
 // import AttendClass from '/src/views/prepare-lessons/model/attend-class';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
-import { useCatchStore } from '/src/store/modules/catchData';
+// import { useCatchStore } from '/src/store/modules/catchData';
 // import TheEmpty from '/src/components/TheEmpty';
 import {
   api_teacherChapterLessonCoursewareAdd,
   api_teacherChapterLessonCoursewareUpdate,
-  api_teacherChapterLessonCoursewareDetail,
+  api_teacherChapterLessonCoursewareDetail
   // courseScheduleStart,
   // queryCourseware,
-  saveCourseware
+  // saveCourseware
 } from '../../../api';
 import Draggable from 'vuedraggable';
 import iconDelete from '../../../images/icon-delete-default.png';
 import iconAddMusic from '../../../images/icon-add-music.png';
-import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
+// import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
 // import deepClone from '/src/helpers/deep-clone';
 import CardPreview from '/src/components/card-preview';
 import PreviewWindow from '/src/views/preview-window';
 // import { state } from '/src/state';
-import SubjectSync from '../../../model/subject-sync';
+// import SubjectSync from '../../../model/subject-sync';
 import { eventGlobal } from '/src/utils';
 // import iconTips from '../../../images/icon-tips.png';
 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: {
@@ -60,10 +63,12 @@ export default defineComponent({
   },
   emits: ['change'],
   setup(props, { emit }) {
-    const catchStore = useCatchStore();
+    // const catchStore = useCatchStore();
+    const userStore = useUserStore();
+
     const prepareStore = usePrepareStore();
     // const route = useRoute();
-    const router = useRouter();
+    // const router = useRouter();
     // const dialog = useDialog();
     const message = useMessage();
 
@@ -72,6 +77,7 @@ export default defineComponent({
       openFlagEnable: true, // 是否支持修改公开状态
       name: '',
       openFlag: false,
+      createId: null,
       baseCoursewareList: [
         {
           name: '',
@@ -105,7 +111,6 @@ export default defineComponent({
         confirmButtonText: '确认',
         index: 0
       },
-      subjectSyncVisiable: false, // 同步声部
       show: false,
       item: {} as any,
       previewModal: false,
@@ -117,6 +122,7 @@ export default defineComponent({
       addOtherSource: false,
       addOtherIndex: 0 // 添加其它的索引
     });
+    const showModalMask = ref(false);
 
     // 获取列表
     const getList = async () => {
@@ -179,39 +185,39 @@ export default defineComponent({
     };
 
     // 完成编辑
-    const onOverEdit = async () => {
-      try {
-        const temp: any = [];
-        forms.coursewareList.forEach((item: any) => {
-          temp.push({
-            materialName: item.name,
-            materialType: item.type,
-            materialId: item.materialId,
-            id: item.id
-          });
-        });
-        // 保存课件
-        // 判断是否编辑,如果编辑则取选择的声部
-        await saveCourseware({
-          coursewareDetailKnowledgeId: prepareStore.getSelectKey,
-          lessonCoursewareId: prepareStore.getLessonCoursewareId,
-          lessonCoursewareDetailId: prepareStore.getLessonCoursewareDetailId,
-          // subjectId: forms.isEdit
-          //   ? forms.editSubjectIds
-          //   : prepareStore.getSubjectId,
-          materialList: [...temp]
-        });
+    // const onOverEdit = async () => {
+    //   try {
+    //     const temp: any = [];
+    //     forms.coursewareList.forEach((item: any) => {
+    //       temp.push({
+    //         materialName: item.name,
+    //         materialType: item.type,
+    //         materialId: item.materialId,
+    //         id: item.id
+    //       });
+    //     });
+    //     // 保存课件
+    //     // 判断是否编辑,如果编辑则取选择的声部
+    //     await saveCourseware({
+    //       coursewareDetailKnowledgeId: prepareStore.getSelectKey,
+    //       lessonCoursewareId: prepareStore.getLessonCoursewareId,
+    //       lessonCoursewareDetailId: prepareStore.getLessonCoursewareDetailId,
+    //       // subjectId: forms.isEdit
+    //       //   ? forms.editSubjectIds
+    //       //   : prepareStore.getSubjectId,
+    //       materialList: [...temp]
+    //     });
 
-        message.success('编辑成功');
-        // forms.removeVisiable = false;
-        prepareStore.setIsEditResource(false);
-        // 重置临时删除编号
-        forms.removeIds = [];
-        await getList();
-      } catch {
-        //
-      }
-    };
+    //     message.success('编辑成功');
+    //     // forms.removeVisiable = false;
+    //     prepareStore.setIsEditResource(false);
+    //     // 重置临时删除编号
+    //     forms.removeIds = [];
+    //     await getList();
+    //   } catch {
+    //     //
+    //   }
+    // };
 
     const isPointInsideElement = (element: any, x: number, y: number) => {
       const rect = element.getBoundingClientRect();
@@ -302,17 +308,25 @@ export default defineComponent({
         }
 
         forms.messageOperation.loading = true;
-        await onSaveCourseWare();
+        const resultStatus = await onSaveCourseWare();
         forms.messageOperation.loading = false;
-
-        if (
-          type === 'pageLive' &&
-          typeof forms.messageCallBack === 'function'
-        ) {
-          forms.messageCallBack();
+        if (resultStatus) {
+          if (
+            type === 'pageLive' &&
+            typeof forms.messageCallBack === 'function'
+          ) {
+            forms.messageCallBack();
+          }
+          emit('change', {
+            status: false,
+            addParam: {
+              isAdd: !props.groupItem.id ? true : false,
+              name: forms.name,
+              id: forms.createId
+            }
+          });
+          eventGlobal.emit('teacher-slideshow', false);
         }
-        emit('change', { status: false });
-        eventGlobal.emit('teacher-slideshow', false);
       }
       forms.messageOperation.visiable = false;
     };
@@ -361,6 +375,10 @@ export default defineComponent({
           message.error('请选择声部');
           return;
         }
+        if (forms.coursewareList.length <= 0) {
+          message.error('请至少添加一个知识点');
+          return;
+        }
 
         let isNotAdd = false;
         for (const item of forms.coursewareList) {
@@ -377,12 +395,29 @@ export default defineComponent({
           message.error('请至少添加一个资源');
           return;
         }
-        await onSaveCourseWare(true);
+
+        if (forms.openFlag && !userStore.getReadCoursewareOpenAgreement) {
+          showModalMask.value = true;
+          return;
+        }
+        const resultStatus = await onSaveCourseWare();
+
+        if (resultStatus) {
+          emit('change', {
+            status: false,
+            addParam: {
+              isAdd: !props.groupItem.id ? true : false,
+              name: forms.name,
+              id: forms.createId
+            }
+          });
+          eventGlobal.emit('teacher-slideshow', false);
+        }
       } catch {
         //
       }
     };
-    const onSaveCourseWare = async (hasBack = false) => {
+    const onSaveCourseWare = async () => {
       try {
         const params = {
           name: forms.name,
@@ -392,7 +427,6 @@ export default defineComponent({
           chapterKnowledgeList: [] as any
         };
 
-        console.log(forms.coursewareList, '121212');
         forms.coursewareList.forEach((item: any) => {
           let tempItem: any = [];
           if (Array.isArray(item.list) && item.list.length > 0) {
@@ -424,17 +458,16 @@ export default defineComponent({
             id: props.groupItem.id,
             ...params
           });
+          message.success('保存成功');
         } else {
-          await api_teacherChapterLessonCoursewareAdd(params);
+          const { data } = await api_teacherChapterLessonCoursewareAdd(params);
+          forms.createId = data.id;
         }
-        message.success('保存成功');
 
-        if (hasBack) {
-          emit('change', { status: false });
-          eventGlobal.emit('teacher-slideshow', false);
-        }
+        return true;
       } catch {
         //
+        return false;
       }
     };
 
@@ -469,7 +502,14 @@ export default defineComponent({
         if (typeof event === 'function') {
           event();
 
-          emit('change', { status: false });
+          emit('change', {
+            status: false,
+            addParam: {
+              isAdd: false,
+              name: forms.name,
+              id: forms.createId
+            }
+          });
           eventGlobal.emit('teacher-slideshow', false);
         }
       } else {
@@ -595,11 +635,20 @@ export default defineComponent({
           <NSpace>
             <NButton
               type="error"
+              bordered={false}
               onClick={() => {
                 const objA = JSON.stringify(forms.coursewareList);
                 const objB = JSON.stringify(forms.baseCoursewareList);
                 if (objA === objB) {
-                  emit('change', { status: false });
+                  emit('change', {
+                    status: false,
+                    addParam: {
+                      isAdd: false,
+                      name: forms.name,
+                      id: forms.createId
+                    }
+                  });
+
                   eventGlobal.emit('teacher-slideshow', false);
                 } else {
                   forms.messageOperation = {
@@ -794,7 +843,7 @@ export default defineComponent({
                                 }}>
                                 <img src={iconAddMusic} />
 
-                                <p class={styles.addMusicName}>添加功能</p>
+                                <p class={styles.addMusicName}>添加资源</p>
                               </div>
                             </div>
                           </div>
@@ -818,7 +867,7 @@ export default defineComponent({
                             }}>
                             <img src={iconAddMusic} />
 
-                            <p class={styles.addMusicName}>添加功能</p>
+                            <p class={styles.addMusicName}>添加资源</p>
                           </div>
                         </div>
                       </div>
@@ -902,7 +951,14 @@ export default defineComponent({
                 forms.messageOperation.type === 'save' ||
                 forms.messageOperation.type === 'pageLive'
               ) {
-                emit('change', { status: false });
+                emit('change', {
+                  status: false,
+                  addParam: {
+                    isAdd: false,
+                    name: forms.name,
+                    id: forms.createId
+                  }
+                });
                 eventGlobal.emit('teacher-slideshow', false);
                 if (
                   forms.messageOperation.type === 'pageLive' &&
@@ -922,34 +978,12 @@ export default defineComponent({
           params={forms.previewParams}
         />
 
-        {/* 完成编辑时,选择声部 */}
-        <NModal
-          v-model:show={forms.subjectSyncVisiable}
-          preset="card"
-          class={['modalTitle background', styles.subjectSyncModal]}
-          title={'同步声部'}>
-          <SubjectSync
-            subjectId={prepareStore.getSubjectId as any}
-            onClose={() => (forms.subjectSyncVisiable = false)}
-            onConfirm={async (subjectIds: any) => {
-              //
-              try {
-                forms.editSubjectIds = subjectIds.join(',');
-                await onOverEdit();
-                forms.subjectSyncVisiable = false;
-              } catch {
-                //
-              }
-            }}
-          />
-        </NModal>
-
         {/* 添加其它类型的资源 */}
         <NModal
           v-model:show={forms.addOtherSource}
           preset="card"
           class={['modalTitle background', styles.addOtherSource]}
-          title={'添加功能'}>
+          title={'添加资源'}>
           <AddOtherSource
             onClose={() => (forms.addOtherSource = false)}
             onComfirm={item => {
@@ -963,6 +997,31 @@ 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,
+                    addParam: {
+                      isAdd: !props.groupItem.id ? true : false,
+                      name: forms.name,
+                      id: forms.createId
+                    }
+                  });
+                  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;

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

@@ -15,6 +15,11 @@ export default defineComponent({
     const state = reactive({
       editCoursewareShow: false, // 是否编辑课件
       coursewareType: 'create' as 'create' | 'edit',
+      addParam: {
+        isAdd: false, // 是否添加
+        id: null,
+        addName: '' // 添加的名称
+      },
       editCourseware: {} as any, //
       editWorkShow: false, // 是否编辑预设
       editWork: {} as any // 预设模板编号
@@ -32,39 +37,42 @@ 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) => {
+                    if (val.addParam) {
+                      state.addParam = val.addParam;
+                    }
                     state.editCoursewareShow = val.status;
                     prepareStore.setIsEditResource(val.status);
                     if (!val.status) {
@@ -72,64 +80,60 @@ export default defineComponent({
                     }
                   }}
                 />
+              </NTabPane>
+            )}
+          </NTabs>
+        ) : (
+          <CoursewarePresets
+            addParam={state.addParam}
+            onChange={(val: any) => {
+              state.coursewareType = val.type;
+              state.editCoursewareShow = val.status;
+              prepareStore.setIsEditResource(val.status);
+              state.editCourseware = val.groupItem;
+              state.addParam = {
+                isAdd: false, // 是否添加
+                id: null,
+                addName: '' // 添加的名称
+              };
+            }}
+          />
+        )}
+
+        {/* {!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>
     );
   }

+ 10 - 2
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: () => ({})
@@ -44,7 +49,7 @@ export default defineComponent({
     },
     homeworkType: {
       type: String as PropType<'CLASSWORK' | 'HOMEWORK'>,
-      default: 'HOMEWORK'
+      default: 'CLASSWORK'
     }
   },
   emits: ['close', 'confirm'],
@@ -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 = '';
@@ -366,6 +373,7 @@ export default defineComponent({
             classGroupId={props.classGroupId}
             currentGradeNum={forms.currentGradeNum as any}
             selectIds={forms.selectIds}
+            studentList={forms.studentList}
             classList={forms.classList}
             onClose={() => (forms.workVisiable = false)}
             onConfirm={(val: any) => {

+ 3 - 3
src/views/prepare-lessons/components/lesson-main/train/assign-student/index.module.less

@@ -49,11 +49,11 @@
 
   .student {
     padding: 0 30px 15px;
-    height: 50vh;
+    height: 48vh;
   }
 
   .loadingSection {
-    height: calc(50vh - 15px);
+    height: calc(48vh - 15px);
   }
 
   .studentItem {
@@ -159,7 +159,7 @@
 
   .student {
     padding: 0 30px;
-    height: calc(50vh);
+    height: calc(48vh);
   }
 
   .studentItem {

+ 21 - 5
src/views/prepare-lessons/components/lesson-main/train/assign-student/index.tsx

@@ -27,6 +27,11 @@ export default defineComponent({
       type: Array,
       default: () => []
     },
+    /** 所选学生列表 */
+    studentList: {
+      type: Array,
+      default: () => []
+    },
     /** 学年 */
     currentGradeNum: {
       type: [String || Number],
@@ -60,7 +65,7 @@ export default defineComponent({
       },
       pagination: {
         page: 1,
-        rows: 10,
+        rows: 20,
         pageTotal: 0
       },
       tableList: [] as any,
@@ -131,7 +136,7 @@ export default defineComponent({
 
     // 切换学生状态
     const onCheckStudents = () => {
-      state.selectStudents = [];
+      // state.selectStudents = [];
       if (state.checkboxIds.length <= 0 || state.tableList.length <= 0) {
         state.indeterminate = false;
         state.checkAllStatus = false;
@@ -142,7 +147,10 @@ export default defineComponent({
       state.tableList.forEach((item: any) => {
         if (state.checkboxIds.includes(item.id)) {
           count++;
-          state.selectStudents.push(item);
+          const index = state.selectStudents.findIndex(
+            (select: any) => select.id === item.id
+          );
+          if (index === -1) state.selectStudents.push(item);
         }
       });
 
@@ -169,7 +177,8 @@ export default defineComponent({
       state.selectStudents.forEach((item: any) => {
         studentInfo.push({
           id: item.id,
-          name: item.nickname
+          name: item.nickname,
+          avatar: item.avatar
         });
       });
 
@@ -182,7 +191,14 @@ export default defineComponent({
       await catchStore.getSubjects();
       await getYearList();
       await getStudentLists();
-      onCheckStudents();
+      // onCheckStudents();
+      // 重置选择的学生
+      state.selectStudents = props.studentList?.map((item: any) => {
+        return {
+          ...item,
+          nickname: item.name
+        };
+      });
     });
     return () => (
       <div class={styles.assignStudent}>

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

@@ -2,9 +2,9 @@
   display: flex;
   align-items: center;
   justify-content: space-between;
-  padding-left: 22px !important;
+  padding-left: 36px !important;
   height: 40px;
-  padding-right: 22px !important;
+  padding-right: 36px !important;
 
 
   :global {
@@ -25,6 +25,7 @@
       font-size: max(18px, 13Px);
       font-weight: 600 !important;
       padding: 0 27px;
+      margin-left: 14px;
     }
 
     .n-button--default-type {
@@ -105,13 +106,13 @@
     font-weight: 600;
   }
 
-  &:last-child {
-    margin-left: 12px;
-  }
+  // &:last-child {
+  //   margin-left: 12px;
+  // }
 
   :global {
     .n-input {
-      --n-height: 38px !important;
+      --n-height: 42px !important;
       width: 280px;
     }
   }
@@ -147,14 +148,14 @@
 }
 
 .list {
-  padding: 12px 22px 12px;
+  padding: 12px 36px 12px;
   display: flex;
   flex-flow: row wrap;
   justify-content: flex-start;
   gap: 20px 2%;
 
   &>div {
-    width: 49% !important;
+    width: 32% !important;
   }
 
   .itemBlock {

+ 67 - 60
src/views/prepare-lessons/components/lesson-main/train/index.tsx

@@ -39,6 +39,7 @@ import { eventGlobal } from '/src/utils';
 import iconTips from '../../../images/icon-tips.png';
 import { typeFormat } from '../../resource-main/components/select-music';
 import TheMessageDialog from '/src/components/TheMessageDialog';
+import { useResizeObserver } from '@vueuse/core';
 export default defineComponent({
   name: 'courseware-modal',
   props: {
@@ -47,7 +48,7 @@ export default defineComponent({
       default: () => ({})
     },
     cardType: {
-      type: String as PropType<'' | 'homeworkRecord'>,
+      type: String as PropType<'' | 'homeworkRecord' | 'prepare'>,
       default: ''
     },
     /** 编辑编号  - 目前从上传传 */
@@ -68,6 +69,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 +86,6 @@ export default defineComponent({
       editStatus: false,
       editItem: {} as any,
       removeIds: [] as any, // 临时删除的编号
-
       removeVisiable1: false,
       preSaveVisiable: false
     });
@@ -94,13 +95,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 +142,6 @@ export default defineComponent({
       forms.loadingStatus = false;
     };
 
-    // // 声部变化时
-    // watch(
-    //   () => prepareStore.getSubjectId,
-    //   () => {
-    //     getList();
-    //   }
-    // );
     // 监听选择的key 左侧选择了其它的课
     watch(
       () => prepareStore.getSelectKey,
@@ -159,7 +154,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 +197,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();
@@ -216,7 +211,19 @@ export default defineComponent({
       forms.preBtnLoading = false;
     };
 
+    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');
+        });
+      }
+    };
+
     onMounted(async () => {
+      getModalHeight();
       await getList();
       // 动态添加数据
       eventGlobal.on('onTrainAddItem', (item: any) => {
@@ -237,7 +244,9 @@ export default defineComponent({
         <div class={styles.btnGroup}>
           <NSpace>
             <div class={styles.btnItem}>
-              <span class={styles.btnTitle}>标题:</span>
+              <span class={styles.btnTitle}>
+                <i style={{ color: '#ea4132', fontStyle: 'normal' }}>*</i>标题:
+              </span>
               <NInput
                 placeholder={'请输入标题'}
                 v-model:value={forms.title}
@@ -246,8 +255,9 @@ export default defineComponent({
             </div>
           </NSpace>
 
-          <NSpace>
+          <div class={styles.spaceBtnGroup}>
             <NButton
+              bordered={false}
               type="error"
               disabled={forms.trainList.length <= 0}
               onClick={() => {
@@ -256,23 +266,30 @@ export default defineComponent({
               清空
             </NButton>
             <NButton
+              bordered={false}
               type="error"
               onClick={() => {
                 // forms.drag = false;
                 prepareStore.setIsEditTrain(false);
                 forms.removeIds = [];
-                prepareStore.setTrainList([]);
+                // prepareStore.setTrainList([]);
                 // getList();
                 emit('change', { status: false });
               }}>
               取消
             </NButton>
 
-            {props.cardType === 'homeworkRecord' ? (
+            {(props.cardType !== 'homeworkRecord' ||
+              props.courseScheduleId) && (
               <NButton
-                type="primary"
+                bordered={false}
+                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) {
@@ -283,23 +300,40 @@ export default defineComponent({
                     message.error('作业内容不能为空');
                     return;
                   }
-                  forms.assignHomeworkStatus = true;
-                }}>
-                立即布置
-              </NButton>
-            ) : (
-              <NButton
-                type="default"
-                disabled={forms.trainList.length <= 0}
-                onClick={() => {
+
                   forms.preSaveVisiable = true;
                 }}
                 // loading={forms.preBtnLoading}
               >
-                保存预设
+                保存
               </NButton>
             )}
-          </NSpace>
+            {(props.cardType === 'homeworkRecord' ||
+              props.courseScheduleId) && (
+              <NButton
+                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) {
+                      count++;
+                    }
+                  });
+                  if (count <= 0) {
+                    message.error('作业内容不能为空');
+                    return;
+                  }
+                  forms.assignHomeworkStatus = true;
+                }}>
+                立即布置
+              </NButton>
+            )}
+          </div>
         </div>
 
         <NScrollbar
@@ -415,35 +449,6 @@ 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++;
-                    }
-                  });
-                  if (count <= 0) {
-                    message.error('作业内容不能为空');
-                    return;
-                  }
-                  forms.assignHomeworkStatus = true;
-                }}>
-                立即布置
-              </NButton>
-            </NSpace>
-          </div>
-        )}
-
         {/* 编辑 */}
         <NModal
           v-model:show={forms.editStatus}
@@ -491,6 +496,8 @@ export default defineComponent({
           <AssignHomework
             classGroupId={props.classGroupId}
             courseScheduleId={props.courseScheduleId}
+            chapterLessonCoursewareId={props.lessonPreTraining.chapterId}
+            homeworkType={props.courseScheduleId ? 'HOMEWORK' : 'CLASSWORK'}
             item={{
               title: forms.title,
               lessonPreTrainingDetails: forms.trainList
@@ -545,10 +552,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()}

+ 22 - 19
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;
@@ -116,8 +119,8 @@ export default defineComponent({
             isCollect: !!row.favoriteFlag,
             isSelected: row.sourceFrom === 'PLATFORM' ? true : false,
             content: row.id,
-            xmlFileUrl: row.xmlFileUrl,
-            exist: index !== -1 ? true : false // 是否存在
+            xmlFileUrl: row.xmlFileUrl
+            // exist: index !== -1 ? true : false // 是否存在
           });
         });
         state.tableList.push(...temp);
@@ -141,21 +144,21 @@ export default defineComponent({
         onSearch(state.searchGroup);
       }
     );
-    watch(
-      () => prepareStore.trainList,
-      () => {
-        state.tableList.forEach((item: any) => {
-          const index = prepareStore.getTrainList.findIndex(
-            (course: any) => course.musicId === item.id
-          );
-          item.exist = index !== -1 ? true : false; // 是否存在
-        });
-      },
-      {
-        deep: true,
-        immediate: true
-      }
-    );
+    // watch(
+    //   () => prepareStore.trainList,
+    //   () => {
+    //     state.tableList.forEach((item: any) => {
+    //       const index = prepareStore.getTrainList.findIndex(
+    //         (course: any) => course.musicId === item.id
+    //       );
+    //       item.exist = index !== -1 ? true : false; // 是否存在
+    //     });
+    //   },
+    //   {
+    //     deep: true,
+    //     immediate: true
+    //   }
+    // );
 
     const throttledFn = useThrottleFn(() => {
       state.pagination.page = state.pagination.page + 1;

+ 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

二進制
src/views/prepare-lessons/images/active-arrow.png


二進制
src/views/prepare-lessons/images/addSource/icon8.png


二進制
src/views/prepare-lessons/images/default-arrow.png


二進制
src/views/prepare-lessons/images/icon-add.png


二進制
src/views/prepare-lessons/images/icon-look.png


二進制
src/views/prepare-lessons/images/icon-menu-active.png


二進制
src/views/prepare-lessons/images/icon-menu-default.png


二進制
src/views/prepare-lessons/images/icon-no-work.png


二進制
src/views/prepare-lessons/images/protocol/check-active.png


二進制
src/views/prepare-lessons/images/protocol/check-default.png


二進制
src/views/prepare-lessons/images/protocol/closeAble.png


二進制
src/views/prepare-lessons/images/protocol/dingPng.png


二進制
src/views/prepare-lessons/images/protocol/moveTop.png


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

@@ -0,0 +1,180 @@
+.protocolModal {
+  position: relative;
+
+  .dingPng {
+    width: 162px;
+    height: 98px;
+    position: absolute;
+    left: 50%;
+    margin-left: -81px;
+    top: -49px;
+    z-index: 100;
+  }
+}
+
+.downMove {
+  width: 533px;
+  // height: 309px;
+  background: #FFFFFF;
+  border-radius: 16px;
+  position: relative;
+  padding: 0 40px 35px;
+  overflow: hidden;
+
+  .downMoveBg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 533px;
+    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: max(18px, 13Px);
+    font-family: PingFangSC-Regular, PingFang SC;
+    font-weight: 400;
+    color: #777777;
+    line-height: 30px;
+    text-align: justify;
+  }
+
+  .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;
+  // padding-bottom: 4px;
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+
+  p {
+    font-size: max(16px, 12Px);
+    // line-height: 20px;
+    color: #333333;
+  }
+
+  .checkbox {
+    // margin-top: 5px;
+    margin-right: 5px;
+    width: 15Px;
+    height: 15Px;
+    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 0 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 {
+    padding: 0 50px;
+    font-size: max(18px, 13Px);
+    line-height: 30px;
+    color: #777;
+    // text-align: justify;
+
+    p {
+      font-weight: 600;
+      color: #333333;
+      padding-top: 20px;
+    }
+
+    .child {
+      padding-left: 28px;
+    }
+  }
+
+
+
+  .submitAppBtn,
+  .cancelBtn {
+    min-width: 150px;
+    height: 45px;
+    line-height: 45px;
+    border-radius: 24px;
+    border: none;
+  }
+
+  .submitAppBtn {
+    background: linear-gradient(305deg, #40C8FF 0%, #3192FF 100%)
+  }
+}

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

@@ -0,0 +1,240 @@
+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"
+                onClick={() => emit('close')}>
+                取消
+              </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();
+                forms.protocolShow = false;
+              }}
+              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>
+    );
+  }
+});

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

@@ -66,4 +66,28 @@
 .theoryModal {
   width: 1360px;
   position: relative;
+}
+
+.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;
+    }
+  }
 }

+ 157 - 11
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';
@@ -15,18 +16,30 @@ import SourceKnowledge from '../source-knowledge';
 import SourceMusician from '../source-musician';
 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',
   emits: ['close', 'comfirm'],
   setup(props, { emit }) {
+    const prepareStore = usePrepareStore();
     const router = useRouter();
     const sourceList = ref([
-      // {
-      //   image: icon1,
-      //   name: '听音练习',
-      //   index: 0
-      // },
+      {
+        image: icon8,
+        name: '上传资源',
+        index: 7
+      },
+      {
+        image: icon1,
+        name: '听音练习',
+        index: 0
+      },
       {
         image: icon2,
         name: '节奏练习',
@@ -51,12 +64,13 @@ export default defineComponent({
         image: icon4,
         name: '乐理知识',
         index: 5
-      },
-      {
-        image: icon7,
-        name: '制作曲谱',
-        index: 6
       }
+
+      // {
+      //   image: icon7,
+      //   name: '制作曲谱',
+      //   index: 6
+      // }
     ]);
     const state = reactive({
       listenStatus: false, // 听音练习
@@ -64,12 +78,23 @@ 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) => {
       switch (item.index) {
+        case 0:
+          state.listenStatus = true;
+          break;
         case 1:
           state.rhythmStatus = true;
           break;
@@ -92,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;
       }
@@ -261,6 +294,119 @@ export default defineComponent({
             }}
           />
         </NModal>
+
+        {/* 听音练习 */}
+        <NModal
+          v-model:show={state.listenStatus}
+          preset="card"
+          class={['modalTitle background', styles.subjectSyncModal]}
+          title={'选择乐器'}>
+          <SubjectSync
+            // subjectId={prepareStore.getSubjectId as any}
+            onClose={() => (state.listenStatus = false)}
+            onConfirm={async (item: any) => {
+              //
+              try {
+                state.musicStatus = false;
+                const value = item.subjectCode || [];
+                const temp: any[] = [];
+                value.forEach((item: any) => {
+                  temp.push({
+                    materialId: item.materialId,
+                    coverImg: item.coverImg,
+                    dataJson: null,
+                    title: item.title,
+                    isCollect: false,
+                    isSelected: false,
+                    content: item.content,
+                    type: 'LISTEN'
+                  });
+                });
+                state.listenStatus = false;
+                emit('comfirm', temp);
+                emit('close');
+              } catch {
+                //
+              }
+            }}
+          />
+        </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>
       </>
     );
   }

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

@@ -1,8 +1,21 @@
 .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;
+
+    &:hover .footer {
+      opacity: 1;
+      visibility: visible;
+      transition: all .2s ease;
+    }
+  }
 
   &.coursewareTypeHover:hover {
     .addBtn {
@@ -53,13 +66,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 .1s ease;
+      }
+    }
+
+    .iconNoWork {
       position: absolute;
+      right: 0;
       top: 0;
-      left: 0;
+      display: inline-block;
+      width: 97px;
+      height: 26px;
+      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 +148,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 +262,234 @@
   }
 }
 
+.operationBottom {
+  padding-bottom: 16px;
+
+
+}
+
+
 .footer {
+  opacity: 0;
+  visibility: hidden;
   display: flex;
   align-items: center;
   padding-top: 10px;
+  padding-bottom: 8px;
+  position: absolute;
+  left: 10px;
+  right: 10px;
+  bottom: 12px;
+  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;
+    margin: 3px 0;
+
+    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.1s ease;
 
-    .iconDelete {
-      background: url('../../images/icon-courseware-delete.png') no-repeat center;
+  .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;
+
+  }
+
+  .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;
     }
   }
+
 }

+ 88 - 47
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,85 @@ 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>
-              <NTooltip showArrow={false}>
+          {/* 是否布置作业 */}
+          {props.item.isNotWork && <i class={styles.iconNoWork}></i>}
+          {/* 是否有添加逻辑 */}
+          {props.isShowAdd && (
+            <div class={styles.function}>
+              <NTooltip showArrow={false} duration={30} animated={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}>
+              <NTooltip showArrow={false} duration={30} animated={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}
+                  duration={50}>
+                  {{
+                    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>
     );
   }

+ 6 - 5
src/views/prepare-lessons/model/related-class/index.module.less

@@ -7,8 +7,8 @@
   display: flex;
   align-items: center;
   gap: 0 24px;
-  margin-bottom: 28px;
-  padding: 0 40px;
+  margin-bottom: 18px;
+  padding: 0 50px;
 
   :global {
 
@@ -24,7 +24,7 @@
 
     .n-input,
     .n-select {
-      max-width: 250px;
+      max-width: 280px;
     }
   }
 
@@ -37,10 +37,10 @@
 .classList {
   max-height: 60vh;
   min-height: 60vh;
-  padding: 0 40px;
 
   .listSection {
     min-height: 60vh;
+    padding: 0 40px 0;
   }
 
   .emptySection {
@@ -53,7 +53,8 @@
   display: flex;
   flex-flow: row wrap;
   justify-content: flex-start;
-  padding: 0 0 12px;
+
+  padding: 12px 0 12px;
   gap: 20px 0;
   margin: 0 -10px 0;
   min-height: 202px;

+ 45 - 37
src/views/prepare-lessons/model/related-class/index.tsx

@@ -1,10 +1,11 @@
-import { defineComponent, onMounted, reactive, watch } from 'vue';
+import { defineComponent, onMounted, onUnmounted, reactive, watch } from 'vue';
 import styles from './index.module.less';
 import { NInput, NScrollbar, NSelect, NSpin } from 'naive-ui';
 import { useThrottleFn } from '@vueuse/core';
 import CoursewareType from '../courseware-type';
 import TheEmpty from '/src/components/TheEmpty';
 import { api_queryOpenCoursewareByPage } from '../../api';
+import { eventGlobal } from '/src/utils';
 
 export default defineComponent({
   name: 'related-class',
@@ -37,7 +38,7 @@ export default defineComponent({
       },
       tableList: [] as any,
       searchGroup: {
-        subjectId: props.subjectId,
+        subjectId: props.subjectId ? Number(props.subjectId) : '',
         keyword: null
       }
     });
@@ -69,7 +70,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,26 +85,32 @@ 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.pagination.page = 1;
       forms.tableList = [];
       getList();
     }, 500);
 
     onMounted(() => {
       getList();
+      eventGlobal.on('openCoursewareChanged', throttleFn);
+    });
+
+    onUnmounted(() => {
+      eventGlobal.off('openCoursewareChanged', throttleFn);
     });
     return () => (
       <div class={styles.relatedClass}>
@@ -140,25 +148,25 @@ export default defineComponent({
           </NInput>
         </div>
 
-        <NScrollbar
-          class={styles.classList}
-          style={{
-            'max-height': `60vh`
-          }}
-          onScroll={(e: any) => {
-            const clientHeight = e.target?.clientHeight;
-            const scrollTop = e.target?.scrollTop;
-            const scrollHeight = e.target?.scrollHeight;
-            // 是否到底,是否加载完
-            if (
-              clientHeight + scrollTop + 20 >= scrollHeight &&
-              !forms.finshed &&
-              !forms.loading
-            ) {
-              throttleFn();
-            }
-          }}>
-          <NSpin show={forms.loading} size={'small'}>
+        <NSpin show={forms.loading} size={'small'}>
+          <NScrollbar
+            class={styles.classList}
+            style={{
+              'max-height': `60vh`
+            }}
+            onScroll={(e: any) => {
+              const clientHeight = e.target?.clientHeight;
+              const scrollTop = e.target?.scrollTop;
+              const scrollHeight = e.target?.scrollHeight;
+              // 是否到底,是否加载完
+              if (
+                clientHeight + scrollTop + 20 >= scrollHeight &&
+                !forms.finshed &&
+                !forms.loading
+              ) {
+                throttleFn();
+              }
+            }}>
             <div
               style={{
                 'min-height': `60vh)`
@@ -182,7 +190,7 @@ export default defineComponent({
                           onAdd={() => {
                             emit('add', item);
                           }}
-                          onClick={() => emit('click', item)}
+                          onLook={() => emit('click', item)}
                         />
                       </div>
                     </div>
@@ -191,8 +199,8 @@ export default defineComponent({
               )}
               {!forms.loading && forms.tableList.length <= 0 && <TheEmpty />}
             </div>
-          </NSpin>
-        </NScrollbar>
+          </NScrollbar>
+        </NSpin>
       </div>
     );
   }

+ 1 - 1
src/views/prepare-lessons/model/subject-sync/index.module.less

@@ -28,7 +28,7 @@
   display: flex;
   align-items: center;
   flex-wrap: wrap;
-  padding: 0 30px;
+  padding: 40px 30px 0;
   gap: 20px 40px;
 }
 

+ 95 - 46
src/views/prepare-lessons/model/subject-sync/index.tsx

@@ -1,9 +1,10 @@
 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';
+import { PageEnum } from '/src/enums/pageEnum';
 
 export default defineComponent({
   name: 'subject-sync',
@@ -21,72 +22,120 @@ export default defineComponent({
     const selectSubjectIds = ref([] as any);
     const subjectList = ref([] as any);
 
+    const subjectImgs = {
+      Panpipes: 'https://oss.dayaedu.com/ktqy/17103860536976fd4a751.png',
+      Ocarina: 'https://oss.dayaedu.com/ktqy/171038605369851874b22.png',
+      Woodwind: 'https://oss.dayaedu.com/ktqy/17103860536966826c50d.png',
+      'Tenor Recorder':
+        'https://oss.dayaedu.com/ktqy/17103860536950592e357.png',
+      Nai: 'https://oss.dayaedu.com/ktqy/1710386053697af4aa985.png',
+      'Baroque Recorder':
+        'https://oss.dayaedu.com/ktqy/1710386053698031e847a.png'
+    } as any;
+    /*
+      https://oss.dayaedu.com/ktqy/17103860536950592e357.png
+      https://oss.dayaedu.com/ktqy/17103860536966826c50d.png
+      https://oss.dayaedu.com/ktqy/1710386053697af4aa985.png
+      https://oss.dayaedu.com/ktqy/17103860536976fd4a751.png
+      https://oss.dayaedu.com/ktqy/171038605369851874b22.png
+      https://oss.dayaedu.com/ktqy/1710386053698031e847a.png
+    */
     const onSubmit = () => {
       if (selectSubjectIds.value.length <= 0) {
         message.error('至少选择一个声部进行同步');
         return;
       }
 
-      emit('confirm', selectSubjectIds.value);
-    };
-    onMounted(async () => {
-      // 获取教材分类列表
-      await catchStore.getSubjects();
-
-      const baseAllSubjectList = catchStore.getSubjectList;
-      const teachingSubjectList = prepareStore.getSubjectList; // 教材自带声部;
-      const tempSubjectList: any = [];
-      baseAllSubjectList.forEach((subject: any) => {
-        const index = teachingSubjectList.findIndex(
-          (t: any) => t.id == subject.id
+      const subjectCode: any[] = [];
+      // subjectList.value.forEach((subject: any) => {
+      //   if (selectSubjectIds.value.includes(subject.id)) {
+      //     subjectCode.push({
+      //       materialId: subject.id,
+      //       coverImg: subjectImgs[subject.code] || subjectImgs.Panpipes,
+      //       dataJson: null,
+      //       title: subject.name,
+      //       isCollect: false,
+      //       isSelected: false,
+      //       content: subject.code
+      //     });
+      //   }
+      // });
+      selectSubjectIds.value.forEach((id: any) => {
+        const item = subjectList.value.find(
+          (subject: any) => subject.id === id
         );
-        if (index != -1) {
-          tempSubjectList.push(subject);
+        if (item) {
+          subjectCode.push({
+            materialId: item.id,
+            coverImg: subjectImgs[item.code] || subjectImgs.Panpipes,
+            dataJson: null,
+            title: item.name,
+            isCollect: false,
+            isSelected: false,
+            content: item.code
+          });
         }
       });
+      emit('confirm', { subjectIds: selectSubjectIds.value, subjectCode });
+    };
+    onMounted(async () => {
+      // 获取教材分类列表
+      await catchStore.getMusicInstrument();
+      subjectList.value = catchStore.getMusicInstruments;
+      // const teachingSubjectList = prepareStore.getSubjectList; // 教材自带声部;
+      // const tempSubjectList: any = [];
+      // baseAllSubjectList.forEach((subject: any) => {
+      //   const index = teachingSubjectList.findIndex(
+      //     (t: any) => t.id == subject.id
+      //   );
+      //   if (index != -1) {
+      //     tempSubjectList.push(subject);
+      //   }
+      // });
 
-      subjectList.value = tempSubjectList;
-
+      // subjectList.value = tempSubjectList;
       if (props.subjectId) {
         selectSubjectIds.value = [Number(props.subjectId)];
       }
     });
     return () => (
       <div class={styles.subjectSync}>
-        <div class={styles.tips}>
+        {/* <div class={styles.tips}>
           请选择当前课件可使用的乐器
           <span>(勾选后则对应乐器下的课件内容将被当前课件内容全部替换)</span>
-        </div>
+        </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(() => {