Browse Source

添加备课训练

lex 1 năm trước cách đây
mục cha
commit
fa7561a56a
33 tập tin đã thay đổi với 1894 bổ sung522 xóa
  1. 312 196
      package-lock.json
  2. 1 0
      package.json
  3. 1 2
      src/components/card-type/index.tsx
  4. 4 1
      src/components/layout/layoutTop.tsx
  5. 29 2
      src/store/modules/prepareLessons.ts
  6. 1 1
      src/store/mutation-types.ts
  7. 15 2
      src/styles/index.less
  8. 10 0
      src/utils/contants.ts
  9. 1 1
      src/views/attend-class/model/train-settings/index.module.less
  10. BIN
      src/views/attend-class/model/train-type/images/icon-delete.png
  11. 28 0
      src/views/attend-class/model/train-type/index.module.less
  12. 58 23
      src/views/attend-class/model/train-type/index.tsx
  13. 4 0
      src/views/attend-class/model/train-update/index.module.less
  14. 208 58
      src/views/attend-class/model/train-update/index.tsx
  15. 81 0
      src/views/prepare-lessons/api.ts
  16. 65 55
      src/views/prepare-lessons/components/directory-main/index.tsx
  17. 52 0
      src/views/prepare-lessons/components/lesson-main/courseware/index.module.less
  18. 192 26
      src/views/prepare-lessons/components/lesson-main/courseware/index.tsx
  19. 168 0
      src/views/prepare-lessons/components/lesson-main/train/assign-homework.tsx
  20. 62 0
      src/views/prepare-lessons/components/lesson-main/train/index.module.less
  21. 295 14
      src/views/prepare-lessons/components/lesson-main/train/index.tsx
  22. 58 3
      src/views/prepare-lessons/components/resource-main/components/resource-item/index.tsx
  23. 1 1
      src/views/prepare-lessons/components/resource-main/components/resource-item/resource-search-group/index.tsx
  24. 4 0
      src/views/prepare-lessons/components/resource-main/components/select-music/index.module.less
  25. 72 16
      src/views/prepare-lessons/components/resource-main/components/select-music/index.tsx
  26. 2 4
      src/views/prepare-lessons/components/resource-main/components/select-music/resource-search-group/index.tsx
  27. 4 0
      src/views/prepare-lessons/components/resource-main/index.module.less
  28. 58 10
      src/views/prepare-lessons/components/resource-main/index.tsx
  29. BIN
      src/views/prepare-lessons/images/icon-delete.png
  30. 23 58
      src/views/prepare-lessons/model/attend-class/index.tsx
  31. 19 14
      src/views/prepare-lessons/model/select-music/index.tsx
  32. 12 33
      src/views/prepare-lessons/model/select-music/search-group.tsx
  33. 54 2
      src/views/prepare-lessons/model/select-resources/select-item/index.tsx

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 312 - 196
package-lock.json


+ 1 - 0
package.json

@@ -43,6 +43,7 @@
     "vue": "^3.3.4",
     "vue-router": "^4.1.6",
     "vue3-lottie": "^2.7.0",
