Browse Source

Merge branch 'iteration-class'

lex 1 year ago
parent
commit
98a3fb5de2
39 changed files with 1424 additions and 741 deletions
  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. 1 1
      src/router/router-guards.ts
  5. 9 0
      src/store/modules/prepareLessons.ts
  6. 26 1
      src/views/classList/api.ts
  7. 113 51
      src/views/classList/index.tsx
  8. 23 3
      src/views/classList/modals/createClass.tsx
  9. 106 0
      src/views/classList/modals/resetSubject.tsx
  10. 110 0
      src/views/classList/modals/updateSubject.tsx
  11. BIN
      src/views/home/images/home/home-1.png
  12. BIN
      src/views/home/images/home/home-2.png
  13. BIN
      src/views/home/images/home/home-bg-1.png
  14. BIN
      src/views/home/images/home/home-bg-2.png
  15. BIN
      src/views/home/images/home/home-text-1.png
  16. BIN
      src/views/home/images/home/home-text-2.png
  17. 50 80
      src/views/home/index.module.less
  18. 99 361
      src/views/home/index.tsx
  19. 1 1
      src/views/login/index.module.less
  20. 110 83
      src/views/login/index.tsx
  21. 40 4
      src/views/natural-resources/model/add-teaching/index.tsx
  22. 21 2
      src/views/prepare-lessons/components/directory-main/index.tsx
  23. 99 3
      src/views/prepare-lessons/components/lesson-main/courseware/index.module.less
  24. 205 54
      src/views/prepare-lessons/components/lesson-main/courseware/index.tsx
  25. 36 24
      src/views/prepare-lessons/components/resource-main/components/resource-item/index.tsx
  26. 1 1
      src/views/prepare-lessons/components/resource-main/components/select-music/index.tsx
  27. BIN
      src/views/prepare-lessons/images/icon-class-name.png
  28. BIN
      src/views/prepare-lessons/images/icon-select.png
  29. BIN
      src/views/prepare-lessons/images/icon-subject-name.png
  30. 9 6
      src/views/prepare-lessons/index.tsx
  31. 21 1
      src/views/prepare-lessons/model/attend-class/index.module.less
  32. 65 8
      src/views/prepare-lessons/model/attend-class/index.tsx
  33. 1 1
      src/views/prepare-lessons/model/select-music/index.tsx
  34. 41 29
      src/views/prepare-lessons/model/select-resources/select-item/index.tsx
  35. 29 1
      src/views/prepare-lessons/model/select-resources/select-item/resource-search-group/index.tsx
  36. 75 0
      src/views/prepare-lessons/model/subject-sync/index.module.less
  37. 85 0
      src/views/prepare-lessons/model/subject-sync/index.tsx
  38. 0 1
      src/views/preview-window/index.tsx
  39. 31 0
      src/views/xiaoku-ai/index.tsx

+ 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

@@ -450,7 +450,9 @@
 
 
 .showClass {
-  width: 520px;
+  width: 800px;
+  border-radius: 16px;
+  overflow: hidden;
 }
 
 
@@ -472,4 +474,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>

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

+ 26 - 1
src/views/classList/api.ts

@@ -160,7 +160,6 @@ export const getTrainingClassDetail = (params: any) => {
   });
 };
 
-
 /**
  * 学生练习记录
  */
@@ -169,3 +168,29 @@ export const getTrainingStatList = (params: any) => {
     data: params
   });
 };
+
+/**
+ * 获取班级列表
+ */
+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 } from './api';
+import { classGroupList, deleteClass, getSubject } 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,
@@ -58,7 +61,10 @@ export default defineComponent({
       removeRow: {} as any,
       previewModal: false,
       previewParams: {} as any,
-      lastCourse:null as any
+      lastCourse: null as any,
+      subjectList: [] as any,
+      showResetClass: false,
+      showSubjectClass: false
     });
     const formRef = ref();
     const dialog = useDialog();
