Преглед изворни кода

Merge remote-tracking branch 'origin/iteration-class' into startLogin

mo пре 1 година
родитељ
комит
be0df362a0
65 измењених фајлова са 2405 додато и 979 уклоњено
  1. 3 3
      src/components/card-type/index.module.less
  2. 4 2
      src/components/layout/index.module.less
  3. 10 20
      src/components/layout/index.tsx
  4. 23 1
      src/components/layout/modals/update-password.module.less
  5. 44 9
      src/components/layout/modals/update-password.tsx
  6. 1 1
      src/router/router-guards.ts
  7. 9 0
      src/store/modules/prepareLessons.ts
  8. BIN
      src/views/attend-class/image/icon-change.png
  9. BIN
      src/views/attend-class/image/icon-down.png
  10. BIN
      src/views/attend-class/image/icon-up.png
  11. 12 2
      src/views/attend-class/index.module.less
  12. 267 170
      src/views/attend-class/index.tsx
  13. BIN
      src/views/attend-class/model/chapter/images/arrow-active.png
  14. BIN
      src/views/attend-class/model/chapter/images/arrow-default.png
  15. 39 0
      src/views/attend-class/model/chapter/images/icon-custom.svg
  16. BIN
      src/views/attend-class/model/chapter/images/icon-d-active.png
  17. BIN
      src/views/attend-class/model/chapter/images/icon-d.png
  18. BIN
      src/views/attend-class/model/chapter/images/icon-upload-bg.png
  19. 40 0
      src/views/attend-class/model/chapter/images/icon_default.svg
  20. 93 0
      src/views/attend-class/model/chapter/index.module.less
  21. 145 0
      src/views/attend-class/model/chapter/index.tsx
  22. 7 1
      src/views/attend-class/model/train-settings/index.tsx
  23. 25 0
      src/views/classList/api.ts
  24. 113 51
      src/views/classList/index.tsx
  25. 3 1
      src/views/classList/modals/Gotoclass.tsx
  26. 23 3
      src/views/classList/modals/createClass.tsx
  27. 106 0
      src/views/classList/modals/resetSubject.tsx
  28. 109 0
      src/views/classList/modals/updateSubject.tsx
  29. BIN
      src/views/home/images/home/home-1.png
  30. BIN
      src/views/home/images/home/home-2.png
  31. BIN
      src/views/home/images/home/home-bg-1.png
  32. BIN
      src/views/home/images/home/home-bg-2.png
  33. BIN
      src/views/home/images/home/home-text-1.png
  34. BIN
      src/views/home/images/home/home-text-2.png
  35. 50 80
      src/views/home/index.module.less
  36. 99 357
      src/views/home/index.tsx
  37. 3 1
      src/views/home/modals/chioseModal.tsx
  38. 1 1
      src/views/login/index.module.less
  39. 110 83
      src/views/login/index.tsx
  40. 40 4
      src/views/natural-resources/model/add-teaching/index.tsx
  41. 11 1
      src/views/prepare-lessons/api.ts
  42. 1 1
      src/views/prepare-lessons/components/directory-main/index.module.less
  43. 34 3
      src/views/prepare-lessons/components/directory-main/index.tsx
  44. 18 1
      src/views/prepare-lessons/components/directory-main/select-lessonware/index.tsx
  45. 108 3
      src/views/prepare-lessons/components/lesson-main/courseware/index.module.less
  46. 316 85
      src/views/prepare-lessons/components/lesson-main/courseware/index.tsx
  47. 3 2
      src/views/prepare-lessons/components/lesson-main/index.tsx
  48. 39 0
      src/views/prepare-lessons/components/lesson-main/train/index.module.less
  49. 43 7
      src/views/prepare-lessons/components/lesson-main/train/index.tsx
  50. 40 25
      src/views/prepare-lessons/components/resource-main/components/resource-item/index.tsx
  51. 1 1
      src/views/prepare-lessons/components/resource-main/components/select-music/index.tsx
  52. BIN
      src/views/prepare-lessons/images/icon-class-name.png
  53. BIN
      src/views/prepare-lessons/images/icon-select.png
  54. BIN
      src/views/prepare-lessons/images/icon-subject-name.png
  55. 14 8
      src/views/prepare-lessons/index.tsx
  56. 28 1
      src/views/prepare-lessons/model/attend-class/index.module.less
  57. 74 14
      src/views/prepare-lessons/model/attend-class/index.tsx
  58. 14 2
      src/views/prepare-lessons/model/select-music/index.tsx
  59. 42 30
      src/views/prepare-lessons/model/select-resources/select-item/index.tsx
  60. 29 1
      src/views/prepare-lessons/model/select-resources/select-item/resource-search-group/index.tsx
  61. 75 0
      src/views/prepare-lessons/model/subject-sync/index.module.less
  62. 102 0
      src/views/prepare-lessons/model/subject-sync/index.tsx
  63. 2 1
      src/views/preview-window/index.tsx
  64. 30 1
      src/views/xiaoku-ai/index.tsx
  65. 2 2
      vite.config.ts

+ 3 - 3
src/components/card-type/index.module.less

@@ -142,8 +142,8 @@
     font-weight: 600;
     height: 32px;
     border-radius: 8px;
-    display: none;
-    opacity: 0;
+    // display: none;
+    // opacity: 0;
     z-index: 99;
     transition: all .3s ease-in-out;
 
@@ -188,4 +188,4 @@
       min-width: 124px;
     }
   }
-}
+}

+ 4 - 2
src/components/layout/index.module.less

@@ -451,7 +451,9 @@
 
 
 .showClass {
-  width: 520px;
+  width: 800px;
+  border-radius: 16px;
+  overflow: hidden;
 }
 
 
@@ -473,4 +475,4 @@
     }
   }
 
-}
+}

+ 10 - 20
src/components/layout/index.tsx

@@ -30,6 +30,7 @@ import PlaceholderTone from './modals/placeholderTone';
 import { state } from '/src/state';
 import PreviewWindow from '/src/views/preview-window';
 import { fscreen } from '@/utils/index';
