瀏覽代碼

修改样式与登录

lex 1 年之前
父節點
當前提交
8423f742d4
共有 33 個文件被更改,包括 2111 次插入517 次删除
  1. 4 2
      src/components/searchInput/index.tsx
  2. 8 0
      src/router/routes/index.ts
  3. 1 1
      src/views/attend-class/index.module.less
  4. 9 9
      src/views/attend-class/index.tsx
  5. 159 0
      src/views/attend-class/model/class-work/index.module.less
  6. 298 0
      src/views/attend-class/model/class-work/index.tsx
  7. 1 46
      src/views/attend-class/model/train-type/index.tsx
  8. 3 2
      src/views/classList/components/afterWork.tsx
  9. 二進制
      src/views/classList/images/icon-menu.png
  10. 二進制
      src/views/classList/images/icon-pen.png
  11. 31 0
      src/views/classList/index.module.less
  12. 94 89
      src/views/classList/modals/TrainingDetails.tsx
  13. 60 57
      src/views/classList/modals/classTrainingDetails.tsx
  14. 18 0
      src/views/homework-record/api.ts
  15. 235 0
      src/views/homework-record/detail/index.module.less
  16. 376 0
      src/views/homework-record/detail/index.tsx
  17. 97 98
      src/views/homework-record/index.tsx
  18. 9 6
      src/views/login/components/pwdLogin.tsx
  19. 1 1
      src/views/prepare-lessons/api.ts
  20. 2 3
      src/views/prepare-lessons/components/lesson-main/train-presets/index.tsx
  21. 84 13
      src/views/prepare-lessons/components/lesson-main/train/assign-homework.tsx
  22. 25 14
      src/views/prepare-lessons/components/lesson-main/train/assign-student/index.module.less
  23. 250 39
      src/views/prepare-lessons/components/lesson-main/train/assign-student/index.tsx
  24. 5 0
      src/views/prepare-lessons/components/lesson-main/train/index.module.less
  25. 30 52
      src/views/prepare-lessons/components/lesson-main/train/index.tsx
  26. 1 0
      src/views/prepare-lessons/components/resource-main/components/select-music/index.tsx
  27. 26 12
      src/views/prepare-lessons/model/work-section/index.tsx
  28. 128 0
      src/views/studentList/modals/comment-work/index.module.less
  29. 56 0
      src/views/studentList/modals/comment-work/index.tsx
  30. 二進制
      src/views/studentList/modals/images/common-bg.png
  31. 二進制
      src/views/studentList/modals/images/common-top.png
  32. 二進制
      src/views/studentList/modals/images/icon-close-line.png
  33. 100 73
      src/views/studentList/modals/studentTraomomhDetails.tsx

+ 4 - 2
src/components/searchInput/index.tsx

@@ -5,7 +5,7 @@ import { NInput } from 'naive-ui';
 export default defineComponent({
   name: 'student-studentList',
   props: ['searchWord'],
-  emits: ['changeValue'],
+  emits: ['changeValue', 'keyup', 'clear'],
   setup(props, { emit, attrs }) {
     return () => (
       <div>
@@ -19,7 +19,9 @@ export default defineComponent({
           value={props.searchWord}
           onInput={(str: string) => {
             emit('changeValue', str);
-          }}></NInput>
+          }}
+          onClear={() => emit('clear')}
+          onKeyup={(e: KeyboardEvent) => emit('keyup', e)}></NInput>
       </div>
     );
   }

+ 8 - 0
src/router/routes/index.ts

@@ -211,6 +211,14 @@ export const constantRoutes: RouteRecordRaw[] = [
         meta: {
           title: '作业记录'
         }
+      },
+      {
+        path: '/homework-record-detail',
+        name: 'homework-record-detail',
+        component: () => import('@/views/homework-record/detail'),
+        meta: {
+          title: '作业详情'
+        }
       }
     ]
   },

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