@@ -75,7 +81,8 @@ export default defineComponent({
       state.searchForm = {
         keyword: null as any,
         currentClass: null,
-        currentGradeNum: null
+        currentGradeNum: null,
+        subjectId: null
       };
       getList();
     };
@@ -113,23 +120,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: '25%'
+          width: '20%'
         },
         {
           title: '上次学习',
           key: 'lastStudy',
-          width: '25%',
+          width: '20%',
           render(row: any) {
             return row.lastStudy ? (
               <TheTooltip
@@ -177,7 +198,12 @@ export default defineComponent({
                       详情
                     </NButton>
                   )}
-
+                  <NButton
+                    type="primary"
+                    text
+                    onClick={() => resetClassSubject(row)}>
+                    修改声部
+                  </NButton>
                   {index == 0 ? (
                     <NButton
                       type="primary"
@@ -240,25 +266,38 @@ 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;
     };
     onMounted(() => {
       getList();
+      getSubjectList();
     });
     return () => (
       <div class={styles.listWrap}>
@@ -294,7 +333,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}>
@@ -360,40 +408,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>
 
         {/* 上课弹窗 */}

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

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

@@ -0,0 +1,110 @@
+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 = () => {
+      data.uploading = true;
+      foemsRef.value.validate(async (error: any) => {
+        if (error) {
+          return;
+        }
+        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');
+          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: 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 - 361
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,60 +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) {
-            const res = await courseScheduleStart({
-              lessonCoursewareKnowledgeDetailId: forms.unit,
-              classGroupId: forms.applyClassItem?.classGroupId
-            });
-
-            if (window.matchMedia('(display-mode: standalone)').matches) {
-              forms.showPreview = true;
-              forms.itemPreview = {
-                type: 'class',
-                classId: res.data, // 上课编号
-                lessonCourseId: forms.category,
-                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',
-                  classId: res.data, // 上课编号
-                  lessonCourseId: forms.category,
-                  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;
@@ -392,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>
 
@@ -417,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} />
@@ -431,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>
@@ -728,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"
@@ -835,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>
     );
   }

+ 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

+ 21 - 2
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,8 +23,8 @@ 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
     });
@@ -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(selectItem.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}>

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

@@ -11,6 +11,71 @@
     line-height: 38px;
   }
 
+  .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 {
     .n-base-selection {
       --n-height: 38px !important;
@@ -55,15 +120,41 @@
       }
     }
   }
+
+  .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);
   }
 
   .emptySection {
@@ -180,3 +271,8 @@
     }
   }
 }
+
+
+.subjectSyncModal {
+  width: 920px;
+}

+ 205 - 54
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: 'select', //
       removeIds: [] as any, // 临时删除的编号
       drag: false,
+      isEdit: false, // 是否更新数据
+      editSubjectIds: '', // 声部编号
       removeVisiable: false,
       removeVisiable1: false,
+      subjectSyncVisiable: false, // 同步声部
       show: false,
       item: {} as any,
       previewModal: false,
@@ -133,18 +142,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 +160,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 {
         //
       }
-      //   }
-      // });
     };
 
     // 预览上课
@@ -251,24 +256,59 @@ export default defineComponent({
       }
     };
 
-    onMounted(async () => {
-      // 获取教材分类列表
-      await catchStore.getSubjects();
+    watch(
+      () => prepareStore.getSubjectList,
+      () => {
+        checkSubjectIds();
+      }
+    );
 
-      const subjectList = catchStore.getSubjectList;
+    const checkSubjectIds = () => {
+      const subjectList = prepareStore.getSubjectList;
+      // console.log(subjectList, 'subjectList');
       // 并且没有声部时才会更新
-      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) {
           // 判断是否有缓存
           prepareStore.setSubjectId(subjectList[0].id);
         }
       }
+    };
+
+    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 () => (
@@ -279,7 +319,11 @@ export default defineComponent({
               <NButton
                 type="default"
                 onClick={() => {
-                  forms.removeVisiable = true;
+                  if (forms.isEdit) {
+                    forms.subjectSyncVisiable = true;
+                  } else {
+                    forms.removeVisiable = true;
+                  }
                 }}>
                 完成编辑
               </NButton>
@@ -287,6 +331,7 @@ export default defineComponent({
                 type="error"
                 onClick={() => {
                   forms.drag = false;
+                  forms.isEdit = false;
                   prepareStore.setIsEditResource(false);
                   forms.removeIds = [];
                   getList();
@@ -297,6 +342,7 @@ export default defineComponent({
                 type="error"
                 onClick={() => {
                   forms.removeVisiable1 = true;
+                  forms.isEdit = true;
                 }}>
                 清空资源
               </NButton>
@@ -304,51 +350,59 @@ export default defineComponent({
             </NSpace>
           ) : (
             <NSpace>
-              <NSelect
-                placeholder="选择声部"
-                options={catchStore.getSubjectList}
-                labelField="name"
-                valueField="id"
-                value={prepareStore.getSubjectId}
-                onUpdate:value={(val: any) => {
-                  prepareStore.setSubjectId(val);
-                  getList();
-                }}
-              />
+              {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.drag = true;
                   prepareStore.setIsEditResource(true);
+
+                  // forms.subjectSyncVisiable = 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' }}>
@@ -405,7 +459,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}
@@ -432,6 +486,66 @@ export default defineComponent({
           </NSpin>
         </NScrollbar>
 
+        <div class={styles.btnGroup} style={{ justifyContent: 'flex-end' }}>
+          <NSpace justify="end">
+            <NButton type="primary" onClick={onPreviewAttend}>
+              预览课件
+            </NButton>
+            <NButton
+              {...{ id: 'lessons-3' }}
+              type="primary"
+              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) {
+                  // 开始上课
+                  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;
+                    forms.previewParams = {
+                      type: 'class',
+                      classGroupId: forms.classGroupId,
+                      subjectId: prepareStore.getSubjectId,
+                      detailId: prepareStore.getSelectKey
+                    };
+                  } else {
+                    const { href } = router.resolve({
+                      path: '/attend-class',
+                      query: {
+                        type: 'class',
+                        classGroupId: forms.classGroupId,
+                        subjectId: prepareStore.getSubjectId,
+                        detailId: prepareStore.getSelectKey
+                      }
+                    });
+                    window.open(href, +new Date() + '');
+                  }
+                } else {
+                  forms.showAttendClass = true;
+                  forms.attendClassType = 'select';
+                }
+              }}>
+              开始上课
+            </NButton>
+          </NSpace>
+        </div>
+
         <NModal
           v-model:show={forms.showAttendClass}
           preset="card"
@@ -441,6 +555,7 @@ 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(
@@ -460,6 +575,20 @@ export default defineComponent({
                 window.open(href, +new Date() + '');
               }
             }}
+            onConfirm={(item: any) => {
+              console.log(item, 'confirm');
+              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
+              });
+            }}
           />
         </NModal>
 
@@ -524,6 +653,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>
     );
   }

+ 36 - 24
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 {
         //
       }
@@ -222,7 +234,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


+ 9 - 6
src/views/prepare-lessons/index.tsx

@@ -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);
     });
 
     // 当前页面离开时

+ 21 - 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,20 @@
     }
   }
 