+import AttendClass from '/src/views/prepare-lessons/model/attend-class';
 export default defineComponent({
   name: 'layoutView',
   setup() {
@@ -400,7 +401,6 @@ export default defineComponent({
           }
         }
         rate(target, 0);
-
       });
       target.addEventListener('mouseout', () => {
         if (wrapInfo.isBoundary) {
@@ -680,27 +680,17 @@ export default defineComponent({
           class={['modalTitle background', styles.showClass]}
           preset="card"
           title={'开始上课'}>
-          <ChioseModal
+          <AttendClass
             onClose={() => (showClass.value = false)}
-            onPreview={(item: any) => {
-              if (window.matchMedia('(display-mode: standalone)').matches) {
-                previewModal.value = true;
-                previewItem.value = {
+            type="change"
+            onConfirm={(item: any) => {
+              showClass.value = false;
+              router.push({
+                path: '/prepare-lessons',
+                query: {
                   ...item
-                };
-                state.application = window.matchMedia(
-                  '(display-mode: standalone)'
-                ).matches;
-                fscreen();
-              } else {
-                const { href } = router.resolve({
-                  path: '/attend-class',
-                  query: {
-                    ...item
-                  }
-                });
-                window.open(href, +new Date() + '');
-              }
+                }
+              });
             }}
           />
         </NModal>

+ 23 - 1
src/components/layout/modals/update-password.module.less

@@ -1,3 +1,18 @@
+.no-pwd {
+  font-family: 'dotfont';
+
+  :global {
+    .n-input__input-el {
+      -webkit-text-security: disc !important;
+      -moz-text-security: disc !important;
+    }
+
+    .n-input__placeholder span {
+      margin-top: -5px;
+    }
+  }
+}
+
 .updatePassword {
   padding: 0px 40px 30px;
 
@@ -7,6 +22,13 @@
     color: #777777;
   }
 
+
+  .pwdIcon {
+    width: 24px;
+    height: 24px;
+    cursor: pointer;
+  }
+
   .phoneContainer {
     :global {
       .n-form-item-feedback-wrapper {
@@ -60,4 +82,4 @@
       line-height: 62px;
     }
   }
-}
+}

+ 44 - 9
src/components/layout/modals/update-password.tsx

@@ -13,7 +13,9 @@ import { useRouter } from 'vue-router';
 import { useUserStore } from '/src/store/modules/users';
 import { gradeToCN } from '/src/utils/contants';
 import { sendSms } from '/src/views/login/api';
-import { updatePassword } from '@/views/home/api'
+import { updatePassword } from '@/views/home/api';
+import openEye from '/src/views/login/images/openEye.png';
+import closeEye from '/src/views/login/images/closeEye.png';
 export default defineComponent({
   name: 'train-update',
   emits: ['close', 'submit'],
@@ -28,6 +30,11 @@ export default defineComponent({
       code: null
     });
 
+    const password = reactive({
+      passowrdStatus: false,
+      rePasswordStatus: false
+    });
+
     const validatePass2 = (rule: any, value: any, callback: any): any => {
       const reg = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/;
       if (value === '' || !value) {
@@ -143,10 +150,24 @@ export default defineComponent({
             <NInput
               v-model:value={forms.password}
               clearable
-              type="password"
-              show-password-on="click"
-              placeholder={'请输入新密码'}
-            />
+              type="text"
+              showPasswordOn="click"
+              inputProps={{ autocomplete: 'off' }}
+              class={[password.passowrdStatus ? '' : styles['no-pwd']]}
+              placeholder={'请输入新密码'}>
+              {{
+                suffix: () => (
+                  <img
+                    src={password.passowrdStatus ? openEye : closeEye}
+                    class={styles.pwdIcon}
+                    alt=""
+                    onClick={() => {
+                      password.passowrdStatus = !password.passowrdStatus;
+                    }}
+                  />
+                )
+              }}
+            </NInput>
           </NFormItem>
           <NFormItem
             path="rePassword"
@@ -161,10 +182,24 @@ export default defineComponent({
             <NInput
               v-model:value={forms.rePassword}
               clearable
-              type="password"
-              show-password-on="click"
-              placeholder={'再次输入新密码'}
-            />
+              type="text"
+              showPasswordOn="click"
+              inputProps={{ autocomplete: 'off' }}
+              class={[password.rePasswordStatus ? '' : styles['no-pwd']]}
+              placeholder={'再次输入新密码'}>
+              {{
+                suffix: () => (
+                  <img
+                    src={password.rePasswordStatus ? openEye : closeEye}
+                    class={styles.pwdIcon}
+                    alt=""
+                    onClick={() => {
+                      password.rePasswordStatus = !password.rePasswordStatus;
+                    }}
+                  />
+                )
+              }}
+            </NInput>
           </NFormItem>
           <NFormItem
             path="code"

+ 1 - 1
src/router/router-guards.ts

@@ -26,7 +26,7 @@ const isChrome = () => {
 export function createRouterGuards(router: Router) {
   const userStore = useUserStore();
   router.beforeEach(async (to, from, next) => {
-    console.log(to, '修改标题');
+    // console.log(to, '修改标题');
     // console.log('access token');
     document.title = to.meta.title ? to.meta.title : ('音乐数字课堂' as string);
     if (!isChrome()) {

+ 9 - 0
src/store/modules/prepareLessons.ts

@@ -7,6 +7,7 @@ export const usePrepareStore = defineStore('prepare-lessons-store', {
     baseCourseware: {} as any, // 基础教学课件
     selectKey: '', // 选的哪一节课
     lessonCoursewareId: '', // 哪个教材分类
+    subjectList: [] as any, // 教材带的声部列表
     lessonCoursewareDetailId: '', // 哪个教材详情
     treeList: [] as any[], // 左边教学课件列表
     coursewareList: [] as any[], // 课件信息
@@ -79,6 +80,10 @@ export const usePrepareStore = defineStore('prepare-lessons-store', {
     /** 获取是否添加训练 */
     getIsAddTrain(): boolean {
       return this.isAddTrain;
+    },
+    /** 获取声部列表 */
+    getSubjectList(): any {
+      return this.subjectList;
     }
   },
   actions: {
@@ -141,6 +146,10 @@ export const usePrepareStore = defineStore('prepare-lessons-store', {
     /** 设置训练状态 */
     setIsEditTrain(status: boolean) {
       this.iseditTrain = status;
+    },
+    /** 设置声部列表 */
+    setSubjectList(subjects: any): any {
+      this.subjectList = subjects;
     }
   }
 });

BIN
src/views/attend-class/image/icon-change.png


BIN
src/views/attend-class/image/icon-down.png


BIN
src/views/attend-class/image/icon-up.png


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

@@ -220,7 +220,7 @@
   overflow: hidden;
 
   &.point {
-    margin: 20px 0;
+    margin: 20px 0 0;
   }
 
   img {
@@ -232,9 +232,15 @@
     opacity: 0.8;
   }
 
+  &.iconUp,
+  &.iconDown {
+    margin-top: 20px;
+  }
+
   &.btnsDisabled {
     opacity: 0;
     pointer-events: none;
+    display: none;
   }
 }
 
@@ -287,13 +293,17 @@
 .drawerContainer {
   width: 360px !important;
 
+  .cardContainer {
+    margin-bottom: 24px;
+  }
+
   :global {
     .n-drawer-body-content-wrapper {
       padding: 8px 0px 0px !important;
       text-align: center;
 
       &>div {
-        margin-bottom: 24px;
+        // margin-bottom: 24px;
       }
     }
 

+ 267 - 170
src/views/attend-class/index.tsx

@@ -11,6 +11,7 @@ import {
 import styles from './index.module.less';
 import 'plyr/dist/plyr.css';
 import MusicScore from './component/musicScore';
+import iconChange from './image/icon-change.png';
 import iconMenu from './image/icon-menu.png';
 import iconUp from './image/icon-up.png';
 import iconDown from './image/icon-down.png';
@@ -39,7 +40,12 @@ import Pen from './component/tools/pen';
 import AudioPay from './component/audio-pay';
 import TrainSettings from './model/train-settings';
 import { useRoute } from 'vue-router';
-import { lessonPreTrainingPage, queryCourseware } from '../prepare-lessons/api';
+import {
+  courseScheduleUpdate,
+  lessonCoursewareDetail,
+  lessonPreTrainingPage,
+  queryCourseware
+} from '../prepare-lessons/api';
 import Attentguide from '@/custom-plugins/guide-page/attent-guide';
 import { vaildUrl } from '/src/utils/urlUtils';
 import TimerMeter from '/src/components/timerMeter';
@@ -51,6 +57,7 @@ import toneIcon from '/src/components/layout/images/toneIcon.png';
 import { px2vw } from '/src/utils';
 import PlaceholderTone from '/src/components/layout/modals/placeholderTone';
 import { state as globalState } from '/src/state';
+import Chapter from './model/chapter';
 export type ToolType = 'init' | 'pen' | 'whiteboard';
 export type ToolItem = {
   type: ToolType;
@@ -69,13 +76,24 @@ export default defineComponent({
       type: [String, Number],
       default: ''
     },
+    // 教材编号
+    lessonCourseId: {
+      type: [String, Number],
+      default: ''
+    },
     detailId: {
       type: String,
       default: ''
     },
+    // 班级编号
     classGroupId: {
       type: String,
       default: ''
+    },
+    // 上课记录编号
+    classId: {
+      type: String,
+      defaault: ''
     }
   },
   emits: ['close'],
@@ -120,8 +138,10 @@ export default defineComponent({
     const data = reactive({
       type: 'class' as '' | 'preview' | 'class', // 预览类型
       subjectId: '' as any, // 声部编号
+      lessonCourseId: '' as any, // 教材编号
       detailId: '' as any, // 编号 - 章节编号
       classGroupId: '' as any, // 上课时需要 班级编号
+      classId: '' as any, // 上课编号
       // detail: null,
       knowledgePointList: [] as any,
       itemList: [] as any,
@@ -360,7 +380,9 @@ export default defineComponent({
       data.type = props.type || (query.type as any);
       data.subjectId = props.subjectId || query.subjectId;
       data.detailId = props.detailId || query.detailId;
+      data.lessonCourseId = props.lessonCourseId || query.lessonCourseId;
       data.classGroupId = props.classGroupId || query.classGroupId;
+      data.classId = props.classId || query.classId;
 
       const subdEl = document.getElementById(`moveNPopoverA`) as HTMLDivElement;
       initBoundaryWrap(subdEl, boxBoundaryInfo);
@@ -368,6 +390,7 @@ export default defineComponent({
 
       window.addEventListener('message', iframeHandle);
       getDetail();
+      getLessonCoursewareDetail();
       window.addEventListener('resize', resetSize);
     });
 
@@ -523,9 +546,36 @@ export default defineComponent({
     const popupData = reactive({
       open: false,
       activeIndex: 0,
-      toolOpen: false // 工具弹窗控制
+      toolOpen: false, // 工具弹窗控制
+      chapterOpen: false, // 切换章节
+      chapterDetails: {} as any,
+      chapterLoading: false // 加载数据
     });
 
+    /** 获取章节 */
+    const getLessonCoursewareDetail = async () => {
+      try {
+        const res = await lessonCoursewareDetail({
+          id: data.lessonCourseId,
+          subjectId: data.subjectId
+        });
+
+        popupData.chapterDetails = res.data.lessonList || [];
+      } catch {
+        //
+      }
+    };
+    /** 更新上课记录 */
+    const classCourseScheduleUpdate = async () => {
+      try {
+        if (!data.classId) return;
+        await courseScheduleUpdate({
+          lessonCoursewareKnowledgeDetailId: data.detailId,
+          id: data.classId
+        });
+      } catch {}
+    };
+
     const activeName = computed(() => {
       let name = '';
       data.knowledgePointList.forEach((item: any, index: number) => {
@@ -850,79 +900,117 @@ export default defineComponent({
 
     return () => (
       <div id="playContent" class={[styles.playContent, 'wrap']}>
-        <div
-          onClick={() => {
-            clearTimeout(activeData.timer);
-            activeData.model = !activeData.model;
-            Object.values(data.videoRefs).map((n: any) =>
-              n.toggleHideControl(activeData.model)
-            );
-            Object.values(data.audioRefs).map((n: any) =>
-              n.toggleHideControl(activeData.model)
-            );
-          }}>
+        {!popupData.chapterLoading ? (
           <div
-            class={styles.coursewarePlay}
-            style={{ width: parentContainer.width }}
-            onClick={(e: Event) => {
-              e.stopPropagation();
-              setModelOpen();
+            onClick={() => {
+              clearTimeout(activeData.timer);
+              activeData.model = !activeData.model;
+              Object.values(data.videoRefs).map((n: any) =>
+                n.toggleHideControl(activeData.model)
+              );
+              Object.values(data.audioRefs).map((n: any) =>
+                n.toggleHideControl(activeData.model)
+              );
             }}>
-            <div class={styles.wraps}>
-              {data.itemList.map((m: any, mIndex: number) => {
-                const isRender =
-                  m.isRender || Math.abs(popupData.activeIndex - mIndex) < 2;
-                const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4;
-                if (isRender) {
-                  m.isRender = true;
-                }
-                return isRender ? (
-                  <div
-                    key={'index' + mIndex}
-                    class={[
-                      styles.itemDiv,
-                      popupData.activeIndex === mIndex && styles.itemActive,
-                      activeData.isAnimation && styles.acitveAnimation,
-                      Math.abs(popupData.activeIndex - mIndex) < 2
-                        ? styles.show
-                        : styles.hide
-                    ]}
-                    style={
-                      mIndex < popupData.activeIndex
-                        ? effects[effectIndex.value].prev
-                        : mIndex > popupData.activeIndex
-                        ? effects[effectIndex.value].next
-                        : {}
-                    }
-                    onClick={(e: Event) => {
-                      e.stopPropagation();
-                      clearTimeout(activeData.timer);
-                      if (Date.now() - activeData.nowTime < 300) {
-                        handleDbClick(m);
-                        return;
+            <div
+              class={styles.coursewarePlay}
+              style={{ width: parentContainer.width }}
+              onClick={(e: Event) => {
+                e.stopPropagation();
+                setModelOpen();
+              }}>
+              <div class={styles.wraps}>
+                {data.itemList.map((m: any, mIndex: number) => {
+                  const isRender =
+                    m.isRender || Math.abs(popupData.activeIndex - mIndex) < 2;
+                  const isEmtry = Math.abs(popupData.activeIndex - mIndex) > 4;
+                  if (isRender) {
+                    m.isRender = true;
+                  }
+                  return isRender ? (
+                    <div
+                      key={'index' + mIndex}
+                      class={[
+                        styles.itemDiv,
+                        popupData.activeIndex === mIndex && styles.itemActive,
+                        activeData.isAnimation && styles.acitveAnimation,
+                        Math.abs(popupData.activeIndex - mIndex) < 2
+                          ? styles.show
+                          : styles.hide
+                      ]}
+                      style={
+                        mIndex < popupData.activeIndex
+                          ? effects[effectIndex.value].prev
+                          : mIndex > popupData.activeIndex
+                          ? effects[effectIndex.value].next
+                          : {}
                       }
-                      activeData.nowTime = Date.now();
-                      activeData.timer = setTimeout(() => {
-                        activeData.model = !activeData.model;
-                        Object.values(data.videoRefs).map((n: any) =>
-                          n.toggleHideControl(activeData.model)
-                        );
-                        Object.values(data.audioRefs).map((n: any) =>
-                          n.toggleHideControl(activeData.model)
-                        );
-                        if (activeData.model) {
-                          setModelOpen();
+                      onClick={(e: Event) => {
+                        e.stopPropagation();
+                        clearTimeout(activeData.timer);
+                        if (Date.now() - activeData.nowTime < 300) {
+                          handleDbClick(m);
+                          return;
                         }
-                      }, 300);
-                    }}>
-                    {m.type === 'VIDEO' ? (
-                      <>
-                        <VideoPlay
-                          ref={(v: any) => (data.videoRefs[mIndex] = v)}
+                        activeData.nowTime = Date.now();
+                        activeData.timer = setTimeout(() => {
+                          activeData.model = !activeData.model;
+                          Object.values(data.videoRefs).map((n: any) =>
+                            n.toggleHideControl(activeData.model)
+                          );
+                          Object.values(data.audioRefs).map((n: any) =>
+                            n.toggleHideControl(activeData.model)
+                          );
+                          if (activeData.model) {
+                            setModelOpen();
+                          }
+                        }, 300);
+                      }}>
+                      {m.type === 'VIDEO' ? (
+                        <>
+                          <VideoPlay
+                            ref={(v: any) => (data.videoRefs[mIndex] = v)}
+                            item={m}
+                            isEmtry={isEmtry}
+                            onLoadedmetadata={(videoItem: any) => {
+                              m.videoEle = videoItem;
+                              m.isprepare = true;
+                            }}
+                            onTogglePlay={(paused: boolean) => {
+                              m.autoPlay = false;
+                              if (paused || popupData.open) {
+                                clearTimeout(activeData.timer);
+                              } else {
+                                setModelOpen();
+                              }
+                            }}
+                            onReset={() => {
+                              if (!m.videoEle?.paused) {
+                                setModelOpen();
+                              }
+                            }}
+                            onError={() => {
+                              console.log('video error');
+                              m.error = true;
+                            }}
+                          />
+                          <Transition name="van-fade">
+                            {!m.isprepare && (
+                              <div class={styles.loadWrap}>
+                                <Vue3Lottie
+                                  animationData={playLoadData}></Vue3Lottie>
+                              </div>
+                            )}
+                          </Transition>
+                        </>
+                      ) : m.type === 'IMG' ? (
+                        <img src={m.content} />
+                      ) : m.type === 'SONG' ? (
+                        <AudioPay
                           item={m}
-                          isEmtry={isEmtry}
-                          onLoadedmetadata={(videoItem: any) => {
-                            m.videoEle = videoItem;
+                          ref={(v: any) => (data.audioRefs[mIndex] = v)}
+                          onLoadedmetadata={(audioItem: any) => {
+                            m.audioEle = audioItem;
                             m.isprepare = true;
                           }}
                           onTogglePlay={(paused: boolean) => {
@@ -933,115 +1021,91 @@ export default defineComponent({
                               setModelOpen();
                             }
                           }}
+                          onEnded={() => {
+                            const _index = popupData.activeIndex + 1;
+                            if (_index < data.itemList.length) {
+                              handleSwipeChange(_index);
+                            }
+                          }}
                           onReset={() => {
-                            if (!m.videoEle?.paused) {
+                            if (!m.audioEle?.paused) {
                               setModelOpen();
                             }
                           }}
-                          onError={() => {
-                            console.log('video error');
-                            m.error = true;
+                        />
+                      ) : (
+                        <MusicScore
+                          activeModel={activeData.model}
+                          data-vid={m.id}
+                          music={m}
+                          onSetIframe={(el: any) => {
+                            m.iframeRef = el;
                           }}
                         />
-                        <Transition name="van-fade">
-                          {!m.isprepare && (
-                            <div class={styles.loadWrap}>
-                              <Vue3Lottie
-                                animationData={playLoadData}></Vue3Lottie>
-                            </div>
-                          )}
-                        </Transition>
-                      </>
-                    ) : m.type === 'IMG' ? (
-                      <img src={m.content} />
-                    ) : m.type === 'SONG' ? (
-                      <AudioPay
-                        item={m}
-                        ref={(v: any) => (data.audioRefs[mIndex] = v)}
-                        onLoadedmetadata={(audioItem: any) => {
-                          m.audioEle = audioItem;
-                          m.isprepare = true;
-                        }}
-                        onTogglePlay={(paused: boolean) => {
-                          m.autoPlay = false;
-                          if (paused || popupData.open) {
-                            clearTimeout(activeData.timer);
-                          } else {
-                            setModelOpen();
-                          }
-                        }}
-                        onEnded={() => {
-                          const _index = popupData.activeIndex + 1;
-                          if (_index < data.itemList.length) {
-                            handleSwipeChange(_index);
-                          }
-                        }}
-                        onReset={() => {
-                          if (!m.audioEle?.paused) {
-                            setModelOpen();
-                          }
-                        }}
-                      />
-                    ) : (
-                      <MusicScore
-                        activeModel={activeData.model}
-                        data-vid={m.id}
-                        music={m}
-                        onSetIframe={(el: any) => {
-                          m.iframeRef = el;
-                        }}
-                      />
-                    )}
-                  </div>
-                ) : null;
-              })}
-            </div>
-            <Transition name="right">
-              {activeData.model && (
-                <div
-                  class={styles.rightFixedBtns}
-                  onClick={(e: Event) => {
-                    e.stopPropagation();
-                    clearTimeout(activeData.timer);
-                  }}>
+                      )}
+                    </div>
+                  ) : null;
+                })}
+              </div>
+              <Transition name="right">
+                {activeData.model && (
                   <div
-                    class={[
-                      styles.fullBtn,
-                      popupData.activeIndex === 0 ? styles.btnsDisabled : ''
-                    ]}
-                    onClick={() => {
-                      if (popupData.activeIndex === 0) return;
-                      handlePreAndNext('up');
+                    class={styles.rightFixedBtns}
+                    onClick={(e: Event) => {
+                      e.stopPropagation();
+                      clearTimeout(activeData.timer);
                     }}>
-                    <img src={iconUp} />
-                  </div>
-                  <div id="attent-0">
                     <div
-                      class={[styles.fullBtn, styles.point]}
-                      onClick={() => (popupData.open = true)}>
-                      <img src={iconMenu} />
+                      class={[styles.fullBtn]}
+                      onClick={() => (popupData.chapterOpen = true)}>
+                      <img src={iconChange} />
                     </div>
-
                     <div
                       class={[
                         styles.fullBtn,
-                        popupData.activeIndex === data.itemList.length - 1
-                          ? styles.btnsDisabled
-                          : ''
+                        styles.iconUp,
+                        popupData.activeIndex === 0 ? styles.btnsDisabled : ''
                       ]}
                       onClick={() => {
-                        if (popupData.activeIndex === data.itemList.length - 1)
-                          return;
-                        handlePreAndNext('down');
+                        if (popupData.activeIndex === 0) return;
+                        handlePreAndNext('up');
                       }}>
-                      <img src={iconDown} />
+                      <img src={iconUp} />
+                    </div>
+                    <div id="attent-0">
+                      <div
+                        class={[styles.fullBtn, styles.point]}
+                        onClick={() => (popupData.open = true)}>
+                        <img src={iconMenu} />
+                      </div>
+
+                      <div
+                        class={[
+                          styles.fullBtn,
+                          styles.iconDown,
+                          popupData.activeIndex === data.itemList.length - 1
+                            ? styles.btnsDisabled
+                            : ''
+                        ]}
+                        onClick={() => {
+                          if (
+                            popupData.activeIndex ===
+                            data.itemList.length - 1
+                          )
+                            return;
+                          handlePreAndNext('down');
+                        }}>
+                        <img src={iconDown} />
+                      </div>
                     </div>
                   </div>
-                </div>
-              )}
-            </Transition>
+                )}
+              </Transition>
+            </div>
           </div>
-        </div>
+        ) : (
+          ''
+        )}
 
         <div
           style={{ transform: activeData.model ? '' : 'translateY(-100%)' }}
@@ -1161,20 +1225,53 @@ export default defineComponent({
           showMask={false}>
           <NDrawerContent title="资源列表" closable>
             {data.knowledgePointList.map((item: any, index: number) => (
-              <CardType
-                item={item}
-                isActive={popupData.activeIndex === index}
-                isCollect={false}
-                isShowCollect={false}
-                onClick={(item: any) => {
-                  popupData.open = false;
-                  toggleMaterial(item.id);
-                }}
-              />
+              <div class={styles.cardContainer}>
+                <CardType
+                  item={item}
+                  isActive={popupData.activeIndex === index}
+                  isCollect={false}
+                  isShowCollect={false}
+                  onClick={(item: any) => {
+                    popupData.open = false;
+                    toggleMaterial(item.id);
+                  }}
+                />
+              </div>
             ))}
           </NDrawerContent>
         </NDrawer>
 
+        {/* 显示列表 */}
+        <NDrawer
+          v-model:show={popupData.chapterOpen}
+          class={styles.drawerContainer}
+          onAfterLeave={handleClosePopup}
+          showMask={false}>
+          <NDrawerContent title="切换章节" closable>
+            <Chapter
+              treeList={popupData.chapterDetails}
+              itemActive={data.detailId as any}
+              onHandleSelect={async (val: any) => {
+                popupData.chapterLoading = true;
+
+                try {
+                  data.detailId = val.itemActive;
+                  // 更新上课记录 上课的时候才更新
+                  if (data.type !== 'preview') {
+                    await classCourseScheduleUpdate();
+                  }
+                  await getDetail();
+                  popupData.activeIndex = 0;
+                  popupData.chapterOpen = false;
+                } catch {
+                  //
+                }
+                popupData.chapterLoading = false;
+              }}
+            />
+          </NDrawerContent>
+        </NDrawer>
+
         {/* 批注 */}
         {studyData.penShow && (
           <Pen

BIN
src/views/attend-class/model/chapter/images/arrow-active.png


BIN
src/views/attend-class/model/chapter/images/arrow-default.png


+ 39 - 0
src/views/attend-class/model/chapter/images/icon-custom.svg

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="80px" height="82px" viewBox="0 0 80 82" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组 6</title>
+    <defs>
+        <linearGradient x1="16.0585307%" y1="48.1812553%" x2="88.7815146%" y2="51.655599%" id="linearGradient-1">
+            <stop stop-color="#308AFF" offset="0%"></stop>
+            <stop stop-color="#60A9FF" offset="100%"></stop>
+        </linearGradient>
+        <path d="M26.1421356,-9.86533591e-13 L77.8578644,-9.86533591e-13 C80.5100293,-9.92349856e-13 83.0535684,1.0535684 84.9289322,2.92893219 L104,22 L104,22 L-8.56952355e-13,22 L19.0710678,2.92893219 C20.9464316,1.0535684 23.4899707,-9.86046396e-13 26.1421356,-9.86533591e-13 Z" id="path-2"></path>
+        <linearGradient x1="50%" y1="13.692199%" x2="50%" y2="69.4941756%" id="linearGradient-4">
+            <stop stop-color="#FFFFFF" stop-opacity="0.404392483" offset="0%"></stop>
+            <stop stop-color="#FFFFFF" stop-opacity="0" offset="100%"></stop>
+        </linearGradient>
+    </defs>
+    <g id="所有页面" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="46、选择教材" transform="translate(-1010.000000, -322.000000)">
+            <g id="编组-4" transform="translate(385.000000, 127.000000)">
+                <g id="编组-2备份-2" transform="translate(605.000000, 179.703907)">
+                    <g id="编组-6" transform="translate(3.703907, 0.000000)">
+                        <g id="编组-5" transform="translate(48.777762, 48.777762) rotate(-45.000000) translate(-48.777762, -48.777762) translate(-4.075266, 32.648617)">
+                            <path d="M0.577507392,31.6130682 L-1.44486575e-13,24.5356078 L-1.44486575e-13,24.5356078 C0.150435792,23.0883 1.1854755,22.0762681 3.10511912,21.4995122 C5.02476274,20.9227562 7.59556321,20.1716791 10.8175205,19.2462809 L11.1122213,21.7991185 L1.08841361,31.8010336 C0.971128318,31.9180628 0.781178938,31.9178551 0.664149799,31.8005698 C0.613985136,31.7502954 0.583283393,31.6838541 0.577507392,31.6130682 Z" id="路径-33" fill="#1E3DCC"></path>
+                            <path d="M95.1713422,31.6130682 L94.5938348,24.5356078 L94.5938348,24.5356078 C94.7442706,23.0883 95.7793103,22.0762681 97.698954,21.4995122 C99.6185976,20.9227562 102.189398,20.1716791 105.411355,19.2462809 L105.706056,21.7991185 L95.6822485,31.8010336 C95.5649632,31.9180628 95.3750138,31.9178551 95.2579846,31.8005698 C95.20782,31.7502954 95.1771182,31.6838541 95.1713422,31.6130682 Z" id="路径-33备份" fill="#1E3DCC" transform="translate(100.149945, 25.752285) scale(-1, 1) translate(-100.149945, -25.752285) "></path>
+                            <g id="矩形" transform="translate(0.845434, 0.711632)">
+                                <mask id="mask-3" fill="white">
+                                    <use xlink:href="#path-2"></use>
+                                </mask>
+                                <use id="蒙版" fill="url(#linearGradient-1)" transform="translate(52.000000, 11.000000) rotate(-360.000000) translate(-52.000000, -11.000000) " xlink:href="#path-2"></use>
+                                <rect fill="url(#linearGradient-4)" mask="url(#mask-3)" transform="translate(30.713441, 12.039373) rotate(-315.000000) translate(-30.713441, -12.039373) " x="24.713441" y="-13.960627" width="12" height="52"></rect>
+                            </g>
+                            <text id="自定义" transform="translate(52.499475, 11.000000) rotate(-360.000000) translate(-52.499475, -11.000000) " font-family="PingFangSC-Semibold, PingFang SC" font-size="16" font-weight="500" letter-spacing="1" fill="#FFFFFF">
+                                <tspan x="26.9994747" y="17">自定义</tspan>
+                            </text>
+                        </g>
+                    </g>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

BIN
src/views/attend-class/model/chapter/images/icon-d-active.png


BIN
src/views/attend-class/model/chapter/images/icon-d.png


BIN
src/views/attend-class/model/chapter/images/icon-upload-bg.png


+ 40 - 0
src/views/attend-class/model/chapter/images/icon_default.svg

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="51px" height="49px" viewBox="0 0 51 49" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>编组@2x</title>
+    <defs>
+        <linearGradient x1="60.3790157%" y1="14.1179897%" x2="60.3790157%" y2="99.1367692%" id="linearGradient-1">
+            <stop stop-color="#D8D9DB" offset="0%"></stop>
+            <stop stop-color="#D3D4D6" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="47.928727%" y1="88.2137009%" x2="47.928727%" y2="-1.32949207e-12%" id="linearGradient-2">
+            <stop stop-color="#D0D1D3" offset="0%"></stop>
+            <stop stop-color="#CACBCD" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="57.5601934%" y1="1.66592618%" x2="56.4312509%" y2="99.1367692%" id="linearGradient-3">
+            <stop stop-color="#D8D9DB" offset="0%"></stop>
+            <stop stop-color="#D1D2D4" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="47.928727%" y1="88.2137009%" x2="47.928727%" y2="-1.32949207e-12%" id="linearGradient-4">
+            <stop stop-color="#D0D1D3" offset="0%"></stop>
+            <stop stop-color="#C8C9CB" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="47.928727%" y1="88.2137009%" x2="47.928727%" y2="-1.32949207e-12%" id="linearGradient-5">
+            <stop stop-color="#CFD0D2" offset="0%"></stop>
+            <stop stop-color="#CBCCCE" offset="100%"></stop>
+        </linearGradient>
+    </defs>
+    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="67、AI学练" transform="translate(-1720.000000, -737.000000)" fill-rule="nonzero">
+            <g id="编组" transform="translate(1720.000000, 737.000000)">
+                <g id="编组-6" transform="translate(0.600000, 0.666667)">
+                    <path d="M0.755328486,15.0252525 C-0.830540243,19.9313215 0.11107891,25.262202 3.29524979,29.4047571 C6.47942068,33.5473122 11.5319763,36.0147566 16.9240016,36.0606061 C24.4286025,36.09296 31.0759035,31.4573623 33.3061224,24.6361115 L0.755328486,15.0252525 Z" id="路径" fill="url(#linearGradient-1)"></path>
+                    <path d="M17.7262393,31.0568351 C8.4521233,30.9803529 0.940551474,23.8601879 0.813375057,15.0252525 L0.754723889,15.0252525 C-0.829875444,19.9313026 0.110989998,25.2621626 3.29261214,29.4047017 C6.47423428,33.5472408 11.5227456,36.0146758 16.9104549,36.060355 C24.4276023,36.0999738 31.0862959,31.4479077 33.3061224,24.6055962 L31.7758601,24.1535015 C28.5683718,28.5148198 23.3157927,31.0956941 17.7262393,31.0568351 L17.7262393,31.0568351 Z" id="路径" fill="url(#linearGradient-2)"></path>
+                    <path d="M14.7677422,5.37825957 C14.7677422,5.37825957 18.3328036,4.44948812 20.0004184,7.54187485 C15.9970737,11.1250329 13.586515,16.8823604 15.6870684,21.3679043 C16.5048411,23.1304592 18.0548677,24.5341705 18.6962581,26.4392074 C20.6791709,32.3335664 18.6004257,38.818554 13.5437557,42.5132859 L13.4849616,42.5555028 C13.4849616,42.5555028 12.9504696,42.951286 12.1113173,43.5370453 C14.650154,45.1782267 18.4236672,47.1360346 21.8711403,47.7956734 C30.2359393,49.4051921 38.8305699,44.0067081 42.0642462,36.4921028 C42.3367119,35.8608728 42.5652914,35.2120441 42.7483959,34.5501262 C43.5795078,31.4293042 43.8097647,28.1821626 43.4272007,24.9774478 C43.202714,23.0407483 42.6147729,19.4681445 45.3674064,19.0354214 C46.4363903,18.8665539 47.4626149,19.4048192 48.2803876,20.0539038 C48.4033207,20.1488917 49.5364437,21.067109 49.4562699,21.2518078 C51.2735425,16.9351315 48.2964223,5.51018733 38.3869416,1.64734247 C31.0376772,-1.32367072 20.3692179,-0.389622168 14.7677422,5.37825957 Z" id="路径" fill="url(#linearGradient-3)"></path>
+                    <path d="M26.6124657,47.6072826 C21.4912819,46.636432 18.7337213,44.7527707 16.9716508,43.1698621 C17.8393774,42.600015 18.3557545,42.2148405 18.3557545,42.2148405 L18.4143128,42.1726296 C23.5265567,38.5761859 25.72439,32.1283454 23.8602286,26.1958052 C23.2533524,24.2752094 21.7308383,22.8505916 20.9589343,21.077734 C18.9626308,16.5558917 21.459341,10.8415915 25.5105062,7.33281071 C23.9134634,4.20920435 20.3414111,5.07452773 20.3414111,5.07452773 C23.237382,2.18835767 27.421634,0.584343589 31.7922078,0.114747363 C25.5850348,-0.47092883 18.8188969,1.17001978 14.7570848,5.39110946 C14.7570848,5.39110946 18.3078432,4.46774609 19.9687677,7.55441792 C15.9814843,11.1370678 13.5805966,16.8988552 15.6727227,21.383763 C16.4872145,23.1407916 18.0310225,24.5495802 18.6698396,26.4490706 C20.6447971,32.3425937 18.5743917,38.8266616 13.5380088,42.5208695 L13.4794506,42.5630804 C13.4794506,42.5630804 12.947103,42.9640839 12.1113173,43.5444838 C14.6399683,45.1907088 18.3983423,47.1429627 21.8319843,47.8077844 C23.7881287,48.171816 25.7955452,48.171816 27.7516896,47.8077844 C27.3524289,47.744468 26.9797855,47.6811517 26.6124657,47.6072826 Z" id="路径" fill="url(#linearGradient-4)"></path>
+                    <path d="M45.0423476,14.0093804 C46.200789,13.854566 48.445269,13.5716291 48.445269,13.5716291 C46.707607,9.89878877 42.4247158,7.65130943 39.7402509,6.83452952 C39.7068343,6.83452952 38.3200464,6.42347036 38.3311852,6.43948565 C33.2852822,5.3717995 29.0190992,6.29000959 25.799969,9.25283865 C22.37477,12.4025128 21.6340358,17.9598192 24.3964728,21.7340897 C25.154529,22.6993831 26.100114,23.5151123 27.1811875,24.1363836 C25.0592349,38.8384218 37.0335082,40.6855189 37.1560356,40.562735 C39.1777385,38.2405176 41.2105802,35.939654 41.2105802,28.0601302 C41.2105802,25.9247579 40.2916244,22.4120704 40.1579581,21.5045372 C39.5230431,17.1750699 40.8652756,14.5966078 45.0423476,14.0093804 Z" id="路径" fill="#F4F5F7"></path>
+                    <ellipse id="椭圆形" fill="url(#linearGradient-5)" cx="35.5769944" cy="14.2739899" rx="2.27087199" ry="2.25378788"></ellipse>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

+ 93 - 0
src/views/attend-class/model/chapter/index.module.less

@@ -0,0 +1,93 @@
+.scrollBar {
+  margin: 0 20px;
+  width: calc(100% - 40px);
+}
+
+.treeParent {
+  transition: height 1s ease-in-out;
+}
+
+.treeChild {
+  line-height: 54px;
+}
+
+.treeItem {
+  display: flex;
+  align-items: center;
+  line-height: 54px;
+  border-radius: 10px;
+  padding: 0 5px;
+  cursor: pointer;
+  border-radius: 10px;
+  font-size: max(17px, 13px);
+
+  &:hover {
+    background: #F5F6FA;
+  }
+
+  .title {
+    padding-left: 8px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    max-width: 280px !important;
+    color: rgba(0, 0, 0, .5);
+    display: flex;
+    align-items: center;
+
+    .dir {
+      flex-shrink: 1;
+      display: inline-block;
+      width: 16px;
+      height: 18px;
+      background: url('./images/icon-d.png') no-repeat center;
+      background-size: contain;
+      margin-right: 6px;
+    }
+
+    &.titleSelect {
+      color: #198CFE;
+      font-weight: bold;
+
+      .dir {
+        background: url('./images/icon-d-active.png') no-repeat center;
+        background-size: contain;
+      }
+    }
+  }
+
+  .arrow {
+    display: inline-block;
+    width: 14px;
+    height: 15px;
+    background: url('./images/arrow-default.png') no-repeat center;
+    background-size: contain;
+
+    &.arrowSelect {
+      background: url('./images/arrow-active.png') no-repeat center;
+      background-size: contain;
+    }
+  }
+
+  .childArrow {
+    width: 12px;
+  }
+
+  &.childItem {
+    padding-left: 30px;
+    font-size: 15px;
+
+    .title {
+      color: #131415;
+    }
+  }
+
+  &.childSelect {
+    background: #F5F6FA;
+
+    .title {
+      color: #198CFE;
+      font-weight: bold;
+    }
+  }
+}

+ 145 - 0
src/views/attend-class/model/chapter/index.tsx

@@ -0,0 +1,145 @@
+import { defineComponent, onMounted, reactive, toRefs, watch } from 'vue';
+import styles from './index.module.less';
+import { NScrollbar, useMessage } from 'naive-ui';
+
+export default defineComponent({
+  name: 'chapter-modal',
+  props: {
+    treeList: {
+      type: Array,
+      default: () => []
+    },
+    itemActive: {
+      type: String,
+      default: ''
+    }
+  },
+  emits: ['handleSelect'],
+  setup(props, { emit }) {
+    const message = useMessage();
+    const { treeList, itemActive } = toRefs(props);
+
+    const formatParentId = (id: any, list: any, ids = [] as any) => {
+      for (const item of list) {
+        if (item.knowledgeList && item.knowledgeList.length > 0) {
+          const cIds: any = formatParentId(id, item.knowledgeList, [
+            ...ids,
+            item.id
+          ]);
+          if (cIds.includes(id)) {
+            return cIds;
+          }
+        }
+        if (item.id === id) {
+          return [...ids, id];
+        }
+      }
+      return ids;
+    };
+
+    watch(
+      () => props.itemActive,
+      () => {
+        const ids = formatParentId(itemActive.value, treeList.value);
+        if (ids.length > 0) {
+          treeList.value.forEach((tree: any) => {
+            if (tree.id == ids[0]) {
+              tree.selected = true;
+            } else {
+              tree.selected = false;
+            }
+          });
+        }
+      }
+    );
+
+    onMounted(() => {
+      const ids = formatParentId(itemActive.value, treeList.value);
+      if (ids.length > 0) {
+        treeList.value.forEach((tree: any) => {
+          if (tree.id == ids[0]) {
+            tree.selected = true;
+          }
+        });
+      }
+    });
+    return () => (
+      <NScrollbar class={styles.scrollBar}>
+        <div class={[styles.listSection]}>
+          {treeList.value.map((item: any, index: number) => (
+            <div class={styles.treeParent} key={'parent' + index}>
+              <div
+                class={[styles.treeItem, styles.parentItem]}
+                onClick={() => {
+                  treeList.value.forEach((child: any) => {
+                    if (item.id !== child.id) {
+                      child.selected = false;
+                    }
+                  });
+                  item.selected = item.selected ? false : true;
+                }}>
+                {item.knowledgeList && item.knowledgeList.length > 0 && (
+                  <span
+                    class={[
+                      styles.arrow,
+                      item.selected ? styles.arrowSelect : ''
+                    ]}></span>
+                )}
+                <p
+                  class={[
+                    styles.title,
+                    item.selected ? styles.titleSelect : ''
+                  ]}>
+                  <span
+                    class={[
+                      styles.dir,
+                      item.selected ? styles.dirSelect : ''
+                    ]}></span>
+                  {item.name}
+                </p>
+              </div>
+
+              {item.selected &&
+                item.knowledgeList &&
+                item.knowledgeList.map((child: any, j: number) => (
+                  <div
+                    key={'child' + j}
+                    class={[
+                      styles.treeItem,
+                      styles.childItem,
+                      styles.animation,
+                      itemActive.value === child.id ? styles.childSelect : ''
+                    ]}
+                    onClick={() => {
+                      // 判断是否选择的同一个课件
+                      if (itemActive.value == child.id) {
+                        return;
+                      }
+                      if (!child.containMaterial) {
+                        message.error('该章节暂无课件');
+                        return;
+                      }
+                      emit('handleSelect', {
+                        itemActive: child.id,
+                        itemName: child.name
+                      });
+                      // emit('handleSelect', {});
+                      // prepareStore.setSelectKey(child.id);
+                      // prepareStore.setLessonCoursewareId(
+                      //   child.lessonCoursewareId
+                      // );
+                      // prepareStore.setLessonCoursewareDetailId(
+                      //   child.lessonCoursewareDetailId
+                      // );
+                    }}>
+                    <span class={styles.childArrow}></span>
+                    <p class={styles.title}>{child.name}</p>
+                  </div>
+                ))}
+            </div>
+          ))}
+        </div>
+      </NScrollbar>
+    );
+  }
+});

+ 7 - 1
src/views/attend-class/model/train-settings/index.tsx

@@ -133,6 +133,10 @@ export default defineComponent({
 
     const onSubmit = async () => {
       // 训练内容不能为空
+      if (!trainForms.expireDate) {
+        message.error('请选择截止日期');
+        return;
+      }
       if (trainForms.trainList.length <= 0) {
         message.error('训练内容不能为空');
         return;
@@ -188,7 +192,6 @@ export default defineComponent({
               placeholder="请选择截止日期"
               v-model:formatted-value={trainForms.expireDate}
               type="date"
-              clearable
               valueFormat="yyyy-MM-dd"
               isDateDisabled={(ts: number) => {
                 return ts < trainForms.currentTime;
@@ -267,6 +270,7 @@ export default defineComponent({
             type="homework"
             onClose={() => (trainForms.editStatus = false)}
             onConfirm={(item: any) => {
+              console.log(item, 'update', trainForms);
               const tList = typeFormat(
                 item.trainingType,
                 item.trainingConfigJson
@@ -276,6 +280,7 @@ export default defineComponent({
                 trainForms.trainList.forEach((train: any) => {
                   if (train.id === item.id) {
                     train.trainingType = item.trainingType;
+                    train.trainingConfigJson = item.trainingConfigJson;
                     train.typeList = tList;
                   }
                 });
@@ -299,6 +304,7 @@ export default defineComponent({
           preset="card"
           title={'选择曲目'}>
           <SelectMusic
+            type="homework"
             onAdd={(item: any) => {
               trainForms.selectMusicStatus = false;
               onAdd(item);

+ 25 - 0
src/views/classList/api.ts

@@ -178,3 +178,28 @@ export const addGroup = (params: any) => {
     requestType: 'form'
   });
 };
+/**
+ * 获取班级列表
+ */
+export const getSubject = (params: any) => {
+  return request.post(`/edu-app/subject/page`, {
+    data: params
+  });
+};
+
+/**
+ * 修改班级
+ */
+export const resetClass = (params: any) => {
+  return request.post('/edu-app/classGroup/update', {
+    data: params
+  });
+};
+/**
+ * 修改声部
+ */
+export const updateSubject = (params: any) => {
+  return request.post('/edu-app/classGroup/updateSubject', {
+    data: params
+  });
+};

+ 113 - 51
src/views/classList/index.tsx

@@ -15,7 +15,7 @@ import {
 import SearchInput from '@/components/searchInput';
 import CSelect from '@/components/CSelect';
 import Pagination from '@/components/pagination';
-import { classGroupList, deleteClass,addGroup } from './api';
+import { classGroupList, deleteClass, getSubject ,addGroup} from './api';
 
 import CreateClass from './modals/createClass';
 import RestStudentBox from './modals/restStudentBox';
@@ -29,8 +29,10 @@ import TheEmpty from '/src/components/TheEmpty';
 import TheTooltip from '/src/components/TheTooltip';
 import PreviewWindow from '../preview-window';
 import { state as globalState } from '/src/state';
-import {courseSchedulePage} from '../home/api'
+import { courseSchedulePage } from '../home/api';
+import ResetSubject from './modals/resetSubject';
 import { fscreen } from '/src/utils';
+import UpdateSubject from './modals/updateSubject';
 export default defineComponent({
   name: 'class-classList',
   setup(props, { emit }) {
@@ -38,7 +40,8 @@ export default defineComponent({
       searchForm: {
         keyword: null as any,
         currentClass: null,
-        currentGradeNum: null
+        currentGradeNum: null,
+        subjectId: null
       },
       orchestraType: null,
       courseTypeCode: null,
@@ -59,7 +62,10 @@ export default defineComponent({
       previewModal: false,
       previewParams: {} as any,
       lastCourse:null as any,
-      groupVisiable:false
+      groupVisiable:false,
+      subjectList: [] as any,
+      showResetClass: false,
+      showSubjectClass: false
     });
     const formRef = ref();
     const dialog = useDialog();
@@ -76,7 +82,8 @@ export default defineComponent({
       state.searchForm = {
         keyword: null as any,
         currentClass: null,
-        currentGradeNum: null
+        currentGradeNum: null,
+        subjectId: null
       };
       getList();
     };
@@ -114,23 +121,37 @@ export default defineComponent({
       }
       console.log('getList');
     };
-
+    const getSubjectList = async () => {
+      const res = await getSubject({ page: 1, rows: 9999 });
+      state.subjectList = res.data.rows.map((item: any) => {
+        return {
+          value: item.id,
+          label: item.name
+        };
+      });
+      state.subjectList.unshift({ value: null, label: '选择声部' });
+    };
     const columns = () => {
       return [
         {
           title: '班级名称',
           key: 'name',
-          width: '25%'
+          width: '20%'
+        },
+        {
+          title: '班级声部',
+          key: 'subjectName',
+          width: '20%'
         },
         {
           title: '学生人数',
           key: 'preStudentNum',
-
+          width: '20%'
         },
         {
           title: '上次学习',
           key: 'lastStudy',
-          width: '25%',
+          width: '20%',
           render(row: any) {
             return row.lastStudy ? (
               <TheTooltip
@@ -178,7 +199,12 @@ export default defineComponent({
                       详情
                     </NButton>
                   )}
-
+                  <NButton
+                    type="primary"
+                    text
+                    onClick={() => resetClassSubject(row)}>
+                    修改声部
+                  </NButton>
                   {index == 0 ? (
                     <NButton
                       type="primary"
@@ -250,22 +276,34 @@ export default defineComponent({
       state.studentVisible = true;
     };
 
-    const classesBegin = async(row: any) => {
-       try{
-        const res = await courseSchedulePage({"classGroupId": row.id})
-      if(res.data.rows.length>0){
-        state.lastCourse = res.data.rows[0]
-        console.log(state.lastCourse,'state.lastCourse')
-      }else{
-        state.lastCourse = null
+    const classesBegin = async (row: any) => {
+      try {
+        console.log(row, 'row');
+        // 判断是否有声部
+        if (row.subjectId) {
+          //
+          router.push({
+            path: '/prepare-lessons',
+            query: {
+              lastUseCoursewareId: row.lessonCoursewareId,
+              unit: row.lessonCoursewareKnowledgeDetailId,
+              subjectId: row.subjectId,
+              name: row.name, // 班级名称
+              classGroupId: row.id // 班级编号
+            }
+          });
+        } else {
+          state.showSubjectClass = true;
+          state.activeRow = row;
+        }
+      } catch (e) {
+        console.log(e);
       }
-        console.log(res)
-        // const lastCorse =
-       }catch(e){
-        console.log(e)
-       }
+    };
+
+    const resetClassSubject = (row: any) => {
       state.activeRow = row;
-      state.goCourseVisiable = true;
+      state.showResetClass = true;
     };
     const createImgroup = async(row:any)=>{
       state.activeRow = row
@@ -285,6 +323,7 @@ export default defineComponent({
     }
     onMounted(() => {
       getList();
+      getSubjectList();
     });
     return () => (
       <div class={styles.listWrap}>
@@ -320,7 +359,16 @@ export default defineComponent({
                 } as any)}
                 v-model:value={state.searchForm.currentClass}></CSelect>
             </NFormItem>
-
+            <NFormItem>
+              <CSelect
+                {...({
+                  options: state.subjectList,
+                  placeholder: '选择声部',
+                  clearable: true,
+                  inline: true
+                } as any)}
+                v-model:value={state.searchForm.subjectId}></CSelect>
+            </NFormItem>
             <NFormItem>
               <NSpace justify="end">
                 <NButton type="primary" class="searchBtn" onClick={search}>
@@ -386,40 +434,54 @@ export default defineComponent({
           preset="card"
           class={['modalTitle background']}
           title={'创建班级'}>
-          <CreateClass
-            gradeNumList={state.gradeNumList}
-            classArray={classArray}
-            onGetList={() => getList()}
-            onClose={() => (state.showaddClass = false)}
-          />
+          {state.showaddClass ? (
+            <CreateClass
+              gradeNumList={state.gradeNumList}
+              classArray={classArray}
+              subjectList={state.subjectList}
+              onGetList={() => getList()}
+              onClose={() => (state.showaddClass = false)}
+            />
+          ) : null}
+        </NModal>
+        <NModal
+          v-model:show={state.showResetClass}
+          style={{ width: '500px' }}
+          preset="card"
+          class={['modalTitle background']}
+          title={'修改声部'}>
+          {state.showResetClass ? (
+            <ResetSubject
+              activeRow={state.activeRow}
+              subjectList={state.subjectList}
+              onGetList={() => getList()}
+              onClose={() => (state.showResetClass = false)}
+            />
+          ) : null}
         </NModal>
 
         <NModal
-          v-model:show={state.goCourseVisiable}
+          v-model:show={state.showSubjectClass}
+          style={{ width: '500px' }}
           preset="card"
-          class={['modalTitle background', styles.chioseModel]}
-          title={'选择课件'}>
-          <TrainDetail
-            activeRow={state.activeRow}
-            onClose={() => (state.goCourseVisiable = false)}
-            onPreview={(item: any) => {
-              if (globalState.application) {
-                state.previewModal = true;
-                state.previewParams = {
-                  ...item
-                };
-                globalState.application= window.matchMedia('(display-mode: standalone)').matches
-                fscreen()
-              } else {
-                const { href } = router.resolve({
-                  path: '/attend-class',
+          class={['modalTitle background']}
+          title={'修改声部'}>
+          {state.showSubjectClass ? (
+            <UpdateSubject
+              activeRow={state.activeRow}
+              onGetList={() => getList()}
+              onConfirm={(item: any) => {
+                //
+                router.push({
+                  path: '/prepare-lessons',
                   query: {
                     ...item
                   }
                 });
-                window.open(href, +new Date() + '');
-              }
-            }}></TrainDetail>
+              }}
+              onClose={() => (state.showSubjectClass = false)}
+            />
+          ) : null}
         </NModal>
 
         {/* 上课弹窗 */}

+ 3 - 1
src/views/classList/modals/Gotoclass.tsx

@@ -56,7 +56,7 @@ export default defineComponent({
           rows: 99
         });
         if (data.rows && data.rows.length > 0) {
-          await courseScheduleStart({
+          const res = await courseScheduleStart({
             lessonCoursewareKnowledgeDetailId: forms.chapter,
             classGroupId: props.activeRow.id
           });
@@ -64,6 +64,8 @@ export default defineComponent({
           emit('close');
           emit('preview', {
             type: 'class',
+            classId: res.data, // 上课编号
+            lessonCourseId: forms.category,
             classGroupId: props.activeRow.id,
             subjectId: forms.subjectId,
             detailId: forms.chapter

+ 23 - 3
src/views/classList/modals/createClass.tsx

@@ -23,6 +23,10 @@ export default defineComponent({
     classArray: {
       type: Array,
       default: () => []
+    },
+    subjectList:{
+      type: Array,
+      default: () => []
     }
   },
   name: 'resetStudent',
@@ -35,13 +39,12 @@ export default defineComponent({
     const foemsRef = ref();
     const createClassForm = reactive({
       currentGradeNum: null,
-      currentClass: null
+      currentClass: null,
+      subjectId:null
     });
     const submitForms = () => {
       data.uploading = true
       foemsRef.value.validate(async (error: any) => {
-        console.log(createClassForm);
-        console.log(error);
         if (error) {
           return;
         }
@@ -94,6 +97,23 @@ export default defineComponent({
               } as any)}
               v-model:value={createClassForm.currentClass}></CSelect>
           </NFormItem>
+          <NFormItem
+            path="subjectId"
+            rule={[
+              {
+                required: true,
+                message: '请选择声部'
+              }
+            ]}>
+            <CSelect
+              {...({
+                style: { width: '400px' },
+                options: props.subjectList,
+                placeholder: '选择声部',
+                clearable: true
+              } as any)}
+              v-model:value={createClassForm.subjectId}></CSelect>
+          </NFormItem>
         </NForm>
         <NSpace class={styles.btnGroup} justify="center">
           <NButton round onClick={() => emit('close')}>

+ 106 - 0
src/views/classList/modals/resetSubject.tsx

@@ -0,0 +1,106 @@
+import {
+  NButton,
+  NSpace,
+  useMessage,
+  NForm,
+  NFormItem,
+  NSelect
+} from 'naive-ui';
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from '../index.module.less';
+import CSelect from '/src/components/CSelect';
+import { resetClass } from '../api';
+export default defineComponent({
+  props: {
+    activeRow: {
+      type: Object,
+      default: () => ({ id: '' })
+    },
+    gradeNumList: {
+      type: Array,
+      default: () => []
+    },
+    classArray: {
+      type: Array,
+      default: () => []
+    },
+    subjectList: {
+      type: Array,
+      default: () => []
+    }
+  },
+  name: 'resetStudent',
+  emits: ['close', 'getList'],
+  setup(props, { emit }) {
+    const data = reactive({
+      uploading: false
+    });
+    const message = useMessage();
+    const foemsRef = ref();
+    const createClassForm = reactive({
+      currentGradeNum: null,
+      currentClass: null,
+      subjectId: null,
+      id: null
+    });
+    onMounted(() => {
+      createClassForm.currentGradeNum = props.activeRow.currentGradeNum;
+      createClassForm.currentClass = props.activeRow.currentClass;
+      createClassForm.subjectId = props.activeRow.subjectId;
+      createClassForm.id = props.activeRow.id;
+    });
+    const submitForms = () => {
+      data.uploading = true;
+      foemsRef.value.validate(async (error: any) => {
+        if (error) {
+          return;
+        }
+        try {
+          const res = await resetClass({ ...createClassForm });
+          message.success('修改成功');
+          emit('close');
+          emit('getList');
+          data.uploading = false;
+        } catch (e) {
+          data.uploading = false;
+          console.log(e);
+        }
+      });
+    };
+    return () => (
+      <div class={[styles.addClass]}>
+        <NForm label-placement="left" model={createClassForm} ref={foemsRef}>
+          <NFormItem
+            path="subjectId"
+            rule={[
+              {
+                required: true,
+                message: '请选择声部'
+              }
+            ]}>
+            <CSelect
+              {...({
+                style: { width: '400px' },
+                options: props.subjectList,
+                placeholder: '选择声部',
+                clearable: true
+              } as any)}
+              v-model:value={createClassForm.subjectId}></CSelect>
+          </NFormItem>
+        </NForm>
+        <NSpace class={styles.btnGroup} justify="center">
+          <NButton round onClick={() => emit('close')}>
+            取消
+          </NButton>
+          <NButton
+            round
+            loading={data.uploading}
+            onClick={() => submitForms()}
+            type="primary">
+            保存
+          </NButton>
+        </NSpace>
+      </div>
+    );
+  }
+});

+ 109 - 0
src/views/classList/modals/updateSubject.tsx

@@ -0,0 +1,109 @@
+import { NButton, NSpace, useMessage, NForm, NFormItem } from 'naive-ui';
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from '../index.module.less';
+import CSelect from '/src/components/CSelect';
+import { resetClass } from '../api';
+import { useCatchStore } from '/src/store/modules/catchData';
+export default defineComponent({
+  props: {
+    activeRow: {
+      type: Object,
+      default: () => ({ id: '' })
+    },
+    subjectList: {
+      type: Array,
+      default: () => []
+    }
+  },
+  name: 'resetStudent',
+  emits: ['close', 'getList', 'confirm'],
+  setup(props, { emit }) {
+    const catchStore = useCatchStore();
+    const data = reactive({
+      uploading: false
+    });
+    const message = useMessage();
+    const subjectList = ref([] as any);
+    const foemsRef = ref();
+    const createClassForm = reactive({
+      currentGradeNum: null,
+      currentClass: null,
+      subjectId: null,
+      id: null
+    });
+    onMounted(async () => {
+      // 获取教材分类列表
+      await catchStore.getSubjects();
+      subjectList.value = [
+        { id: null, name: '选择声部' },
+        ...catchStore.getSubjectList
+      ];
+
+      createClassForm.currentGradeNum = props.activeRow.currentGradeNum;
+      createClassForm.currentClass = props.activeRow.currentClass;
+      createClassForm.subjectId = props.activeRow.subjectId;
+      createClassForm.id = props.activeRow.id;
+    });
+    const submitForms = () => {
+      foemsRef.value.validate(async (error: any) => {
+        if (error) {
+          return;
+        }
+        data.uploading = true;
+        try {
+          await resetClass({ ...createClassForm });
+          message.success('修改成功');
+          emit('close');
+          emit('confirm', {
+            lastUseCoursewareId: props.activeRow.lessonCoursewareId,
+            unit: props.activeRow.lessonCoursewareKnowledgeDetailId,
+            subjectId: createClassForm.subjectId,
+            name: props.activeRow.name, // 班级名称
+            classGroupId: props.activeRow.id // 班级编号
+          });
+          emit('getList');
+        } catch (e) {
+          console.log(e);
+        }
+        data.uploading = false;
+      });
+    };
+    return () => (
+      <div class={[styles.addClass]}>
+        <NForm label-placement="left" model={createClassForm} ref={foemsRef}>
+          <NFormItem
+            path="subjectId"
+            rule={[
+              {
+                required: true,
+                message: '请选择声部'
+              }
+            ]}>
+            <CSelect
+              {...({
+                style: { width: '400px' },
+                options: subjectList.value,
+                placeholder: '选择声部',
+                labelField: 'name',
+                valueField: 'id',
+                clearable: true
+              } as any)}
+              v-model:value={createClassForm.subjectId}></CSelect>
+          </NFormItem>
+        </NForm>
+        <NSpace class={styles.btnGroup} justify="center">
+          <NButton round onClick={() => emit('close')}>
+            取消
+          </NButton>
+          <NButton
+            round
+            loading={data.uploading}
+            onClick={() => submitForms()}
+            type="primary">
+            确定
+          </NButton>
+        </NSpace>
+      </div>
+    );
+  }
+});

BIN
src/views/home/images/home/home-1.png


BIN
src/views/home/images/home/home-2.png


BIN
src/views/home/images/home/home-bg-1.png


BIN
src/views/home/images/home/home-bg-2.png


BIN
src/views/home/images/home/home-text-1.png


BIN
src/views/home/images/home/home-text-2.png


+ 50 - 80
src/views/home/index.module.less

@@ -85,6 +85,9 @@
   position: relative;
   display: flex;
   align-items: center;
+  height: 490px;
+  background: url('./images/home/home-bg-1.png') no-repeat right bottom, url('./images/home/home-bg-2.png') no-repeat left top #E6F2FF;
+  background-size: 69px, 68px 61px;
 
   .userName {
     left: 218px;
@@ -127,7 +130,7 @@
 
   .blackborad {
     padding-left: 182px;
-    padding-right: 85px;
+    padding-right: 60px;
 
     .blackBoardBg {
       width: 466px;
@@ -137,97 +140,60 @@
 }
 
 .applyContainer {
-  width: 390px;
-  padding: 24px 0;
-
-  .applyTitle {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    height: 42px;
+  width: 450px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-top: -45px;
+  // padding: 24px 0;
 
-    .className {
-      font-size: 26px;
-      font-family: Alibaba-PuHuiTi-B, Alibaba-PuHuiTi;
-      font-weight: bold;
-      color: #323DC5;
-      line-height: 35px;
 
+  .applyItem {
+    width: 211px;
+    height: 296px;
 
-      &::after {
-        content: ' ';
-        margin-left: 8px;
-        display: inline-block;
-        width: 16px;
-        height: 16px;
-        background: url('./images/icon-right-arrow.png') no-repeat center;
-        background-size: contain;
-      }
+    p {
+      text-align: justify;
+      font-size: 14px;
+      font-weight: 600;
+      color: #FFFFFF;
+      line-height: 20px;
+      padding: 175px 22px 0;
     }
-  }
 
-  .informations {
-    line-height: 40px;
-    height: 40px;
-    font-size: 14px;
-    color: #6AA1E0;
-    display: flex;
-  }
+    .applyBtn {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      margin: 19px auto 0;
+      width: 158px;
+      height: 40px;
+      background: #FFFFFF;
+      border-radius: 10px;
+      cursor: pointer;
+    }
 
-  .selectContainer {
-    display: flex;
-    align-items: center;
-    width: 100%;
-    background-color: #fff;
-    border-radius: 10px;
+    .applyBtn1 img {
+      width: 58px;
+      height: 20px;
+    }
 
-    img {
-      position: absolute;
-      left: 20px;
-      top: 10px;
-      width: 32px;
-      height: 32px;
-      z-index: 9;
+    .applyBtn2 img {
+      width: 77px;
+      height: 19px;
     }
   }
 
-  .startClass,
-  .beforClass {
-    --n-height: 60px !important;
-    min-width: 182px !important;
-    --n-font-size: max(20px, 16px) !important;
-    font-weight: bold !important;
+  .applyItem1 {
+    background: url('./images/home/home-1.png') no-repeat center;
+    background-size: contain;
   }
 
-  .btnGroup {
-    margin-top: 10px;
-    width: 100%;
+  .applyItem2 {
+    background: url('./images/home/home-2.png') no-repeat center;
+    background-size: contain;
   }
 
-  :global {
-    .n-avatar {
-      --n-merged-size: 42px !important;
-      --n-merged-color: #3E9FFF !important;
-    }
-
-    .n-base-selection {
-      --n-height: 52px !important;
-      --n-border: none !important;
-      --n-border-radius: 10px !important;
-      --n-padding-single: 0 26px 0 0px !important;
-      // --n-placeholder-color: #626264 !important;
-      padding-left: 70px;
-    }
-
-    .n-form-item {
-      --n-feedback-height: 20px !important;
-    }
-
-    .n-base-selection.n-base-selection--disabled {
-      background-color: var(--n-color-disabled);
-      cursor: not-allowed;
-    }
-  }
 }
 
 .toolContainer {
@@ -848,7 +814,6 @@
   margin-top: 40px;
 }
 
-
 .showModalTone {
   width: 500px;
 
@@ -866,9 +831,14 @@
       }
     }
   }
+}
 
+.attendClassModal {
+  width: 800px;
+  border-radius: 16px;
+  overflow: hidden;
 }
 
 .showUpdatePassword {
   width: 514px;
-}
+}

+ 99 - 357
src/views/home/index.tsx

@@ -1,22 +1,10 @@
-import {
-  defineComponent,
-  onBeforeUnmount,
-  onMounted,
-  reactive,
-  ref
-} from 'vue';
+import { defineComponent, onMounted, reactive, ref } from 'vue';
 import styles from './index.module.less';
 import {
   NImage,
   NButton,
-  NAvatarGroup,
-  NForm,
-  NFormItem,
-  NSelect,
   NPopselect,
-  NSpace,
   NModal,
-  NCascader,
   useMessage,
   NSpin
 } from 'naive-ui';
@@ -25,10 +13,9 @@ import defultHeade from '@/components/layout/images/teacherIcon.png';
 import blackBoardBg from './images/blackboard_bg.png';
 import teacherMan from './images/teacher_man.png';
 import teacherWoman from './images/teacher_woman.png';
-import iconLession from './images/icon-lession.png';
-import iconBook from './images/icon-book.png';
-import iconDetail from './images/icon-detail.png';
-import iconSubject from './images/icon-subject.png';
+
+import homeText1 from './images/home/home-text-1.png';
+import homeText2 from './images/home/home-text-2.png';
 import iconTo from './images/icon-to.png';
 import t1 from './images/t1.png';
 import t2 from './images/t2.png';
@@ -38,31 +25,19 @@ import { useUserStore } from '/src/store/modules/users';
 import SelectClass from './modals/selectClass';
 import dayjs from 'dayjs';
 import { gradeToCN, weekToCN } from '/src/utils/contants';
-import { getCLassStudent, getCourseChapter } from '../classList/api';
 import { useCatchStore } from '/src/store/modules/catchData';
-import { useThrottleFn } from '@vueuse/core';
-import {
-  bookVersionPage,
-  courseScheduleStart,
-  lessonCoursewarePage,
-  queryCourseware
-} from '../prepare-lessons/api';
-import TheNoticeBar from '/src/components/TheNoticeBar';
 import TeachGroup from './modals/teachGroup';
 import { classGroupList, courseSchedulePage } from './api';
 import TheEmpty from '/src/components/TheEmpty';
-import { setTabsCaches } from '/src/hooks/use-async';
 import HomeGuide from '/src/custom-plugins/guide-page/home-guide';
 import TimerMeter from '/src/components/timerMeter';
 import { vaildUrl } from '/src/utils/urlUtils';
-import toneImage from '@/components/layout/images/toneImage.png';
 import { px2vw } from '/src/utils';
 import PlaceholderTone from '@/components/layout/modals/placeholderTone';
-import { state } from '/src/state';
-import { fscreen } from '@/utils/index';
 import PreviewWindow from '../preview-window';
 import UpdatePassword from '/src/components/layout/modals/update-password';
-import { state as globalState } from '/src/state';
+import AttendClass from '../prepare-lessons/model/attend-class';
+import { state } from '/src/state';
 export const formatDateToDay = () => {
   const hours = dayjs().hour();
   if (hours < 12) {
@@ -86,8 +61,7 @@ export default defineComponent({
     const showModalTone = ref(false);
     const showModalTime = ref(false);
     const forms = reactive({
-      applyClassItem: {} as any, // 选择的内容
-      applyStatus: false,
+      showAttendClass: false,
       useStatus: false,
       studentList: [] as any,
       bookVersionId: null,
@@ -117,28 +91,6 @@ export default defineComponent({
 
     // 学生列表
     // getStdentList
-    // 应用选择年级班级
-    const onApplyConfirm = async (item: any) => {
-      try {
-        //
-        const { data } = await getCLassStudent({
-          page: 1,
-          rows: 999,
-          classGroupId: item.classGroupId
-        });
-
-        const temps = data.rows || [];
-        temps.forEach((row: any) => {
-          forms.studentList.push({
-            name: row.nickname,
-            src: row.avatar
-          });
-        });
-        forms.applyClassItem = item;
-      } catch {
-        //
-      }
-    };
 
     const onUseConfirm = (item: any) => {
       forms.classSelect = {
@@ -149,71 +101,6 @@ export default defineComponent({
 
       getCourseSchedulePage();
     };
-    const throttledFn = useThrottleFn(() => getLessonCourseware(), 500);
-    const getLessonCourseware = async () => {
-      forms.category = null;
-      forms.unit = null;
-      forms.category = null;
-      forms.loading = true;
-
-      try {
-        const { data } = await lessonCoursewarePage({
-          bookVersionId: forms.bookVersionId,
-          enableFlag: 1,
-          page: 1,
-          rows: 99,
-          type: 'COURSEWARE'
-          // currentGradeNum: forms.applyClassItem.currentGradeNum
-        });
-
-        forms.list = data.rows.map((item: any) => {
-          return {
-            label: item.name,
-            value: item.id
-          };
-        });
-      } catch {
-        //
-      }
-
-      forms.loading = false;
-    };
-
-    const getunitList = async () => {
-      forms.unit = null;
-      try {
-        if (forms.category) {
-          const res = await getCourseChapter(forms.category);
-          forms.unitList = res.data.lessonList.map((item: any) => {
-            return { ...item, label: item.name, value: item.id };
-          });
-        } else {
-          forms.unitList = [];
-        }
-      } catch (e) {
-        console.log(e);
-      }
-    };
-    const getVersion = async () => {
-      forms.unit = null;
-
-      try {
-        const { data } = await bookVersionPage({
-          page: 1,
-          rows: 99,
-          type: 'COURSEWARE'
-        });
-        const temp = data.rows || [];
-        temp.forEach((item: any) => {
-          forms.musicTagList.push({
-            id: item.id,
-            name: item.name
-          });
-        });
-      } catch {
-        //
-      }
-    };
 
     // 获取年级班级
     const getClassList = async () => {
@@ -317,7 +204,6 @@ export default defineComponent({
           value: item.id
         };
       });
-      getVersion();
 
       if (!userStore.getUserInfo.account.updatePasswordFlag) {
         showUpdatePassword.value = true;
@@ -326,56 +212,56 @@ export default defineComponent({
       }
     });
 
-    const formsRef = ref();
-    const gotoClassPage = () => {
-      formsRef.value.validate(async (error: any) => {
-        if (error) return;
-        try {
-          const { data } = await queryCourseware({
-            coursewareDetailKnowledgeId: forms.unit,
-            subjectId: forms.subjectId,
-            page: 1,
-            rows: 99
-          });
-          if (data.rows && data.rows.length > 0) {
-            await courseScheduleStart({
-              lessonCoursewareKnowledgeDetailId: forms.unit,
-              classGroupId: forms.applyClassItem?.classGroupId
-            });
-
-            if (window.matchMedia('(display-mode: standalone)').matches) {
-              forms.showPreview = true;
-              forms.itemPreview = {
-                type: 'class',
-                classGroupId: forms.applyClassItem?.classGroupId,
-                subjectId: forms.subjectId,
-                detailId: forms.unit
-              };
-              globalState.application = window.matchMedia(
-                '(display-mode: standalone)'
-              ).matches;
-              // 加全屏
-              fscreen();
-            } else {
-              const { href } = router.resolve({
-                path: '/attend-class',
-                query: {
-                  type: 'class',
-                  classGroupId: forms.applyClassItem?.classGroupId,
-                  subjectId: forms.subjectId,
-                  detailId: forms.unit
-                }
-              });
-              window.open(href, +new Date() + '');
-            }
-          } else {
-            message.error('当前章节暂无课件,请重新选择');
-          }
-        } catch {
-          //
-        }
-      });
-    };
+    // const formsRef = ref();
+    // const gotoClassPage = () => {
+    //   formsRef.value.validate(async (error: any) => {
+    //     if (error) return;
+    //     try {
+    //       const { data } = await queryCourseware({
+    //         coursewareDetailKnowledgeId: forms.unit,
+    //         subjectId: forms.subjectId,
+    //         page: 1,
+    //         rows: 99
+    //       });
+    //       if (data.rows && data.rows.length > 0) {
+    //         await courseScheduleStart({
+    //           lessonCoursewareKnowledgeDetailId: forms.unit,
+    //           classGroupId: forms.applyClassItem?.classGroupId
+    //         });
+
+    //         if (window.matchMedia('(display-mode: standalone)').matches) {
+    //           forms.showPreview = true;
+    //           forms.itemPreview = {
+    //             type: 'class',
+    //             classGroupId: forms.applyClassItem?.classGroupId,
+    //             subjectId: forms.subjectId,
+    //             detailId: forms.unit
+    //           };
+    //           globalState.application = window.matchMedia(
+    //             '(display-mode: standalone)'
+    //           ).matches;
+    //           // 加全屏
+    //           fscreen();
+    //         } else {
+    //           const { href } = router.resolve({
+    //             path: '/attend-class',
+    //             query: {
+    //               type: 'class',
+    //               classGroupId: forms.applyClassItem?.classGroupId,
+    //               subjectId: forms.subjectId,
+    //               detailId: forms.unit
+    //             }
+    //           });
+    //           window.open(href, +new Date() + '');
+    //         }
+    //       } else {
+    //         message.error('当前章节暂无课件,请重新选择');
+    //       }
+    //     } catch {
+    //       //
+    //     }
+    //   });
+    // };
     const clearStorng = () => {
       localStorage.removeItem('teacher-guideInfo');
       forms.showGuide = false;
@@ -388,24 +274,6 @@ export default defineComponent({
       <div class={styles.homeWrap}>
         <div class={styles.homeInfoLeft}>
           <div class={styles.homeBanner}>
-            {/* <div class={styles.welcomeInfo}>
-              <div class={styles.userInfo}>
-                <div class={styles.userName}>
-                  Hi,{userStore.getUserInfo?.nickname} {formatDateToDay()}~
-                </div>
-                <div class={styles.userTime}>
-                  {dayjs().format('MM月DD日')},{weekToCN[dayjs().day()]}
-                </div>
-              </div>
-              <div class={styles.userTips}>
-                <span>欢迎您使用酷乐秀课堂乐器数字化教学平台!</span>
-                <NButton color="#40A1FF" round class={styles.guide_btn} {...{id:'home-1'}}  onClick={()=>clearStorng()}>
-                  功能引导
-                  <i></i>
-                </NButton>
-              </div>
-            </div> */}
-            {/* <div class={styles.centerInfo}>11111</div> */}
             <div class={styles.applyInfo} id="home-1">
               <div class={styles.centerInfo} id="home-0"></div>
 
@@ -413,9 +281,6 @@ export default defineComponent({
                 <div class={styles.userName}>
                   Hi,{userStore.getUserInfo?.nickname} {formatDateToDay()}~
                 </div>
-                {/* <div class={styles.userTime}>
-                  {dayjs().format('MM月DD日')},{weekToCN[dayjs().day()]}
-                </div> */}
               </div>
               {userStore.getUserInfo.gender === 1 ? (
                 <img src={teacherMan} class={styles.teacherMan} />
@@ -427,154 +292,29 @@ export default defineComponent({
                 <img src={blackBoardBg} class={styles.blackBoardBg} />
               </div>
               <div class={styles.applyContainer}>
-                <div class={styles.applyTitle}>
-                  <span
-                    class={styles.className}
-                    onClick={() => (forms.applyStatus = true)}>
-                    {forms.applyClassItem.name || '请选择班级'}
-                  </span>
-                  <NAvatarGroup options={forms.studentList} max={5} />
+                <div class={[styles.applyItem, styles.applyItem1]}>
+                  <p>可根据声部进行备课,更好的规划教学哦~</p>
+                  <div
+                    class={[styles.applyBtn, styles.applyBtn1]}
+                    onClick={() => {
+                      // 备课
+                      router.push({
+                        path: '/prepare-lessons'
+                      });
+                    }}>
+                    <img src={homeText1} />
+                  </div>
                 </div>
-                <div class={styles.informations}>
-                  {forms.applyClassItem.lastStudy ? (
-                    <>
-                      <span style="flex-shrink: 0;">上次课程:</span>
-                      <TheNoticeBar text={forms.applyClassItem.lastStudy} />
-                    </>
-                  ) : (
-                    ''
-                  )}
+                <div class={[styles.applyItem, styles.applyItem2]}>
+                  <p>点击这里,选择班级快速进入备课区域</p>
+                  <div
+                    class={[styles.applyBtn, styles.applyBtn2]}
+                    onClick={() => {
+                      forms.showAttendClass = true;
+                    }}>
+                    <img src={homeText2} />
+                  </div>
                 </div>
-
-                <NForm showLabel={false} ref={formsRef} model={forms}>
-                  <NFormItem
-                    path="bookVersionId"
-                    rule={[
-                      {
-                        required: true,
-                        message: '',
-                        trigger: ['blur', 'change']
-                      }
-                    ]}>
-                    <div class={styles.selectContainer}>
-                      <img src={iconLession} />
-                      <NSelect
-                        placeholder="请选择教材版本"
-                        disabled={
-                          forms.applyClassItem?.currentGradeNum ? false : true
-                        }
-                        clearable
-                        options={[...forms.musicTagList]}
-                        labelField="name"
-                        valueField="id"
-                        v-model:value={forms.bookVersionId}
-                        onUpdate:value={() => throttledFn()}
-                      />
-                    </div>
-                  </NFormItem>
-                  <NFormItem
-                    path="category"
-                    rule={[
-                      {
-                        required: true,
-                        message: '',
-                        trigger: ['blur', 'change']
-                      }
-                    ]}>
-                    <div class={styles.selectContainer}>
-                      <img src={iconBook} />
-                      <NSelect
-                        placeholder="请选择册别"
-                        options={[...forms.list]}
-                        clearable
-                        disabled={!forms.bookVersionId}
-                        v-model:value={forms.category}
-                        onUpdate:value={() => getunitList()}
-                      />
-                    </div>
-                  </NFormItem>
-                  <NFormItem
-                    path="unit"
-                    rule={[
-                      {
-                        required: true,
-                        message: '',
-                        trigger: ['blur', 'change']
-                      }
-                    ]}>
-                    <div class={styles.selectContainer}>
-                      <img src={iconDetail} />
-                      <NCascader
-                        disabled={!forms.category}
-                        {...({
-                          options: [...forms.unitList],
-                          placeholder: '选择章节',
-                          clearable: true
-                        } as any)}
-                        childrenField="knowledgeList"
-                        valueField="id"
-                        labelField="name"
-                        v-model:value={forms.unit}
-                        checkStrategy="child"
-                        expandTrigger="hover"
-                      />
-                    </div>
-                  </NFormItem>
-                  <NFormItem
-                    path="subjectId"
-                    rule={[
-                      {
-                        required: true,
-                        message: '',
-                        trigger: ['blur', 'change'],
-                        type: 'number'
-                      }
-                    ]}>
-                    <div class={styles.selectContainer}>
-                      <img src={iconSubject} />
-                      <NSelect
-                        {...({
-                          options: [...forms.subjectList],
-                          placeholder: '选择乐器',
-                          clearable: true
-                        } as any)}
-                        v-model:value={forms.subjectId}
-                      />
-                    </div>
-                  </NFormItem>
-                  <NSpace class={styles.btnGroup} justify="center">
-                    <NButton
-                      round
-                      block
-                      class={styles.startClass}
-                      color="#FF6E4C"
-                      onClick={gotoClassPage}>
-                      开始上课
-                    </NButton>
-                    <NButton
-                      round
-                      block
-                      class={styles.beforClass}
-                      color="#5B64D1"
-                      onClick={() => {
-                        // 携带默认数据显示
-                        formsRef.value.validate(async (error: any) => {
-                          if (error) return;
-
-                          router.push({
-                            path: '/prepare-lessons',
-                            query: {
-                              lastUseCoursewareId: forms.category,
-                              unit: forms.unit,
-                              subjectId: forms.subjectId
-                            }
-                          });
-                        });
-                      }}>
-                      去备课
-                    </NButton>
-                  </NSpace>
-                </NForm>
               </div>
             </div>
           </div>
@@ -724,25 +464,6 @@ export default defineComponent({
           </div>
         </div>
 
-        {/* 添加自定义教材 */}
-        <NModal
-          v-model:show={forms.applyStatus}
-          preset="card"
-          showIcon={false}
-          class={['modalTitle background', styles.assignHomework]}
-          title={'选择班级'}
-          blockScroll={false}>
-          <SelectClass
-            useDetail={{
-              currentGradeNum: forms.applyClassItem.currentGradeNum,
-              classGroupId: forms.applyClassItem.classGroupId
-            }}
-            gradeList={forms.gradeList}
-            onConfirm={(item: any) => onApplyConfirm(item)}
-            onClose={() => (forms.applyStatus = false)}
-          />
-        </NModal>
-
         <NModal
           v-model:show={forms.useStatus}
           preset="card"
@@ -831,6 +552,27 @@ export default defineComponent({
             }}
           />
         </NModal>
+
+        <NModal
+          v-model:show={forms.showAttendClass}
+          preset="card"
+          showIcon={false}
+          class={['modalTitle background', styles.attendClassModal]}
+          title={'选择班级'}
+          blockScroll={false}>
+          <AttendClass
+            onClose={() => (forms.showAttendClass = false)}
+            type="change"
+            onConfirm={(item: any) => {
+              router.push({
+                path: '/prepare-lessons',
+                query: {
+                  ...item
+                }
+              });
+            }}
+          />
+        </NModal>
       </div>
     );
   }

+ 3 - 1
src/views/home/modals/chioseModal.tsx

@@ -53,14 +53,16 @@ export default defineComponent({
           rows: 99
         });
         if (data.rows && data.rows.length > 0) {
-          await courseScheduleStart({
+          const res = await courseScheduleStart({
             lessonCoursewareKnowledgeDetailId: forms.chapter,
             classGroupId: forms.currentClass
           });
           emit('close');
           emit('preview', {
             type: 'class',
+            classId: res.data, // 上课编号
             classGroupId: forms.currentClass,
+            lessonCourseId: forms.category, // 册别编号
             subjectId: forms.subjectId,
             detailId: forms.chapter
           });

+ 1 - 1
src/views/login/index.module.less

@@ -368,4 +368,4 @@
     cursor: pointer;
   }
 
-}
+}

+ 110 - 83
src/views/login/index.tsx

@@ -1,17 +1,32 @@
-import { defineComponent, onBeforeUnmount, reactive, ref, onMounted } from 'vue';
+import {
+  defineComponent,
+  onBeforeUnmount,
+  reactive,
+  ref,
+  onMounted
+} from 'vue';
 import loginStyles from './images/login_styles.png';
 import loginLeft from './images/login-left.png';
 import loginRight from './images/loginright.png';
 import colLogo from './images/colLogo.png';
 import CodeLogin from './components/codeLogin';
 import PwdLogin from './components/pwdLogin';
-import { NTabs, NTabPane, useDialog, NModal, NButton, NSpace,NAlert, NImage } from 'naive-ui';
+import {
+  NTabs,
+  NTabPane,
+  useDialog,
+  NModal,
+  NButton,
+  NSpace,
+  NAlert,
+  NImage
+} from 'naive-ui';
 import styles from './index.module.less';
 import ForgotPassword from './components/forgotPassword';
-import moveTop from './images/moveTopBg.png'
-import dingPng from './images/ding.png'
-import closeAble from './images/closeAble.png'
-import infoIcon from './images/infoIcon.png'
+import moveTop from './images/moveTopBg.png';
+import dingPng from './images/ding.png';
+import closeAble from './images/closeAble.png';
+import infoIcon from './images/infoIcon.png';
 import { state } from '/src/state';
 export default defineComponent({
   name: 'login-page',
@@ -20,14 +35,14 @@ export default defineComponent({
     const NavsValue = ref('pwdLogin');
     const pwdLoginRef = ref();
     const forgotPasswordRef = ref();
-    const popEvent = ref()
+    const popEvent = ref();
     const dialog = useDialog();
-    const showModalMask = ref(false)
+    const showModalMask = ref(false);
     const checkInstall = async (event: any) => {
       event.preventDefault();
-      console.log('checkInstall', event)
-      popEvent.value = event
-      console.log('beforeoutcome')
+      console.log('checkInstall', event);
+      popEvent.value = event;
+      console.log('beforeoutcome');
       // const { outcome } = await event.userChoice;
       // console.log(outcome, 'outcome')
       // setTimeout(async function () {
@@ -40,68 +55,74 @@ export default defineComponent({
 
       // }, 2000)
 
-
       if (window.matchMedia('(display-mode: standalone)').matches) {
-        state.application = window.matchMedia('(display-mode: standalone)').matches
-        console.log('应用内打开')
-      }else{
-        console.log(popEvent.value,'popEvent.value')
-        if(popEvent.value){
-        showModalMask.value = true;
-        setTimeout(()=>{
-          const btn = document.querySelector('#submitBtn')
-          console.log(btn)
-          if(btn){
-            btn.addEventListener('click', () => {
-              showModalMask.value = false;
-              if( !popEvent.value ) {
-                return;
-              }
-              popEvent.value.prompt();
-              popEvent.value.userChoice.then( (choiceResult:any) => {
-                if (choiceResult.outcome === 'accepted') {
-                  console.log('用户已同意添加到桌面')
-                  showModalMask.value = false;
-                } else {
-                  console.log('用户已取消添加到桌面')
-                  showModalMask.value = false;
+        state.application = window.matchMedia(
+          '(display-mode: standalone)'
+        ).matches;
+        console.log('应用内打开');
+      } else {
+        console.log(popEvent.value, 'popEvent.value');
+        if (popEvent.value) {
+          showModalMask.value = true;
+          setTimeout(() => {
+            const btn = document.querySelector('#submitBtn');
+            console.log(btn);
+            if (btn) {
+              btn.addEventListener('click', () => {
+                showModalMask.value = false;
+                if (!popEvent.value) {
+                  return;
                 }
-              })
-            })
-          }
-
-        },500)
-
+                popEvent.value.prompt();
+                popEvent.value.userChoice.then((choiceResult: any) => {
+                  if (choiceResult.outcome === 'accepted') {
+                    console.log('用户已同意添加到桌面');
+                    showModalMask.value = false;
+                  } else {
+                    console.log('用户已取消添加到桌面');
+                    showModalMask.value = false;
+                  }
+                });
+              });
+            }
+          }, 500);
+        }
       }
-      }
-
-    }
+    };
 
-
-    window.addEventListener('beforeinstallprompt', checkInstall, { once: true })
+    window.addEventListener('beforeinstallprompt', checkInstall, {
+      once: true
+    });
 
     onBeforeUnmount(() => {
-      window.removeEventListener('beforeinstallprompt', checkInstall)
-    })
+      window.removeEventListener('beforeinstallprompt', checkInstall);
+    });
 
     onMounted(async () => {
       // const relatedApps = await navigator?.getInstalledRelatedApps();
       //
-
-    })
-    const downChrome = ()=>{
+    });
+    const downChrome = () => {
       const agent = navigator.userAgent.toLowerCase();
-      const isMac = function () { return /macintosh|mac os x/i.test(navigator.userAgent); }();
-      if (agent.indexOf("win32") >= 0 || agent.indexOf("wow32") >= 0) {
-        window.open('https://appstore.ks3-cn-beijing.ksyuncs.com/ChromeStandaloneSetup32.exe');
+      const isMac = (function () {
+        return /macintosh|mac os x/i.test(navigator.userAgent);
+      })();
+      if (agent.indexOf('win32') >= 0 || agent.indexOf('wow32') >= 0) {
+        window.open(
+          'https://appstore.ks3-cn-beijing.ksyuncs.com/ChromeStandaloneSetup32.exe'
+        );
       }
-      if (agent.indexOf("win64") >= 0 || agent.indexOf("wow64") >= 0) {
-        window.open('https://appstore.ks3-cn-beijing.ksyuncs.com/ChromeStandaloneSetup64.exe');
+      if (agent.indexOf('win64') >= 0 || agent.indexOf('wow64') >= 0) {
+        window.open(
+          'https://appstore.ks3-cn-beijing.ksyuncs.com/ChromeStandaloneSetup64.exe'
+        );
       }
       if (isMac) {
-        window.open('https://appstore.ks3-cn-beijing.ksyuncs.com/googlechrome-mac.dmg');
+        window.open(
+          'https://appstore.ks3-cn-beijing.ksyuncs.com/googlechrome-mac.dmg'
+        );
       }
-    }
+    };
 
     return () => (
       <div class={styles['view-account']}>
@@ -151,18 +172,20 @@ export default defineComponent({
             </NTabs>
           )}
 
-
-<div class={styles.alertWrap}>
-          <div class={styles.alertInfo}>
-            <NImage src={infoIcon} class={styles.infoIcon} previewDisabled></NImage>
-            为了您更好的上课体验,推荐使用Chrome浏览器
+          <div class={styles.alertWrap}>
+            <div class={styles.alertInfo}>
+              <NImage
+                src={infoIcon}
+                class={styles.infoIcon}
+                previewDisabled></NImage>
+              为了您更好的上课体验,推荐使用Chrome浏览器
+            </div>
+            <div class={styles.down} onClick={downChrome}>
+              立即下载
+            </div>
           </div>
-          <div class={styles.down} onClick={downChrome}>立即下载</div>
-         </div>
         </div>
-        <NModal
-          v-model:show={showModalMask.value}
-          >
+        <NModal v-model:show={showModalMask.value}>
           {/* <div
             onClick={() => {
               showModalTone.value = false;
@@ -175,24 +198,28 @@ export default defineComponent({
           <div class={styles.downMove}>
             <img src={dingPng} class={styles.dingPng} alt="" />
             <img src={moveTop} class={styles.downMoveBg} alt="" />
-            <img src={closeAble} class={styles.closeAble} onClick={()=>{
-              showModalMask.value = false
-            }} alt="" />
+            <img
+              src={closeAble}
+              class={styles.closeAble}
+              onClick={() => {
+                showModalMask.value = false;
+              }}
+              alt=""
+            />
             <h2>温馨提示</h2>
-            <p>检测到您尚未安装“音乐数字课堂”应用程序,为了更好的使用体验,是否立即下载?</p>
+            <p>
+              检测到您尚未安装“音乐数字课堂”应用程序,为了更好的使用体验,是否立即下载?
+            </p>
             {/* <NButton>确定</NButton> */}
             <NSpace style={{ padding: '25px 0 0 0' }} justify="center">
-          <NButton
-          {...{id
-          :'submitBtn'}}
-            class={styles.submitAppBtn}
-            round
-            type="primary"
-
-            >
-            立即下载
-          </NButton>
-        </NSpace>
+              <NButton
+                {...{ id: 'submitBtn' }}
+                class={styles.submitAppBtn}
+                round
+                type="primary">
+                立即下载
+              </NButton>
+            </NSpace>
           </div>
         </NModal>
       </div>

+ 40 - 4
src/views/natural-resources/model/add-teaching/index.tsx

@@ -30,6 +30,7 @@ import btnUp from '../../images/btn-up.png';
 import btnDown from '../../images/btn-down.png';
 import btnRemove from '../../images/btn-remove.png';
 import { lessonCoursewareDetail } from '/src/views/prepare-lessons/api';
+import { useCatchStore } from '/src/store/modules/catchData';
 export const BOOK_DATA = {
   grades: [
     { label: '一年级', value: 1 },
@@ -65,7 +66,8 @@ const createLesson = () => {
 const initState = () => ({
   id: null, // 教材id
   name: '',
-  currentGradeNum: null,
+  currentGradeNum: null as any,
+  subjectIds: null as any,
   // bookType: null, // 上下册
   coverImg: '', // 封面
   enableFlag: true, // 状态
@@ -83,6 +85,7 @@ export default defineComponent({
   },
   emits: ['close', 'confirm'],
   setup(props, { emit }) {
+    const catchStore = useCatchStore();
     const message = useMessage();
     const data = reactive({
       uploading: false
@@ -101,7 +104,12 @@ export default defineComponent({
     const handleSubmit = async () => {
       data.uploading = true;
       try {
-        await api_lessonCoursewareSave(form);
+        const { currentGradeNum, subjectIds, ...more } = form;
+        await api_lessonCoursewareSave({
+          currentGradeNum: currentGradeNum.join(','),
+          subjectIds: subjectIds.join(','),
+          ...more
+        });
         Object.assign(form, initState());
         message.success(props.item.id ? '保存成功' : '添加成功');
         emit('close', true);
@@ -113,11 +121,17 @@ export default defineComponent({
     };
 
     onMounted(async () => {
+      await catchStore.getSubjects();
       if (props.item.id) {
         const { data } = await lessonCoursewareDetail({ id: props.item.id });
         form.id = data.id;
         form.name = data.name;
-        form.currentGradeNum = data.currentGradeNum;
+        form.currentGradeNum = data.currentGradeNum
+          ? data.currentGradeNum.split(',').map((item: any) => Number(item))
+          : null;
+        form.subjectIds = data.subjectIds
+          ? data.subjectIds.split(',').map((item: any) => Number(item))
+          : null;
         // form.bookType = data.bookType;
         form.coverImg = data.coverImg;
         form.lessonList = [];
@@ -210,7 +224,7 @@ export default defineComponent({
                     required: true,
                     message: '请选择年级',
                     trigger: 'change',
-                    type: 'number'
+                    type: 'array'
                   }}>
                   <NSelect
                     style={{ minWidth: '360px' }}
@@ -218,7 +232,29 @@ export default defineComponent({
                     options={BOOK_DATA.grades}
                     v-model:value={form.currentGradeNum}
                     clearable
+                    multiple
                     filterable
+                    maxTagCount={3}
+                  />
+                </NFormItem>
+                <NFormItem
+                  path="subjectIds"
+                  style={{ width: '360px' }}
+                  rule={{
+                    required: true,
+                    message: '请选择声部',
+                    trigger: 'change',
+                    type: 'array'
+                  }}>
+                  <NSelect
+                    placeholder="请选择声部"
+                    options={catchStore.getSubjectList}
+                    v-model:value={form.subjectIds}
+                    clearable
+                    valueField="id"
+                    labelField="name"
+                    multiple
+                    maxTagCount={3}
                   />
                 </NFormItem>
                 {/* <NFormItem

+ 11 - 1
src/views/prepare-lessons/api.ts

@@ -13,7 +13,9 @@ export const lessonCoursewarePage = (params: any) => {
  * 备课 - 教学课件详情
  */
 export const lessonCoursewareDetail = (params: any) => {
-  return request.get('/edu-app/lessonCourseware/detail/' + params.id, {});
+  return request.get('/edu-app/lessonCourseware/detail/' + params.id, {
+    params: params
+  });
 };
 
 /**
@@ -151,3 +153,11 @@ export const tagUseCourseware = (params?: any) => {
     data: params
   });
 };
+/**
+ * 备课 - 更新上课记录
+ */
+export const courseScheduleUpdate = (params?: any) => {
+  return request.post('/edu-app/courseSchedule/update', {
+    data: params
+  });
+};

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

@@ -131,4 +131,4 @@
 
 .coursewareModal {
   width: 1150px;
-}
+}

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

@@ -11,6 +11,7 @@ import TheEmpty from '/src/components/TheEmpty';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
 import { useUserStore } from '/src/store/modules/users';
 import { useRoute } from 'vue-router';
+import { eventGlobal } from '/src/utils';
 
 export default defineComponent({
   name: 'directory-main',
@@ -22,13 +23,13 @@ export default defineComponent({
     const forms = reactive({
       lastUseCoursewareId: route.query.lastUseCoursewareId
         ? route.query.lastUseCoursewareId + ''
-        : null,
-      unit: route.query.unit ? route.query.unit + '' : null,
+        : null, // 专辑编号
+      unit: route.query.unit ? route.query.unit + '' : null, // 声部
       showSelectBookStatus: false,
       coursewareStatus: false
     });
 
-    console.log(forms.lastUseCoursewareId, 'route.query.lastUseCoursewareId');
+    // console.log(forms.lastUseCoursewareId, 'route.query.lastUseCoursewareId');
     const getLessonCourseware = async () => {
       try {
         const { data } = await lessonCoursewarePage({
@@ -50,9 +51,13 @@ export default defineComponent({
           if (selectItem) {
             prepareStore.setBaseCourseware(selectItem);
             id = selectItem.id;
+
+            prepareStore.setSubjectList(selectItem.subjectList || []);
           } else {
             prepareStore.setBaseCourseware(result[0]);
             id = result[0]?.id;
+
+            prepareStore.setSubjectList(result[0].subjectList || []);
           }
           setLastUseCoursewareId(id);
         }
@@ -133,11 +138,25 @@ export default defineComponent({
       }
     };
 
+    const onChangeClass = async (item: any) => {
+      show.value = true;
+      forms.lastUseCoursewareId = item.lastUseCoursewareId;
+      forms.unit = item.unit;
+      await getLessonCourseware();
+      await getLessonCoursewareDetail();
+      show.value = false;
+    };
+
     onMounted(async () => {
       show.value = true;
       await getLessonCourseware();
       await getLessonCoursewareDetail();
       show.value = false;
+
+      // 切换班级时触发
+      eventGlobal.on('onChangeClass', async (item: any) => {
+        onChangeClass(item);
+      });
     });
     return () => (
       <div class={styles.directoryList}>
@@ -269,6 +288,18 @@ export default defineComponent({
             onClose={() => (forms.coursewareStatus = false)}
             onConfirm={(item: any) => {
               prepareStore.setBaseCourseware(item);
+              prepareStore.setSubjectList(item.subjectList);
+              const index = item.subjectList.findIndex(
+                (subject: any) => subject.id == prepareStore.getSubjectId
+              );
+              // 判断教材里面是否有当前选择的声部,如果没有则默认选择第一个
+              if (index < 0) {
+                const subjectId = item.subjectList[0].id
+                  ? Number(item.subjectList[0].id)
+                  : '';
+                prepareStore.setSubjectId(subjectId);
+              }
+
               getLessonCoursewareDetail();
               setLastUseCoursewareId(item.id);
             }}

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

@@ -40,6 +40,7 @@ export default defineComponent({
       bookVersionId: null,
       keyword: null,
       currentGradeNum: null,
+      subjectId: null,
       removeVisiable: false,
       removeRow: {} as any
       // bookType: null
@@ -55,7 +56,8 @@ export default defineComponent({
           enableFlag: 1,
           bookVersionId: forms.bookVersionId,
           keyword: forms.keyword,
-          currentGradeNum: forms.currentGradeNum
+          currentGradeNum: forms.currentGradeNum,
+          subjectId: forms.subjectId
           // bookType: forms.bookType
         });
 
@@ -88,6 +90,7 @@ export default defineComponent({
     onMounted(async () => {
       // 获取教材分类列表
       try {
+        await catchStore.getSubjects();
         const { data } = await bookVersionPage({
           page: 1,
           rows: 99,
@@ -127,6 +130,20 @@ export default defineComponent({
             }}
           </NInput>
           <NSelect
+            placeholder="全部声部"
+            clearable
+            options={
+              [
+                { name: '全部声部', id: null },
+                ...catchStore.getSubjectList
+              ] as any
+            }
+            valueField="id"
+            labelField="name"
+            v-model:value={forms.subjectId}
+            onUpdate:value={() => throttledFn()}
+          />
+          <NSelect
             placeholder="全部版本"
             clearable
             options={[{ id: null, name: '全部版本' }, ...forms.musicTagList]}

+ 108 - 3
src/views/prepare-lessons/components/lesson-main/courseware/index.module.less

@@ -9,6 +9,72 @@
     color: #0378EC;
     font-size: 16px;
     line-height: 38px;
+    font-weight: 500;
+  }
+
+  .btnClassList {
+    :global {
+      .n-base-selection {
+        width: 200px;
+      }
+
+      .n-base-selection-label {
+        &::before {
+          margin-left: 12px;
+          content: ' ';
+          width: 24px;
+          height: 24px;
+          background: url('../../../images/icon-class-name.png') no-repeat center;
+          background-size: contain;
+        }
+      }
+
+      .n-base-selection-input {
+        padding-left: 8px;
+      }
+
+      .n-base-selection.n-base-selection--disabled {
+        cursor: pointer;
+      }
+
+      .n-base-selection.n-base-selection--disabled .n-base-selection-label {
+        background-color: #fff;
+        cursor: pointer;
+      }
+
+      .n-base-selection.n-base-selection--disabled .n-base-selection-label .n-base-selection-input {
+        color: var(--n-text-color);
+        cursor: pointer;
+      }
+
+      .n-base-selection .n-base-selection-overlay {
+        left: 24px;
+      }
+    }
+  }
+
+  .btnSubjectList {
+    :global {
+
+      .n-base-selection-label {
+        &::before {
+          margin-left: 12px;
+          content: ' ';
+          width: 24px;
+          height: 24px;
+          background: url('../../../images/icon-subject-name.png') no-repeat center;
+          background-size: contain;
+        }
+      }
+
+      .n-base-selection-input {
+        padding-left: 8px;
+      }
+
+      .n-base-selection .n-base-selection-overlay {
+        left: 24px;
+      }
+    }
   }
 
   :global {
@@ -55,15 +121,49 @@
       }
     }
   }
+
+  .btnClassStart {
+    background: #F44541 !important;
+    color: #fff !important;
+
+    :global {
+      .n-button__border {
+        border: 1px solid #F44541;
+      }
+    }
+  }
+
+  .btnItem {
+    display: flex;
+    align-items: center;
+
+    .btnTitle {
+      flex-shrink: 0;
+      font-size: 18px;
+      font-weight: 600;
+    }
+
+    &:last-child {
+      margin-left: 12px;
+    }
+  }
 }
 
 .listContainer {
   margin-top: 12px;
-  // // 52 + 28 + 38
-  max-height: calc(var(--window-page-lesson-height) - 148px);
+  // // 52 + 28 + 38 + 38 多余10像素空间
+  max-height: calc(var(--window-page-lesson-height) - 196px);
 
   .listSection {
-    min-height: calc(var(--window-page-lesson-height) - 148px);
+    min-height: calc(var(--window-page-lesson-height) - 196px);
+  }
+
+  &.listContainerDrag {
+    max-height: calc(var(--window-page-lesson-height) - 148px);
+
+    .listSection {
+      min-height: calc(var(--window-page-lesson-height) - 148px);
+    }
   }
 
   .emptySection {
@@ -180,3 +280,8 @@
     }
   }
 }
+
+
+.subjectSyncModal {
+  width: 920px;
+}

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

@@ -16,17 +16,20 @@ import { usePrepareStore } from '/src/store/modules/prepareLessons';
 import { useCatchStore } from '/src/store/modules/catchData';
 import TheEmpty from '/src/components/TheEmpty';
 import {
+  courseScheduleStart,
   queryCourseware,
   saveCourseware,
   teacherKnowledgeMaterialDelete
 } from '../../../api';
 import Draggable from 'vuedraggable';
 import iconDelete from '../../../images/icon-delete.png';
-import { 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 { eventGlobal } from '/src/utils';
 
 export default defineComponent({
   name: 'courseware-modal',
@@ -38,14 +41,20 @@ export default defineComponent({
     const dialog = useDialog();
     const message = useMessage();
     const forms = reactive({
+      className: route.query.name as any,
+      classGroupId: route.query.classGroupId,
       subjectId: route.query.subjectId ? Number(route.query.subjectId) : null,
       coursewareList: [] as any,
       loadingStatus: false,
       showAttendClass: false,
+      attendClassType: 'change', //
       removeIds: [] as any, // 临时删除的编号
       drag: false,
+      isEdit: false, // 是否更新数据
+      editSubjectIds: '', // 声部编号
       removeVisiable: false,
       removeVisiable1: false,
+      subjectSyncVisiable: false, // 同步声部
       show: false,
       item: {} as any,
       previewModal: false,
@@ -105,6 +114,7 @@ export default defineComponent({
       () => prepareStore.getSelectKey,
       () => {
         forms.drag = false;
+        prepareStore.setIsEditResource(false);
         getList();
       }
     );
@@ -124,6 +134,18 @@ export default defineComponent({
         }
       }
     );
+    // 监听列表变化,如果变化了,则弹选择声部的
+    watch(
+      () => forms.coursewareList,
+      () => {
+        if (forms.drag) {
+          forms.isEdit = true;
+        }
+      },
+      {
+        deep: true
+      }
+    );
 
     // 删除
     const onDelete = (item: any) => {
@@ -133,18 +155,13 @@ export default defineComponent({
         (c: any) => c.id === item.id
       );
       forms.coursewareList.splice(index, 1);
+      forms.isEdit = true;
       // prepareStore.setCoursewareList(forms.coursewareList);
       // console.log(prepareStore.getCoursewareList, 'getCourseware');
     };
 
     // 完成编辑
     const onOverEdit = async () => {
-      // dialog.warning({
-      //   title: '提示',
-      //   content: `是否完成编辑?`,
-      //   positiveText: '确定',
-      //   negativeText: '取消',
-      //   onPositiveClick: async () => {
       try {
         const temp: any = [];
         forms.coursewareList.forEach((item: any) => {
@@ -156,26 +173,27 @@ export default defineComponent({
           });
         });
         // 保存课件
+        // 判断是否编辑,如果编辑则取选择的声部
         await saveCourseware({
           coursewareDetailKnowledgeId: prepareStore.getSelectKey,
           lessonCoursewareId: prepareStore.getLessonCoursewareId,
           lessonCoursewareDetailId: prepareStore.getLessonCoursewareDetailId,
-          subjectId: prepareStore.getSubjectId,
+          subjectId: forms.isEdit
+            ? forms.editSubjectIds
+            : prepareStore.getSubjectId,
           materialList: [...temp]
         });
 
         forms.drag = false;
         message.success('编辑成功');
         forms.removeVisiable = false;
-        prepareStore.setCoursewareList(deepClone(forms.coursewareList));
         prepareStore.setIsEditResource(false);
         // 重置临时删除编号
         forms.removeIds = [];
+        await getList();
       } catch {
         //
       }
-      //   }
-      // });
     };
 
     // 预览上课
@@ -193,38 +211,44 @@ export default defineComponent({
       }
       // 判断是否在应用里面
       if (window.matchMedia('(display-mode: standalone)').matches) {
-        state.application = window.matchMedia('(display-mode: standalone)').matches
+        state.application = window.matchMedia(
+          '(display-mode: standalone)'
+        ).matches;
         forms.previewModal = true;
-        fscreen()
+        fscreen();
         forms.previewParams = {
           type: 'preview',
           subjectId: prepareStore.getSubjectId,
-          detailId: prepareStore.getSelectKey
+          detailId: prepareStore.getSelectKey,
+          lessonCourseId: prepareStore.getBaseCourseware.id
         };
-
-
       } else {
         const { href } = router.resolve({
           path: '/attend-class',
           query: {
             type: 'preview',
             subjectId: prepareStore.getSubjectId,
-            detailId: prepareStore.getSelectKey
+            detailId: prepareStore.getSelectKey,
+            lessonCourseId: prepareStore.getBaseCourseware.id
           }
         });
         window.open(href, +new Date() + '');
       }
     };
-    const fscreen=()=> {
-      const el = document.documentElement
-      const isFullscreen = document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen
+    const fscreen = () => {
+      const el = document.documentElement;
+      const isFullscreen =
+        document.fullScreen ||
+        document.mozFullScreen ||
+        document.webkitIsFullScreen;
       if (!isFullscreen) {
         //进入全屏
         (el.requestFullscreen && el.requestFullscreen()) ||
-        (el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
-        (el.webkitRequestFullscreen && el.webkitRequestFullscreen()) ||
-        (el.msRequestFullscreen && el.msRequestFullscreen())
-      } }
+          (el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
+          (el.webkitRequestFullscreen && el.webkitRequestFullscreen()) ||
+          (el.msRequestFullscreen && el.msRequestFullscreen());
+      }
+    };
     // 单个删除
     const onRemove = async (item: any) => {
       try {
@@ -245,24 +269,62 @@ export default defineComponent({
       }
     };
 
-    onMounted(async () => {
-      // 获取教材分类列表
-      await catchStore.getSubjects();
+    watch(
+      () => prepareStore.getSubjectList,
+      () => {
+        checkSubjectIds();
+      }
+    );
+
+    const checkSubjectIds = () => {
+      const subjectList = prepareStore.getSubjectList;
 
-      const subjectList = catchStore.getSubjectList;
       // 并且没有声部时才会更新
-      console.log(forms.subjectId, subjectList, 'forms.subjectId');
       if (subjectList.length > 0) {
         // 判断浏览器上面是否有
-        if (forms.subjectId) {
+        const index = subjectList.findIndex(
+          (subject: any) => subject.id == forms.subjectId
+        );
+
+        // 并且声部在列表中
+        if (forms.subjectId && index >= 0) {
           prepareStore.setSubjectId(forms.subjectId);
-        } else if (!prepareStore.getSubjectId) {
+        } else {
           // 判断是否有缓存
           prepareStore.setSubjectId(subjectList[0].id);
         }
+
+        // console.log(prepareStore.getSubjectId, 'getSubjectId');
       }
+    };
+
+    watch(
+      () => route.query,
+      async () => {
+        forms.className = route.query.name as any;
+        forms.classGroupId = route.query.classGroupId as any;
+        forms.subjectId = route.query.subjectId
+          ? Number(route.query.subjectId)
+          : null;
+
+        checkSubjectIds();
+        await getList();
+      }
+    );
+
+    onMounted(async () => {
+      // 获取教材分类列表
+      checkSubjectIds();
 
       await getList();
+
+      // 动态添加数据
+      eventGlobal.on('onPrepareAddItem', (item: any) => {
+        forms.coursewareList.push(item);
+        prepareStore.setCoursewareList(forms.coursewareList);
+
+        forms.isEdit = true;
+      });
     });
 
     return () => (
@@ -270,82 +332,103 @@ export default defineComponent({
         <div class={styles.btnGroup}>
           {forms.drag ? (
             <NSpace>
+              <span class={styles.tips}>拖动可将资源进行排序哦~</span>
+            </NSpace>
+          ) : (
+            <NSpace>
+              {forms.classGroupId && (
+                <div class={styles.btnItem}>
+                  <span class={styles.btnTitle}>上课班级:</span>
+                  <div
+                    onClick={() => {
+                      forms.showAttendClass = true;
+                      forms.attendClassType = 'change';
+                    }}>
+                    <NSelect
+                      placeholder="选择声部"
+                      labelField="name"
+                      valueField="id"
+                      class={styles.btnClassList}
+                      value={forms.className}
+                      disabled
+                    />
+                  </div>
+                </div>
+              )}
+
+              <div class={styles.btnItem}>
+                <span class={styles.btnTitle}>声部:</span>
+                <NSelect
+                  placeholder="选择声部"
+                  class={styles.btnSubjectList}
+                  options={prepareStore.getSubjectList}
+                  labelField="name"
+                  valueField="id"
+                  value={prepareStore.getSubjectId}
+                  onUpdate:value={(val: any) => {
+                    prepareStore.setSubjectId(val);
+                    getList();
+                  }}
+                />
+              </div>
+            </NSpace>
+          )}
+
+          {/* 编辑 */}
+          {!forms.drag ? (
+            <NSpace>
               <NButton
                 type="default"
                 onClick={() => {
-                  forms.removeVisiable = true;
+                  forms.drag = true;
+                  prepareStore.setIsEditResource(true);
+
+                  // forms.subjectSyncVisiable = true;
                 }}>
-                完成编辑
+                编辑
               </NButton>
+            </NSpace>
+          ) : (
+            <NSpace>
               <NButton
                 type="error"
                 onClick={() => {
-                  forms.drag = false;
-                  prepareStore.setIsEditResource(false);
-                  forms.removeIds = [];
-                  getList();
+                  forms.removeVisiable1 = true;
                 }}>
-                取消编辑
+                清空资源
               </NButton>
               <NButton
                 type="error"
                 onClick={() => {
-                  forms.removeVisiable1 = true;
+                  forms.drag = false;
+                  forms.isEdit = false;
+                  prepareStore.setIsEditResource(false);
+                  forms.removeIds = [];
+                  getList();
                 }}>
-                清空资源
+                取消编辑
               </NButton>
-              <span class={styles.tips}>拖动可将资源进行排序</span>
-            </NSpace>
-          ) : (
-            <NSpace>
-              <NSelect
-                placeholder="选择声部"
-                options={catchStore.getSubjectList}
-                labelField="name"
-                valueField="id"
-                value={prepareStore.getSubjectId}
-                onUpdate:value={(val: any) => {
-                  prepareStore.setSubjectId(val);
-                  getList();
-                }}
-              />
               <NButton
                 type="default"
                 onClick={() => {
-                  forms.drag = true;
-                  prepareStore.setIsEditResource(true);
+                  if (forms.isEdit) {
+                    forms.subjectSyncVisiable = true;
+                  } else {
+                    forms.removeVisiable = true;
+                  }
                 }}>
-                编辑
+                完成编辑
               </NButton>
             </NSpace>
           )}
-
-          <NSpace>
-            <NButton type="default" onClick={onPreviewAttend}>
-              预览
-            </NButton>
-            <NButton
-              {...{ id: 'lessons-3' }}
-              type="primary"
-              onClick={() => {
-                let count = 0;
-                forms.coursewareList.forEach((item: any) => {
-                  if (!item.removeFlag) {
-                    count++;
-                  }
-                });
-                if (count <= 0) {
-                  message.error('课件不能为空');
-                  return;
-                }
-                forms.showAttendClass = true;
-              }}>
-              开始上课
-            </NButton>
-          </NSpace>
         </div>
 
-        <NScrollbar class={styles.listContainer} {...{ id: 'lessons-2' }}>
+        <NScrollbar
+          class={[
+            styles.listContainer,
+            forms.drag ? styles.listContainerDrag : ''
+          ]}
+          {...{ id: 'lessons-2' }}>
           <NSpin show={forms.loadingStatus}>
             <div
               class={[
@@ -399,7 +482,7 @@ export default defineComponent({
                     </Draggable>
                   ) : (
                     <div class={styles.list}>
-                      {forms.coursewareList.map((item: any, index: number) => (
+                      {forms.coursewareList.map((item: any) => (
                         <CardType
                           class={[styles.itemContent, 'handle']}
                           isShowCollect={false}
@@ -426,6 +509,77 @@ export default defineComponent({
           </NSpin>
         </NScrollbar>
 
+        {!forms.drag ? (
+          <div class={styles.btnGroup} style={{ justifyContent: 'flex-end' }}>
+            <NSpace justify="end">
+              <NButton type="primary" onClick={onPreviewAttend}>
+                预览课件
+              </NButton>
+              <NButton
+                {...{ id: 'lessons-3' }}
+                type="error"
+                class={styles.btnClassStart}
+                onClick={async () => {
+                  let count = 0;
+                  forms.coursewareList.forEach((item: any) => {
+                    if (!item.removeFlag) {
+                      count++;
+                    }
+                  });
+                  if (count <= 0) {
+                    message.error('课件不能为空');
+                    return;
+                  }
+
+                  if (forms.classGroupId) {
+                    // 开始上课
+                    const res = await courseScheduleStart({
+                      lessonCoursewareKnowledgeDetailId: prepareStore.selectKey,
+                      classGroupId: forms.classGroupId
+                    });
+                    if (
+                      window.matchMedia('(display-mode: standalone)').matches
+                    ) {
+                      state.application = window.matchMedia(
+                        '(display-mode: standalone)'
+                      ).matches;
+                      forms.previewModal = true;
+                      fscreen();
+                      forms.previewParams = {
+                        type: 'class',
+                        classGroupId: forms.classGroupId,
+                        subjectId: prepareStore.getSubjectId,
+                        detailId: prepareStore.getSelectKey,
+                        classId: res.data,
+                        lessonCourseId: prepareStore.getBaseCourseware.id
+                      };
+                    } else {
+                      const { href } = router.resolve({
+                        path: '/attend-class',
+                        query: {
+                          type: 'class',
+                          classGroupId: forms.classGroupId,
+                          subjectId: prepareStore.getSubjectId,
+                          detailId: prepareStore.getSelectKey,
+                          classId: res.data,
+                          lessonCourseId: prepareStore.getBaseCourseware.id
+                        }
+                      });
+                      window.open(href, +new Date() + '');
+                    }
+                  } else {
+                    forms.showAttendClass = true;
+                    forms.attendClassType = 'change';
+                  }
+                }}>
+                开始上课
+              </NButton>
+            </NSpace>
+          </div>
+        ) : (
+          ''
+        )}
+
         <NModal
           v-model:show={forms.showAttendClass}
           preset="card"
@@ -435,9 +589,12 @@ export default defineComponent({
           blockScroll={false}>
           <AttendClass
             onClose={() => (forms.showAttendClass = false)}
+            type={forms.attendClassType}
             onPreview={(item: any) => {
               if (window.matchMedia('(display-mode: standalone)').matches) {
-                state.application = window.matchMedia('(display-mode: standalone)').matches
+                state.application = window.matchMedia(
+                  '(display-mode: standalone)'
+                ).matches;
                 forms.previewModal = true;
                 forms.previewParams = {
                   ...item
@@ -452,6 +609,57 @@ export default defineComponent({
                 window.open(href, +new Date() + '');
               }
             }}
+            onConfirm={async (item: any) => {
+              if (forms.classGroupId) {
+                forms.className = item.name;
+                forms.classGroupId = item.classGroupId;
+                forms.subjectId = item.subjectId;
+                forms.showAttendClass = false;
+
+                checkSubjectIds();
+                // 声部切换时
+                eventGlobal.emit('onChangeClass', {
+                  lastUseCoursewareId: item.lastUseCoursewareId,
+                  unit: item.unit
+                });
+              } else {
+                const res = await courseScheduleStart({
+                  lessonCoursewareKnowledgeDetailId: prepareStore.selectKey,
+                  classGroupId: item.classGroupId
+                });
+                forms.showAttendClass = false;
+                if (window.matchMedia('(display-mode: standalone)').matches) {
+                  state.application = window.matchMedia(
+                    '(display-mode: standalone)'
+                  ).matches;
+                  forms.previewModal = true;
+                  forms.previewParams = {
+                    type: 'class',
+                    classId: res.data, // 上课编号
+                    classGroupId: item.classGroupId,
+                    subjectId: prepareStore.getSubjectId,
+                    detailId: prepareStore.getSelectKey,
+                    lessonCourseId: prepareStore.getBaseCourseware.id
+                  };
+                  setTimeout(() => {
+                    fscreen();
+                  }, 200);
+                } else {
+                  const { href } = router.resolve({
+                    path: '/attend-class',
+                    query: {
+                      type: 'class',
+                      classId: res.data, // 上课编号
+                      classGroupId: item.classGroupId,
+                      subjectId: prepareStore.getSubjectId,
+                      detailId: prepareStore.getSelectKey,
+                      lessonCourseId: prepareStore.getBaseCourseware.id
+                    }
+                  });
+                  window.open(href, +new Date() + '');
+                }
+              }
+            }}
           />
         </NModal>
 
@@ -498,6 +706,7 @@ export default defineComponent({
                   });
                   forms.coursewareList = [];
                   forms.removeVisiable1 = false;
+                  forms.isEdit = true;
                   // prepareStore.setCoursewareList([]);
 
                   console.log(prepareStore.getCoursewareList, 'getCourseware1');
@@ -516,6 +725,28 @@ export default defineComponent({
           type="attend"
           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>
       </div>
     );
   }

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

@@ -17,15 +17,16 @@ export default defineComponent({
           paneClass={styles.paneTitle}
           justifyContent="center"
           paneWrapperClass={styles.paneWrapperContainer}
-          onUpdate: value={(val: string) => {
+          onUpdate:value={(val: string) => {
             prepareStore.setTabType(val);
           }}>
-          <NTabPane name="courseware" tab="课件">
+          <NTabPane name="courseware" tab="课件" displayDirective="show">
             <Courseware />
           </NTabPane>
           <NTabPane
             name="train"
             tab="作业"
+            displayDirective="show"
             v-slots={{ tab: () => <span id="lessons-4">作业</span> }}
             {...{ id: 'lessons-4' }}>
             <Train />

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

@@ -57,6 +57,45 @@
   }
 }
 
+.btnSubjectList {
+  :global {
+
+    .n-base-selection-label {
+      &::before {
+        margin-left: 12px;
+        content: ' ';
+        width: 24px;
+        height: 24px;
+        background: url('../../../images/icon-subject-name.png') no-repeat center;
+        background-size: contain;
+      }
+    }
+
+    .n-base-selection-input {
+      padding-left: 8px;
+    }
+
+    .n-base-selection .n-base-selection-overlay {
+      left: 24px;
+    }
+  }
+}
+
+.btnItem {
+  display: flex;
+  align-items: center;
+
+  .btnTitle {
+    flex-shrink: 0;
+    font-size: 18px;
+    font-weight: 600;
+  }
+
+  &:last-child {
+    margin-left: 12px;
+  }
+}
+
 .listContainer {
   margin-top: 12px;
   // // 52 + 28 + 38

+ 43 - 7
src/views/prepare-lessons/components/lesson-main/train/index.tsx

@@ -185,14 +185,36 @@ export default defineComponent({
       }
     };
 
+    // const checkSubjectIds = () => {
+    //   const subjectList = prepareStore.getSubjectList;
+
+    //   // 并且没有声部时才会更新
+    //   if (subjectList.length > 0) {
+    //     // 判断浏览器上面是否有
+    //     const index = subjectList.findIndex(
+    //       (subject: any) => subject.id == forms.subjectId
+    //     );
+
+    //     // 并且声部在列表中
+    //     if (forms.subjectId && index >= 0) {
+    //       prepareStore.setSubjectId(forms.subjectId);
+    //     } else {
+    //       // 判断是否有缓存
+    //       prepareStore.setSubjectId(subjectList[0].id);
+    //     }
+    //   }
+    // };
+
     onMounted(async () => {
       // 获取教材分类列表
-      await catchStore.getSubjects();
+      // await catchStore.getSubjects();
 
-      const subjectList = catchStore.getSubjectList;
-      if (subjectList.length > 0 && !prepareStore.getSubjectId) {
-        prepareStore.setSubjectId(subjectList[0].id);
-      }
+      // const subjectList = catchStore.getSubjectList;
+      // if (subjectList.length > 0 && !prepareStore.getSubjectId) {
+      //   prepareStore.setSubjectId(subjectList[0].id);
+      // }
+      // 获取教材分类列表
+      // checkSubjectIds();
 
       await getList();
     });
@@ -231,7 +253,7 @@ export default defineComponent({
             </NSpace>
           ) : (
             <NSpace>
-              <NSelect
+              {/* <NSelect
                 placeholder="选择声部"
                 options={catchStore.getSubjectList}
                 labelField="name"
@@ -240,7 +262,21 @@ export default defineComponent({
                 onUpdate:value={(val: any) => {
                   prepareStore.setSubjectId(val);
                 }}
-              />
+              /> */}
+              <div class={styles.btnItem}>
+                <span class={styles.btnTitle}>声部:</span>
+                <NSelect
+                  placeholder="选择声部"
+                  class={styles.btnSubjectList}
+                  options={prepareStore.getSubjectList}
+                  labelField="name"
+                  valueField="id"
+                  value={prepareStore.getSubjectId}
+                  onUpdate:value={(val: any) => {
+                    prepareStore.setSubjectId(val);
+                  }}
+                />
+              </div>
               <NButton
                 type="default"
                 onClick={() => {

+ 40 - 25
src/views/prepare-lessons/components/resource-main/components/resource-item/index.tsx

@@ -9,6 +9,7 @@ import { usePrepareStore } from '/src/store/modules/prepareLessons';
 import { useDebounceFn, useThrottleFn } from '@vueuse/core';
 import { saveCourseware } from '/src/views/prepare-lessons/api';
 import CardPreview from '/src/components/card-preview';
+import { eventGlobal } from '/src/utils';
 
 const formatType = (type: string) => {
   if (type === 'shareResources') {
@@ -140,33 +141,44 @@ export default defineComponent({
       try {
         console.log(item, 'any');
         const temp: any = [];
-        prepareStore.getCoursewareList.forEach((item: any) => {
-          temp.push({
-            materialName: item.title,
-            materialType: item.type,
-            materialId: item.materialId,
-            id: item.id
-          });
+        // prepareStore.getCoursewareList.forEach((item: any) => {
+        //   temp.push({
+        //     materialName: item.title,
+        //     materialType: item.type,
+        //     materialId: item.materialId,
+        //     id: item.id
+        //   });
+        // });
+        eventGlobal.emit('onPrepareAddItem', {
+          materialId: item.id,
+          coverImg: item.coverImg,
+          type: item.type,
+          title: item.title,
+          isCollect: item.isCollect,
+          isSelected: item.isSelected,
+          content: item.content,
+          removeFlag: false
         });
 
         // 保存课件
-        await saveCourseware({
-          coursewareDetailKnowledgeId: prepareStore.getSelectKey,
-          lessonCoursewareId: prepareStore.getLessonCoursewareId,
-          lessonCoursewareDetailId: prepareStore.getLessonCoursewareDetailId,
-          materialList: [
-            ...temp,
-            {
-              materialName: item.title,
-              materialType: item.type,
-              materialId: item.id
-            }
-          ],
-          subjectId: prepareStore.getSubjectId
-        });
+        // await saveCourseware({
+        //   coursewareDetailKnowledgeId: prepareStore.getSelectKey,
+        //   lessonCoursewareId: prepareStore.getLessonCoursewareId,
+        //   lessonCoursewareDetailId: prepareStore.getLessonCoursewareDetailId,
+        //   materialList: [
+        //     ...temp,
+        //     {
+        //       materialName: item.title,
+        //       materialType: item.type,
+        //       materialId: item.id
+        //     }
+        //   ],
+        //   subjectId: prepareStore.getSubjectId
+        // });
+
+        // message.success('添加成功');
 
-        message.success('添加成功');
-        prepareStore.setIsAddResource(true);
+        // prepareStore.setIsAddResource(true);
       } catch {
         //
       }
@@ -181,7 +193,10 @@ export default defineComponent({
       <div>
         <ResourceSearchGroup
           type={props.type}
-          onSearch={(item: any) => throttledFnSearch(item)}
+          onSearch={(item: any) => {
+            state.searchGroup = Object.assign(state.searchGroup, item);
+            throttledFnSearch(item);
+          }}
         />
         <NScrollbar
           class={[
@@ -222,7 +237,7 @@ export default defineComponent({
                         state.show = true;
                         state.item = item;
                       }}
-                      isShowAddDisabled={prepareStore.getIsEditResource}
+                      isShowAddDisabled={!prepareStore.getIsEditResource}
                       onAdd={(item: any) => onAdd(item)}
                     />
                   ))}

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

@@ -154,7 +154,7 @@ export default defineComponent({
                       isShowAdd
                       isShowCollect={false}
                       item={item}
-                      isShowAddDisabled={prepareStore.getIsEditTrain}
+                      isShowAddDisabled={!prepareStore.getIsEditTrain}
                       disabledMouseHover={false}
                       onClick={() => {
                         if (item.type === 'IMG') return;

BIN
src/views/prepare-lessons/images/icon-class-name.png


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


BIN
src/views/prepare-lessons/images/icon-subject-name.png


+ 14 - 8
src/views/prepare-lessons/index.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, onMounted, ref, computed } from 'vue';
+import { defineComponent, onMounted, ref, computed, onUnmounted } from 'vue';
 import styles from './index.module.less';
 import DirectoryList from './components/directory-main';
 import LessonMain from './components/lesson-main';
@@ -13,12 +13,16 @@ export default defineComponent({
   setup() {
     const prepareStore = usePrepareStore();
 
-    console.log(prepareStore, 'prepareStore');
+    // console.log(prepareStore, 'prepareStore');
     const { treeList, coursewareList } = storeToRefs(prepareStore);
     const showGuide = computed(() => {
-      return treeList.value.length > 0 && coursewareList.value.length > 0 && isEndMounted.value;
+      return (
+        treeList.value.length > 0 &&
+        coursewareList.value.length > 0 &&
+        isEndMounted.value
+      );
     });
-    const isEndMounted = ref(false)
+    const isEndMounted = ref(false);
     const directroyRef = ref();
     onMounted(() => {
       useResizeObserver(
@@ -33,9 +37,8 @@ export default defineComponent({
         }
       );
       setTimeout(() => {
-        isEndMounted.value = true
-      }, 300)
-
+        isEndMounted.value = true;
+      }, 300);
     });
 
     // 当前页面离开时
@@ -48,11 +51,14 @@ export default defineComponent({
       prepareStore.setIsAddTrain(false);
       prepareStore.setIsEditResource(false);
       prepareStore.setIsEditTrain(false);
-      prepareStore.setSubjectId('');
+      // prepareStore.setSubjectId('');
       // prepareStore.setCoursewareList([]);
       // prepareStore.setTreeList([]);
       // prepareStore.setTrainList([]);
     });
+    onUnmounted(() => {
+      prepareStore.setSubjectId('');
+    });
     return () => (
       <div class={styles.prepareLessons}>
         {/* 左侧目录 */}

+ 28 - 1
src/views/prepare-lessons/model/attend-class/index.module.less

@@ -60,8 +60,17 @@
     .n-thing-header {
       margin-bottom: 0 !important;
     }
+
+    .n-thing-header__title {
+      width: 100%;
+      display: flex;
+      align-items: center;
+      // justify-content: space-between;
+    }
   }
 
+
+
   .title {
     display: flex;
     align-items: center;
@@ -81,9 +90,27 @@
     }
   }
 
+  .subjects {
+    margin-left: 14px;
+    font-size: 16px !important;
+    line-height: 22px;
+    color: #0A81F7;
+    background: #E8F4FF;
+    border-radius: 5px;
+    border: 1px solid #198CFE;
+    padding: 0 8px;
+    font-weight: bold;
+
+    &.noSubjects {
+      color: #F4130D !important;
+      background: transparent;
+      border: 1px solid #F4130D;
+    }
+  }
+
   .content {
     margin-top: 12px !important;
     font-size: 16px;
     color: #777777;
   }
-}
+}

+ 74 - 14
src/views/prepare-lessons/model/attend-class/index.tsx

@@ -1,6 +1,6 @@
-import { defineComponent, onMounted, reactive, ref } from 'vue';
+import { defineComponent, onMounted, reactive, ref, toRef } from 'vue';
 import styles from './index.module.less';
-import { NInput, NScrollbar, NSelect, NSpin, NThing } from 'naive-ui';
+import { NInput, NModal, NScrollbar, NSelect, NSpin, NThing } from 'naive-ui';
 import { useRouter } from 'vue-router';
 import { BOOK_DATA } from '/src/views/natural-resources/model/add-teaching';
 import { classGroupPage, courseScheduleStart } from '../../api';
@@ -9,6 +9,7 @@ import TheEmpty from '/src/components/TheEmpty';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
 import { state } from '/src/state';
 import { nextTick } from 'process';
+import UpdateSubject from '/src/views/classList/modals/updateSubject';
 const classList: any = [];
 for (let i = 1; i <= 40; i++) {
   classList.push({ label: i + '班', value: i });
@@ -16,11 +17,20 @@ for (let i = 1; i <= 40; i++) {
 
 export default defineComponent({
   name: 'attend-class',
-  emits: ['close', 'preview'],
+  props: {
+    // change 切换班级 select 直接进入上课
+    type: {
+      type: String,
+      default: 'change'
+    }
+  },
+  emits: ['close', 'preview', 'confirm'],
   setup(props, { emit }) {
+    // const { type } = toRef(props);
     const prepareStore = usePrepareStore();
-    const router = useRouter();
     const forms = reactive({
+      showSubjectClass: false,
+      activeRow: {} as any,
       keyword: null,
       currentGradeNum: null,
       currentClass: null
@@ -31,21 +41,41 @@ export default defineComponent({
     // 开始上课
     const onAttendClass = async (item: any) => {
       try {
-        await courseScheduleStart({
+        // 判断是否是切换班级
+        if (props.type == 'change') {
+          if (item.subjectId) {
+            emit('confirm', {
+              lastUseCoursewareId: item.lessonCoursewareId,
+              unit: item.lessonCoursewareKnowledgeDetailId,
+              subjectId: item.subjectId,
+              name: item.name, // 班级名称
+              classGroupId: item.id // 班级编号
+            });
+          } else {
+            forms.showSubjectClass = true;
+            forms.activeRow = item;
+          }
+          return;
+        }
+
+        const res = await courseScheduleStart({
           lessonCoursewareKnowledgeDetailId: prepareStore.selectKey,
           classGroupId: item.id
         });
 
         emit('close');
-
         emit('preview', {
           type: 'class',
+          classId: res.data, // 上课编号
           classGroupId: item.id,
           subjectId: prepareStore.getSubjectId,
-          detailId: prepareStore.getSelectKey
+          detailId: prepareStore.getSelectKey,
+          lessonCourseId: prepareStore.getBaseCourseware.id
         });
         if (window.matchMedia('(display-mode: standalone)').matches) {
-          state.application = window.matchMedia('(display-mode: standalone)').matches
+          state.application = window.matchMedia(
+            '(display-mode: standalone)'
+          ).matches;
           setTimeout(() => {
             fscreen();
           }, 200);
@@ -55,8 +85,8 @@ export default defineComponent({
       }
     };
     const fscreen = () => {
-      var el = document.documentElement;
-      var isFullscreen =
+      const el = document.documentElement;
+      const isFullscreen =
         document.fullScreen ||
         document.mozFullScreen ||
         document.webkitIsFullScreen;
@@ -86,12 +116,14 @@ export default defineComponent({
           ...forms
         });
         const result = data.rows || [];
+        const temp: any = [];
         result.forEach((item: any) => {
           // 判断班级里面有学生的
           if (item.preStudentNum > 0) {
-            list.value.push(item);
+            temp.push(item);
           }
         });
+        list.value = temp;
       } catch {
         //
       }
@@ -156,9 +188,18 @@ export default defineComponent({
                   <NThing class={[styles.thingItem, 'isFull']}>
                     {{
                       header: () => (
-                        <div class={styles.title}>
-                          {item.name} {item.preStudentNum}人
-                        </div>
+                        <>
+                          <div class={styles.title}>
+                            {item.name} {item.preStudentNum}人
+                          </div>
+                          <div
+                            class={[
+                              styles.subjects,
+                              item.subjectName ? '' : styles.noSubjects
+                            ]}>
+                            {item.subjectName ? item.subjectName : '暂无声部'}
+                          </div>
+                        </>
                       ),
                       default: () =>
                         item.lastStudy && (
@@ -172,6 +213,25 @@ export default defineComponent({
             </div>
           </NSpin>
         </NScrollbar>
+
+        <NModal
+          v-model:show={forms.showSubjectClass}
+          style={{ width: '500px' }}
+          preset="card"
+          class={['modalTitle background']}
+          title={'修改声部'}>
+          {forms.showSubjectClass ? (
+            <UpdateSubject
+              activeRow={forms.activeRow}
+              onGetList={() => getList()}
+              onConfirm={(item: any) => {
+                //
+                emit('confirm', item);
+              }}
+              onClose={() => (forms.showSubjectClass = false)}
+            />
+          ) : null}
+        </NModal>
       </div>
     );
   }

+ 14 - 2
src/views/prepare-lessons/model/select-music/index.tsx

@@ -11,8 +11,15 @@ import CardPreview from '/src/components/card-preview';
 
 export default defineComponent({
   name: 'select-music',
+  props: {
+    type: {
+      type: String,
+      default: ''
+    }
+  },
   emits: ['add'],
   setup(props, { emit }) {
+    console.log(props.type);
     const prepareStore = usePrepareStore();
     const state = reactive({
       loading: false,
@@ -30,7 +37,8 @@ export default defineComponent({
       },
       tableList: [] as any,
       show: false,
-      item: {} as any
+      item: {} as any,
+      isShowAddDisabled: !prepareStore.getIsEditTrain
     });
     const getList = async () => {
       try {
@@ -58,6 +66,7 @@ export default defineComponent({
           });
         });
         state.tableList.push(...temp);
+
         state.finshed = data.pages <= data.current ? true : false;
       } catch {
         state.loading = false;
@@ -83,6 +92,9 @@ export default defineComponent({
     }, 500);
 
     onMounted(() => {
+      if (props.type === 'homework') {
+        state.isShowAddDisabled = false;
+      }
       getList();
     });
     return () => (
@@ -125,7 +137,7 @@ export default defineComponent({
                           isShowAdd
                           isShowCollect={false}
                           item={item}
-                          isShowAddDisabled={prepareStore.getIsEditTrain}
+                          isShowAddDisabled={state.isShowAddDisabled}
                           onAdd={() => emit('add', item)}
                           disabledMouseHover={false}
                           onClick={() => {

+ 42 - 30
src/views/prepare-lessons/model/select-resources/select-item/index.tsx

@@ -16,6 +16,7 @@ import { usePrepareStore } from '/src/store/modules/prepareLessons';
 import { saveCourseware } from '../../../api';
 import { useDebounceFn } from '@vueuse/core';
 import CardPreview from '/src/components/card-preview';
+import { eventGlobal } from '/src/utils';
 
 const formatType = (type: string) => {
   if (type === 'shareResources') {
@@ -50,7 +51,7 @@ export default defineComponent({
         type: type.value === 'shareResources' ? 'MUSIC' : '', //
         name: '',
         bookVersionId: null,
-        subjectId: null,
+        subjectId: prepareStore.getSubjectId,
         sourceType: formatType(type.value),
         enableFlag: true
       },
@@ -67,8 +68,8 @@ export default defineComponent({
         }
         const { data } = await materialQueryPage({
           ...state.searchGroup,
-          ...state.pagination,
-          subjectId: prepareStore.getSubjectId
+          ...state.pagination
+          // subjectId: prepareStore.getSubjectId
         });
         state.loading = false;
         const tempRows = data.rows || [];
@@ -118,34 +119,44 @@ export default defineComponent({
       //   negativeText: '取消',
       //   onPositiveClick: async () => {
       try {
-        const temp: any = [];
-        prepareStore.getCoursewareList.forEach((item: any) => {
-          temp.push({
-            materialId: item.materialId,
-            materialName: item.title,
-            materialType: item.type,
-            id: item.id
-          });
-        });
+        // const temp: any = [];
+        // prepareStore.getCoursewareList.forEach((item: any) => {
+        //   temp.push({
+        //     materialId: item.materialId,
+        //     materialName: item.title,
+        //     materialType: item.type,
+        //     id: item.id
+        //   });
+        // });
 
-        // 保存课件
-        await saveCourseware({
-          coursewareDetailKnowledgeId: prepareStore.getSelectKey,
-          lessonCoursewareId: prepareStore.getLessonCoursewareId,
-          lessonCoursewareDetailId: prepareStore.getLessonCoursewareDetailId,
-          materialList: [
-            ...temp,
-            {
-              materialName: item.title,
-              materialType: item.type,
-              materialId: item.id
-            }
-          ],
-          subjectId: prepareStore.getSubjectId
-        });
+        // // 保存课件
+        // await saveCourseware({
+        //   coursewareDetailKnowledgeId: prepareStore.getSelectKey,
+        //   lessonCoursewareId: prepareStore.getLessonCoursewareId,
+        //   lessonCoursewareDetailId: prepareStore.getLessonCoursewareDetailId,
+        //   materialList: [
+        //     ...temp,
+        //     {
+        //       materialName: item.title,
+        //       materialType: item.type,
+        //       materialId: item.id
+        //     }
+        //   ],
+        //   subjectId: prepareStore.getSubjectId
+        // });
 
-        message.success('添加成功');
-        prepareStore.setIsAddResource(true);
+        // message.success('添加成功');
+        // prepareStore.setIsAddResource(true);
+        eventGlobal.emit('onPrepareAddItem', {
+          materialId: item.id,
+          coverImg: item.coverImg,
+          type: item.type,
+          title: item.title,
+          isCollect: item.isCollect,
+          isSelected: item.isSelected,
+          content: item.content,
+          removeFlag: false
+        });
       } catch {
         //
       }
@@ -176,6 +187,7 @@ export default defineComponent({
       <div>
         <ResourceSearchGroup
           type={props.type}
+          subjectId={prepareStore.getSubjectId as any}
           onSearch={(item: any) => throttledFnSearch(item)}
         />
         <NScrollbar
@@ -209,7 +221,7 @@ export default defineComponent({
                       isShowAdd
                       item={item}
                       isShowCollect={false}
-                      isShowAddDisabled={prepareStore.getIsEditResource}
+                      isShowAddDisabled={!prepareStore.getIsEditResource}
                       onAdd={(item: any) => onAdd(item)}
                       disabledMouseHover={false}
                       onClick={() => {

+ 29 - 1
src/views/prepare-lessons/model/select-resources/select-item/resource-search-group/index.tsx

@@ -4,7 +4,8 @@ import {
   nextTick,
   onMounted,
   reactive,
-  ref
+  ref,
+  toRef
 } from 'vue';
 import styles from './index.module.less';
 import { NButton, NForm, NFormItem, NImage, NSpace } from 'naive-ui';
@@ -20,14 +21,20 @@ export default defineComponent({
     type: {
       type: String as PropType<'shareResources' | 'myResources' | 'myCollect'>,
       default: 'shareResources'
+    },
+    subjectId: {
+      type: String,
+      default: ''
     }
   },
   emits: ['search'],
   setup(props, { emit }) {
+    const subjectId = toRef(props.subjectId);
     const catchStore = useCatchStore();
     const forms = reactive({
       type: 'MUSIC', //
       name: '',
+      subjectId: subjectId.value as any,
       bookVersionId: null
     });
     const resourceType = ref([] as any);
@@ -103,6 +110,8 @@ export default defineComponent({
 
       // 获取教材分类列表
       await catchStore.getMusicSheetCategory();
+      // 获取声部
+      await catchStore.getSubjects();
 
       // 这里开始
       // musicCateRef
@@ -214,6 +223,25 @@ export default defineComponent({
               </NFormItem>
             </div>
           )}
+
+          <NFormItem label="声部:">
+            <NSpace class={styles.spaceSection}>
+              {catchStore.getSubjectAllList.map((music: any) => (
+                <NButton
+                  secondary={forms.subjectId === music.id}
+                  quaternary={forms.subjectId !== music.id}
+                  strong
+                  focusable={false}
+                  type={forms.subjectId === music.id ? 'primary' : 'default'}
+                  onClick={() => {
+                    forms.subjectId = music.id;
+                    onSearch();
+                  }}>
+                  {music.name}
+                </NButton>
+              ))}
+            </NSpace>
+          </NFormItem>
         </NForm>
       </div>
     );

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

@@ -0,0 +1,75 @@
+.btnGroupModal {
+  padding: 32px 0;
+
+  :global {
+    .n-button {
+      height: 47px;
+      min-width: 156px;
+    }
+  }
+}
+
+
+.subjectSync {
+
+  .tips {
+    padding: 40px;
+    font-size: 20px;
+    color: #131415;
+    line-height: 28px;
+
+    span {
+      color: #F20606;
+    }
+  }
+}
+
+.subjectList {
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+  padding: 0 30px;
+  gap: 20px 40px;
+}
+
+.subjectItem {
+  .imgSection {
+    position: relative;
+    padding: 7px;
+    border: 3px solid transparent;
+    border-radius: 24px;
+    line-height: 0;
+  }
+
+  img {
+    overflow: hidden;
+    border-radius: 18px;
+    width: 120px;
+    height: 120px;
+  }
+
+  .iconSelect {
+    position: absolute;
+    top: 11px;
+    right: 11px;
+    border-radius: 0;
+    width: 24px;
+    height: 24px;
+  }
+
+  .subjectName {
+    text-align: center;
+    color: #777777;
+  }
+}
+
+.subjectSelect {
+  .imgSection {
+    border: 3px solid #198CFE;
+  }
+
+  .subjectName {
+    font-weight: 600;
+    color: #198CFE;
+  }
+}

+ 102 - 0
src/views/prepare-lessons/model/subject-sync/index.tsx

@@ -0,0 +1,102 @@
+import { defineComponent, onMounted, ref } from 'vue';
+import styles from './index.module.less';
+import { NButton, 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';
+
+export default defineComponent({
+  name: 'subject-sync',
+  props: {
+    subjectId: {
+      type: [String, Number],
+      default: ''
+    }
+  },
+  emits: ['close', 'confirm'],
+  setup(props, { emit }) {
+    const catchStore = useCatchStore();
+    const prepareStore = usePrepareStore();
+    const message = useMessage();
+    const selectSubjectIds = ref([] as any);
+    const subjectList = ref([] as any);
+
+    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
+        );
+        if (index != -1) {
+          tempSubjectList.push(subject);
+        }
+      });
+
+      subjectList.value = tempSubjectList;
+
+      if (props.subjectId) {
+        selectSubjectIds.value = [Number(props.subjectId)];
+      }
+    });
+    return () => (
+      <div class={styles.subjectSync}>
+        <div class={styles.tips}>
+          请选择当前课件可使用的乐器
+          <span>(勾选后则对应乐器下的课件内容将被当前课件内容全部替换)</span>
+        </div>
+
+        <div class={styles.subjectList}>
+          {subjectList.value.map((subject: any) => (
+            <div
+              class={[
+                styles.subjectItem,
+                selectSubjectIds.value.includes(subject.id)
+                  ? styles.subjectSelect
+                  : ''
+              ]}
+              onClick={() => {
+                if (selectSubjectIds.value.includes(subject.id)) {
+                  const index = selectSubjectIds.value.indexOf(subject.id);
+                  selectSubjectIds.value.splice(index, 1);
+                } else {
+                  selectSubjectIds.value.push(subject.id);
+                }
+              }}>
+              <div class={styles.imgSection}>
+                <img src={subject.img} />
+
+                {selectSubjectIds.value.includes(subject.id) && (
+                  <img src={iconSelect} class={styles.iconSelect} />
+                )}
+              </div>
+
+              <p class={styles.subjectName}>{subject.name}</p>
+            </div>
+          ))}
+        </div>
+
+        <NSpace class={styles.btnGroupModal} justify="center">
+          <NButton round onClick={() => emit('close')}>
+            取消
+          </NButton>
+          <NButton round type="primary" onClick={onSubmit}>
+            确定
+          </NButton>
+        </NSpace>
+      </div>
+    );
+  }
+});

+ 2 - 1
src/views/preview-window/index.tsx

@@ -22,7 +22,6 @@ export default defineComponent({
   emit: ['update:show'],
   setup(props, { emit }) {
     const { show, type, params } = toRefs(props);
-    console.log(type.value, 'type');
     watch(
       () => props.show,
       () => {
@@ -62,6 +61,8 @@ export default defineComponent({
                   subjectId={params.value.subjectId || ''}
                   detailId={params.value.detailId || ''}
                   classGroupId={params.value.classGroupId || ''}
+                  lessonCourseId={params.value.lessonCourseId || ''}
+                  classId={params.value.classId || ''}
                   onClose={() => emit('update:show', false)}
                 />
               ) : type.value == 'notation' ? (

+ 30 - 1
src/views/xiaoku-ai/index.tsx

@@ -5,14 +5,17 @@ import { NButton, NImage, NSpace, NSpin } from 'naive-ui';
 import { useRouter } from 'vue-router';
 import { api_musicSheetCategoriesPage, api_musicTagTree } from './api';
 import TheEmpty from '/src/components/TheEmpty';
+import { useCatchStore } from '/src/store/modules/catchData';
 
 export default defineComponent({
   name: 'XiaokuAi',
   setup() {
+    const catchStore = useCatchStore();
     const router = useRouter();
     const forms = reactive({
       musicTagIds: [] as any[],
       enable: true,
+      subjectId: null,
       keyword: '',
       page: 1,
       rows: 9999
@@ -80,6 +83,8 @@ export default defineComponent({
       try {
         await getTags();
         await getList();
+        // 获取教材分类列表
+        await catchStore.getSubjects();
       } catch {
         //
       }
@@ -150,7 +155,7 @@ export default defineComponent({
             {data.tagChildren.map((column: any, columnIndex: number) => {
               return (
                 <div class={styles.tags}>
-                  <NSpace size={[24, 12]}>
+                  <NSpace size={[20, 12]}>
                     <span class={styles.firstButton}>{column.columnName}</span>
 
                     {column.list.map((item: any, index: number) => {
@@ -176,6 +181,30 @@ export default defineComponent({
                 </div>
               );
             })}
+            <div class={styles.tags}>
+              <NSpace size={[20, 12]}>
+                <span class={styles.firstButton}>声部</span>
+
+                {catchStore.getSubjectAllList.map((item: any) => {
+                  return (
+                    <>
+                      <NButton
+                        round
+                        secondary={item.id === forms.subjectId ? false : true}
+                        type={
+                          item.id === forms.subjectId ? 'primary' : 'default'
+                        }
+                        onClick={() => {
+                          forms.subjectId = item.id;
+                          getList();
+                        }}>
+                        {item.name}
+                      </NButton>
+                    </>
+                  );
+                })}
+              </NSpace>
+            </div>
           </div>
           <TheSearch
             round

+ 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.lexiaoya.cn';
+const proxyUrl = 'https://dev.kt.colexiu.com/';
+// const proxyUrl = 'https://test.lexiaoya.cn';
 export default defineConfig({
   base: './',
   plugins: [