@@ -440,7 +440,7 @@
 }
 
 .trainClassModal {
-  width: 1028px;
+  width: 728px;
 
   :global {
     .n-card-header {

+ 9 - 9
src/views/attend-class/index.tsx

@@ -91,6 +91,7 @@ import rightHideIcon from './image/right_hide_icon.png';
 import SelectResources from '../prepare-lessons/model/select-resources';
 import { getStudentAfterWork } from '../studentList/api';
 import TheNoticeBar from '/src/components/TheNoticeBar';
+import ClassWork from './model/class-work';
 
 export type ToolType = 'init' | 'pen' | 'whiteboard';
 export type ToolItem = {
@@ -1723,7 +1724,7 @@ export default defineComponent({
           preset="card"
           class={[styles.attendClassModal, styles.trainClassModal]}
           title={'作业设置'}>
-          <TrainSettings
+          {/* <TrainSettings
             detailId={data.detailId}
             subjectId={data.subjectId}
             courseScheduleId={data.classId}
@@ -1731,16 +1732,15 @@ export default defineComponent({
             onClose={() => (data.modelTrainStatus = false)}
             onConfirm={() => {
               // 布置完作业之后直接关闭
-              // setTimeout(() => {
-              //   handleStop();
-              //   if (state.application) {
-              //     emit('close');
-              //   } else {
-              //     window.close();
-              //   }
-              // }, 1000);
               data.modelTrainStatus = false;
             }}
+          /> */}
+          <ClassWork
+            detailId={data.detailId}
+            subjectId={data.subjectId}
+            courseScheduleId={data.classId}
+            classGroupId={data.classGroupId}
+            onClose={() => (data.modelTrainStatus = false)}
           />
         </NModal>
 

+ 159 - 0
src/views/attend-class/model/class-work/index.module.less

@@ -0,0 +1,159 @@
+.btnGroup {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  // padding-left: 22px !important;
+  height: 40px;
+  // padding-right: 22px !important;
+  padding: 0 40px;
+
+  margin: 18px 0;
+
+  .addBtnIcon {
+    width: 13px !important;
+    height: 14px !important;
+  }
+
+  .addPreset {
+    padding: 0 16px 0 10px !important;
+
+    :global {
+      .n-button__icon {
+        margin-right: 0;
+      }
+    }
+  }
+
+  :global {
+    .n-base-selection {
+      --n-height: max(40px, 36Px) !important;
+      width: 160px;
+      font-size: 15px;
+      border-radius: 8px !important;
+    }
+
+    .n-base-selection-input__content {
+      font-size: max(15px, 13Px);
+    }
+
+    .n-button {
+      border-radius: 8px;
+      height: 38px;
+      font-size: max(18px, 13Px);
+      font-weight: 600 !important;
+      padding: 0 27px;
+    }
+
+
+
+    .n-button--default-type {
+      background: #E8F4FF;
+      color: #0378EC;
+
+      &:not(.n-button--disabled):hover {
+        background: #E8F4FF;
+      }
+
+      .n-button__border {
+        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;
+      }
+    }
+  }
+}
+
+.removeVisiable1 {
+  width: 432px;
+
+  :global {
+    .n-card-header {
+      font-size: max(22px, 16Px);
+    }
+  }
+
+  .studentRemove {
+    padding: 20px 40px 0;
+
+    p {
+      font-size: max(18px, 14Px);
+      color: #777777;
+      line-height: 30px;
+
+      span {
+        color: #EA4132;
+      }
+    }
+  }
+
+  .btnGroupModal {
+    padding: 32px 0;
+
+    :global {
+      .n-button {
+        height: 47px;
+        min-width: 156px;
+      }
+    }
+  }
+}
+
+.assignHomework {
+  width: 720px;
+}
+
+.listContainer {
+  padding-bottom: 24px;
+}
+
+.listSection {
+  padding: 0 40px;
+}
+
+.workVisiable {
+  width: 1258px;
+}
+
+.workContainer {
+  display: flex;
+  align-items: center;
+
+  .workTrain {
+    flex: 1;
+    height: 75vh;
+
+    &>div {
+      padding-top: 15px;
+    }
+  }
+
+  :global {
+    .train-container {
+      max-height: calc(var(--window-page-lesson-height) - 135px) !important;
+      // max-height: calc(var(--window-page-lesson-height) - 100px) !important;
+
+      .train-listSection {
+        min-height: calc(var(--window-page-lesson-height) - 135px) !important;
+      }
+    }
+  }
+
+  .resourceMain {
+    flex: 0 0 360px;
+    height: 75vh;
+    box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1);
+  }
+}

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

@@ -0,0 +1,298 @@
+import { defineComponent, nextTick, onMounted, reactive } from 'vue';
+import styles from './index.module.less';
+import {
+  NButton,
+  NImage,
+  NInput,
+  NModal,
+  NScrollbar,
+  NSpace,
+  NSpin,
+  useMessage
+} from 'naive-ui';
+import add from '@/views/studentList/images/add.png';
+import WorkSection from '/src/views/prepare-lessons/model/work-section';
+import {
+  lessonPreTrainingV2Page,
+  lessonPreTrainingV2Save
+} from '/src/views/prepare-lessons/api';
+import { storeToRefs } from 'pinia';
+import { useUserStore } from '/src/store/modules/users';
+import AssignHomework from '/src/views/prepare-lessons/components/lesson-main/train/assign-homework';
+import Train from '/src/views/prepare-lessons/components/lesson-main/train';
+import ResourceMain from '/src/views/prepare-lessons/components/resource-main';
+import dayjs from 'dayjs';
+import { useResizeObserver } from '@vueuse/core';
+
+export default defineComponent({
+  name: 'class-work',
+  props: {
+    /** 章节编号 */
+    detailId: {
+      type: String,
+      default: ''
+    },
+    /** 声部编号 */
+    subjectId: {
+      type: String,
+      default: ''
+    },
+    /** 班级编号 */
+    classGroupId: {
+      type: String,
+      default: ''
+    },
+    /** 上课编号 */
+    courseScheduleId: {
+      type: String,
+      default: ''
+    }
+  },
+  emits: ['close'],
+  setup(props, { emit }) {
+    const users = useUserStore();
+    const { info } = storeToRefs(users);
+    const message = useMessage();
+    const forms = reactive({
+      loadingStatus: false,
+      tableList: [] as any,
+      editTitleVisiable: false,
+      selectItem: {} as any,
+      editTitle: '',
+      assignHomeworkStatus: false,
+      editBtnLoading: false,
+      workVisiable: false
+    });
+    const getList = async () => {
+      forms.loadingStatus = true;
+      try {
+        // 判断是否有选择对应的课件 或声部
+        if (!props.detailId) return (forms.loadingStatus = false);
+        const { data } = await lessonPreTrainingV2Page({
+          page: 1,
+          coursewareDetailKnowledgeId: props.detailId
+        });
+        const result = data.rows || [];
+        const tempList: any = [];
+        result.forEach((item: any) => {
+          const { lessonPreTrainingDetails, ...ies } = item;
+          const tList: any = {
+            ...ies,
+            pTitle: '',
+            eTitle: '',
+            teacherAvatar: info.value?.avatar,
+            teacherName: info.value?.nickname,
+            lessonPreTrainingDetails
+          };
+          lessonPreTrainingDetails.forEach((child: any) => {
+            if (child.trainingType === 'PRACTICE' && child.musicName) {
+              tList.pTitle += tList.pTitle
+                ? '、' + child.musicName
+                : child.musicName;
+            }
+            if (child.trainingType === 'EVALUATION' && child.musicName) {
+              tList.eTitle += tList.eTitle
+                ? '、' + child.musicName
+                : child.musicName;
+            }
+          });
+
+          tempList.push(tList);
+        });
+
+        forms.tableList = tempList;
+      } catch {
+        //
+      }
+      forms.loadingStatus = false;
+    };
+
+    /** 修改标题 */
+    const onEditTitleSubmit = async () => {
+      if (!forms.editTitle) {
+        message.error('请输入作业标题');
+        return;
+      }
+      forms.editBtnLoading = true;
+      try {
+        await lessonPreTrainingV2Save({
+          id: forms.selectItem.id,
+          title: forms.editTitle
+        });
+        message.success('修改成功');
+        forms.editTitleVisiable = false;
+        forms.tableList.forEach((item: any) => {
+          if (item.id === forms.selectItem.id) {
+            item.title = forms.editTitle;
+          }
+        });
+      } catch {
+        //
+      }
+      forms.editBtnLoading = false;
+    };
+
+    const getModalHeight = () => {
+      useResizeObserver(
+        document.querySelector('#model-homework-height') as HTMLElement,
+        (entries: any) => {
+          const entry = entries[0];
+          const { height } = entry.contentRect;
+          document.documentElement.style.setProperty(
+            '--window-page-lesson-height',
+            height + 'px'
+          );
+        }
+      );
+    };
+
+    onMounted(() => {
+      getList();
+    });
+    return () => (
+      <div class={styles.classWork}>
+        <div class={styles.btnGroup}>
+          <NSpace>
+            <NButton
+              type="primary"
+              class={styles.addPreset}
+              onClick={() => {
+                // 设置右侧栏状态
+                forms.workVisiable = true;
+                forms.selectItem = {};
+                nextTick(() => {
+                  getModalHeight();
+                });
+              }}
+              v-slots={{
+                icon: () => (
+                  <>
+                    <NImage
+                      class={styles.addBtnIcon}
+                      previewDisabled
+                      src={add}></NImage>
+                  </>
+                )
+              }}>
+              添加作业
+            </NButton>
+          </NSpace>
+        </div>
+
+        <NScrollbar style={{ height: '60vh' }} class={[styles.listContainer]}>
+          <NSpin show={forms.loadingStatus}>
+            <div class={styles.listSection} style={{ minHeight: '60vh' }}>
+              <NSpace vertical>
+                {forms.tableList.map((item: any) => (
+                  <WorkSection
+                    hideDelete
+                    item={item}
+                    onEditTitle={() => {
+                      forms.selectItem = item;
+                      forms.editTitle = item.title;
+                      forms.editTitleVisiable = true;
+                    }}
+                    onEdit={() => {
+                      forms.workVisiable = true;
+                      forms.selectItem = item;
+                      nextTick(() => {
+                        getModalHeight();
+                      });
+                    }}
+                    onConfirm={() => {
+                      if (
+                        !item.lessonPreTrainingDetails ||
+                        item.lessonPreTrainingDetails.length <= 0
+                      ) {
+                        message.error('作业预设不能为空');
+                        return;
+                      }
+                      forms.assignHomeworkStatus = true;
+                      forms.selectItem = item;
+                    }}
+                  />
+                ))}
+              </NSpace>
+            </div>
+          </NSpin>
+        </NScrollbar>
+
+        <NModal
+          v-model:show={forms.editTitleVisiable}
+          preset="card"
+          class={['modalTitle', styles.removeVisiable1]}
+          title={'作业重命名'}>
+          <div class={styles.studentRemove}>
+            <NInput
+              placeholder="请输入作业标题"
+              v-model:value={forms.editTitle}
+              maxlength={100}
+              onKeyup={(e: any) => {
+                if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') {
+                  e.stopPropagation();
+                }
+              }}
+            />
+
+            <NSpace class={styles.btnGroupModal} justify="center">
+              <NButton round onClick={() => (forms.editTitleVisiable = false)}>
+                取消
+              </NButton>
+              <NButton
+                round
+                type="primary"
+                onClick={onEditTitleSubmit}
+                loading={forms.editBtnLoading}>
+                确定
+              </NButton>
+            </NSpace>
+          </div>
+        </NModal>
+
+        {/* 添加自定义教材 */}
+        <NModal
+          v-model:show={forms.assignHomeworkStatus}
+          preset="card"
+          showIcon={false}
+          class={['modalTitle background', styles.assignHomework]}
+          title={'布置作业'}
+          blockScroll={false}>
+          <AssignHomework
+            classGroupId={props.classGroupId}
+            item={forms.selectItem}
+            trainList={[]}
+            onClose={() => (forms.assignHomeworkStatus = false)}
+          />
+        </NModal>
+
+        <NModal
+          v-model:show={forms.workVisiable}
+          preset="card"
+          class={['modalTitle background', styles.workVisiable]}
+          title={'作业详情'}>
+          <div id="model-homework-height" class={styles.workContainer}>
+            <div class={styles.workTrain}>
+              <Train
+                coursewareKnowledgeDetailId={props.detailId}
+                classGroupId={props.classGroupId}
+                courseScheduleId={props.courseScheduleId}
+                lessonPreTraining={{
+                  title: dayjs().format('YYYY年MM月DD日') + '-课堂作业',
+                  id: forms.selectItem.id
+                }}
+                // cardType={'homeworkRecord'}
+                onChange={(val: any) => {
+                  forms.workVisiable = val.status;
+                  getList();
+                }}
+              />
+            </div>
+            <div class={styles.resourceMain}>
+              <ResourceMain cardType="homerowk-record" />
+            </div>
+          </div>
+        </NModal>
+      </div>
+    );
+  }
+});

+ 1 - 46
src/views/attend-class/model/train-type/index.tsx

@@ -175,45 +175,6 @@ export default defineComponent({
           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.trainingType === 'EVALUATION' ? eTag : pTag}
-              class={styles['title-tag']}
-            />
-            <NEllipsis class={styles['title-text']} tooltip={false}>
-              {props.item.musicName}
-            </NEllipsis>
-          </div>
-
-          {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={() => {
-                if (props.isDisabled) {
-                  return;
-                }
-                onDetail();
-              }}>
-              {props.item.trainingType === 'EVALUATION'
-                ? '评测模式'
-                : '练习模式'}
-            </NButton>
-          )}
-        </div> */}
         <div class={styles['train-content']}>
           <NImage src={props.item.coverImg} previewDisabled objectFit="cover" />
           {props.isDisabled && !props.isCLassWork ? (
@@ -232,13 +193,7 @@ export default defineComponent({
                 offset-degree={180}
                 type="circle"
                 rail-color={'8b8b8b'}
-                color={
-                  // props.item.trainingType === 'EVALUATION'
-                  //   ? '#FF7E65'
-                  //   : '#44B3FF'
-
-                  isPass.value ? '#6CFFC1' : '#FF7794'
-                }
+                color={isPass.value ? '#6CFFC1' : '#FF7794'}
                 style="width: 120px; margin: 0  0 10px;">
                 <div class={styles.BProgress}>
                   {props.item.trainingType === 'EVALUATION' ? (

+ 3 - 2
src/views/classList/components/afterWork.tsx

@@ -25,6 +25,7 @@ import {
 import TrainSettings from '../../attend-class/model/train-settings';
 import TheEmpty from '/src/components/TheEmpty';
 import { initCache, setCache } from '/src/hooks/use-async';
+import StudentTraomomhDetails from '../../studentList/modals/studentTraomomhDetails';
 export default defineComponent({
   name: 'afterWork',
   props: {
@@ -274,14 +275,14 @@ export default defineComponent({
             </NFormItem>
           </NForm>
         </div>
-        {!state.upgradeFlag && (
+        {/* {!state.upgradeFlag && (
           <NButton
             class={styles.addBtn}
             type="primary"
             onClick={() => (state.addWorkVisible = true)}>
             布置作业
           </NButton>
-        )}
+        )} */}
 
         <div class={styles.tableWrap}>
           <NDataTable

二進制
src/views/classList/images/icon-menu.png


二進制
src/views/classList/images/icon-pen.png


+ 31 - 0
src/views/classList/index.module.less

@@ -683,6 +683,36 @@
     }
   }
 
+  .commentBtnGroup {
+    background-color: #E8F4FF !important;
+    margin-left: 32px;
+    margin-bottom: 20px;
+    --n-border: 1px solid #198CFE !important;
+    --n-height: max(38px, 32Px) !important;
+    border-radius: 8px !important;
+
+    .text {
+      display: flex;
+      align-items: center;
+      color: #198cfe;
+
+      i {
+        width: 18px;
+        height: 18px;
+        display: inline-block;
+        background: url('./images/icon-pen.png') no-repeat center;
+        background-size: contain;
+        margin-right: 6px;
+
+        &.look {
+          background: url('./images/icon-menu.png') no-repeat center;
+          background-size: contain;
+        }
+      }
+    }
+
+  }
+
   .workList {
     display: flex;
     flex-direction: row;
@@ -690,6 +720,7 @@
     justify-content: space-between;
     flex-wrap: wrap;
     padding: 0 32px;
+    min-height: 360px;
   }
 
   .allTotal {

+ 94 - 89
src/views/classList/modals/TrainingDetails.tsx

@@ -6,7 +6,8 @@ import {
   NFormItem,
   NSelect,
   NImage,
-  NScrollbar
+  NScrollbar,
+  NSpin
 } from 'naive-ui';
 import { defineComponent, onMounted, reactive, ref } from 'vue';
 import { getTrainingStudentDetail } from '../api';
@@ -37,9 +38,7 @@ export default defineComponent({
   emits: ['close', 'next', 'pre'],
 
   setup(props, { emit, expose }) {
-    const data = reactive({
-      uploading: false
-    });
+    const loading = ref(false);
     const studnetInfo = ref({
       studentName: '',
       submitTime: '',
@@ -73,6 +72,7 @@ export default defineComponent({
       return tList;
     };
     const getTrainingDetail = async (id: any) => {
+      loading.value = true;
       try {
         const res = await getTrainingStudentDetail({
           studentLessonTrainingId: id
@@ -97,6 +97,7 @@ export default defineComponent({
       } catch (e) {
         console.log(e);
       }
+      loading.value = false;
     };
     expose({ getTrainingDetail });
     onMounted(() => {
@@ -105,95 +106,99 @@ export default defineComponent({
 
     return () => (
       <div class={[styles.trainingDetails]}>
-        <div class={styles.studentList}>
-          <div class={styles.studentHeaderWrap}>
-            <div class={styles.studentHeader}>
-              <div class={styles.studentHeaderBorder}>
-                <NImage
-                  class={styles.studentHeaderImg}
-                  src={
-                    studnetInfo.value.studentAvatar
-                      ? studnetInfo.value.studentAvatar
-                      : defultHeade
-                  }
-                  previewDisabled></NImage>
+        <NSpin show={loading.value}>
+          <div class={styles.studentList}>
+            <div class={styles.studentHeaderWrap}>
+              <div class={styles.studentHeader}>
+                <div class={styles.studentHeaderBorder}>
+                  <NImage
+                    class={styles.studentHeaderImg}
+                    src={
+                      studnetInfo.value.studentAvatar
+                        ? studnetInfo.value.studentAvatar
+                        : defultHeade
+                    }
+                    previewDisabled></NImage>
+                </div>
               </div>
-            </div>
 
-            <div class={styles.workafterInfo}>
-              <h4>
-                {studnetInfo.value.studentName}{' '}
-                <div class={styles.workafterInfoDot}>学生</div>
-              </h4>
-              <p>
-                提交时间:
-                {studnetInfo.value.submitTime
-                  ? dayjs(new Date(studnetInfo.value.submitTime)).format(
-                      'YYYY-MM-DD'
-                    )
-                  : '--'}
-              </p>
+              <div class={styles.workafterInfo}>
+                <h4>
+                  {studnetInfo.value.studentName}{' '}
+                  <div class={styles.workafterInfoDot}>学生</div>
+                </h4>
+                <p>
+                  提交时间:
+                  {studnetInfo.value.submitTime
+                    ? dayjs(new Date(studnetInfo.value.submitTime)).format(
+                        'YYYY-MM-DD'
+                      )
+                    : '--'}
+                </p>
+              </div>
             </div>
+            {studnetInfo.value.trainingStatus == 'UNSUBMITTED' ? (
+              <NImage
+                previewDisabled
+                class={styles.workStatus}
+                src={noSub}></NImage>
+            ) : null}
+            {studnetInfo.value.trainingStatus == 'SUBMITTED' ? (
+              <NImage
+                previewDisabled
+                class={styles.workStatus}
+                src={unqualified}></NImage>
+            ) : null}
+            {studnetInfo.value.trainingStatus == 'TARGET' ? (
+              <NImage
+                previewDisabled
+                class={styles.workStatus}
+                src={qualified}></NImage>
+            ) : null}
           </div>
-          {studnetInfo.value.trainingStatus == 'UNSUBMITTED' ? (
-            <NImage
-              previewDisabled
-              class={styles.workStatus}
-              src={noSub}></NImage>
-          ) : null}
-          {studnetInfo.value.trainingStatus == 'SUBMITTED' ? (
-            <NImage
-              previewDisabled
-              class={styles.workStatus}
-              src={unqualified}></NImage>
-          ) : null}
-          {studnetInfo.value.trainingStatus == 'TARGET' ? (
-            <NImage
-              previewDisabled
-              class={styles.workStatus}
-              src={qualified}></NImage>
-          ) : null}
-        </div>
-        <NScrollbar style="max-height:400px" trigger="none">
-          <div class={styles.workList}>
-            {studnetInfo.value.studentLessonTrainingDetails.map((item: any) => (
-              <TrainType
-                style={{ marginBottom: '20px' }}
-                isDisabled={true}
-                isDelete={false}
-                item={item}></TrainType>
-            ))}
-          </div>
-        </NScrollbar>
-        <NSpace
-          class={[styles.btnGroups, styles.nextWrap]}
-          justify="space-between">
-          <div class={styles.allTotal}>
-            {props.current}/{props.total}名学生
-          </div>
-          <div>
-            <NSpace>
-              <NButton
-                disabled={props.current <= 1}
-                round
-                type="primary"
-                onClick={() => {
-                  emit('pre');
-                }}>
-                上一名
-              </NButton>
-              <NButton
-                disabled={props.current >= props.total}
-                round
-                type="primary"
-                onClick={() => {
-                  emit('next');
-                }}>
-                下一名
-              </NButton>
-            </NSpace>
-          </div>
-        </NSpace>
+          <NScrollbar style="max-height:400px" trigger="none">
+            <div class={styles.workList}>
+              {studnetInfo.value.studentLessonTrainingDetails.map(
+                (item: any) => (
+                  <TrainType
+                    style={{ marginBottom: '20px' }}
+                    isDisabled={true}
+                    isDelete={false}
+                    item={item}></TrainType>
+                )
+              )}
+            </div>
+          </NScrollbar>
+          <NSpace
+            class={[styles.btnGroups, styles.nextWrap]}
+            justify="space-between">
+            <div class={styles.allTotal}>
+              {props.current}/{props.total}名学生
+            </div>
+            <div>
+              <NSpace>
+                <NButton
+                  disabled={props.current <= 1}
+                  round
+                  type="primary"
+                  onClick={() => {
+                    emit('pre');
+                  }}>
+                  上一名
+                </NButton>
+                <NButton
+                  disabled={props.current >= props.total}
+                  round
+                  type="primary"
+                  onClick={() => {
+                    emit('next');
+                  }}>
+                  下一名
+                </NButton>
+              </NSpace>
+            </div>
+          </NSpace>
+        </NSpin>
       </div>
     );
   }

+ 60 - 57
src/views/classList/modals/classTrainingDetails.tsx

@@ -6,7 +6,8 @@ import {
   NFormItem,
   NSelect,
   NImage,
-  NScrollbar
+  NScrollbar,
+  NSpin
 } from 'naive-ui';
 import { defineComponent, onMounted, reactive, ref } from 'vue';
 import { getTrainingClassDetail } from '../api';
@@ -37,9 +38,7 @@ export default defineComponent({
   emits: ['close'],
 
   setup(props, { emit, expose }) {
-    const data = reactive({
-      uploading: false
-    });
+    const loading = ref(false);
     const teacherInfo = ref({
       teacherName: '',
       createTime: '',
@@ -47,8 +46,6 @@ export default defineComponent({
       teacherAvatar: '',
       studentLessonTrainingDetails: [] as any
     } as any);
-    const message = useMessage();
-    const foemsRef = ref();
     const typeFormat = (trainingType: string, configJson: any) => {
       let tList: string[] = [];
 
@@ -61,18 +58,17 @@ export default defineComponent({
           // `速度${configJson.evaluateSpeed}`,
           `${configJson.trainingTimes}分合格`
         ];
-        // console.log('configJson.evaluateDifficult--', tList);
       } else {
         tList = [
           `${configJson.practiceChapterBegin}-${configJson.practiceChapterEnd}小节`,
           `速度${configJson.practiceSpeed}`,
           `${configJson.trainingTimes}分钟`
         ];
-        // console.log('configJson.evaluateDifficult', tList);
       }
       return tList;
     };
     const getTrainingDetail = async (id: any) => {
+      loading.value = true;
       try {
         const res = await getTrainingClassDetail({
           trainingId: id
@@ -97,6 +93,7 @@ export default defineComponent({
       } catch (e) {
         console.log(e);
       }
+      loading.value = false;
     };
     expose({ getTrainingDetail });
     onMounted(() => {
@@ -105,59 +102,65 @@ export default defineComponent({
 
     return () => (
       <div class={[styles.trainingDetails]}>
-        <div class={styles.studentList}>
-          <div class={styles.studentHeaderWrap}>
-            <div class={styles.studentHeader}>
-              <div class={styles.studentHeaderBorder}>
-                <NImage
-                  class={styles.studentHeaderImg}
-                  src={
-                    teacherInfo.value.teacherAvatar
-                      ? teacherInfo.value.teacherAvatar
-                      : defultHeade
-                  }
-                  previewDisabled></NImage>
+        <NSpin show={loading.value}>
+          <div class={styles.studentList}>
+            <div class={styles.studentHeaderWrap}>
+              <div class={styles.studentHeader}>
+                <div class={styles.studentHeaderBorder}>
+                  <NImage
+                    class={styles.studentHeaderImg}
+                    src={
+                      teacherInfo.value.teacherAvatar
+                        ? teacherInfo.value.teacherAvatar
+                        : defultHeade
+                    }
+                    previewDisabled></NImage>
+                </div>
               </div>
-            </div>
 
-            <div class={styles.workafterInfo}>
-              <h4>
-                {teacherInfo.value.teacherName}{' '}
-                <div
-                  class={[
-                    styles.workafterInfoDot,
-                    styles.workafterTeacherInfoDot
-                  ]}>
-                  老师
-                </div>
-              </h4>
-              <p>
-                开始时间:
-                {teacherInfo.value.createTime
-                  ? dayjs(new Date(teacherInfo.value.createTime)).format(
-                      'YYYY-MM-DD'
-                    )
-                  : '--'}{' '}
-                | 结束时间:
-                {dayjs(new Date(teacherInfo.value.expireDate)).format(
-                  'YYYY-MM-DD'
-                )}
-              </p>
+              <div class={styles.workafterInfo}>
+                <h4>
+                  {teacherInfo.value.teacherName}{' '}
+                  <div
+                    class={[
+                      styles.workafterInfoDot,
+                      styles.workafterTeacherInfoDot
+                    ]}>
+                    老师
+                  </div>
+                </h4>
+                <p>
+                  开始时间:
+                  {teacherInfo.value.createTime
+                    ? dayjs(new Date(teacherInfo.value.createTime)).format(
+                        'YYYY-MM-DD'
+                      )
+                    : '--'}{' '}
+                  | 结束时间:
+                  {teacherInfo.value.expireDate
+                    ? dayjs(new Date(teacherInfo.value.expireDate)).format(
+                        'YYYY-MM-DD'
+                      )
+                    : '--'}
+                </p>
+              </div>
             </div>
           </div>
-        </div>
-        <NScrollbar style="max-height:400px" trigger="none">
-          <div class={styles.workList}>
-            {teacherInfo.value.studentLessonTrainingDetails.map((item: any) => (
-              <TrainType
-                style={{ marginBottom: '20px' }}
-                isDisabled={true}
-                isDelete={false}
-                isCLassWork={true}
-                item={item}></TrainType>
-            ))}
-          </div>
-        </NScrollbar>
+          <NScrollbar style="max-height:400px" trigger="none">
+            <div class={styles.workList}>
+              {teacherInfo.value.studentLessonTrainingDetails.map(
+                (item: any) => (
+                  <TrainType
+                    style={{ marginBottom: '20px' }}
+                    isDisabled={true}
+                    isDelete={false}
+                    isCLassWork={true}
+                    item={item}></TrainType>
+                )
+              )}
+            </div>
+          </NScrollbar>
+        </NSpin>
       </div>
     );
   }

+ 18 - 0
src/views/homework-record/api.ts

@@ -18,3 +18,21 @@ export const api_withdrawTraining = (params: any) => {
     requestType: 'form'
   });
 };
+
+/**
+ * 作业记录 - 详情
+ */
+export const api_trainingDetail = (params: any) => {
+  return request.get(
+    '/edu-app/lessonTraining/trainingDetail?trainingId=' + params.id
+  );
+};
+
+/**
+ * 作业记录 - 详情学生列表
+ */
+export const api_trainingStudentList = (params: any) => {
+  return request.post('/edu-app/lessonTraining/trainingStudentList', {
+    data: params
+  });
+};

+ 235 - 0
src/views/homework-record/detail/index.module.less

@@ -0,0 +1,235 @@
+.listWrap {
+  min-height: 100%;
+  padding: 32px;
+  background-color: #fff;
+  border-radius: 20px;
+}
+
+.teacherSection {
+  display: flex;
+  align-items: center;
+  border-bottom: 1px solid #E9E9E9;
+  margin-bottom: 30px;
+  padding-bottom: 24px;
+
+  .tTemp {
+    display: flex;
+    align-content: center;
+  }
+
+  .infos {
+    padding: 13px;
+    background: #FFFFFF;
+    border-radius: 10px;
+
+    .homeTitle {
+      font-size: max(17px, 14Px);
+      font-family: PingFangSC, PingFang SC;
+      font-weight: 600;
+      color: #000000;
+      padding-bottom: 5px;
+    }
+
+    .homeContent {
+      padding-bottom: 5px;
+    }
+
+    .homeworkText {
+      display: flex;
+      align-items: flex-start;
+
+      .pSection {
+        max-width: 500px;
+      }
+
+      .p1,
+      .p2 {
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+
+
+      }
+
+      .p1::before,
+      .p2::before {
+        content: '';
+        display: inline-block;
+        width: 5px;
+        height: 5px;
+        background: #198CFE;
+        margin-right: 7px;
+        border-radius: 50%;
+        flex-shrink: 0;
+        transform: translateY(-3px);
+      }
+
+      .p2 {
+        padding-top: 6px;
+      }
+
+      .p2::before {
+        background: #F44040;
+      }
+    }
+
+    .title {
+      font-size: max(13px, 12Px);
+      color: #777777;
+      flex-shrink: 0;
+    }
+
+    .text {
+      font-size: max(13px, 12Px);
+      font-weight: 500;
+      color: #333333;
+      line-height: 22px;
+    }
+
+  }
+
+  .stitcTitle {
+    display: flex;
+    align-items: center;
+    font-size: max(20px, 16Px);
+    font-family: PingFangSC, PingFang SC;
+    font-weight: 600;
+    color: #000000;
+    line-height: 28px;
+    padding-bottom: 30px;
+
+    &::before {
+      content: '';
+      display: inline-block;
+      width: 4px;
+      height: 14px;
+      background: #198CFE;
+      border-radius: 2px;
+      margin-right: 8px;
+    }
+  }
+
+  .stitcConent {
+    :global {
+      .n-progress {
+        width: 116Px;
+      }
+    }
+
+    .contentRect {
+      text-align: center;
+
+      .text {
+        padding-top: 5px;
+        font-size: 12Px;
+        font-family: PingFangSC, PingFang SC;
+        font-weight: 400;
+        color: #777777;
+        line-height: 17px;
+      }
+    }
+
+    .nums {
+      font-size: max(26px, 18Px);
+      font-family: DINAlternate, DINAlternate;
+      font-weight: bold;
+      color: #000000;
+      line-height: 30px;
+
+      i {
+        font-style: normal;
+        font-size: max(20px, 14Px);
+      }
+
+      span {
+        font-size: 12Px;
+        font-family: PingFangSC, PingFang SC;
+        font-weight: 500;
+        color: #333333;
+        line-height: 17px;
+      }
+    }
+  }
+}
+
+.teacherList {
+  display: flex;
+  // align-items: center;
+  flex-direction: column;
+  // margin-bottom: 32px;
+  flex: 1;
+  margin-right: 60px;
+  position: relative;
+
+  &::after {
+    content: '';
+    position: absolute;
+    right: 0;
+    width: 1px;
+    height: 120px;
+    background: #E9E9E9;
+    top: 50%;
+    margin-top: -60px;
+  }
+
+
+
+  .teacherHeader {
+    width: 100px;
+    height: 100px;
+    padding: 4px;
+    border-radius: 99px;
+    background: linear-gradient(228deg,
+        rgba(2, 186, 255, 1),
+        rgba(0, 122, 254, 1));
+    margin-right: 20px;
+
+    .teacherHeaderBorder {
+      width: 100%;
+      height: 100%;
+      background: #fff;
+      border-radius: 99px;
+      overflow: hidden;
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      justify-content: center;
+      padding: 4px;
+    }
+  }
+
+  .teacherHeaderImg {
+    width: 84px;
+    height: 84px;
+    border-radius: 50%;
+    overflow: hidden;
+  }
+
+  .workafterInfo {
+    display: flex;
+    justify-content: center;
+    flex-direction: column;
+
+    h4 {
+      font-size: 22px;
+      line-height: 30px;
+      font-weight: 600;
+      color: #131415;
+      margin-bottom: 12px;
+    }
+
+    p {
+      font-size: max(16px, 12Px);
+      line-height: 22px;
+      color: #777;
+
+      span {
+        color: #ea4132;
+      }
+    }
+  }
+}
+
+.wordDetailModel {
+  width: 1012px;
+}

+ 376 - 0
src/views/homework-record/detail/index.tsx

@@ -0,0 +1,376 @@
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from './index.module.less';
+import {
+  NButton,
+  NDataTable,
+  NForm,
+  NFormItem,
+  NImage,
+  NModal,
+  NProgress,
+  NSpace
+} from 'naive-ui';
+import SearchInput from '@/components/searchInput';
+import CSelect from '@/components/CSelect';
+import Pagination from '@/components/pagination';
+import { api_trainingDetail, api_trainingStudentList } from '../api';
+import { useRoute } from 'vue-router';
+import CBreadcrumb from '/src/components/CBreadcrumb';
+import defultHeade from '@/components/layout/images/teacherIcon.png';
+import { trainingStatusArray } from '@/utils/searchArray';
+import dayjs from 'dayjs';
+import TheEmpty from '/src/components/TheEmpty';
+import TrainingDetails from '../../classList/modals/TrainingDetails';
+export default defineComponent({
+  name: 'homewrok-record-detail',
+  setup() {
+    const route = useRoute();
+    const state = reactive({
+      searchForm: { keyword: '', trainingStatus: '' as any },
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 4
+      },
+      tableList: [] as any,
+      workInfo: {} as any,
+      detailVisiable: false,
+      activeRow: null as any,
+      index: 0
+    });
+    const TrainingDetailsRef = ref();
+    const routerList = ref([
+      { name: '作业记录', path: '/homework-record' },
+      { name: route.query.name, path: '/classDetail' }
+    ] as any);
+
+    const search = () => {
+      state.pagination.page = 1;
+      getList();
+    };
+
+    const onReset = () => {
+      state.searchForm = { keyword: '', trainingStatus: '' as any };
+      search();
+    };
+    const getList = async () => {
+      state.loading = true;
+      try {
+        const res = await api_trainingStudentList({
+          trainingId: route.query.id,
+          ...state.searchForm,
+          ...state.pagination
+        });
+        state.tableList = res.data.rows;
+        state.pagination.pageTotal = res.data.total;
+        state.loading = false;
+      } catch (e) {
+        state.loading = false;
+        console.log(e);
+      }
+    };
+    const getWorkInfo = async () => {
+      try {
+        const res = await api_trainingDetail({ id: route.query.id });
+        const result = res.data || {};
+        // state.workInfo
+        let pTitle = '';
+        let eTitle = '';
+        if (
+          result.studentLessonTrainingDetails &&
+          result.studentLessonTrainingDetails.length > 0
+        ) {
+          result.studentLessonTrainingDetails.forEach((child: any) => {
+            if (child.trainingType === 'PRACTICE' && child.musicName) {
+              pTitle += pTitle ? '、' + child.musicName : child.musicName;
+            }
+            if (child.trainingType === 'EVALUATION' && child.musicName) {
+              eTitle += eTitle ? '、' + child.musicName : child.musicName;
+            }
+          });
+        }
+        result.pTitle = pTitle;
+        result.eTitle = eTitle;
+        state.workInfo = result;
+      } catch (e) {
+        console.log(e);
+      }
+    };
+
+    const lookDetail = (row: any, index: number) => {
+      console.log(index, 'index');
+      state.index = index + 1;
+      state.activeRow = row;
+      state.detailVisiable = true;
+    };
+    onMounted(() => {
+      getWorkInfo();
+      getList();
+    });
+    const columns = () => {
+      return [
+        {
+          title: '学生姓名',
+          key: 'studentName'
+        },
+        {
+          title: '最后提交时间',
+          key: 'submitTime',
+          render(row: any) {
+            return row.submitTime
+              ? dayjs(row.submitTime).format('YYYY-MM-DD')
+              : '--';
+          }
+        },
+        {
+          title: '作业状态',
+          key: 'sex',
+          render(row: any) {
+            return (
+              <div>
+                {row.trainingStatus == 'UNSUBMITTED' ? (
+                  <p class={styles.nosub}>未提交</p>
+                ) : null}
+                {row.trainingStatus == 'SUBMITTED' ? (
+                  <p class={styles.ison}>不合格</p>
+                ) : null}
+                {row.trainingStatus == 'TARGET' ? (
+                  <p class={styles.isok}>合格</p>
+                ) : null}
+              </div>
+            );
+          }
+        },
+        {
+          title: '操作',
+          key: 'id',
+          render(row: any, index: number) {
+            return (
+              <NButton
+                text
+                type="primary"
+                onClick={() => {
+                  lookDetail(row, index);
+                }}>
+                详情
+              </NButton>
+            );
+          }
+        }
+      ];
+    };
+
+    const goToNext = () => {
+      ++state.index;
+      state.activeRow = state.tableList[state.index - 1];
+      TrainingDetailsRef.value.getTrainingDetail(
+        state.activeRow.studentLessonTrainingId
+      );
+    };
+    const gotoPre = () => {
+      --state.index;
+      state.activeRow = state.tableList[state.index - 1];
+      TrainingDetailsRef.value.getTrainingDetail(
+        state.activeRow.studentLessonTrainingId
+      );
+    };
+
+    return () => (
+      <div>
+        <CBreadcrumb list={routerList.value}></CBreadcrumb>
+        <div class={styles.listWrap}>
+          <div class={styles.teacherSection}>
+            <div class={styles.teacherList}>
+              <div class={styles.tTemp}>
+                <div class={styles.teacherHeader}>
+                  <div class={styles.teacherHeaderBorder}>
+                    <NImage
+                      class={styles.teacherHeaderImg}
+                      src={state.workInfo.teacherAvatar || defultHeade}
+                      previewDisabled></NImage>
+                  </div>
+                </div>
+                <div class={styles.workafterInfo}>
+                  <h4>{state.workInfo.teacherName}</h4>
+                  {state.workInfo.createTime && (
+                    <p>
+                      布置时间:
+                      {state.workInfo.createTime &&
+                        dayjs(state.workInfo.createTime).format(
+                          'YYYY-MM-DD'
+                        )}{' '}
+                      |{' '}
+                      <span>
+                        截止时间:
+                        {state.workInfo.expireDate &&
+                          dayjs(state.workInfo.expireDate).format('YYYY-MM-DD')}
+                      </span>
+                    </p>
+                  )}
+                </div>
+              </div>
+              <div class={styles.infos}>
+                <div class={styles.homeTitle}>{state.workInfo.name}</div>
+                <div class={[styles.homeContent, styles.homeworkText]}>
+                  <div class={styles.pSection}>
+                    {state.workInfo.pTitle && (
+                      <p class={[styles.text, styles.p1]}>
+                        {state.workInfo.pTitle}
+                      </p>
+                    )}
+                    {state.workInfo.eTitle && (
+                      <p class={[styles.text, styles.p2]}>
+                        {state.workInfo.eTitle}
+                      </p>
+                    )}
+                  </div>
+                </div>
+              </div>
+            </div>
+            <div>
+              <div class={styles.stitcTitle}>作业完成情况</div>
+              <div class={styles.stitcConent}>
+                <NSpace size={[38, 0]}>
+                  <NProgress
+                    percentage={state.workInfo.trainingRate || 0}
+                    // percentage={20}
+                    offset-degree={180}
+                    type="circle"
+                    rail-color={'EDEFFA'}
+                    color={'#64A5FF'}>
+                    <div class={styles.contentRect}>
+                      <div class={styles.nums}>
+                        {state.workInfo.trainingNum || 0}
+                        <i>/</i>
+                        {state.workInfo.expectNum || 0}
+                        <span>人</span>
+                      </div>
+                      <div class={styles.text}>已提交</div>
+                    </div>
+                  </NProgress>
+                  <NProgress
+                    percentage={state.workInfo.trainingRate || 0}
+                    offset-degree={180}
+                    type="circle"
+                    rail-color={'EDEFFA'}
+                    color={'#64A5FF'}>
+                    <div class={styles.contentRect}>
+                      <div class={styles.nums}>
+                        {state.workInfo.trainingRate || 0}%
+                      </div>
+                      <div class={styles.text}>提交率</div>
+                    </div>
+                  </NProgress>
+                  <NProgress
+                    percentage={state.workInfo.standardNum || 0}
+                    offset-degree={180}
+                    type="circle"
+                    rail-color={'EDEFFA'}
+                    color={'#40CEAE'}>
+                    <div class={styles.contentRect}>
+                      <div class={styles.nums}>
+                        {state.workInfo.standardNum || 0}
+                        <span>人</span>
+                      </div>
+                      <div class={styles.text}>合格人数</div>
+                    </div>
+                  </NProgress>
+                  <NProgress
+                    percentage={state.workInfo.qualifiedRate || 0}
+                    offset-degree={180}
+                    type="circle"
+                    rail-color={'EDEFFA'}
+                    color={'#40CEAE'}>
+                    <div class={styles.contentRect}>
+                      <div class={styles.nums}>
+                        {state.workInfo.qualifiedRate || 0}%
+                      </div>
+                      <div class={styles.text}>合格率</div>
+                    </div>
+                  </NProgress>
+                </NSpace>
+              </div>
+            </div>
+          </div>
+          <div class={styles.searchList}>
+            <NForm label-placement="left" inline>
+              <NFormItem>
+                <SearchInput
+                  {...{ placeholder: '请输入学生姓名' }}
+                  class={styles.searchInput}
+                  searchWord={state.searchForm.keyword}
+                  onChangeValue={(val: string) =>
+                    (state.searchForm.keyword = val)
+                  }></SearchInput>
+              </NFormItem>
+
+              <NFormItem>
+                <CSelect
+                  {...({
+                    options: [
+                      {
+                        label: '全部状态',
+                        value: ''
+                      },
+                      ...trainingStatusArray
+                    ],
+                    placeholder: '作业状态',
+                    clearable: true,
+                    inline: true
+                  } as any)}
+                  v-model:value={state.searchForm.trainingStatus}></CSelect>
+              </NFormItem>
+
+              <NFormItem>
+                <NSpace justify="end">
+                  <NButton type="primary" class="searchBtn" onClick={search}>
+                    搜索
+                  </NButton>
+                  <NButton
+                    type="primary"
+                    ghost
+                    class="resetBtn"
+                    onClick={onReset}>
+                    重置
+                  </NButton>
+                </NSpace>
+              </NFormItem>
+            </NForm>
+          </div>
+          <div class={styles.tableWrap}>
+            <NDataTable
+              v-slots={{
+                empty: () => <TheEmpty></TheEmpty>
+              }}
+              class={styles.classTable}
+              loading={state.loading}
+              columns={columns()}
+              data={state.tableList}></NDataTable>
+            <Pagination
+              v-model:page={state.pagination.page}
+              v-model:pageSize={state.pagination.rows}
+              v-model:pageTotal={state.pagination.pageTotal}
+              onList={getList}
+              sync
+            />
+          </div>
+        </div>
+        <NModal
+          v-model:show={state.detailVisiable}
+          preset="card"
+          class={['modalTitle background', styles.wordDetailModel]}
+          title={'作业详情'}>
+          <TrainingDetails
+            onNext={() => goToNext()}
+            onPre={() => gotoPre()}
+            ref={TrainingDetailsRef}
+            onClose={() => (state.detailVisiable = false)}
+            total={state.tableList.length}
+            current={state.index}
+            activeRow={state.activeRow}></TrainingDetails>
+        </NModal>
+      </div>
+    );
+  }
+});

+ 97 - 98
src/views/homework-record/index.tsx

@@ -30,7 +30,6 @@ import Train from '../prepare-lessons/components/lesson-main/train';
 import ResourceMain from '../prepare-lessons/components/resource-main';
 import { useResizeObserver } from '@vueuse/core';
 import { nextTick } from 'process';
-import AssignStudent from '../prepare-lessons/components/lesson-main/train/assign-student';
 
 export const getCurrentMonth = () => {
   return [dayjs().startOf('month').valueOf(), dayjs().endOf('month').valueOf()];
@@ -354,97 +353,108 @@ export default defineComponent({
         </NButton>
         <div class={styles.tableWrap}>
           <NSpin show={state.loading}>
-            <div class={styles.listSection}>
-              {state.tableList.map((item: any) => (
-                <div
-                  class={styles.item}
-                  onClick={() => {
-                    console.log('item', item);
-                  }}>
-                  <div class={styles.header}>
-                    <NAvatar
-                      class={styles.navatar}
-                      round
-                      src={item.teacherAvatar || teacherIcon}
-                    />
-                    <div class={styles.userInfo}>
-                      <h2>{item.teacherName}</h2>
-                      <p>
-                        布置时间:{dayjs(item.createTime).format('YYYY-MM-DD')}
-                        <span>|</span>
-                        <span>
-                          截止时间:
-                          {dayjs(item.expireDate).format('YYYY-MM-DD')}
-                        </span>
-                      </p>
-                    </div>
-                    <div class={item.status === 1 ? styles.over : styles.ing}>
-                      {item.status === 1 ? '已结束' : '进行中'}
-                    </div>
-                  </div>
-                  <div class={styles.content}>
-                    <div>
-                      <div class={styles.homeTitle}>{item.name}</div>
-                      <div class={styles.homeContent}>
-                        <span class={styles.title}>作业对象:</span>
-                        <span class={styles.text}>{item.homeworkObjName}</span>
-                      </div>
-                      <div class={[styles.homeContent, styles.homeworkText]}>
-                        <span class={styles.title}>作业内容:</span>
-                        <div class={styles.pSection}>
-                          {item.pTitle && (
-                            <p class={[styles.text, styles.p1]}>
-                              {item.pTitle}
-                            </p>
-                          )}
-                          {item.eTitle && (
-                            <p class={[styles.text, styles.p2]}>
-                              {item.eTitle}
-                            </p>
-                          )}
-                        </div>
+            <div style={{ minHeight: '40vh' }}>
+              <div class={styles.listSection}>
+                {state.tableList.map((item: any) => (
+                  <div
+                    class={styles.item}
+                    onClick={() => {
+                      router.push({
+                        path: '/homework-record-detail',
+                        query: {
+                          id: item.id,
+                          name: item.name
+                        }
+                      });
+                    }}>
+                    <div class={styles.header}>
+                      <NAvatar
+                        class={styles.navatar}
+                        round
+                        src={item.teacherAvatar || teacherIcon}
+                      />
+                      <div class={styles.userInfo}>
+                        <h2>{item.teacherName}</h2>
+                        <p>
+                          布置时间:
+                          {dayjs(item.createTime).format('YYYY-MM-DD')}
+                          <span>|</span>
+                          <span>
+                            截止时间:
+                            {dayjs(item.expireDate).format('YYYY-MM-DD')}
+                          </span>
+                        </p>
                       </div>
-                      <div class={styles.homeSubmit}>
-                        <span class={styles.title}>已提交:</span>
-                        <span class={styles.text}>
-                          {item.trainingNum || 0}/{item.trainingNum || 0}人
-                        </span>
-                        <NDivider vertical />
-                        <span class={styles.title}>提交率:</span>
-                        <span class={styles.text}>
-                          {item.trainingRate || 0}%
-                        </span>
-                        <NDivider vertical />
-                        <span class={styles.title}>合格人数:</span>
-                        <span class={styles.text}>
-                          {item.standardNum || 0}人
-                        </span>
-                        <NDivider vertical />
-                        <span class={styles.title}>合格率:</span>
-                        <span class={styles.text}>
-                          {item.qualifiedRate || 0}%
-                        </span>
+                      <div class={item.status === 1 ? styles.over : styles.ing}>
+                        {item.status === 1 ? '已结束' : '进行中'}
                       </div>
                     </div>
+                    <div class={styles.content}>
+                      <div>
+                        <div class={styles.homeTitle}>{item.name}</div>
+                        <div class={styles.homeContent}>
+                          <span class={styles.title}>作业对象:</span>
+                          <span class={styles.text}>
+                            {item.homeworkObjName}
+                          </span>
+                        </div>
+                        <div class={[styles.homeContent, styles.homeworkText]}>
+                          <span class={styles.title}>作业内容:</span>
+                          <div class={styles.pSection}>
+                            {item.pTitle && (
+                              <p class={[styles.text, styles.p1]}>
+                                {item.pTitle}
+                              </p>
+                            )}
+                            {item.eTitle && (
+                              <p class={[styles.text, styles.p2]}>
+                                {item.eTitle}
+                              </p>
+                            )}
+                          </div>
+                        </div>
+                        <div class={styles.homeSubmit}>
+                          <span class={styles.title}>已提交:</span>
+                          <span class={styles.text}>
+                            {item.trainingNum || 0}/{item.expectNum || 0}人
+                          </span>
+                          <NDivider vertical />
+                          <span class={styles.title}>提交率:</span>
+                          <span class={styles.text}>
+                            {item.trainingRate || 0}%
+                          </span>
+                          <NDivider vertical />
+                          <span class={styles.title}>合格人数:</span>
+                          <span class={styles.text}>
+                            {item.standardNum || 0}人
+                          </span>
+                          <NDivider vertical />
+                          <span class={styles.title}>合格率:</span>
+                          <span class={styles.text}>
+                            {item.qualifiedRate || 0}%
+                          </span>
+                        </div>
+                      </div>
 
-                    <NButton
-                      class={styles.errorBtn}
-                      type="error"
-                      color="#F94D50"
-                      onClick={(e: any) => {
-                        e.stopPropagation();
-                        state.resetVisiable = true;
-                        state.resetItem = item;
-                      }}>
-                      撤回
-                    </NButton>
+                      <NButton
+                        class={styles.errorBtn}
+                        type="error"
+                        color="#F94D50"
+                        onClick={(e: any) => {
+                          e.stopPropagation();
+                          state.resetVisiable = true;
+                          state.resetItem = item;
+                        }}>
+                        撤回
+                      </NButton>
+                    </div>
                   </div>
-                </div>
-              ))}
+                ))}
+              </div>
+              {state.tableList.length <= 0 && !state.loading && (
+                <TheEmpty class={styles.nowEmpty} />
+              )}
             </div>
-            {state.tableList.length <= 0 && (
-              <TheEmpty class={styles.nowEmpty} />
-            )}
           </NSpin>
 
           {state.tableList.length > 0 && (
@@ -479,7 +489,7 @@ export default defineComponent({
           </div>
         </NModal>
 
-        {/* <NModal
+        <NModal
           v-model:show={state.workVisiable}
           preset="card"
           class={['modalTitle background', styles.workVisiable]}
@@ -501,17 +511,6 @@ export default defineComponent({
               <ResourceMain cardType="homerowk-record" />
             </div>
           </div>
-        </NModal> */}
-
-        <NModal
-          v-model:show={state.workVisiable}
-          preset="card"
-          showIcon={false}
-          class={['modalTitle background']}
-          title={'布置作业'}
-          style={{ width: '640px' }}
-          blockScroll={false}>
-          <AssignStudent />
         </NModal>
       </div>
     );

+ 9 - 6
src/views/login/components/pwdLogin.tsx

@@ -39,12 +39,12 @@ export default defineComponent({
   emits: ['changType', 'update:phone'],
   setup(props, { emit }) {
     const router = useRouter();
-    const route = useRoute();
+    // const route = useRoute();
     const formRef = ref();
     const message = useMessage();
     const loading = ref(false);
-    const autoLogin = ref(true);
-    const LOGIN_NAME = PageEnum.BASE_LOGIN_NAME;
+    // const autoLogin = ref(true);
+    // const LOGIN_NAME = PageEnum.BASE_LOGIN_NAME;
     const showPwd = ref(false);
 
     const userStore = useUserStore();
@@ -89,10 +89,13 @@ export default defineComponent({
             await userStore.login(params);
             message.destroyAll();
             //  判断是否勾选自动登录
-            if (autoLogin.value) {
+            if (formInline.isCaptcha) {
               storage.set('userInfo-teacher', JSON.stringify(formInline));
             } else {
-              storage.remove('userInfo-teacher');
+              storage.set(
+                'userInfo-teacher',
+                JSON.stringify({ ...formInline, password: '', isCaptcha: true })
+              );
             }
 
             // route.query?.redirect ||
@@ -176,7 +179,7 @@ export default defineComponent({
           <NFormItem class={styles['default-color']}>
             <div class={[styles['flex'], styles['justify-between']]}>
               <div class={styles['flex-initial']}>
-                <NCheckbox v-model:checked={autoLogin.value}>
+                <NCheckbox v-model:checked={formInline.isCaptcha}>
                   记住密码
                 </NCheckbox>
               </div>

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

@@ -86,7 +86,7 @@ export const lessonPreTrainingUpdate = (params: any) => {
  * 备课 - 删除训练
  */
 export const lessonPreTrainingDelete = (params: any) => {
-  return request.post('/edu-app/lessonPreTraining/delete', {
+  return request.post('/edu-app/lessonPreTraining/delete?ids=' + params.ids, {
     data: params
   });
 };

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

@@ -22,7 +22,6 @@ import {
 } from '../../../api';
 import { storeToRefs } from 'pinia';
 import AssignHomework from '../train/assign-homework';
-import qs from 'query-string';
 
 export default defineComponent({
   name: 'train-presets',
@@ -65,12 +64,12 @@ export default defineComponent({
             lessonPreTrainingDetails
           };
           lessonPreTrainingDetails.forEach((child: any) => {
-            if (child.trainingType === 'PRACTICE') {
+            if (child.trainingType === 'PRACTICE' && child.musicName) {
               tList.pTitle += tList.pTitle
                 ? '、' + child.musicName
                 : child.musicName;
             }
-            if (child.trainingType === 'EVALUATION') {
+            if (child.trainingType === 'EVALUATION' && child.musicName) {
               tList.eTitle += tList.eTitle
                 ? '、' + child.musicName
                 : child.musicName;

+ 84 - 13
src/views/prepare-lessons/components/lesson-main/train/assign-homework.tsx

@@ -6,6 +6,7 @@ import {
   NForm,
   NFormItem,
   NInput,
+  NModal,
   NScrollbar,
   NSelect,
   NSpace,
@@ -17,10 +18,21 @@ import dayjs from 'dayjs';
 import { classGroupList } from '/src/views/home/api';
 import { gradeToCN } from '/src/utils/contants';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
+import AssignStudent from './assign-student';
+import { state } from '/src/state';
+import { nextTick } from 'process';
 
 export default defineComponent({
   name: 'assign-homework',
   props: {
+    classGroupId: {
+      type: String,
+      default: ''
+    },
+    courseScheduleId: {
+      type: String,
+      default: ''
+    },
     item: {
       type: Object,
       default: () => ({})
@@ -41,25 +53,28 @@ export default defineComponent({
     const prepareStore = usePrepareStore();
     const forms = reactive({
       currentTime: dayjs(dayjs().format('YYYY-MM-DD')).valueOf(),
-      homeworkObj: 'PERSON' as 'PERSON' | 'CLASS',
+      homeworkObj: 'CLASS' as 'PERSON' | 'CLASS',
       homeworkType: props.homeworkType,
+      workVisiable: false,
       id: null as any,
       uploading: false,
       title: props.item.title,
-      courseScheduleId: null,
+      courseScheduleId: props.courseScheduleId || null,
       gradeList: [] as any,
       classList: [] as any,
       currentGradeNum: null,
       expireDate: dayjs().add(7, 'day').format('YYYY-MM-DD') as any, // 默认7天
       classGroupId: null as any,
-      studentList: [] as any
+      studentList: [] as any,
+      selectIds: [] as any
     });
     const formsRef = ref();
 
     // 获取年级班级
     const getClassGroupList = async () => {
       try {
-        const defaultSelectClassGroupId = prepareStore.getClassGroupId;
+        const defaultSelectClassGroupId =
+          props.classGroupId || prepareStore.getClassGroupId;
         const { data } = await classGroupList({
           upgradeFlag: true
         });
@@ -77,7 +92,7 @@ export default defineComponent({
 
             if (i.id === defaultSelectClassGroupId) {
               forms.currentGradeNum = i.currentGradeNum;
-              forms.classGroupId = i.id;
+              forms.classGroupId = [i.id];
             }
           });
 
@@ -134,8 +149,18 @@ export default defineComponent({
             classGroupId: forms.classGroupId
               ? forms.classGroupId.join(',')
               : null,
+            studentIds: null as any,
             courseScheduleId: forms.courseScheduleId
           };
+
+          if (forms.homeworkObj === 'PERSON') {
+            params.classGroupId = '';
+            const ids: any[] = [];
+            forms.studentList.forEach((item: any) => {
+              ids.push(item.id);
+            });
+            params.studentIds = ids.join(',');
+          }
           await lessonTrainingAdd(params);
           message.success('布置成功');
           emit('close');
@@ -147,8 +172,8 @@ export default defineComponent({
       });
     };
 
-    onMounted(() => {
-      getClassGroupList();
+    onMounted(async () => {
+      await getClassGroupList();
     });
     return () => (
       <div class={styles.assignHomeworkContainer}>
@@ -212,6 +237,7 @@ export default defineComponent({
               }
             ]}>
             <NSelect
+              disabled={props.classGroupId ? true : false}
               v-model:value={forms.currentGradeNum}
               placeholder="请选择年级"
               options={forms.gradeList}
@@ -235,17 +261,18 @@ export default defineComponent({
                 }
               ]}>
               <NSelect
+                disabled={props.classGroupId ? true : false}
+                options={forms.classList}
                 v-model:value={forms.classGroupId}
                 placeholder="请选择班级"
                 clearable
                 multiple
-                options={forms.classList}
               />
             </NFormItem>
           ) : (
             <NFormItem
               label="学生"
-              path="classGroupId"
+              path="studentList"
               rule={[
                 {
                   required: true,
@@ -255,13 +282,37 @@ export default defineComponent({
                 }
               ]}
               class={styles.studentSection}>
-              <span class={styles.selectStudentBtn}>选择学生</span>
+              <span
+                class={[
+                  styles.selectStudentBtn,
+                  !forms.currentGradeNum && styles.disabled
+                ]}
+                onClick={() => {
+                  if (!forms.currentGradeNum) {
+                    return;
+                  }
+                  const tempIds: any = [];
+                  forms.studentList.forEach((item: any) => {
+                    tempIds.push(item.id);
+                  });
+                  forms.selectIds = tempIds;
+                  forms.workVisiable = true;
+                }}>
+                选择学生
+              </span>
               {forms.studentList.length > 0 && (
                 <NScrollbar class={styles.studentList}>
-                  <span class={styles.firstName}>当前选中(0):</span>
-                  {forms.studentList.map((item: any) => (
+                  <span class={styles.firstName}>
+                    当前选中({forms.studentList.length || 0}):
+                  </span>
+                  {forms.studentList.map((item: any, index: number) => (
                     <span class={styles.studentItem}>
-                      林霜降 <i class={styles.iconDelete}></i>
+                      {item.name}{' '}
+                      <i
+                        class={styles.iconDelete}
+                        onClick={() => {
+                          forms.studentList.splice(index, 1);
+                        }}></i>
                     </span>
                   ))}
                 </NScrollbar>
@@ -302,6 +353,26 @@ export default defineComponent({
             </NButton>
           </NSpace>
         </NForm>
+
+        <NModal
+          v-model:show={forms.workVisiable}
+          preset="card"
+          showIcon={false}
+          class={['modalTitle background']}
+          title={'布置作业'}
+          style={{ width: '640px' }}
+          blockScroll={false}>
+          <AssignStudent
+            classGroupId={props.classGroupId}
+            selectIds={forms.selectIds}
+            classList={forms.classList}
+            onClose={() => (forms.workVisiable = false)}
+            onConfirm={(val: any) => {
+              forms.studentList = val || [];
+              forms.workVisiable = false;
+            }}
+          />
+        </NModal>
       </div>
     );
   }

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

@@ -52,30 +52,36 @@
     height: 50vh;
   }
 
+  .loadingSection {
+    height: calc(50vh - 15px);
+  }
+
   .studentItem {
-    padding: 10px;
+    padding: 12px;
     border-radius: 8px;
     display: flex;
     align-items: center;
 
-    &.active {
+    &:hover {
       background: #F5F6FA;
     }
 
+
     .studentInfo {
       display: flex;
       align-items: center;
-      padding-left: 10px;
+      padding-left: 12px;
 
       .studentImg {
-        width: 40px;
-        height: 40px;
+        width: 48px;
+        height: 48px;
         border-radius: 8px;
+        background: transparent !important;
       }
     }
 
     .studentValue {
-      padding-left: 8px;
+      padding-left: 10px;
     }
 
     .userInfo {
@@ -83,7 +89,7 @@
       align-items: center;
 
       .name {
-        font-size: max(13px, 12Px);
+        font-size: max(16px, 13Px);
         font-weight: 500;
         color: #333333;
         line-height: 18px;
@@ -95,8 +101,8 @@
       }
 
       .iconMember {
-        width: max(13px, 12Px);
-        height: max(13px, 12Px);
+        width: max(16px, 14Px);
+        height: max(16px, 14Px);
         display: inline-block;
         margin-right: 5px;
         background: url('../../../../images/icon-member.png') no-repeat center;
@@ -151,16 +157,20 @@
   }
 
   .studentItem {
-    padding: 10px;
+    padding: 10px 12px;
     border-radius: 8px;
     display: flex;
     align-items: center;
     justify-content: space-between;
 
-    &.active {
+    &:hover {
       background: #F5F6FA;
     }
 
+    &.hide {
+      display: none;
+    }
+
     .iconClose {
       width: 13Px;
       height: 13Px;
@@ -177,13 +187,14 @@
   }
 
   .studentImg {
-    width: 28px;
-    height: 28px;
+    width: 34px;
+    height: 34px;
     margin-right: 8px;
+    background: transparent !important;
   }
 
   .name {
-    font-size: max(13px, 12Px);
+    font-size: max(16px, 13Px);
     color: #333333;
     line-height: 18px;
   }

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

@@ -1,4 +1,4 @@
-import { defineComponent, reactive } from 'vue';
+import { computed, defineComponent, onMounted, reactive } from 'vue';
 import styles from './index.module.less';
 import {
   NAvatar,
@@ -8,21 +8,150 @@ import {
   NInput,
   NScrollbar,
   NSelect,
-  NSpace
+  NSpace,
+  NSpin
 } from 'naive-ui';
+import defultHeade from '@/components/layout/images/teacherIcon.png';
 import SearchInput from '/src/components/searchInput';
+import { useCatchStore } from '/src/store/modules/catchData';
+import { getStudentList } from '/src/views/classList/api';
+import { useThrottleFn } from '@vueuse/core';
+import TheEmpty from '/src/components/TheEmpty';
 
 export default defineComponent({
   name: 'assign-student',
-  setup() {
+  props: {
+    /** 班级列表 */
+    classList: {
+      type: Array,
+      default: () => []
+    },
+    selectIds: {
+      type: Array,
+      default: () => []
+    },
+    classGroupId: {
+      type: String,
+      default: ''
+    }
+  },
+  emits: ['close', 'confirm'],
+  setup(props, { emit }) {
+    const catchStore = useCatchStore();
     const state = reactive({
       studentName: '',
+      loading: false,
+      finshed: false, // 是否加载完
+      checkAllStatus: false,
+      indeterminate: false,
       searchFrom: {
-        classGroupId: '',
+        currentGradeNum: '',
+        classGroupId: props.classGroupId || '',
         subjectId: '',
         keyword: ''
       },
-      tableList: [] as any
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 0
+      },
+      tableList: [] as any,
+      checkboxIds: [] as any,
+      selectStudents: [] as any,
+      selectKeyword: ''
+    });
+
+    const getStudentLists = async () => {
+      try {
+        if (state.pagination.page === 1) {
+          state.loading = true;
+          state.tableList = [];
+        }
+        const { data } = await getStudentList({
+          ...state.searchFrom,
+          ...state.pagination
+        });
+        state.loading = false;
+        const rows = data.rows || [];
+        state.tableList.push(...rows);
+        state.finshed = data.pages <= data.current ? true : false;
+      } catch {
+        //
+        state.loading = false;
+      }
+    };
+
+    const onSearch = () => {
+      state.pagination.page = 1;
+      getStudentLists();
+    };
+
+    const selectStudentEmpty = computed(() => {
+      let status = true;
+      state.selectStudents.forEach((item: any) => {
+        if (!item.hide) {
+          status = false;
+        }
+      });
+      return status;
+    });
+
+    const throttledFn = useThrottleFn(() => {
+      state.pagination.page = state.pagination.page + 1;
+      getStudentLists();
+    }, 500);
+
+    // 切换学生状态
+    const onCheckStudents = () => {
+      state.selectStudents = [];
+      if (state.checkboxIds.length <= 0) {
+        state.indeterminate = false;
+        state.checkAllStatus = false;
+        return;
+      }
+      if (state.checkboxIds.length === state.tableList.length) {
+        state.checkAllStatus = true;
+        state.indeterminate = false;
+      } else {
+        state.checkAllStatus = false;
+        state.indeterminate = true;
+      }
+
+      // 右边数据
+      state.tableList.forEach((item: any) => {
+        if (state.checkboxIds.includes(item.id)) {
+          state.selectStudents.push(item);
+        }
+      });
+    };
+
+    // 删除用户
+    const onRemove = (item: any) => {
+      const index = state.checkboxIds.findIndex((id: any) => id === item.id);
+      if (index !== -1) {
+        state.checkboxIds.splice(index, 1);
+        onCheckStudents();
+      }
+    };
+
+    const onSave = () => {
+      const studentInfo: any[] = [];
+      state.selectStudents.forEach((item: any) => {
+        studentInfo.push({
+          id: item.id,
+          name: item.nickname
+        });
+      });
+
+      emit('confirm', studentInfo);
+    };
+
+    onMounted(async () => {
+      state.checkboxIds = props.selectIds || [];
+      state.loading = true;
+      await catchStore.getSubjects();
+      await getStudentLists();
+      onCheckStudents();
     });
     return () => (
       <div class={styles.assignStudent}>
@@ -31,77 +160,159 @@ export default defineComponent({
             <div class={styles.searchSpace}>
               <NSelect
                 placeholder="全部班级"
+                disabled={props.classGroupId ? true : false}
                 v-model:value={state.searchFrom.classGroupId}
+                onUpdate:value={() => onSearch()}
+                options={
+                  [{ label: '全部班级', value: '' }, ...props.classList] as any
+                }
               />
               <NSelect
+                options={[
+                  { label: '全部声部', value: '' },
+                  ...catchStore.getSubjectList
+                ]}
                 placeholder="全部声部"
                 v-model:value={state.searchFrom.subjectId}
+                onUpdate:value={() => onSearch()}
               />
             </div>
             <SearchInput
               {...{ placeholder: '请输入学生姓名/手机号' }}
               class={styles.searchInput}
               searchWord={state.searchFrom.keyword}
-              onChangeValue={(val: string) =>
-                (state.searchFrom.keyword = val)
-              }></SearchInput>
+              onChangeValue={(val: string) => {
+                state.searchFrom.keyword = val;
+              }}
+              onClear={() => {
+                state.searchFrom.keyword = '';
+                onSearch();
+              }}
+              onKeyup={(e: KeyboardEvent) => {
+                if (e.code === 'Enter') {
+                  onSearch();
+                }
+              }}></SearchInput>
           </div>
 
           <div class={styles.studentSection}>
             <div class={styles.checkboxAll}>
-              <NCheckbox></NCheckbox>
+              <NCheckbox
+                v-model:checked={state.checkAllStatus}
+                indeterminate={state.indeterminate}
+                onUpdate:checked={(val: any) => {
+                  if (val) {
+                    const ids: any = [];
+                    state.tableList.forEach((item: any) => {
+                      ids.push(item.id);
+                    });
+                    state.checkboxIds = ids;
+                  } else {
+                    state.checkboxIds = [];
+                    state.indeterminate = false;
+                  }
+                  onCheckStudents();
+                }}></NCheckbox>
               <p>
-                全选 <span class={styles.nums}>(120)</span> :
+                全选 <span class={styles.nums}>({state.tableList.length})</span>{' '}
+                :
               </p>
             </div>
           </div>
-          <NScrollbar class={styles.student}>
-            <NCheckboxGroup>
-              <div class={[styles.studentItem, styles.active]}>
-                <NCheckbox></NCheckbox>
-                <div class={styles.studentInfo}>
-                  <NAvatar class={styles.studentImg} />
-                  <div class={styles.studentValue}>
-                    <div class={styles.userInfo}>
-                      <span class={styles.name}>林夏喜</span>
-                      <i class={styles.iconMember}></i>
-                      <span class={styles.className}>三年级1班</span>
+          <NScrollbar
+            class={styles.student}
+            onScroll={(e: any) => {
+              const clientHeight = e.target?.clientHeight;
+              const scrollTop = e.target?.scrollTop;
+              const scrollHeight = e.target?.scrollHeight;
+              // 是否到底,是否加载完
+              if (
+                clientHeight + scrollTop + 20 >= scrollHeight &&
+                !state.finshed &&
+                !state.loading
+              ) {
+                throttledFn();
+              }
+            }}>
+            <NSpin show={state.loading} class={styles.loadingSection}>
+              <NCheckboxGroup
+                v-model:value={state.checkboxIds}
+                onUpdate:value={onCheckStudents}>
+                {state.tableList.map((item: any) => (
+                  <div class={[styles.studentItem]}>
+                    <NCheckbox value={item.id}></NCheckbox>
+                    <div class={styles.studentInfo}>
+                      <NAvatar
+                        src={item.avatar || defultHeade}
+                        class={styles.studentImg}
+                      />
+                      <div class={styles.studentValue}>
+                        <div class={styles.userInfo}>
+                          <span class={styles.name}>{item.nickname}</span>
+                          {item.membership && <i class={styles.iconMember}></i>}
+                          {item.classGroupName && (
+                            <span class={styles.className}>
+                              {item.classGroupName}
+                            </span>
+                          )}
+                        </div>
+                        <div class={styles.phone}>{item.phone}</div>
+                      </div>
                     </div>
-                    <div class={styles.phone}>15527262536</div>
                   </div>
-                </div>
-              </div>
-            </NCheckboxGroup>
+                ))}
+              </NCheckboxGroup>
+              {state.tableList.length <= 0 && !state.loading && <TheEmpty />}
+            </NSpin>
           </NScrollbar>
         </div>
 
         <div class={styles.selectStudentGroup}>
           <div class={styles.selectCount}>
-            当前选中 <span>(40) </span>:
+            当前选中 <span>({state.selectStudents.length}) </span>:
           </div>
           <div class={styles.searchSection}>
             <SearchInput
               {...{ placeholder: '请输入学生姓名' }}
               class={styles.searchInput}
-              // searchWord={state.searchForm.keyword}
-              // onChangeValue={(val: string) =>
-              //   (state.searchForm.keyword = val)
-              // }
-            ></SearchInput>
+              searchWord={state.selectKeyword}
+              onChangeValue={(val: string) => {
+                state.selectKeyword = val;
+
+                state.selectStudents.forEach((item: any) => {
+                  if (item.nickname?.indexOf(val) === -1) {
+                    item.hide = true;
+                  } else {
+                    item.hide = false;
+                  }
+                });
+              }}></SearchInput>
           </div>
           <NScrollbar class={styles.student}>
-            <div class={[styles.studentItem, styles.active]}>
-              <div class={styles.studentInfo}>
-                <NAvatar class={styles.studentImg} />
-                <span class={styles.name}>林夏喜</span>
+            {state.selectStudents.map((student: any) => (
+              <div class={[styles.studentItem, student.hide && styles.hide]}>
+                <div class={styles.studentInfo}>
+                  <NAvatar
+                    src={student.avatar || defultHeade}
+                    class={styles.studentImg}
+                  />
+                  <span class={styles.name}>{student.nickname}</span>
+                </div>
+                <i
+                  class={styles.iconClose}
+                  onClick={() => onRemove(student)}></i>
               </div>
-              <i class={styles.iconClose}></i>
-            </div>
+            ))}
+            {selectStudentEmpty.value && <TheEmpty />}
           </NScrollbar>
 
           <NSpace justify="end" class={styles.btnGroup}>
-            <NButton type="default">取消</NButton>
-            <NButton type="primary">保存</NButton>
+            <NButton type="default" onClick={() => emit('close')}>
+              取消
+            </NButton>
+            <NButton type="primary" onClick={onSave}>
+              保存
+            </NButton>
           </NSpace>
         </div>
       </div>

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

@@ -213,6 +213,11 @@
     color: #1677FF;
     text-decoration: underline;
     cursor: pointer;
+
+    &.disabled {
+      color: #bbb;
+      cursor: not-allowed;
+    }
   }
 
   .firstName {

+ 30 - 52
src/views/prepare-lessons/components/lesson-main/train/index.tsx

@@ -4,7 +4,8 @@ import {
   reactive,
   watch,
   ref,
-  PropType
+  PropType,
+  onUnmounted
 } from 'vue';
 import styles from './index.module.less';
 import {
@@ -47,6 +48,21 @@ export default defineComponent({
     cardType: {
       type: String as PropType<'' | 'homeworkRecord'>,
       default: ''
+    },
+    /** 编辑编号  - 目前从上传传 */
+    classGroupId: {
+      type: String,
+      default: ''
+    },
+    /** 编辑编号  - 目前从上传传 */
+    coursewareKnowledgeDetailId: {
+      type: String,
+      default: ''
+    },
+    /** 编辑编号  - 目前从上传传 */
+    courseScheduleId: {
+      type: String,
+      default: ''
     }
   },
   emits: ['change'],
@@ -67,40 +83,16 @@ export default defineComponent({
       editStatus: false,
       editItem: {} as any,
       removeIds: [] as any, // 临时删除的编号
-      removeVisiable: false,
+
       removeVisiable1: false
     });
     // const showGuide = ref(false);
-    // 完成编辑
-    const onOverEdit = async () => {
-      try {
-        // 保存课件
-        await lessonPreTrainingBatchSave({
-          coursewareKnowledgeDetailId: prepareStore.getSelectKey,
-          subjectId: prepareStore.getSubjectId,
-          lessonPreTrainingDetails: forms.trainList
-        });
-
-        forms.drag = false;
-        message.success('编辑成功');
-        forms.removeVisiable = false;
-        prepareStore.setCoursewareList(forms.trainList);
-        prepareStore.setIsEditTrain(false);
-        // 重置临时删除编号
-        forms.removeIds = [];
-        await getList();
-      } catch {
-        //
-      }
-      // }
-      // });
-    };
-
     // 获取列表
     const getList = async () => {
       forms.loadingStatus = true;
       try {
         // 判断是否有选择对应的课件
+        console.log(props.lessonPreTraining, 'props.lessonPreTraining');
         if (!props.lessonPreTraining?.id) return (forms.loadingStatus = false);
         const { data } = await lessonPreTrainingV2Detail({
           id: props.lessonPreTraining?.id
@@ -206,12 +198,12 @@ export default defineComponent({
         });
         await lessonPreTrainingV2Save({
           title: forms.title,
-          coursewareKnowledgeDetailId: prepareStore.getSelectKey,
+          id: props.lessonPreTraining?.id,
+          coursewareKnowledgeDetailId:
+            props.coursewareKnowledgeDetailId || prepareStore.getSelectKey,
           lessonPreTrainingDetails
         });
         message.success('保存预设成功');
-        // forms.removeVisiable = true;
-        // forms.drag = false;
         prepareStore.setIsEditTrain(false);
         forms.removeIds = [];
         // getList();
@@ -224,8 +216,6 @@ export default defineComponent({
 
     onMounted(async () => {
       await getList();
-
-      console.log(prepareStore.getTrainList, 'prepareStore.getTrainList');
       // 动态添加数据
       eventGlobal.on('onTrainAddItem', (item: any) => {
         forms.drag = true;
@@ -235,12 +225,17 @@ export default defineComponent({
         prepareStore.setTrainList(forms.trainList);
       });
     });
+
+    onUnmounted(() => {
+      forms.trainList = [];
+      prepareStore.setTrainList([]);
+    });
     return () => (
       <div class={styles.coursewareModal}>
         <div class={styles.btnGroup}>
           <NSpace>
             <div class={styles.btnItem}>
-              <span class={styles.btnTitle}>声部:</span>
+              <span class={styles.btnTitle}>标题:</span>
               <NInput
                 placeholder={'请输入标题'}
                 v-model:value={forms.title}
@@ -489,6 +484,8 @@ export default defineComponent({
           title={'布置作业'}
           blockScroll={false}>
           <AssignHomework
+            classGroupId={props.classGroupId}
+            courseScheduleId={props.courseScheduleId}
             item={{
               title: forms.title,
               lessonPreTrainingDetails: forms.trainList
@@ -508,25 +505,6 @@ export default defineComponent({
         {/* {showGuide.value ? <Trainguide></Trainguide> : null} */}
 
         <NModal
-          v-model:show={forms.removeVisiable}
-          preset="card"
-          class={['modalTitle', styles.removeVisiable]}
-          title={'提示'}>
-          <div class={styles.studentRemove}>
-            <p>是否完成编辑?</p>
-
-            <NSpace class={styles.btnGroupModal} justify="center">
-              <NButton round type="primary" onClick={onOverEdit}>
-                确定
-              </NButton>
-              <NButton round onClick={() => (forms.removeVisiable = false)}>
-                取消
-              </NButton>
-            </NSpace>
-          </div>
-        </NModal>
-
-        <NModal
           v-model:show={forms.removeVisiable1}
           preset="card"
           class={['modalTitle', styles.removeVisiable1]}

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

@@ -70,6 +70,7 @@ export default defineComponent({
       },
       searchGroup: {
         name: '',
+        type: 'MUSIC', //
         musicSheetCategoriesId: '',
         sourceType: formatType(props.type),
         status: 1,

+ 26 - 12
src/views/prepare-lessons/model/work-section/index.tsx

@@ -9,6 +9,16 @@ export default defineComponent({
     item: {
       type: Object,
       default: () => ({})
+    },
+    /** 是否显示编辑按钮 */
+    hideEdit: {
+      type: Boolean,
+      default: false
+    },
+    /** 是否显示编辑按钮 */
+    hideDelete: {
+      type: Boolean,
+      default: false
     }
   },
   emits: ['edit', 'delete', 'confirm', 'editTitle'],
@@ -49,18 +59,22 @@ export default defineComponent({
             {props.item.eTitle && <p class={styles.p2}>{props.item.eTitle}</p>}
           </div>
           <NSpace class={styles.btnGroup} justify="center" vertical>
-            <NButton
-              type="default"
-              class={[styles.btnSize]}
-              onClick={() => emit('edit')}>
-              编辑
-            </NButton>
-            <NButton
-              type="default"
-              class={[styles.btnSize]}
-              onClick={() => emit('delete')}>
-              删除
-            </NButton>
+            {!props.hideEdit && (
+              <NButton
+                type="default"
+                class={[styles.btnSize]}
+                onClick={() => emit('edit')}>
+                编辑
+              </NButton>
+            )}
+            {!props.hideDelete && (
+              <NButton
+                type="default"
+                class={[styles.btnSize]}
+                onClick={() => emit('delete')}>
+                删除
+              </NButton>
+            )}
           </NSpace>
         </div>
       </div>

+ 128 - 0
src/views/studentList/modals/comment-work/index.module.less

@@ -0,0 +1,128 @@
+.commonWork {
+  width: 473px;
+  // height: 309px;
+  background: #FFFFFF;
+  border-radius: 16px;
+  position: relative;
+  padding: 0 30px 35px;
+
+  .downMoveBg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 473px;
+    height: 121px;
+  }
+
+  .dingPng {
+    width: 162px;
+    height: 98px;
+    position: absolute;
+    left: 50%;
+    margin-left: -81px;
+    top: -49px;
+    z-index: 100;
+  }
+
+  .closeAble {
+    cursor: pointer;
+    width: 25px;
+    height: 25px;
+    position: absolute;
+    top: 18px;
+    right: 20px;
+  }
+
+  h2 {
+    margin-top: 64px;
+    height: 33px;
+    font-size: 24px;
+    font-family: PingFangSC-Semibold, PingFang SC;
+    font-weight: 600;
+    color: #000000;
+    line-height: 33px;
+    text-align: center;
+    margin-bottom: 15px;
+  }
+
+  // p {
+  //   font-size: 18px;
+  //   font-family: PingFangSC-Regular, PingFang SC;
+  //   font-weight: 400;
+  //   color: #777777;
+  //   line-height: 30px;
+  // }
+
+  .header {
+    display: flex;
+    align-items: center;
+    padding: 0 0 13px;
+
+    .navatar {
+      width: 60px;
+      height: 60px;
+      border-radius: 50%;
+      padding: 2px;
+      border: 1px solid #198CFE;
+      margin-right: 15px;
+      flex-shrink: 0;
+      background-color: #fff !important;
+
+      :global {
+        img {
+          border-radius: 50%;
+        }
+      }
+    }
+
+    .userInfo {
+      padding-top: 4px;
+      flex: 1;
+
+      h3 {
+        font-size: max(17px, 14Px);
+        font-weight: 600;
+        color: #131415;
+        line-height: 28px;
+      }
+
+      p {
+        font-size: max(16px, 12Px);
+        color: #777777;
+        line-height: 28px;
+      }
+    }
+  }
+
+
+  .textarea {
+    margin-top: 8px;
+    background-color: #F2F4F7 !important;
+    border-radius: 10px;
+    --n-border: 1px solid #F2F4F7 !important;
+  }
+
+  :global {
+    .n-button {
+      min-width: 156px;
+    }
+  }
+
+  .submitAppBtn {
+    // line-height: 45px;
+    background: linear-gradient(305deg, #40C8FF 0%, #3192FF 100%);
+    border-radius: 24px;
+    border: none;
+
+    :global {
+      .n-button__border {
+        display: none;
+      }
+
+      .n-button__state-border {
+        display: none;
+      }
+    }
+
+  }
+}

+ 56 - 0
src/views/studentList/modals/comment-work/index.tsx

@@ -0,0 +1,56 @@
+import { defineComponent } from 'vue';
+import styles from './index.module.less';
+import { NAvatar, NButton, NInput, NSpace } from 'naive-ui';
+import commentBg from '../images/common-bg.png';
+import commentTop from '../images/common-top.png';
+import iconClose from '../images/icon-close-line.png';
+import defultHeade from '@/components/layout/images/teacherIcon.png';
+
+export default defineComponent({
+  name: 'commit-work',
+  emits: ['close'],
+  setup(props, { emit }) {
+    return () => (
+      <div class={styles.commonWork}>
+        <img src={commentTop} class={styles.dingPng} alt="" />
+        <img src={commentBg} class={styles.downMoveBg} alt="" />
+        <img
+          src={iconClose}
+          class={styles.closeAble}
+          onClick={() => {
+            emit('close');
+          }}
+          alt=""
+        />
+        <h2>点评作业</h2>
+
+        <div class={styles.header}>
+          <NAvatar class={styles.navatar} round src={defultHeade} />
+          <div class={styles.userInfo}>
+            <h3>胡小小</h3>
+            <p>提交时间:2024-01-23</p>
+          </div>
+        </div>
+
+        <NInput
+          class={styles.textarea}
+          type="textarea"
+          rows={10}
+          maxlength={500}
+          showCount
+          autosize={false}
+          placeholder="请输入评语…"
+        />
+
+        <NSpace style={{ padding: '25px 0 0 0' }} justify="center">
+          <NButton round type="default" onClick={() => emit('close')}>
+            取消
+          </NButton>
+          <NButton class={styles.submitAppBtn} round type="primary">
+            确定
+          </NButton>
+        </NSpace>
+      </div>
+    );
+  }
+});

二進制
src/views/studentList/modals/images/common-bg.png


二進制
src/views/studentList/modals/images/common-top.png


二進制
src/views/studentList/modals/images/icon-close-line.png


+ 100 - 73
src/views/studentList/modals/studentTraomomhDetails.tsx

@@ -1,4 +1,11 @@
-import { useMessage, NImage, NScrollbar } from 'naive-ui';
+import {
+  useMessage,
+  NImage,
+  NScrollbar,
+  NSpin,
+  NModal,
+  NButton
+} from 'naive-ui';
 import { defineComponent, onMounted, reactive, ref } from 'vue';
 import styles from '@/views/classList/index.module.less';
 import TrainType from '@/views/attend-class/model/train-type';
@@ -9,6 +16,7 @@ import unqualified from '@/views/classList/images/unqualified.png';
 import { evaluateDifficult } from '/src/utils/contants';
 import dayjs from 'dayjs';
 import { getTrainingStudentDetail } from '../../classList/api';
+import CommentWork from './comment-work';
 export default defineComponent({
   props: {
     activeRow: {
@@ -28,9 +36,8 @@ export default defineComponent({
   emits: ['close'],
 
   setup(props, { emit, expose }) {
-    // const data = reactive({
-    //   uploading: false
-    // });
+    const showModalMask = ref(false);
+    const loading = ref(false);
     const teacherInfo = ref({
       teacherName: '',
       createTime: '',
@@ -64,7 +71,7 @@ export default defineComponent({
       return tList;
     };
     const getTrainingDetail = async (id: any) => {
-      // console.log(id, 'getTrainingDetail');
+      loading.value = true;
       try {
         const res = await getTrainingStudentDetail({
           studentLessonTrainingId: id
@@ -82,7 +89,6 @@ export default defineComponent({
           };
         });
 
-        console.log(arr, 'arr');
         teacherInfo.value = {
           ...res.data,
 
@@ -91,6 +97,7 @@ export default defineComponent({
       } catch (e) {
         console.log(e);
       }
+      loading.value = false;
     };
     expose({ getTrainingDetail });
     onMounted(() => {
@@ -99,77 +106,97 @@ export default defineComponent({
 
     return () => (
       <div class={[styles.trainingDetails]}>
-        <div class={styles.studentList}>
-          <div class={styles.studentHeaderWrap}>
-            <div class={styles.studentHeader}>
-              <div class={styles.studentHeaderBorder}>
-                <NImage
-                  class={styles.studentHeaderImg}
-                  src={
-                    teacherInfo.value.teacherAvatar
-                      ? teacherInfo.value.teacherAvatar
-                      : defultHeade
-                  }
-                  previewDisabled></NImage>
+        <NSpin show={loading.value}>
+          <div class={styles.studentList}>
+            <div class={styles.studentHeaderWrap}>
+              <div class={styles.studentHeader}>
+                <div class={styles.studentHeaderBorder}>
+                  <NImage
+                    class={styles.studentHeaderImg}
+                    src={
+                      teacherInfo.value.teacherAvatar
+                        ? teacherInfo.value.teacherAvatar
+                        : defultHeade
+                    }
+                    previewDisabled></NImage>
+                </div>
               </div>
-            </div>
 
-            <div class={styles.workafterInfo}>
-              <h4>
-                {teacherInfo.value.teacherName}{' '}
-                <div
-                  class={[
-                    styles.workafterInfoDot,
-                    styles.workafterTeacherInfoDot
-                  ]}>
-                  老师
-                </div>
-              </h4>
-              <p>
-                开始时间:
-                {teacherInfo.value.createTime
-                  ? dayjs(new Date(teacherInfo.value.createTime)).format(
-                      'YYYY-MM-DD'
-                    )
-                  : '--'}{' '}
-                | 结束时间:
-                {dayjs(new Date(teacherInfo.value.expireDate)).format(
-                  'YYYY-MM-DD'
-                )}
-              </p>
+              <div class={styles.workafterInfo}>
+                <h4>
+                  {teacherInfo.value.teacherName}{' '}
+                  <div
+                    class={[
+                      styles.workafterInfoDot,
+                      styles.workafterTeacherInfoDot
+                    ]}>
+                    老师
+                  </div>
+                </h4>
+                <p>
+                  开始时间:
+                  {teacherInfo.value.createTime
+                    ? dayjs(new Date(teacherInfo.value.createTime)).format(
+                        'YYYY-MM-DD'
+                      )
+                    : '--'}{' '}
+                  | 结束时间:
+                  {teacherInfo.value.expireDate
+                    ? dayjs(new Date(teacherInfo.value.expireDate)).format(
+                        'YYYY-MM-DD'
+                      )
+                    : '--'}
+                </p>
+              </div>
             </div>
+            {teacherInfo.value.trainingStatus == 'UNSUBMITTED' ? (
+              <NImage
+                previewDisabled
+                class={styles.workStatus}
+                src={noSub}></NImage>
+            ) : null}
+            {teacherInfo.value.trainingStatus == 'SUBMITTED' ? (
+              <NImage
+                previewDisabled
+                class={styles.workStatus}
+                src={unqualified}></NImage>
+            ) : null}
+            {teacherInfo.value.trainingStatus == 'TARGET' ? (
+              <NImage
+                previewDisabled
+                class={styles.workStatus}
+                src={qualified}></NImage>
+            ) : null}
           </div>
-          {teacherInfo.value.trainingStatus == 'UNSUBMITTED' ? (
-            <NImage
-              previewDisabled
-              class={styles.workStatus}
-              src={noSub}></NImage>
-          ) : null}
-          {teacherInfo.value.trainingStatus == 'SUBMITTED' ? (
-            <NImage
-              previewDisabled
-              class={styles.workStatus}
-              src={unqualified}></NImage>
-          ) : null}
-          {teacherInfo.value.trainingStatus == 'TARGET' ? (
-            <NImage
-              previewDisabled
-              class={styles.workStatus}
-              src={qualified}></NImage>
-          ) : null}
-        </div>
-        <NScrollbar style="max-height:400px" trigger="none">
-          <div class={styles.workList}>
-            {teacherInfo.value.studentLessonTrainingDetails.map((item: any) => (
-              <TrainType
-                style={{ marginBottom: '20px' }}
-                isDisabled={true}
-                isDelete={false}
-                isCLassWork={false}
-                item={item}></TrainType>
-            ))}
-          </div>
-        </NScrollbar>
+
+          <NButton
+            onClick={() => (showModalMask.value = true)}
+            class={styles.commentBtnGroup}>
+            <div class={styles.text}>
+              <i class={styles.look}></i>
+              点评作业
+            </div>
+          </NButton>
+
+          <NScrollbar style="max-height:400px" trigger="none">
+            <div class={styles.workList}>
+              {teacherInfo.value.studentLessonTrainingDetails.map(
+                (item: any) => (
+                  <TrainType
+                    style={{ marginBottom: '20px' }}
+                    isDisabled={true}
+                    isDelete={false}
+                    isCLassWork={false}
+                    item={item}></TrainType>
+                )
+              )}
+            </div>
+          </NScrollbar>
+        </NSpin>
+
+        <NModal v-model:show={showModalMask.value}>
+          <CommentWork onClose={() => (showModalMask.value = false)} />
+        </NModal>
       </div>
     );
   }