+  .subjects {
+    font-size: 20px !important;
+    font-weight: 600 !important;
+    color: #131415 !important;
+    line-height: 28px;
+
+    &.noSubjects {
+      color: #F44541 !important;
+    }
+  }
+
   .content {
     margin-top: 12px !important;
     font-size: 16px;
     color: #777777;
   }
-}
+}

+ 65 - 8
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,21 @@ 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,13 +42,29 @@ export default defineComponent({
     // 开始上课
     const onAttendClass = async (item: any) => {
       try {
-        const res = 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;
+        }
+
+        await courseScheduleStart({
           lessonCoursewareKnowledgeDetailId: prepareStore.selectKey,
           classGroupId: item.id
         });
 
         emit('close');
-
         emit('preview', {
           type: 'class',
           classId: res.data, // 上课编号
@@ -160,9 +187,20 @@ 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 && (
@@ -176,6 +214,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>
     );
   }

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

@@ -125,7 +125,7 @@ export default defineComponent({
                           isShowAdd
                           isShowCollect={false}
                           item={item}
-                          isShowAddDisabled={prepareStore.getIsEditTrain}
+                          isShowAddDisabled={!prepareStore.getIsEditTrain}
                           onAdd={() => emit('add', item)}
                           disabledMouseHover={false}
                           onClick={() => {

+ 41 - 29
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') {
@@ -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}
           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: 24px;
+    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;
+  }
+}

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

@@ -0,0 +1,85 @@
+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';
+
+export default defineComponent({
+  name: 'subject-sync',
+  props: {
+    subjectId: {
+      type: [String, Number],
+      default: ''
+    }
+  },
+  emits: ['close', 'confirm'],
+  setup(props, { emit }) {
+    const catchStore = useCatchStore();
+    const message = useMessage();
+    const selectSubjectIds = ref([] as any);
+
+    const onSubmit = () => {
+      if (selectSubjectIds.value.length <= 0) {
+        message.error('至少选择一个声部进行同步');
+        return;
+      }
+
+      emit('confirm', selectSubjectIds.value);
+    };
+    onMounted(async () => {
+      // 获取教材分类列表
+      await catchStore.getSubjects();
+
+      if (props.subjectId) {
+        selectSubjectIds.value = [Number(props.subjectId)];
+      }
+    });
+    return () => (
+      <div class={styles.subjectSync}>
+        <div class={styles.tips}>
+          请选择当前课件可使用的乐器
+          <span>(勾选后则对应乐器下的课件内容将被当前课件内容全部替换)</span>
+        </div>
+
+        <div class={styles.subjectList}>
+          {catchStore.getSubjectList.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>
+    );
+  }
+});

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

+ 31 - 0
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 {
         //
       }
@@ -176,6 +181,32 @@ export default defineComponent({
                 </div>
               );
             })}
+            <div class={styles.tags}>
+              <NSpace size={[24, 12]}>
+                <span class={styles.firstButton}>声部</span>
+
+                {catchStore.getSubjectAllList.map(
+                  (item: any, index: number) => {
+                    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