+    "vuedraggable": "^4.1.0",
     "wavesurfer.js": "^7.0.0-beta.11"
   },
   "devDependencies": {

+ 1 - 2
src/components/card-type/index.tsx

@@ -206,8 +206,7 @@ export default defineComponent({
                     onClick={(e: MouseEvent) => {
                       e.stopPropagation();
                       e.preventDefault();
-                      emit('add');
-                      console.log('add');
+                      emit('add', props.item);
                     }}>
                     添加
                   </NButton>

+ 4 - 1
src/components/layout/layoutTop.tsx

@@ -21,7 +21,10 @@ export default defineComponent({
       <>
         <div class={styles.layoutTop}>
           <div class={styles.layoutLeft}>
-            <NImage src={schoolIcon} class={styles.schoolIcon}></NImage>
+            <NImage
+              src={schoolIcon}
+              class={styles.schoolIcon}
+              previewDisabled></NImage>
             <p>武汉市武昌区教育局 | 武汉小学</p>
           </div>
           <div class={styles.layoutRight}>

+ 29 - 2
src/store/modules/prepareLessons.ts

@@ -10,9 +10,12 @@ export const usePrepareStore = defineStore('prepare-lessons-store', {
     lessonCoursewareDetailId: '', // 哪个教材详情
     treeList: [] as any[], // 左边教学课件列表
     coursewareList: [] as any[], // 课件信息
+    trainList: [] as any[], // 训练信息
     tabType: 'courseware', // 备课 - 课件 | 训练 类型切换 'courseware' | 'train'
     selectMusicStatus: false, // 乐谱状态
-    selectResourceStatus: false // 资源状态
+    selectResourceStatus: false, // 资源状态
+    isAddResource: false, // 是否添加资源
+    isAddTrain: false // 是否添加训练
   }),
   getters: {
     /** 获取资源状态 */
@@ -43,6 +46,10 @@ export const usePrepareStore = defineStore('prepare-lessons-store', {
     getCoursewareList(): any[] {
       return this.coursewareList;
     },
+    /** 获取训练列表 */
+    getTrainList(): any[] {
+      return this.trainList;
+    },
     /** 获取课件类型 */
     getTabType(): string {
       return this.tabType;
@@ -54,6 +61,14 @@ export const usePrepareStore = defineStore('prepare-lessons-store', {
     /** 获取资源状态 */
     getSelectResourceStatus(): boolean {
       return this.selectResourceStatus;
+    },
+    /** 获取是否添加资源 */
+    getIsAddResource(): boolean {
+      return this.isAddResource;
+    },
+    /** 获取是否添加训练 */
+    getIsAddTrain(): boolean {
+      return this.isAddTrain;
     }
   },
   actions: {
@@ -85,7 +100,11 @@ export const usePrepareStore = defineStore('prepare-lessons-store', {
     setCoursewareList(list: any[]) {
       this.coursewareList = list;
     },
-    /** 设置课件类型 */
+    /** 设置训练列表 */
+    setTrainList(list: any[]) {
+      this.trainList = list;
+    },
+    /** 设置tab类型 */
     setTabType(type: string) {
       this.tabType = type;
     },
@@ -96,6 +115,14 @@ export const usePrepareStore = defineStore('prepare-lessons-store', {
     /** 设置资源状态 */
     setSelectResourceStatus(status: boolean) {
       this.selectResourceStatus = status;
+    },
+    /** 设置资源状态 */
+    setIsAddResource(status: boolean) {
+      this.isAddResource = status;
+    },
+    /** 设置训练状态 */
+    setIsAddTrain(status: boolean) {
+      this.isAddTrain = status;
     }
   }
 });

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

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

+ 15 - 2
src/styles/index.less

@@ -1,4 +1,3 @@
-
 * {
   padding: 0;
   margin: 0;
@@ -167,7 +166,7 @@ body {
 // 给弹窗设置标题的基础样式
 .modalTitle {
   border-radius: 16px;
-  overflow: hidden;
+  // overflow: hidden;
 
   &.background {
     .n-card-header {
@@ -176,6 +175,7 @@ body {
   }
 
   .n-card-header {
+    border-radius: 16px 16px 0 0;
     position: relative;
     padding: 20px 18px;
     text-align: center;
@@ -290,4 +290,17 @@ body {
 .list-leave-to {
   opacity: 0;
   transform: translateX(30px);
+}
+
+// 拖动时
+.sortable-ghost {
+  opacity: 0.7;
+}
+
+.flip-list-move {
+  transition: transform 0.5s;
+}
+
+.no-move {
+  transition: transform 0s;
 }

+ 10 - 0
src/utils/contants.ts

@@ -28,3 +28,13 @@ export const resourceType = {
   SONG: '音频',
   VIDEO: '视频'
 };
+
+/**
+ * @description: 评测难度
+ * 入门:BEGINNER/进阶:ADVANCED/大师:PERFORMER")
+ */
+export const evaluateDifficult = {
+  BEGINNER: '入门级',
+  ADVANCED: '进阶级',
+  PERFORMER: '大师级'
+} as any;

+ 1 - 1
src/views/attend-class/model/train-settings/index.module.less

@@ -71,7 +71,7 @@
 }
 
 .trainEditModal {
-  width: 494px;
+  width: 580px;
 }
 
 .selectMusicModal {

BIN
src/views/attend-class/model/train-type/images/icon-delete.png


+ 28 - 0
src/views/attend-class/model/train-type/index.module.less

@@ -4,12 +4,25 @@
   background: #ECF6FF;
   border-radius: 16px;
   padding: 20px;
+  position: relative;
+
+  .overflowBg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    z-index: 9;
+    padding: 20px;
+    cursor: move;
+  }
 }
 
 .train-header {
   display: flex;
   align-items: center;
   justify-content: space-between;
+  position: relative;
 
   .title {
     display: flex;
@@ -56,6 +69,21 @@
       height: 10px;
     }
   }
+
+  .iconDelete {
+    position: absolute;
+    right: 0px;
+    top: 0px;
+    z-index: 10;
+    display: flex;
+    height: 30px;
+    padding: 0;
+
+    img {
+      height: 30px;
+      width: 30px;
+    }
+  }
 }
 
 .train-content {

+ 58 - 23
src/views/attend-class/model/train-type/index.tsx

@@ -5,25 +5,34 @@ import pTag from './images/p-tag.svg';
 import eTag from './images/e-tag.svg';
 import pEdit from './images/p-edit.svg';
 import eEdit from './images/e-edit.svg';
-// import iconPlay from './images/icon-play.svg';
 import iconPause from './images/icon-pause.svg';
 import pDelete from './images/p-delete.svg';
 import eDelete from './images/e-delete.svg';
+import iconDelete from './images/icon-delete.png';
 import { useUserStore } from '/src/store/modules/users';
 type ItemType = {
   id: string | number;
-  type: 'practice' | 'evaluation';
-  src: string;
-  name: string;
+  trainingType: 'PRACTICE' | 'EVALUATION';
+  musicId: string | number;
+  coverImg: string;
+  musicName: string;
   typeList: string[];
 };
 
 export default defineComponent({
   name: 'train-type',
   props: {
+    type: {
+      type: String as PropType<'prepare' | 'homework'>,
+      default: 'homework'
+    },
     item: {
       type: Object as PropType<ItemType>,
       default: () => ({})
+    },
+    isDelete: {
+      type: Boolean,
+      default: false
     }
   },
   emits: ['click', 'delete', 'edit'],
@@ -55,27 +64,43 @@ export default defineComponent({
       <div
         class={[
           styles.trainType,
-          props.item.type === 'evaluation' ? styles.evaluationType : ''
+          props.item.trainingType === 'EVALUATION' ? styles.evaluationType : ''
         ]}
         onClick={() => emit('click', props.item)}>
+        {props.isDelete && <div class={styles.overflowBg}></div>}
         <div class={styles['train-header']}>
           <div class={styles.title}>
             <img
-              src={props.item.type === 'evaluation' ? eTag : pTag}
+              src={props.item.trainingType === 'EVALUATION' ? eTag : pTag}
               class={styles['title-tag']}
             />
             <NEllipsis class={styles['title-text']}>
-              {props.item.name}
+              {props.item.musicName}
             </NEllipsis>
           </div>
 
-          <NButton class={styles.btn} round onClick={onDetail}>
-            {props.item.type === 'evaluation' ? '评测模式' : '练习模式'}
-            <img src={iconPause} />
-          </NButton>
+          {props.isDelete ? (
+            <NButton
+              class={styles.iconDelete}
+              quaternary
+              round
+              onClick={(e: MouseEvent) => {
+                e.stopPropagation();
+                emit('delete', props.item);
+              }}>
+              <img src={iconDelete} />
+            </NButton>
+          ) : (
+            <NButton class={styles.btn} round onClick={onDetail}>
+              {props.item.trainingType === 'EVALUATION'
+                ? '评测模式'
+                : '练习模式'}
+              <img src={iconPause} />
+            </NButton>
+          )}
         </div>
         <div class={styles['train-content']}>
-          <NImage src={props.item.src} previewDisabled objectFit="cover" />
+          <NImage src={props.item.coverImg} previewDisabled objectFit="cover" />
           <div class={styles.preview}>
             <NButton
               strong
@@ -88,7 +113,7 @@ export default defineComponent({
         </div>
         <div class={styles['train-footer']}>
           <NSpace class={styles.type}>
-            {props.item.typeList.map((type: string) => (
+            {props.item.typeList?.map((type: string) => (
               <NTag>{type}</NTag>
             ))}
           </NSpace>
@@ -96,22 +121,32 @@ export default defineComponent({
           <NSpace size={6}>
             <n-button
               quaternary
+              disabled={props.isDelete}
               class={styles.operation}
               onClick={(e: MouseEvent) => {
                 e.stopPropagation();
                 emit('edit', props.item);
               }}>
-              <img src={props.item.type === 'evaluation' ? eEdit : pEdit} />
-            </n-button>
-            <n-button
-              quaternary
-              class={styles.operation}
-              onClick={(e: MouseEvent) => {
-                e.stopPropagation();
-                onDelete();
-              }}>
-              <img src={props.item.type === 'evaluation' ? eDelete : pDelete} />
+              <img
+                src={props.item.trainingType === 'EVALUATION' ? eEdit : pEdit}
+              />
             </n-button>
+            {props.type === 'homework' && (
+              <n-button
+                quaternary
+                disabled={props.isDelete}
+                class={styles.operation}
+                onClick={(e: MouseEvent) => {
+                  e.stopPropagation();
+                  onDelete();
+                }}>
+                <img
+                  src={
+                    props.item.trainingType === 'EVALUATION' ? eDelete : pDelete
+                  }
+                />
+              </n-button>
+            )}
           </NSpace>
         </div>
       </div>

+ 4 - 0
src/views/attend-class/model/train-update/index.module.less

@@ -36,4 +36,8 @@
       border-radius: 8px;
     }
   }
+
+  .scoreGroup {
+    display: flex;
+  }
 }

+ 208 - 58
src/views/attend-class/model/train-update/index.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, reactive } from 'vue';
+import { defineComponent, nextTick, onMounted, reactive, ref } from 'vue';
 import styles from './index.module.less';
 import {
   NButton,
@@ -8,136 +8,286 @@ import {
   NInputGroup,
   NInputGroupLabel,
   NInputNumber,
-  NSpace
+  NSpace,
+  useMessage
 } from 'naive-ui';
+import {
+  lessonPreTrainingAdd,
+  lessonPreTrainingUpdate
+} from '/src/views/prepare-lessons/api';
 
 export default defineComponent({
   name: 'train-update',
-  emits: ['close'],
+  props: {
+    /** 初始数据 */
+    item: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  emits: ['close', 'confirm'],
   setup(props, { emit }) {
     // 'practice' | 'evaluation'
+    const message = useMessage();
     const forms = reactive({
-      type: 'practice',
-      minScore: null,
-      maxScore: null,
-      difficulty: '1'
+      id: null as any,
+      uploading: false,
+      baseMaxScore: 99,
+      type: 'PRACTICE',
+      musicId: '',
+      coursewareKnowledgeDetailId: '', // 章节编号
+      minScore: null as any,
+      maxScore: null as any,
+      subjectId: '',
+      coverImg: '',
+      practiceSpeed: null as any, // 练习速度
+      practiceTimes: null as any, // 练习时长
+      difficulty: 'BEGINNER', // 评测难度
+      evaluationSpeed: null as any, // 评测速度
+      evaluationScore: null as any // 评测分数
+    });
+    const formsRef = ref();
+
+    const onSubmit = async () => {
+      formsRef.value?.validate(async (err: any) => {
+        if (err) {
+          return;
+        }
+        forms.uploading = true;
+        try {
+          const params = {
+            trainingType: forms.type,
+            musicId: forms.musicId,
+            coursewareKnowledgeDetailId: forms.coursewareKnowledgeDetailId,
+            subjectId: forms.subjectId,
+            id: forms.id,
+            coverImg: forms.coverImg,
+            trainingConfigJson: ''
+          };
+          const configJson: any = {};
+          if (forms.type === 'PRACTICE') {
+            configJson.practiceChapterBegin = forms.minScore;
+            configJson.practiceChapterEnd = forms.maxScore;
+            configJson.practiceSpeed = forms.practiceSpeed;
+            configJson.trainingTimes = forms.practiceTimes;
+          } else {
+            configJson.evaluateDifficult = forms.difficulty;
+            configJson.evaluateSpeed = forms.evaluationSpeed;
+            configJson.trainingTimes = forms.evaluationScore;
+          }
+          configJson.practiceChapterMax = forms.baseMaxScore;
+          params.trainingConfigJson = configJson;
+          if (forms.id) {
+            await lessonPreTrainingUpdate(params);
+            message.success('修改成功');
+          } else {
+            await lessonPreTrainingAdd(params);
+            message.success('添加成功');
+          }
+
+          emit('close');
+          emit('confirm');
+        } catch {
+          //
+        }
+        forms.uploading = false;
+      });
+    };
+
+    onMounted(() => {
+      const item = props.item;
+      if (item.trainId) {
+        forms.id = item.trainId;
+        forms.minScore = item.practiceChapterBegin;
+        forms.maxScore = item.practiceChapterEnd;
+        forms.practiceSpeed = item.practiceSpeed;
+        if (item.trainingType === 'PRACTICE') {
+          forms.practiceTimes = item.trainingTimes;
+        } else {
+          forms.evaluationScore = item.trainingTimes;
+        }
+        forms.difficulty = item.evaluateDifficult || 'BEGINNER';
+        forms.evaluationSpeed = item.evaluateSpeed;
+      } else {
+        forms.minScore = 1;
+        forms.maxScore = item.practiceChapterMax ? item.practiceChapterMax : 1;
+      }
+      forms.baseMaxScore = item.practiceChapterMax || 99;
+      forms.musicId = item.id;
+      forms.coursewareKnowledgeDetailId = item.coursewareKnowledgeDetailId;
+      forms.subjectId = item.subjectId;
+      forms.coverImg = item.coverImg;
     });
     return () => (
       <div class={styles.trainUpdate}>
-        <NForm labelAlign="left" labelPlacement="left">
-          <NFormItem label="训练方式" path="type">
+        <NForm
+          ref={formsRef}
+          model={forms}
+          labelAlign="right"
+          labelPlacement="left">
+          <NFormItem
+            label="训练方式"
+            path="type"
+            rule={[{ required: true, message: '请选择训练方式' }]}>
             <NSpace>
               <NButton
                 secondary
                 class={[
                   styles.switch,
-                  forms.type === 'practice' ? styles.active : ''
+                  forms.type === 'PRACTICE' ? styles.active : ''
                 ]}
-                onClick={() => (forms.type = 'practice')}>
+                onClick={() => (forms.type = 'PRACTICE')}>
                 练习
               </NButton>
               <NButton
                 secondary
                 class={[
                   styles.switch,
-                  forms.type === 'evaluation' ? styles.active : ''
+                  forms.type === 'EVALUATION' ? styles.active : ''
                 ]}
-                onClick={() => (forms.type = 'evaluation')}>
+                onClick={() => (forms.type = 'EVALUATION')}>
                 评测
               </NButton>
             </NSpace>
           </NFormItem>
-          {forms.type === 'practice' && (
+          {forms.type === 'PRACTICE' && (
             <>
-              <NFormItem label="练习小节" path="minScore">
-                <NInputNumber
-                  v-model:value={forms.minScore}
-                  showButton={false}
-                  min={0}
-                  max={99}
-                  placeholder="最小练习小节"
-                  onUpdate:value={() => {
-                    forms.maxScore = null;
-                  }}
-                  style={{ width: '46%' }}
-                />
+              <div class={styles.scoreGroup}>
+                <NFormItem
+                  label="练习小节"
+                  path="minScore"
+                  rule={[{ required: true, message: '请输入最小练习小节' }]}>
+                  <NInputNumber
+                    v-model:value={forms.minScore}
+                    showButton={false}
+                    min={1}
+                    max={forms.baseMaxScore}
+                    placeholder="最小练习小节"
+                    onUpdate:value={() => {
+                      forms.maxScore = null;
+                    }}
+                    clearable
+                  />
+                </NFormItem>
                 <div
                   style={{
+                    '--n-feedback-height': '24px',
                     display: 'flex',
                     alignItems: 'center',
-                    lineHeight: '1',
-                    marginTop: '-2px',
-                    margin: '-2px 4% 0 4%'
+                    margin: '-2px 2% 0 2%',
+                    marginBottom: 'var(--n-feedback-height)'
                   }}>
                   -
                 </div>
+                <NFormItem
+                  path="maxScore"
+                  rule={[{ required: true, message: '请输入最大练习小节' }]}>
+                  <NInputNumber
+                    v-model:value={forms.maxScore}
+                    showButton={false}
+                    min={forms.minScore || 1}
+                    max={forms.baseMaxScore}
+                    placeholder="最大练习小节"
+                    clearable
+                  />
+                </NFormItem>
+              </div>
+              <NFormItem
+                label="练习速度"
+                path="practiceSpeed"
+                rule={[{ required: true, message: '请输入练习速度' }]}>
                 <NInputNumber
-                  v-model:value={forms.maxScore}
-                  showButton={false}
-                  min={forms.minScore || 0}
-                  max={99}
-                  placeholder="最大练习小节"
-                  style={{ width: '46%' }}
-                />
-              </NFormItem>
-              <NFormItem label="练习速度">
-                <NInputNumber
-                  min={0}
+                  min={60}
+                  max={270}
                   showButton={false}
                   style={{ width: '100%' }}
+                  v-model:value={forms.practiceSpeed}
+                  placeholder="练习速度范围60~270"
+                  clearable
                 />
               </NFormItem>
-              <NFormItem label="练习时长">
+              <NFormItem
+                label="练习时长"
+                path="practiceTimes"
+                rule={[{ required: true, message: '请输入练习时长' }]}>
                 <NInputGroup>
-                  <NInput />
+                  <NInputNumber
+                    min={0}
+                    showButton={false}
+                    style={{ width: '100%' }}
+                    v-model:value={forms.practiceTimes}
+                    placeholder="请输入练习时长"
+                    clearable
+                  />
                   <NInputGroupLabel>分钟</NInputGroupLabel>
                 </NInputGroup>
               </NFormItem>
             </>
           )}
 
-          {forms.type === 'evaluation' && (
+          {forms.type === 'EVALUATION' && (
             <>
-              <NFormItem label="评测难度" path="type">
+              <NFormItem
+                label="评测难度"
+                path="type"
+                rule={[{ required: true, message: '请选择评测难度' }]}>
                 <NSpace>
                   <NButton
                     secondary
                     class={[
                       styles.switch,
-                      forms.difficulty === '1' ? styles.active : ''
+                      forms.difficulty === 'BEGINNER' ? styles.active : ''
                     ]}
-                    onClick={() => (forms.difficulty = '1')}>
+                    onClick={() => (forms.difficulty = 'BEGINNER')}>
                     入门级
                   </NButton>
                   <NButton
                     secondary
                     class={[
                       styles.switch,
-                      forms.difficulty === '2' ? styles.active : ''
+                      forms.difficulty === 'ADVANCED' ? styles.active : ''
                     ]}
-                    onClick={() => (forms.difficulty = '2')}>
+                    onClick={() => (forms.difficulty = 'ADVANCED')}>
                     进阶级
                   </NButton>
                   <NButton
                     secondary
                     class={[
                       styles.switch,
-                      forms.difficulty === '3' ? styles.active : ''
+                      forms.difficulty === 'PERFORMER' ? styles.active : ''
                     ]}
-                    onClick={() => (forms.difficulty = '3')}>
+                    onClick={() => (forms.difficulty = 'PERFORMER')}>
                     大师级
                   </NButton>
                 </NSpace>
               </NFormItem>
-              <NFormItem label="评测速度">
-                <NInputGroup>
-                  <NInput />
-                  <NInputGroupLabel>分钟</NInputGroupLabel>
-                </NInputGroup>
+              <NFormItem
+                label="评测速度"
+                path="evaluationSpeed"
+                rule={[{ required: true, message: '请输入评测速度' }]}>
+                <NInputNumber
+                  min={60}
+                  max={270}
+                  showButton={false}
+                  style={{ width: '100%' }}
+                  v-model:value={forms.evaluationSpeed}
+                  placeholder="评测速度范围60~270"
+                  clearable
+                />
               </NFormItem>
-              <NFormItem label="合格分数">
+              <NFormItem
+                label="合格分数"
+                path="evaluationScore"
+                rule={[{ required: true, message: '请输入合格分数' }]}>
                 <NInputGroup>
-                  <NInput />
+                  <NInputNumber
+                    min={0}
+                    showButton={false}
+                    style={{ width: '100%' }}
+                    v-model:value={forms.evaluationScore}
+                    placeholder="请输入合格分数"
+                    clearable
+                  />
                   <NInputGroupLabel>分</NInputGroupLabel>
                 </NInputGroup>
               </NFormItem>
@@ -148,7 +298,7 @@ export default defineComponent({
             <NButton strong type="default" round onClick={() => emit('close')}>
               取消
             </NButton>
-            <NButton strong type="primary" round onClick={() => emit('close')}>
+            <NButton strong type="primary" round onClick={() => onSubmit()}>
               确认
             </NButton>
           </NSpace>

+ 81 - 0
src/views/prepare-lessons/api.ts

@@ -33,3 +33,84 @@ export const queryCourseware = (params: any) => {
     data: params
   });
 };
+
+/**
+ * 备课 - 保存课件
+ */
+export const saveCourseware = (params: any) => {
+  return request.post('/edu-app/teacherKnowledgeMaterial/saveCourseware', {
+    data: params
+  });
+};
+
+/**
+ * 备课 - 训练列表
+ */
+export const lessonPreTrainingPage = (params: any) => {
+  return request.post('/edu-app/lessonPreTraining/page', {
+    data: params
+  });
+};
+
+/**
+ * 备课 - 新增训练
+ */
+export const lessonPreTrainingAdd = (params: any) => {
+  return request.post('/edu-app/lessonPreTraining/add', {
+    data: params
+  });
+};
+
+/**
+ * 备课 - 修改训练
+ */
+export const lessonPreTrainingUpdate = (params: any) => {
+  return request.post('/edu-app/lessonPreTraining/update', {
+    data: params
+  });
+};
+
+/**
+ * 备课 - 删除训练
+ */
+export const lessonPreTrainingDelete = (params: any) => {
+  return request.post('/edu-app/lessonPreTraining/delete', {
+    data: params
+  });
+};
+
+/**
+ * 备课 - 保存预训练曲目
+ */
+export const lessonPreTrainingBatchSave = (params: any) => {
+  return request.post('/edu-app/lessonPreTraining/batchSave', {
+    data: params
+  });
+};
+
+/**
+ * 备课 - 乐谱列表
+ */
+export const musicSheetPage = (params: any) => {
+  return request.post('/edu-app/musicSheet/page', {
+    data: params
+  });
+};
+
+/**
+ * 备课 - 班级列表
+ */
+export const classGroupPage = (params: any) => {
+  return request.post('/edu-app/classGroup/page', {
+    data: params
+  });
+};
+
+/**
+ * 备课 - 布置作业
+ */
+export const lessonTrainingAdd = (params: any) => {
+  return request.post('/edu-app/lessonTraining/add', {
+    data: params
+  });
+};

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

@@ -106,64 +106,74 @@ export default defineComponent({
 
         <NScrollbar class={styles.scrollBar}>
           <NSpin show={show.value}>
-            {prepareStore.getTreeList.map((item: any, index: number) => (
-              <div class={styles.treeParent} key={'parent' + index}>
-                <div
-                  class={[styles.treeItem, styles.parentItem]}
-                  onClick={() => {
-                    prepareStore.getTreeList.forEach((child: any) => {
-                      if (item.id !== child.id) {
-                        child.selected = false;
-                      }
-                    });
-                    item.selected = item.selected ? false : true;
-                  }}>
-                  {item.knowledgeList && item.knowledgeList.length > 0 && (
-                    <span
+            <div
+              class={[
+                styles.listSection,
+                !show.value && prepareStore.getTreeList.length <= 0
+                  ? styles.emptySection
+                  : ''
+              ]}>
+              {prepareStore.getTreeList.map((item: any, index: number) => (
+                <div class={styles.treeParent} key={'parent' + index}>
+                  <div
+                    class={[styles.treeItem, styles.parentItem]}
+                    onClick={() => {
+                      prepareStore.getTreeList.forEach((child: any) => {
+                        if (item.id !== child.id) {
+                          child.selected = false;
+                        }
+                      });
+                      item.selected = item.selected ? false : true;
+                    }}>
+                    {item.knowledgeList && item.knowledgeList.length > 0 && (
+                      <span
+                        class={[
+                          styles.arrow,
+                          item.selected ? styles.arrowSelect : ''
+                        ]}></span>
+                    )}
+                    <p
                       class={[
-                        styles.arrow,
-                        item.selected ? styles.arrowSelect : ''
-                      ]}></span>
-                  )}
-                  <p
-                    class={[
-                      styles.title,
-                      item.selected ? styles.titleSelect : ''
-                    ]}>
-                    {item.name}
-                  </p>
-                </div>
+                        styles.title,
+                        item.selected ? styles.titleSelect : ''
+                      ]}>
+                      {item.name}
+                    </p>
+                  </div>
 
-                {item.selected &&
-                  item.knowledgeList &&
-                  item.knowledgeList.map((child: any, j: number) => (
-                    <div
-                      key={'child' + j}
-                      class={[
-                        styles.treeItem,
-                        styles.childItem,
-                        styles.animation,
-                        prepareStore.getSelectKey === child.id
-                          ? styles.childSelect
-                          : ''
-                      ]}
-                      onClick={() => {
-                        prepareStore.setSelectKey(child.id);
-                        prepareStore.setLessonCoursewareId(
-                          child.lessonCoursewareId
-                        );
-                        prepareStore.setLessonCoursewareDetailId(
-                          child.lessonCoursewareDetailId
-                        );
-                      }}>
-                      <span class={styles.childArrow}></span>
-                      <p class={styles.title}>{child.name}</p>
-                    </div>
-                  ))}
-              </div>
-            ))}
+                  {item.selected &&
+                    item.knowledgeList &&
+                    item.knowledgeList.map((child: any, j: number) => (
+                      <div
+                        key={'child' + j}
+                        class={[
+                          styles.treeItem,
+                          styles.childItem,
+                          styles.animation,
+                          prepareStore.getSelectKey === child.id
+                            ? styles.childSelect
+                            : ''
+                        ]}
+                        onClick={() => {
+                          prepareStore.setSelectKey(child.id);
+                          prepareStore.setLessonCoursewareId(
+                            child.lessonCoursewareId
+                          );
+                          prepareStore.setLessonCoursewareDetailId(
+                            child.lessonCoursewareDetailId
+                          );
+                        }}>
+                        <span class={styles.childArrow}></span>
+                        <p class={styles.title}>{child.name}</p>
+                      </div>
+                    ))}
+                </div>
+              ))}
+            </div>
+            {!show.value && prepareStore.getTreeList.length <= 0 && (
+              <TheEmpty />
+            )}
           </NSpin>
-          {!show.value && prepareStore.getTreeList.length <= 0 && <TheEmpty />}
         </NScrollbar>
 
         {/* 选择教材 */}

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

@@ -5,6 +5,11 @@
   padding-left: 22px !important;
   padding-right: 22px !important;
 
+  .tips {
+    color: #0378EC;
+    font-size: 16px;
+    line-height: 38px;
+  }
 
   :global {
     .n-base-selection {
@@ -34,6 +39,21 @@
         border: 1px solid #198CFE;
       }
     }
+
+    .n-button--error-type {
+      background: #FDEBED !important;
+      color: #EC3A4E !important;
+
+      &:not(.n-button--disabled):hover,
+      &:not(.n-button--disabled):active {
+        background: #FDEBED;
+        color: #EC3A4E;
+      }
+
+      .n-button__border {
+        border: 1px solid #EC3A4E;
+      }
+    }
   }
 }
 
@@ -41,6 +61,15 @@
   margin-top: 12px;
   // // 52 + 28 + 38
   max-height: calc(var(--window-page-lesson-height) - 148px);
+
+  .listSection {
+    min-height: calc(var(--window-page-lesson-height) - 148px);
+  }
+
+  .emptySection {
+    display: flex;
+    align-items: center;
+  }
 }
 
 .list {
@@ -55,4 +84,27 @@
   width: 800px;
   border-radius: 16px;
   overflow: hidden;
+}
+
+.itemBlock {
+  position: relative;
+
+  .itemOperation {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    text-align: right;
+    z-index: 2;
+    cursor: move;
+  }
+
+  .iconDelete {
+    width: 27px;
+    height: 27px;
+    margin-top: 8px;
+    margin-right: 8px;
+    cursor: pointer;
+  }
 }

+ 192 - 26
src/views/prepare-lessons/components/lesson-main/courseware/index.tsx

@@ -1,21 +1,38 @@
 import { defineComponent, onMounted, reactive, watch } from 'vue';
 import styles from './index.module.less';
-import { NButton, NModal, NScrollbar, NSelect, NSpace, NSpin } from 'naive-ui';
+import {
+  NButton,
+  NModal,
+  NScrollbar,
+  NSelect,
+  NSpace,
+  NSpin,
+  useMessage,
+  useDialog
+} from 'naive-ui';
 import CardType from '/src/components/card-type';
 import AttendClass from '/src/views/prepare-lessons/model/attend-class';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
 import { useCatchStore } from '/src/store/modules/catchData';
 import TheEmpty from '/src/components/TheEmpty';
-import { queryCourseware } from '../../../api';
+import { queryCourseware, saveCourseware } from '../../../api';
+import Draggable from 'vuedraggable';
+import iconDelete from '../../../images/icon-delete.png';
+import { useRouter } from 'vue-router';
 
 export default defineComponent({
   name: 'courseware-modal',
   setup() {
     const catchStore = useCatchStore();
     const prepareStore = usePrepareStore();
+    const router = useRouter();
+    const dialog = useDialog();
+    const message = useMessage();
     const forms = reactive({
+      coursewareList: [] as any,
       loadingStatus: false,
-      showAttendClass: false
+      showAttendClass: false,
+      drag: false
     });
 
     // 获取列表
@@ -35,6 +52,7 @@ export default defineComponent({
         tempRows.forEach((row: any) => {
           temp.push({
             id: row.id,
+            materialId: row.materialId,
             coverImg: row.coverImg,
             type: row.materialType,
             title: row.materialName,
@@ -44,6 +62,7 @@ export default defineComponent({
           });
         });
 
+        forms.coursewareList = temp || [];
         prepareStore.setCoursewareList(temp || []);
       } catch {
         //
@@ -58,6 +77,75 @@ export default defineComponent({
         getList();
       }
     );
+    watch(
+      () => prepareStore.getIsAddResource,
+      (val: boolean) => {
+        if (val) {
+          getList();
+          prepareStore.setIsAddResource(false);
+        }
+      }
+    );
+
+    // 删除
+    const onDelete = (item: any) => {
+      //
+      const index = forms.coursewareList.findIndex(
+        (c: any) => c.id === item.id
+      );
+      forms.coursewareList.splice(index, 1);
+      prepareStore.setCoursewareList(forms.coursewareList);
+    };
+
+    // 完成编辑
+    const onOverEdit = async () => {
+      dialog.warning({
+        title: '提示',
+        content: `是否完成编辑?`,
+        positiveText: '确定',
+        negativeText: '取消',
+        onPositiveClick: async () => {
+          try {
+            const temp: any = [];
+            forms.coursewareList.forEach((item: any) => {
+              temp.push({
+                materialName: item.title,
+                materialType: item.type,
+                materialId: item.id
+              });
+            });
+            // 保存课件
+            await saveCourseware({
+              coursewareDetailKnowledgeId: prepareStore.getSelectKey,
+              lessonCoursewareId: prepareStore.getLessonCoursewareId,
+              lessonCoursewareDetailId:
+                prepareStore.getLessonCoursewareDetailId,
+              materialList: [...temp]
+            });
+
+            forms.drag = false;
+            message.success('编辑成功');
+
+            prepareStore.setCoursewareList(forms.coursewareList);
+          } catch {
+            //
+          }
+        }
+      });
+    };
+
+    // 预览上课
+    const onPreviewAttend = () => {
+      const { href } = router.resolve({
+        path: '/attend-class',
+        query: {
+          type: 'preview',
+          subjectId: prepareStore.getSubjectId,
+          detailId: prepareStore.getSelectKey
+        }
+      });
+      window.open(href, +new Date() + '');
+    };
 
     onMounted(async () => {
       // 获取教材分类列表
@@ -70,28 +158,48 @@ export default defineComponent({
 
       await getList();
     });
+
     return () => (
       <div class={styles.coursewareModal}>
         <div class={styles.btnGroup}>
-          <NSpace>
-            <NSelect
-              class={styles.selectSubject}
-              placeholder="选择声部"
-              options={catchStore.getSubjectList}
-              clearable
-              labelField="name"
-              valueField="id"
-              value={prepareStore.getSubjectId}
-              onUpdate:value={(val: any) => {
-                prepareStore.setSubjectId(val);
-                getList();
-              }}
-            />
-            <NButton type="default">编辑</NButton>
-          </NSpace>
+          {forms.drag ? (
+            <NSpace>
+              <NButton type="default" onClick={onOverEdit}>
+                完成编辑
+              </NButton>
+              <NButton
+                type="error"
+                onClick={() => {
+                  forms.coursewareList = [];
+                  prepareStore.setCoursewareList([]);
+                }}>
+                清空资源
+              </NButton>
+              <span class={styles.tips}>拖动可将资源进行排序</span>
+            </NSpace>
+          ) : (
+            <NSpace>
+              <NSelect
+                placeholder="选择声部"
+                options={catchStore.getSubjectList}
+                labelField="name"
+                valueField="id"
+                value={prepareStore.getSubjectId}
+                onUpdate:value={(val: any) => {
+                  prepareStore.setSubjectId(val);
+                  getList();
+                }}
+              />
+              <NButton type="default" onClick={() => (forms.drag = true)}>
+                编辑
+              </NButton>
+            </NSpace>
+          )}
 
           <NSpace>
-            <NButton type="default">预览</NButton>
+            <NButton type="default" onClick={onPreviewAttend}>
+              预览
+            </NButton>
             <NButton
               type="primary"
               onClick={() => (forms.showAttendClass = true)}>
@@ -102,13 +210,71 @@ export default defineComponent({
 
         <NScrollbar class={styles.listContainer}>
           <NSpin show={forms.loadingStatus}>
-            <div class={styles.list}>
-              {prepareStore.getCoursewareList.map((item: any) => (
-                <CardType isShowCollect={false} item={item} />
-              ))}
+            <div
+              class={[
+                styles.listSection,
+                !forms.loadingStatus &&
+                prepareStore.getCoursewareList.length <= 0
+                  ? styles.emptySection
+                  : ''
+              ]}>
+              {forms.coursewareList.length > 0 && (
+                <>
+                  {forms.drag ? (
+                    <Draggable
+                      v-model:modelValue={forms.coursewareList}
+                      itemKey="id"
+                      componentData={{
+                        animation: 200,
+                        group: 'description'
+                      }}
+                      class={styles.list}>
+                      {{
+                        item: (element: any) => {
+                          const item = element.element;
+                          return (
+                            <div
+                              data-id={item.id}
+                              class={[styles.itemBlock, 'row-nav']}>
+                              <CardType
+                                class={[styles.itemContent, 'handle']}
+                                isShowCollect={false}
+                                item={item}
+                              />
+                              <div class={styles.itemOperation}>
+                                <img
+                                  src={iconDelete}
+                                  class={styles.iconDelete}
+                                  onClick={(e: MouseEvent) => {
+                                    e.stopPropagation();
+                                    onDelete(item);
+                                  }}
+                                />
+                              </div>
+                            </div>
+                          );
+                        }
+                      }}
+                    </Draggable>
+                  ) : (
+                    <div class={styles.list}>
+                      {forms.coursewareList.map((item: any) => (
+                        <CardType
+                          class={[styles.itemContent, 'handle']}
+                          isShowCollect={false}
+                          item={item}
+                        />
+                      ))}
+                    </div>
+                  )}
+                </>
+              )}
+
+              {!forms.loadingStatus &&
+                prepareStore.getCoursewareList.length <= 0 && (
+                  <TheEmpty description="暂无课件" />
+                )}
             </div>
-            {!forms.loadingStatus &&
-              prepareStore.getCoursewareList.length <= 0 && <TheEmpty />}
           </NSpin>
         </NScrollbar>
 

+ 168 - 0
src/views/prepare-lessons/components/lesson-main/train/assign-homework.tsx

@@ -0,0 +1,168 @@
+import { defineComponent, reactive, ref } from 'vue';
+import styles from './index.module.less';
+import {
+  NButton,
+  NDatePicker,
+  NForm,
+  NFormItem,
+  NSelect,
+  NSpace,
+  useMessage
+} from 'naive-ui';
+import { BOOK_DATA } from '/src/views/natural-resources/model/add-teaching';
+import { classGroupPage, lessonTrainingAdd } from '../../../api';
+import dayjs from 'dayjs';
+
+export default defineComponent({
+  name: 'assign-homework',
+  props: {
+    /** 初始数据 */
+    trainList: {
+      type: Array,
+      default: () => []
+    }
+  },
+  emits: ['close', 'confirm'],
+  setup(props, { emit }) {
+    const message = useMessage();
+
+    const forms = reactive({
+      currentTime: dayjs(dayjs().format('YYYY-MM-DD')).valueOf(),
+      id: null as any,
+      uploading: false,
+      classList: [] as any,
+      currentGradeNum: null,
+      expireDate: dayjs().add(7, 'day').format('YYYY-MM-DD') as any, // 默认7天
+      classGroupId: null as any
+    });
+    const formsRef = ref();
+
+    const getClassList = async () => {
+      try {
+        const { data } = await classGroupPage({
+          currentGradeNum: forms.currentGradeNum,
+          page: 1,
+          rows: 99
+        });
+        console.log(data);
+        const temp = data.rows || [];
+        temp.forEach((row: any) => {
+          forms.classList.push({
+            label: row.currentClass + '班',
+            value: row.id
+          });
+        });
+      } catch {
+        //
+      }
+    };
+    const onSubmit = async () => {
+      formsRef.value?.validate(async (err: any) => {
+        if (err) {
+          return;
+        }
+        forms.uploading = true;
+        try {
+          const trainList = props.trainList || [];
+          const details: any[] = [];
+          trainList.forEach((item: any) => {
+            details.push({
+              trainingType: item.trainingType,
+              musicId: item.musicId,
+              trainingConfigJsonObject: item.trainingConfigJson
+            });
+          });
+          const params = {
+            lessonTrainingDetails: details,
+            expireDate: forms.expireDate + ' 23:59:59',
+            classGroupId: forms.classGroupId
+          };
+          await lessonTrainingAdd(params);
+          message.success('布置成功');
+          emit('close');
+        } catch {
+          //
+        }
+        forms.uploading = false;
+      });
+    };
+
+    return () => (
+      <div class={styles.assignHomeworkContainer}>
+        <NForm
+          ref={formsRef}
+          model={forms}
+          labelAlign="right"
+          labelWidth={'auto'}
+          labelPlacement="left">
+          <NFormItem
+            label="年级"
+            path="currentGradeNum"
+            rule={[
+              {
+                required: true,
+                message: '请选择年级',
+                trigger: 'change',
+                type: 'number'
+              }
+            ]}>
+            <NSelect
+              v-model:value={forms.currentGradeNum}
+              placeholder="请选择年级"
+              options={BOOK_DATA.grades}
+              clearable
+              onUpdate:value={() => {
+                getClassList();
+              }}
+            />
+          </NFormItem>
+          <NFormItem
+            label="班级"
+            path="classGroupId"
+            rule={[
+              { required: true, message: '请选择班级', trigger: 'change' }
+            ]}>
+            <NSelect
+              v-model:value={forms.classGroupId}
+              placeholder="请选择班级"
+              clearable
+              options={forms.classList}
+            />
+          </NFormItem>
+          <NFormItem
+            label="截止日期"
+            path="expireDate"
+            rule={[
+              { required: true, message: '请选择截止日期', trigger: 'change' }
+            ]}>
+            <NDatePicker
+              v-model:formatted-value={forms.expireDate}
+              type="date"
+              clearable
+              valueFormat="yyyy-MM-dd"
+              style={{ width: '100%' }}
+              isDateDisabled={(ts: number) => {
+                return ts < forms.currentTime;
+              }}
+            />
+          </NFormItem>
+
+          <NSpace class={styles.updateBtnGroup}>
+            <NButton strong type="default" round onClick={() => emit('close')}>
+              取消
+            </NButton>
+            <NButton
+              strong
+              type="primary"
+              round
+              disabled={forms.uploading}
+              loading={forms.uploading}
+              onClick={onSubmit}>
+              确认
+            </NButton>
+          </NSpace>
+        </NForm>
+      </div>
+    );
+  }
+});

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

@@ -5,7 +5,20 @@
   padding-left: 22px !important;
   padding-right: 22px !important;
 
+  .tips {
+    color: #0378EC;
+    font-size: 16px;
+    line-height: 38px;
+  }
+
   :global {
+    .n-base-selection {
+      --n-height: 38px !important;
+      width: 160px;
+      font-size: 15px;
+      border-radius: 8px !important;
+    }
+
     .n-button {
       border-radius: 8px;
       height: 38px;
@@ -26,6 +39,21 @@
         border: 1px solid #198CFE;
       }
     }
+
+    .n-button--error-type {
+      background: #FDEBED !important;
+      color: #EC3A4E !important;
+
+      &:not(.n-button--disabled):hover,
+      &:not(.n-button--disabled):active {
+        background: #FDEBED;
+        color: #EC3A4E;
+      }
+
+      .n-button__border {
+        border: 1px solid #EC3A4E;
+      }
+    }
   }
 }
 
@@ -33,6 +61,15 @@
   margin-top: 12px;
   // // 52 + 28 + 38
   max-height: calc(var(--window-page-lesson-height) - 148px);
+
+  .listSection {
+    min-height: calc(var(--window-page-lesson-height) - 148px);
+  }
+
+  .emptySection {
+    display: flex;
+    align-items: center;
+  }
 }
 
 .list {
@@ -41,4 +78,29 @@
   flex-flow: row wrap;
   justify-content: flex-start;
   gap: 20px;
+}
+
+.trainEditModal {
+  width: 580px;
+}
+
+.assignHomework {
+  width: 520px;
+}
+
+.assignHomeworkContainer {
+  padding: 24px 30px;
+
+
+  .updateBtnGroup {
+    padding: 0;
+    justify-content: center !important;
+
+    :global {
+      .n-button {
+        height: 48px !important;
+        min-width: 156px;
+      }
+    }
+  }
 }

+ 295 - 14
src/views/prepare-lessons/components/lesson-main/train/index.tsx

@@ -1,35 +1,316 @@
-import { defineComponent, reactive } from 'vue';
+import { defineComponent, onMounted, reactive, watch } from 'vue';
 import styles from './index.module.less';
-import { NButton, NScrollbar, NSpace } from 'naive-ui';
-import CardType from '/src/components/card-type';
+import {
+  NButton,
+  NModal,
+  NScrollbar,
+  NSelect,
+  NSpace,
+  NSpin,
+  useDialog,
+  useMessage
+} from 'naive-ui';
+import { usePrepareStore } from '/src/store/modules/prepareLessons';
+import { useCatchStore } from '/src/store/modules/catchData';
+import TrainType from '/src/views/attend-class/model/train-type';
+import TheEmpty from '/src/components/TheEmpty';
+import Draggable from 'vuedraggable';
+import {
+  lessonPreTrainingBatchSave,
+  lessonPreTrainingPage
+} from '../../../api';
+import { evaluateDifficult } from '/src/utils/contants';
+import TrainUpdate from '/src/views/attend-class/model/train-update';
+import AssignHomework from './assign-homework';
 
 export default defineComponent({
   name: 'courseware-modal',
   setup() {
+    const catchStore = useCatchStore();
+    const prepareStore = usePrepareStore();
+    const dialog = useDialog();
+    const message = useMessage();
     const forms = reactive({
       showAttendClass: false,
-      list: [] as any
+      list: [] as any,
+      drag: false,
+      loadingStatus: false,
+      trainList: [] as any,
+      assignHomeworkStatus: false,
+      editStatus: false,
+      editItem: {} as any
+    });
+
+    // 完成编辑
+    const onOverEdit = async () => {
+      dialog.warning({
+        title: '提示',
+        content: `是否完成编辑?`,
+        positiveText: '确定',
+        negativeText: '取消',
+        onPositiveClick: async () => {
+          try {
+            // 保存课件
+            await lessonPreTrainingBatchSave(forms.trainList);
+
+            forms.drag = false;
+            message.success('编辑成功');
+
+            prepareStore.setCoursewareList(forms.trainList);
+          } catch {
+            //
+          }
+        }
+      });
+    };
+
+    // 获取列表
+    const getList = async () => {
+      forms.loadingStatus = true;
+      try {
+        // 判断是否有选择对应的课件
+        if (!prepareStore.getSelectKey) return;
+        const { data } = await lessonPreTrainingPage({
+          coursewareKnowledgeDetailId: prepareStore.getSelectKey,
+          subjectId: prepareStore.getSubjectId,
+          pag: 1,
+          rows: 99
+        });
+        const tempRows = data.rows || [];
+        const temp: any = [];
+
+        tempRows.forEach((row: any) => {
+          let tList: string[] = [];
+          const configJson = row.trainingConfigJson;
+          if (row.trainingType === 'EVALUATION') {
+            tList = [
+              `${evaluateDifficult[configJson.evaluateDifficult]}`,
+              '全部小节',
+              `速度${configJson.evaluateSpeed}`,
+              `${configJson.trainingTimes}分钟`
+            ];
+          } else {
+            tList = [
+              `${configJson.practiceChapterBegin}-${configJson.practiceChapterEnd}小节`,
+              `速度${configJson.practiceSpeed}`,
+              `${configJson.trainingTimes}分钟`
+            ];
+          }
+          temp.push({
+            typeList: tList || [],
+            ...row
+          });
+        });
+
+        forms.trainList = temp || [];
+        prepareStore.setTrainList(temp || []);
+      } catch {
+        //
+      }
+      forms.loadingStatus = false;
+    };
+
+    // 监听选择的key 左侧选择了其它的课
+    watch(
+      () => prepareStore.getSelectKey,
+      () => {
+        forms.trainList = [];
+        getList();
+      }
+    );
+    watch(
+      () => prepareStore.getIsAddTrain,
+      (val: boolean) => {
+        if (val) {
+          forms.trainList = [];
+          getList();
+          prepareStore.setIsAddTrain(false);
+        }
+      }
+    );
+
+    // 删除
+    const onDelete = (item: any) => {
+      //
+      const index = forms.trainList.findIndex((c: any) => c.id === item.id);
+      forms.trainList.splice(index, 1);
+      prepareStore.setCoursewareList(forms.trainList);
+    };
+
+    onMounted(async () => {
+      // 获取教材分类列表
+      await catchStore.getSubjects();
+
+      const subjectList = catchStore.getSubjectList;
+      if (subjectList.length > 0) {
+        prepareStore.setSubjectId(subjectList[0].id);
+      }
+
+      await getList();
     });
     return () => (
       <div class={styles.coursewareModal}>
         <div class={styles.btnGroup}>
-          <NSpace>
-            <NButton type="default">添加训练</NButton>
-            <NButton type="default">编辑</NButton>
-          </NSpace>
+          {forms.drag ? (
+            <NSpace>
+              <NButton type="default" onClick={onOverEdit}>
+                完成编辑
+              </NButton>
+              <NButton
+                type="error"
+                onClick={() => {
+                  forms.trainList = [];
+                  prepareStore.setTrainList([]);
+                }}>
+                清空资源
+              </NButton>
+              <span class={styles.tips}>拖动可将资源进行排序</span>
+            </NSpace>
+          ) : (
+            <NSpace>
+              <NSelect
+                placeholder="选择声部"
+                options={catchStore.getSubjectList}
+                labelField="name"
+                valueField="id"
+                value={prepareStore.getSubjectId}
+                onUpdate:value={(val: any) => {
+                  prepareStore.setSubjectId(val);
+                }}
+              />
+              <NButton type="default" onClick={() => (forms.drag = true)}>
+                编辑
+              </NButton>
+            </NSpace>
+          )}
 
           <NSpace>
-            <NButton type="primary">布置训练</NButton>
+            <NButton
+              type="primary"
+              disabled={forms.drag}
+              onClick={() => {
+                if (forms.trainList.length <= 0) {
+                  message.error('训练内容不能为空');
+                  return;
+                }
+                forms.assignHomeworkStatus = true;
+              }}>
+              布置训练
+            </NButton>
           </NSpace>
         </div>
 
         <NScrollbar class={styles.listContainer}>
-          <div class={styles.list}>
-            {forms.list.map((item: any) => (
-              <CardType isShowCollect={false} item={item} />
-            ))}
-          </div>
+          <NSpin show={forms.loadingStatus}>
+            <div
+              class={[
+                styles.listSection,
+                !forms.loadingStatus && prepareStore.getTrainList.length <= 0
+                  ? styles.emptySection
+                  : ''
+              ]}>
+              {forms.trainList.length > 0 && (
+                <>
+                  {forms.drag ? (
+                    <Draggable
+                      v-model:modelValue={forms.trainList}
+                      itemKey="id"
+                      // tag="transition-group"
+                      componentData={{
+                        itemKey: 'id',
+                        tag: 'div',
+                        //   type: 'transition-group',
+                        //   name: !forms.drag ? 'flip-list' : null,
+                        animation: 200,
+                        group: 'description',
+                        disabled: false
+                        // ghostClass: 'ghost',
+                        //   ondragstart: () => {
+                        //     forms.drag = true;
+                        //   },
+                        //   ondragend: () => {
+                        //     forms.drag = false;
+                        //   }
+                      }}
+                      class={styles.list}>
+                      {{
+                        item: (element: any) => {
+                          const item = element.element;
+                          return (
+                            <div data-id={item.id} class={styles.itemBlock}>
+                              <TrainType
+                                item={item}
+                                isDelete
+                                type="prepare"
+                                onDelete={(child: any) => onDelete(child)}
+                              />
+                            </div>
+                          );
+                        }
+                      }}
+                    </Draggable>
+                  ) : (
+                    <div class={styles.list}>
+                      {forms.trainList.map((item: any) => (
+                        <TrainType
+                          item={item}
+                          type="prepare"
+                          onEdit={(child: any) => {
+                            console.log('edit', child);
+                            const { trainingConfigJson, id, musicId, ...res } =
+                              child;
+                            forms.editItem = {
+                              ...res,
+                              id: musicId,
+                              trainId: id,
+                              ...trainingConfigJson
+                            };
+                            forms.editStatus = true;
+                          }}
+                        />
+                      ))}
+                    </div>
+                  )}
+                </>
+              )}
+
+              {!forms.loadingStatus &&
+                prepareStore.getTrainList.length <= 0 && (
+                  <TheEmpty description="暂无训练" />
+                )}
+            </div>
+          </NSpin>
         </NScrollbar>
+
+        {/* 编辑 */}
+        <NModal
+          v-model:show={forms.editStatus}
+          class={['modalTitle background', styles.trainEditModal]}
+          preset="card"
+          title="训练设置">
+          <TrainUpdate
+            item={forms.editItem}
+            onClose={() => (forms.editStatus = false)}
+            onConfirm={() => {
+              forms.editItem = {};
+              prepareStore.setIsAddTrain(true);
+            }}
+          />
+        </NModal>
+
+        {/* 添加自定义教材 */}
+        <NModal
+          v-model:show={forms.assignHomeworkStatus}
+          preset="card"
+          showIcon={false}
+          class={['modalTitle background', styles.assignHomework]}
+          title={'布置作业'}
+          blockScroll={false}>
+          <AssignHomework
+            trainList={forms.trainList}
+            onClose={() => (forms.assignHomeworkStatus = false)}
+          />
+        </NModal>
       </div>
     );
   }

+ 58 - 3
src/views/prepare-lessons/components/resource-main/components/resource-item/index.tsx

@@ -1,12 +1,13 @@
 import { PropType, defineComponent, onMounted, reactive, watch } from 'vue';
 import ResourceSearchGroup from './resource-search-group';
-import { NScrollbar, NSpin } from 'naive-ui';
+import { NScrollbar, NSpin, useDialog, useMessage } from 'naive-ui';
 import styles from './index.module.less';
 import CardType from '/src/components/card-type';
 import { materialQueryPage } from '/src/views/natural-resources/api';
 import TheEmpty from '/src/components/TheEmpty';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
 import { useThrottleFn } from '@vueuse/core';
+import { saveCourseware } from '/src/views/prepare-lessons/api';
 
 const formatType = (type: string) => {
   if (type === 'shareResources') {
@@ -27,6 +28,8 @@ export default defineComponent({
   },
   setup(props) {
     const prepareStore = usePrepareStore();
+    const message = useMessage();
+    const dialog = useDialog();
     const state = reactive({
       loading: false,
       finshed: false, // 是否加载完
@@ -97,12 +100,60 @@ export default defineComponent({
       getList();
     }, 500);
 
+    // 添加资源
+    const onAdd = async (item: any) => {
+      dialog.warning({
+        title: '提示',
+        content: `是否添加"${item.title}"资源?`,
+        positiveText: '确定',
+        negativeText: '取消',
+        onPositiveClick: async () => {
+          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
+              });
+            });
+
+            // 保存课件
+            await saveCourseware({
+              coursewareDetailKnowledgeId: prepareStore.getSelectKey,
+              lessonCoursewareId: prepareStore.getLessonCoursewareId,
+              lessonCoursewareDetailId:
+                prepareStore.getLessonCoursewareDetailId,
+              materialList: [
+                ...temp,
+                {
+                  materialName: item.title,
+                  materialType: item.type,
+                  materialId: item.id
+                }
+              ]
+            });
+
+            message.success('添加成功');
+            prepareStore.setIsAddResource(true);
+          } catch {
+            //
+          }
+        }
+      });
+    };
+
     onMounted(() => {
       getList();
     });
     return () => (
       <div>
-        <ResourceSearchGroup onSearch={(item: any) => onSearch(item)} />
+        <ResourceSearchGroup
+          type={props.type}
+          onSearch={(item: any) => onSearch(item)}
+        />
         <NScrollbar
           class={styles.listContainer}
           onScroll={(e: any) => {
@@ -129,7 +180,11 @@ export default defineComponent({
               {state.tableList.length > 0 && (
                 <div class={styles.list}>
                   {state.tableList.map((item: any) => (
-                    <CardType isShowAdd item={item} />
+                    <CardType
+                      isShowAdd
+                      item={item}
+                      onAdd={(item: any) => onAdd(item)}
+                    />
                   ))}
                 </div>
               )}

+ 1 - 1
src/views/prepare-lessons/components/resource-main/components/resource-item/resource-search-group/index.tsx

@@ -51,7 +51,7 @@ export default defineComponent({
       <>
         <div class={styles.searchGroup}>
           <NSpace size="small" class={styles.btnType}>
-            {resourceTypeArray.map((item: any) => (
+            {resourceType.value.map((item: any) => (
               <NButton
                 type={forms.type === item.value ? 'primary' : 'default'}
                 secondary={forms.type === item.value ? false : true}

+ 4 - 0
src/views/prepare-lessons/components/resource-main/components/select-music/index.module.less

@@ -24,4 +24,8 @@
       }
     }
   }
+}
+
+.trainEditModal {
+  width: 580px;
 }

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

@@ -1,16 +1,20 @@
 import { defineComponent, onMounted, reactive, watch } from 'vue';
 import ResourceSearchGroup from './resource-search-group';
-import { NScrollbar, NSpin } from 'naive-ui';
+import { NModal, NScrollbar, NSpin, useDialog, useMessage } from 'naive-ui';
 import styles from './index.module.less';
 import CardType from '/src/components/card-type';
-import { materialQueryPage } from '/src/views/natural-resources/api';
 import TheEmpty from '/src/components/TheEmpty';
 import { useThrottleFn } from '@vueuse/core';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
+import { musicSheetPage } from '/src/views/prepare-lessons/api';
+import TrainUpdate from '/src/views/attend-class/model/train-update';
+import requestOrigin from 'umi-request';
 export default defineComponent({
   name: 'share-resources',
   setup() {
     const prepareStore = usePrepareStore();
+    const dialog = useDialog();
+    const message = useMessage();
     const state = reactive({
       loading: false,
       finshed: false, // 是否加载完
@@ -19,14 +23,15 @@ export default defineComponent({
         rows: 20
       },
       searchGroup: {
-        type: 'MUSIC', //
         keyword: '',
-        bookVersionId: null,
-        subjectId: null,
-        sourceType: 2,
-        enableFlag: true
+        musicSheetCategoriesId: '',
+        status: 1,
+        versionFlag: false,
+        subjectId: null
       },
-      tableList: [] as any
+      tableList: [] as any,
+      editStatus: false,
+      editItem: {} as any
     });
     const getList = async () => {
       try {
@@ -34,7 +39,7 @@ export default defineComponent({
         if (state.pagination.page === 1) {
           state.loading = true;
         }
-        const { data } = await materialQueryPage({
+        const { data } = await musicSheetPage({
           ...state.searchGroup,
           ...state.pagination,
           subjectId: prepareStore.getSubjectId
@@ -45,12 +50,13 @@ export default defineComponent({
         tempRows.forEach((row: any) => {
           temp.push({
             id: row.id,
-            coverImg: row.coverImg,
-            type: row.type,
-            title: row.name,
-            isCollect: !!row.favoriteFlag,
-            isSelected: row.sourceFrom === 'PLATFORM' ? true : false,
-            content: row.content
+            coverImg: row.titleImg,
+            type: 'MUSIC',
+            title: row.musicSheetName,
+            isCollect: false,
+            isSelected: true,
+            content: row.id,
+            xmlFileUrl: row.xmlFileUrl
           });
         });
         state.tableList.push(...temp);
@@ -81,6 +87,37 @@ export default defineComponent({
       getList();
     }, 500);
 
+    // 添加资源
+    const onAdd = async (item: any) => {
+      let xmlStatus = 'init';
+      // 第一个声部小节
+      let firstMeasures: any = null;
+      try {
+        // 获取文件
+        const res = await requestOrigin.get(item.xmlFileUrl, {
+          mode: 'cors'
+        });
+        const xmlParse = new DOMParser().parseFromString(res, 'text/xml');
+        const parts = xmlParse.getElementsByTagName('part');
+        firstMeasures = parts[0]?.getElementsByTagName('measure');
+        xmlStatus = 'success';
+      } catch (error) {
+        xmlStatus = 'error';
+      }
+
+      // 判断读取小节数
+      if (xmlStatus == 'success') {
+        item.practiceChapterMax = firstMeasures.length;
+      } else {
+        item.practiceChapterMax = 0;
+      }
+      item.coursewareKnowledgeDetailId = prepareStore.getSelectKey;
+      item.subjectId = prepareStore.getSubjectId;
+
+      state.editItem = item;
+      state.editStatus = true;
+    };
+
     onMounted(() => {
       getList();
     });
@@ -113,7 +150,11 @@ export default defineComponent({
               {state.tableList.length > 0 && (
                 <div class={styles.list}>
                   {state.tableList.map((item: any) => (
-                    <CardType isShowAdd item={item} />
+                    <CardType
+                      isShowAdd
+                      item={item}
+                      onAdd={(child: any) => onAdd(child)}
+                    />
                   ))}
                 </div>
               )}
@@ -121,6 +162,21 @@ export default defineComponent({
             </div>
           </NSpin>
         </NScrollbar>
+
+        <NModal
+          v-model:show={state.editStatus}
+          class={['modalTitle background', styles.trainEditModal]}
+          preset="card"
+          title="训练设置">
+          <TrainUpdate
+            item={state.editItem}
+            onClose={() => (state.editStatus = false)}
+            onConfirm={() => {
+              state.editItem = {};
+              prepareStore.setIsAddTrain(true);
+            }}
+          />
+        </NModal>
       </div>
     );
   }

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

@@ -10,10 +10,8 @@ export default defineComponent({
   setup(props, { emit }) {
     const catchStore = useCatchStore();
     const forms = reactive({
-      type: 'MUSIC', //
       keyword: '',
-      bookVersionId: null,
-      subjectId: null
+      musicSheetCategoriesId: null
     });
 
     const onSearch = () => {
@@ -38,7 +36,7 @@ export default defineComponent({
               clearable
               labelField="name"
               valueField="id"
-              v-model:value={forms.bookVersionId}
+              v-model:value={forms.musicSheetCategoriesId}
               onUpdate:value={() => {
                 onSearch();
               }}

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

@@ -67,4 +67,8 @@
       }
     }
   }
+}
+
+.trainEditModal {
+  width: 580px;
 }

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

@@ -6,6 +6,8 @@ import { usePrepareStore } from '/src/store/modules/prepareLessons';
 import SelectResources from '../../model/select-resources';
 import SelectMusic from './components/select-music';
 import ResourceItem from './components/resource-item';
+import TrainUpdate from '/src/views/attend-class/model/train-update';
+import requestOrigin from 'umi-request';
 
 export default defineComponent({
   name: 'resource-main',
@@ -14,8 +16,41 @@ export default defineComponent({
     const forms = reactive({
       tabType: 'shareResources',
       selectMusicStatus: false,
-      selectResourceStatus: false
+      selectResourceStatus: false,
+      editStatus: false,
+      editItem: {} as any
     });
+
+    const onAdd = async (item: any) => {
+      let xmlStatus = 'init';
+      // 第一个声部小节
+      let firstMeasures: any = null;
+      try {
+        // 获取文件
+        const res = await requestOrigin.get(item.xmlFileUrl, {
+          mode: 'cors'
+        });
+        const xmlParse = new DOMParser().parseFromString(res, 'text/xml');
+        const parts = xmlParse.getElementsByTagName('part');
+        firstMeasures = parts[0]?.getElementsByTagName('measure');
+        xmlStatus = 'success';
+      } catch (error) {
+        xmlStatus = 'error';
+      }
+
+      // 判断读取小节数
+      if (xmlStatus == 'success') {
+        item.practiceChapterMax = firstMeasures.length;
+      } else {
+        item.practiceChapterMax = 0;
+      }
+      item.coursewareKnowledgeDetailId = prepareStore.getSelectKey;
+      item.subjectId = prepareStore.getSubjectId;
+
+      forms.editItem = item;
+      forms.editStatus = true;
+    };
+
     return () => (
       <div
         class={[
@@ -106,31 +141,44 @@ export default defineComponent({
             }}
           </NTabs>
         )}
-
         <NModal
-          v-model:show={forms.selectMusicStatus}
+          v-model:show={forms.selectResourceStatus}
           onUpdate:show={(val: any) => {
             if (!val) {
-              prepareStore.setSelectMusicStatus(val);
+              prepareStore.setSelectResourceStatus(val);
             }
           }}
           class={['modalTitle', styles.selectMusicModal]}
           preset="card"
-          title={'选择曲目'}>
-          <SelectMusicModal />
+          title={'选择资源'}>
+          <SelectResources />
         </NModal>
 
         <NModal
-          v-model:show={forms.selectResourceStatus}
+          v-model:show={forms.selectMusicStatus}
           onUpdate:show={(val: any) => {
             if (!val) {
-              prepareStore.setSelectResourceStatus(val);
+              prepareStore.setSelectMusicStatus(val);
             }
           }}
           class={['modalTitle', styles.selectMusicModal]}
           preset="card"
-          title={'选择资源'}>
-          <SelectResources />
+          title={'选择曲目'}>
+          <SelectMusicModal onAdd={(item: any) => onAdd(item)} />
+        </NModal>
+        <NModal
+          v-model:show={forms.editStatus}
+          class={['modalTitle background', styles.trainEditModal]}
+          preset="card"
+          title="训练设置">
+          <TrainUpdate
+            item={forms.editItem}
+            onClose={() => (forms.editStatus = false)}
+            onConfirm={() => {
+              forms.editItem = {};
+              prepareStore.setIsAddTrain(true);
+            }}
+          />
         </NModal>
       </div>
     );

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


+ 23 - 58
src/views/prepare-lessons/model/attend-class/index.tsx

@@ -1,8 +1,14 @@
-import { defineComponent } from 'vue';
+import { defineComponent, onMounted } from 'vue';
 import styles from './index.module.less';
 import { NInput, NScrollbar, NSelect, NThing } from 'naive-ui';
-import iconSearch from '../../images/icon-search.png';
 import { useRouter } from 'vue-router';
+import { BOOK_DATA } from '/src/views/natural-resources/model/add-teaching';
+import { classGroupPage } from '../../api';
+
+const classList: any = [];
+for (let i = 1; i <= 40; i++) {
+  classList.push({ label: i + '班', value: i });
+}
 
 export default defineComponent({
   name: 'attend-class',
@@ -17,6 +23,19 @@ export default defineComponent({
       });
       window.open(href, +new Date() + '');
     };
+
+    const getList = async () => {
+      try {
+        await classGroupPage({});
+      } catch {
+        //
+      }
+    };
+
+    onMounted(() => {
+      getList();
+    });
+
     return () => (
       <div class={styles.attendClass}>
         <div class={styles.attendClassSearch}>
@@ -25,62 +44,8 @@ export default defineComponent({
               prefix: () => <span class="icon-search-input"></span>
             }}
           </NInput>
-          <NSelect
-            placeholder="年级"
-            clearable
-            options={[
-              {
-                label: '一年级',
-                value: '1'
-              },
-              {
-                label: '二年级',
-                value: '2'
-              },
-              {
-                label: '三年级',
-                value: '3'
-              },
-              {
-                label: '四年级',
-                value: '4'
-              },
-              {
-                label: '五年级',
-                value: '5'
-              },
-              {
-                label: '六年级',
-                value: '6'
-              }
-            ]}
-          />
-          <NSelect
-            placeholder="班级"
-            clearable
-            options={[
-              {
-                label: '一班',
-                value: '1'
-              },
-              {
-                label: '二班',
-                value: '2'
-              },
-              {
-                label: '三班',
-                value: '3'
-              },
-              {
-                label: '四班',
-                value: '4'
-              },
-              {
-                label: '五班',
-                value: '5'
-              }
-            ]}
-          />
+          <NSelect placeholder="年级" clearable options={BOOK_DATA.grades} />
+          <NSelect placeholder="班级" clearable options={classList} />
         </div>
         <NScrollbar class={styles.classList}>
           {[1, 2, 3, 4, 5, 6, 7].map(i => (

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

@@ -3,14 +3,14 @@ import { defineComponent, onMounted, reactive } from 'vue';
 import styles from './index.module.less';
 import CardType from '@/components/card-type';
 import SearchGroup from './search-group';
-import { materialQueryPage } from '/src/views/natural-resources/api';
 import TheEmpty from '/src/components/TheEmpty';
 import { useThrottleFn } from '@vueuse/core';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
+import { musicSheetPage } from '../../api';
 
 export default defineComponent({
   name: 'select-music',
-  emits: ['select'],
+  emits: ['select', 'add'],
   setup(props, { emit }) {
     const prepareStore = usePrepareStore();
     const state = reactive({
@@ -21,11 +21,11 @@ export default defineComponent({
         rows: 20
       },
       searchGroup: {
-        type: 'MUSIC', //
         keyword: '',
-        bookVersionId: null,
-        subjectId: null,
-        sourceType: 2
+        musicSheetCategoriesId: '',
+        status: 1,
+        versionFlag: false,
+        subjectId: null
       },
       tableList: [] as any
     });
@@ -34,7 +34,7 @@ export default defineComponent({
         if (state.pagination.page === 1) {
           state.loading = true;
         }
-        const { data } = await materialQueryPage({
+        const { data } = await musicSheetPage({
           ...state.searchGroup,
           ...state.pagination,
           subjectId: prepareStore.getSubjectId
@@ -45,12 +45,13 @@ export default defineComponent({
         tempRows.forEach((row: any) => {
           temp.push({
             id: row.id,
-            coverImg: row.coverImg,
-            type: row.type,
-            title: row.name,
-            isCollect: !!row.favoriteFlag,
-            isSelected: row.sourceFrom === 'PLATFORM' ? true : false,
-            content: row.content
+            coverImg: row.titleImg,
+            type: 'MUSIC',
+            title: row.musicSheetName,
+            isCollect: false,
+            isSelected: true,
+            content: row.id,
+            xmlFileUrl: row.xmlFileUrl
           });
         });
         state.tableList.push(...temp);
@@ -111,7 +112,11 @@ export default defineComponent({
                   {state.tableList.length > 0 && (
                     <div class={styles.list}>
                       {state.tableList.map((item: any) => (
-                        <CardType isShowAdd item={item} />
+                        <CardType
+                          isShowAdd
+                          item={item}
+                          onAdd={() => emit('add', item)}
+                        />
                       ))}
                     </div>
                   )}

+ 12 - 33
src/views/prepare-lessons/model/select-music/search-group.tsx

@@ -11,9 +11,8 @@ export default defineComponent({
   setup(props, { emit }) {
     const catchStore = useCatchStore();
     const forms = reactive({
-      type: 'MUSIC', //
       keyword: '',
-      bookVersionId: null
+      musicSheetCategoriesId: null
     });
 
     const onSearch = () => {
@@ -33,48 +32,28 @@ export default defineComponent({
     return () => (
       <div class={styles.searchGroup}>
         <NForm labelAlign="left" labelPlacement="left">
-          {forms.type === 'MUSIC' && (
-            <NFormItem label="教材:">
-              <NSpace class={styles.spaceSection}>
-                {catchStore.getAllMusicCategories.map((music: any) => (
-                  <NButton
-                    secondary={forms.bookVersionId === music.id}
-                    quaternary={forms.bookVersionId !== music.id}
-                    strong
-                    focusable={false}
-                    type={
-                      forms.bookVersionId === music.id ? 'primary' : 'default'
-                    }
-                    onClick={() => {
-                      forms.bookVersionId = music.id;
-                      throttledFn();
-                    }}>
-                    {music.name}
-                  </NButton>
-                ))}
-              </NSpace>
-            </NFormItem>
-          )}
-
-          <NFormItem label="乐器:">
+          <NFormItem label="教材:">
             <NSpace class={styles.spaceSection}>
-              {catchStore.getSubjectAllList.map((subject: any) => (
+              {catchStore.getAllMusicCategories.map((music: any) => (
                 <NButton
-                  secondary={forms.subjectId === subject.id}
-                  quaternary={forms.subjectId !== subject.id}
+                  secondary={forms.musicSheetCategoriesId === music.id}
+                  quaternary={forms.musicSheetCategoriesId !== music.id}
                   strong
                   focusable={false}
-                  type={forms.subjectId === subject.id ? 'primary' : 'default'}
+                  type={
+                    forms.musicSheetCategoriesId === music.id
+                      ? 'primary'
+                      : 'default'
+                  }
                   onClick={() => {
-                    forms.subjectId = subject.id;
+                    forms.musicSheetCategoriesId = music.id;
                     throttledFn();
                   }}>
-                  {subject.name}
+                  {music.name}
                 </NButton>
               ))}
             </NSpace>
           </NFormItem>
-
           <TheSearch
             class={styles.inputSearch}
             round

+ 54 - 2
src/views/prepare-lessons/model/select-resources/select-item/index.tsx

@@ -1,11 +1,12 @@
 import { PropType, defineComponent, onMounted, reactive } from 'vue';
 import ResourceSearchGroup from './resource-search-group';
-import { NScrollbar, NSpin } from 'naive-ui';
+import { NScrollbar, NSpin, useDialog, useMessage } from 'naive-ui';
 import styles from './index.module.less';
 import CardType from '/src/components/card-type';
 import { materialQueryPage } from '/src/views/natural-resources/api';
 import TheEmpty from '/src/components/TheEmpty';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
+import { saveCourseware } from '../../../api';
 
 const formatType = (type: string) => {
   if (type === 'shareResources') {
@@ -27,6 +28,8 @@ export default defineComponent({
   },
   setup(props) {
     const prepareStore = usePrepareStore();
+    const message = useMessage();
+    const dialog = useDialog();
     const state = reactive({
       loading: false,
       finshed: false, // 是否加载完
@@ -85,6 +88,51 @@ export default defineComponent({
       getList();
     };
 
+    // 添加资源
+    const onAdd = async (item: any) => {
+      dialog.warning({
+        title: '提示',
+        content: `是否添加"${item.title}"资源?`,
+        positiveText: '确定',
+        negativeText: '取消',
+        onPositiveClick: async () => {
+          try {
+            console.log(item, 'any');
+            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
+                }
+              ]
+            });
+
+            message.success('添加成功');
+            prepareStore.setIsAddResource(true);
+          } catch {
+            //
+          }
+        }
+      });
+    };
+
     onMounted(() => {
       getList();
     });
@@ -121,7 +169,11 @@ export default defineComponent({
               {state.tableList.length > 0 && (
                 <div class={styles.list}>
                   {state.tableList.map((item: any) => (
-                    <CardType isShowAdd item={item} />
+                    <CardType
+                      isShowAdd
+                      item={item}
+                      onAdd={(item: any) => onAdd(item)}
+                    />
                   ))}
                 </div>
               )}

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác