ソースを参照

修改弹窗点击问题

lex 8 ヶ月 前
コミット
c591e56b98
56 ファイル変更9419 行追加9240 行削除
  1. 384 381
      src/components/Metronome/MetronomeBox.vue
  2. 2 0
      src/components/card-preview/index.tsx
  3. 894 889
      src/components/layout/index.tsx
  4. 502 498
      src/components/layout/layoutTop.tsx
  5. 453 451
      src/components/layout/modals/suggestion-option.tsx
  6. 261 260
      src/components/layout/modals/update-password.tsx
  7. 2 0
      src/components/upload-file/index.tsx
  8. 41 38
      src/state.ts
  9. 135 133
      src/views/attend-class/component/roll-call/pen.tsx
  10. 2 0
      src/views/attend-class/component/tools/pen.tsx
  11. 11 1
      src/views/attend-class/index.tsx
  12. 321 317
      src/views/attend-class/model/class-work/index.tsx
  13. 328 325
      src/views/attend-class/model/train-settings/index.tsx
  14. 3 0
      src/views/attend-class/model/train-type/index.tsx
  15. 3 0
      src/views/classList/components/afterWork.tsx
  16. 2 0
      src/views/classList/components/afterWorkDetail.tsx
  17. 333 331
      src/views/classList/components/classStudent.tsx
  18. 7 0
      src/views/classList/index.tsx
  19. 4 1
      src/views/classList/modals/TrainingDetails.tsx
  20. 2 0
      src/views/classList/work-item/index.tsx
  21. 628 620
      src/views/home/index copy.tsx
  22. 324 318
      src/views/home/index.tsx
  23. 2 0
      src/views/homework-record/detail/index.tsx
  24. 3 1
      src/views/homework-record/index.tsx
  25. 257 256
      src/views/login/components/codeLogin.tsx
  26. 2 1
      src/views/login/index.tsx
  27. 2 0
      src/views/natural-resources/components/my-collect/index.tsx
  28. 4 0
      src/views/natural-resources/components/my-resources/index.tsx
  29. 458 456
      src/views/natural-resources/components/my-resources/upload-modal/index.tsx
  30. 456 454
      src/views/natural-resources/components/my-resources/upload-modal/upload-file.tsx
  31. 2 0
      src/views/natural-resources/components/share-resources/index.tsx
  32. 132 130
      src/views/notation/index.tsx
  33. 2 0
      src/views/prepare-lessons/components/directory-main/index.tsx
  34. 3 0
      src/views/prepare-lessons/components/directory-main/select-lessonware/index.tsx
  35. 6 1
      src/views/prepare-lessons/components/lesson-main/courseware-presets/index.tsx
  36. 7 1
      src/views/prepare-lessons/components/lesson-main/courseware/addCourseware.tsx
  37. 962 959
      src/views/prepare-lessons/components/lesson-main/courseware/index.tsx
  38. 301 297
      src/views/prepare-lessons/components/lesson-main/train-presets/index.tsx
  39. 2 0
      src/views/prepare-lessons/components/lesson-main/train/assign-homework.tsx
  40. 5 0
      src/views/prepare-lessons/components/lesson-main/train/index.tsx
  41. 2 0
      src/views/prepare-lessons/components/resource-main/components/select-music/index.tsx
  42. 4 0
      src/views/prepare-lessons/components/resource-main/index.tsx
  43. 9 0
      src/views/prepare-lessons/model/add-other-source/index.tsx
  44. 2 1
      src/views/prepare-lessons/model/attend-class/index.tsx
  45. 308 297
      src/views/prepare-lessons/model/the-create/index.tsx
  46. 117 115
      src/views/preview-window/index.tsx
  47. 308 306
      src/views/setting/components/personInfo.tsx
  48. 4 0
      src/views/setting/components/schoolInfo/index.tsx
  49. 254 253
      src/views/setting/modal/forgotPassword.tsx
  50. 322 320
      src/views/studentList/components/baseInfo.tsx
  51. 2 0
      src/views/studentList/components/evaluationRecords.tsx
  52. 303 301
      src/views/studentList/components/studentAfterWork.tsx
  53. 528 526
      src/views/studentList/index.tsx
  54. 2 0
      src/views/studentList/modals/comment-work/index.tsx
  55. 4 1
      src/views/studentList/modals/studentTraomomhDetails.tsx
  56. 2 1
      src/views/xiaoku-music/index.tsx

+ 384 - 381
src/components/Metronome/MetronomeBox.vue

@@ -1,381 +1,384 @@
-<template>
-  <div v-if="isDrag" class="metronomeMinConMask"></div>
-  <div
-    class="metronomeMinCon"
-    :class="[metronomeMinConBoxClass, metronomeShow && 'metronomeMinConHide']"
-    :style="metronomeMinConDragData.styleDrag.value"
-    @mousedown="hanldeMetronomeMinmousedown"
-    v-if="metronomeShow || windowMet"
-  >
-    <div class="topMetronomeMin">
-      <img
-        class="zhen"
-        :style="{
-          '--rotateWagTime': rotateWagTime + 's'
-        }"
-        :class="[
-          playState === 'play' && 'playWagAnimation' + beatSymbolOpt[beatSymbol]
-        ]"
-        src="./imgs/zhen.png"
-      />
-      <img class="bai" src="./imgs/bai.png" />
-    </div>
-    <div class="bomMetronomeMin">
-      <img class="setting" @click="handleSetting" src="./imgs/setting.png" />
-      <img
-        class="play"
-        v-if="playState === 'pause'"
-        @click="
-          () => {
-            metronomeDom?.startPlay();
-          }
-        "
-        src="./imgs/paly.png"
-      />
-      <img
-        class="pause"
-        @click="
-          () => {
-            metronomeDom?.pausePlay();
-          }
-        "
-        v-else
-        src="./imgs/pause.png"
-      />
-      <img class="close" @click="handleCloseMet" src="./imgs/close.png" />
-    </div>
-  </div>
-  <NModal
-    :style="{
-      ...dragStyle,
-      '--transformOrigin': transformOrigin
-    }"
-    class="metronomeNModal"
-    v-model:show="metronomeShow"
-    :class="[dragClass, windowMet ? 'transformOrigin' : '']"
-    :display-directive="'show'"
-    @mask-click="handleCloseMet"
-    @esc="handleCloseMet"
-    @after-enter="
-      () => {
-        animationEnds = true;
-      }
-    "
-    @after-leave="
-      () => {
-        animationEnds = false;
-      }
-    "
-  >
-    <div>
-      <div class="topDragDom"></div>
-      <Metronome
-        v-if="windowMet || metronomeShow || animationEnds"
-        ref="metronomeDom"
-        @closeMet="handleCloseMet"
-        @windowMet="handleWindowMet"
-        @playStateChange="handlePlayStateChange"
-      ></Metronome>
-    </div>
-  </NModal>
-</template>
-
-<script setup lang="ts">
-import { NModal } from 'naive-ui';
-import { computed, ref, watch, onMounted, onUnmounted, nextTick } from 'vue';
-import Metronome from './Metronome.vue';
-import { useUserStore } from '@/store/modules/users';
-import useDrag from '@/hooks/useDrag';
-
-const props = defineProps<{
-  modelValue: boolean;
-  dragClass: string;
-  dragStyle: Record<string, any>;
-}>();
-const emits = defineEmits<{
-  (e: 'update:modelValue', value: boolean): void;
-}>();
-const animationEnds = ref(true); //防止动画没结束 窗口就消失了
-const metronomeDom = ref<InstanceType<typeof Metronome>>();
-/* 窗口化 */
-const windowMet = ref(false);
-const metronomeShow = computed({
-  get() {
-    return props.modelValue;
-  },
-  set(value) {
-    emits('update:modelValue', value);
-  }
-});
-watch(metronomeShow, () => {
-  if (metronomeShow.value) {
-    windowMet.value = false;
-  }
-});
-const playState = ref<'play' | 'pause'>('pause');
-function handlePlayStateChange(state: 'play' | 'pause') {
-  if (state === 'play') {
-    rotateWagTime.value = parseFloat(
-      (60 / metronomeDom.value!.speedNum).toFixed(4)
-    );
-    beatSymbol.value = metronomeDom.value!.beatSymbol;
-  }
-  playState.value = state;
-}
-const beatSymbol = ref('1');
-const beatSymbolOpt = {
-  '1': '1',
-  '0.5-0.5': '2',
-  '0.3333333-0.3333333-0.3333333': '3',
-  '0.25-0.25-0.25-0.25': '4',
-  '0.6666666-0.3333333': '5',
-  '0.75-0.25': '6',
-  '0.5-0.25-0.25': '7'
-};
-const rotateWagTime = ref(60 / 90);
-onMounted(() => {
-  document.addEventListener('visibilitychange', documentHidePause);
-});
-onUnmounted(() => {
-  document.removeEventListener('visibilitychange', documentHidePause);
-});
-function documentHidePause() {
-  if (document.visibilityState === 'hidden') {
-    metronomeDom.value?.pausePlay();
-  }
-}
-function handleCloseMet() {
-  metronomeShow.value = false;
-  windowMet.value = false;
-  playState.value = 'pause';
-}
-function handleWindowMet() {
-  windowMet.value = true;
-  nextTick(() => {
-    computedTransformOrigin();
-    metronomeShow.value = false;
-  });
-}
-function handleSetting() {
-  metronomeShow.value = true;
-}
-/* 指针拖动 */
-const users = useUserStore();
-const metronomeMinConBoxClass = 'metronomeMinConBoxClass_drag';
-const metronomeMinConDragData = useDrag(
-  [`${metronomeMinConBoxClass}`],
-  metronomeMinConBoxClass,
-  windowMet,
-  users.info.id
-);
-const transformOrigin = ref('initial');
-function computedTransformOrigin() {
-  const dragDom = document.querySelector(`.${props.dragClass}`) as HTMLElement;
-  const dragRect = dragDom.getBoundingClientRect();
-  const metMinLeft =
-    metronomeMinConDragData.pos.value.left === -1
-      ? 12 //初始化时候窗口的left
-      : metronomeMinConDragData.pos.value.left;
-  const metMinTop =
-    metronomeMinConDragData.pos.value.top === -1
-      ? document.body.clientHeight - 81 - 10 //初始化时候窗口的高
-      : metronomeMinConDragData.pos.value.top;
-  const dragLeft = dragRect.left;
-  const dragTop = dragRect.top;
-  transformOrigin.value = `${metMinLeft - dragLeft}Px ${metMinTop - dragTop}Px`;
-}
-const isDrag = ref(false); // 拖动metronomeMin的时候防止iframe
-function hanldeMetronomeMinmousedown() {
-  isDrag.value = true;
-  document.addEventListener('mouseup', onMouseup);
-  function onMouseup() {
-    isDrag.value = false;
-    document.removeEventListener('mouseup', onMouseup);
-  }
-}
-</script>
-
-<style lang="less" scoped>
-.metronomeMinConMask {
-  position: fixed;
-  width: 100vw;
-  height: 100vh;
-  top: 0;
-  left: 0;
-  z-index: 9;
-}
-.metronomeMinCon {
-  position: fixed;
-  left: 12Px;
-  bottom: 10Px;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  z-index: 10;
-  height: 81Px;
-  &.metronomeMinConHide {
-    visibility: hidden;
-  }
-  .topMetronomeMin {
-    position: relative;
-    display: flex;
-    pointer-events: none;
-    .bai {
-      width: 37Px;
-      height: 46Px;
-    }
-    .zhen {
-      width: 13Px;
-      height: 35Px;
-      position: absolute;
-      bottom: 8Px;
-      left: 50%;
-      transform: translateX(-50%);
-      transform-origin: 50% 100%;
-      &.playWagAnimation1 {
-        animation: rotateWag1 var(--rotateWagTime) linear infinite alternate;
-      }
-      @keyframes rotateWag1 {
-        0% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-        100% {
-          transform: translateX(-50%) rotate(90deg);
-        }
-      }
-      &.playWagAnimation2 {
-        animation: rotateWag2 var(--rotateWagTime) linear infinite;
-      }
-      @keyframes rotateWag2 {
-        0% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-        50% {
-          transform: translateX(-50%) rotate(90deg);
-        }
-        100% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-      }
-      &.playWagAnimation3 {
-        animation: rotateWag3 var(--rotateWagTime) linear infinite alternate;
-      }
-      @keyframes rotateWag3 {
-        0% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-        33.33% {
-          transform: translateX(-50%) rotate(90deg);
-        }
-        66.66% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-        100% {
-          transform: translateX(-50%) rotate(90deg);
-        }
-      }
-      &.playWagAnimation4 {
-        animation: rotateWag4 var(--rotateWagTime) linear infinite;
-      }
-      @keyframes rotateWag4 {
-        0% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-        25% {
-          transform: translateX(-50%) rotate(90deg);
-        }
-        50% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-        75% {
-          transform: translateX(-50%) rotate(90deg);
-        }
-        100% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-      }
-      &.playWagAnimation5 {
-        animation: rotateWag5 var(--rotateWagTime) linear infinite;
-      }
-      @keyframes rotateWag5 {
-        0% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-        66.66% {
-          transform: translateX(-50%) rotate(90deg);
-        }
-        100% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-      }
-      &.playWagAnimation6 {
-        animation: rotateWag6 var(--rotateWagTime) linear infinite;
-      }
-      @keyframes rotateWag6 {
-        0% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-        75% {
-          transform: translateX(-50%) rotate(90deg);
-        }
-        100% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-      }
-      &.playWagAnimation7 {
-        animation: rotateWag7 calc(var(--rotateWagTime) * 2) linear infinite;
-      }
-      @keyframes rotateWag7 {
-        0% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-        25% {
-          transform: translateX(-50%) rotate(90deg);
-        }
-        37.5% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-        50% {
-          transform: translateX(-50%) rotate(90deg);
-        }
-        75% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-        87.5% {
-          transform: translateX(-50%) rotate(90deg);
-        }
-        100% {
-          transform: translateX(-50%) rotate(-90deg);
-        }
-      }
-    }
-  }
-  .bomMetronomeMin {
-    margin-top: 10Px;
-    display: flex;
-    & > img {
-      cursor: pointer;
-      width: 34Px;
-      height: 25Px;
-    }
-    .play,
-    .pause {
-      margin: 0 4Px;
-    }
-  }
-}
-.metronomeNModal {
-  position: relative;
-  box-shadow: initial;
-  &.transformOrigin {
-    transform-origin: var(--transformOrigin) !important;
-  }
-  .topDragDom {
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 40Px;
-  }
-}
-</style>
+<template>
+  <div v-if="isDrag" class="metronomeMinConMask"></div>
+  <div
+    class="metronomeMinCon"
+    :class="[metronomeMinConBoxClass, metronomeShow && 'metronomeMinConHide']"
+    :style="metronomeMinConDragData.styleDrag.value"
+    @mousedown="hanldeMetronomeMinmousedown"
+    v-if="metronomeShow || windowMet"
+  >
+    <div class="topMetronomeMin">
+      <img
+        class="zhen"
+        :style="{
+          '--rotateWagTime': rotateWagTime + 's'
+        }"
+        :class="[
+          playState === 'play' && 'playWagAnimation' + beatSymbolOpt[beatSymbol]
+        ]"
+        src="./imgs/zhen.png"
+      />
+      <img class="bai" src="./imgs/bai.png" />
+    </div>
+    <div class="bomMetronomeMin">
+      <img class="setting" @click="handleSetting" src="./imgs/setting.png" />
+      <img
+        class="play"
+        v-if="playState === 'pause'"
+        @click="
+          () => {
+            metronomeDom?.startPlay();
+          }
+        "
+        src="./imgs/paly.png"
+      />
+      <img
+        class="pause"
+        @click="
+          () => {
+            metronomeDom?.pausePlay();
+          }
+        "
+        v-else
+        src="./imgs/pause.png"
+      />
+      <img class="close" @click="handleCloseMet" src="./imgs/close.png" />
+    </div>
+  </div>
+  <NModal
+    :style="{
+      ...dragStyle,
+      '--transformOrigin': transformOrigin
+    }"
+    class="metronomeNModal"
+    v-model:show="metronomeShow"
+    :class="[dragClass, windowMet ? 'transformOrigin' : '']"
+    :display-directive="'show'"
+    :mask-closable="modalClickMask"
+    @mask-click="handleCloseMet"
+    @esc="handleCloseMet"
+    @after-enter="
+      () => {
+        animationEnds = true;
+      }
+    "
+    @after-leave="
+      () => {
+        animationEnds = false;
+      }
+    "
+  >
+    <div>
+      <div class="topDragDom"></div>
+      <Metronome
+        v-if="windowMet || metronomeShow || animationEnds"
+        ref="metronomeDom"
+        @closeMet="handleCloseMet"
+        @windowMet="handleWindowMet"
+        @playStateChange="handlePlayStateChange"
+      ></Metronome>
+    </div>
+  </NModal>
+</template>
+
+<script setup lang="ts">
+import { NModal } from 'naive-ui';
+import { computed, ref, watch, onMounted, onUnmounted, nextTick } from 'vue';
+import Metronome from './Metronome.vue';
+import { useUserStore } from '@/store/modules/users';
+import useDrag from '@/hooks/useDrag';
+
+const modalClickMask = false;
+
+const props = defineProps<{
+  modelValue: boolean;
+  dragClass: string;
+  dragStyle: Record<string, any>;
+}>();
+const emits = defineEmits<{
+  (e: 'update:modelValue', value: boolean): void;
+}>();
+const animationEnds = ref(true); //防止动画没结束 窗口就消失了
+const metronomeDom = ref<InstanceType<typeof Metronome>>();
+/* 窗口化 */
+const windowMet = ref(false);
+const metronomeShow = computed({
+  get() {
+    return props.modelValue;
+  },
+  set(value) {
+    emits('update:modelValue', value);
+  }
+});
+watch(metronomeShow, () => {
+  if (metronomeShow.value) {
+    windowMet.value = false;
+  }
+});
+const playState = ref<'play' | 'pause'>('pause');
+function handlePlayStateChange(state: 'play' | 'pause') {
+  if (state === 'play') {
+    rotateWagTime.value = parseFloat(
+      (60 / metronomeDom.value!.speedNum).toFixed(4)
+    );
+    beatSymbol.value = metronomeDom.value!.beatSymbol;
+  }
+  playState.value = state;
+}
+const beatSymbol = ref('1');
+const beatSymbolOpt = {
+  '1': '1',
+  '0.5-0.5': '2',
+  '0.3333333-0.3333333-0.3333333': '3',
+  '0.25-0.25-0.25-0.25': '4',
+  '0.6666666-0.3333333': '5',
+  '0.75-0.25': '6',
+  '0.5-0.25-0.25': '7'
+};
+const rotateWagTime = ref(60 / 90);
+onMounted(() => {
+  document.addEventListener('visibilitychange', documentHidePause);
+});
+onUnmounted(() => {
+  document.removeEventListener('visibilitychange', documentHidePause);
+});
+function documentHidePause() {
+  if (document.visibilityState === 'hidden') {
+    metronomeDom.value?.pausePlay();
+  }
+}
+function handleCloseMet() {
+  metronomeShow.value = false;
+  windowMet.value = false;
+  playState.value = 'pause';
+}
+function handleWindowMet() {
+  windowMet.value = true;
+  nextTick(() => {
+    computedTransformOrigin();
+    metronomeShow.value = false;
+  });
+}
+function handleSetting() {
+  metronomeShow.value = true;
+}
+/* 指针拖动 */
+const users = useUserStore();
+const metronomeMinConBoxClass = 'metronomeMinConBoxClass_drag';
+const metronomeMinConDragData = useDrag(
+  [`${metronomeMinConBoxClass}`],
+  metronomeMinConBoxClass,
+  windowMet,
+  users.info.id
+);
+const transformOrigin = ref('initial');
+function computedTransformOrigin() {
+  const dragDom = document.querySelector(`.${props.dragClass}`) as HTMLElement;
+  const dragRect = dragDom.getBoundingClientRect();
+  const metMinLeft =
+    metronomeMinConDragData.pos.value.left === -1
+      ? 12 //初始化时候窗口的left
+      : metronomeMinConDragData.pos.value.left;
+  const metMinTop =
+    metronomeMinConDragData.pos.value.top === -1
+      ? document.body.clientHeight - 81 - 10 //初始化时候窗口的高
+      : metronomeMinConDragData.pos.value.top;
+  const dragLeft = dragRect.left;
+  const dragTop = dragRect.top;
+  transformOrigin.value = `${metMinLeft - dragLeft}Px ${metMinTop - dragTop}Px`;
+}
+const isDrag = ref(false); // 拖动metronomeMin的时候防止iframe
+function hanldeMetronomeMinmousedown() {
+  isDrag.value = true;
+  document.addEventListener('mouseup', onMouseup);
+  function onMouseup() {
+    isDrag.value = false;
+    document.removeEventListener('mouseup', onMouseup);
+  }
+}
+</script>
+
+<style lang="less" scoped>
+.metronomeMinConMask {
+  position: fixed;
+  width: 100vw;
+  height: 100vh;
+  top: 0;
+  left: 0;
+  z-index: 9;
+}
+.metronomeMinCon {
+  position: fixed;
+  left: 12px;
+  bottom: 10px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  z-index: 10;
+  height: 81px;
+  &.metronomeMinConHide {
+    visibility: hidden;
+  }
+  .topMetronomeMin {
+    position: relative;
+    display: flex;
+    pointer-events: none;
+    .bai {
+      width: 37px;
+      height: 46px;
+    }
+    .zhen {
+      width: 13px;
+      height: 35px;
+      position: absolute;
+      bottom: 8px;
+      left: 50%;
+      transform: translateX(-50%);
+      transform-origin: 50% 100%;
+      &.playWagAnimation1 {
+        animation: rotateWag1 var(--rotateWagTime) linear infinite alternate;
+      }
+      @keyframes rotateWag1 {
+        0% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+        100% {
+          transform: translateX(-50%) rotate(90deg);
+        }
+      }
+      &.playWagAnimation2 {
+        animation: rotateWag2 var(--rotateWagTime) linear infinite;
+      }
+      @keyframes rotateWag2 {
+        0% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+        50% {
+          transform: translateX(-50%) rotate(90deg);
+        }
+        100% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+      }
+      &.playWagAnimation3 {
+        animation: rotateWag3 var(--rotateWagTime) linear infinite alternate;
+      }
+      @keyframes rotateWag3 {
+        0% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+        33.33% {
+          transform: translateX(-50%) rotate(90deg);
+        }
+        66.66% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+        100% {
+          transform: translateX(-50%) rotate(90deg);
+        }
+      }
+      &.playWagAnimation4 {
+        animation: rotateWag4 var(--rotateWagTime) linear infinite;
+      }
+      @keyframes rotateWag4 {
+        0% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+        25% {
+          transform: translateX(-50%) rotate(90deg);
+        }
+        50% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+        75% {
+          transform: translateX(-50%) rotate(90deg);
+        }
+        100% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+      }
+      &.playWagAnimation5 {
+        animation: rotateWag5 var(--rotateWagTime) linear infinite;
+      }
+      @keyframes rotateWag5 {
+        0% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+        66.66% {
+          transform: translateX(-50%) rotate(90deg);
+        }
+        100% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+      }
+      &.playWagAnimation6 {
+        animation: rotateWag6 var(--rotateWagTime) linear infinite;
+      }
+      @keyframes rotateWag6 {
+        0% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+        75% {
+          transform: translateX(-50%) rotate(90deg);
+        }
+        100% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+      }
+      &.playWagAnimation7 {
+        animation: rotateWag7 calc(var(--rotateWagTime) * 2) linear infinite;
+      }
+      @keyframes rotateWag7 {
+        0% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+        25% {
+          transform: translateX(-50%) rotate(90deg);
+        }
+        37.5% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+        50% {
+          transform: translateX(-50%) rotate(90deg);
+        }
+        75% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+        87.5% {
+          transform: translateX(-50%) rotate(90deg);
+        }
+        100% {
+          transform: translateX(-50%) rotate(-90deg);
+        }
+      }
+    }
+  }
+  .bomMetronomeMin {
+    margin-top: 10px;
+    display: flex;
+    & > img {
+      cursor: pointer;
+      width: 34px;
+      height: 25px;
+    }
+    .play,
+    .pause {
+      margin: 0 4px;
+    }
+  }
+}
+.metronomeNModal {
+  position: relative;
+  box-shadow: initial;
+  &.transformOrigin {
+    transform-origin: var(--transformOrigin) !important;
+  }
+  .topDragDom {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 40px;
+  }
+}
+</style>

+ 2 - 0
src/components/card-preview/index.tsx

@@ -13,6 +13,7 @@ import ListenModal from './listen-modal';
 import useDrag from '@/hooks/useDrag';
 import Dragbom from '@/hooks/useDrag/dragbom';
 import { useUserStore } from '@/store/modules/users';
+import { modalClickMask } from '/src/state';
 
 export default defineComponent({
   name: 'card-preview',
@@ -85,6 +86,7 @@ export default defineComponent({
     return () => (
       <>
         <NModal
+          maskClosable={modalClickMask}
           style={
             props.from === 'class' ? cardPreviewBoxDragData.styleDrag.value : {}
           }

+ 894 - 889
src/components/layout/index.tsx

@@ -1,889 +1,894 @@
-import {
-  Transition,
-  defineComponent,
-  onMounted,
-  ref,
-  reactive,
-  onUnmounted,
-  watch,
-  computed
-} from 'vue';
-import LayoutSilder from './layoutSilder';
-import LayoutTop from './layoutTop';
-import styles from './index.module.less';
-import { NButton, NImage, NModal, NPopover, NSpace, useDialog } from 'naive-ui';
-import Moveable from 'moveable';
-import toolStartClass from './images/toolStartClass.png';
-import timerMeterClose from './images/close.png';
-import toolbox from './images/toolbox.png';
-import setTimeIcon from './images/setTimeIcon.png';
-import beatIcon from './images/beatIcon.png';
-import toneIcon from './images/toneIcon.png';
-import iconWhiteBorad from './images/icon-whiteborad.png';
-import iconPen from './images/icon-pen.png';
-import iconNote from './images/icon-note.png';
-import beatImage from './images/beatImage.png';
-import toneImage from './images/toneImage.png';
-import setTimeImage from './images/setTimeImage.png';
-import dragingBoxIcon from './images/dragingBoxIcon.png';
-import TimerMeter from '../timerMeter';
-import Metronome from '../Metronome';
-import { useRoute, useRouter } from 'vue-router';
-import { vaildUrl } from '/src/utils/urlUtils';
-import ChioseModal from '/src/views/home/modals/chioseModal';
-import {
-  eventGlobal,
-  iframeDislableKeyboard,
-  px2vw,
-  px2vwH
-} from '@/utils/index';
-import PlaceholderTone from './modals/placeholderTone';
-import { state } from '/src/state';
-import PreviewWindow from '/src/views/preview-window';
-import { fscreen } from '@/utils/index';
-import AttendClass from '/src/views/prepare-lessons/model/attend-class';
-import Pen from '/src/views/attend-class/component/tools/pen';
-import study from '/src/views/home/components/study';
-import TheAuth from '../TheAuth';
-import useDrag from '@/hooks/useDrag';
-import { getGuidanceShow } from '@/hooks/useDrag/useDragGuidance';
-import { useUserStore } from '@/store/modules/users';
-export default defineComponent({
-  name: 'layoutView',
-  setup() {
-    const router = useRouter();
-    const previewModal = ref(false);
-    const previewItem = ref({});
-    const directionType = ref('left');
-    const showClass = ref(false);
-
-    const showAuthMask = ref(false);
-    const showModalBeat = ref(false);
-    const showModalTone = ref(false);
-    const showModalTime = ref(false);
-    const showBoxConent = ref(false);
-    const dialog = useDialog();
-    const boxBoundaryInfo = reactive({
-      isBoundary: false,
-      isBoundaryType: '' as any,
-      mainWidth: '' as any,
-      mainHeight: '' as any,
-      subWidth: '' as any,
-      subHeight: '' as any
-    });
-
-    const classBoundaryInfo = reactive({
-      isBoundary: true,
-      isBoundaryType: 'right' as any,
-      mainWidth: '' as any,
-      mainHeight: '' as any,
-      subWidth: '' as any,
-      subHeight: '' as any
-    });
-    const route = useRoute();
-    const isDragIng = ref(false);
-    const NPopoverRef = ref();
-    const initMoveable = async () => {
-      if (document.querySelector('.wrap')) {
-        const moveable = new Moveable(document.querySelector('.wrap') as any, {
-          target: document.querySelector('#moveNPopover') as any,
-          // If the container is null, the position is fixed. (default: parentElement(document.body))
-          container: document.querySelector('.wrap') as any,
-          // snappable: true,
-          // bounds: {"left":100,"top":100,"right":100,"bottom":100},
-          draggable: true,
-          resizable: false,
-          scalable: false,
-          rotatable: false,
-          warpable: false,
-          pinchable: false, // ["resizable", "scalable", "rotatable"]
-          origin: false,
-          keepRatio: false,
-          // Resize, Scale Events at edges.
-          edge: false,
-          throttleDrag: 0,
-          throttleResize: 0,
-          throttleScale: 0,
-          throttleRotate: 0
-        });
-        moveable
-          // .on('dragStart', ({ target, clientX, clientY }) => {
-          //   console.log('dragStart');
-          // })
-          .on(
-            'drag',
-            ({
-              target,
-              // transform,
-              left,
-              top,
-              right,
-              bottom
-              // beforeDelta,
-              // beforeDist,
-              // delta,
-              // dist,
-              // clientX,
-              // clientY
-            }) => {
-              isDragIng.value = true;
-              if (NPopoverRef.value) {
-                NPopoverRef.value.setShow(false);
-              }
-
-              const subdEl = document.getElementById(
-                `moveNPopover`
-              ) as HTMLDivElement;
-              // console.log(subdEl, "subdEl", "drag");
-              const subdElStyle = getComputedStyle(subdEl, null);
-              const RectInfo = {
-                left: Number(subdElStyle.left.replace('px', '')),
-                top: Number(subdElStyle.top.replace('px', '')),
-                width: Number(subdElStyle.width.replace('px', '')),
-                height: Number(subdElStyle.height.replace('px', ''))
-              };
-              // target.style.transition = ''
-              const mainWidth =
-                parseInt(
-                  window.getComputedStyle(
-                    document.querySelector('.wrap') as Element
-                  ).width
-                ) - RectInfo.width;
-
-              const mainHeight =
-                parseInt(
-                  window.getComputedStyle(
-                    document.querySelector('.wrap') as Element
-                  ).height
-                ) - RectInfo.height;
-              subdEl.style.transition = '';
-              boxBoundaryInfo.isBoundary = false;
-              boxBoundaryInfo.isBoundaryType = '';
-              boxBoundaryInfo.mainHeight = mainHeight;
-              boxBoundaryInfo.mainWidth = mainWidth;
-              boxBoundaryInfo.subWidth = RectInfo.width;
-              boxBoundaryInfo.subHeight = RectInfo.height;
-              if (left < 0) {
-                left = 2;
-                boxBoundaryInfo.isBoundary = true;
-                boxBoundaryInfo.isBoundaryType = 'left';
-              }
-              if (top < 0) {
-                top = 2;
-                boxBoundaryInfo.isBoundary = true;
-                boxBoundaryInfo.isBoundaryType = 'top';
-              }
-              if (right < 0) {
-                right = 2;
-              }
-              if (bottom < 0) {
-                bottom = 2;
-              }
-              if (left > mainWidth - 2) {
-                left = mainWidth - 2;
-                // top = 2;
-                boxBoundaryInfo.isBoundary = true;
-                boxBoundaryInfo.isBoundaryType = 'right';
-              }
-              if (top > mainHeight - 2) {
-                top = mainHeight - 2;
-                boxBoundaryInfo.isBoundary = true;
-                boxBoundaryInfo.isBoundaryType = 'bottom';
-              }
-
-              target!.style.left = `${left}px`;
-              target!.style.top = `${top}px`;
-            }
-          )
-          .on(
-            'dragEnd',
-            async ({
-              target,
-              // isDrag,
-              clientX
-              // clientY
-            }) => {
-              if (document.body.clientWidth / 2 - clientX > 0) {
-                // 往左出
-                directionType.value = 'right';
-              } else {
-                // 往又出
-                directionType.value = 'left';
-              }
-              isDragIng.value = false;
-              // 在这里进行动画
-              if (boxBoundaryInfo.isBoundary) {
-                // 这里说明贴边了
-                target.style.transition = '.3s';
-                actionEnd(target, boxBoundaryInfo.isBoundaryType);
-              }
-            }
-          );
-      }
-    };
-    const initMoveableClass = async () => {
-      if (document.querySelector('.wrap')) {
-        const moveable = new Moveable(document.querySelector('.wrap') as any, {
-          target: document.querySelector('#moveNPopover2') as any,
-          // If the container is null, the position is fixed. (default: parentElement(document.body))
-          container: document.querySelector('.wrap') as any,
-          // snappable: true,
-          // bounds: {"left":100,"top":100,"right":100,"bottom":100},
-          draggable: true,
-          resizable: false,
-          scalable: false,
-          rotatable: false,
-          warpable: false,
-          pinchable: false, // ["resizable", "scalable", "rotatable"]
-          origin: false,
-          keepRatio: false,
-          // Resize, Scale Events at edges.
-          edge: false,
-          throttleDrag: 0,
-          throttleResize: 0,
-          throttleScale: 0,
-          throttleRotate: 0
-        });
-        moveable
-          .on(
-            'drag',
-            ({
-              target,
-              // transform,
-              left,
-              top,
-              right,
-              bottom
-            }) => {
-              isDragIng.value = true;
-              const subdEl = document.getElementById(
-                `moveNPopover2`
-              ) as HTMLDivElement;
-              // console.log(subdEl, "subdEl", "drag");
-              const subdElStyle = getComputedStyle(subdEl, null);
-              const RectInfo = {
-                left: Number(subdElStyle.left.replace('px', '')),
-                top: Number(subdElStyle.top.replace('px', '')),
-                width: Number(subdElStyle.width.replace('px', '')),
-                height: Number(subdElStyle.height.replace('px', ''))
-              };
-
-              const mainWidth =
-                parseInt(
-                  window.getComputedStyle(
-                    document.querySelector('.wrap') as Element
-                  ).width
-                ) - RectInfo.width;
-
-              const mainHeight =
-                parseInt(
-                  window.getComputedStyle(
-                    document.querySelector('.wrap') as Element
-                  ).height
-                ) - RectInfo.height;
-
-              subdEl.style.transition = '';
-              classBoundaryInfo.isBoundary = false;
-              classBoundaryInfo.isBoundaryType = '';
-              classBoundaryInfo.mainHeight = mainHeight;
-              classBoundaryInfo.mainWidth = mainWidth;
-              classBoundaryInfo.subWidth = RectInfo.width;
-              classBoundaryInfo.subHeight = RectInfo.height;
-              if (left < 0) {
-                left = 2;
-                classBoundaryInfo.isBoundary = true;
-                classBoundaryInfo.isBoundaryType = 'left';
-              }
-              if (top < 0) {
-                top = 2;
-                classBoundaryInfo.isBoundary = true;
-                classBoundaryInfo.isBoundaryType = 'top';
-              }
-              if (right < 0) {
-                right = 2;
-              }
-              if (bottom < 0) {
-                bottom = 2;
-              }
-              if (left > mainWidth - 2) {
-                left = mainWidth - 2;
-                // top = 2;
-                classBoundaryInfo.isBoundary = true;
-                classBoundaryInfo.isBoundaryType = 'right';
-              }
-              if (top > mainHeight - 2) {
-                top = mainHeight - 2;
-                classBoundaryInfo.isBoundary = true;
-                classBoundaryInfo.isBoundaryType = 'bottom';
-              }
-
-              target!.style.left = `${left}px`;
-              target!.style.top = `${top}px`;
-            }
-          )
-          .on(
-            'dragEnd',
-            async ({
-              target,
-              //  isDrag,
-              clientX
-              // clientY
-            }) => {
-              if (document.body.clientWidth / 2 - clientX > 0) {
-                // 往左出
-                directionType.value = 'right';
-              } else {
-                // 往又出
-                directionType.value = 'left';
-              }
-              if (classBoundaryInfo.isBoundary) {
-                // 这里说明贴边了
-                target.style.transition = '.3s';
-                actionEnd(target, classBoundaryInfo.isBoundaryType);
-              }
-              isDragIng.value = false;
-            }
-          )
-          .on('click', () => {
-            showClass.value = true;
-          });
-      }
-    };
-
-    watch(
-      () => route.path,
-      (val: any) => {
-        const elDoc = document.getElementById('WrapcoreViewWrap') as any;
-        if (elDoc) {
-          elDoc.scrollTo(0, 0);
-          window.scrollTo(0, 0);
-        }
-      }
-    );
-
-    // 帮助指引状态
-    const helpNoteList = reactive({
-      baseListTab: ''
-    });
-    const helpNoteStatus = computed(() => {
-      const routePath = route.path;
-      const hidePath = [
-        '/classDetail',
-        '/classStudentDetail',
-        '/notation',
-        '/xiaoku-ai',
-        '/studentDetail',
-        '/classStudentRecode',
-        '/afterWorkDetail'
-      ];
-      // 单独判断个人信息页面[学校设置]有引导
-      if (route.path === '/setting') {
-        return helpNoteList.baseListTab === 'school' ? true : false;
-      } else {
-        return hidePath.includes(routePath) ? false : true;
-      }
-    });
-    const startClassStatus = computed(() => {
-      const routePath = route.path;
-      console.log(routePath, 'routePath', routePath);
-      const hidePath = ['/prepare-lessons'];
-      return hidePath.includes(routePath) ? false : true;
-    });
-
-    onMounted(() => {
-      initMoveable();
-      // // initMoveableClass();
-      const subdEl = document.getElementById(`moveNPopover`) as HTMLDivElement;
-      // // const classEl = document.getElementById(
-      // //   `moveNPopover2`
-      // // ) as HTMLDivElement;
-
-      // // initBoxRectInfo(classEl, classBoundaryInfo);
-      initBoundaryWrap(subdEl, boxBoundaryInfo);
-      initBoxRectInfo(subdEl, boxBoundaryInfo);
-      // // initBoundaryWrap(classEl, classBoundaryInfo);
-      window.addEventListener('resize', resetSize);
-
-      eventGlobal.on('base-setting-emit', (val: string) => {
-        helpNoteList.baseListTab = val;
-      });
-
-      // 判断是否显示证书提示
-      eventGlobal.on('auth-not-installed', () => {
-        showAuthMask.value = true;
-      });
-    });
-
-    const resetSize = () => {
-      const subdEl = document.getElementById(`moveNPopover`) as HTMLDivElement;
-      subdEl.style.display = 'none';
-      // const boxBoundaryInfo = reactive({
-      //   isBoundary: true,
-      //   isBoundaryType: 'right' as any,
-      //   mainWidth: '' as any,
-      //   mainHeight: '' as any,
-      //   subWidth: '' as any,
-      //   subHeight: '' as any
-      // });
-
-      // boxBoundaryInfo.isBoundary = true;
-      // boxBoundaryInfo.isBoundaryType= 'right'
-      if (NPopoverRef.value) {
-        NPopoverRef.value.setShow(false);
-      }
-
-      setTimeout(() => {
-        subdEl.style.transition = '';
-        initBoxRectInfo(subdEl, boxBoundaryInfo);
-        initBoundaryWrap(subdEl, boxBoundaryInfo);
-        console.log('resize');
-        subdEl.style.display = 'block';
-      }, 100);
-    };
-    onUnmounted(() => {
-      window.removeEventListener('resize', resetSize);
-    });
-    const initBoundaryWrap = (target: any, wrapInfo: any) => {
-      target.addEventListener('mouseover', () => {
-        if (wrapInfo.isBoundary) {
-          // 如果在边框 就得还原 元素位置 还原完毕后 去除transition
-          if (wrapInfo.isBoundaryType == 'left') {
-            target.style.left = '2px';
-          } else if (wrapInfo.isBoundaryType == 'right') {
-            target.style.left = `${wrapInfo.mainWidth - 2}px`;
-          } else if (wrapInfo.isBoundaryType == 'top') {
-            target.style.top = `${2}px`;
-          } else if (wrapInfo.isBoundaryType == 'bottom') {
-            target.style.top = `${wrapInfo.mainHeight - 2}px`;
-          }
-        }
-        rate(target, 0);
-      });
-      target.addEventListener('mouseout', () => {
-        if (wrapInfo.isBoundary) {
-          // 如果在边框 就得还原 元素位置 还原完毕后 去除transition
-          if (wrapInfo.isBoundaryType == 'left') {
-            actionEnd(target, 'left');
-          } else if (wrapInfo.isBoundaryType == 'right') {
-            actionEnd(target, 'right');
-          } else if (wrapInfo.isBoundaryType == 'top') {
-            actionEnd(target, 'top');
-          } else if (wrapInfo.isBoundaryType == 'bottom') {
-            actionEnd(target, 'bottom');
-          }
-        }
-        // rate(target, 0)
-      });
-      // target.addEventListener('contextmenu', (event: any) => {
-      //   event.preventDefault();
-      //   dialog.warning({
-      //     title: '提示',
-      //     content: '是否收入托盘',
-      //     positiveText: '确定',
-      //     negativeText: '取消',
-      //     onPositiveClick: () => {
-      //       console.log('确定');
-      //     },
-      //     onNegativeClick: () => {
-      //       console.log('取消');
-      //     }
-      //   });
-      // });
-
-      // actionEnd(target, 'right');
-    };
-    const startShowModal = (
-      val:
-        | 'setTimeIcon'
-        | 'beatIcon'
-        | 'toneIcon'
-        | 'iconWhiteBorad'
-        | 'iconPen'
-        | 'iconNote'
-    ) => {
-      if (val == 'setTimeIcon') {
-        showModalTime.value = true;
-      }
-      if (val == 'beatIcon') {
-        showModalBeat.value = true;
-      }
-      if (val == 'toneIcon') {
-        showModalTone.value = true;
-      }
-      if (val == 'iconNote') {
-        if (NPopoverRef.value) {
-          NPopoverRef.value.setShow(false);
-        }
-        console.log(route.name, 'guideInfo');
-        eventGlobal.emit('teacher-guideInfo', route.name);
-      }
-      if (val == 'iconWhiteBorad') {
-        studyData.whiteboardShow = true;
-        studyData.type = 'whiteboard';
-        studyData.homeStatus = false;
-        if (NPopoverRef.value) {
-          NPopoverRef.value.setShow(false);
-        }
-      }
-      if (val == 'iconPen') {
-        studyData.penShow = true;
-        studyData.type = 'pen';
-        studyData.homeStatus = false;
-        if (NPopoverRef.value) {
-          NPopoverRef.value.setShow(false);
-        }
-      }
-    };
-    // const moveTargetBoundary = (target: any, type: any) => {
-    //   console.log('moveTargetBoundary', target, type);
-    // };
-    // 这里是旋转
-    const rate = (target: any, rate: any) => {
-      target.style.transform = ' rotate(' + rate + ')';
-    };
-
-    //  这里是选装的方式
-    const actionEnd = (target: any, type: any) => {
-      switch (type) {
-        case 'left':
-          rate(target, '90deg');
-          target!.style.left = `${2 - boxBoundaryInfo.subWidth / 2}px`;
-          target!.style.top = `${top}px`;
-          break;
-        case 'right':
-          rate(target, '-90deg');
-          target!.style.left = `${
-            boxBoundaryInfo.mainWidth - 2 + boxBoundaryInfo.subWidth / 2
-          }px`;
-          target!.style.top = `${top}px`;
-          break;
-
-        case 'top':
-          target!.style.top = `${2 - boxBoundaryInfo.subHeight / 2}px`;
-          rate(target, '-180deg');
-          break;
-        case 'bottom':
-          target!.style.top = `${
-            boxBoundaryInfo.mainHeight - 2 + boxBoundaryInfo.subHeight / 2
-          }px`;
-          break;
-        default:
-          rate(target, '-0');
-          break;
-      }
-    };
-
-    const initBoxRectInfo = (target: any, wrapInfo: any) => {
-      // const subdEl = document.getElementById(`moveNPopover`) as HTMLDivElement;
-      // console.log(subdEl, "subdEl", "drag");
-      const subdElStyle = getComputedStyle(target, null);
-      const RectInfo = {
-        left: Number(subdElStyle.left.replace('px', '')),
-        top: Number(subdElStyle.top.replace('px', '')),
-        width: Number(subdElStyle.width.replace('px', '')),
-        height: Number(subdElStyle.height.replace('px', ''))
-      };
-      // target.style.transition = ''
-      const mainWidth =
-        parseInt(
-          window.getComputedStyle(document.querySelector('.wrap') as Element)
-            .width
-        ) - RectInfo.width;
-
-      const mainHeight =
-        parseInt(
-          window.getComputedStyle(document.querySelector('.wrap') as Element)
-            .height
-        ) - RectInfo.height;
-      // boxBoundaryInfo.isBoundary = false;
-      // boxBoundaryInfo.isBoundaryType = '';
-      wrapInfo.mainHeight = mainHeight;
-      wrapInfo.mainWidth = mainWidth;
-      wrapInfo.subWidth = RectInfo.width;
-      wrapInfo.subHeight = RectInfo.height;
-      target.style.transition = '.3s .3s';
-    };
-
-    /** 教学数据 */
-    const studyData = reactive({
-      homeStatus: true, // 是否显示首页
-      type: '',
-      penShow: false,
-      whiteboardShow: false
-    });
-    /* 弹窗加拖动 */
-    // 引导页
-    getGuidanceShow();
-    //计时器
-    const users = useUserStore();
-    const timerMeterConBoxClass = 'timerMeterConBoxClass_drag';
-    const timerMeterConDragData = useDrag(
-      [
-        `${timerMeterConBoxClass} .timeBomCon .bom_drag`,
-        `${timerMeterConBoxClass} .topDragDom`
-      ],
-      timerMeterConBoxClass,
-      showModalTime,
-      users.info.id
-    );
-    // 节拍器
-    const metronomeConBoxClass = 'metronomeConBoxClass_drag';
-    const metronomeConBoxDragData = useDrag(
-      [
-        `${metronomeConBoxClass} .topDragDom`,
-        `${metronomeConBoxClass} .bom_drag`
-      ],
-      metronomeConBoxClass,
-      showModalBeat,
-      users.info.id
-    );
-    return () => (
-      <div class={[styles.wrap, 'wrap']}>
-        <div>
-          <LayoutSilder></LayoutSilder>
-        </div>
-        <div class={styles.Wrapcore}>
-          <LayoutTop></LayoutTop>
-          <div class={styles.WrapcoreView} id="WrapcoreViewWrap">
-            {/* <div class={styles.WrapcoreViewInfo}> */}
-            <router-view>
-              {(obj: any) => (
-                <Transition name="fade-slide" mode="out-in">
-                  <obj.Component />
-                </Transition>
-              )}
-            </router-view>
-            {/* </div> */}
-          </div>
-        </div>
-        {/* <img
-          src={toolStartClass}
-          id="moveNPopover2"
-          style={{
-            display: ['/', '/home', '/classList', '/prepare-lessons'].includes(
-              route.path
-            )
-              ? 'none'
-              : 'block'
-          }}
-          class={[
-            styles.toolClassImg,
-            'moveNPopover2',
-            isDragIng.value ? styles.isDragIng : ''
-          ]}
-          alt=""
-        /> */}
-        <NPopover
-          raw
-          trigger="click"
-          ref={NPopoverRef}
-          show-arrow={false}
-          placement={directionType.value as 'left' | 'right'}
-          v-slots={{
-            trigger: () => (
-              // 首页不显示工具箱 ['/', '/home'].includes(route.path) ||
-              <img
-                // src={isDragIng.value ? dragingBoxIcon : toolbox}
-                src={toolbox}
-                id="moveNPopover"
-                style={{
-                  display: !studyData.homeStatus ? 'none' : 'block'
-                }}
-                //兼容触摸屏
-                onTouchstart={() => {
-                  NPopoverRef?.value.setShow(true);
-                }}
-                class={[
-                  styles.toolboxImg,
-                  'moveNPopover',
-                  isDragIng.value ? styles.isDragIng : ''
-                ]}
-                alt=""
-              />
-            )
-          }}>
-          <div class={styles.booxToolWrap}>
-            <div>
-              <div
-                class={styles.booxToolItem}
-                onClick={() => startShowModal('beatIcon')}>
-                <img src={beatIcon} alt="" />
-                节拍器
-              </div>
-              {/* <div
-                class={styles.booxToolItem}
-                onClick={() => startShowModal('toneIcon')}>
-                <img src={toneIcon} alt="" />
-                调音器
-              </div> */}
-              <div
-                class={styles.booxToolItem}
-                onClick={() => startShowModal('setTimeIcon')}>
-                <img src={setTimeIcon} alt="" />
-                计时器
-              </div>
-              {/* <div
-                class={[
-                  styles.booxToolItem,
-                  !startClassStatus.value && styles.booxToolDisabled
-                ]}
-                onClick={() => {
-                  if (!startClassStatus.value) return;
-                  showClass.value = true;
-                }}>
-                <img
-                  src={toolStartClass}
-                  class={[styles.toolClassImg]}
-                  alt=""
-                />
-                开始上课
-              </div> */}
-            </div>
-            <div>
-              {/* <div
-                class={[
-                  styles.booxToolItem,
-                  !helpNoteStatus.value && styles.booxToolDisabled
-                ]}
-                onClick={() => {
-                  if (!helpNoteStatus.value) return;
-                  // 默认滚动到页面顶部,在显示指引
-                  document.querySelector('#WrapcoreViewWrap')?.scrollTo(0, 0);
-                  startShowModal('iconNote');
-                }}>
-                <img src={iconNote} alt="" />
-                功能引导
-              </div> */}
-
-              <div
-                class={styles.booxToolItem}
-                onClick={() => startShowModal('iconPen')}>
-                <img src={iconWhiteBorad} alt="" />
-                批注
-              </div>
-              <div
-                class={styles.booxToolItem}
-                onClick={() => startShowModal('iconWhiteBorad')}>
-                <img src={iconPen} alt="" />
-                白板
-              </div>
-            </div>
-          </div>
-        </NPopover>
-
-        {/* 批注 */}
-        {studyData.penShow && (
-          <Pen
-            show={studyData.type === 'pen'}
-            type={studyData.type as any}
-            close={() => {
-              studyData.type = 'init';
-              studyData.homeStatus = true;
-            }}
-          />
-        )}
-
-        {studyData.whiteboardShow && (
-          <Pen
-            show={studyData.type === 'whiteboard'}
-            type={studyData.type as any}
-            close={() => {
-              studyData.type = 'init';
-              studyData.homeStatus = true;
-            }}
-          />
-        )}
-
-        {/* <NModal
-          class={['modalTitle background']}
-          style={{ width: '687px' }}
-          title={'节拍器'}
-          preset="card"
-          v-model:show={showModalBeat.value}>
-          <div class={styles.modeWrap}>
-            <iframe
-              src={`${vaildUrl()}/metronome/?id=${new Date().getTime()}`}
-              scrolling="no"
-              frameborder="0"
-              width="100%"
-              onLoad={(val: any) => {
-                iframeDislableKeyboard(val.target);
-              }}
-              height={'650px'}></iframe>
-          </div>
-        </NModal> */}
-        <Metronome
-          v-model={showModalBeat.value}
-          dragClass={metronomeConBoxClass}
-          dragStyle={metronomeConBoxDragData.styleDrag.value}></Metronome>
-        <NModal v-model:show={showModalTone.value} class={['background']}>
-          {/* <div
-            onClick={() => {
-              showModalTone.value = false;
-            }}>
-            <NImage
-              src={toneImage}
-              previewDisabled
-              class={styles.beatImage}></NImage>
-          </div> */}
-          <div>
-            <PlaceholderTone
-              onClose={() => {
-                showModalTone.value = false;
-              }}></PlaceholderTone>
-          </div>
-        </NModal>
-        <NModal
-          v-model:show={showModalTime.value}
-          class={timerMeterConBoxClass}
-          style={timerMeterConDragData.styleDrag.value}>
-          <div>
-            <img
-              class={styles.timerMeterClose}
-              src={timerMeterClose}
-              onClick={() => {
-                showModalTime.value = false;
-              }}
-            />
-            <div class="topDragDom"></div>
-            <TimerMeter></TimerMeter>
-          </div>
-        </NModal>
-
-        <NModal
-          v-model:show={showClass.value}
-          class={['modalTitle background', styles.showClass]}
-          preset="card"
-          title={'开始上课'}>
-          <AttendClass
-            onClose={() => (showClass.value = false)}
-            type="change"
-            onConfirm={(item: any) => {
-              showClass.value = false;
-              router.push({
-                path: '/prepare-lessons',
-                query: {
-                  ...item
-                }
-              });
-            }}
-          />
-        </NModal>
-
-        {/* 弹窗查看 */}
-        <PreviewWindow
-          v-model:show={previewModal.value}
-          type="attend"
-          params={previewItem.value}
-        />
-
-        <NModal
-          v-model:show={showAuthMask.value}
-          closeOnEsc={false}
-          maskClosable={false}>
-          <TheAuth onClose={() => (showAuthMask.value = false)} />
-        </NModal>
-      </div>
-    );
-  }
-});
+import {
+  Transition,
+  defineComponent,
+  onMounted,
+  ref,
+  reactive,
+  onUnmounted,
+  watch,
+  computed
+} from 'vue';
+import LayoutSilder from './layoutSilder';
+import LayoutTop from './layoutTop';
+import styles from './index.module.less';
+import { NButton, NImage, NModal, NPopover, NSpace, useDialog } from 'naive-ui';
+import Moveable from 'moveable';
+import toolStartClass from './images/toolStartClass.png';
+import timerMeterClose from './images/close.png';
+import toolbox from './images/toolbox.png';
+import setTimeIcon from './images/setTimeIcon.png';
+import beatIcon from './images/beatIcon.png';
+import toneIcon from './images/toneIcon.png';
+import iconWhiteBorad from './images/icon-whiteborad.png';
+import iconPen from './images/icon-pen.png';
+import iconNote from './images/icon-note.png';
+import beatImage from './images/beatImage.png';
+import toneImage from './images/toneImage.png';
+import setTimeImage from './images/setTimeImage.png';
+import dragingBoxIcon from './images/dragingBoxIcon.png';
+import TimerMeter from '../timerMeter';
+import Metronome from '../Metronome';
+import { useRoute, useRouter } from 'vue-router';
+import { vaildUrl } from '/src/utils/urlUtils';
+import ChioseModal from '/src/views/home/modals/chioseModal';
+import {
+  eventGlobal,
+  iframeDislableKeyboard,
+  px2vw,
+  px2vwH
+} from '@/utils/index';
+import PlaceholderTone from './modals/placeholderTone';
+import { modalClickMask, state } from '/src/state';
+import PreviewWindow from '/src/views/preview-window';
+import { fscreen } from '@/utils/index';
+import AttendClass from '/src/views/prepare-lessons/model/attend-class';
+import Pen from '/src/views/attend-class/component/tools/pen';
+import study from '/src/views/home/components/study';
+import TheAuth from '../TheAuth';
+import useDrag from '@/hooks/useDrag';
+import { getGuidanceShow } from '@/hooks/useDrag/useDragGuidance';
+import { useUserStore } from '@/store/modules/users';
+export default defineComponent({
+  name: 'layoutView',
+  setup() {
+    const router = useRouter();
+    const previewModal = ref(false);
+    const previewItem = ref({});
+    const directionType = ref('left');
+    const showClass = ref(false);
+
+    const showAuthMask = ref(false);
+    const showModalBeat = ref(false);
+    const showModalTone = ref(false);
+    const showModalTime = ref(false);
+    const showBoxConent = ref(false);
+    const dialog = useDialog();
+    const boxBoundaryInfo = reactive({
+      isBoundary: false,
+      isBoundaryType: '' as any,
+      mainWidth: '' as any,
+      mainHeight: '' as any,
+      subWidth: '' as any,
+      subHeight: '' as any
+    });
+
+    const classBoundaryInfo = reactive({
+      isBoundary: true,
+      isBoundaryType: 'right' as any,
+      mainWidth: '' as any,
+      mainHeight: '' as any,
+      subWidth: '' as any,
+      subHeight: '' as any
+    });
+    const route = useRoute();
+    const isDragIng = ref(false);
+    const NPopoverRef = ref();
+    const initMoveable = async () => {
+      if (document.querySelector('.wrap')) {
+        const moveable = new Moveable(document.querySelector('.wrap') as any, {
+          target: document.querySelector('#moveNPopover') as any,
+          // If the container is null, the position is fixed. (default: parentElement(document.body))
+          container: document.querySelector('.wrap') as any,
+          // snappable: true,
+          // bounds: {"left":100,"top":100,"right":100,"bottom":100},
+          draggable: true,
+          resizable: false,
+          scalable: false,
+          rotatable: false,
+          warpable: false,
+          pinchable: false, // ["resizable", "scalable", "rotatable"]
+          origin: false,
+          keepRatio: false,
+          // Resize, Scale Events at edges.
+          edge: false,
+          throttleDrag: 0,
+          throttleResize: 0,
+          throttleScale: 0,
+          throttleRotate: 0
+        });
+        moveable
+          // .on('dragStart', ({ target, clientX, clientY }) => {
+          //   console.log('dragStart');
+          // })
+          .on(
+            'drag',
+            ({
+              target,
+              // transform,
+              left,
+              top,
+              right,
+              bottom
+              // beforeDelta,
+              // beforeDist,
+              // delta,
+              // dist,
+              // clientX,
+              // clientY
+            }) => {
+              isDragIng.value = true;
+              if (NPopoverRef.value) {
+                NPopoverRef.value.setShow(false);
+              }
+
+              const subdEl = document.getElementById(
+                `moveNPopover`
+              ) as HTMLDivElement;
+              // console.log(subdEl, "subdEl", "drag");
+              const subdElStyle = getComputedStyle(subdEl, null);
+              const RectInfo = {
+                left: Number(subdElStyle.left.replace('px', '')),
+                top: Number(subdElStyle.top.replace('px', '')),
+                width: Number(subdElStyle.width.replace('px', '')),
+                height: Number(subdElStyle.height.replace('px', ''))
+              };
+              // target.style.transition = ''
+              const mainWidth =
+                parseInt(
+                  window.getComputedStyle(
+                    document.querySelector('.wrap') as Element
+                  ).width
+                ) - RectInfo.width;
+
+              const mainHeight =
+                parseInt(
+                  window.getComputedStyle(
+                    document.querySelector('.wrap') as Element
+                  ).height
+                ) - RectInfo.height;
+              subdEl.style.transition = '';
+              boxBoundaryInfo.isBoundary = false;
+              boxBoundaryInfo.isBoundaryType = '';
+              boxBoundaryInfo.mainHeight = mainHeight;
+              boxBoundaryInfo.mainWidth = mainWidth;
+              boxBoundaryInfo.subWidth = RectInfo.width;
+              boxBoundaryInfo.subHeight = RectInfo.height;
+              if (left < 0) {
+                left = 2;
+                boxBoundaryInfo.isBoundary = true;
+                boxBoundaryInfo.isBoundaryType = 'left';
+              }
+              if (top < 0) {
+                top = 2;
+                boxBoundaryInfo.isBoundary = true;
+                boxBoundaryInfo.isBoundaryType = 'top';
+              }
+              if (right < 0) {
+                right = 2;
+              }
+              if (bottom < 0) {
+                bottom = 2;
+              }
+              if (left > mainWidth - 2) {
+                left = mainWidth - 2;
+                // top = 2;
+                boxBoundaryInfo.isBoundary = true;
+                boxBoundaryInfo.isBoundaryType = 'right';
+              }
+              if (top > mainHeight - 2) {
+                top = mainHeight - 2;
+                boxBoundaryInfo.isBoundary = true;
+                boxBoundaryInfo.isBoundaryType = 'bottom';
+              }
+
+              target!.style.left = `${left}px`;
+              target!.style.top = `${top}px`;
+            }
+          )
+          .on(
+            'dragEnd',
+            async ({
+              target,
+              // isDrag,
+              clientX
+              // clientY
+            }) => {
+              if (document.body.clientWidth / 2 - clientX > 0) {
+                // 往左出
+                directionType.value = 'right';
+              } else {
+                // 往又出
+                directionType.value = 'left';
+              }
+              isDragIng.value = false;
+              // 在这里进行动画
+              if (boxBoundaryInfo.isBoundary) {
+                // 这里说明贴边了
+                target.style.transition = '.3s';
+                actionEnd(target, boxBoundaryInfo.isBoundaryType);
+              }
+            }
+          );
+      }
+    };
+    const initMoveableClass = async () => {
+      if (document.querySelector('.wrap')) {
+        const moveable = new Moveable(document.querySelector('.wrap') as any, {
+          target: document.querySelector('#moveNPopover2') as any,
+          // If the container is null, the position is fixed. (default: parentElement(document.body))
+          container: document.querySelector('.wrap') as any,
+          // snappable: true,
+          // bounds: {"left":100,"top":100,"right":100,"bottom":100},
+          draggable: true,
+          resizable: false,
+          scalable: false,
+          rotatable: false,
+          warpable: false,
+          pinchable: false, // ["resizable", "scalable", "rotatable"]
+          origin: false,
+          keepRatio: false,
+          // Resize, Scale Events at edges.
+          edge: false,
+          throttleDrag: 0,
+          throttleResize: 0,
+          throttleScale: 0,
+          throttleRotate: 0
+        });
+        moveable
+          .on(
+            'drag',
+            ({
+              target,
+              // transform,
+              left,
+              top,
+              right,
+              bottom
+            }) => {
+              isDragIng.value = true;
+              const subdEl = document.getElementById(
+                `moveNPopover2`
+              ) as HTMLDivElement;
+              // console.log(subdEl, "subdEl", "drag");
+              const subdElStyle = getComputedStyle(subdEl, null);
+              const RectInfo = {
+                left: Number(subdElStyle.left.replace('px', '')),
+                top: Number(subdElStyle.top.replace('px', '')),
+                width: Number(subdElStyle.width.replace('px', '')),
+                height: Number(subdElStyle.height.replace('px', ''))
+              };
+
+              const mainWidth =
+                parseInt(
+                  window.getComputedStyle(
+                    document.querySelector('.wrap') as Element
+                  ).width
+                ) - RectInfo.width;
+
+              const mainHeight =
+                parseInt(
+                  window.getComputedStyle(
+                    document.querySelector('.wrap') as Element
+                  ).height
+                ) - RectInfo.height;
+
+              subdEl.style.transition = '';
+              classBoundaryInfo.isBoundary = false;
+              classBoundaryInfo.isBoundaryType = '';
+              classBoundaryInfo.mainHeight = mainHeight;
+              classBoundaryInfo.mainWidth = mainWidth;
+              classBoundaryInfo.subWidth = RectInfo.width;
+              classBoundaryInfo.subHeight = RectInfo.height;
+              if (left < 0) {
+                left = 2;
+                classBoundaryInfo.isBoundary = true;
+                classBoundaryInfo.isBoundaryType = 'left';
+              }
+              if (top < 0) {
+                top = 2;
+                classBoundaryInfo.isBoundary = true;
+                classBoundaryInfo.isBoundaryType = 'top';
+              }
+              if (right < 0) {
+                right = 2;
+              }
+              if (bottom < 0) {
+                bottom = 2;
+              }
+              if (left > mainWidth - 2) {
+                left = mainWidth - 2;
+                // top = 2;
+                classBoundaryInfo.isBoundary = true;
+                classBoundaryInfo.isBoundaryType = 'right';
+              }
+              if (top > mainHeight - 2) {
+                top = mainHeight - 2;
+                classBoundaryInfo.isBoundary = true;
+                classBoundaryInfo.isBoundaryType = 'bottom';
+              }
+
+              target!.style.left = `${left}px`;
+              target!.style.top = `${top}px`;
+            }
+          )
+          .on(
+            'dragEnd',
+            async ({
+              target,
+              //  isDrag,
+              clientX
+              // clientY
+            }) => {
+              if (document.body.clientWidth / 2 - clientX > 0) {
+                // 往左出
+                directionType.value = 'right';
+              } else {
+                // 往又出
+                directionType.value = 'left';
+              }
+              if (classBoundaryInfo.isBoundary) {
+                // 这里说明贴边了
+                target.style.transition = '.3s';
+                actionEnd(target, classBoundaryInfo.isBoundaryType);
+              }
+              isDragIng.value = false;
+            }
+          )
+          .on('click', () => {
+            showClass.value = true;
+          });
+      }
+    };
+
+    watch(
+      () => route.path,
+      (val: any) => {
+        const elDoc = document.getElementById('WrapcoreViewWrap') as any;
+        if (elDoc) {
+          elDoc.scrollTo(0, 0);
+          window.scrollTo(0, 0);
+        }
+      }
+    );
+
+    // 帮助指引状态
+    const helpNoteList = reactive({
+      baseListTab: ''
+    });
+    const helpNoteStatus = computed(() => {
+      const routePath = route.path;
+      const hidePath = [
+        '/classDetail',
+        '/classStudentDetail',
+        '/notation',
+        '/xiaoku-ai',
+        '/studentDetail',
+        '/classStudentRecode',
+        '/afterWorkDetail'
+      ];
+      // 单独判断个人信息页面[学校设置]有引导
+      if (route.path === '/setting') {
+        return helpNoteList.baseListTab === 'school' ? true : false;
+      } else {
+        return hidePath.includes(routePath) ? false : true;
+      }
+    });
+    const startClassStatus = computed(() => {
+      const routePath = route.path;
+      console.log(routePath, 'routePath', routePath);
+      const hidePath = ['/prepare-lessons'];
+      return hidePath.includes(routePath) ? false : true;
+    });
+
+    onMounted(() => {
+      initMoveable();
+      // // initMoveableClass();
+      const subdEl = document.getElementById(`moveNPopover`) as HTMLDivElement;
+      // // const classEl = document.getElementById(
+      // //   `moveNPopover2`
+      // // ) as HTMLDivElement;
+
+      // // initBoxRectInfo(classEl, classBoundaryInfo);
+      initBoundaryWrap(subdEl, boxBoundaryInfo);
+      initBoxRectInfo(subdEl, boxBoundaryInfo);
+      // // initBoundaryWrap(classEl, classBoundaryInfo);
+      window.addEventListener('resize', resetSize);
+
+      eventGlobal.on('base-setting-emit', (val: string) => {
+        helpNoteList.baseListTab = val;
+      });
+
+      // 判断是否显示证书提示
+      eventGlobal.on('auth-not-installed', () => {
+        showAuthMask.value = true;
+      });
+    });
+
+    const resetSize = () => {
+      const subdEl = document.getElementById(`moveNPopover`) as HTMLDivElement;
+      subdEl.style.display = 'none';
+      // const boxBoundaryInfo = reactive({
+      //   isBoundary: true,
+      //   isBoundaryType: 'right' as any,
+      //   mainWidth: '' as any,
+      //   mainHeight: '' as any,
+      //   subWidth: '' as any,
+      //   subHeight: '' as any
+      // });
+
+      // boxBoundaryInfo.isBoundary = true;
+      // boxBoundaryInfo.isBoundaryType= 'right'
+      if (NPopoverRef.value) {
+        NPopoverRef.value.setShow(false);
+      }
+
+      setTimeout(() => {
+        subdEl.style.transition = '';
+        initBoxRectInfo(subdEl, boxBoundaryInfo);
+        initBoundaryWrap(subdEl, boxBoundaryInfo);
+        console.log('resize');
+        subdEl.style.display = 'block';
+      }, 100);
+    };
+    onUnmounted(() => {
+      window.removeEventListener('resize', resetSize);
+    });
+    const initBoundaryWrap = (target: any, wrapInfo: any) => {
+      target.addEventListener('mouseover', () => {
+        if (wrapInfo.isBoundary) {
+          // 如果在边框 就得还原 元素位置 还原完毕后 去除transition
+          if (wrapInfo.isBoundaryType == 'left') {
+            target.style.left = '2px';
+          } else if (wrapInfo.isBoundaryType == 'right') {
+            target.style.left = `${wrapInfo.mainWidth - 2}px`;
+          } else if (wrapInfo.isBoundaryType == 'top') {
+            target.style.top = `${2}px`;
+          } else if (wrapInfo.isBoundaryType == 'bottom') {
+            target.style.top = `${wrapInfo.mainHeight - 2}px`;
+          }
+        }
+        rate(target, 0);
+      });
+      target.addEventListener('mouseout', () => {
+        if (wrapInfo.isBoundary) {
+          // 如果在边框 就得还原 元素位置 还原完毕后 去除transition
+          if (wrapInfo.isBoundaryType == 'left') {
+            actionEnd(target, 'left');
+          } else if (wrapInfo.isBoundaryType == 'right') {
+            actionEnd(target, 'right');
+          } else if (wrapInfo.isBoundaryType == 'top') {
+            actionEnd(target, 'top');
+          } else if (wrapInfo.isBoundaryType == 'bottom') {
+            actionEnd(target, 'bottom');
+          }
+        }
+        // rate(target, 0)
+      });
+      // target.addEventListener('contextmenu', (event: any) => {
+      //   event.preventDefault();
+      //   dialog.warning({
+      //     title: '提示',
+      //     content: '是否收入托盘',
+      //     positiveText: '确定',
+      //     negativeText: '取消',
+      //     onPositiveClick: () => {
+      //       console.log('确定');
+      //     },
+      //     onNegativeClick: () => {
+      //       console.log('取消');
+      //     }
+      //   });
+      // });
+
+      // actionEnd(target, 'right');
+    };
+    const startShowModal = (
+      val:
+        | 'setTimeIcon'
+        | 'beatIcon'
+        | 'toneIcon'
+        | 'iconWhiteBorad'
+        | 'iconPen'
+        | 'iconNote'
+    ) => {
+      if (val == 'setTimeIcon') {
+        showModalTime.value = true;
+      }
+      if (val == 'beatIcon') {
+        showModalBeat.value = true;
+      }
+      if (val == 'toneIcon') {
+        showModalTone.value = true;
+      }
+      if (val == 'iconNote') {
+        if (NPopoverRef.value) {
+          NPopoverRef.value.setShow(false);
+        }
+        console.log(route.name, 'guideInfo');
+        eventGlobal.emit('teacher-guideInfo', route.name);
+      }
+      if (val == 'iconWhiteBorad') {
+        studyData.whiteboardShow = true;
+        studyData.type = 'whiteboard';
+        studyData.homeStatus = false;
+        if (NPopoverRef.value) {
+          NPopoverRef.value.setShow(false);
+        }
+      }
+      if (val == 'iconPen') {
+        studyData.penShow = true;
+        studyData.type = 'pen';
+        studyData.homeStatus = false;
+        if (NPopoverRef.value) {
+          NPopoverRef.value.setShow(false);
+        }
+      }
+    };
+    // const moveTargetBoundary = (target: any, type: any) => {
+    //   console.log('moveTargetBoundary', target, type);
+    // };
+    // 这里是旋转
+    const rate = (target: any, rate: any) => {
+      target.style.transform = ' rotate(' + rate + ')';
+    };
+
+    //  这里是选装的方式
+    const actionEnd = (target: any, type: any) => {
+      switch (type) {
+        case 'left':
+          rate(target, '90deg');
+          target!.style.left = `${2 - boxBoundaryInfo.subWidth / 2}px`;
+          target!.style.top = `${top}px`;
+          break;
+        case 'right':
+          rate(target, '-90deg');
+          target!.style.left = `${
+            boxBoundaryInfo.mainWidth - 2 + boxBoundaryInfo.subWidth / 2
+          }px`;
+          target!.style.top = `${top}px`;
+          break;
+
+        case 'top':
+          target!.style.top = `${2 - boxBoundaryInfo.subHeight / 2}px`;
+          rate(target, '-180deg');
+          break;
+        case 'bottom':
+          target!.style.top = `${
+            boxBoundaryInfo.mainHeight - 2 + boxBoundaryInfo.subHeight / 2
+          }px`;
+          break;
+        default:
+          rate(target, '-0');
+          break;
+      }
+    };
+
+    const initBoxRectInfo = (target: any, wrapInfo: any) => {
+      // const subdEl = document.getElementById(`moveNPopover`) as HTMLDivElement;
+      // console.log(subdEl, "subdEl", "drag");
+      const subdElStyle = getComputedStyle(target, null);
+      const RectInfo = {
+        left: Number(subdElStyle.left.replace('px', '')),
+        top: Number(subdElStyle.top.replace('px', '')),
+        width: Number(subdElStyle.width.replace('px', '')),
+        height: Number(subdElStyle.height.replace('px', ''))
+      };
+      // target.style.transition = ''
+      const mainWidth =
+        parseInt(
+          window.getComputedStyle(document.querySelector('.wrap') as Element)
+            .width
+        ) - RectInfo.width;
+
+      const mainHeight =
+        parseInt(
+          window.getComputedStyle(document.querySelector('.wrap') as Element)
+            .height
+        ) - RectInfo.height;
+      // boxBoundaryInfo.isBoundary = false;
+      // boxBoundaryInfo.isBoundaryType = '';
+      wrapInfo.mainHeight = mainHeight;
+      wrapInfo.mainWidth = mainWidth;
+      wrapInfo.subWidth = RectInfo.width;
+      wrapInfo.subHeight = RectInfo.height;
+      target.style.transition = '.3s .3s';
+    };
+
+    /** 教学数据 */
+    const studyData = reactive({
+      homeStatus: true, // 是否显示首页
+      type: '',
+      penShow: false,
+      whiteboardShow: false
+    });
+    /* 弹窗加拖动 */
+    // 引导页
+    getGuidanceShow();
+    //计时器
+    const users = useUserStore();
+    const timerMeterConBoxClass = 'timerMeterConBoxClass_drag';
+    const timerMeterConDragData = useDrag(
+      [
+        `${timerMeterConBoxClass} .timeBomCon .bom_drag`,
+        `${timerMeterConBoxClass} .topDragDom`
+      ],
+      timerMeterConBoxClass,
+      showModalTime,
+      users.info.id
+    );
+    // 节拍器
+    const metronomeConBoxClass = 'metronomeConBoxClass_drag';
+    const metronomeConBoxDragData = useDrag(
+      [
+        `${metronomeConBoxClass} .topDragDom`,
+        `${metronomeConBoxClass} .bom_drag`
+      ],
+      metronomeConBoxClass,
+      showModalBeat,
+      users.info.id
+    );
+    return () => (
+      <div class={[styles.wrap, 'wrap']}>
+        <div>
+          <LayoutSilder></LayoutSilder>
+        </div>
+        <div class={styles.Wrapcore}>
+          <LayoutTop></LayoutTop>
+          <div class={styles.WrapcoreView} id="WrapcoreViewWrap">
+            {/* <div class={styles.WrapcoreViewInfo}> */}
+            <router-view>
+              {(obj: any) => (
+                <Transition name="fade-slide" mode="out-in">
+                  <obj.Component />
+                </Transition>
+              )}
+            </router-view>
+            {/* </div> */}
+          </div>
+        </div>
+        {/* <img
+          src={toolStartClass}
+          id="moveNPopover2"
+          style={{
+            display: ['/', '/home', '/classList', '/prepare-lessons'].includes(
+              route.path
+            )
+              ? 'none'
+              : 'block'
+          }}
+          class={[
+            styles.toolClassImg,
+            'moveNPopover2',
+            isDragIng.value ? styles.isDragIng : ''
+          ]}
+          alt=""
+        /> */}
+        <NPopover
+          raw
+          trigger="click"
+          ref={NPopoverRef}
+          show-arrow={false}
+          placement={directionType.value as 'left' | 'right'}
+          v-slots={{
+            trigger: () => (
+              // 首页不显示工具箱 ['/', '/home'].includes(route.path) ||
+              <img
+                // src={isDragIng.value ? dragingBoxIcon : toolbox}
+                src={toolbox}
+                id="moveNPopover"
+                style={{
+                  display: !studyData.homeStatus ? 'none' : 'block'
+                }}
+                //兼容触摸屏
+                onTouchstart={() => {
+                  NPopoverRef?.value.setShow(true);
+                }}
+                class={[
+                  styles.toolboxImg,
+                  'moveNPopover',
+                  isDragIng.value ? styles.isDragIng : ''
+                ]}
+                alt=""
+              />
+            )
+          }}>
+          <div class={styles.booxToolWrap}>
+            <div>
+              <div
+                class={styles.booxToolItem}
+                onClick={() => startShowModal('beatIcon')}>
+                <img src={beatIcon} alt="" />
+                节拍器
+              </div>
+              {/* <div
+                class={styles.booxToolItem}
+                onClick={() => startShowModal('toneIcon')}>
+                <img src={toneIcon} alt="" />
+                调音器
+              </div> */}
+              <div
+                class={styles.booxToolItem}
+                onClick={() => startShowModal('setTimeIcon')}>
+                <img src={setTimeIcon} alt="" />
+                计时器
+              </div>
+              {/* <div
+                class={[
+                  styles.booxToolItem,
+                  !startClassStatus.value && styles.booxToolDisabled
+                ]}
+                onClick={() => {
+                  if (!startClassStatus.value) return;
+                  showClass.value = true;
+                }}>
+                <img
+                  src={toolStartClass}
+                  class={[styles.toolClassImg]}
+                  alt=""
+                />
+                开始上课
+              </div> */}
+            </div>
+            <div>
+              {/* <div
+                class={[
+                  styles.booxToolItem,
+                  !helpNoteStatus.value && styles.booxToolDisabled
+                ]}
+                onClick={() => {
+                  if (!helpNoteStatus.value) return;
+                  // 默认滚动到页面顶部,在显示指引
+                  document.querySelector('#WrapcoreViewWrap')?.scrollTo(0, 0);
+                  startShowModal('iconNote');
+                }}>
+                <img src={iconNote} alt="" />
+                功能引导
+              </div> */}
+
+              <div
+                class={styles.booxToolItem}
+                onClick={() => startShowModal('iconPen')}>
+                <img src={iconWhiteBorad} alt="" />
+                批注
+              </div>
+              <div
+                class={styles.booxToolItem}
+                onClick={() => startShowModal('iconWhiteBorad')}>
+                <img src={iconPen} alt="" />
+                白板
+              </div>
+            </div>
+          </div>
+        </NPopover>
+
+        {/* 批注 */}
+        {studyData.penShow && (
+          <Pen
+            show={studyData.type === 'pen'}
+            type={studyData.type as any}
+            close={() => {
+              studyData.type = 'init';
+              studyData.homeStatus = true;
+            }}
+          />
+        )}
+
+        {studyData.whiteboardShow && (
+          <Pen
+            show={studyData.type === 'whiteboard'}
+            type={studyData.type as any}
+            close={() => {
+              studyData.type = 'init';
+              studyData.homeStatus = true;
+            }}
+          />
+        )}
+
+        {/* <NModal
+          class={['modalTitle background']}
+          style={{ width: '687px' }}
+          title={'节拍器'}
+          preset="card"
+          v-model:show={showModalBeat.value}>
+          <div class={styles.modeWrap}>
+            <iframe
+              src={`${vaildUrl()}/metronome/?id=${new Date().getTime()}`}
+              scrolling="no"
+              frameborder="0"
+              width="100%"
+              onLoad={(val: any) => {
+                iframeDislableKeyboard(val.target);
+              }}
+              height={'650px'}></iframe>
+          </div>
+        </NModal> */}
+        <Metronome
+          v-model={showModalBeat.value}
+          dragClass={metronomeConBoxClass}
+          dragStyle={metronomeConBoxDragData.styleDrag.value}></Metronome>
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={showModalTone.value}
+          class={['background']}>
+          {/* <div
+            onClick={() => {
+              showModalTone.value = false;
+            }}>
+            <NImage
+              src={toneImage}
+              previewDisabled
+              class={styles.beatImage}></NImage>
+          </div> */}
+          <div>
+            <PlaceholderTone
+              onClose={() => {
+                showModalTone.value = false;
+              }}></PlaceholderTone>
+          </div>
+        </NModal>
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={showModalTime.value}
+          class={timerMeterConBoxClass}
+          style={timerMeterConDragData.styleDrag.value}>
+          <div>
+            <img
+              class={styles.timerMeterClose}
+              src={timerMeterClose}
+              onClick={() => {
+                showModalTime.value = false;
+              }}
+            />
+            <div class="topDragDom"></div>
+            <TimerMeter></TimerMeter>
+          </div>
+        </NModal>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={showClass.value}
+          class={['modalTitle background', styles.showClass]}
+          preset="card"
+          title={'开始上课'}>
+          <AttendClass
+            onClose={() => (showClass.value = false)}
+            type="change"
+            onConfirm={(item: any) => {
+              showClass.value = false;
+              router.push({
+                path: '/prepare-lessons',
+                query: {
+                  ...item
+                }
+              });
+            }}
+          />
+        </NModal>
+
+        {/* 弹窗查看 */}
+        <PreviewWindow
+          v-model:show={previewModal.value}
+          type="attend"
+          params={previewItem.value}
+        />
+
+        <NModal
+          v-model:show={showAuthMask.value}
+          closeOnEsc={false}
+          maskClosable={false}>
+          <TheAuth onClose={() => (showAuthMask.value = false)} />
+        </NModal>
+      </div>
+    );
+  }
+});

+ 502 - 498
src/components/layout/layoutTop.tsx

@@ -1,498 +1,502 @@
-import {
-  defineComponent,
-  ref,
-  onMounted,
-  nextTick,
-  onUnmounted,
-  reactive,
-  computed
-} from 'vue';
-import styles from './index.module.less';
-import { NImage, NBadge, NPopover, NIcon, NModal, NTooltip } from 'naive-ui';
-import styles2 from './modals/suggestion-option.module.less';
-import schoolIcon from './images/schoolIcon.png';
-import teacherIcon from './images/teacherIcon.png';
-import messageIcon from './images/messageIcon.png';
-import closeIcon from './images/closeIcon.png';
-import clockIcon from './images/clockIcon.png';
-import schoolDot from './images/schoolDot.png';
-import personIcon from './images/personIcon.png';
-import iconAboutus from './images/icon-aboutus.png';
-import { useUserStore } from '@/store/modules/users';
-import inFront from './images/inFront.png';
-import inBack from './images/inBack.png';
-import submitBtn from './images/submitBtn.png';
-import sealing from './images/sealing.png';
-import boxBg from './images/boxBg.png';
-import { useRouter, useRoute } from 'vue-router';
-import { storeToRefs } from 'pinia';
-import opinionIcon from './images/opinionIcon.png';
-import inviteIcon from './images/invite_student_icon.png';
-import gnydIcon from './images/gnyd.png';
-import classHistoryIcon from './images/classHistoryIcon.png';
-import 'animate.css';
-import ForgotPassword from '/src/views/setting/modal/forgotPassword';
-import ImGroup from './imGroup';
-import SuggestionOption from './modals/suggestion-option';
-import dayjs from 'dayjs';
-import ClassModal from '/src/views/home/modals/class-modal';
-import { suggestMessageUnread } from '/src/api/user';
-import { eventGlobal } from '/src/utils';
-import { usePrepareStore } from '/src/store/modules/prepareLessons';
-import { schoolDetail } from '/src/views/studentList/api';
-import AddStudentModel from '/src/views/studentList/modals/addStudentModel';
-
-export default defineComponent({
-  name: 'layoutTop',
-  setup() {
-    const router = useRouter();
-    const noReadCount = ref(0); // 未读数
-    const showHeadFlag = ref(false);
-    const showImGroup = ref(false);
-    const showImGroupLoading = ref(true);
-    const showSuggestionViseble = ref(false);
-    const users = useUserStore();
-    const showWord = ref(false);
-    const { info } = storeToRefs(users);
-    const userInfoStatus = ref(false);
-    const classRecordStatus = ref(false);
-    const prepareStore = usePrepareStore();
-    const state = reactive({
-      addStudentVisible: false,
-      activeRow: {} as any
-    });
-
-    const oncheckEditStatus = (callBack: any) => {
-      if (prepareStore.getIsEditResource) {
-        eventGlobal.emit('pageBeforeLeave', () => callBack());
-      } else {
-        callBack();
-      }
-    };
-    const gotoPerson = () => {
-      userInfoStatus.value = false;
-      router.push({ path: '/setting', query: { activeTab: 'person' } });
-    };
-    const gotoSchool = () => {
-      router.push({ path: '/setting', query: { activeTab: 'school' } });
-    };
-    const suggestionOptionRef = ref();
-    const resetPwd = () => {
-      showWord.value = true;
-      userInfoStatus.value = false;
-    };
-    const aboutUs = () => {
-      router.push({ path: '/aboutUs' });
-    };
-    const body = document.querySelector('body');
-    if (body) {
-      body.className = 'myBody body';
-    }
-
-    const showOption = () => {
-      showSuggestionViseble.value = true;
-      if (suggestionOptionRef.value) {
-        suggestionOptionRef.value.onReset();
-      }
-      console.log(suggestionOptionRef.value, 'suggestionOptionRef');
-    };
-
-    // 邀请学生二维码
-    const showInviteQrcode = async () => {
-      try {
-        const { schoolInfos } = users.getUserInfo;
-        const schoolId = schoolInfos.length > 0 ? schoolInfos[0].id : null;
-        if (schoolId) {
-          const { data } = await schoolDetail({ id: schoolId });
-          state.activeRow = data;
-
-          state.addStudentVisible = true;
-        }
-      } catch {
-        //
-      }
-    };
-
-    const suggestionStatus = ref(false);
-    const getSuggestMessageUnread = async () => {
-      try {
-        const { data } = await suggestMessageUnread();
-        const temp = data || [];
-        let system: any = {};
-        temp.forEach((item: any) => {
-          if (item.group === 'SYSTEM') {
-            system = item;
-          }
-        });
-        if (system.number > 0) {
-          suggestionStatus.value = system.number > 0 ? true : false;
-        } else {
-          suggestionStatus.value = false;
-        }
-      } catch {
-        //
-      }
-    };
-
-    onMounted(() => {
-      window.addEventListener('message', onImMessage);
-      showImGroupLoading.value = true;
-      showImGroup.value = true;
-
-      getSuggestMessageUnread();
-
-      eventGlobal.on('onSuggestionRead', () => {
-        if (suggestionStatus.value) {
-          getSuggestMessageUnread();
-        }
-      });
-
-      nextTick(() => {
-        setTimeout(() => {
-          showImGroup.value = false;
-        }, 50);
-        setTimeout(() => {
-          showImGroupLoading.value = false;
-          if (body) {
-            body.className = 'myBody';
-          }
-        }, 1000);
-      });
-    });
-
-    const onImMessage = (evt: MessageEvent) => {
-      if (evt.data.api === 'onImClose') {
-        showImGroup.value = false;
-      } else if (evt.data.api === 'getNoReadMessageCount') {
-        console.log(evt, 'onMessage');
-        noReadCount.value = evt.data.count || 0;
-      }
-    };
-
-    onUnmounted(() => {
-      window.removeEventListener('message', onImMessage);
-    });
-
-    const imglist = [inFront, inBack, submitBtn, sealing, boxBg];
-    const loadImg = (imgList: any) => {
-      for (let i = 0; i < imgList.length; i++) {
-        const img = new Image();
-        // let currentSrc = ''
-        img.src = imgList[i];
-        img.onload = function (e) {
-          // console.log('加载完毕', e, img.complete);
-        };
-        img.onerror = function (e) {
-          // console.log('加载错误', e);
-        };
-      }
-    };
-    loadImg(imglist);
-
-    // 功能引导
-    const route = useRoute();
-    const helpNoteList = reactive({
-      baseListTab: ''
-    });
-    const helpNoteStatus = computed(() => {
-      const routePath = route.path;
-      const hidePath = [
-        '/classDetail',
-        '/classStudentDetail',
-        '/notation',
-        '/xiaoku-ai',
-        '/studentDetail',
-        '/classStudentRecode',
-        '/afterWorkDetail'
-      ];
-      // 单独判断个人信息页面[学校设置]有引导
-      if (route.path === '/setting') {
-        return helpNoteList.baseListTab === 'school' ? true : false;
-      } else {
-        return hidePath.includes(routePath) ? false : true;
-      }
-    });
-    return () => (
-      <>
-        <div class={styles.layoutTop}>
-          <div class={styles.layoutLeft}>
-            <NImage
-              src={schoolIcon}
-              class={styles.schoolIcon}
-              previewDisabled></NImage>
-            <p>
-              {/* {info.value.schoolInfos[0].tenantName} |{' '} */}
-              {(info.value?.schoolInfos && info.value?.schoolInfos[0].name) ||
-                ''}
-            </p>
-          </div>
-          <div class={styles.layoutRight}>
-            <NTooltip showArrow={false}>
-              {{
-                trigger: () => (
-                  <div
-                    class={[
-                      styles.optons,
-                      !helpNoteStatus.value && styles.booxToolDisabled
-                    ]}
-                    id="home-1"
-                    onClick={() => {
-                      if (!helpNoteStatus.value) return;
-                      // 默认滚动到页面顶部,在显示指引
-                      document
-                        .querySelector('#WrapcoreViewWrap')
-                        ?.scrollTo(0, 0);
-                      console.log(route.name, 'guideInfo');
-                      eventGlobal.emit('teacher-guideInfo', route.name);
-                    }}>
-                    <NImage src={gnydIcon} previewDisabled></NImage>
-                  </div>
-                ),
-                default: '功能引导'
-              }}
-            </NTooltip>
-            {/* <NTooltip showArrow={false}>
-              {{
-                trigger: () => (
-                  <div class={styles.optons} onClick={showInviteQrcode}>
-                    <NBadge dot={suggestionStatus.value} color={'#FF1036'}>
-                      <NImage src={inviteIcon} previewDisabled></NImage>
-                    </NBadge>
-                  </div>
-                ),
-                default: '邀请学生'
-              }}
-            </NTooltip> */}
-            <NPopover
-              width={380}
-              class={styles.popoverClassModel}
-              placement={'bottom'}
-              v-model:show={classRecordStatus.value}
-              trigger="click"
-              displayDirective="show"
-              v-slots={{
-                trigger: () => (
-                  <NTooltip showArrow={false}>
-                    {{
-                      trigger: () => (
-                        <div class={styles.optons}>
-                          <NImage
-                            src={classHistoryIcon}
-                            previewDisabled></NImage>
-                        </div>
-                      ),
-                      default: '上课记录'
-                    }}
-                  </NTooltip>
-                )
-              }}>
-              <ClassModal
-                onConfirm={() => {
-                  classRecordStatus.value = false;
-                }}
-              />
-            </NPopover>
-            <NTooltip showArrow={false}>
-              {{
-                trigger: () => (
-                  <div class={styles.optons} onClick={showOption} id="home-2">
-                    <NBadge dot={suggestionStatus.value} color={'#FF1036'}>
-                      <NImage src={opinionIcon} previewDisabled></NImage>
-                    </NBadge>
-                  </div>
-                ),
-                default: '意见反馈'
-              }}
-            </NTooltip>
-            {/* </div> */}
-            <div onClick={() => (showImGroup.value = true)}>
-              <NTooltip showArrow={false}>
-                {{
-                  trigger: () => (
-                    <NBadge
-                      value={noReadCount.value}
-                      max={99}
-                      class={[
-                        noReadCount.value > 0 ? '' : styles.messageBadgeHide,
-                        styles.messageBadge,
-                        noReadCount.value > 0 ? '' : styles.messageBadgeNo
-                      ]}
-                      {...{ id: 'home-3' }}
-                      color={'#FF1036'}>
-                      <NImage
-                        class={[
-                          styles.messageIcon,
-                          noReadCount.value > 0 ? styles.animation : ''
-                        ]}
-                        preview-disabled
-                        src={messageIcon}></NImage>
-                    </NBadge>
-                  ),
-                  default: '聊天'
-                }}
-              </NTooltip>
-            </div>
-
-            <div class={styles.line}></div>
-            <NPopover
-              show-arrow={false}
-              trigger="click"
-              v-model:show={userInfoStatus.value}
-              onUpdate:show={val => {
-                showHeadFlag.value = val;
-              }}
-              class={styles.popoverHeader}
-              placement="bottom-end"
-              raw={true}
-              v-slots={{
-                trigger: () => (
-                  <div class={styles.mesgWrap} style={{ cursor: 'pointer' }}>
-                    <NImage
-                      preview-disabled
-                      class={styles.teacherIcon}
-                      src={
-                        info.value.avatar ? info.value.avatar : teacherIcon
-                      }></NImage>
-                    <NIcon
-                      class={
-                        showHeadFlag.value
-                          ? styles.rotueLeft
-                          : styles.rotueRight
-                      }>
-                      <svg
-                        xmlns="http://www.w3.org/2000/svg"
-                        viewBox="0 0 24 24">
-                        <path
-                          d="M7.38 21.01c.49.49 1.28.49 1.77 0l8.31-8.31a.996.996 0 0 0 0-1.41L9.15 2.98c-.49-.49-1.28-.49-1.77 0s-.49 1.28 0 1.77L14.62 12l-7.25 7.25c-.48.48-.48 1.28.01 1.76z"
-                          fill="currentColor"></path>
-                      </svg>
-                    </NIcon>
-                  </div>
-                )
-              }}>
-              <div class={styles.propWrap}>
-                <div class={styles.teacherInfo}>
-                  <NImage
-                    class={styles.teacherIcon}
-                    src={info.value.avatar ? info.value.avatar : teacherIcon}
-                    previewDisabled></NImage>
-                  <NTooltip class={styles.nameTool}>
-                    {{
-                      trigger: () => (
-                        <p class={styles.teacherName}>{info.value.nickname}</p>
-                      ),
-                      default: () => info.value.nickname
-                    }}
-                  </NTooltip>
-                </div>
-                <div class={styles.propWrapList}>
-                  <div
-                    class={styles.propWrapItem}
-                    onClick={() => oncheckEditStatus(gotoPerson)}>
-                    <NImage
-                      class={styles.smallIcon}
-                      src={personIcon}
-                      previewDisabled></NImage>
-                    <p class={styles.smallTitle}>个人信息</p>
-                  </div>
-                  {info.value.isSuperAdmin ? (
-                    <div
-                      class={styles.propWrapItem}
-                      onClick={() => {
-                        oncheckEditStatus(gotoSchool);
-                      }}>
-                      <NImage
-                        class={styles.smallIcon}
-                        src={schoolDot}
-                        previewDisabled></NImage>
-                      <p class={styles.smallTitle}>学校信息</p>
-                    </div>
-                  ) : null}
-
-                  <div class={styles.propWrapItem} onClick={() => resetPwd()}>
-                    <NImage
-                      class={styles.smallIcon}
-                      src={clockIcon}
-                      previewDisabled></NImage>
-                    <p class={styles.smallTitle}>修改密码</p>
-                  </div>
-
-                  <div
-                    class={styles.propWrapItem}
-                    onClick={() => oncheckEditStatus(aboutUs)}>
-                    <NImage
-                      class={styles.smallIcon}
-                      src={iconAboutus}
-                      previewDisabled></NImage>
-                    <p class={styles.smallTitle}>关于我们</p>
-                  </div>
-                </div>
-                <div
-                  class={styles.logoutInfo}
-                  onClick={() => {
-                    users.logout();
-                    router.replace('/login');
-                  }}>
-                  <div class={styles.propWrapItem}>
-                    <NImage
-                      class={styles.smallIcon}
-                      src={closeIcon}
-                      previewDisabled></NImage>
-                    <p class={styles.smallTitle}>退出登录</p>
-                  </div>
-                </div>
-              </div>
-            </NPopover>
-            <div class={styles2.isHidden}></div>
-          </div>
-          <NModal
-            class={styles.changePwdModal}
-            v-model:show={showWord.value}
-            preset="dialog"
-            showIcon={false}
-            title="修改密码">
-            <ForgotPassword
-              phone={info.value.phone}
-              onClose={() => {
-                showWord.value = false;
-              }}
-            />
-          </NModal>
-
-          <NModal
-            v-model:show={showImGroup.value}
-            showIcon={false}
-            class={showImGroupLoading.value ? styles.hideModal : ''}
-            {...{ id: 'imGroupDiv' }}
-            displayDirective="show">
-            <ImGroup />
-          </NModal>
-
-          <NModal
-            class={['modalTitle', 'background', styles.suggestWrap]}
-            v-model:show={showSuggestionViseble.value}
-            display-directive="show"
-            showIcon={false}>
-            <SuggestionOption
-              ref={suggestionOptionRef}
-              onClose={() =>
-                (showSuggestionViseble.value = false)
-              }></SuggestionOption>
-          </NModal>
-
-          {/* {state.addStudentVisible ? (
-            <div
-              v-model:show={state.addStudentVisible}
-              class={['n-modal-mask', styles.popBox]}>
-              <AddStudentModel
-                activeRow={state.activeRow}
-                onClose={() => {
-                  state.addStudentVisible = false;
-                }}></AddStudentModel>
-            </div>
-          ) : null} */}
-        </div>
-      </>
-    );
-  }
-});
+import {
+  defineComponent,
+  ref,
+  onMounted,
+  nextTick,
+  onUnmounted,
+  reactive,
+  computed
+} from 'vue';
+import styles from './index.module.less';
+import { NImage, NBadge, NPopover, NIcon, NModal, NTooltip } from 'naive-ui';
+import styles2 from './modals/suggestion-option.module.less';
+import schoolIcon from './images/schoolIcon.png';
+import teacherIcon from './images/teacherIcon.png';
+import messageIcon from './images/messageIcon.png';
+import closeIcon from './images/closeIcon.png';
+import clockIcon from './images/clockIcon.png';
+import schoolDot from './images/schoolDot.png';
+import personIcon from './images/personIcon.png';
+import iconAboutus from './images/icon-aboutus.png';
+import { useUserStore } from '@/store/modules/users';
+import inFront from './images/inFront.png';
+import inBack from './images/inBack.png';
+import submitBtn from './images/submitBtn.png';
+import sealing from './images/sealing.png';
+import boxBg from './images/boxBg.png';
+import { useRouter, useRoute } from 'vue-router';
+import { storeToRefs } from 'pinia';
+import opinionIcon from './images/opinionIcon.png';
+import inviteIcon from './images/invite_student_icon.png';
+import gnydIcon from './images/gnyd.png';
+import classHistoryIcon from './images/classHistoryIcon.png';
+import 'animate.css';
+import ForgotPassword from '/src/views/setting/modal/forgotPassword';
+import ImGroup from './imGroup';
+import SuggestionOption from './modals/suggestion-option';
+import dayjs from 'dayjs';
+import ClassModal from '/src/views/home/modals/class-modal';
+import { suggestMessageUnread } from '/src/api/user';
+import { eventGlobal } from '/src/utils';
+import { usePrepareStore } from '/src/store/modules/prepareLessons';
+import { schoolDetail } from '/src/views/studentList/api';
+import AddStudentModel from '/src/views/studentList/modals/addStudentModel';
+import { modalClickMask } from '/src/state';
+
+export default defineComponent({
+  name: 'layoutTop',
+  setup() {
+    const router = useRouter();
+    const noReadCount = ref(0); // 未读数
+    const showHeadFlag = ref(false);
+    const showImGroup = ref(false);
+    const showImGroupLoading = ref(true);
+    const showSuggestionViseble = ref(false);
+    const users = useUserStore();
+    const showWord = ref(false);
+    const { info } = storeToRefs(users);
+    const userInfoStatus = ref(false);
+    const classRecordStatus = ref(false);
+    const prepareStore = usePrepareStore();
+    const state = reactive({
+      addStudentVisible: false,
+      activeRow: {} as any
+    });
+
+    const oncheckEditStatus = (callBack: any) => {
+      if (prepareStore.getIsEditResource) {
+        eventGlobal.emit('pageBeforeLeave', () => callBack());
+      } else {
+        callBack();
+      }
+    };
+    const gotoPerson = () => {
+      userInfoStatus.value = false;
+      router.push({ path: '/setting', query: { activeTab: 'person' } });
+    };
+    const gotoSchool = () => {
+      router.push({ path: '/setting', query: { activeTab: 'school' } });
+    };
+    const suggestionOptionRef = ref();
+    const resetPwd = () => {
+      showWord.value = true;
+      userInfoStatus.value = false;
+    };
+    const aboutUs = () => {
+      router.push({ path: '/aboutUs' });
+    };
+    const body = document.querySelector('body');
+    if (body) {
+      body.className = 'myBody body';
+    }
+
+    const showOption = () => {
+      showSuggestionViseble.value = true;
+      if (suggestionOptionRef.value) {
+        suggestionOptionRef.value.onReset();
+      }
+      console.log(suggestionOptionRef.value, 'suggestionOptionRef');
+    };
+
+    // 邀请学生二维码
+    const showInviteQrcode = async () => {
+      try {
+        const { schoolInfos } = users.getUserInfo;
+        const schoolId = schoolInfos.length > 0 ? schoolInfos[0].id : null;
+        if (schoolId) {
+          const { data } = await schoolDetail({ id: schoolId });
+          state.activeRow = data;
+
+          state.addStudentVisible = true;
+        }
+      } catch {
+        //
+      }
+    };
+
+    const suggestionStatus = ref(false);
+    const getSuggestMessageUnread = async () => {
+      try {
+        const { data } = await suggestMessageUnread();
+        const temp = data || [];
+        let system: any = {};
+        temp.forEach((item: any) => {
+          if (item.group === 'SYSTEM') {
+            system = item;
+          }
+        });
+        if (system.number > 0) {
+          suggestionStatus.value = system.number > 0 ? true : false;
+        } else {
+          suggestionStatus.value = false;
+        }
+      } catch {
+        //
+      }
+    };
+
+    onMounted(() => {
+      window.addEventListener('message', onImMessage);
+      showImGroupLoading.value = true;
+      showImGroup.value = true;
+
+      getSuggestMessageUnread();
+
+      eventGlobal.on('onSuggestionRead', () => {
+        if (suggestionStatus.value) {
+          getSuggestMessageUnread();
+        }
+      });
+
+      nextTick(() => {
+        setTimeout(() => {
+          showImGroup.value = false;
+        }, 50);
+        setTimeout(() => {
+          showImGroupLoading.value = false;
+          if (body) {
+            body.className = 'myBody';
+          }
+        }, 1000);
+      });
+    });
+
+    const onImMessage = (evt: MessageEvent) => {
+      if (evt.data.api === 'onImClose') {
+        showImGroup.value = false;
+      } else if (evt.data.api === 'getNoReadMessageCount') {
+        console.log(evt, 'onMessage');
+        noReadCount.value = evt.data.count || 0;
+      }
+    };
+
+    onUnmounted(() => {
+      window.removeEventListener('message', onImMessage);
+    });
+
+    const imglist = [inFront, inBack, submitBtn, sealing, boxBg];
+    const loadImg = (imgList: any) => {
+      for (let i = 0; i < imgList.length; i++) {
+        const img = new Image();
+        // let currentSrc = ''
+        img.src = imgList[i];
+        img.onload = function (e) {
+          // console.log('加载完毕', e, img.complete);
+        };
+        img.onerror = function (e) {
+          // console.log('加载错误', e);
+        };
+      }
+    };
+    loadImg(imglist);
+
+    // 功能引导
+    const route = useRoute();
+    const helpNoteList = reactive({
+      baseListTab: ''
+    });
+    const helpNoteStatus = computed(() => {
+      const routePath = route.path;
+      const hidePath = [
+        '/classDetail',
+        '/classStudentDetail',
+        '/notation',
+        '/xiaoku-ai',
+        '/studentDetail',
+        '/classStudentRecode',
+        '/afterWorkDetail'
+      ];
+      // 单独判断个人信息页面[学校设置]有引导
+      if (route.path === '/setting') {
+        return helpNoteList.baseListTab === 'school' ? true : false;
+      } else {
+        return hidePath.includes(routePath) ? false : true;
+      }
+    });
+    return () => (
+      <>
+        <div class={styles.layoutTop}>
+          <div class={styles.layoutLeft}>
+            <NImage
+              src={schoolIcon}
+              class={styles.schoolIcon}
+              previewDisabled></NImage>
+            <p>
+              {/* {info.value.schoolInfos[0].tenantName} |{' '} */}
+              {(info.value?.schoolInfos && info.value?.schoolInfos[0].name) ||
+                ''}
+            </p>
+          </div>
+          <div class={styles.layoutRight}>
+            <NTooltip showArrow={false}>
+              {{
+                trigger: () => (
+                  <div
+                    class={[
+                      styles.optons,
+                      !helpNoteStatus.value && styles.booxToolDisabled
+                    ]}
+                    id="home-1"
+                    onClick={() => {
+                      if (!helpNoteStatus.value) return;
+                      // 默认滚动到页面顶部,在显示指引
+                      document
+                        .querySelector('#WrapcoreViewWrap')
+                        ?.scrollTo(0, 0);
+                      console.log(route.name, 'guideInfo');
+                      eventGlobal.emit('teacher-guideInfo', route.name);
+                    }}>
+                    <NImage src={gnydIcon} previewDisabled></NImage>
+                  </div>
+                ),
+                default: '功能引导'
+              }}
+            </NTooltip>
+            {/* <NTooltip showArrow={false}>
+              {{
+                trigger: () => (
+                  <div class={styles.optons} onClick={showInviteQrcode}>
+                    <NBadge dot={suggestionStatus.value} color={'#FF1036'}>
+                      <NImage src={inviteIcon} previewDisabled></NImage>
+                    </NBadge>
+                  </div>
+                ),
+                default: '邀请学生'
+              }}
+            </NTooltip> */}
+            <NPopover
+              width={380}
+              class={styles.popoverClassModel}
+              placement={'bottom'}
+              v-model:show={classRecordStatus.value}
+              trigger="click"
+              displayDirective="show"
+              v-slots={{
+                trigger: () => (
+                  <NTooltip showArrow={false}>
+                    {{
+                      trigger: () => (
+                        <div class={styles.optons}>
+                          <NImage
+                            src={classHistoryIcon}
+                            previewDisabled></NImage>
+                        </div>
+                      ),
+                      default: '上课记录'
+                    }}
+                  </NTooltip>
+                )
+              }}>
+              <ClassModal
+                onConfirm={() => {
+                  classRecordStatus.value = false;
+                }}
+              />
+            </NPopover>
+            <NTooltip showArrow={false}>
+              {{
+                trigger: () => (
+                  <div class={styles.optons} onClick={showOption} id="home-2">
+                    <NBadge dot={suggestionStatus.value} color={'#FF1036'}>
+                      <NImage src={opinionIcon} previewDisabled></NImage>
+                    </NBadge>
+                  </div>
+                ),
+                default: '意见反馈'
+              }}
+            </NTooltip>
+            {/* </div> */}
+            <div onClick={() => (showImGroup.value = true)}>
+              <NTooltip showArrow={false}>
+                {{
+                  trigger: () => (
+                    <NBadge
+                      value={noReadCount.value}
+                      max={99}
+                      class={[
+                        noReadCount.value > 0 ? '' : styles.messageBadgeHide,
+                        styles.messageBadge,
+                        noReadCount.value > 0 ? '' : styles.messageBadgeNo
+                      ]}
+                      {...{ id: 'home-3' }}
+                      color={'#FF1036'}>
+                      <NImage
+                        class={[
+                          styles.messageIcon,
+                          noReadCount.value > 0 ? styles.animation : ''
+                        ]}
+                        preview-disabled
+                        src={messageIcon}></NImage>
+                    </NBadge>
+                  ),
+                  default: '聊天'
+                }}
+              </NTooltip>
+            </div>
+
+            <div class={styles.line}></div>
+            <NPopover
+              show-arrow={false}
+              trigger="click"
+              v-model:show={userInfoStatus.value}
+              onUpdate:show={val => {
+                showHeadFlag.value = val;
+              }}
+              class={styles.popoverHeader}
+              placement="bottom-end"
+              raw={true}
+              v-slots={{
+                trigger: () => (
+                  <div class={styles.mesgWrap} style={{ cursor: 'pointer' }}>
+                    <NImage
+                      preview-disabled
+                      class={styles.teacherIcon}
+                      src={
+                        info.value.avatar ? info.value.avatar : teacherIcon
+                      }></NImage>
+                    <NIcon
+                      class={
+                        showHeadFlag.value
+                          ? styles.rotueLeft
+                          : styles.rotueRight
+                      }>
+                      <svg
+                        xmlns="http://www.w3.org/2000/svg"
+                        viewBox="0 0 24 24">
+                        <path
+                          d="M7.38 21.01c.49.49 1.28.49 1.77 0l8.31-8.31a.996.996 0 0 0 0-1.41L9.15 2.98c-.49-.49-1.28-.49-1.77 0s-.49 1.28 0 1.77L14.62 12l-7.25 7.25c-.48.48-.48 1.28.01 1.76z"
+                          fill="currentColor"></path>
+                      </svg>
+                    </NIcon>
+                  </div>
+                )
+              }}>
+              <div class={styles.propWrap}>
+                <div class={styles.teacherInfo}>
+                  <NImage
+                    class={styles.teacherIcon}
+                    src={info.value.avatar ? info.value.avatar : teacherIcon}
+                    previewDisabled></NImage>
+                  <NTooltip class={styles.nameTool}>
+                    {{
+                      trigger: () => (
+                        <p class={styles.teacherName}>{info.value.nickname}</p>
+                      ),
+                      default: () => info.value.nickname
+                    }}
+                  </NTooltip>
+                </div>
+                <div class={styles.propWrapList}>
+                  <div
+                    class={styles.propWrapItem}
+                    onClick={() => oncheckEditStatus(gotoPerson)}>
+                    <NImage
+                      class={styles.smallIcon}
+                      src={personIcon}
+                      previewDisabled></NImage>
+                    <p class={styles.smallTitle}>个人信息</p>
+                  </div>
+                  {info.value.isSuperAdmin ? (
+                    <div
+                      class={styles.propWrapItem}
+                      onClick={() => {
+                        oncheckEditStatus(gotoSchool);
+                      }}>
+                      <NImage
+                        class={styles.smallIcon}
+                        src={schoolDot}
+                        previewDisabled></NImage>
+                      <p class={styles.smallTitle}>学校信息</p>
+                    </div>
+                  ) : null}
+
+                  <div class={styles.propWrapItem} onClick={() => resetPwd()}>
+                    <NImage
+                      class={styles.smallIcon}
+                      src={clockIcon}
+                      previewDisabled></NImage>
+                    <p class={styles.smallTitle}>修改密码</p>
+                  </div>
+
+                  <div
+                    class={styles.propWrapItem}
+                    onClick={() => oncheckEditStatus(aboutUs)}>
+                    <NImage
+                      class={styles.smallIcon}
+                      src={iconAboutus}
+                      previewDisabled></NImage>
+                    <p class={styles.smallTitle}>关于我们</p>
+                  </div>
+                </div>
+                <div
+                  class={styles.logoutInfo}
+                  onClick={() => {
+                    users.logout();
+                    router.replace('/login');
+                  }}>
+                  <div class={styles.propWrapItem}>
+                    <NImage
+                      class={styles.smallIcon}
+                      src={closeIcon}
+                      previewDisabled></NImage>
+                    <p class={styles.smallTitle}>退出登录</p>
+                  </div>
+                </div>
+              </div>
+            </NPopover>
+            <div class={styles2.isHidden}></div>
+          </div>
+          <NModal
+            maskClosable={modalClickMask}
+            class={styles.changePwdModal}
+            v-model:show={showWord.value}
+            preset="dialog"
+            showIcon={false}
+            title="修改密码">
+            <ForgotPassword
+              phone={info.value.phone}
+              onClose={() => {
+                showWord.value = false;
+              }}
+            />
+          </NModal>
+
+          <NModal
+            maskClosable={modalClickMask}
+            v-model:show={showImGroup.value}
+            showIcon={false}
+            class={showImGroupLoading.value ? styles.hideModal : ''}
+            {...{ id: 'imGroupDiv' }}
+            displayDirective="show">
+            <ImGroup />
+          </NModal>
+
+          <NModal
+            maskClosable={modalClickMask}
+            class={['modalTitle', 'background', styles.suggestWrap]}
+            v-model:show={showSuggestionViseble.value}
+            display-directive="show"
+            showIcon={false}>
+            <SuggestionOption
+              ref={suggestionOptionRef}
+              onClose={() =>
+                (showSuggestionViseble.value = false)
+              }></SuggestionOption>
+          </NModal>
+
+          {/* {state.addStudentVisible ? (
+            <div
+              v-model:show={state.addStudentVisible}
+              class={['n-modal-mask', styles.popBox]}>
+              <AddStudentModel
+                activeRow={state.activeRow}
+                onClose={() => {
+                  state.addStudentVisible = false;
+                }}></AddStudentModel>
+            </div>
+          ) : null} */}
+        </div>
+      </>
+    );
+  }
+});

+ 453 - 451
src/components/layout/modals/suggestion-option.tsx

@@ -1,451 +1,453 @@
-import { defineComponent, onMounted, reactive, ref } from 'vue';
-import styles from './suggestion-option.module.less';
-import {
-  NButton,
-  NForm,
-  NFormItem,
-  NInput,
-  NSelect,
-  NSpace,
-  useMessage,
-  NUpload,
-  UploadFileInfo,
-  NImage,
-  UploadCustomRequestOptions,
-  NModal,
-  NBadge
-} from 'naive-ui';
-import { useUserStore } from '/src/store/modules/users';
-import bgLine from '../images/bg-line.png';
-import chioseAdd from '../images/chioseAdd.png';
-import CSelect from '../../CSelect';
-import { policy } from '../../upload-file/api';
-import suggestClose from '../images/suggestClose.png';
-import inFront from '../images/inFront.png';
-import inBack from '../images/inBack.png';
-import submitBtn from '../images/submitBtn.png';
-import sealing from '../images/sealing.png';
-import boxBg from '../images/boxBg.png';
-import {
-  addSuggestion,
-  getSuggestionList,
-  sysParamConfigPage
-} from '../modals/api';
-import { nextTick } from 'process';
-import { getUploadSign, onFileUpload } from '/src/helpers/oss-file-upload';
-import SuggestionList from './suggestion-list';
-import { suggestMessageUnread } from '/src/api/user';
-
-export default defineComponent({
-  name: 'train-update',
-  emits: ['close', 'submit'],
-  setup(props, { emit, expose }) {
-    const message = useMessage();
-    const userStore = useUserStore();
-    const forms = reactive({
-      suggestionTypeId: null,
-      clientType: 'TEACHER',
-      content: '',
-      attachmentUrls: '',
-      type: 'APP',
-      mobileNo: userStore.getUserInfo.phone
-    });
-    const state = reactive([]) as any;
-    const isubmit = ref(false);
-
-    const showSuggestion = ref(false);
-    const suggestionTypeList = ref([] as any);
-    const ossUploadUrl = `https://gyt.ks3-cn-beijing.ksyuncs.com/`;
-    const uploadRef = ref();
-    const fileListRef = ref<UploadFileInfo[]>([]);
-    const formsRef = ref();
-    const btnLoading = ref(false);
-    const tempFiileBuffer = ref();
-    const email = ref('');
-    const phone = ref('');
-    const isLoading = ref(false);
-    const ishidden = ref(false);
-    const onSubmit = async () => {
-      // if (!forms.suggestionTypeId) {
-      //   message.error('请选择反馈类型');
-      //   return;
-      // }
-      // if (!forms.content) {
-      //   message.error('请填写反馈信息');
-      //   return;
-      // }
-      formsRef.value?.validate(async (err: any) => {
-        if (err) {
-          return;
-        }
-        const attachmentUrlsList = fileListRef.value.map((item: any) => {
-          // console.log(item, 'item');
-          // const name = item.name;
-          // // const suffix = name.slice(name.lastIndexOf('.'));
-          // const fileName = `${item.id + name}`;
-          // const url = ossUploadUrl + fileName;
-          return item.url;
-        });
-        const attachmentUrls = attachmentUrlsList.join(',');
-        try {
-          const res = await addSuggestion({ ...forms, attachmentUrls });
-          isubmit.value = true;
-          // message.success('提交成功');
-          setTimeout(() => {
-            onReset();
-            ishidden.value = true;
-            emit('close');
-          }, 3000);
-        } catch (e) {
-          console.log(e);
-        }
-        console.log('onSubmit');
-      });
-    };
-    const onReset = () => {
-      ishidden.value = false;
-      isubmit.value = false;
-      forms.suggestionTypeId = null;
-      forms.clientType = 'TEACHER';
-      forms.content = '';
-      forms.attachmentUrls = '';
-      fileListRef.value = [];
-      forms.type = 'APP';
-      forms.mobileNo = userStore.getUserInfo.phone;
-    };
-    expose({ onReset });
-    const onBeforeUpload = async (options: any) => {
-      console.log(options, 'onBeforeUpload');
-      const file = options.file;
-      // 文件大小
-      let isLt2M = true;
-
-      const size = 2;
-      if (size) {
-        isLt2M = file.file.size / 1024 / 1024 < size;
-        if (!isLt2M) {
-          message.error(`文件大小不能超过${size}M`);
-          return false;
-        }
-      }
-
-      if (!isLt2M) {
-        return isLt2M;
-      }
-      // 是否裁切
-      try {
-        btnLoading.value = true;
-        const name = file.file.name;
-        const fileName = `${file.id + name}`;
-        const obj = {
-          filename: fileName,
-          bucketName: 'gyt',
-          postData: {
-            filename: fileName,
-            acl: 'public-read',
-            key: fileName,
-            unknowValueField: []
-          }
-        };
-        // const { data } = await policy(obj);
-        const { data } = await getUploadSign(obj);
-        state.push({
-          id: file.id,
-          tempFiileBuffer: file.file,
-          policy: data.policy,
-          signature: data.signature,
-          acl: 'public-read',
-          key: fileName,
-          KSSAccessKeyId: data.kssAccessKeyId,
-          name: fileName
-        });
-      } catch {
-        //
-        // message.error('上传失败')
-        btnLoading.value = false;
-        return false;
-      }
-      return true;
-    };
-    const onCustomRequest = ({
-      file,
-      // data,
-      // headers,
-      // withCredentials,
-      action,
-      onFinish,
-      onError,
-      onProgress
-    }: UploadCustomRequestOptions) => {
-      const item = state.find((c: any) => {
-        return c.id == file.id;
-      });
-
-      item.file = file;
-      onFileUpload({ file, action, data: item, onProgress, onFinish, onError });
-    };
-    const onFinish = (options: any) => {
-      // const url =
-      const name = options.file.name;
-      // const suffix = name.slice(name.lastIndexOf('.'));
-      const fileName = `${options.file.id + name}`;
-      const url = ossUploadUrl + fileName;
-      // urlList.value.push(url);
-      // onFinishAfter(options);
-    };
-    const onRemove = async (event: any) => {
-      console.log(event);
-      btnLoading.value = false;
-    };
-
-    const getTypeList = async () => {
-      try {
-        const res = await getSuggestionList({ rows: 9999, page: 1 });
-        suggestionTypeList.value = res.data.rows || [];
-      } catch (e) {
-        console.log(e);
-      }
-    };
-
-    const getPhoneInfo = async () => {
-      try {
-        const { data } = await sysParamConfigPage({
-          page: 1,
-          rows: 999,
-          group: 'OTHER'
-        });
-        const rows = data.rows || [];
-        email.value = rows.find((item: any) => {
-          return item.paramName == 'customer_service_email';
-        }).paramValue;
-        phone.value = rows.find((item: any) => {
-          return item.paramName == 'customer_service_phone';
-        }).paramValue;
-
-        console.log(email.value, phone.value);
-      } catch (e) {
-        console.log('请求报错');
-        console.log(e);
-      }
-    };
-
-    const imglist = [inFront, inBack, submitBtn, sealing, boxBg];
-    const loadImg = (imgList: any) => {
-      for (let i = 0; i < imgList.length; i++) {
-        const img = new Image();
-        // let currentSrc = ''
-        img.src = imgList[i];
-        img.onload = function (e) {
-          // console.log('加载完毕', e, img.complete);
-        };
-        img.onerror = function (e) {
-          // console.log('加载错误', e);
-        };
-      }
-    };
-    loadImg(imglist);
-
-    const suggestionStatus = ref(false);
-    const getSuggestMessageUnread = async () => {
-      try {
-        const { data } = await suggestMessageUnread();
-        const temp = data || [];
-        temp.forEach((item: any) => {
-          if (item.group === 'SYSTEM') {
-            suggestionStatus.value = item.number > 0 ? true : false;
-          }
-        });
-      } catch {
-        //
-      }
-    };
-
-    onMounted(() => {
-      // getSuggestMessageUnread();
-      getTypeList();
-      getPhoneInfo();
-    });
-    return () => (
-      <div class={[styles.suggestOption]}>
-        <div
-          class={[
-            styles.updatePassword,
-            isubmit.value ? styles.isAni : '',
-            ishidden.value ? styles.isend : null
-          ]}>
-          <div class={[styles.formWrap, isubmit.value ? styles.isAni : '']}>
-            <NImage
-              class={styles.closeBtn}
-              src={suggestClose}
-              previewDisabled
-              onClick={() => {
-                onReset();
-                emit('close');
-              }}></NImage>
-            <NImage class={styles.bgLine} src={bgLine} previewDisabled></NImage>
-            <h2 class={styles.formTitle}>
-              意见反馈
-              {/* suggestionStatus */}
-              <NBadge
-                dot={suggestionStatus.value}
-                color={'#FF1036'}
-                offset={[-5, 4]}
-                class={styles.suggestionBtnDot}>
-                <NButton
-                  type="primary"
-                  round
-                  secondary
-                  class={styles.suggestionBtn}
-                  onClick={() => (showSuggestion.value = true)}>
-                  反馈记录
-                </NButton>
-              </NBadge>
-            </h2>
-
-            <div class={styles.formWrapInfo}>
-              <NForm
-                labelAlign="right"
-                labelPlacement="left"
-                labelWidth={'auto'}
-                ref={formsRef}
-                model={forms}
-                requireMarkPlacement="left">
-                {/* <NFormItem
-            path="currentClass"
-            label=""
-            class={styles.phoneContainer}>
-            <p class={styles.phone}>{forms.mobile}</p>
-          </NFormItem> */}
-
-                <NFormItem
-                  rule={[
-                    {
-                      required: true,
-                      message: '请选择反馈类型'
-                    }
-                  ]}
-                  path="suggestionTypeId">
-                  <CSelect
-                    class={styles.suggestSelect}
-                    value-field="id"
-                    label-field="name"
-                    style={{ width: '227px!important' }}
-                    {...({
-                      options: suggestionTypeList.value,
-                      placeholder: '反馈类型(必选)',
-                      clearable: true,
-                      inline: true
-                    } as any)}
-                    v-model:value={forms.suggestionTypeId}></CSelect>
-                </NFormItem>
-                <NFormItem
-                  path="content"
-                  rule={[
-                    {
-                      required: true,
-                      message: '请输入反馈内容'
-                    }
-                  ]}>
-                  <NInput
-                    class={styles.countInput}
-                    type="textarea"
-                    rows={5}
-                    placeholder={'请输入反馈内容'}
-                    maxlength={200}
-                    resizable={false}
-                    showCount
-                    v-model:value={forms.content}></NInput>
-                </NFormItem>
-                <NFormItem>
-                  <NUpload
-                    list-type="image-card"
-                    accept=".jpg,jpeg,.png"
-                    v-model:fileList={fileListRef.value}
-                    ref={uploadRef}
-                    multiple={true}
-                    max={5}
-                    // data={(file: any) => {
-                    //   const item = state.find((c: any) => {
-                    //     return c.id == file.file.id;
-                    //   });
-                    //   const { id, tempFiileBuffer, ...more } = item;
-                    //   return { ...more };
-                    // }}
-                    showPreviewButton
-                    action={ossUploadUrl}
-                    customRequest={onCustomRequest}
-                    onBeforeUpload={(options: any) => onBeforeUpload(options)}
-                    onRemove={(options: any) => onRemove(options)}
-                    onFinish={(options: any) => onFinish(options)}>
-                    <div class={styles.addInput}>
-                      <NImage previewDisabled src={chioseAdd}></NImage>
-                      <p> 点击上传图片</p>
-                      <p>(最多五张)</p>
-                    </div>
-                  </NUpload>
-                </NFormItem>
-                {/* {phone.value || email.value ? (
-                <NFormItem>
-                  <div class={styles.messageWrap}>
-                    {phone.value ? <p>客服电话:{phone.value}</p> : null}
-                    {email.value ? <p>邮箱:{email.value}</p> : null}
-                  </div>
-                </NFormItem>
-              ) : null} */}
-
-                {/* <NSpace class={styles.updateBtnGroup}>
-                <NButton
-                  strong
-                  type="default"
-                  round
-                  onClick={() => emit('close')}>
-                  取消
-                </NButton>
-                <NButton strong type="primary" round onClick={() => onSubmit()}>
-                  确认
-                </NButton>
-              </NSpace> */}
-              </NForm>
-            </div>
-          </div>
-          <div class={[styles.inBack, isubmit.value ? styles.isAni : '']}></div>
-          {/* <div class={styles.inBackBottom}></div>  boxBg */}
-          <NImage
-            src={boxBg}
-            class={styles.inBackBottom}
-            previewDisabled></NImage>
-          <NImage src={inFront} class={styles.inFront} previewDisabled></NImage>
-          <NImage
-            src={sealing}
-            class={[styles.sealing, isubmit.value ? styles.isAni : '']}
-            previewDisabled></NImage>
-          {!isubmit.value ? (
-            <>
-              <NImage
-                src={submitBtn}
-                onClick={() => {
-                  onSubmit();
-                }}
-                class={styles.submitBtn}
-                previewDisabled></NImage>
-              <div class={styles.messageWrap}>
-                {phone.value ? <p>客服电话:{phone.value}</p> : null}
-                {email.value ? <p>邮箱:{email.value}</p> : null}
-              </div>
-            </>
-          ) : null}
-        </div>
-
-        <NModal
-          v-model:show={showSuggestion.value}
-          class={['modalTitle background']}
-          title={'反馈记录'}
-          preset="card"
-          closeOnEsc={false}
-          style={{ width: '758px' }}>
-          <SuggestionList typeList={suggestionTypeList.value} />
-        </NModal>
-      </div>
-    );
-  }
-});
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from './suggestion-option.module.less';
+import {
+  NButton,
+  NForm,
+  NFormItem,
+  NInput,
+  NSelect,
+  NSpace,
+  useMessage,
+  NUpload,
+  UploadFileInfo,
+  NImage,
+  UploadCustomRequestOptions,
+  NModal,
+  NBadge
+} from 'naive-ui';
+import { useUserStore } from '/src/store/modules/users';
+import bgLine from '../images/bg-line.png';
+import chioseAdd from '../images/chioseAdd.png';
+import CSelect from '../../CSelect';
+import { policy } from '../../upload-file/api';
+import suggestClose from '../images/suggestClose.png';
+import inFront from '../images/inFront.png';
+import inBack from '../images/inBack.png';
+import submitBtn from '../images/submitBtn.png';
+import sealing from '../images/sealing.png';
+import boxBg from '../images/boxBg.png';
+import {
+  addSuggestion,
+  getSuggestionList,
+  sysParamConfigPage
+} from '../modals/api';
+import { nextTick } from 'process';
+import { getUploadSign, onFileUpload } from '/src/helpers/oss-file-upload';
+import SuggestionList from './suggestion-list';
+import { suggestMessageUnread } from '/src/api/user';
+import { modalClickMask } from '/src/state';
+
+export default defineComponent({
+  name: 'train-update',
+  emits: ['close', 'submit'],
+  setup(props, { emit, expose }) {
+    const message = useMessage();
+    const userStore = useUserStore();
+    const forms = reactive({
+      suggestionTypeId: null,
+      clientType: 'TEACHER',
+      content: '',
+      attachmentUrls: '',
+      type: 'APP',
+      mobileNo: userStore.getUserInfo.phone
+    });
+    const state = reactive([]) as any;
+    const isubmit = ref(false);
+
+    const showSuggestion = ref(false);
+    const suggestionTypeList = ref([] as any);
+    const ossUploadUrl = `https://gyt.ks3-cn-beijing.ksyuncs.com/`;
+    const uploadRef = ref();
+    const fileListRef = ref<UploadFileInfo[]>([]);
+    const formsRef = ref();
+    const btnLoading = ref(false);
+    const tempFiileBuffer = ref();
+    const email = ref('');
+    const phone = ref('');
+    const isLoading = ref(false);
+    const ishidden = ref(false);
+    const onSubmit = async () => {
+      // if (!forms.suggestionTypeId) {
+      //   message.error('请选择反馈类型');
+      //   return;
+      // }
+      // if (!forms.content) {
+      //   message.error('请填写反馈信息');
+      //   return;
+      // }
+      formsRef.value?.validate(async (err: any) => {
+        if (err) {
+          return;
+        }
+        const attachmentUrlsList = fileListRef.value.map((item: any) => {
+          // console.log(item, 'item');
+          // const name = item.name;
+          // // const suffix = name.slice(name.lastIndexOf('.'));
+          // const fileName = `${item.id + name}`;
+          // const url = ossUploadUrl + fileName;
+          return item.url;
+        });
+        const attachmentUrls = attachmentUrlsList.join(',');
+        try {
+          const res = await addSuggestion({ ...forms, attachmentUrls });
+          isubmit.value = true;
+          // message.success('提交成功');
+          setTimeout(() => {
+            onReset();
+            ishidden.value = true;
+            emit('close');
+          }, 3000);
+        } catch (e) {
+          console.log(e);
+        }
+        console.log('onSubmit');
+      });
+    };
+    const onReset = () => {
+      ishidden.value = false;
+      isubmit.value = false;
+      forms.suggestionTypeId = null;
+      forms.clientType = 'TEACHER';
+      forms.content = '';
+      forms.attachmentUrls = '';
+      fileListRef.value = [];
+      forms.type = 'APP';
+      forms.mobileNo = userStore.getUserInfo.phone;
+    };
+    expose({ onReset });
+    const onBeforeUpload = async (options: any) => {
+      console.log(options, 'onBeforeUpload');
+      const file = options.file;
+      // 文件大小
+      let isLt2M = true;
+
+      const size = 2;
+      if (size) {
+        isLt2M = file.file.size / 1024 / 1024 < size;
+        if (!isLt2M) {
+          message.error(`文件大小不能超过${size}M`);
+          return false;
+        }
+      }
+
+      if (!isLt2M) {
+        return isLt2M;
+      }
+      // 是否裁切
+      try {
+        btnLoading.value = true;
+        const name = file.file.name;
+        const fileName = `${file.id + name}`;
+        const obj = {
+          filename: fileName,
+          bucketName: 'gyt',
+          postData: {
+            filename: fileName,
+            acl: 'public-read',
+            key: fileName,
+            unknowValueField: []
+          }
+        };
+        // const { data } = await policy(obj);
+        const { data } = await getUploadSign(obj);
+        state.push({
+          id: file.id,
+          tempFiileBuffer: file.file,
+          policy: data.policy,
+          signature: data.signature,
+          acl: 'public-read',
+          key: fileName,
+          KSSAccessKeyId: data.kssAccessKeyId,
+          name: fileName
+        });
+      } catch {
+        //
+        // message.error('上传失败')
+        btnLoading.value = false;
+        return false;
+      }
+      return true;
+    };
+    const onCustomRequest = ({
+      file,
+      // data,
+      // headers,
+      // withCredentials,
+      action,
+      onFinish,
+      onError,
+      onProgress
+    }: UploadCustomRequestOptions) => {
+      const item = state.find((c: any) => {
+        return c.id == file.id;
+      });
+
+      item.file = file;
+      onFileUpload({ file, action, data: item, onProgress, onFinish, onError });
+    };
+    const onFinish = (options: any) => {
+      // const url =
+      const name = options.file.name;
+      // const suffix = name.slice(name.lastIndexOf('.'));
+      const fileName = `${options.file.id + name}`;
+      const url = ossUploadUrl + fileName;
+      // urlList.value.push(url);
+      // onFinishAfter(options);
+    };
+    const onRemove = async (event: any) => {
+      console.log(event);
+      btnLoading.value = false;
+    };
+
+    const getTypeList = async () => {
+      try {
+        const res = await getSuggestionList({ rows: 9999, page: 1 });
+        suggestionTypeList.value = res.data.rows || [];
+      } catch (e) {
+        console.log(e);
+      }
+    };
+
+    const getPhoneInfo = async () => {
+      try {
+        const { data } = await sysParamConfigPage({
+          page: 1,
+          rows: 999,
+          group: 'OTHER'
+        });
+        const rows = data.rows || [];
+        email.value = rows.find((item: any) => {
+          return item.paramName == 'customer_service_email';
+        }).paramValue;
+        phone.value = rows.find((item: any) => {
+          return item.paramName == 'customer_service_phone';
+        }).paramValue;
+
+        console.log(email.value, phone.value);
+      } catch (e) {
+        console.log('请求报错');
+        console.log(e);
+      }
+    };
+
+    const imglist = [inFront, inBack, submitBtn, sealing, boxBg];
+    const loadImg = (imgList: any) => {
+      for (let i = 0; i < imgList.length; i++) {
+        const img = new Image();
+        // let currentSrc = ''
+        img.src = imgList[i];
+        img.onload = function (e) {
+          // console.log('加载完毕', e, img.complete);
+        };
+        img.onerror = function (e) {
+          // console.log('加载错误', e);
+        };
+      }
+    };
+    loadImg(imglist);
+
+    const suggestionStatus = ref(false);
+    const getSuggestMessageUnread = async () => {
+      try {
+        const { data } = await suggestMessageUnread();
+        const temp = data || [];
+        temp.forEach((item: any) => {
+          if (item.group === 'SYSTEM') {
+            suggestionStatus.value = item.number > 0 ? true : false;
+          }
+        });
+      } catch {
+        //
+      }
+    };
+
+    onMounted(() => {
+      // getSuggestMessageUnread();
+      getTypeList();
+      getPhoneInfo();
+    });
+    return () => (
+      <div class={[styles.suggestOption]}>
+        <div
+          class={[
+            styles.updatePassword,
+            isubmit.value ? styles.isAni : '',
+            ishidden.value ? styles.isend : null
+          ]}>
+          <div class={[styles.formWrap, isubmit.value ? styles.isAni : '']}>
+            <NImage
+              class={styles.closeBtn}
+              src={suggestClose}
+              previewDisabled
+              onClick={() => {
+                onReset();
+                emit('close');
+              }}></NImage>
+            <NImage class={styles.bgLine} src={bgLine} previewDisabled></NImage>
+            <h2 class={styles.formTitle}>
+              意见反馈
+              {/* suggestionStatus */}
+              <NBadge
+                dot={suggestionStatus.value}
+                color={'#FF1036'}
+                offset={[-5, 4]}
+                class={styles.suggestionBtnDot}>
+                <NButton
+                  type="primary"
+                  round
+                  secondary
+                  class={styles.suggestionBtn}
+                  onClick={() => (showSuggestion.value = true)}>
+                  反馈记录
+                </NButton>
+              </NBadge>
+            </h2>
+
+            <div class={styles.formWrapInfo}>
+              <NForm
+                labelAlign="right"
+                labelPlacement="left"
+                labelWidth={'auto'}
+                ref={formsRef}
+                model={forms}
+                requireMarkPlacement="left">
+                {/* <NFormItem
+            path="currentClass"
+            label=""
+            class={styles.phoneContainer}>
+            <p class={styles.phone}>{forms.mobile}</p>
+          </NFormItem> */}
+
+                <NFormItem
+                  rule={[
+                    {
+                      required: true,
+                      message: '请选择反馈类型'
+                    }
+                  ]}
+                  path="suggestionTypeId">
+                  <CSelect
+                    class={styles.suggestSelect}
+                    value-field="id"
+                    label-field="name"
+                    style={{ width: '227px!important' }}
+                    {...({
+                      options: suggestionTypeList.value,
+                      placeholder: '反馈类型(必选)',
+                      clearable: true,
+                      inline: true
+                    } as any)}
+                    v-model:value={forms.suggestionTypeId}></CSelect>
+                </NFormItem>
+                <NFormItem
+                  path="content"
+                  rule={[
+                    {
+                      required: true,
+                      message: '请输入反馈内容'
+                    }
+                  ]}>
+                  <NInput
+                    class={styles.countInput}
+                    type="textarea"
+                    rows={5}
+                    placeholder={'请输入反馈内容'}
+                    maxlength={200}
+                    resizable={false}
+                    showCount
+                    v-model:value={forms.content}></NInput>
+                </NFormItem>
+                <NFormItem>
+                  <NUpload
+                    list-type="image-card"
+                    accept=".jpg,jpeg,.png"
+                    v-model:fileList={fileListRef.value}
+                    ref={uploadRef}
+                    multiple={true}
+                    max={5}
+                    // data={(file: any) => {
+                    //   const item = state.find((c: any) => {
+                    //     return c.id == file.file.id;
+                    //   });
+                    //   const { id, tempFiileBuffer, ...more } = item;
+                    //   return { ...more };
+                    // }}
+                    showPreviewButton
+                    action={ossUploadUrl}
+                    customRequest={onCustomRequest}
+                    onBeforeUpload={(options: any) => onBeforeUpload(options)}
+                    onRemove={(options: any) => onRemove(options)}
+                    onFinish={(options: any) => onFinish(options)}>
+                    <div class={styles.addInput}>
+                      <NImage previewDisabled src={chioseAdd}></NImage>
+                      <p> 点击上传图片</p>
+                      <p>(最多五张)</p>
+                    </div>
+                  </NUpload>
+                </NFormItem>
+                {/* {phone.value || email.value ? (
+                <NFormItem>
+                  <div class={styles.messageWrap}>
+                    {phone.value ? <p>客服电话:{phone.value}</p> : null}
+                    {email.value ? <p>邮箱:{email.value}</p> : null}
+                  </div>
+                </NFormItem>
+              ) : null} */}
+
+                {/* <NSpace class={styles.updateBtnGroup}>
+                <NButton
+                  strong
+                  type="default"
+                  round
+                  onClick={() => emit('close')}>
+                  取消
+                </NButton>
+                <NButton strong type="primary" round onClick={() => onSubmit()}>
+                  确认
+                </NButton>
+              </NSpace> */}
+              </NForm>
+            </div>
+          </div>
+          <div class={[styles.inBack, isubmit.value ? styles.isAni : '']}></div>
+          {/* <div class={styles.inBackBottom}></div>  boxBg */}
+          <NImage
+            src={boxBg}
+            class={styles.inBackBottom}
+            previewDisabled></NImage>
+          <NImage src={inFront} class={styles.inFront} previewDisabled></NImage>
+          <NImage
+            src={sealing}
+            class={[styles.sealing, isubmit.value ? styles.isAni : '']}
+            previewDisabled></NImage>
+          {!isubmit.value ? (
+            <>
+              <NImage
+                src={submitBtn}
+                onClick={() => {
+                  onSubmit();
+                }}
+                class={styles.submitBtn}
+                previewDisabled></NImage>
+              <div class={styles.messageWrap}>
+                {phone.value ? <p>客服电话:{phone.value}</p> : null}
+                {email.value ? <p>邮箱:{email.value}</p> : null}
+              </div>
+            </>
+          ) : null}
+        </div>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={showSuggestion.value}
+          class={['modalTitle background']}
+          title={'反馈记录'}
+          preset="card"
+          closeOnEsc={false}
+          style={{ width: '758px' }}>
+          <SuggestionList typeList={suggestionTypeList.value} />
+        </NModal>
+      </div>
+    );
+  }
+});

+ 261 - 260
src/components/layout/modals/update-password.tsx

@@ -1,260 +1,261 @@
-import { defineComponent, onMounted, reactive, ref } from 'vue';
-import styles from './update-password.module.less';
-import {
-  NButton,
-  NForm,
-  NFormItem,
-  NInput,
-  NModal,
-  NSelect,
-  NSpace,
-  useMessage
-} from 'naive-ui';
-import { useRouter } from 'vue-router';
-import { useUserStore } from '/src/store/modules/users';
-import { gradeToCN } from '/src/utils/contants';
-import { sendSms } from '/src/views/login/api';
-import { updatePassword } from '@/views/home/api';
-import openEye from '/src/views/login/images/openEye.png';
-import closeEye from '/src/views/login/images/closeEye.png';
-import SendSms from '/src/views/login/components/sendSms';
-export default defineComponent({
-  name: 'train-update',
-  emits: ['close', 'submit'],
-  setup(props, { emit }) {
-    const message = useMessage();
-    const userStore = useUserStore();
-    const showSmsClass = ref(false);
-    const forms = reactive({
-      mobile: userStore.getUserInfo.phone || '',
-      password: null,
-      rePassword: null,
-      clientType: 'TEACHER',
-      code: null
-    });
-
-    const password = reactive({
-      passowrdStatus: false,
-      rePasswordStatus: false
-    });
-
-    const validatePass2 = (rule: any, value: any, callback: any): any => {
-      const reg = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/;
-      if (value === '' || !value) {
-        callback(new Error('请再次输入新密码'));
-      } else if (value !== forms.password) {
-        callback(new Error('两次输入密码不一致'));
-      } else if (value.length < 8 || value.length > 20) {
-        callback(new Error('8~20位含数字、字母、特殊字符(如:%、&、#等)组合'));
-      } else {
-        callback();
-      }
-    };
-    const validatePass = (rule: any, value: any, callback: any): any => {
-      // const reg = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/;
-      if (value === '' || !value) {
-        callback(new Error('请输入新密码'));
-      } else if (value.length < 8 || value.length > 20) {
-        callback(new Error('8~20位含数字、字母、特殊字符(如:%、&、#等)组合'));
-      } else {
-        callback();
-      }
-    };
-
-    const gotoClassPage = () => {
-      formsRef.value.validate(async (error: any) => {
-        if (error) return;
-
-        if (forms.password !== forms.rePassword) {
-          message.error('两次输入密码不一致');
-          return;
-        }
-
-        await updatePassword({
-          // mobile: forms.mobile,
-          password: forms.password,
-          // clientType: 'TEACHER',
-          code: forms.code
-        });
-
-        message.success('更新成功');
-        emit('submit');
-      });
-    };
-
-    const formsRef = ref();
-
-    const isDisabledCode = ref(false);
-    const starTimer = ref(60);
-    const codeName = '发送短信';
-    const sendMessage = async () => {
-      if (!forms.mobile) {
-        message.error('请输入手机号');
-        return;
-      }
-      try {
-        // await sendSms({
-        //   clientId: 'cooleshow-teacher',
-        //   mobile: forms.mobile,
-        //   type: 'PASSWORD'
-        // });
-        // checkTimeOut();
-        showSmsClass.value = true;
-      } catch (e) {
-        console.log(e);
-      }
-    };
-
-    const checkTimeOut = () => {
-      if (isDisabledCode.value) {
-        return;
-      }
-      isDisabledCode.value = true;
-      const tiemr = setInterval(() => {
-        starTimer.value--;
-        console.log(starTimer.value);
-        if (starTimer.value <= 0) {
-          isDisabledCode.value = false;
-          clearInterval(tiemr);
-        }
-      }, 1000);
-    };
-
-    onMounted(async () => {
-      //
-    });
-    return () => (
-      <div class={styles.updatePassword}>
-        <p class={styles.tips}>
-          检测到您尚未修改默认密码,为了您的账户安全,请重新设置登录密码
-        </p>
-        <NForm
-          labelAlign="right"
-          labelPlacement="left"
-          labelWidth={'auto'}
-          ref={formsRef}
-          model={forms}
-          requireMarkPlacement="left">
-          <NFormItem
-            path="currentClass"
-            label="手机号"
-            class={styles.phoneContainer}>
-            <p class={styles.phone}>{forms.mobile}</p>
-          </NFormItem>
-          <NFormItem
-            path="password"
-            label="新密码"
-            rule={[
-              {
-                validator: validatePass,
-                trigger: 'blur',
-                required: true
-              }
-            ]}>
-            <NInput
-              v-model:value={forms.password}
-              clearable
-              type="text"
-              showPasswordOn="click"
-              inputProps={{ autocomplete: 'off' }}
-              class={[password.passowrdStatus ? '' : styles['no-pwd']]}
-              placeholder={'请输入新密码'}>
-              {{
-                suffix: () => (
-                  <img
-                    src={password.passowrdStatus ? openEye : closeEye}
-                    class={styles.pwdIcon}
-                    alt=""
-                    onClick={() => {
-                      password.passowrdStatus = !password.passowrdStatus;
-                    }}
-                  />
-                )
-              }}
-            </NInput>
-          </NFormItem>
-          <NFormItem
-            path="rePassword"
-            label="再次输入"
-            rule={[
-              {
-                validator: validatePass2,
-                trigger: 'blur',
-                required: true
-              }
-            ]}>
-            <NInput
-              v-model:value={forms.rePassword}
-              clearable
-              type="text"
-              showPasswordOn="click"
-              inputProps={{ autocomplete: 'off' }}
-              class={[password.rePasswordStatus ? '' : styles['no-pwd']]}
-              placeholder={'再次输入新密码'}>
-              {{
-                suffix: () => (
-                  <img
-                    src={password.rePasswordStatus ? openEye : closeEye}
-                    class={styles.pwdIcon}
-                    alt=""
-                    onClick={() => {
-                      password.rePasswordStatus = !password.rePasswordStatus;
-                    }}
-                  />
-                )
-              }}
-            </NInput>
-          </NFormItem>
-          <NFormItem
-            path="code"
-            label="验证码"
-            rule={[
-              {
-                required: true,
-                message: '请输入验证码',
-                trigger: 'blur'
-              }
-            ]}>
-            <NInput
-              v-model:value={forms.code}
-              placeholder={'请输入验证码'}
-              clearable
-              class={styles.sendInput}
-              maxlength={6}>
-              {{
-                suffix: () => (
-                  <NButton
-                    class={styles.sendMsg}
-                    disabled={isDisabledCode.value}
-                    onClick={() => sendMessage()}>
-                    {isDisabledCode.value ? starTimer.value + 'S' : codeName}
-                  </NButton>
-                )
-              }}
-            </NInput>
-          </NFormItem>
-          <div class={styles.updateBtnGroup}>
-            <NButton
-              strong
-              type="primary"
-              round
-              onClick={() => gotoClassPage()}>
-              确认
-            </NButton>
-          </div>
-        </NForm>
-
-        <NModal v-model:show={showSmsClass.value}>
-          <SendSms
-            phone={forms.mobile}
-            type="PASSWORD"
-            onClose={() => (showSmsClass.value = false)}
-            onSendCode={() => {
-              checkTimeOut();
-            }}
-          />
-        </NModal>
-      </div>
-    );
-  }
-});
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from './update-password.module.less';
+import {
+  NButton,
+  NForm,
+  NFormItem,
+  NInput,
+  NModal,
+  NSelect,
+  NSpace,
+  useMessage
+} from 'naive-ui';
+import { useRouter } from 'vue-router';
+import { useUserStore } from '/src/store/modules/users';
+import { gradeToCN } from '/src/utils/contants';
+import { sendSms } from '/src/views/login/api';
+import { updatePassword } from '@/views/home/api';
+import openEye from '/src/views/login/images/openEye.png';
+import closeEye from '/src/views/login/images/closeEye.png';
+import SendSms from '/src/views/login/components/sendSms';
+import { modalClickMask } from '/src/state';
+export default defineComponent({
+  name: 'train-update',
+  emits: ['close', 'submit'],
+  setup(props, { emit }) {
+    const message = useMessage();
+    const userStore = useUserStore();
+    const showSmsClass = ref(false);
+    const forms = reactive({
+      mobile: userStore.getUserInfo.phone || '',
+      password: null,
+      rePassword: null,
+      clientType: 'TEACHER',
+      code: null
+    });
+
+    const password = reactive({
+      passowrdStatus: false,
+      rePasswordStatus: false
+    });
+
+    const validatePass2 = (rule: any, value: any, callback: any): any => {
+      const reg = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/;
+      if (value === '' || !value) {
+        callback(new Error('请再次输入新密码'));
+      } else if (value !== forms.password) {
+        callback(new Error('两次输入密码不一致'));
+      } else if (value.length < 8 || value.length > 20) {
+        callback(new Error('8~20位含数字、字母、特殊字符(如:%、&、#等)组合'));
+      } else {
+        callback();
+      }
+    };
+    const validatePass = (rule: any, value: any, callback: any): any => {
+      // const reg = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/;
+      if (value === '' || !value) {
+        callback(new Error('请输入新密码'));
+      } else if (value.length < 8 || value.length > 20) {
+        callback(new Error('8~20位含数字、字母、特殊字符(如:%、&、#等)组合'));
+      } else {
+        callback();
+      }
+    };
+
+    const gotoClassPage = () => {
+      formsRef.value.validate(async (error: any) => {
+        if (error) return;
+
+        if (forms.password !== forms.rePassword) {
+          message.error('两次输入密码不一致');
+          return;
+        }
+
+        await updatePassword({
+          // mobile: forms.mobile,
+          password: forms.password,
+          // clientType: 'TEACHER',
+          code: forms.code
+        });
+
+        message.success('更新成功');
+        emit('submit');
+      });
+    };
+
+    const formsRef = ref();
+
+    const isDisabledCode = ref(false);
+    const starTimer = ref(60);
+    const codeName = '发送短信';
+    const sendMessage = async () => {
+      if (!forms.mobile) {
+        message.error('请输入手机号');
+        return;
+      }
+      try {
+        // await sendSms({
+        //   clientId: 'cooleshow-teacher',
+        //   mobile: forms.mobile,
+        //   type: 'PASSWORD'
+        // });
+        // checkTimeOut();
+        showSmsClass.value = true;
+      } catch (e) {
+        console.log(e);
+      }
+    };
+
+    const checkTimeOut = () => {
+      if (isDisabledCode.value) {
+        return;
+      }
+      isDisabledCode.value = true;
+      const tiemr = setInterval(() => {
+        starTimer.value--;
+        console.log(starTimer.value);
+        if (starTimer.value <= 0) {
+          isDisabledCode.value = false;
+          clearInterval(tiemr);
+        }
+      }, 1000);
+    };
+
+    onMounted(async () => {
+      //
+    });
+    return () => (
+      <div class={styles.updatePassword}>
+        <p class={styles.tips}>
+          检测到您尚未修改默认密码,为了您的账户安全,请重新设置登录密码
+        </p>
+        <NForm
+          labelAlign="right"
+          labelPlacement="left"
+          labelWidth={'auto'}
+          ref={formsRef}
+          model={forms}
+          requireMarkPlacement="left">
+          <NFormItem
+            path="currentClass"
+            label="手机号"
+            class={styles.phoneContainer}>
+            <p class={styles.phone}>{forms.mobile}</p>
+          </NFormItem>
+          <NFormItem
+            path="password"
+            label="新密码"
+            rule={[
+              {
+                validator: validatePass,
+                trigger: 'blur',
+                required: true
+              }
+            ]}>
+            <NInput
+              v-model:value={forms.password}
+              clearable
+              type="text"
+              showPasswordOn="click"
+              inputProps={{ autocomplete: 'off' }}
+              class={[password.passowrdStatus ? '' : styles['no-pwd']]}
+              placeholder={'请输入新密码'}>
+              {{
+                suffix: () => (
+                  <img
+                    src={password.passowrdStatus ? openEye : closeEye}
+                    class={styles.pwdIcon}
+                    alt=""
+                    onClick={() => {
+                      password.passowrdStatus = !password.passowrdStatus;
+                    }}
+                  />
+                )
+              }}
+            </NInput>
+          </NFormItem>
+          <NFormItem
+            path="rePassword"
+            label="再次输入"
+            rule={[
+              {
+                validator: validatePass2,
+                trigger: 'blur',
+                required: true
+              }
+            ]}>
+            <NInput
+              v-model:value={forms.rePassword}
+              clearable
+              type="text"
+              showPasswordOn="click"
+              inputProps={{ autocomplete: 'off' }}
+              class={[password.rePasswordStatus ? '' : styles['no-pwd']]}
+              placeholder={'再次输入新密码'}>
+              {{
+                suffix: () => (
+                  <img
+                    src={password.rePasswordStatus ? openEye : closeEye}
+                    class={styles.pwdIcon}
+                    alt=""
+                    onClick={() => {
+                      password.rePasswordStatus = !password.rePasswordStatus;
+                    }}
+                  />
+                )
+              }}
+            </NInput>
+          </NFormItem>
+          <NFormItem
+            path="code"
+            label="验证码"
+            rule={[
+              {
+                required: true,
+                message: '请输入验证码',
+                trigger: 'blur'
+              }
+            ]}>
+            <NInput
+              v-model:value={forms.code}
+              placeholder={'请输入验证码'}
+              clearable
+              class={styles.sendInput}
+              maxlength={6}>
+              {{
+                suffix: () => (
+                  <NButton
+                    class={styles.sendMsg}
+                    disabled={isDisabledCode.value}
+                    onClick={() => sendMessage()}>
+                    {isDisabledCode.value ? starTimer.value + 'S' : codeName}
+                  </NButton>
+                )
+              }}
+            </NInput>
+          </NFormItem>
+          <div class={styles.updateBtnGroup}>
+            <NButton
+              strong
+              type="primary"
+              round
+              onClick={() => gotoClassPage()}>
+              确认
+            </NButton>
+          </div>
+        </NForm>
+
+        <NModal maskClosable={modalClickMask} v-model:show={showSmsClass.value}>
+          <SendSms
+            phone={forms.mobile}
+            type="PASSWORD"
+            onClose={() => (showSmsClass.value = false)}
+            onSendCode={() => {
+              checkTimeOut();
+            }}
+          />
+        </NModal>
+      </div>
+    );
+  }
+});

+ 2 - 0
src/components/upload-file/index.tsx

@@ -15,6 +15,7 @@ import {
   onFileUpload,
   onOnlyFileUpload
 } from '/src/helpers/oss-file-upload';
+import { modalClickMask } from '/src/state';
 
 export default defineComponent({
   name: 'upload-file',
@@ -374,6 +375,7 @@ export default defineComponent({
         )}
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={visiable.value}
           preset="dialog"
           showIcon={false}

+ 41 - 38
src/state.ts

@@ -1,38 +1,41 @@
-import { reactive } from 'vue';
-type status = 'init' | 'login' | 'logout' | 'error';
-
-export const state = reactive({
-  user: {
-    status: 'init' as status,
-    data: {} as any
-  },
-  application: window.matchMedia('(display-mode: standalone)').matches, // 是否在应用里面
-  navBarHeight: 0, // 状态栏高度
-  ossUploadUrl: 'https://ks3-cn-beijing.ksyuncs.com/'
-});
-
-// 预览上传到oss的地址
-export const getOssUploadUrl = (bucket: string) => {
-  const tmpBucket = bucket || 'gym';
-  return `https://${tmpBucket}.ks3-cn-beijing.ksyuncs.com/`;
-};
-
-export const setLoginInit = () => {
-  state.user.status = 'init';
-  state.user.data = null;
-};
-
-export const setLogin = (data: any) => {
-  state.user.status = 'login';
-  state.user.data = data;
-};
-
-export const setLogout = () => {
-  state.user.status = 'logout';
-  state.user.data = null;
-};
-
-export const setLoginError = () => {
-  state.user.status = 'error';
-  state.user.data = null;
-};
+import { reactive } from 'vue';
+type status = 'init' | 'login' | 'logout' | 'error';
+
+// 是否允许点击空白位置关闭弹窗
+export const modalClickMask = false;
+
+export const state = reactive({
+  user: {
+    status: 'init' as status,
+    data: {} as any
+  },
+  application: window.matchMedia('(display-mode: standalone)').matches, // 是否在应用里面
+  navBarHeight: 0, // 状态栏高度
+  ossUploadUrl: 'https://ks3-cn-beijing.ksyuncs.com/'
+});
+
+// 预览上传到oss的地址
+export const getOssUploadUrl = (bucket: string) => {
+  const tmpBucket = bucket || 'gym';
+  return `https://${tmpBucket}.ks3-cn-beijing.ksyuncs.com/`;
+};
+
+export const setLoginInit = () => {
+  state.user.status = 'init';
+  state.user.data = null;
+};
+
+export const setLogin = (data: any) => {
+  state.user.status = 'login';
+  state.user.data = data;
+};
+
+export const setLogout = () => {
+  state.user.status = 'logout';
+  state.user.data = null;
+};
+
+export const setLoginError = () => {
+  state.user.status = 'error';
+  state.user.data = null;
+};

+ 135 - 133
src/views/attend-class/component/roll-call/pen.tsx

@@ -1,133 +1,135 @@
-import { defineComponent, toRefs, ref, PropType, reactive } from 'vue';
-import styles from './pen.module.less';
-import { ToolType } from '../../index';
-import { NButton, NModal, NSpace } from 'naive-ui';
-import { iframeDislableKeyboard } from '/src/utils';
-
-export default defineComponent({
-  name: 'pen-page',
-  props: {
-    show: {
-      type: Boolean,
-      default: false
-    },
-    type: {
-      type: String as PropType<ToolType>,
-      default: 'pen'
-    },
-    close: {
-      type: Function,
-      default: () => ({})
-    }
-  },
-  setup(props) {
-    const { show, type } = toRefs(props);
-    // const modelAttendStatus = ref(false);
-    const modal = reactive({
-      status: false,
-      title: type.value === 'pen' ? '退出批注' : '退出白板',
-      content:
-        type.value === 'pen' ? '确认是否退出批注?' : '确认是否退出白板?'
-    });
-    const firstRender = ref(true);
-    const origin = /(localhost|192)/.test(location.host)
-      ? // ? 'https://test.lexiaoya.cn/'
-        'http://localhost:5002/'
-      : location.origin;
-    let src = `${origin}/classroom-whiteboard?t=${+new Date()}`;
-
-    console.log(props.type, '121212');
-    if (props.type === 'call') {
-      src = `${origin}/roll-call/index.html?t=${+new Date()}`;
-    }
-    return () => (
-      <div
-        class={[
-          styles.pen,
-          type.value === 'whiteboard' ? styles.whiteboard : '',
-          firstRender.value ? styles.dely : '',
-          show.value ? styles.open : styles.hide
-        ]}>
-        <iframe
-          class={styles.iframe}
-          frameborder="0"
-          width="100vw"
-          height="100vh"
-          src={src}
-          onLoad={(val: any) => {
-            firstRender.value = false;
-            iframeDislableKeyboard(val.target);
-          }}></iframe>
-        <div class={styles.rightItem} onClick={() => (modal.status = true)}>
-          <svg
-            width="28px"
-            height="28px"
-            viewBox="0 0 34 34"
-            version="1.1"
-            xmlns="http://www.w3.org/2000/svg">
-            <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-              <g
-                transform="translate(-1842.000000, -1016.000000)"
-                stroke="#FFFFFF">
-                <g transform="translate(980.000000, 1006.000000)">
-                  <g transform="translate(862.000000, 10.000000)">
-                    <g transform="translate(4.000000, 5.000000)">
-                      <g
-                        transform="translate(8.058241, 5.009812)"
-                        fill="#FFFFFF"
-                        fill-rule="nonzero"
-                        stroke-width="0.3">
-                        <path d="M11.6084252,-0.15 C11.9880433,-0.15 12.3676614,-0.00518057514 12.6573002,0.284458275 L12.6573002,0.284458275 L18.3141545,5.94131252 C18.6037933,6.23095137 18.7486128,6.61056948 18.7486128,6.99018758 C18.7486128,7.36980569 18.6037933,7.74942379 18.3141545,8.03906264 L18.3141545,8.03906264 L12.6573002,13.6959169 C12.3676614,13.9855557 11.9880433,14.1303752 11.6084252,14.1303752 C11.2288071,14.1303752 10.849189,13.9855557 10.5595501,13.6959169 C10.2699113,13.406278 10.1250918,13.0266599 10.1250918,12.6470418 C10.1250918,12.2674237 10.2699113,11.8878056 10.5595368,11.5981801 L10.5595368,11.5981801 L13.6839174,8.47301484 L1.33333333,8.47352092 C0.923722144,8.47352092 0.552888811,8.30749318 0.284458275,8.03906264 C0.0160277386,7.77063211 -0.15,7.39979877 -0.15,6.99018758 C-0.15,6.61194961 -0.00841906355,6.26678575 0.224608408,6.00476938 C0.462154637,5.73767211 0.794779811,5.55707713 1.16932931,5.51583101 L1.16932931,5.51583101 L13.6841044,5.50627626 L10.5595501,2.38220839 C10.2699113,2.09256954 10.1250918,1.71295144 10.1250918,1.33333333 C10.1250918,0.953715229 10.2699113,0.574097124 10.5595501,0.284458275 C10.849189,-0.00518057514 11.2288071,-0.15 11.6084252,-0.15 Z"></path>
-                      </g>
-                      <path
-                        d="M15,24 L3,24 C1.34314575,24 -1.1293615e-15,22.6568542 0,21 L0,3 C-2.02906125e-16,1.34314575 1.34314575,3.04359188e-16 3,0 L15,0 L15,0"
-                        stroke-width="3.5"
-                        stroke-linecap="round"
-                        stroke-linejoin="round"></path>
-                    </g>
-                  </g>
-                </g>
-              </g>
-            </g>
-          </svg>
-        </div>
-
-        {/* 布置作业 */}
-        <NModal
-          transformOrigin="center"
-          v-model:show={modal.status}
-          preset="card"
-          // class={styles.attendClassModal}
-          title={modal.title}
-          class={['modalTitle', styles.removeVisiable]}>
-          <div class={styles.studentRemove}>
-            <p>{modal.content}</p>
-            {/* <div class={styles.modelAttendContent}>
-              {data.modalAttendMessage}
-            </div> */}
-            <NSpace class={styles.btnGroupModal} justify="center">
-              <NButton
-                type="default"
-                round
-                onClick={() => {
-                  modal.status = false;
-                }}>
-                取消
-              </NButton>
-              <NButton
-                type="primary"
-                round
-                onClick={() => {
-                  // data.modelTrainStatus = true;
-                  modal.status = false;
-                  props.close();
-                }}>
-                确认
-              </NButton>
-            </NSpace>
-          </div>
-        </NModal>
-      </div>
-    );
-  }
-});
+import { defineComponent, toRefs, ref, PropType, reactive } from 'vue';
+import styles from './pen.module.less';
+import { ToolType } from '../../index';
+import { NButton, NModal, NSpace } from 'naive-ui';
+import { iframeDislableKeyboard } from '/src/utils';
+import { modalClickMask } from '/src/state';
+
+export default defineComponent({
+  name: 'pen-page',
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    },
+    type: {
+      type: String as PropType<ToolType>,
+      default: 'pen'
+    },
+    close: {
+      type: Function,
+      default: () => ({})
+    }
+  },
+  setup(props) {
+    const { show, type } = toRefs(props);
+    // const modelAttendStatus = ref(false);
+    const modal = reactive({
+      status: false,
+      title: type.value === 'pen' ? '退出批注' : '退出白板',
+      content:
+        type.value === 'pen' ? '确认是否退出批注?' : '确认是否退出白板?'
+    });
+    const firstRender = ref(true);
+    const origin = /(localhost|192)/.test(location.host)
+      ? // ? 'https://test.lexiaoya.cn/'
+        'http://localhost:5002/'
+      : location.origin;
+    let src = `${origin}/classroom-whiteboard?t=${+new Date()}`;
+
+    console.log(props.type, '121212');
+    if (props.type === 'call') {
+      src = `${origin}/roll-call/index.html?t=${+new Date()}`;
+    }
+    return () => (
+      <div
+        class={[
+          styles.pen,
+          type.value === 'whiteboard' ? styles.whiteboard : '',
+          firstRender.value ? styles.dely : '',
+          show.value ? styles.open : styles.hide
+        ]}>
+        <iframe
+          class={styles.iframe}
+          frameborder="0"
+          width="100vw"
+          height="100vh"
+          src={src}
+          onLoad={(val: any) => {
+            firstRender.value = false;
+            iframeDislableKeyboard(val.target);
+          }}></iframe>
+        <div class={styles.rightItem} onClick={() => (modal.status = true)}>
+          <svg
+            width="28px"
+            height="28px"
+            viewBox="0 0 34 34"
+            version="1.1"
+            xmlns="http://www.w3.org/2000/svg">
+            <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+              <g
+                transform="translate(-1842.000000, -1016.000000)"
+                stroke="#FFFFFF">
+                <g transform="translate(980.000000, 1006.000000)">
+                  <g transform="translate(862.000000, 10.000000)">
+                    <g transform="translate(4.000000, 5.000000)">
+                      <g
+                        transform="translate(8.058241, 5.009812)"
+                        fill="#FFFFFF"
+                        fill-rule="nonzero"
+                        stroke-width="0.3">
+                        <path d="M11.6084252,-0.15 C11.9880433,-0.15 12.3676614,-0.00518057514 12.6573002,0.284458275 L12.6573002,0.284458275 L18.3141545,5.94131252 C18.6037933,6.23095137 18.7486128,6.61056948 18.7486128,6.99018758 C18.7486128,7.36980569 18.6037933,7.74942379 18.3141545,8.03906264 L18.3141545,8.03906264 L12.6573002,13.6959169 C12.3676614,13.9855557 11.9880433,14.1303752 11.6084252,14.1303752 C11.2288071,14.1303752 10.849189,13.9855557 10.5595501,13.6959169 C10.2699113,13.406278 10.1250918,13.0266599 10.1250918,12.6470418 C10.1250918,12.2674237 10.2699113,11.8878056 10.5595368,11.5981801 L10.5595368,11.5981801 L13.6839174,8.47301484 L1.33333333,8.47352092 C0.923722144,8.47352092 0.552888811,8.30749318 0.284458275,8.03906264 C0.0160277386,7.77063211 -0.15,7.39979877 -0.15,6.99018758 C-0.15,6.61194961 -0.00841906355,6.26678575 0.224608408,6.00476938 C0.462154637,5.73767211 0.794779811,5.55707713 1.16932931,5.51583101 L1.16932931,5.51583101 L13.6841044,5.50627626 L10.5595501,2.38220839 C10.2699113,2.09256954 10.1250918,1.71295144 10.1250918,1.33333333 C10.1250918,0.953715229 10.2699113,0.574097124 10.5595501,0.284458275 C10.849189,-0.00518057514 11.2288071,-0.15 11.6084252,-0.15 Z"></path>
+                      </g>
+                      <path
+                        d="M15,24 L3,24 C1.34314575,24 -1.1293615e-15,22.6568542 0,21 L0,3 C-2.02906125e-16,1.34314575 1.34314575,3.04359188e-16 3,0 L15,0 L15,0"
+                        stroke-width="3.5"
+                        stroke-linecap="round"
+                        stroke-linejoin="round"></path>
+                    </g>
+                  </g>
+                </g>
+              </g>
+            </g>
+          </svg>
+        </div>
+
+        {/* 布置作业 */}
+        <NModal
+          maskClosable={modalClickMask}
+          transformOrigin="center"
+          v-model:show={modal.status}
+          preset="card"
+          // class={styles.attendClassModal}
+          title={modal.title}
+          class={['modalTitle', styles.removeVisiable]}>
+          <div class={styles.studentRemove}>
+            <p>{modal.content}</p>
+            {/* <div class={styles.modelAttendContent}>
+              {data.modalAttendMessage}
+            </div> */}
+            <NSpace class={styles.btnGroupModal} justify="center">
+              <NButton
+                type="default"
+                round
+                onClick={() => {
+                  modal.status = false;
+                }}>
+                取消
+              </NButton>
+              <NButton
+                type="primary"
+                round
+                onClick={() => {
+                  // data.modelTrainStatus = true;
+                  modal.status = false;
+                  props.close();
+                }}>
+                确认
+              </NButton>
+            </NSpace>
+          </div>
+        </NModal>
+      </div>
+    );
+  }
+});

+ 2 - 0
src/views/attend-class/component/tools/pen.tsx

@@ -6,6 +6,7 @@ import { iframeDislableKeyboard } from '/src/utils';
 import useDrag from '@/hooks/useDrag';
 import Dragbom from '@/hooks/useDrag/dragbom';
 import { useUserStore } from '@/store/modules/users';
+import { modalClickMask } from '/src/state';
 
 export default defineComponent({
   name: 'pen-page',
@@ -133,6 +134,7 @@ export default defineComponent({
 
         {/* 布置作业 */}
         <NModal
+          maskClosable={modalClickMask}
           style={props.isDrag ? penBoxDragData.styleDrag.value : {}}
           z-index={3000}
           transformOrigin="center"

+ 11 - 1
src/views/attend-class/index.tsx

@@ -54,7 +54,7 @@ import TimerMeter from '/src/components/timerMeter';
 import Metronome from '/src/components/Metronome';
 import { iframeDislableKeyboard, px2vw } from '/src/utils';
 import PlaceholderTone from '/src/components/layout/modals/placeholderTone';
-import { state as globalState } from '/src/state';
+import { state as globalState, modalClickMask } from '/src/state';
 import Chapter from './model/chapter';
 import { useRouter } from 'vue-router';
 import { useUserStore } from '@/store/modules/users';
@@ -2025,6 +2025,7 @@ export default defineComponent({
           </NDrawerContent>
         </NDrawer> */}
         <NModal
+          maskClosable={modalClickMask}
           transformOrigin="center"
           v-model:show={popupData.open}
           preset="card"
@@ -2070,6 +2071,7 @@ export default defineComponent({
           </NDrawerContent>
         </NDrawer> */}
         <NModal
+          maskClosable={modalClickMask}
           transformOrigin="center"
           v-model:show={popupData.chapterOpen}
           preset="card"
@@ -2127,6 +2129,7 @@ export default defineComponent({
 
         {/* 选择课件 */}
         <NModal
+          maskClosable={modalClickMask}
           transformOrigin="center"
           v-model:show={data.selectClassStatus}
           preset="card"
@@ -2172,6 +2175,7 @@ export default defineComponent({
 
         {/* 布置作业 */}
         <NModal
+          maskClosable={modalClickMask}
           transformOrigin="center"
           v-model:show={data.modelAttendStatus}
           preset="card"
@@ -2228,6 +2232,7 @@ export default defineComponent({
           />
         </NModal> */}
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={data.modelTrainStatus}
           preset="card"
           class={[
@@ -2296,6 +2301,7 @@ export default defineComponent({
           dragClass={metronomeConBoxClass}
           dragStyle={metronomeConBoxDragData.styleDrag.value}></Metronome>
         <NModal
+          maskClosable={modalClickMask}
           transformOrigin="center"
           class={['background']}
           v-model:show={showModalTone.value}>
@@ -2307,6 +2313,7 @@ export default defineComponent({
           </div>
         </NModal>
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={showModalTime.value}
           class={timerMeterConBoxClass}
           style={timerMeterConDragData.styleDrag.value}>
@@ -2324,6 +2331,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={data.selectResourceStatus}
           class={['modalTitle', styles.selectMusicModal, selResourBoxClass]}
           style={selResourDragData.styleDrag.value}
@@ -2333,6 +2341,7 @@ export default defineComponent({
           <Dragbom></Dragbom>
         </NModal>
         <NModal
+          maskClosable={modalClickMask}
           transformOrigin="center"
           v-model:show={data.removeVisiable}
           preset="card"
@@ -2408,6 +2417,7 @@ export default defineComponent({
           </div>
         </NModal>
         <NModal
+          maskClosable={modalClickMask}
           style={nextEndBoxDragData.styleDrag.value}
           z-index={3000}
           transformOrigin="center"

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

@@ -1,317 +1,321 @@
-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';
-import TheEmpty from '/src/components/TheEmpty';
-
-export default defineComponent({
-  name: 'class-work',
-  props: {
-    /** 章节编号 */
-    detailId: {
-      type: String,
-      default: ''
-    },
-    /** 声部编号 */
-    subjectId: {
-      type: String,
-      default: ''
-    },
-    /** 班级编号 */
-    classGroupId: {
-      type: String,
-      default: ''
-    },
-    /** 上课编号 */
-    courseScheduleId: {
-      type: String,
-      default: ''
-    },
-    activeName: {
-      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,
-          coursewareKnowledgeDetailId: 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,
-                !forms.loadingStatus &&
-                  forms.tableList.length <= 0 &&
-                  styles.listSectionEmpty
-              ]}
-              style={{ minHeight: '55vh' }}>
-              <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>
-
-              {!forms.loadingStatus && forms.tableList.length <= 0 && (
-                <TheEmpty description="暂无作业" />
-              )}
-            </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}
-            courseScheduleId={props.courseScheduleId}
-            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:
-                    (props.activeName || 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>
-    );
-  }
-});
+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';
+import TheEmpty from '/src/components/TheEmpty';
+import { modalClickMask } from '/src/state';
+
+export default defineComponent({
+  name: 'class-work',
+  props: {
+    /** 章节编号 */
+    detailId: {
+      type: String,
+      default: ''
+    },
+    /** 声部编号 */
+    subjectId: {
+      type: String,
+      default: ''
+    },
+    /** 班级编号 */
+    classGroupId: {
+      type: String,
+      default: ''
+    },
+    /** 上课编号 */
+    courseScheduleId: {
+      type: String,
+      default: ''
+    },
+    activeName: {
+      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,
+          coursewareKnowledgeDetailId: 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,
+                !forms.loadingStatus &&
+                  forms.tableList.length <= 0 &&
+                  styles.listSectionEmpty
+              ]}
+              style={{ minHeight: '55vh' }}>
+              <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>
+
+              {!forms.loadingStatus && forms.tableList.length <= 0 && (
+                <TheEmpty description="暂无作业" />
+              )}
+            </div>
+          </NSpin>
+        </NScrollbar>
+
+        <NModal
+          maskClosable={modalClickMask}
+          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
+          maskClosable={modalClickMask}
+          v-model:show={forms.assignHomeworkStatus}
+          preset="card"
+          showIcon={false}
+          class={['modalTitle background', styles.assignHomework]}
+          title={'布置作业'}
+          blockScroll={false}>
+          <AssignHomework
+            classGroupId={props.classGroupId}
+            courseScheduleId={props.courseScheduleId}
+            item={forms.selectItem}
+            trainList={[]}
+            onClose={() => (forms.assignHomeworkStatus = false)}
+          />
+        </NModal>
+
+        <NModal
+          maskClosable={modalClickMask}
+          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:
+                    (props.activeName || 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>
+    );
+  }
+});

+ 328 - 325
src/views/attend-class/model/train-settings/index.tsx

@@ -1,325 +1,328 @@
-import { defineComponent, onMounted, reactive } from 'vue';
-import styles from './index.module.less';
-import {
-  NButton,
-  NDatePicker,
-  NModal,
-  NScrollbar,
-  NSpace,
-  NSpin,
-  useMessage
-} from 'naive-ui';
-import TrainType from '@/views/attend-class/model/train-type';
-import TrainUpdate from '../train-update';
-import SelectMusic from '@/views/prepare-lessons/model/select-music';
-import {
-  lessonPreTrainingPage,
-  lessonTrainingAdd
-} from '/src/views/prepare-lessons/api';
-import { evaluateDifficult } from '/src/utils/contants';
-import dayjs from 'dayjs';
-import TheEmpty from '/src/components/TheEmpty';
-import requestOrigin from 'umi-request';
-
-export default defineComponent({
-  name: 'train-settings',
-  props: {
-    /** 章节编号 */
-    detailId: {
-      type: String,
-      default: ''
-    },
-    /** 声部编号 */
-    subjectId: {
-      type: String,
-      default: ''
-    },
-    /** 班级编号 */
-    classGroupId: {
-      type: String,
-      default: ''
-    },
-    /** 上课编号 */
-    courseScheduleId: {
-      type: String,
-      default: ''
-    }
-  },
-  emits: ['close', 'confirm'],
-  setup(props, { emit }) {
-    const message = useMessage();
-    const trainForms = reactive({
-      type: 'add' as 'add' | 'update',
-      btnLoading: false,
-      loadingStatus: false,
-      editStatus: false,
-      editItem: {} as any,
-      selectMusicStatus: false,
-      trainList: [] as any,
-      currentTime: dayjs(dayjs().format('YYYY-MM-DD')).valueOf(),
-      expireDate: dayjs().add(7, 'day').format('YYYY-MM-DD') as any // 默认7天
-    });
-    const getList = async () => {
-      trainForms.loadingStatus = true;
-      try {
-        // 判断是否有选择对应的课件
-        const { data } = await lessonPreTrainingPage({
-          coursewareKnowledgeDetailId: props.detailId,
-          subjectId: props.subjectId,
-          page: 1,
-          rows: 99
-        });
-        const tempRows = data.rows || [];
-        const temp: any = [];
-
-        tempRows.forEach((row: any) => {
-          const tList = typeFormat(row.trainingType, row.trainingConfigJson);
-          temp.push({
-            typeList: tList || [],
-            ...row
-          });
-        });
-        trainForms.trainList = temp || [];
-      } catch {
-        //
-      }
-      trainForms.loadingStatus = false;
-    };
-
-    const typeFormat = (trainingType: string, configJson: any) => {
-      let tList: string[] = [];
-
-      if (trainingType === 'EVALUATION') {
-        tList = [
-          `${evaluateDifficult[configJson.evaluateDifficult]}`,
-          configJson.practiceChapterBegin || configJson.practiceChapterEnd
-            ? `${configJson.practiceChapterBegin}-${configJson.practiceChapterEnd}小节`
-            : '全部小节',
-          // `速度${configJson.evaluateSpeed}`,
-          `${configJson.trainingTimes}分合格`
-        ];
-      } else {
-        tList = [
-          `${configJson.practiceChapterBegin}-${configJson.practiceChapterEnd}小节`,
-          `速度${configJson.practiceSpeed}`,
-          `${configJson.trainingTimes}分钟`
-        ];
-      }
-      return tList;
-    };
-
-    const onAdd = async (item: any) => {
-      let xmlStatus = 'init';
-      // 第一个声部小节
-      let firstMeasures: any = null;
-      try {
-        // 获取文件
-        const res = await requestOrigin.get(item.xmlFileUrl, {
-          mode: 'cors'
-        });
-        const xmlParse = new DOMParser().parseFromString(res, 'text/xml');
-        const parts = xmlParse.getElementsByTagName('part');
-        firstMeasures = parts[0]?.getElementsByTagName('measure');
-        xmlStatus = 'success';
-      } catch (error) {
-        xmlStatus = 'error';
-      }
-
-      // 判断读取小节数
-      if (xmlStatus == 'success') {
-        item.practiceChapterMax = firstMeasures.length;
-      } else {
-        item.practiceChapterMax = 0;
-      }
-      item.coursewareKnowledgeDetailId = props.detailId;
-      item.subjectId = props.subjectId;
-
-      trainForms.editItem = item;
-      trainForms.editStatus = true;
-    };
-
-    const onSubmit = async () => {
-      // 训练内容不能为空
-      if (!trainForms.expireDate) {
-        message.error('请选择截止日期');
-        return;
-      }
-      if (trainForms.trainList.length <= 0) {
-        message.error('训练内容不能为空');
-        return;
-      }
-      trainForms.btnLoading = true;
-      try {
-        const trainList = trainForms.trainList || [];
-        const details: any[] = [];
-        trainList.forEach((item: any) => {
-          details.push({
-            trainingType: item.trainingType,
-            musicId: item.musicId,
-            trainingConfigJsonObject: item.trainingConfigJson
-          });
-        });
-        const params = {
-          lessonTrainingDetails: details,
-          expireDate: trainForms.expireDate + ' 23:59:59',
-          classGroupId: props.classGroupId,
-          courseScheduleId: props.courseScheduleId || null
-        };
-        await lessonTrainingAdd(params);
-        message.success('布置成功');
-
-        emit('close');
-        emit('confirm');
-      } catch {
-        //
-      }
-      trainForms.btnLoading = false;
-    };
-
-    onMounted(() => {
-      // 判断是否有数据
-      if (props.detailId && props.subjectId) {
-        getList();
-      }
-    });
-    return () => (
-      <div class={styles.trainSettings}>
-        <div class={styles.searchGroup}>
-          <NButton
-            onClick={() => {
-              trainForms.selectMusicStatus = true;
-              trainForms.type = 'add';
-            }}>
-            添加作业
-          </NButton>
-
-          <div class={styles.datetime}>
-            <label>截止时间:</label>
-            <NDatePicker
-              style={{ width: '200px' }}
-              placeholder="请选择截止日期"
-              v-model:formatted-value={trainForms.expireDate}
-              type="date"
-              valueFormat="yyyy-MM-dd"
-              isDateDisabled={(ts: number) => {
-                return ts < trainForms.currentTime;
-              }}
-            />
-          </div>
-        </div>
-        <NScrollbar class={styles.trainList}>
-          <NSpin show={trainForms.loadingStatus}>
-            <div
-              class={[
-                styles.listSection,
-                !trainForms.loadingStatus && trainForms.trainList.length <= 0
-                  ? styles.emptySection
-                  : ''
-              ]}>
-              {trainForms.trainList.length > 0 && (
-                <div class={styles.list}>
-                  {trainForms.trainList.map((item: any, index: number) => (
-                    <TrainType
-                      item={item}
-                      type="homework"
-                      onEdit={(child: any) => {
-                        const { trainingConfigJson, id, musicId, ...res } =
-                          child;
-                        trainForms.editItem = {
-                          ...res,
-                          id: musicId,
-                          trainId: id,
-                          ...trainingConfigJson
-                        };
-                        console.log(trainForms.editItem);
-                        trainForms.type = 'update';
-                        trainForms.editStatus = true;
-                      }}
-                      onDelete={() => {
-                        // 删除
-                        // const index = trainForms.trainList.findIndex(
-                        //   (c: any) => c.id === item.id
-                        // );
-                        trainForms.trainList.splice(index, 1);
-                      }}
-                    />
-                  ))}
-                </div>
-              )}
-
-              {!trainForms.loadingStatus &&
-                trainForms.trainList.length <= 0 && (
-                  <TheEmpty description="暂无作业" />
-                )}
-            </div>
-          </NSpin>
-        </NScrollbar>
-        <NSpace class={styles.trainBtnGroup}>
-          <NButton strong type="default" round onClick={() => emit('close')}>
-            取消布置
-          </NButton>
-          <NButton
-            strong
-            type="primary"
-            round
-            disabled={trainForms.trainList.length <= 0 ? true : false}
-            onClick={onSubmit}>
-            立即布置
-          </NButton>
-        </NSpace>
-
-        <NModal
-          v-model:show={trainForms.editStatus}
-          class={['modalTitle background', styles.trainEditModal]}
-          preset="card"
-          title="作业设置">
-          <TrainUpdate
-            item={trainForms.editItem}
-            type="homework"
-            onClose={() => (trainForms.editStatus = false)}
-            onConfirm={(item: any) => {
-              console.log(item, 'update', trainForms);
-              const tList = typeFormat(
-                item.trainingType,
-                item.trainingConfigJson
-              );
-              // 更新
-              if (trainForms.type === 'update') {
-                trainForms.trainList.forEach((train: any) => {
-                  if (train.id === item.id) {
-                    train.trainingType = item.trainingType;
-                    train.trainingConfigJson = item.trainingConfigJson;
-                    train.typeList = tList;
-                  }
-                });
-              } else {
-                //
-                trainForms.trainList.push({
-                  ...item,
-                  id: +new Date(),
-                  musicName: trainForms.editItem.title,
-                  typeList: tList
-                });
-              }
-              trainForms.editItem = {};
-            }}
-          />
-        </NModal>
-
-        <NModal
-          v-model:show={trainForms.selectMusicStatus}
-          class={['modalTitle', styles.selectMusicModal]}
-          preset="card"
-          title={'选择曲目'}>
-          <SelectMusic
-            type="homework"
-            onAdd={(item: any) => {
-              trainForms.selectMusicStatus = false;
-              onAdd(item);
-            }}
-          />
-        </NModal>
-      </div>
-    );
-  }
-});
+import { defineComponent, onMounted, reactive } from 'vue';
+import styles from './index.module.less';
+import {
+  NButton,
+  NDatePicker,
+  NModal,
+  NScrollbar,
+  NSpace,
+  NSpin,
+  useMessage
+} from 'naive-ui';
+import TrainType from '@/views/attend-class/model/train-type';
+import TrainUpdate from '../train-update';
+import SelectMusic from '@/views/prepare-lessons/model/select-music';
+import {
+  lessonPreTrainingPage,
+  lessonTrainingAdd
+} from '/src/views/prepare-lessons/api';
+import { evaluateDifficult } from '/src/utils/contants';
+import dayjs from 'dayjs';
+import TheEmpty from '/src/components/TheEmpty';
+import requestOrigin from 'umi-request';
+import { modalClickMask } from '/src/state';
+
+export default defineComponent({
+  name: 'train-settings',
+  props: {
+    /** 章节编号 */
+    detailId: {
+      type: String,
+      default: ''
+    },
+    /** 声部编号 */
+    subjectId: {
+      type: String,
+      default: ''
+    },
+    /** 班级编号 */
+    classGroupId: {
+      type: String,
+      default: ''
+    },
+    /** 上课编号 */
+    courseScheduleId: {
+      type: String,
+      default: ''
+    }
+  },
+  emits: ['close', 'confirm'],
+  setup(props, { emit }) {
+    const message = useMessage();
+    const trainForms = reactive({
+      type: 'add' as 'add' | 'update',
+      btnLoading: false,
+      loadingStatus: false,
+      editStatus: false,
+      editItem: {} as any,
+      selectMusicStatus: false,
+      trainList: [] as any,
+      currentTime: dayjs(dayjs().format('YYYY-MM-DD')).valueOf(),
+      expireDate: dayjs().add(7, 'day').format('YYYY-MM-DD') as any // 默认7天
+    });
+    const getList = async () => {
+      trainForms.loadingStatus = true;
+      try {
+        // 判断是否有选择对应的课件
+        const { data } = await lessonPreTrainingPage({
+          coursewareKnowledgeDetailId: props.detailId,
+          subjectId: props.subjectId,
+          page: 1,
+          rows: 99
+        });
+        const tempRows = data.rows || [];
+        const temp: any = [];
+
+        tempRows.forEach((row: any) => {
+          const tList = typeFormat(row.trainingType, row.trainingConfigJson);
+          temp.push({
+            typeList: tList || [],
+            ...row
+          });
+        });
+        trainForms.trainList = temp || [];
+      } catch {
+        //
+      }
+      trainForms.loadingStatus = false;
+    };
+
+    const typeFormat = (trainingType: string, configJson: any) => {
+      let tList: string[] = [];
+
+      if (trainingType === 'EVALUATION') {
+        tList = [
+          `${evaluateDifficult[configJson.evaluateDifficult]}`,
+          configJson.practiceChapterBegin || configJson.practiceChapterEnd
+            ? `${configJson.practiceChapterBegin}-${configJson.practiceChapterEnd}小节`
+            : '全部小节',
+          // `速度${configJson.evaluateSpeed}`,
+          `${configJson.trainingTimes}分合格`
+        ];
+      } else {
+        tList = [
+          `${configJson.practiceChapterBegin}-${configJson.practiceChapterEnd}小节`,
+          `速度${configJson.practiceSpeed}`,
+          `${configJson.trainingTimes}分钟`
+        ];
+      }
+      return tList;
+    };
+
+    const onAdd = async (item: any) => {
+      let xmlStatus = 'init';
+      // 第一个声部小节
+      let firstMeasures: any = null;
+      try {
+        // 获取文件
+        const res = await requestOrigin.get(item.xmlFileUrl, {
+          mode: 'cors'
+        });
+        const xmlParse = new DOMParser().parseFromString(res, 'text/xml');
+        const parts = xmlParse.getElementsByTagName('part');
+        firstMeasures = parts[0]?.getElementsByTagName('measure');
+        xmlStatus = 'success';
+      } catch (error) {
+        xmlStatus = 'error';
+      }
+
+      // 判断读取小节数
+      if (xmlStatus == 'success') {
+        item.practiceChapterMax = firstMeasures.length;
+      } else {
+        item.practiceChapterMax = 0;
+      }
+      item.coursewareKnowledgeDetailId = props.detailId;
+      item.subjectId = props.subjectId;
+
+      trainForms.editItem = item;
+      trainForms.editStatus = true;
+    };
+
+    const onSubmit = async () => {
+      // 训练内容不能为空
+      if (!trainForms.expireDate) {
+        message.error('请选择截止日期');
+        return;
+      }
+      if (trainForms.trainList.length <= 0) {
+        message.error('训练内容不能为空');
+        return;
+      }
+      trainForms.btnLoading = true;
+      try {
+        const trainList = trainForms.trainList || [];
+        const details: any[] = [];
+        trainList.forEach((item: any) => {
+          details.push({
+            trainingType: item.trainingType,
+            musicId: item.musicId,
+            trainingConfigJsonObject: item.trainingConfigJson
+          });
+        });
+        const params = {
+          lessonTrainingDetails: details,
+          expireDate: trainForms.expireDate + ' 23:59:59',
+          classGroupId: props.classGroupId,
+          courseScheduleId: props.courseScheduleId || null
+        };
+        await lessonTrainingAdd(params);
+        message.success('布置成功');
+
+        emit('close');
+        emit('confirm');
+      } catch {
+        //
+      }
+      trainForms.btnLoading = false;
+    };
+
+    onMounted(() => {
+      // 判断是否有数据
+      if (props.detailId && props.subjectId) {
+        getList();
+      }
+    });
+    return () => (
+      <div class={styles.trainSettings}>
+        <div class={styles.searchGroup}>
+          <NButton
+            onClick={() => {
+              trainForms.selectMusicStatus = true;
+              trainForms.type = 'add';
+            }}>
+            添加作业
+          </NButton>
+
+          <div class={styles.datetime}>
+            <label>截止时间:</label>
+            <NDatePicker
+              style={{ width: '200px' }}
+              placeholder="请选择截止日期"
+              v-model:formatted-value={trainForms.expireDate}
+              type="date"
+              valueFormat="yyyy-MM-dd"
+              isDateDisabled={(ts: number) => {
+                return ts < trainForms.currentTime;
+              }}
+            />
+          </div>
+        </div>
+        <NScrollbar class={styles.trainList}>
+          <NSpin show={trainForms.loadingStatus}>
+            <div
+              class={[
+                styles.listSection,
+                !trainForms.loadingStatus && trainForms.trainList.length <= 0
+                  ? styles.emptySection
+                  : ''
+              ]}>
+              {trainForms.trainList.length > 0 && (
+                <div class={styles.list}>
+                  {trainForms.trainList.map((item: any, index: number) => (
+                    <TrainType
+                      item={item}
+                      type="homework"
+                      onEdit={(child: any) => {
+                        const { trainingConfigJson, id, musicId, ...res } =
+                          child;
+                        trainForms.editItem = {
+                          ...res,
+                          id: musicId,
+                          trainId: id,
+                          ...trainingConfigJson
+                        };
+                        console.log(trainForms.editItem);
+                        trainForms.type = 'update';
+                        trainForms.editStatus = true;
+                      }}
+                      onDelete={() => {
+                        // 删除
+                        // const index = trainForms.trainList.findIndex(
+                        //   (c: any) => c.id === item.id
+                        // );
+                        trainForms.trainList.splice(index, 1);
+                      }}
+                    />
+                  ))}
+                </div>
+              )}
+
+              {!trainForms.loadingStatus &&
+                trainForms.trainList.length <= 0 && (
+                  <TheEmpty description="暂无作业" />
+                )}
+            </div>
+          </NSpin>
+        </NScrollbar>
+        <NSpace class={styles.trainBtnGroup}>
+          <NButton strong type="default" round onClick={() => emit('close')}>
+            取消布置
+          </NButton>
+          <NButton
+            strong
+            type="primary"
+            round
+            disabled={trainForms.trainList.length <= 0 ? true : false}
+            onClick={onSubmit}>
+            立即布置
+          </NButton>
+        </NSpace>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={trainForms.editStatus}
+          class={['modalTitle background', styles.trainEditModal]}
+          preset="card"
+          title="作业设置">
+          <TrainUpdate
+            item={trainForms.editItem}
+            type="homework"
+            onClose={() => (trainForms.editStatus = false)}
+            onConfirm={(item: any) => {
+              console.log(item, 'update', trainForms);
+              const tList = typeFormat(
+                item.trainingType,
+                item.trainingConfigJson
+              );
+              // 更新
+              if (trainForms.type === 'update') {
+                trainForms.trainList.forEach((train: any) => {
+                  if (train.id === item.id) {
+                    train.trainingType = item.trainingType;
+                    train.trainingConfigJson = item.trainingConfigJson;
+                    train.typeList = tList;
+                  }
+                });
+              } else {
+                //
+                trainForms.trainList.push({
+                  ...item,
+                  id: +new Date(),
+                  musicName: trainForms.editItem.title,
+                  typeList: tList
+                });
+              }
+              trainForms.editItem = {};
+            }}
+          />
+        </NModal>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={trainForms.selectMusicStatus}
+          class={['modalTitle', styles.selectMusicModal]}
+          preset="card"
+          title={'选择曲目'}>
+          <SelectMusic
+            type="homework"
+            onAdd={(item: any) => {
+              trainForms.selectMusicStatus = false;
+              onAdd(item);
+            }}
+          />
+        </NModal>
+      </div>
+    );
+  }
+});

+ 3 - 0
src/views/attend-class/model/train-type/index.tsx

@@ -23,6 +23,7 @@ import { vaildMusicScoreUrl, vaildUrl } from '/src/utils/urlUtils';
 import { musicPracticeRecordDetail } from '/src/views/prepare-lessons/api';
 import { checkUrlType, iframeDislableKeyboard } from '/src/utils';
 import TheNoticeBar from '/src/components/TheNoticeBar';
+import { modalClickMask } from '/src/state';
 type ItemType = {
   id: string | number;
   trainingType: 'PRACTICE' | 'EVALUATION';
@@ -389,6 +390,7 @@ export default defineComponent({
         )}
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={removeVisiable.value}
           preset="card"
           class={['modalTitle', styles.removeVisiable]}
@@ -421,6 +423,7 @@ export default defineComponent({
         />
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={detailVisiable.value}
           preset="card"
           class={['modalTitle background', styles.reportModel]}

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

@@ -26,6 +26,7 @@ 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';
+import { modalClickMask } from '/src/state';
 export default defineComponent({
   name: 'afterWork',
   props: {
@@ -301,6 +302,7 @@ export default defineComponent({
           />
         </div>
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.addWorkVisible}
           preset="card"
           class={[styles.attendClassModal, styles.trainClassModal]}
@@ -314,6 +316,7 @@ export default defineComponent({
           />
         </NModal>
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.detailVisiable}
           preset="card"
           class={['modalTitle background', styles.wordDetailModel]}

+ 2 - 0
src/views/classList/components/afterWorkDetail.tsx

@@ -30,6 +30,7 @@ import TrainingDetails from '../modals/TrainingDetails';
 import dayjs from 'dayjs';
 import TheEmpty from '/src/components/TheEmpty';
 import { evaluateDifficult } from '/src/utils/contants';
+import { modalClickMask } from '/src/state';
 export default defineComponent({
   name: 'student-studentList',
   setup() {
@@ -492,6 +493,7 @@ export default defineComponent({
           </div>
         </div>
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.detailVisiable}
           preset="card"
           class={['modalTitle background', styles.wordDetailModel]}

+ 333 - 331
src/views/classList/components/classStudent.tsx

@@ -1,331 +1,333 @@
-import { defineComponent, onMounted, reactive, watch } from 'vue';
-import styles from '../index.module.less';
-import {
-  NButton,
-  NDataTable,
-  NForm,
-  NFormItem,
-  NModal,
-  NSpace,
-  NTooltip,
-  useMessage
-} from 'naive-ui';
-import SearchInput from '@/components/searchInput';
-import CSelect from '@/components/CSelect';
-import Pagination from '@/components/pagination';
-import { getStudentList } from '../api';
-import { useRoute, useRouter } from 'vue-router';
-import TheEmpty from '/src/components/TheEmpty';
-import UpdateStudent from '../../studentList/modals/update-student';
-import { initCache, setCache } from '/src/hooks/use-async';
-export default defineComponent({
-  name: 'student-studentList',
-  props: {
-    upgradeFlag: {
-      type: Number
-    }
-  },
-  setup(props) {
-    const message = useMessage();
-    const route = useRoute();
-    const router = useRouter();
-
-    const state = reactive({
-      upgradeFlag: props.upgradeFlag == 0 ? true : false, // 是否为历史班
-      searchForm: { keyword: '', gender: '' as any, membership: '' as any },
-      loading: false,
-      pagination: {
-        page: 1,
-        rows: 10,
-        pageTotal: 4
-      },
-      tableList: [] as any,
-      editStatus: false,
-      activeRow: {} as any
-    });
-
-    watch(
-      () => props.upgradeFlag,
-      () => {
-        state.upgradeFlag = props.upgradeFlag == 0 ? true : false;
-      }
-    );
-    const search = () => {
-      state.pagination.page = 1;
-      getList();
-      setCache({ current: state.searchForm, saveKey: 'classDetailStudent' });
-    };
-
-    const onReset = () => {
-      state.searchForm = {
-        keyword: '',
-        gender: '' as any,
-        membership: '' as any
-      };
-      search();
-      setCache({ current: state.searchForm, saveKey: 'classDetailStudent' });
-    };
-    const getList = async () => {
-      state.loading = true;
-      try {
-        const res = await getStudentList({
-          classGroupId: 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);
-      }
-    };
-
-    initCache({
-      current: state.searchForm,
-      saveKey: 'classDetailStudent',
-      callBack: (active: any) => {
-        state.searchForm = active;
-      }
-    });
-    onMounted(() => {
-      getList();
-    });
-
-    const copyTo = (text: string) => {
-      const input = document.createElement('input');
-      input.value = text;
-      document.body.appendChild(input);
-      input.select();
-      input.setSelectionRange(0, input.value.length);
-      document.execCommand('Copy');
-      document.body.removeChild(input);
-      message.success('复制成功');
-    };
-    const gotoDetail = (row: any) => {
-      router.push({
-        path: '/classStudentDetail',
-        query: {
-          ...route.query,
-          studentId: row.id,
-          studentName: row.nickname,
-          upgradeFlag: state.upgradeFlag ? 0 : 1
-        }
-      });
-    };
-    const columns = () => {
-      return [
-        {
-          title: '学生姓名',
-          key: 'nickname',
-          render: (row: any) => {
-            return (
-              <NTooltip showArrow={false} placement="top-start">
-                {{
-                  trigger: () => (
-                    <div
-                      style={{ userSelect: 'all', cursor: 'pointer' }}
-                      onClick={() => copyTo(row.nickname)}>
-                      {row.nickname}
-                    </div>
-                  ),
-                  default: '点击复制'
-                }}
-              </NTooltip>
-            );
-          }
-        },
-        {
-          title: '手机号',
-          key: 'phone',
-          render: (row: any) => {
-            return (
-              <NTooltip showArrow={false} placement="top-start">
-                {{
-                  trigger: () => (
-                    <div
-                      style={{ userSelect: 'all', cursor: 'pointer' }}
-                      onClick={() => copyTo(row.phone)}>
-                      {row.phone}
-                    </div>
-                  ),
-                  default: '点击复制'
-                }}
-              </NTooltip>
-            );
-          }
-        },
-        {
-          title: '性别',
-          key: 'gender',
-          render(row: any) {
-            return (
-              <>
-                {row.gender + '' != 'null'
-                  ? row.gender == '0'
-                    ? '女'
-                    : '男'
-                  : '--'}
-              </>
-            );
-          }
-        },
-        {
-          title: '学生类型',
-          key: 'vipMember',
-          render(row: any) {
-            return <>{row.vipMember ? '会员' : '普通'}</>;
-          }
-        },
-        {
-          title: '操作',
-          key: 'id',
-          render(row: any) {
-            return (
-              <NSpace>
-                <NButton text type="primary" onClick={() => gotoDetail(row)}>
-                  详情
-                </NButton>
-                <NButton
-                  text
-                  type="primary"
-                  onClick={() => onUpdate(row)}
-                  disabled={row.historyClassStudent}>
-                  修改
-                </NButton>
-              </NSpace>
-            );
-          }
-        }
-      ];
-    };
-    // 修改
-    const onUpdate = (row: any) => {
-      state.editStatus = true;
-      state.activeRow = row;
-    };
-    return () => (
-      <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: ''
-                    },
-                    {
-                      label: '男',
-                      value: '1'
-                    },
-                    {
-                      label: '女',
-                      value: '0'
-                    }
-                  ],
-                  placeholder: '性别',
-                  clearable: true,
-                  inline: true
-                } as any)}
-                v-model:value={state.searchForm.gender}></CSelect>
-            </NFormItem>
-            <NFormItem>
-              <CSelect
-                {...({
-                  options: [
-                    {
-                      label: '全部类型',
-                      value: ''
-                    },
-                    {
-                      label: '会员',
-                      value: true
-                    },
-                    {
-                      label: '普通',
-                      value: false
-                    }
-                  ],
-                  placeholder: '学生类型',
-                  clearable: true,
-                  inline: true
-                } as any)}
-                v-model:value={state.searchForm.membership}></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>
-        {/* <NButton
-          class={styles.addBtn}
-          type="primary"
-          v-slots={{
-            icon: () => (
-              <>
-                <NImage class={styles.addBtnIcon} src={add}></NImage>
-              </>
-            )
-          }}>
-          新增学生
-        </NButton> */}
-        <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>
-
-        <NModal
-          v-model:show={state.editStatus}
-          class={['modalTitle background', styles.updateStudent]}
-          preset="card"
-          title="修改信息">
-          <UpdateStudent
-            onClose={() => (state.editStatus = false)}
-            onConfirm={() => getList()}
-            row={state.activeRow}
-          />
-        </NModal>
-      </div>
-    );
-  }
-});
+import { defineComponent, onMounted, reactive, watch } from 'vue';
+import styles from '../index.module.less';
+import {
+  NButton,
+  NDataTable,
+  NForm,
+  NFormItem,
+  NModal,
+  NSpace,
+  NTooltip,
+  useMessage
+} from 'naive-ui';
+import SearchInput from '@/components/searchInput';
+import CSelect from '@/components/CSelect';
+import Pagination from '@/components/pagination';
+import { getStudentList } from '../api';
+import { useRoute, useRouter } from 'vue-router';
+import TheEmpty from '/src/components/TheEmpty';
+import UpdateStudent from '../../studentList/modals/update-student';
+import { initCache, setCache } from '/src/hooks/use-async';
+import { modalClickMask } from '/src/state';
+export default defineComponent({
+  name: 'student-studentList',
+  props: {
+    upgradeFlag: {
+      type: Number
+    }
+  },
+  setup(props) {
+    const message = useMessage();
+    const route = useRoute();
+    const router = useRouter();
+
+    const state = reactive({
+      upgradeFlag: props.upgradeFlag == 0 ? true : false, // 是否为历史班
+      searchForm: { keyword: '', gender: '' as any, membership: '' as any },
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 4
+      },
+      tableList: [] as any,
+      editStatus: false,
+      activeRow: {} as any
+    });
+
+    watch(
+      () => props.upgradeFlag,
+      () => {
+        state.upgradeFlag = props.upgradeFlag == 0 ? true : false;
+      }
+    );
+    const search = () => {
+      state.pagination.page = 1;
+      getList();
+      setCache({ current: state.searchForm, saveKey: 'classDetailStudent' });
+    };
+
+    const onReset = () => {
+      state.searchForm = {
+        keyword: '',
+        gender: '' as any,
+        membership: '' as any
+      };
+      search();
+      setCache({ current: state.searchForm, saveKey: 'classDetailStudent' });
+    };
+    const getList = async () => {
+      state.loading = true;
+      try {
+        const res = await getStudentList({
+          classGroupId: 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);
+      }
+    };
+
+    initCache({
+      current: state.searchForm,
+      saveKey: 'classDetailStudent',
+      callBack: (active: any) => {
+        state.searchForm = active;
+      }
+    });
+    onMounted(() => {
+      getList();
+    });
+
+    const copyTo = (text: string) => {
+      const input = document.createElement('input');
+      input.value = text;
+      document.body.appendChild(input);
+      input.select();
+      input.setSelectionRange(0, input.value.length);
+      document.execCommand('Copy');
+      document.body.removeChild(input);
+      message.success('复制成功');
+    };
+    const gotoDetail = (row: any) => {
+      router.push({
+        path: '/classStudentDetail',
+        query: {
+          ...route.query,
+          studentId: row.id,
+          studentName: row.nickname,
+          upgradeFlag: state.upgradeFlag ? 0 : 1
+        }
+      });
+    };
+    const columns = () => {
+      return [
+        {
+          title: '学生姓名',
+          key: 'nickname',
+          render: (row: any) => {
+            return (
+              <NTooltip showArrow={false} placement="top-start">
+                {{
+                  trigger: () => (
+                    <div
+                      style={{ userSelect: 'all', cursor: 'pointer' }}
+                      onClick={() => copyTo(row.nickname)}>
+                      {row.nickname}
+                    </div>
+                  ),
+                  default: '点击复制'
+                }}
+              </NTooltip>
+            );
+          }
+        },
+        {
+          title: '手机号',
+          key: 'phone',
+          render: (row: any) => {
+            return (
+              <NTooltip showArrow={false} placement="top-start">
+                {{
+                  trigger: () => (
+                    <div
+                      style={{ userSelect: 'all', cursor: 'pointer' }}
+                      onClick={() => copyTo(row.phone)}>
+                      {row.phone}
+                    </div>
+                  ),
+                  default: '点击复制'
+                }}
+              </NTooltip>
+            );
+          }
+        },
+        {
+          title: '性别',
+          key: 'gender',
+          render(row: any) {
+            return (
+              <>
+                {row.gender + '' != 'null'
+                  ? row.gender == '0'
+                    ? '女'
+                    : '男'
+                  : '--'}
+              </>
+            );
+          }
+        },
+        {
+          title: '学生类型',
+          key: 'vipMember',
+          render(row: any) {
+            return <>{row.vipMember ? '会员' : '普通'}</>;
+          }
+        },
+        {
+          title: '操作',
+          key: 'id',
+          render(row: any) {
+            return (
+              <NSpace>
+                <NButton text type="primary" onClick={() => gotoDetail(row)}>
+                  详情
+                </NButton>
+                <NButton
+                  text
+                  type="primary"
+                  onClick={() => onUpdate(row)}
+                  disabled={row.historyClassStudent}>
+                  修改
+                </NButton>
+              </NSpace>
+            );
+          }
+        }
+      ];
+    };
+    // 修改
+    const onUpdate = (row: any) => {
+      state.editStatus = true;
+      state.activeRow = row;
+    };
+    return () => (
+      <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: ''
+                    },
+                    {
+                      label: '男',
+                      value: '1'
+                    },
+                    {
+                      label: '女',
+                      value: '0'
+                    }
+                  ],
+                  placeholder: '性别',
+                  clearable: true,
+                  inline: true
+                } as any)}
+                v-model:value={state.searchForm.gender}></CSelect>
+            </NFormItem>
+            <NFormItem>
+              <CSelect
+                {...({
+                  options: [
+                    {
+                      label: '全部类型',
+                      value: ''
+                    },
+                    {
+                      label: '会员',
+                      value: true
+                    },
+                    {
+                      label: '普通',
+                      value: false
+                    }
+                  ],
+                  placeholder: '学生类型',
+                  clearable: true,
+                  inline: true
+                } as any)}
+                v-model:value={state.searchForm.membership}></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>
+        {/* <NButton
+          class={styles.addBtn}
+          type="primary"
+          v-slots={{
+            icon: () => (
+              <>
+                <NImage class={styles.addBtnIcon} src={add}></NImage>
+              </>
+            )
+          }}>
+          新增学生
+        </NButton> */}
+        <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>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={state.editStatus}
+          class={['modalTitle background', styles.updateStudent]}
+          preset="card"
+          title="修改信息">
+          <UpdateStudent
+            onClose={() => (state.editStatus = false)}
+            onConfirm={() => getList()}
+            row={state.activeRow}
+          />
+        </NModal>
+      </div>
+    );
+  }
+});

+ 7 - 0
src/views/classList/index.tsx

@@ -31,6 +31,7 @@ import { initCache, setCache } from '/src/hooks/use-async';
 import AddStudentModel from '../studentList/modals/addStudentModel';
 import { useUserStore } from '/src/store/modules/users';
 import { useCatchStore } from '/src/store/modules/catchData';
+import { modalClickMask } from '/src/state';
 export default defineComponent({
   name: 'class-classList',
   setup() {
@@ -589,6 +590,7 @@ export default defineComponent({
           />
         </div>
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.studentVisible}
           preset="card"
           class={['modalTitle background', styles.studentVisible]}
@@ -599,6 +601,7 @@ export default defineComponent({
             onGetList={() => getList()}></RestStudentBox>
         </NModal>
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.showaddClass}
           style={{ width: '500px' }}
           display-directive="if"
@@ -614,6 +617,7 @@ export default defineComponent({
           />
         </NModal>
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.showResetClass}
           style={{ width: '500px' }}
           display-directive="if"
@@ -628,6 +632,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.showSubjectClass}
           style={{ width: '500px' }}
           preset="card"
@@ -659,6 +664,7 @@ export default defineComponent({
         />
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.removeVisiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable]}
@@ -681,6 +687,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.groupVisiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable]}

+ 4 - 1
src/views/classList/modals/TrainingDetails.tsx

@@ -20,6 +20,7 @@ import { evaluateDifficult } from '/src/utils/contants';
 import dayjs from 'dayjs';
 import CommentWork from '../../studentList/modals/comment-work';
 import WorkItem from '../work-item';
+import { modalClickMask } from '/src/state';
 export default defineComponent({
   props: {
     activeRow: {
@@ -259,7 +260,9 @@ export default defineComponent({
           </NSpace>
         </NSpin>
 
-        <NModal v-model:show={showModalMask.value}>
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={showModalMask.value}>
           <CommentWork
             comment={studnetInfo.value.comment}
             workInfo={{

+ 2 - 0
src/views/classList/work-item/index.tsx

@@ -16,6 +16,7 @@ import { checkUrlType, iframeDislableKeyboard } from '/src/utils';
 import { useUserStore } from '/src/store/modules/users';
 import { vaildMusicScoreUrl } from '/src/utils/urlUtils';
 import { saveAs } from 'file-saver';
+import { modalClickMask } from '/src/state';
 
 export default defineComponent({
   name: 'work-item',
@@ -265,6 +266,7 @@ export default defineComponent({
         />
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={detailVisiable.value}
           preset="card"
           class={['modalTitle background', styles.reportModel]}

+ 628 - 620
src/views/home/index copy.tsx

@@ -1,620 +1,628 @@
-import { defineComponent, onMounted, reactive, ref } from 'vue';
-import styles from './index.module.less';
-import {
-  NImage,
-  NButton,
-  NPopselect,
-  NModal,
-  useMessage,
-  NSpin,
-  NSelect
-} from 'naive-ui';
-import headerD from './images/headerD.png';
-import defultHeade from '@/components/layout/images/teacherIcon.png';
-import blackBoardBg from './images/blackboard_bg.png';
-import teacherMan from './images/teacher_man.png';
-import teacherWoman from './images/teacher_woman.png';
-
-import homeText1 from './images/home/home-text-1.png';
-import homeText2 from './images/home/home-text-2.png';
-import iconTo from './images/icon-to.png';
-import t1 from './images/t1.png';
-import t2 from './images/t2.png';
-import t3 from './images/t3.png';
-import { useRouter } from 'vue-router';
-import { useUserStore } from '/src/store/modules/users';
-// import SelectClass from './modals/selectClass';
-import dayjs from 'dayjs';
-import { gradeToCN, weekToCN } from '/src/utils/contants';
-import { useCatchStore } from '/src/store/modules/catchData';
-import TeachGroup from './modals/teachGroup';
-import {
-  classGroupList,
-  courseSchedulePage,
-  getGradeLevelList,
-  getGradeYearList
-} from './api';
-import TheEmpty from '/src/components/TheEmpty';
-import HomeGuide from '/src/custom-plugins/guide-page/home-guide';
-import TimerMeter from '/src/components/timerMeter';
-import { vaildUrl } from '/src/utils/urlUtils';
-import { iframeDislableKeyboard, px2vw } from '/src/utils';
-import PlaceholderTone from '@/components/layout/modals/placeholderTone';
-import PreviewWindow from '../preview-window';
-import UpdatePassword from '/src/components/layout/modals/update-password';
-import AttendClass from '../prepare-lessons/model/attend-class';
-import { useResizeObserver } from '@vueuse/core';
-// import { state } from '/src/state';
-export const formatDateToDay = () => {
-  const hours = dayjs().hour();
-  if (hours < 12) {
-    return '早上好'; //如果小时数小于12则输出“早上好!”
-  } else if (hours > 12 && hours < 18) {
-    return '下午好'; //如果小时数大于12并且小于18,输入“下午好!”
-  } else {
-    return '晚上好'; //如果上面两个条件都不符合,则输出“晚上好!”
-  }
-};
-
-export default defineComponent({
-  name: 'home-page',
-  setup() {
-    const catchStore = useCatchStore();
-    const router = useRouter();
-    const userStore = useUserStore();
-    const showUpdatePassword = ref(false);
-    const showModalBeat = ref(false);
-    const showModalTone = ref(false);
-    const showModalTime = ref(false);
-    const forms = reactive({
-      showAttendClass: false,
-      // useStatus: false,
-      studentList: [] as any,
-      bookVersionId: null,
-      classGroupId: null,
-      category: null,
-      subjectId: null,
-      musicTagList: [] as any,
-      loading: false,
-      list: [] as any,
-      unit: null,
-      unitList: [],
-      subjectList: [] as any,
-      gradeList: [] as any,
-      classLoading: false,
-      total: 0, // 上课数量
-      // 上次上课的数据
-      lastClassSelect: {
-        currentClass: null,
-        name: '',
-        upgradeFlag: false, // 是否为历史班
-        gradeYear: null as any,
-        gradeLevel: null as any
-      },
-      classSelect: {
-        currentGradeNum: null,
-        currentClass: null,
-        name: '',
-        upgradeFlag: false, // 是否为历史班
-        gradeLevel: null as any,
-        gradeYear: null
-      } as any,
-      popSelectOptions: [] as any,
-      popSelectYearList: [] as any,
-      popSelectLevelList: [] as any,
-      showGuide: false,
-      showPreview: false,
-      itemPreview: {} as any,
-      homeLeftHeight: 'auto'
-    });
-    const teachList = ref({} as any);
-
-    // 获取年级班级
-    const getClassList = async () => {
-      try {
-        const { data } = await classGroupList({
-          gradeLevel: forms.classSelect.gradeLevel,
-          gradeYear: forms.classSelect.gradeYear
-        });
-        const cList = data || [];
-        const gradeList: any = [];
-        const popSelectOptions: any = [];
-        // getLastClassRecode()
-        cList.forEach((item: any, index: number) => {
-          // 判断是否已经有班级了
-          if (index === 0) {
-            if (forms.lastClassSelect.currentClass) {
-              forms.classSelect.currentClass =
-                forms.lastClassSelect.currentClass;
-              forms.classSelect.name = forms.lastClassSelect.name;
-              forms.classSelect.upgradeFlag = forms.lastClassSelect.upgradeFlag;
-            } else {
-              const temp = item.classGroupList[0];
-              forms.classSelect.currentGradeNum = item.currentGradeNum;
-              forms.classSelect.currentClass = temp.id;
-              forms.classSelect.name = temp.name;
-            }
-          }
-
-          const classList: any = [];
-          item.classGroupList.forEach((i: any) => {
-            classList.push({
-              label: i.currentClass + '班',
-              value: i.id,
-              lastStudy: i.lastStudy
-            });
-
-            popSelectOptions.push({
-              label: i.name,
-              value: i.id,
-              currentGradeNum: item.currentGradeNum,
-              lastStudy: i.lastStudy
-            });
-          });
-
-          gradeList.push({
-            label: gradeToCN[item.currentGradeNum],
-            value: item.currentGradeNum,
-            childrens: classList
-          });
-        });
-        forms.popSelectOptions = popSelectOptions;
-        forms.gradeList = gradeList;
-      } catch {
-        //
-      }
-    };
-    const getLastClassRecode = async () => {
-      const { data } = await courseSchedulePage({
-        page: 1,
-        rows: 1,
-        teacherId: userStore.getUserInfo.id
-      });
-      if (data.rows.length > 0 && data.rows[0]) {
-        const tempRow = data.rows[0];
-        // forms.lastClassSelect.currentClass = tempRow.classGroupId;
-        // forms.lastClassSelect.name = tempRow.classGroupName;
-        forms.lastClassSelect = {
-          currentClass: tempRow.classGroupId,
-          name: tempRow.classGroupName,
-          upgradeFlag: tempRow.upgradeFlag,
-          gradeYear: tempRow.gradeYear + '',
-          gradeLevel: tempRow.gradeLevel + ''
-        };
-      }
-    };
-
-    // 获取学年
-    const getYearList = async () => {
-      try {
-        const { data } = await getGradeYearList();
-        const temp = data || [];
-        temp.forEach((i: any) => {
-          i.name = i.name + '学年';
-        });
-        forms.popSelectYearList = temp || [];
-        if (temp.length > 0) {
-          if (forms.lastClassSelect.gradeYear) {
-            forms.classSelect.gradeYear = forms.lastClassSelect.gradeYear;
-          } else {
-            forms.classSelect.gradeYear = temp[0].id;
-          }
-        }
-      } catch (e: any) {
-        //
-      }
-    };
-    // 获取学级
-    const getLevelList = async () => {
-      try {
-        const { data } = await getGradeLevelList();
-        const temp = data || [];
-        temp.forEach((i: any) => {
-          i.name = i.name + '级';
-        });
-        temp.unshift({
-          id: '',
-          name: '全部学级'
-        });
-        forms.popSelectLevelList = temp || [];
-        if (temp.length > 0) {
-          if (forms.lastClassSelect.gradeLevel) {
-            forms.classSelect.gradeLevel =
-              forms.lastClassSelect.gradeLevel + '';
-          } else {
-            forms.classSelect.gradeLevel = temp[0].id;
-          }
-        }
-      } catch {
-        //
-      }
-    };
-
-    const getCourseSchedulePage = async () => {
-      forms.classLoading = true;
-      try {
-        const { data } = await courseSchedulePage({
-          classGroupId: forms.classSelect.currentClass,
-          gradeLevel: forms.classSelect.gradeLevel,
-          gradeYear: forms.classSelect.gradeYear,
-          page: 1,
-          rows: 4,
-          teacherId: userStore.getUserInfo.id
-        });
-
-        const result = data.rows || [];
-        forms.total = data.total || 0;
-        const dateTime: any = {};
-        result.forEach((item: any) => {
-          const tempTime = dayjs(item.classDate).format('MM-DD');
-          if (!dateTime[tempTime]) {
-            dateTime[tempTime] = [];
-          }
-
-          const lessonCourseware = item.lessonCoursewareJson
-            ? JSON.parse(item.lessonCoursewareJson)
-            : {};
-          dateTime[tempTime].push({
-            classGroup: forms.classSelect.name,
-            teacherName: item.teacherName,
-            conent:
-              lessonCourseware.lessonCoursewareName +
-              ' | ' +
-              lessonCourseware.lessonCoursewareDetailName +
-              ' | ' +
-              lessonCourseware.lessonCoursewareKnowledgeDetailName,
-            image: item.teacherAvatar,
-            subjectName: item.subjectName
-          });
-        });
-
-        teachList.value = dateTime;
-      } catch (e: any) {
-        //
-        console.log(e);
-      }
-
-      forms.classLoading = false;
-    };
-
-    onMounted(async () => {
-      useResizeObserver(
-        document.querySelector('#homeInfoLeft-home') as any,
-        (entries: any) => {
-          const entry = entries[0];
-          const { height } = entry.contentRect;
-          forms.homeLeftHeight = height + 'px';
-        }
-      );
-
-      forms.classLoading = true;
-      await getLastClassRecode();
-
-      await getYearList();
-      await getLevelList();
-      await getClassList();
-      // await catchStore.getSubjects();
-
-      await getCourseSchedulePage();
-
-      if (!userStore.getUserInfo.account.updatePasswordFlag) {
-        showUpdatePassword.value = true;
-      } else {
-        forms.showGuide = true;
-      }
-
-      forms.classLoading = false;
-    });
-    return () => (
-      <div class={styles.homeWrap}>
-        <div class={styles.homeInfoLeft} id="homeInfoLeft-home">
-          <div class={styles.homeBanner}>
-            <div class={styles.applyInfo} id="home-1">
-              <div class={styles.centerInfo} id="home-0"></div>
-
-              <div class={styles.userInfo}>
-                <div class={styles.userName}>
-                  Hi,{userStore.getUserInfo?.nickname} {formatDateToDay()}~
-                </div>
-              </div>
-              {userStore.getUserInfo.gender === 1 ? (
-                <img src={teacherMan} class={styles.teacherMan} />
-              ) : (
-                <img src={teacherWoman} class={styles.teacherWoman} />
-              )}
-
-              <div class={styles.blackborad}>
-                <img src={blackBoardBg} class={styles.blackBoardBg} />
-              </div>
-              <div class={styles.applyContainer}>
-                <div class={[styles.applyItem, styles.applyItem1]}>
-                  <p>合理的规划教学内容,让学生更好掌握知识</p>
-                  <div
-                    class={[styles.applyBtn, styles.applyBtn1]}
-                    onClick={() => {
-                      // 备课
-                      router.push({
-                        path: '/prepare-lessons'
-                      });
-                    }}>
-                    <img src={homeText1} />
-                  </div>
-                </div>
-                <div class={[styles.applyItem, styles.applyItem2]}>
-                  <p>从这里开始,带领学生在知识的海洋中遨游</p>
-                  <div
-                    class={[styles.applyBtn, styles.applyBtn2]}
-                    onClick={() => {
-                      forms.showAttendClass = true;
-                    }}>
-                    <img src={homeText2} />
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <div class={styles.toolContainer}>
-            <div class={styles.toolTips}>
-              <div class={styles.toolTitle}>工具箱</div>
-              <div class={styles.toolContent}>
-                这里是常用的教学辅助工具,可帮助学生集中注意力、提高演奏效率,使演奏更完整平稳。让您在课堂上完成更好的教学。
-              </div>
-            </div>
-            <img src={iconTo} class={styles.iconTo} />
-            <div class={styles.toolFunction} id="home-3">
-              <div class={[styles.toolItem, styles.item1]}>
-                <img src={t1} />
-                {/* <p class={styles.toolMemo}>提升效率,练习好节奏</p> */}
-                <NButton
-                  class={styles.btn1}
-                  onClick={() => {
-                    showModalBeat.value = true;
-                  }}>
-                  节拍器
-                </NButton>
-              </div>
-              <div class={[styles.toolItem, styles.item2]}>
-                <img src={t2} />
-                {/* <p class={styles.toolMemo}>精准调音,一劳永逸</p> */}
-                <NButton
-                  class={styles.btn2}
-                  onClick={() => {
-                    showModalTone.value = true;
-                  }}>
-                  调音器
-                </NButton>
-              </div>
-              <div class={[styles.toolItem, styles.item3]}>
-                <img src={t3} />
-                {/* <p class={styles.toolMemo}>创造时间,集中注意力</p> */}
-                <NButton
-                  class={styles.btn3}
-                  onClick={() => {
-                    showModalTime.value = true;
-                  }}>
-                  计时器
-                </NButton>
-              </div>
-            </div>
-          </div>
-        </div>
-        <div
-          class={styles.homeInfoRight}
-          style={{ height: forms.homeLeftHeight }}>
-          <div class={styles.rightTeachingWrap}>
-            <div class={styles.headerContainer}>
-              <div
-                class={styles.HeaderWrap}
-                onClick={() => router.push('/setting')}>
-                <NImage
-                  previewDisabled
-                  class={styles.headerD}
-                  src={headerD}></NImage>
-                <NImage
-                  previewDisabled
-                  class={styles.defultHeade}
-                  src={userStore.getUserInfo.avatar || defultHeade}></NImage>
-              </div>
-            </div>
-            <div class={styles.headerInfo}>
-              <p class={styles.headerTitle}>{userStore.getUserInfo.nickname}</p>
-              {userStore.getUserInfo.schoolInfos &&
-                userStore.getUserInfo.schoolInfos.length > 0 && (
-                  <p class={styles.headerSubTitle}>
-                    {userStore.getUserInfo.schoolInfos[0].name}
-                    {/* | 音乐老师 */}
-                  </p>
-                )}
-            </div>
-            <div class={styles.rightTeachingWrapTitle}>
-              <h3 class={styles.rightTitle}>
-                <div class={styles.titleDot}></div>上课记录
-              </h3>
-
-              <div class={styles.classSearchList}>
-                <NSelect
-                  v-model:value={forms.classSelect.gradeYear}
-                  class={styles.lookMoreSearch}
-                  placeholder="全部学年"
-                  options={forms.popSelectYearList}
-                  labelField="name"
-                  valueField="id"
-                  onUpdate:value={async (val: any) => {
-                    forms.classSelect.gradeYear = val;
-                    forms.lastClassSelect.currentClass = null;
-                    forms.classSelect.currentClass = null;
-                    await getClassList();
-                    await getCourseSchedulePage();
-                  }}></NSelect>
-                <NSelect
-                  v-model:value={forms.classSelect.gradeLevel}
-                  class={styles.lookMoreSearch}
-                  placeholder="全部学级"
-                  options={forms.popSelectLevelList}
-                  labelField="name"
-                  valueField="id"
-                  onUpdate:value={async (val: any) => {
-                    forms.classSelect.gradeLevel = val;
-                    forms.lastClassSelect.currentClass = null;
-                    forms.classSelect.currentClass = null;
-                    await getClassList();
-                    await getCourseSchedulePage();
-                  }}></NSelect>
-                <NSelect
-                  v-model:value={forms.classSelect.currentClass}
-                  class={styles.lookMoreSearch}
-                  placeholder="选择班级"
-                  options={forms.popSelectOptions}
-                  onUpdate:value={(val: any) => {
-                    forms.popSelectOptions.forEach((item: any) => {
-                      if (item.value === val) {
-                        forms.classSelect.currentGradeNum =
-                          item.currentGradeNum;
-                        forms.classSelect.currentClass = item.value;
-                        forms.classSelect.name = item.label;
-                        forms.classSelect.upgradeFlag = item.upgradeFlag;
-                        getCourseSchedulePage();
-                      }
-                    });
-                  }}></NSelect>
-              </div>
-            </div>
-            <NSpin show={forms.classLoading} style={{ minHeight: '40vh' }}>
-              {Object.keys(teachList.value).length > 0 && (
-                <div class={styles.teachListWrap}>
-                  {Object.keys(teachList.value).map(key => (
-                    <TeachGroup
-                      list={teachList.value[key]}
-                      keys={key}></TeachGroup>
-                  ))}
-                </div>
-              )}
-
-              {Object.keys(teachList.value).length <= 0 &&
-                !forms.classLoading && <TheEmpty />}
-            </NSpin>
-
-            {forms.total > 4 && (
-              <div class={styles.teachListWrapWall}>
-                <span
-                  onClick={() => {
-                    // setTabsCaches('attendclass', 'tabName', {
-                    //   path: '/classDetail'
-                    // });
-                    sessionStorage.setItem('classDetailTabs', 'attendclass');
-                    router.push({
-                      path: '/classDetail',
-                      query: {
-                        name: forms.classSelect.name,
-                        id: forms.classSelect.currentClass,
-                        gradeYear: forms.classSelect.gradeYear,
-                        upgradeFlag: forms.classSelect.upgradeFlag ? 1 : 0 // 是否为历史班
-                      }
-                    });
-                  }}>
-                  查看全部
-                </span>
-              </div>
-            )}
-          </div>
-        </div>
-
-        <NModal
-          class={['modalTitle background']}
-          title={'节拍器'}
-          preset="card"
-          v-model:show={showModalBeat.value}
-          style={{ width: '687px' }}>
-          <div class={styles.modeWrap}>
-            <iframe
-              src={`${vaildUrl()}/metronome/?id=${new Date().getTime()}`}
-              scrolling="no"
-              frameborder="0"
-              width="100%"
-              onLoad={(val: any) => {
-                iframeDislableKeyboard(val.target);
-              }}
-              height={'650px'}></iframe>
-          </div>
-        </NModal>
-
-        <NModal
-          v-model:show={showModalTime.value}
-          class={['modalTitle background']}
-          title={'计时器'}
-          preset="card"
-          style={{ width: px2vw(772) }}>
-          <div>
-            <TimerMeter></TimerMeter>
-          </div>
-        </NModal>
-
-        <NModal class={['background']} v-model:show={showModalTone.value}>
-          <div>
-            <PlaceholderTone
-              onClose={() => {
-                showModalTone.value = false;
-              }}></PlaceholderTone>
-          </div>
-        </NModal>
-
-        {/* 弹窗查看 */}
-        <PreviewWindow
-          v-model:show={forms.showPreview}
-          type="attend"
-          params={forms.itemPreview}
-        />
-
-        {forms.showGuide ? <HomeGuide></HomeGuide> : null}
-
-        <NModal
-          v-model:show={showUpdatePassword.value}
-          class={['modalTitle', styles.showUpdatePassword]}
-          style="--n-title-font-weight: 600;"
-          preset="card"
-          title={'修改密码'}
-          closable={false}
-          maskClosable={false}
-          closeOnEsc={false}>
-          <UpdatePassword
-            onSubmit={() => {
-              // 密码更新成功
-              showUpdatePassword.value = true;
-              forms.showGuide = true;
-              userStore.logout().then(() => {
-                // 移除标签页
-                router
-                  .replace({
-                    name: 'login'
-                  })
-                  .finally(() => location.reload());
-              });
-            }}
-          />
-        </NModal>
-
-        <NModal
-          v-model:show={forms.showAttendClass}
-          preset="card"
-          showIcon={false}
-          class={['modalTitle background', styles.attendClassModal]}
-          title={'选择班级'}
-          blockScroll={false}>
-          <AttendClass
-            onClose={() => (forms.showAttendClass = false)}
-            type="change"
-            onConfirm={(item: any) => {
-              router.push({
-                path: '/prepare-lessons',
-                query: {
-                  ...item
-                }
-              });
-            }}
-          />
-        </NModal>
-      </div>
-    );
-  }
-});
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from './index.module.less';
+import {
+  NImage,
+  NButton,
+  NPopselect,
+  NModal,
+  useMessage,
+  NSpin,
+  NSelect
+} from 'naive-ui';
+import headerD from './images/headerD.png';
+import defultHeade from '@/components/layout/images/teacherIcon.png';
+import blackBoardBg from './images/blackboard_bg.png';
+import teacherMan from './images/teacher_man.png';
+import teacherWoman from './images/teacher_woman.png';
+
+import homeText1 from './images/home/home-text-1.png';
+import homeText2 from './images/home/home-text-2.png';
+import iconTo from './images/icon-to.png';
+import t1 from './images/t1.png';
+import t2 from './images/t2.png';
+import t3 from './images/t3.png';
+import { useRouter } from 'vue-router';
+import { useUserStore } from '/src/store/modules/users';
+// import SelectClass from './modals/selectClass';
+import dayjs from 'dayjs';
+import { gradeToCN, weekToCN } from '/src/utils/contants';
+import { useCatchStore } from '/src/store/modules/catchData';
+import TeachGroup from './modals/teachGroup';
+import {
+  classGroupList,
+  courseSchedulePage,
+  getGradeLevelList,
+  getGradeYearList
+} from './api';
+import TheEmpty from '/src/components/TheEmpty';
+import HomeGuide from '/src/custom-plugins/guide-page/home-guide';
+import TimerMeter from '/src/components/timerMeter';
+import { vaildUrl } from '/src/utils/urlUtils';
+import { iframeDislableKeyboard, px2vw } from '/src/utils';
+import PlaceholderTone from '@/components/layout/modals/placeholderTone';
+import PreviewWindow from '../preview-window';
+import UpdatePassword from '/src/components/layout/modals/update-password';
+import AttendClass from '../prepare-lessons/model/attend-class';
+import { useResizeObserver } from '@vueuse/core';
+import { modalClickMask } from '/src/state';
+// import { state } from '/src/state';
+export const formatDateToDay = () => {
+  const hours = dayjs().hour();
+  if (hours < 12) {
+    return '早上好'; //如果小时数小于12则输出“早上好!”
+  } else if (hours > 12 && hours < 18) {
+    return '下午好'; //如果小时数大于12并且小于18,输入“下午好!”
+  } else {
+    return '晚上好'; //如果上面两个条件都不符合,则输出“晚上好!”
+  }
+};
+
+export default defineComponent({
+  name: 'home-page',
+  setup() {
+    const catchStore = useCatchStore();
+    const router = useRouter();
+    const userStore = useUserStore();
+    const showUpdatePassword = ref(false);
+    const showModalBeat = ref(false);
+    const showModalTone = ref(false);
+    const showModalTime = ref(false);
+    const forms = reactive({
+      showAttendClass: false,
+      // useStatus: false,
+      studentList: [] as any,
+      bookVersionId: null,
+      classGroupId: null,
+      category: null,
+      subjectId: null,
+      musicTagList: [] as any,
+      loading: false,
+      list: [] as any,
+      unit: null,
+      unitList: [],
+      subjectList: [] as any,
+      gradeList: [] as any,
+      classLoading: false,
+      total: 0, // 上课数量
+      // 上次上课的数据
+      lastClassSelect: {
+        currentClass: null,
+        name: '',
+        upgradeFlag: false, // 是否为历史班
+        gradeYear: null as any,
+        gradeLevel: null as any
+      },
+      classSelect: {
+        currentGradeNum: null,
+        currentClass: null,
+        name: '',
+        upgradeFlag: false, // 是否为历史班
+        gradeLevel: null as any,
+        gradeYear: null
+      } as any,
+      popSelectOptions: [] as any,
+      popSelectYearList: [] as any,
+      popSelectLevelList: [] as any,
+      showGuide: false,
+      showPreview: false,
+      itemPreview: {} as any,
+      homeLeftHeight: 'auto'
+    });
+    const teachList = ref({} as any);
+
+    // 获取年级班级
+    const getClassList = async () => {
+      try {
+        const { data } = await classGroupList({
+          gradeLevel: forms.classSelect.gradeLevel,
+          gradeYear: forms.classSelect.gradeYear
+        });
+        const cList = data || [];
+        const gradeList: any = [];
+        const popSelectOptions: any = [];
+        // getLastClassRecode()
+        cList.forEach((item: any, index: number) => {
+          // 判断是否已经有班级了
+          if (index === 0) {
+            if (forms.lastClassSelect.currentClass) {
+              forms.classSelect.currentClass =
+                forms.lastClassSelect.currentClass;
+              forms.classSelect.name = forms.lastClassSelect.name;
+              forms.classSelect.upgradeFlag = forms.lastClassSelect.upgradeFlag;
+            } else {
+              const temp = item.classGroupList[0];
+              forms.classSelect.currentGradeNum = item.currentGradeNum;
+              forms.classSelect.currentClass = temp.id;
+              forms.classSelect.name = temp.name;
+            }
+          }
+
+          const classList: any = [];
+          item.classGroupList.forEach((i: any) => {
+            classList.push({
+              label: i.currentClass + '班',
+              value: i.id,
+              lastStudy: i.lastStudy
+            });
+
+            popSelectOptions.push({
+              label: i.name,
+              value: i.id,
+              currentGradeNum: item.currentGradeNum,
+              lastStudy: i.lastStudy
+            });
+          });
+
+          gradeList.push({
+            label: gradeToCN[item.currentGradeNum],
+            value: item.currentGradeNum,
+            childrens: classList
+          });
+        });
+        forms.popSelectOptions = popSelectOptions;
+        forms.gradeList = gradeList;
+      } catch {
+        //
+      }
+    };
+    const getLastClassRecode = async () => {
+      const { data } = await courseSchedulePage({
+        page: 1,
+        rows: 1,
+        teacherId: userStore.getUserInfo.id
+      });
+      if (data.rows.length > 0 && data.rows[0]) {
+        const tempRow = data.rows[0];
+        // forms.lastClassSelect.currentClass = tempRow.classGroupId;
+        // forms.lastClassSelect.name = tempRow.classGroupName;
+        forms.lastClassSelect = {
+          currentClass: tempRow.classGroupId,
+          name: tempRow.classGroupName,
+          upgradeFlag: tempRow.upgradeFlag,
+          gradeYear: tempRow.gradeYear + '',
+          gradeLevel: tempRow.gradeLevel + ''
+        };
+      }
+    };
+
+    // 获取学年
+    const getYearList = async () => {
+      try {
+        const { data } = await getGradeYearList();
+        const temp = data || [];
+        temp.forEach((i: any) => {
+          i.name = i.name + '学年';
+        });
+        forms.popSelectYearList = temp || [];
+        if (temp.length > 0) {
+          if (forms.lastClassSelect.gradeYear) {
+            forms.classSelect.gradeYear = forms.lastClassSelect.gradeYear;
+          } else {
+            forms.classSelect.gradeYear = temp[0].id;
+          }
+        }
+      } catch (e: any) {
+        //
+      }
+    };
+    // 获取学级
+    const getLevelList = async () => {
+      try {
+        const { data } = await getGradeLevelList();
+        const temp = data || [];
+        temp.forEach((i: any) => {
+          i.name = i.name + '级';
+        });
+        temp.unshift({
+          id: '',
+          name: '全部学级'
+        });
+        forms.popSelectLevelList = temp || [];
+        if (temp.length > 0) {
+          if (forms.lastClassSelect.gradeLevel) {
+            forms.classSelect.gradeLevel =
+              forms.lastClassSelect.gradeLevel + '';
+          } else {
+            forms.classSelect.gradeLevel = temp[0].id;
+          }
+        }
+      } catch {
+        //
+      }
+    };
+
+    const getCourseSchedulePage = async () => {
+      forms.classLoading = true;
+      try {
+        const { data } = await courseSchedulePage({
+          classGroupId: forms.classSelect.currentClass,
+          gradeLevel: forms.classSelect.gradeLevel,
+          gradeYear: forms.classSelect.gradeYear,
+          page: 1,
+          rows: 4,
+          teacherId: userStore.getUserInfo.id
+        });
+
+        const result = data.rows || [];
+        forms.total = data.total || 0;
+        const dateTime: any = {};
+        result.forEach((item: any) => {
+          const tempTime = dayjs(item.classDate).format('MM-DD');
+          if (!dateTime[tempTime]) {
+            dateTime[tempTime] = [];
+          }
+
+          const lessonCourseware = item.lessonCoursewareJson
+            ? JSON.parse(item.lessonCoursewareJson)
+            : {};
+          dateTime[tempTime].push({
+            classGroup: forms.classSelect.name,
+            teacherName: item.teacherName,
+            conent:
+              lessonCourseware.lessonCoursewareName +
+              ' | ' +
+              lessonCourseware.lessonCoursewareDetailName +
+              ' | ' +
+              lessonCourseware.lessonCoursewareKnowledgeDetailName,
+            image: item.teacherAvatar,
+            subjectName: item.subjectName
+          });
+        });
+
+        teachList.value = dateTime;
+      } catch (e: any) {
+        //
+        console.log(e);
+      }
+
+      forms.classLoading = false;
+    };
+
+    onMounted(async () => {
+      useResizeObserver(
+        document.querySelector('#homeInfoLeft-home') as any,
+        (entries: any) => {
+          const entry = entries[0];
+          const { height } = entry.contentRect;
+          forms.homeLeftHeight = height + 'px';
+        }
+      );
+
+      forms.classLoading = true;
+      await getLastClassRecode();
+
+      await getYearList();
+      await getLevelList();
+      await getClassList();
+      // await catchStore.getSubjects();
+
+      await getCourseSchedulePage();
+
+      if (!userStore.getUserInfo.account.updatePasswordFlag) {
+        showUpdatePassword.value = true;
+      } else {
+        forms.showGuide = true;
+      }
+
+      forms.classLoading = false;
+    });
+    return () => (
+      <div class={styles.homeWrap}>
+        <div class={styles.homeInfoLeft} id="homeInfoLeft-home">
+          <div class={styles.homeBanner}>
+            <div class={styles.applyInfo} id="home-1">
+              <div class={styles.centerInfo} id="home-0"></div>
+
+              <div class={styles.userInfo}>
+                <div class={styles.userName}>
+                  Hi,{userStore.getUserInfo?.nickname} {formatDateToDay()}~
+                </div>
+              </div>
+              {userStore.getUserInfo.gender === 1 ? (
+                <img src={teacherMan} class={styles.teacherMan} />
+              ) : (
+                <img src={teacherWoman} class={styles.teacherWoman} />
+              )}
+
+              <div class={styles.blackborad}>
+                <img src={blackBoardBg} class={styles.blackBoardBg} />
+              </div>
+              <div class={styles.applyContainer}>
+                <div class={[styles.applyItem, styles.applyItem1]}>
+                  <p>合理的规划教学内容,让学生更好掌握知识</p>
+                  <div
+                    class={[styles.applyBtn, styles.applyBtn1]}
+                    onClick={() => {
+                      // 备课
+                      router.push({
+                        path: '/prepare-lessons'
+                      });
+                    }}>
+                    <img src={homeText1} />
+                  </div>
+                </div>
+                <div class={[styles.applyItem, styles.applyItem2]}>
+                  <p>从这里开始,带领学生在知识的海洋中遨游</p>
+                  <div
+                    class={[styles.applyBtn, styles.applyBtn2]}
+                    onClick={() => {
+                      forms.showAttendClass = true;
+                    }}>
+                    <img src={homeText2} />
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+
+          <div class={styles.toolContainer}>
+            <div class={styles.toolTips}>
+              <div class={styles.toolTitle}>工具箱</div>
+              <div class={styles.toolContent}>
+                这里是常用的教学辅助工具,可帮助学生集中注意力、提高演奏效率,使演奏更完整平稳。让您在课堂上完成更好的教学。
+              </div>
+            </div>
+            <img src={iconTo} class={styles.iconTo} />
+            <div class={styles.toolFunction} id="home-3">
+              <div class={[styles.toolItem, styles.item1]}>
+                <img src={t1} />
+                {/* <p class={styles.toolMemo}>提升效率,练习好节奏</p> */}
+                <NButton
+                  class={styles.btn1}
+                  onClick={() => {
+                    showModalBeat.value = true;
+                  }}>
+                  节拍器
+                </NButton>
+              </div>
+              <div class={[styles.toolItem, styles.item2]}>
+                <img src={t2} />
+                {/* <p class={styles.toolMemo}>精准调音,一劳永逸</p> */}
+                <NButton
+                  class={styles.btn2}
+                  onClick={() => {
+                    showModalTone.value = true;
+                  }}>
+                  调音器
+                </NButton>
+              </div>
+              <div class={[styles.toolItem, styles.item3]}>
+                <img src={t3} />
+                {/* <p class={styles.toolMemo}>创造时间,集中注意力</p> */}
+                <NButton
+                  class={styles.btn3}
+                  onClick={() => {
+                    showModalTime.value = true;
+                  }}>
+                  计时器
+                </NButton>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div
+          class={styles.homeInfoRight}
+          style={{ height: forms.homeLeftHeight }}>
+          <div class={styles.rightTeachingWrap}>
+            <div class={styles.headerContainer}>
+              <div
+                class={styles.HeaderWrap}
+                onClick={() => router.push('/setting')}>
+                <NImage
+                  previewDisabled
+                  class={styles.headerD}
+                  src={headerD}></NImage>
+                <NImage
+                  previewDisabled
+                  class={styles.defultHeade}
+                  src={userStore.getUserInfo.avatar || defultHeade}></NImage>
+              </div>
+            </div>
+            <div class={styles.headerInfo}>
+              <p class={styles.headerTitle}>{userStore.getUserInfo.nickname}</p>
+              {userStore.getUserInfo.schoolInfos &&
+                userStore.getUserInfo.schoolInfos.length > 0 && (
+                  <p class={styles.headerSubTitle}>
+                    {userStore.getUserInfo.schoolInfos[0].name}
+                    {/* | 音乐老师 */}
+                  </p>
+                )}
+            </div>
+            <div class={styles.rightTeachingWrapTitle}>
+              <h3 class={styles.rightTitle}>
+                <div class={styles.titleDot}></div>上课记录
+              </h3>
+
+              <div class={styles.classSearchList}>
+                <NSelect
+                  v-model:value={forms.classSelect.gradeYear}
+                  class={styles.lookMoreSearch}
+                  placeholder="全部学年"
+                  options={forms.popSelectYearList}
+                  labelField="name"
+                  valueField="id"
+                  onUpdate:value={async (val: any) => {
+                    forms.classSelect.gradeYear = val;
+                    forms.lastClassSelect.currentClass = null;
+                    forms.classSelect.currentClass = null;
+                    await getClassList();
+                    await getCourseSchedulePage();
+                  }}></NSelect>
+                <NSelect
+                  v-model:value={forms.classSelect.gradeLevel}
+                  class={styles.lookMoreSearch}
+                  placeholder="全部学级"
+                  options={forms.popSelectLevelList}
+                  labelField="name"
+                  valueField="id"
+                  onUpdate:value={async (val: any) => {
+                    forms.classSelect.gradeLevel = val;
+                    forms.lastClassSelect.currentClass = null;
+                    forms.classSelect.currentClass = null;
+                    await getClassList();
+                    await getCourseSchedulePage();
+                  }}></NSelect>
+                <NSelect
+                  v-model:value={forms.classSelect.currentClass}
+                  class={styles.lookMoreSearch}
+                  placeholder="选择班级"
+                  options={forms.popSelectOptions}
+                  onUpdate:value={(val: any) => {
+                    forms.popSelectOptions.forEach((item: any) => {
+                      if (item.value === val) {
+                        forms.classSelect.currentGradeNum =
+                          item.currentGradeNum;
+                        forms.classSelect.currentClass = item.value;
+                        forms.classSelect.name = item.label;
+                        forms.classSelect.upgradeFlag = item.upgradeFlag;
+                        getCourseSchedulePage();
+                      }
+                    });
+                  }}></NSelect>
+              </div>
+            </div>
+            <NSpin show={forms.classLoading} style={{ minHeight: '40vh' }}>
+              {Object.keys(teachList.value).length > 0 && (
+                <div class={styles.teachListWrap}>
+                  {Object.keys(teachList.value).map(key => (
+                    <TeachGroup
+                      list={teachList.value[key]}
+                      keys={key}></TeachGroup>
+                  ))}
+                </div>
+              )}
+
+              {Object.keys(teachList.value).length <= 0 &&
+                !forms.classLoading && <TheEmpty />}
+            </NSpin>
+
+            {forms.total > 4 && (
+              <div class={styles.teachListWrapWall}>
+                <span
+                  onClick={() => {
+                    // setTabsCaches('attendclass', 'tabName', {
+                    //   path: '/classDetail'
+                    // });
+                    sessionStorage.setItem('classDetailTabs', 'attendclass');
+                    router.push({
+                      path: '/classDetail',
+                      query: {
+                        name: forms.classSelect.name,
+                        id: forms.classSelect.currentClass,
+                        gradeYear: forms.classSelect.gradeYear,
+                        upgradeFlag: forms.classSelect.upgradeFlag ? 1 : 0 // 是否为历史班
+                      }
+                    });
+                  }}>
+                  查看全部
+                </span>
+              </div>
+            )}
+          </div>
+        </div>
+
+        <NModal
+          maskClosable={modalClickMask}
+          class={['modalTitle background']}
+          title={'节拍器'}
+          preset="card"
+          v-model:show={showModalBeat.value}
+          style={{ width: '687px' }}>
+          <div class={styles.modeWrap}>
+            <iframe
+              src={`${vaildUrl()}/metronome/?id=${new Date().getTime()}`}
+              scrolling="no"
+              frameborder="0"
+              width="100%"
+              onLoad={(val: any) => {
+                iframeDislableKeyboard(val.target);
+              }}
+              height={'650px'}></iframe>
+          </div>
+        </NModal>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={showModalTime.value}
+          class={['modalTitle background']}
+          title={'计时器'}
+          preset="card"
+          style={{ width: px2vw(772) }}>
+          <div>
+            <TimerMeter></TimerMeter>
+          </div>
+        </NModal>
+
+        <NModal
+          maskClosable={modalClickMask}
+          class={['background']}
+          v-model:show={showModalTone.value}>
+          <div>
+            <PlaceholderTone
+              onClose={() => {
+                showModalTone.value = false;
+              }}></PlaceholderTone>
+          </div>
+        </NModal>
+
+        {/* 弹窗查看 */}
+        <PreviewWindow
+          v-model:show={forms.showPreview}
+          type="attend"
+          params={forms.itemPreview}
+        />
+
+        {forms.showGuide ? <HomeGuide></HomeGuide> : null}
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={showUpdatePassword.value}
+          class={['modalTitle', styles.showUpdatePassword]}
+          style="--n-title-font-weight: 600;"
+          preset="card"
+          title={'修改密码'}
+          closable={false}
+          maskClosable={false}
+          closeOnEsc={false}>
+          <UpdatePassword
+            onSubmit={() => {
+              // 密码更新成功
+              showUpdatePassword.value = true;
+              forms.showGuide = true;
+              userStore.logout().then(() => {
+                // 移除标签页
+                router
+                  .replace({
+                    name: 'login'
+                  })
+                  .finally(() => location.reload());
+              });
+            }}
+          />
+        </NModal>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={forms.showAttendClass}
+          preset="card"
+          showIcon={false}
+          class={['modalTitle background', styles.attendClassModal]}
+          title={'选择班级'}
+          blockScroll={false}>
+          <AttendClass
+            onClose={() => (forms.showAttendClass = false)}
+            type="change"
+            onConfirm={(item: any) => {
+              router.push({
+                path: '/prepare-lessons',
+                query: {
+                  ...item
+                }
+              });
+            }}
+          />
+        </NModal>
+      </div>
+    );
+  }
+});

+ 324 - 318
src/views/home/index.tsx

@@ -1,318 +1,324 @@
-import { defineComponent, onMounted, reactive, ref } from 'vue';
-import styles from './index.module.less';
-import { NImage, NModal } from 'naive-ui';
-import defultHeade from '@/components/layout/images/teacherIcon.png';
-import teacherMan from './img/teacher-man.png';
-import teacherWoman from './img/teacher-woman.png';
-import iconEchats from './img/icon-echats.png';
-import t1 from './img/t1.png';
-import t11 from './img/t1-1.png';
-import t12 from './img/t1-4.png';
-
-import t2 from './img/t2.png';
-import t21 from './img/t2-1.png';
-import t22 from './img/t2-4.png';
-
-import f1 from './img/f1.png';
-import f11 from './img/f1-1.png';
-
-import f2 from './img/f2.png';
-import f21 from './img/f2-1.png';
-
-import f3 from './img/f3.png';
-import f31 from './img/f3-1.png';
-
-import f4 from './img/f4.png';
-import f41 from './img/f4-1.png';
-
-import { useRouter } from 'vue-router';
-import { useUserStore } from '/src/store/modules/users';
-// import SelectClass from './modals/selectClass';
-import dayjs from 'dayjs';
-import UpdatePassword from '/src/components/layout/modals/update-password';
-import AttendClass from '../prepare-lessons/model/attend-class';
-import { useResizeObserver } from '@vueuse/core';
-import PlaceholderTone from '/src/components/layout/modals/placeholderTone';
-import PreviewWindow from '../preview-window';
-import { state } from '/src/state';
-import SubjectModal from './modals/subject-modal';
-import { vaildMusicScoreUrl } from '/src/utils/urlUtils';
-import HomeGuide from '/src/custom-plugins/guide-page/home-guide';
-// import { state } from '/src/state';
-export const formatDateToDay = () => {
-  const hours = dayjs().hour();
-  if (hours < 12) {
-    return '早上好'; //如果小时数小于12则输出“早上好!”
-  } else if (hours > 12 && hours < 18) {
-    return '下午好'; //如果小时数大于12并且小于18,输入“下午好!”
-  } else {
-    return '晚上好'; //如果上面两个条件都不符合,则输出“晚上好!”
-  }
-};
-
-export default defineComponent({
-  name: 'home-page',
-  setup() {
-    const router = useRouter();
-    const userStore = useUserStore();
-    const showUpdatePassword = ref(false);
-    const showModalTone = ref(false);
-    const forms = reactive({
-      previewModal: false,
-      previewParams: {} as any,
-      showAttendClass: false,
-      loading: false,
-      message: '',
-      list: [] as any,
-      unit: null,
-      classLoading: false,
-      showGuide: false,
-      homeLeftHeight: 'auto',
-      subjectSyncVisiable: false
-    });
-
-    onMounted(async () => {
-      useResizeObserver(
-        document.querySelector('#homeInfoLeft-home') as any,
-        (entries: any) => {
-          const entry = entries[0];
-          const { height } = entry.contentRect;
-          forms.homeLeftHeight = height + 'px';
-        }
-      );
-
-      forms.classLoading = true;
-
-      if (!userStore.getUserInfo.account.updatePasswordFlag) {
-        showUpdatePassword.value = true;
-      } else {
-        forms.showGuide = true;
-      }
-
-      forms.classLoading = false;
-    });
-    return () => (
-      <div class={styles.homeWrap}>
-        <div id="home-0"></div>
-        <div class={styles.homeSection}>
-          <div class={styles.homeLeft}>
-            <i class={styles.homeWindow}></i>
-            <div class={styles.homeUserInfo}>
-              <i class={styles.homeTag}></i>
-              <div class={styles.homeUsers}>
-                <NImage
-                  src={userStore.getUserInfo?.avatar || defultHeade}
-                  class={styles.userImg}
-                />
-                <p class={styles.userName}>
-                  Hi,{userStore.getUserInfo?.nickname} {formatDateToDay()}~
-                </p>
-              </div>
-              <p class={styles.desc}>
-                点击下方按钮,您可以根据声部进行备课或是直接选择班级开始上课,更好的规划教学哦!
-              </p>
-
-              <div class={styles.homeBtnGroup}>
-                <div
-                  class={styles.btnBk}
-                  onClick={() => {
-                    // 备课
-                    router.push({
-                      path: '/prepare-lessons'
-                    });
-                  }}></div>
-                <div
-                  id="home-4"
-                  class={styles.btnClass}
-                  onClick={() => {
-                    forms.showAttendClass = true;
-                  }}></div>
-              </div>
-
-              <img src={iconEchats} class={styles.homeEchats} />
-            </div>
-
-            {userStore.getUserInfo.gender === 1 ? (
-              <div>
-                <img src={teacherMan} class={styles.teacherMan} />
-                <span class={styles.teacherBook}></span>
-              </div>
-            ) : (
-              <img src={teacherWoman} class={styles.teacherWoman} />
-            )}
-          </div>
-
-          <div class={styles.homeRight}>
-            <div class={styles.rightTop}>
-              <div
-                class={[styles.topSection, styles.topSection1]}
-                onClick={() => {
-                  forms.subjectSyncVisiable = true;
-                }}>
-                <img src={t1} class={styles.tFun} />
-                <img src={t11} class={styles.tTxt} />
-                <img src={t12} class={styles.tTitle} />
-              </div>
-              <div
-                class={[styles.topSection, styles.topSection2]}
-                onClick={() => {
-                  const origin = /(localhost|192)/.test(location.host)
-                    ? 'https://test.lexiaoya.cn'
-                    : location.origin;
-                  const src = `${origin}/classroom-app/#/tempo-practice?Authorization=${userStore.getToken}&win=pc&platform=modal&back=show`;
-                  if (window.matchMedia('(display-mode: standalone)').matches) {
-                    state.application = window.matchMedia(
-                      '(display-mode: standalone)'
-                    ).matches;
-                    forms.previewModal = true;
-                    forms.previewParams.src = src + '&backBtnType=microapp';
-                  } else {
-                    window.open(src);
-                  }
-                }}>
-                <img src={t2} class={styles.tFun} />
-                <img src={t21} class={styles.tTxt} />
-                <img src={t22} class={styles.tTitle} />
-              </div>
-            </div>
-
-            <div class={styles.rightBottom}>
-              <div
-                class={styles.bottomSection}
-                onClick={() => {
-                  router.push('/content-instruments');
-                }}>
-                <img src={f1} class={styles.bFun} />
-                <img src={f11} class={styles.bTxt} />
-              </div>
-              <div
-                class={styles.bottomSection}
-                onClick={() => {
-                  router.push('/content-music');
-                }}>
-                <img src={f2} class={styles.bFun} />
-                <img src={f21} class={styles.bTxt} />
-              </div>
-              <div
-                class={styles.bottomSection}
-                onClick={() => {
-                  router.push('/content-musician');
-                }}>
-                <img src={f3} class={styles.bFun} />
-                <img src={f31} class={[styles.bTxt, styles.bTxt2]} />
-              </div>
-              <div
-                class={styles.bottomSection}
-                onClick={() => {
-                  router.push('/content-knowledge');
-                }}>
-                <img src={f4} class={styles.bFun} />
-                <img src={f41} class={[styles.bTxt, styles.bTxt3]} />
-              </div>
-            </div>
-          </div>
-        </div>
-
-        {forms.showGuide ? <HomeGuide></HomeGuide> : null}
-
-        <NModal class={['background']} v-model:show={showModalTone.value}>
-          <div>
-            <PlaceholderTone
-              message={forms.message}
-              onClose={() => {
-                showModalTone.value = false;
-              }}></PlaceholderTone>
-          </div>
-        </NModal>
-
-        <NModal
-          v-model:show={showUpdatePassword.value}
-          class={['modalTitle', styles.showUpdatePassword]}
-          style="--n-title-font-weight: 600;"
-          preset="card"
-          title={'修改密码'}
-          closable={false}
-          maskClosable={false}
-          closeOnEsc={false}>
-          <UpdatePassword
-            onSubmit={() => {
-              // 密码更新成功
-              showUpdatePassword.value = true;
-              forms.showGuide = true;
-              userStore.logout().then(() => {
-                // 移除标签页
-                router
-                  .replace({
-                    name: 'login'
-                  })
-                  .finally(() => location.reload());
-              });
-            }}
-          />
-        </NModal>
-
-        <NModal
-          v-model:show={forms.showAttendClass}
-          preset="card"
-          showIcon={false}
-          class={['modalTitle background', styles.attendClassModal]}
-          title={'选择班级'}
-          blockScroll={false}>
-          <AttendClass
-            onClose={() => (forms.showAttendClass = false)}
-            type="change"
-            onConfirm={(item: any) => {
-              router.push({
-                path: '/prepare-lessons',
-                query: {
-                  ...item
-                }
-              });
-            }}
-          />
-        </NModal>
-
-        <PreviewWindow
-          v-model:show={forms.previewModal}
-          type="music"
-          params={forms.previewParams}
-        />
-
-        {/* 完成编辑时,选择声部 */}
-        <NModal
-          v-model:show={forms.subjectSyncVisiable}
-          preset="card"
-          class={['modalTitle background', styles.subjectSyncModal]}
-          title={'选择乐器'}>
-          <SubjectModal
-            onClose={() => (forms.subjectSyncVisiable = false)}
-            onConfirm={async (item: any) => {
-              // musicsrc = `${origin}/instrument?modelType=practise&id=${
-              //   data.list[data.listActive].xmlFileUrl
-              // }&Authorization=${userStore.getToken}/#/preview`;
-              // const origin = /(localhost|192)/.test(location.host)
-              //   ? 'https://test.lexiaoya.cn'
-              //   : location.origin;
-              // view-figner?subjectCode=pan-flute
-              const src = `${vaildMusicScoreUrl()}/instrument/#/view-figner?Authorization=${
-                userStore.getToken
-              }&code=${item.code}&platform=pc&type=listenMode`;
-              // const src = `http://192.168.3.220:3000/instrument.html#/view-figner?Authorization=${userStore.getToken}&code=${item.code}`;
-              if (window.matchMedia('(display-mode: standalone)').matches) {
-                state.application = window.matchMedia(
-                  '(display-mode: standalone)'
-                ).matches;
-                forms.previewModal = true;
-                forms.previewParams.src = src + '&matchMedia=1';
-              } else {
-                window.open(src);
-              }
-
-              forms.subjectSyncVisiable = false;
-            }}
-          />
-        </NModal>
-      </div>
-    );
-  }
-});
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from './index.module.less';
+import { NImage, NModal } from 'naive-ui';
+import defultHeade from '@/components/layout/images/teacherIcon.png';
+import teacherMan from './img/teacher-man.png';
+import teacherWoman from './img/teacher-woman.png';
+import iconEchats from './img/icon-echats.png';
+import t1 from './img/t1.png';
+import t11 from './img/t1-1.png';
+import t12 from './img/t1-4.png';
+
+import t2 from './img/t2.png';
+import t21 from './img/t2-1.png';
+import t22 from './img/t2-4.png';
+
+import f1 from './img/f1.png';
+import f11 from './img/f1-1.png';
+
+import f2 from './img/f2.png';
+import f21 from './img/f2-1.png';
+
+import f3 from './img/f3.png';
+import f31 from './img/f3-1.png';
+
+import f4 from './img/f4.png';
+import f41 from './img/f4-1.png';
+
+import { useRouter } from 'vue-router';
+import { useUserStore } from '/src/store/modules/users';
+// import SelectClass from './modals/selectClass';
+import dayjs from 'dayjs';
+import UpdatePassword from '/src/components/layout/modals/update-password';
+import AttendClass from '../prepare-lessons/model/attend-class';
+import { useResizeObserver } from '@vueuse/core';
+import PlaceholderTone from '/src/components/layout/modals/placeholderTone';
+import PreviewWindow from '../preview-window';
+import { modalClickMask, state } from '/src/state';
+import SubjectModal from './modals/subject-modal';
+import { vaildMusicScoreUrl } from '/src/utils/urlUtils';
+import HomeGuide from '/src/custom-plugins/guide-page/home-guide';
+// import { state } from '/src/state';
+export const formatDateToDay = () => {
+  const hours = dayjs().hour();
+  if (hours < 12) {
+    return '早上好'; //如果小时数小于12则输出“早上好!”
+  } else if (hours > 12 && hours < 18) {
+    return '下午好'; //如果小时数大于12并且小于18,输入“下午好!”
+  } else {
+    return '晚上好'; //如果上面两个条件都不符合,则输出“晚上好!”
+  }
+};
+
+export default defineComponent({
+  name: 'home-page',
+  setup() {
+    const router = useRouter();
+    const userStore = useUserStore();
+    const showUpdatePassword = ref(false);
+    const showModalTone = ref(false);
+    const forms = reactive({
+      previewModal: false,
+      previewParams: {} as any,
+      showAttendClass: false,
+      loading: false,
+      message: '',
+      list: [] as any,
+      unit: null,
+      classLoading: false,
+      showGuide: false,
+      homeLeftHeight: 'auto',
+      subjectSyncVisiable: false
+    });
+
+    onMounted(async () => {
+      useResizeObserver(
+        document.querySelector('#homeInfoLeft-home') as any,
+        (entries: any) => {
+          const entry = entries[0];
+          const { height } = entry.contentRect;
+          forms.homeLeftHeight = height + 'px';
+        }
+      );
+
+      forms.classLoading = true;
+
+      if (!userStore.getUserInfo.account.updatePasswordFlag) {
+        showUpdatePassword.value = true;
+      } else {
+        forms.showGuide = true;
+      }
+
+      forms.classLoading = false;
+    });
+    return () => (
+      <div class={styles.homeWrap}>
+        <div id="home-0"></div>
+        <div class={styles.homeSection}>
+          <div class={styles.homeLeft}>
+            <i class={styles.homeWindow}></i>
+            <div class={styles.homeUserInfo}>
+              <i class={styles.homeTag}></i>
+              <div class={styles.homeUsers}>
+                <NImage
+                  src={userStore.getUserInfo?.avatar || defultHeade}
+                  class={styles.userImg}
+                />
+                <p class={styles.userName}>
+                  Hi,{userStore.getUserInfo?.nickname} {formatDateToDay()}~
+                </p>
+              </div>
+              <p class={styles.desc}>
+                点击下方按钮,您可以根据声部进行备课或是直接选择班级开始上课,更好的规划教学哦!
+              </p>
+
+              <div class={styles.homeBtnGroup}>
+                <div
+                  class={styles.btnBk}
+                  onClick={() => {
+                    // 备课
+                    router.push({
+                      path: '/prepare-lessons'
+                    });
+                  }}></div>
+                <div
+                  id="home-4"
+                  class={styles.btnClass}
+                  onClick={() => {
+                    forms.showAttendClass = true;
+                  }}></div>
+              </div>
+
+              <img src={iconEchats} class={styles.homeEchats} />
+            </div>
+
+            {userStore.getUserInfo.gender === 1 ? (
+              <div>
+                <img src={teacherMan} class={styles.teacherMan} />
+                <span class={styles.teacherBook}></span>
+              </div>
+            ) : (
+              <img src={teacherWoman} class={styles.teacherWoman} />
+            )}
+          </div>
+
+          <div class={styles.homeRight}>
+            <div class={styles.rightTop}>
+              <div
+                class={[styles.topSection, styles.topSection1]}
+                onClick={() => {
+                  forms.subjectSyncVisiable = true;
+                }}>
+                <img src={t1} class={styles.tFun} />
+                <img src={t11} class={styles.tTxt} />
+                <img src={t12} class={styles.tTitle} />
+              </div>
+              <div
+                class={[styles.topSection, styles.topSection2]}
+                onClick={() => {
+                  const origin = /(localhost|192)/.test(location.host)
+                    ? 'https://test.lexiaoya.cn'
+                    : location.origin;
+                  const src = `${origin}/classroom-app/#/tempo-practice?Authorization=${userStore.getToken}&win=pc&platform=modal&back=show`;
+                  if (window.matchMedia('(display-mode: standalone)').matches) {
+                    state.application = window.matchMedia(
+                      '(display-mode: standalone)'
+                    ).matches;
+                    forms.previewModal = true;
+                    forms.previewParams.src = src + '&backBtnType=microapp';
+                  } else {
+                    window.open(src);
+                  }
+                }}>
+                <img src={t2} class={styles.tFun} />
+                <img src={t21} class={styles.tTxt} />
+                <img src={t22} class={styles.tTitle} />
+              </div>
+            </div>
+
+            <div class={styles.rightBottom}>
+              <div
+                class={styles.bottomSection}
+                onClick={() => {
+                  router.push('/content-instruments');
+                }}>
+                <img src={f1} class={styles.bFun} />
+                <img src={f11} class={styles.bTxt} />
+              </div>
+              <div
+                class={styles.bottomSection}
+                onClick={() => {
+                  router.push('/content-music');
+                }}>
+                <img src={f2} class={styles.bFun} />
+                <img src={f21} class={styles.bTxt} />
+              </div>
+              <div
+                class={styles.bottomSection}
+                onClick={() => {
+                  router.push('/content-musician');
+                }}>
+                <img src={f3} class={styles.bFun} />
+                <img src={f31} class={[styles.bTxt, styles.bTxt2]} />
+              </div>
+              <div
+                class={styles.bottomSection}
+                onClick={() => {
+                  router.push('/content-knowledge');
+                }}>
+                <img src={f4} class={styles.bFun} />
+                <img src={f41} class={[styles.bTxt, styles.bTxt3]} />
+              </div>
+            </div>
+          </div>
+        </div>
+
+        {forms.showGuide ? <HomeGuide></HomeGuide> : null}
+
+        <NModal
+          maskClosable={modalClickMask}
+          class={['background']}
+          v-model:show={showModalTone.value}>
+          <div>
+            <PlaceholderTone
+              message={forms.message}
+              onClose={() => {
+                showModalTone.value = false;
+              }}></PlaceholderTone>
+          </div>
+        </NModal>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={showUpdatePassword.value}
+          class={['modalTitle', styles.showUpdatePassword]}
+          style="--n-title-font-weight: 600;"
+          preset="card"
+          title={'修改密码'}
+          closable={false}
+          maskClosable={false}
+          closeOnEsc={false}>
+          <UpdatePassword
+            onSubmit={() => {
+              // 密码更新成功
+              showUpdatePassword.value = true;
+              forms.showGuide = true;
+              userStore.logout().then(() => {
+                // 移除标签页
+                router
+                  .replace({
+                    name: 'login'
+                  })
+                  .finally(() => location.reload());
+              });
+            }}
+          />
+        </NModal>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={forms.showAttendClass}
+          preset="card"
+          showIcon={false}
+          class={['modalTitle background', styles.attendClassModal]}
+          title={'选择班级'}
+          blockScroll={false}>
+          <AttendClass
+            onClose={() => (forms.showAttendClass = false)}
+            type="change"
+            onConfirm={(item: any) => {
+              router.push({
+                path: '/prepare-lessons',
+                query: {
+                  ...item
+                }
+              });
+            }}
+          />
+        </NModal>
+
+        <PreviewWindow
+          v-model:show={forms.previewModal}
+          type="music"
+          params={forms.previewParams}
+        />
+
+        {/* 完成编辑时,选择声部 */}
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={forms.subjectSyncVisiable}
+          preset="card"
+          class={['modalTitle background', styles.subjectSyncModal]}
+          title={'选择乐器'}>
+          <SubjectModal
+            onClose={() => (forms.subjectSyncVisiable = false)}
+            onConfirm={async (item: any) => {
+              // musicsrc = `${origin}/instrument?modelType=practise&id=${
+              //   data.list[data.listActive].xmlFileUrl
+              // }&Authorization=${userStore.getToken}/#/preview`;
+              // const origin = /(localhost|192)/.test(location.host)
+              //   ? 'https://test.lexiaoya.cn'
+              //   : location.origin;
+              // view-figner?subjectCode=pan-flute
+              const src = `${vaildMusicScoreUrl()}/instrument/#/view-figner?Authorization=${
+                userStore.getToken
+              }&code=${item.code}&platform=pc&type=listenMode`;
+              // const src = `http://192.168.3.220:3000/instrument.html#/view-figner?Authorization=${userStore.getToken}&code=${item.code}`;
+              if (window.matchMedia('(display-mode: standalone)').matches) {
+                state.application = window.matchMedia(
+                  '(display-mode: standalone)'
+                ).matches;
+                forms.previewModal = true;
+                forms.previewParams.src = src + '&matchMedia=1';
+              } else {
+                window.open(src);
+              }
+
+              forms.subjectSyncVisiable = false;
+            }}
+          />
+        </NModal>
+      </div>
+    );
+  }
+});

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

@@ -22,6 +22,7 @@ import dayjs from 'dayjs';
 import TheEmpty from '/src/components/TheEmpty';
 import TrainingDetails from '../../classList/modals/TrainingDetails';
 import { evaluateDifficult } from '/src/utils/contants';
+import { modalClickMask } from '/src/state';
 export default defineComponent({
   name: 'homewrok-record-detail',
   setup() {
@@ -482,6 +483,7 @@ export default defineComponent({
           </div>
         </div>
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.detailVisiable}
           preset="card"
           class={['modalTitle background', styles.wordDetailModel]}

+ 3 - 1
src/views/homework-record/index.tsx

@@ -32,7 +32,7 @@ import ResourceMain from '../prepare-lessons/components/resource-main';
 import { useResizeObserver } from '@vueuse/core';
 import { nextTick } from 'process';
 import PreviewWindow from '../preview-window';
-import { state as baseState } from '@/state';
+import { state as baseState, modalClickMask } from '@/state';
 import { evaluateDifficult } from '/src/utils/contants';
 import TheTooltip from '/src/components/TheTooltip';
 
@@ -645,6 +645,7 @@ export default defineComponent({
           )}
         </div>
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.resetVisiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable]}
@@ -667,6 +668,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.workVisiable}
           preset="card"
           class={['modalTitle background', styles.workVisiable]}

+ 257 - 256
src/views/login/components/codeLogin.tsx

@@ -1,256 +1,257 @@
-import { defineComponent, reactive, ref } from 'vue';
-import styles from '../index.module.less';
-import lockIcon from '../images/pwdIcon.png';
-import useIcon from '../images/phoneIcon.png';
-import {
-  useMessage,
-  NForm,
-  NFormItem,
-  NInput,
-  NButton,
-  NInputGroup,
-  NModal,
-  NSpace
-} from 'naive-ui';
-import { useRoute, useRouter } from 'vue-router';
-import { PageEnum } from '/src/enums/pageEnum';
-import { storage } from '@/utils/storage';
-import { useUserStore } from '/src/store/modules/users';
-import { sendSms } from '../api';
-import SendSms from './sendSms';
-import { formLight } from 'naive-ui/es/form/styles';
-interface FormState {
-  username: string;
-  password: string;
-  grant_type: string;
-  loginType: string;
-  client_id: string;
-  client_secret: string;
-}
-
-export default defineComponent({
-  name: 'codeLogin',
-  props: {
-    phone: {
-      type: String,
-      default: ''
-    }
-  },
-  emits: ['update:phone'],
-  setup(props, { emit }) {
-    const router = useRouter();
-    const route = useRoute();
-    const formRef = ref();
-    const message = useMessage();
-    const loading = ref(false);
-    const autoLogin = ref(true);
-    const showSmsClass = ref(false);
-    const LOGIN_NAME = PageEnum.BASE_LOGIN_NAME;
-    const userStore = useUserStore();
-    const formInline = reactive({
-      username: '',
-      password: '',
-      isCaptcha: true
-    });
-    const isDisabledCode = ref(false);
-    const starTimer = ref(60);
-    const codeName = '发送短信';
-    const formInlineHistory = storage.get('userInfo-teacher');
-    if (formInlineHistory) {
-      formInline.username = JSON.parse(formInlineHistory).username;
-    }
-
-    if (formInline.username) {
-      if (formInline.username !== props.phone && props.phone) {
-        formInline.username = props.phone;
-      } else {
-        emit('update:phone', formInline.username);
-      }
-    } else {
-      if (props.phone) {
-        formInline.username = props.phone;
-      }
-    }
-
-    const handleSubmit = async () => {
-      formRef.value.validate(async (errors: any) => {
-        if (!errors) {
-          const { username, password } = formInline;
-          message.loading('登录中...');
-          loading.value = true;
-          storage.set('userInfo-teacher', JSON.stringify({ username }));
-          const params: FormState = {
-            username,
-            password,
-            loginType: 'SMS',
-            grant_type: 'password',
-            client_id: 'cooleshow-teacher',
-            client_secret: 'cooleshow-teacher'
-          };
-
-          try {
-            await userStore.login(params);
-            // return;
-            message.destroyAll();
-            // if (some.code == ResultEnum.SUCCESS) {
-            //  判断是否勾选自动登录
-            if (autoLogin.value) {
-              storage.set('userInfo', JSON.stringify(formInline));
-            } else {
-              storage.remove('userInfo');
-            }
-
-            // route.query?.redirect ||
-            const toPath = decodeURIComponent('/' as string);
-
-            message.success('登录成功,即将进入系统');
-            if (route.name === LOGIN_NAME) {
-              router.replace('/');
-            } else router.replace(toPath);
-            // } else {
-            //   // message.info(some.msg || "登录失败");
-            // }
-          } catch (e: any) {
-            message.destroyAll();
-            loading.value = false;
-            message.error(e.msg);
-            console.log(e);
-          } finally {
-            loading.value = false;
-          }
-        }
-      });
-    };
-
-    const sendMessage = async () => {
-      // if (!formInline.username) {
-      //   message.error('请输入手机号');
-      //   return;
-      // }
-      showSmsClass.value = true;
-      // try {
-      //   const res = await sendSms({
-      //     clientId: 'cooleshow-teacher',
-      //     mobile: formInline.username,
-      //     type: 'LOGIN'
-      //   });
-      //   checkTimeOut();
-      // } catch (e) {
-      //   console.log(e);
-      // }
-    };
-
-    const checkTimeOut = () => {
-      if (isDisabledCode.value) {
-        return;
-      }
-      isDisabledCode.value = true;
-      const tiemr = setInterval(() => {
-        starTimer.value--;
-        if (starTimer.value <= 0) {
-          isDisabledCode.value = false;
-          clearInterval(tiemr);
-        }
-      }, 1000);
-    };
-    return () => (
-      <div class={styles['view-account-form-wrap']}>
-        {/* <div class={styles.formTitle}>
-      <div class={styles.dot}></div>
-      酷乐秀课堂乐器
-    </div> */}
-        <NForm
-          ref={formRef}
-          label-placement="left"
-          size="large"
-          model={formInline}>
-          <NFormItem
-            path="username"
-            rule={[
-              { required: true, message: '请输入手机号', trigger: 'blur' }
-            ]}>
-            <NInput
-              maxlength={11}
-              v-model:value={formInline.username}
-              placeholder="请输入手机号"
-              onInput={(val: any) => {
-                emit('update:phone', val);
-              }}>
-              {{
-                prefix: () => (
-                  <img src={useIcon} class={styles.prefixIcon} alt="" />
-                )
-              }}
-            </NInput>
-          </NFormItem>
-          <NFormItem
-            path="password"
-            rule={[
-              { required: true, message: '请输入验证码', trigger: 'blur' }
-            ]}>
-            <NInputGroup>
-              <NInput
-                v-model:value={formInline.password}
-                type="text"
-                showPasswordOn="click"
-                placeholder="请输入验证码"
-                inputProps={{ autocomplete: 'off' }}
-                class={styles.sendInput}
-                maxlength={6}
-                onKeydown={(e: KeyboardEvent) => {
-                  if (e.code === 'Enter' || e.code === 'NumpadEnter') {
-                    handleSubmit();
-                  }
-                }}>
-                {{
-                  prefix: () => (
-                    <img src={lockIcon} class={styles.prefixIcon} alt="" />
-                  ),
-                  suffix: () => (
-                    <NButton
-                      class={styles.sendMsg}
-                      disabled={isDisabledCode.value}
-                      onClick={() => sendMessage()}>
-                      {isDisabledCode.value ? starTimer.value + 'S' : codeName}
-                    </NButton>
-                  )
-                }}
-              </NInput>
-            </NInputGroup>
-          </NFormItem>
-
-          <NFormItem class={styles['default-color']}>
-            <div class={[styles['flex'], styles['justify-between']]}>
-              <div class={styles['flex-initial']}>
-                {/* <NCheckbox v-model:checked={autoLogin.value}>
-                  记住密码
-                </NCheckbox> */}
-              </div>
-            </div>
-          </NFormItem>
-          <NFormItem>
-            <NButton
-              class={styles.submitBtm}
-              type="primary"
-              onClick={handleSubmit}
-              size="large"
-              disabled={loading.value}
-              block>
-              立即登录
-            </NButton>
-          </NFormItem>
-        </NForm>
-
-        <NModal v-model:show={showSmsClass.value}>
-          <SendSms
-            phone={formInline.username}
-            onClose={() => (showSmsClass.value = false)}
-            onSendCode={() => {
-              checkTimeOut();
-            }}
-          />
-        </NModal>
-      </div>
-    );
-  }
-});
+import { defineComponent, reactive, ref } from 'vue';
+import styles from '../index.module.less';
+import lockIcon from '../images/pwdIcon.png';
+import useIcon from '../images/phoneIcon.png';
+import {
+  useMessage,
+  NForm,
+  NFormItem,
+  NInput,
+  NButton,
+  NInputGroup,
+  NModal,
+  NSpace
+} from 'naive-ui';
+import { useRoute, useRouter } from 'vue-router';
+import { PageEnum } from '/src/enums/pageEnum';
+import { storage } from '@/utils/storage';
+import { useUserStore } from '/src/store/modules/users';
+import { sendSms } from '../api';
+import SendSms from './sendSms';
+import { formLight } from 'naive-ui/es/form/styles';
+import { modalClickMask } from '/src/state';
+interface FormState {
+  username: string;
+  password: string;
+  grant_type: string;
+  loginType: string;
+  client_id: string;
+  client_secret: string;
+}
+
+export default defineComponent({
+  name: 'codeLogin',
+  props: {
+    phone: {
+      type: String,
+      default: ''
+    }
+  },
+  emits: ['update:phone'],
+  setup(props, { emit }) {
+    const router = useRouter();
+    const route = useRoute();
+    const formRef = ref();
+    const message = useMessage();
+    const loading = ref(false);
+    const autoLogin = ref(true);
+    const showSmsClass = ref(false);
+    const LOGIN_NAME = PageEnum.BASE_LOGIN_NAME;
+    const userStore = useUserStore();
+    const formInline = reactive({
+      username: '',
+      password: '',
+      isCaptcha: true
+    });
+    const isDisabledCode = ref(false);
+    const starTimer = ref(60);
+    const codeName = '发送短信';
+    const formInlineHistory = storage.get('userInfo-teacher');
+    if (formInlineHistory) {
+      formInline.username = JSON.parse(formInlineHistory).username;
+    }
+
+    if (formInline.username) {
+      if (formInline.username !== props.phone && props.phone) {
+        formInline.username = props.phone;
+      } else {
+        emit('update:phone', formInline.username);
+      }
+    } else {
+      if (props.phone) {
+        formInline.username = props.phone;
+      }
+    }
+
+    const handleSubmit = async () => {
+      formRef.value.validate(async (errors: any) => {
+        if (!errors) {
+          const { username, password } = formInline;
+          message.loading('登录中...');
+          loading.value = true;
+          storage.set('userInfo-teacher', JSON.stringify({ username }));
+          const params: FormState = {
+            username,
+            password,
+            loginType: 'SMS',
+            grant_type: 'password',
+            client_id: 'cooleshow-teacher',
+            client_secret: 'cooleshow-teacher'
+          };
+
+          try {
+            await userStore.login(params);
+            // return;
+            message.destroyAll();
+            // if (some.code == ResultEnum.SUCCESS) {
+            //  判断是否勾选自动登录
+            if (autoLogin.value) {
+              storage.set('userInfo', JSON.stringify(formInline));
+            } else {
+              storage.remove('userInfo');
+            }
+
+            // route.query?.redirect ||
+            const toPath = decodeURIComponent('/' as string);
+
+            message.success('登录成功,即将进入系统');
+            if (route.name === LOGIN_NAME) {
+              router.replace('/');
+            } else router.replace(toPath);
+            // } else {
+            //   // message.info(some.msg || "登录失败");
+            // }
+          } catch (e: any) {
+            message.destroyAll();
+            loading.value = false;
+            message.error(e.msg);
+            console.log(e);
+          } finally {
+            loading.value = false;
+          }
+        }
+      });
+    };
+
+    const sendMessage = async () => {
+      // if (!formInline.username) {
+      //   message.error('请输入手机号');
+      //   return;
+      // }
+      showSmsClass.value = true;
+      // try {
+      //   const res = await sendSms({
+      //     clientId: 'cooleshow-teacher',
+      //     mobile: formInline.username,
+      //     type: 'LOGIN'
+      //   });
+      //   checkTimeOut();
+      // } catch (e) {
+      //   console.log(e);
+      // }
+    };
+
+    const checkTimeOut = () => {
+      if (isDisabledCode.value) {
+        return;
+      }
+      isDisabledCode.value = true;
+      const tiemr = setInterval(() => {
+        starTimer.value--;
+        if (starTimer.value <= 0) {
+          isDisabledCode.value = false;
+          clearInterval(tiemr);
+        }
+      }, 1000);
+    };
+    return () => (
+      <div class={styles['view-account-form-wrap']}>
+        {/* <div class={styles.formTitle}>
+      <div class={styles.dot}></div>
+      酷乐秀课堂乐器
+    </div> */}
+        <NForm
+          ref={formRef}
+          label-placement="left"
+          size="large"
+          model={formInline}>
+          <NFormItem
+            path="username"
+            rule={[
+              { required: true, message: '请输入手机号', trigger: 'blur' }
+            ]}>
+            <NInput
+              maxlength={11}
+              v-model:value={formInline.username}
+              placeholder="请输入手机号"
+              onInput={(val: any) => {
+                emit('update:phone', val);
+              }}>
+              {{
+                prefix: () => (
+                  <img src={useIcon} class={styles.prefixIcon} alt="" />
+                )
+              }}
+            </NInput>
+          </NFormItem>
+          <NFormItem
+            path="password"
+            rule={[
+              { required: true, message: '请输入验证码', trigger: 'blur' }
+            ]}>
+            <NInputGroup>
+              <NInput
+                v-model:value={formInline.password}
+                type="text"
+                showPasswordOn="click"
+                placeholder="请输入验证码"
+                inputProps={{ autocomplete: 'off' }}
+                class={styles.sendInput}
+                maxlength={6}
+                onKeydown={(e: KeyboardEvent) => {
+                  if (e.code === 'Enter' || e.code === 'NumpadEnter') {
+                    handleSubmit();
+                  }
+                }}>
+                {{
+                  prefix: () => (
+                    <img src={lockIcon} class={styles.prefixIcon} alt="" />
+                  ),
+                  suffix: () => (
+                    <NButton
+                      class={styles.sendMsg}
+                      disabled={isDisabledCode.value}
+                      onClick={() => sendMessage()}>
+                      {isDisabledCode.value ? starTimer.value + 'S' : codeName}
+                    </NButton>
+                  )
+                }}
+              </NInput>
+            </NInputGroup>
+          </NFormItem>
+
+          <NFormItem class={styles['default-color']}>
+            <div class={[styles['flex'], styles['justify-between']]}>
+              <div class={styles['flex-initial']}>
+                {/* <NCheckbox v-model:checked={autoLogin.value}>
+                  记住密码
+                </NCheckbox> */}
+              </div>
+            </div>
+          </NFormItem>
+          <NFormItem>
+            <NButton
+              class={styles.submitBtm}
+              type="primary"
+              onClick={handleSubmit}
+              size="large"
+              disabled={loading.value}
+              block>
+              立即登录
+            </NButton>
+          </NFormItem>
+        </NForm>
+
+        <NModal maskClosable={modalClickMask} v-model:show={showSmsClass.value}>
+          <SendSms
+            phone={formInline.username}
+            onClose={() => (showSmsClass.value = false)}
+            onSendCode={() => {
+              checkTimeOut();
+            }}
+          />
+        </NModal>
+      </div>
+    );
+  }
+});

+ 2 - 1
src/views/login/index.tsx

@@ -27,7 +27,7 @@ import moveTop from './images/moveTopBg.png';
 import dingPng from './images/ding.png';
 import closeAble from './images/closeAble.png';
 import infoIcon from './images/infoIcon.png';
-import { state } from '/src/state';
+import { modalClickMask, state } from '/src/state';
 import TheAuth from '/src/components/TheAuth';
 import { mutualTLSQuery } from './api';
 export default defineComponent({
@@ -223,6 +223,7 @@ export default defineComponent({
           </div>
         </div>
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={showModalMask.value}
           onMaskClick={() => {
             checkAuthShow();

+ 2 - 0
src/views/natural-resources/components/my-collect/index.tsx

@@ -15,6 +15,7 @@ import {
 import TheEmpty from '@/components/TheEmpty';
 import CardPreview from '@/components/card-preview';
 import MyCollogeGuide from '@/custom-plugins/guide-page/myColloge-guide';
+import { modalClickMask } from '/src/state';
 export default defineComponent({
   name: 'share-resources',
   setup() {
@@ -174,6 +175,7 @@ export default defineComponent({
         {showGuide.value ? <MyCollogeGuide></MyCollogeGuide> : null}
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.removeVisiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable]}

+ 4 - 0
src/views/natural-resources/components/my-resources/index.tsx

@@ -28,6 +28,7 @@ import MyResourcesGuide from '@/custom-plugins/guide-page/myResources-guide';
 import SaveModal from './save-modal';
 import deepClone from '/src/helpers/deep-clone';
 import UploadCover from './upload-cover';
+import { modalClickMask } from '/src/state';
 export default defineComponent({
   name: 'share-resources',
   setup() {
@@ -356,6 +357,7 @@ export default defineComponent({
         <CardPreview v-model:show={state.show} item={state.item} />
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.uploadStatus}
           preset="card"
           showIcon={false}
@@ -402,6 +404,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.saveStatus}
           preset="card"
           showIcon={false}
@@ -436,6 +439,7 @@ export default defineComponent({
         {showGuide.value ? <MyResourcesGuide></MyResourcesGuide> : null}
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.removeVisiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable]}

+ 458 - 456
src/views/natural-resources/components/my-resources/upload-modal/index.tsx

@@ -1,456 +1,458 @@
-import {
-  computed,
-  defineComponent,
-  nextTick,
-  onMounted,
-  reactive,
-  ref
-} from 'vue';
-import styles from './index.module.less';
-import {
-  NButton,
-  NCascader,
-  NForm,
-  NFormItem,
-  NImage,
-  NInput,
-  NModal,
-  NScrollbar,
-  NSelect,
-  NSpace,
-  NSwitch,
-  useMessage
-} from 'naive-ui';
-import UploadFile from './upload-file';
-import { useCatchStore } from '/src/store/modules/catchData';
-import iconImage from '@common/images/icon-image.png';
-import iconVideo from '@common/images/icon-video.png';
-import iconAudio from '@common/images/icon-audio.png';
-import iconMusic from '@common/images/icon-music.png';
-import iconPPT from '@common/images/icon-ppt.png';
-import iconUploadDelete from '../../../images/btn-remove2.png';
-import { materialSave, materialUpdateAll } from '../../../api';
-import { NaturalTypeEnum } from '@/enums/pageEnum';
-import { scrollToErrorForm } from '/src/utils';
-import UploadCover from '../upload-cover';
-
-// 判断链接后辍
-export const formatUrlType = (url: string) => {
-  if (!url) return '';
-  if (url?.indexOf('.mp3') > -1) {
-    return NaturalTypeEnum.SONG;
-  } else if (url?.indexOf('.mp4') > -1) {
-    return NaturalTypeEnum.VIDEO;
-  } else if (url?.indexOf('.ppt') > -1) {
-    return NaturalTypeEnum.PPT;
-  } else {
-    return NaturalTypeEnum.IMG;
-  }
-};
-
-export default defineComponent({
-  name: 'upload-modal',
-  props: {
-    list: {
-      type: Array,
-      default: () => []
-    },
-    showDelete: {
-      type: Boolean,
-      default: false
-    },
-    editStatus: {
-      type: Boolean,
-      default: true
-    }
-  },
-  emits: ['close', 'confirm', 'editAll'],
-  setup(props, { emit }) {
-    const catchStore = useCatchStore();
-    const formRef = ref();
-    const message = useMessage();
-
-    // const uploadRef = ref();
-    // const uploadList = ref([] as any);
-    const uploadForms = reactive({
-      list: [] as any[],
-      uploading: false,
-      uploadUrl: '',
-      name: '',
-      instrumentIds: [] as any
-    });
-    const changeCover = reactive({
-      uploadCoverStatus: false,
-      uploadType: '',
-      uploadImg: null as any,
-      uploadIndex: 0
-    });
-
-    // 判断类型
-    const formatType = (type: string) => {
-      let typeImg = iconImage;
-      switch (type) {
-        case 'IMG':
-          typeImg = iconImage;
-          break;
-        case 'VIDEO':
-          typeImg = iconVideo;
-          break;
-        case 'SONG':
-          typeImg = iconAudio;
-          break;
-        case 'MUSIC':
-          typeImg = iconMusic;
-          break;
-        case 'PPT':
-          typeImg = iconPPT;
-          break;
-      }
-      return typeImg;
-    };
-
-    const onSubmit = async () => {
-      //
-      formRef.value?.validate(async (err: any) => {
-        if (err) {
-          nextTick(scrollToErrorForm);
-          return;
-        }
-        uploadForms.uploading = true;
-        try {
-          const body: any[] = [];
-          uploadForms.list.forEach(item => {
-            body.push({
-              instrumentIds: item.instrumentIds.join(','),
-              openFlag: item.openFlag,
-              coverImg: item.coverImg,
-              name: item.name,
-              type: item.type,
-              enableFlag: 1,
-              content: item.content,
-              id: item.id || null
-            });
-          });
-          if (isUpdate.value) {
-            if (!props.editStatus) {
-              const { data } = await materialUpdateAll(body);
-              message.success('保存成功');
-              uploadForms.list = [];
-              emit('close', true);
-              emit('confirm', data);
-            } else {
-              emit('editAll', body);
-            }
-
-            uploadForms.list = [];
-          } else {
-            await materialSave(body);
-            message.success('保存成功');
-            uploadForms.list = [];
-            emit('close', true);
-            emit('confirm');
-          }
-        } catch {
-          //
-        }
-        uploadForms.uploading = false;
-      });
-    };
-
-    const handleRemove = (index: number) => {
-      uploadForms.list.splice(index, 1);
-    };
-
-    const isUpdate = computed(() => (props.list.length > 0 ? true : false));
-
-    onMounted(async () => {
-      const list = props.list || [];
-      const temps: any[] = [];
-      list.forEach((item: any) => {
-        temps.push({
-          instrumentIds: item.instrumentIds
-            ? item.instrumentIds.split(',').map((instrument: any) => instrument)
-            : [],
-          openFlag: item.openFlag,
-          coverImg: item.coverImg,
-          name: item.title,
-          type: item.type,
-          sourceFrom: item.sourceFrom,
-          enableFlag: item.enableFlag,
-          content: item.content,
-
-          id: item.id
-        });
-      });
-      uploadForms.list = temps || [];
-
-      // console.log(temps, 'uploadForms');
-      await catchStore.getSubjects();
-    });
-
-    // 全选
-    const chioseAll = (item: any, list: any) => {
-      // item.instrumentIds = list.map((item: any) => {
-      //   return item.id;
-      // }) as any;
-      const ids = [] as any;
-      list.map((item: any) => {
-        if (Array.isArray(item.instruments)) {
-          item.instruments.forEach((c: any) => {
-            ids.push(c.value);
-          });
-        }
-      }) as any;
-      item.instrumentIds = ids;
-    };
-    return () => (
-      <div class={styles.uploadModal}>
-        <NScrollbar style={{ 'max-height': '55vh' }}>
-          <NForm
-            ref={formRef}
-            labelPlacement="left"
-            labelWidth={120}
-            model={uploadForms}
-            class={styles.formModal}>
-            <NSpace class={styles.formSpace}>
-              {uploadForms.list.map((item: any, index: number) => (
-                <div class={styles.formItem} key={index}>
-                  <div class={styles.previewModal}>
-                    <NImage
-                      class={[styles.titleType]}
-                      src={formatType(item.type)}
-                      previewDisabled
-                      objectFit="cover"
-                    />
-                    {/* 编辑时不能删除 */}
-                    {(!isUpdate.value || props.showDelete) && (
-                      <img
-                        class={[styles.iconUploadDelete]}
-                        src={iconUploadDelete}
-                        onClick={() => handleRemove(index)}
-                      />
-                    )}
-
-                    <NImage
-                      class={[styles.cover, styles.image]}
-                      lazy
-                      previewDisabled
-                      src={item.coverImg}
-                      objectFit="cover"
-                    />
-
-                    <div class={styles.commonType}>
-                      {item.type !== 'IMG' ? (
-                        <NButton
-                          class={styles.changeCover}
-                          type="default"
-                          bordered={false}
-                          onClick={() => {
-                            changeCover.uploadIndex = index;
-                            changeCover.uploadImg = item.coverImg;
-                            changeCover.uploadType = item.type;
-                            changeCover.uploadCoverStatus = true;
-                          }}>
-                          更换封面
-                        </NButton>
-                      ) : (
-                        <span></span>
-                      )}
-
-                      <div>
-                        是否公开
-                        <NSwitch
-                          size="small"
-                          v-model:value={item.openFlag}
-                          disabled={
-                            item.sourceFrom === 'TEACHER' &&
-                            item.type === 'MUSIC'
-                          }
-                        />
-                      </div>
-                    </div>
-                  </div>
-                  <NFormItem
-                    showFeedback={false}
-                    path={`list.${index}.name`}
-                    rule={[
-                      {
-                        required: true,
-                        message: '请输入资源名称',
-                        trigger: ['input', 'blur']
-                      }
-                    ]}>
-                    <NInput
-                      v-model:value={item.name}
-                      placeholder="请输入资源名称"
-                      maxlength={25}
-                      clearable
-                    />
-                  </NFormItem>
-                  <NFormItem
-                    path={`list[${index}].instrumentIds`}
-                    showFeedback={false}
-                    rule={[
-                      {
-                        required: true,
-                        message: '请选择素材可用乐器',
-                        trigger: 'change',
-                        type: 'array'
-                      }
-                    ]}>
-                    <NCascader
-                      v-model:value={item.instrumentIds}
-                      placeholder="请选择素材可用乐器(可多选)"
-                      options={catchStore.getEnableSubjects}
-                      checkStrategy="child"
-                      showPath={false}
-                      childrenField="instruments"
-                      expandTrigger="hover"
-                      labelField="name"
-                      valueField="id"
-                      clearable
-                      filterable
-                      multiple
-                      maxTagCount="responsive"
-                      v-slots={{
-                        action: () => (
-                          <>
-                            <NButton
-                              text
-                              style=" --n-width: 100% "
-                              onClick={() =>
-                                chioseAll(item, catchStore.getEnableSubjects)
-                              }>
-                              全选
-                            </NButton>
-                          </>
-                        )
-                      }}
-                    />
-                  </NFormItem>
-                </div>
-              ))}
-
-              {/* {!isUpdate.value && (
-                <div class={styles.formItem}>
-                  <UploadFile
-                    v-model:fileList={uploadForms.uploadUrl}
-                    accept=".jpg,jpeg,.png,audio/mp3,video/mp4"
-                    showFileList={false}
-                    ref={uploadRef}
-                    // cropper
-                    multiple
-                    max={10}
-                    // options={{
-                    //   autoCropWidth: 320,
-                    //   autoCropHeight: 180,
-                    //   fixedBox: true
-                    // }}
-                    onFinished={(val: any) => {
-                      console.log(val, 'val');
-                      uploadList.value.push({
-                        instrumentIds: uploadForms.instrumentIds || [],
-                        openFlag: true,
-                        coverImg: val.coverImg,
-                        name: uploadForms.name || '',
-                        type: formatUrlType(val.content),
-                        enableFlag: 1,
-                        content: val.content
-                      });
-                      // uploadForms.list.push({
-                      //   instrumentIds: uploadForms.instrumentIds || [],
-                      //   openFlag: true,
-                      //   coverImg: val.coverImg,
-                      //   name: uploadForms.name || '',
-                      //   type: formatUrlType(val.content),
-                      //   enableFlag: 1,
-                      //   content: val.content
-                      // });
-                      const timer = setTimeout(() => {
-                        uploadForms.list.push(...uploadList.value);
-                        uploadList.value = [];
-                        uploadForms.uploadUrl = '';
-                        uploadForms.name = '';
-                        uploadForms.instrumentIds = [];
-                        uploadRef.value.handleClearFile();
-                      }, 1000);
-                    }}
-                  />
-                  <NFormItem showFeedback={false}>
-                    <NInput
-                      v-model:value={uploadForms.name}
-                      placeholder="请输入资源名称"
-                      maxlength={25}
-                      clearable
-                    />
-                  </NFormItem>
-                  <NFormItem showFeedback={false}>
-                    <NSelect
-                      v-model:value={uploadForms.instrumentIds}
-                      placeholder="请选择素材可用声部(可多选)"
-                      options={catchStore.getSubjectList}
-                      labelField="name"
-                      valueField="id"
-                      multiple
-                      maxTagCount={2}
-                      clearable
-                      v-slots={{
-                        action: () => (
-                          <>
-                            <NButton
-                              text
-                              style=" --n-width: 100% "
-                              onClick={() =>
-                                chioseAll(
-                                  uploadForms,
-                                  catchStore.getSubjectList
-                                )
-                              }>
-                              全选
-                            </NButton>
-                          </>
-                        )
-                      }}
-                    />
-                  </NFormItem>
-                </div>
-              )} */}
-            </NSpace>
-          </NForm>
-        </NScrollbar>
-
-        <NSpace class={styles.btnGroup} justify="center">
-          <NButton round onClick={() => emit('close')}>
-            {props.editStatus ? '取消' : '上一步'}
-          </NButton>
-          <NButton
-            round
-            type="primary"
-            loading={uploadForms.uploading}
-            disabled={uploadForms.list.length === 0}
-            onClick={onSubmit}>
-            确定
-          </NButton>
-        </NSpace>
-
-        <NModal
-          v-model:show={changeCover.uploadCoverStatus}
-          preset="card"
-          showIcon={false}
-          class={['modalTitle ', styles.uploadCover]}
-          blockScroll={false}>
-          <UploadCover
-            img={changeCover.uploadImg}
-            onClose={() => (changeCover.uploadCoverStatus = false)}
-            onConfirm={(val: any) => {
-              if (changeCover.uploadType === 'IMG') {
-                uploadForms.list[changeCover.uploadIndex].content = val;
-              }
-              uploadForms.list[changeCover.uploadIndex].coverImg = val;
-            }}
-          />
-        </NModal>
-      </div>
-    );
-  }
-});
+import {
+  computed,
+  defineComponent,
+  nextTick,
+  onMounted,
+  reactive,
+  ref
+} from 'vue';
+import styles from './index.module.less';
+import {
+  NButton,
+  NCascader,
+  NForm,
+  NFormItem,
+  NImage,
+  NInput,
+  NModal,
+  NScrollbar,
+  NSelect,
+  NSpace,
+  NSwitch,
+  useMessage
+} from 'naive-ui';
+import UploadFile from './upload-file';
+import { useCatchStore } from '/src/store/modules/catchData';
+import iconImage from '@common/images/icon-image.png';
+import iconVideo from '@common/images/icon-video.png';
+import iconAudio from '@common/images/icon-audio.png';
+import iconMusic from '@common/images/icon-music.png';
+import iconPPT from '@common/images/icon-ppt.png';
+import iconUploadDelete from '../../../images/btn-remove2.png';
+import { materialSave, materialUpdateAll } from '../../../api';
+import { NaturalTypeEnum } from '@/enums/pageEnum';
+import { scrollToErrorForm } from '/src/utils';
+import UploadCover from '../upload-cover';
+import { modalClickMask } from '/src/state';
+
+// 判断链接后辍
+export const formatUrlType = (url: string) => {
+  if (!url) return '';
+  if (url?.indexOf('.mp3') > -1) {
+    return NaturalTypeEnum.SONG;
+  } else if (url?.indexOf('.mp4') > -1) {
+    return NaturalTypeEnum.VIDEO;
+  } else if (url?.indexOf('.ppt') > -1) {
+    return NaturalTypeEnum.PPT;
+  } else {
+    return NaturalTypeEnum.IMG;
+  }
+};
+
+export default defineComponent({
+  name: 'upload-modal',
+  props: {
+    list: {
+      type: Array,
+      default: () => []
+    },
+    showDelete: {
+      type: Boolean,
+      default: false
+    },
+    editStatus: {
+      type: Boolean,
+      default: true
+    }
+  },
+  emits: ['close', 'confirm', 'editAll'],
+  setup(props, { emit }) {
+    const catchStore = useCatchStore();
+    const formRef = ref();
+    const message = useMessage();
+
+    // const uploadRef = ref();
+    // const uploadList = ref([] as any);
+    const uploadForms = reactive({
+      list: [] as any[],
+      uploading: false,
+      uploadUrl: '',
+      name: '',
+      instrumentIds: [] as any
+    });
+    const changeCover = reactive({
+      uploadCoverStatus: false,
+      uploadType: '',
+      uploadImg: null as any,
+      uploadIndex: 0
+    });
+
+    // 判断类型
+    const formatType = (type: string) => {
+      let typeImg = iconImage;
+      switch (type) {
+        case 'IMG':
+          typeImg = iconImage;
+          break;
+        case 'VIDEO':
+          typeImg = iconVideo;
+          break;
+        case 'SONG':
+          typeImg = iconAudio;
+          break;
+        case 'MUSIC':
+          typeImg = iconMusic;
+          break;
+        case 'PPT':
+          typeImg = iconPPT;
+          break;
+      }
+      return typeImg;
+    };
+
+    const onSubmit = async () => {
+      //
+      formRef.value?.validate(async (err: any) => {
+        if (err) {
+          nextTick(scrollToErrorForm);
+          return;
+        }
+        uploadForms.uploading = true;
+        try {
+          const body: any[] = [];
+          uploadForms.list.forEach(item => {
+            body.push({
+              instrumentIds: item.instrumentIds.join(','),
+              openFlag: item.openFlag,
+              coverImg: item.coverImg,
+              name: item.name,
+              type: item.type,
+              enableFlag: 1,
+              content: item.content,
+              id: item.id || null
+            });
+          });
+          if (isUpdate.value) {
+            if (!props.editStatus) {
+              const { data } = await materialUpdateAll(body);
+              message.success('保存成功');
+              uploadForms.list = [];
+              emit('close', true);
+              emit('confirm', data);
+            } else {
+              emit('editAll', body);
+            }
+
+            uploadForms.list = [];
+          } else {
+            await materialSave(body);
+            message.success('保存成功');
+            uploadForms.list = [];
+            emit('close', true);
+            emit('confirm');
+          }
+        } catch {
+          //
+        }
+        uploadForms.uploading = false;
+      });
+    };
+
+    const handleRemove = (index: number) => {
+      uploadForms.list.splice(index, 1);
+    };
+
+    const isUpdate = computed(() => (props.list.length > 0 ? true : false));
+
+    onMounted(async () => {
+      const list = props.list || [];
+      const temps: any[] = [];
+      list.forEach((item: any) => {
+        temps.push({
+          instrumentIds: item.instrumentIds
+            ? item.instrumentIds.split(',').map((instrument: any) => instrument)
+            : [],
+          openFlag: item.openFlag,
+          coverImg: item.coverImg,
+          name: item.title,
+          type: item.type,
+          sourceFrom: item.sourceFrom,
+          enableFlag: item.enableFlag,
+          content: item.content,
+
+          id: item.id
+        });
+      });
+      uploadForms.list = temps || [];
+
+      // console.log(temps, 'uploadForms');
+      await catchStore.getSubjects();
+    });
+
+    // 全选
+    const chioseAll = (item: any, list: any) => {
+      // item.instrumentIds = list.map((item: any) => {
+      //   return item.id;
+      // }) as any;
+      const ids = [] as any;
+      list.map((item: any) => {
+        if (Array.isArray(item.instruments)) {
+          item.instruments.forEach((c: any) => {
+            ids.push(c.value);
+          });
+        }
+      }) as any;
+      item.instrumentIds = ids;
+    };
+    return () => (
+      <div class={styles.uploadModal}>
+        <NScrollbar style={{ 'max-height': '55vh' }}>
+          <NForm
+            ref={formRef}
+            labelPlacement="left"
+            labelWidth={120}
+            model={uploadForms}
+            class={styles.formModal}>
+            <NSpace class={styles.formSpace}>
+              {uploadForms.list.map((item: any, index: number) => (
+                <div class={styles.formItem} key={index}>
+                  <div class={styles.previewModal}>
+                    <NImage
+                      class={[styles.titleType]}
+                      src={formatType(item.type)}
+                      previewDisabled
+                      objectFit="cover"
+                    />
+                    {/* 编辑时不能删除 */}
+                    {(!isUpdate.value || props.showDelete) && (
+                      <img
+                        class={[styles.iconUploadDelete]}
+                        src={iconUploadDelete}
+                        onClick={() => handleRemove(index)}
+                      />
+                    )}
+
+                    <NImage
+                      class={[styles.cover, styles.image]}
+                      lazy
+                      previewDisabled
+                      src={item.coverImg}
+                      objectFit="cover"
+                    />
+
+                    <div class={styles.commonType}>
+                      {item.type !== 'IMG' ? (
+                        <NButton
+                          class={styles.changeCover}
+                          type="default"
+                          bordered={false}
+                          onClick={() => {
+                            changeCover.uploadIndex = index;
+                            changeCover.uploadImg = item.coverImg;
+                            changeCover.uploadType = item.type;
+                            changeCover.uploadCoverStatus = true;
+                          }}>
+                          更换封面
+                        </NButton>
+                      ) : (
+                        <span></span>
+                      )}
+
+                      <div>
+                        是否公开
+                        <NSwitch
+                          size="small"
+                          v-model:value={item.openFlag}
+                          disabled={
+                            item.sourceFrom === 'TEACHER' &&
+                            item.type === 'MUSIC'
+                          }
+                        />
+                      </div>
+                    </div>
+                  </div>
+                  <NFormItem
+                    showFeedback={false}
+                    path={`list.${index}.name`}
+                    rule={[
+                      {
+                        required: true,
+                        message: '请输入资源名称',
+                        trigger: ['input', 'blur']
+                      }
+                    ]}>
+                    <NInput
+                      v-model:value={item.name}
+                      placeholder="请输入资源名称"
+                      maxlength={25}
+                      clearable
+                    />
+                  </NFormItem>
+                  <NFormItem
+                    path={`list[${index}].instrumentIds`}
+                    showFeedback={false}
+                    rule={[
+                      {
+                        required: true,
+                        message: '请选择素材可用乐器',
+                        trigger: 'change',
+                        type: 'array'
+                      }
+                    ]}>
+                    <NCascader
+                      v-model:value={item.instrumentIds}
+                      placeholder="请选择素材可用乐器(可多选)"
+                      options={catchStore.getEnableSubjects}
+                      checkStrategy="child"
+                      showPath={false}
+                      childrenField="instruments"
+                      expandTrigger="hover"
+                      labelField="name"
+                      valueField="id"
+                      clearable
+                      filterable
+                      multiple
+                      maxTagCount="responsive"
+                      v-slots={{
+                        action: () => (
+                          <>
+                            <NButton
+                              text
+                              style=" --n-width: 100% "
+                              onClick={() =>
+                                chioseAll(item, catchStore.getEnableSubjects)
+                              }>
+                              全选
+                            </NButton>
+                          </>
+                        )
+                      }}
+                    />
+                  </NFormItem>
+                </div>
+              ))}
+
+              {/* {!isUpdate.value && (
+                <div class={styles.formItem}>
+                  <UploadFile
+                    v-model:fileList={uploadForms.uploadUrl}
+                    accept=".jpg,jpeg,.png,audio/mp3,video/mp4"
+                    showFileList={false}
+                    ref={uploadRef}
+                    // cropper
+                    multiple
+                    max={10}
+                    // options={{
+                    //   autoCropWidth: 320,
+                    //   autoCropHeight: 180,
+                    //   fixedBox: true
+                    // }}
+                    onFinished={(val: any) => {
+                      console.log(val, 'val');
+                      uploadList.value.push({
+                        instrumentIds: uploadForms.instrumentIds || [],
+                        openFlag: true,
+                        coverImg: val.coverImg,
+                        name: uploadForms.name || '',
+                        type: formatUrlType(val.content),
+                        enableFlag: 1,
+                        content: val.content
+                      });
+                      // uploadForms.list.push({
+                      //   instrumentIds: uploadForms.instrumentIds || [],
+                      //   openFlag: true,
+                      //   coverImg: val.coverImg,
+                      //   name: uploadForms.name || '',
+                      //   type: formatUrlType(val.content),
+                      //   enableFlag: 1,
+                      //   content: val.content
+                      // });
+                      const timer = setTimeout(() => {
+                        uploadForms.list.push(...uploadList.value);
+                        uploadList.value = [];
+                        uploadForms.uploadUrl = '';
+                        uploadForms.name = '';
+                        uploadForms.instrumentIds = [];
+                        uploadRef.value.handleClearFile();
+                      }, 1000);
+                    }}
+                  />
+                  <NFormItem showFeedback={false}>
+                    <NInput
+                      v-model:value={uploadForms.name}
+                      placeholder="请输入资源名称"
+                      maxlength={25}
+                      clearable
+                    />
+                  </NFormItem>
+                  <NFormItem showFeedback={false}>
+                    <NSelect
+                      v-model:value={uploadForms.instrumentIds}
+                      placeholder="请选择素材可用声部(可多选)"
+                      options={catchStore.getSubjectList}
+                      labelField="name"
+                      valueField="id"
+                      multiple
+                      maxTagCount={2}
+                      clearable
+                      v-slots={{
+                        action: () => (
+                          <>
+                            <NButton
+                              text
+                              style=" --n-width: 100% "
+                              onClick={() =>
+                                chioseAll(
+                                  uploadForms,
+                                  catchStore.getSubjectList
+                                )
+                              }>
+                              全选
+                            </NButton>
+                          </>
+                        )
+                      }}
+                    />
+                  </NFormItem>
+                </div>
+              )} */}
+            </NSpace>
+          </NForm>
+        </NScrollbar>
+
+        <NSpace class={styles.btnGroup} justify="center">
+          <NButton round onClick={() => emit('close')}>
+            {props.editStatus ? '取消' : '上一步'}
+          </NButton>
+          <NButton
+            round
+            type="primary"
+            loading={uploadForms.uploading}
+            disabled={uploadForms.list.length === 0}
+            onClick={onSubmit}>
+            确定
+          </NButton>
+        </NSpace>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={changeCover.uploadCoverStatus}
+          preset="card"
+          showIcon={false}
+          class={['modalTitle ', styles.uploadCover]}
+          blockScroll={false}>
+          <UploadCover
+            img={changeCover.uploadImg}
+            onClose={() => (changeCover.uploadCoverStatus = false)}
+            onConfirm={(val: any) => {
+              if (changeCover.uploadType === 'IMG') {
+                uploadForms.list[changeCover.uploadIndex].content = val;
+              }
+              uploadForms.list[changeCover.uploadIndex].coverImg = val;
+            }}
+          />
+        </NModal>
+      </div>
+    );
+  }
+});

+ 456 - 454
src/views/natural-resources/components/my-resources/upload-modal/upload-file.tsx

@@ -1,454 +1,456 @@
-import {
-  NModal,
-  NSpin,
-  NUpload,
-  NUploadDragger,
-  UploadFileInfo,
-  useMessage
-} from 'naive-ui';
-import { defineComponent, watch, PropType, reactive, ref } from 'vue';
-import { policy } from '@/components/upload-file/api';
-import Copper from '@/components/upload-file/copper';
-import axios from 'axios';
-import styles from './index.module.less';
-import iconUploadAdd from '../../../images/icon-upload-add.png';
-import { NaturalTypeEnum, PageEnum } from '@/enums/pageEnum';
-import { formatUrlType } from '.';
-
-/**
- * 1. 图片上传可以进行裁剪
- * 2. 视频上传可以选择某一帧做为封面
- * 3. 音频只用限制某一种格式
- * 4. 只支持单个上传,因为多个上传没有办法去处理,即有视频,图片等
- */
-export default defineComponent({
-  name: 'upload-file',
-  props: {
-    fileList: {
-      type: String,
-      default: ''
-    },
-    imageList: {
-      type: Array,
-      default: () => []
-    },
-    accept: {
-      // 支持类型
-      type: String,
-      default: '.jpg,.png,.jpeg,.gif'
-    },
-    showType: {
-      type: String as PropType<'default' | 'custom'>,
-      default: 'default'
-    },
-    showFileList: {
-      type: Boolean,
-      default: true
-    },
-    max: {
-      type: Number as PropType<number>,
-      default: 1
-    },
-    multiple: {
-      type: Boolean as PropType<boolean>,
-      default: false
-    },
-    disabled: {
-      type: Boolean as PropType<boolean>,
-      default: false
-    },
-    bucketName: {
-      type: String,
-      default: 'gyt'
-    },
-    directoryDnd: {
-      type: Boolean as PropType<boolean>,
-      default: false
-    },
-    path: {
-      type: String,
-      default: ''
-    },
-    fileName: {
-      type: String,
-      default: ''
-    },
-    cropper: {
-      // 是否裁切, 只有图片才支持 - 失效(不支持)
-      type: Boolean as PropType<boolean>,
-      default: false
-    },
-    options: {
-      type: Object,
-      default: () => {
-        return {
-          viewMode: 0,
-          autoCrop: true, //是否默认生成截图框
-          enlarge: 1, //  图片放大倍数
-          autoCropWidth: 200, //默认生成截图框宽度
-          autoCropHeight: 200, //默认生成截图框高度
-          fixedBox: false, //是否固定截图框大小 不允许改变
-          previewsCircle: true, //预览图是否是原图形
-          title: '上传图片'
-        };
-      }
-    }
-  },
-  emits: [
-    'update:fileList',
-    'close',
-    'readFileInputEventAsArrayBuffer',
-    'remove',
-    'finished'
-  ],
-  setup(props, { emit, expose, slots }) {
-    const ossUploadUrl = `https://${props.bucketName}.ks3-cn-beijing.ksyuncs.com/`;
-    const message = useMessage();
-    const visiable = ref<boolean>(false);
-    const btnLoading = ref<boolean>(false);
-    const tempFiileBuffer = ref();
-    const uploadRef = ref();
-    const state = reactive([
-      // {
-      //   policy: '',
-      //   signature: '',
-      //   key: '',
-      //   KSSAccessKeyId: '',
-      //   acl: 'public-read',
-      //   name: ''
-      // }
-    ]) as any;
-
-    const fileListRef = ref<UploadFileInfo[]>([]);
-    const initFileList = () => {
-      if (props.fileList) {
-        const splitName = props.fileList.split('/');
-        fileListRef.value = [
-          {
-            id: new Date().getTime().toString(),
-            name: splitName[splitName.length - 1],
-            status: 'finished',
-            url: props.fileList
-          }
-        ];
-      } else {
-        fileListRef.value = [];
-      }
-    };
-    initFileList();
-    watch(
-      () => props.imageList,
-      () => {
-        initFileList();
-      }
-    );
-    watch(
-      () => props.fileList,
-      () => {
-        initFileList();
-      }
-    );
-    const handleClearFile = () => {
-      uploadRef.value?.clear();
-    };
-    expose({
-      handleClearFile
-    });
-
-    const CropperModal = ref();
-    const onBeforeUpload = async (options: any) => {
-      const file = options.file;
-      // 文件大小
-      let isLt2M = true;
-
-      const type = file.type.includes('image')
-        ? NaturalTypeEnum.IMG
-        : file.type.includes('audio')
-        ? NaturalTypeEnum.SONG
-        : NaturalTypeEnum.VIDEO;
-
-      const size = type === 'IMG' ? 2 : type === 'SONG' ? 20 : 500;
-      if (size) {
-        isLt2M = file.file.size / 1024 / 1024 < size;
-        if (!isLt2M) {
-          message.error(`文件大小不能超过${size}M`);
-          return false;
-        }
-      }
-
-      if (!isLt2M) {
-        return isLt2M;
-      }
-      // 是否裁切
-      // if (props.cropper && type === 'IMG') {
-      //   getBase64(file.file, (imageUrl: any) => {
-      //     const target = Object.assign({}, props.options, {
-      //       img: imageUrl,
-      //       name: file.file.name // 上传文件名
-      //     });
-      //     visiable.value = true;
-
-      //     setTimeout(() => {
-      //       CropperModal.value?.edit(target);
-      //     }, 100);
-      //   });
-      //   return false;
-      // }
-
-      try {
-        btnLoading.value = true;
-        const name = file.file.name;
-        const suffix = name.slice(name.lastIndexOf('.'));
-        const fileName = `${props.path}${
-          props.fileName || Date.now() + suffix
-        }`;
-        const obj = {
-          filename: fileName,
-          bucketName: props.bucketName,
-          postData: {
-            filename: fileName,
-            acl: 'public-read',
-            key: fileName,
-            unknowValueField: []
-          }
-        };
-        const { data } = await policy(obj);
-
-        state.push({
-          id: file.id,
-          tempFiileBuffer: file.file,
-          policy: data.policy,
-          signature: data.signature,
-          acl: 'public-read',
-          key: fileName,
-          KSSAccessKeyId: data.kssAccessKeyId,
-          name: fileName
-        });
-
-        // tempFiileBuffer.value = file.file;
-      } catch {
-        //
-        // message.error('上传失败')
-        btnLoading.value = false;
-        return false;
-      }
-      return true;
-    };
-    const getBase64 = async (img: any, callback: any) => {
-      const reader = new FileReader();
-      reader.addEventListener('load', () => callback(reader.result));
-      reader.readAsDataURL(img);
-    };
-
-    const onFinish = (options: any) => {
-      console.log(options, 'onFinish');
-      onFinishAfter(options);
-    };
-    const onFinishAfter = async (options: any) => {
-      const item = state.find((c: any) => c.id == options.file.id);
-      const url = ossUploadUrl + item.key;
-      const type = formatUrlType(url);
-      let coverImg = '';
-      if (type === 'IMG') {
-        coverImg = url;
-      } else if (type === 'SONG') {
-        coverImg = PageEnum.SONG_DEFAULT_COVER;
-      } else if (type === 'VIDEO') {
-        // 获取视频封面图
-        coverImg = await getVideoCoverImg(item.tempFiileBuffer);
-      }
-      emit('update:fileList', url);
-      emit('readFileInputEventAsArrayBuffer', item.tempFiileBuffer);
-      console.log(url, 'url onFinishAfter');
-      emit('finished', {
-        coverImg,
-        content: url
-      });
-
-      options.file.url = url;
-      visiable.value = false;
-      btnLoading.value = false;
-    };
-    const getVideoMsg = (file: any) => {
-      return new Promise(resolve => {
-        // let dataURL = '';
-        const videoElement = document.createElement('video');
-        videoElement.currentTime = 1;
-        videoElement.src = URL.createObjectURL(file);
-        videoElement.addEventListener('loadeddata', function () {
-          const canvas: any = document.createElement('canvas'),
-            width = videoElement.videoWidth, //canvas的尺寸和图片一样
-            height = videoElement.videoHeight;
-          canvas.width = width;
-          canvas.height = height;
-          canvas.getContext('2d').drawImage(videoElement, 0, 0, width, height); //绘制canvas
-          // dataURL = canvas.toDataURL('image/jpeg'); //转换为base64
-          console.log(canvas);
-          canvas.toBlob((blob: any) => {
-            // console.log(blob);
-            resolve(blob);
-          });
-        });
-      });
-    };
-
-    const getVideoCoverImg = async (file: any) => {
-      try {
-        btnLoading.value = true;
-        const imgBlob: any = await getVideoMsg(file || tempFiileBuffer.value);
-        const fileName = `${props.path}${Date.now() + '.png'}`;
-        const obj = {
-          filename: fileName,
-          bucketName: props.bucketName,
-          postData: {
-            filename: fileName,
-            acl: 'public-read',
-            key: fileName,
-            unknowValueField: []
-          }
-        };
-        const { data } = await policy(obj);
-
-        const fileParams = {
-          policy: data.policy,
-          signature: data.signature,
-          key: fileName,
-          acl: 'public-read',
-          KSSAccessKeyId: data.kssAccessKeyId,
-          name: fileName
-        } as any;
-        const formData = new FormData();
-        for (const key in fileParams) {
-          formData.append(key, fileParams[key]);
-        }
-
-        formData.append('file', imgBlob);
-        await axios.post(ossUploadUrl, formData);
-
-        const url = ossUploadUrl + fileName;
-        return url;
-      } finally {
-        btnLoading.value = false;
-      }
-    };
-
-    const onRemove = async () => {
-      emit('update:fileList', '');
-      emit('remove');
-      btnLoading.value = false;
-    };
-
-    // 裁切失败
-    // const cropperNo = () => {}
-    // 裁切成功
-    const cropperOk = async (blob: any) => {
-      try {
-        const fileName = `${props.path}${
-          props.fileName || new Date().getTime() + '.png'
-        }`;
-        const obj = {
-          filename: fileName,
-          bucketName: props.bucketName,
-          postData: {
-            filename: fileName,
-            acl: 'public-read',
-            key: fileName,
-            unknowValueField: []
-          }
-        };
-        const { data } = await policy(obj);
-
-        state.policy = data.policy;
-        state.signature = data.signature;
-        state.key = fileName;
-        state.KSSAccessKeyId = data.kssAccessKeyId;
-        state.name = fileName;
-
-        const formData = new FormData();
-        for (const key in state) {
-          formData.append(key, state[key]);
-        }
-        formData.append('file', blob);
-
-        await axios.post(ossUploadUrl, formData).then(() => {
-          const url = ossUploadUrl + state.key;
-          const splitName = url.split('/');
-          fileListRef.value = [
-            {
-              id: new Date().getTime().toString(),
-              name: splitName[splitName.length - 1],
-              status: 'finished',
-              url: url
-            }
-          ];
-          emit('update:fileList', url);
-          emit('finished', {
-            coverImg: url,
-            content: url
-          });
-          visiable.value = false;
-        });
-      } catch {
-        return false;
-      }
-    };
-
-    return () => (
-      <div class={styles.uploadFile}>
-        <NSpin show={btnLoading.value} description="上传中...">
-          <NUpload
-            ref={uploadRef}
-            action={ossUploadUrl}
-            data={(file: any) => {
-              const item = state.find((c: any) => {
-                return c.id == file.file.id;
-              });
-              const { id, tempFiileBuffer, ...more } = item;
-              return { ...more };
-            }}
-            v-model:fileList={fileListRef.value}
-            accept={props.accept}
-            multiple={props.multiple}
-            max={props.max}
-            disabled={props.disabled}
-            directoryDnd={props.directoryDnd}
-            showFileList={props.showFileList}
-            showPreviewButton
-            onBeforeUpload={(options: any) => onBeforeUpload(options)}
-            onFinish={(options: any) => {
-              onFinish(options);
-            }}
-            onChange={(options: any) => {
-              // console.log(options, 'change');
-            }}
-            onRemove={() => onRemove()}>
-            <NUploadDragger>
-              {props.showType === 'default' && (
-                <div class={styles.uploadBtn}>
-                  <img src={iconUploadAdd} class={styles.iconUploadAdd} />
-                  <p>上传</p>
-                </div>
-              )}
-              {props.showType === 'custom' && slots.custom && slots.custom()}
-            </NUploadDragger>
-          </NUpload>
-        </NSpin>
-
-        <NModal
-          v-model:show={visiable.value}
-          preset="dialog"
-          showIcon={false}
-          class={['modalTitle background']}
-          title="上传图片"
-          style={{ width: '800px' }}>
-          {/* @cropper-no="error" @cropper-ok="success" */}
-          <Copper
-            ref={CropperModal}
-            onClose={() => (visiable.value = false)}
-            onCropperOk={cropperOk}
-          />
-        </NModal>
-      </div>
-    );
-  }
-});
+import {
+  NModal,
+  NSpin,
+  NUpload,
+  NUploadDragger,
+  UploadFileInfo,
+  useMessage
+} from 'naive-ui';
+import { defineComponent, watch, PropType, reactive, ref } from 'vue';
+import { policy } from '@/components/upload-file/api';
+import Copper from '@/components/upload-file/copper';
+import axios from 'axios';
+import styles from './index.module.less';
+import iconUploadAdd from '../../../images/icon-upload-add.png';
+import { NaturalTypeEnum, PageEnum } from '@/enums/pageEnum';
+import { formatUrlType } from '.';
+import { modalClickMask } from '/src/state';
+
+/**
+ * 1. 图片上传可以进行裁剪
+ * 2. 视频上传可以选择某一帧做为封面
+ * 3. 音频只用限制某一种格式
+ * 4. 只支持单个上传,因为多个上传没有办法去处理,即有视频,图片等
+ */
+export default defineComponent({
+  name: 'upload-file',
+  props: {
+    fileList: {
+      type: String,
+      default: ''
+    },
+    imageList: {
+      type: Array,
+      default: () => []
+    },
+    accept: {
+      // 支持类型
+      type: String,
+      default: '.jpg,.png,.jpeg,.gif'
+    },
+    showType: {
+      type: String as PropType<'default' | 'custom'>,
+      default: 'default'
+    },
+    showFileList: {
+      type: Boolean,
+      default: true
+    },
+    max: {
+      type: Number as PropType<number>,
+      default: 1
+    },
+    multiple: {
+      type: Boolean as PropType<boolean>,
+      default: false
+    },
+    disabled: {
+      type: Boolean as PropType<boolean>,
+      default: false
+    },
+    bucketName: {
+      type: String,
+      default: 'gyt'
+    },
+    directoryDnd: {
+      type: Boolean as PropType<boolean>,
+      default: false
+    },
+    path: {
+      type: String,
+      default: ''
+    },
+    fileName: {
+      type: String,
+      default: ''
+    },
+    cropper: {
+      // 是否裁切, 只有图片才支持 - 失效(不支持)
+      type: Boolean as PropType<boolean>,
+      default: false
+    },
+    options: {
+      type: Object,
+      default: () => {
+        return {
+          viewMode: 0,
+          autoCrop: true, //是否默认生成截图框
+          enlarge: 1, //  图片放大倍数
+          autoCropWidth: 200, //默认生成截图框宽度
+          autoCropHeight: 200, //默认生成截图框高度
+          fixedBox: false, //是否固定截图框大小 不允许改变
+          previewsCircle: true, //预览图是否是原图形
+          title: '上传图片'
+        };
+      }
+    }
+  },
+  emits: [
+    'update:fileList',
+    'close',
+    'readFileInputEventAsArrayBuffer',
+    'remove',
+    'finished'
+  ],
+  setup(props, { emit, expose, slots }) {
+    const ossUploadUrl = `https://${props.bucketName}.ks3-cn-beijing.ksyuncs.com/`;
+    const message = useMessage();
+    const visiable = ref<boolean>(false);
+    const btnLoading = ref<boolean>(false);
+    const tempFiileBuffer = ref();
+    const uploadRef = ref();
+    const state = reactive([
+      // {
+      //   policy: '',
+      //   signature: '',
+      //   key: '',
+      //   KSSAccessKeyId: '',
+      //   acl: 'public-read',
+      //   name: ''
+      // }
+    ]) as any;
+
+    const fileListRef = ref<UploadFileInfo[]>([]);
+    const initFileList = () => {
+      if (props.fileList) {
+        const splitName = props.fileList.split('/');
+        fileListRef.value = [
+          {
+            id: new Date().getTime().toString(),
+            name: splitName[splitName.length - 1],
+            status: 'finished',
+            url: props.fileList
+          }
+        ];
+      } else {
+        fileListRef.value = [];
+      }
+    };
+    initFileList();
+    watch(
+      () => props.imageList,
+      () => {
+        initFileList();
+      }
+    );
+    watch(
+      () => props.fileList,
+      () => {
+        initFileList();
+      }
+    );
+    const handleClearFile = () => {
+      uploadRef.value?.clear();
+    };
+    expose({
+      handleClearFile
+    });
+
+    const CropperModal = ref();
+    const onBeforeUpload = async (options: any) => {
+      const file = options.file;
+      // 文件大小
+      let isLt2M = true;
+
+      const type = file.type.includes('image')
+        ? NaturalTypeEnum.IMG
+        : file.type.includes('audio')
+        ? NaturalTypeEnum.SONG
+        : NaturalTypeEnum.VIDEO;
+
+      const size = type === 'IMG' ? 2 : type === 'SONG' ? 20 : 500;
+      if (size) {
+        isLt2M = file.file.size / 1024 / 1024 < size;
+        if (!isLt2M) {
+          message.error(`文件大小不能超过${size}M`);
+          return false;
+        }
+      }
+
+      if (!isLt2M) {
+        return isLt2M;
+      }
+      // 是否裁切
+      // if (props.cropper && type === 'IMG') {
+      //   getBase64(file.file, (imageUrl: any) => {
+      //     const target = Object.assign({}, props.options, {
+      //       img: imageUrl,
+      //       name: file.file.name // 上传文件名
+      //     });
+      //     visiable.value = true;
+
+      //     setTimeout(() => {
+      //       CropperModal.value?.edit(target);
+      //     }, 100);
+      //   });
+      //   return false;
+      // }
+
+      try {
+        btnLoading.value = true;
+        const name = file.file.name;
+        const suffix = name.slice(name.lastIndexOf('.'));
+        const fileName = `${props.path}${
+          props.fileName || Date.now() + suffix
+        }`;
+        const obj = {
+          filename: fileName,
+          bucketName: props.bucketName,
+          postData: {
+            filename: fileName,
+            acl: 'public-read',
+            key: fileName,
+            unknowValueField: []
+          }
+        };
+        const { data } = await policy(obj);
+
+        state.push({
+          id: file.id,
+          tempFiileBuffer: file.file,
+          policy: data.policy,
+          signature: data.signature,
+          acl: 'public-read',
+          key: fileName,
+          KSSAccessKeyId: data.kssAccessKeyId,
+          name: fileName
+        });
+
+        // tempFiileBuffer.value = file.file;
+      } catch {
+        //
+        // message.error('上传失败')
+        btnLoading.value = false;
+        return false;
+      }
+      return true;
+    };
+    const getBase64 = async (img: any, callback: any) => {
+      const reader = new FileReader();
+      reader.addEventListener('load', () => callback(reader.result));
+      reader.readAsDataURL(img);
+    };
+
+    const onFinish = (options: any) => {
+      console.log(options, 'onFinish');
+      onFinishAfter(options);
+    };
+    const onFinishAfter = async (options: any) => {
+      const item = state.find((c: any) => c.id == options.file.id);
+      const url = ossUploadUrl + item.key;
+      const type = formatUrlType(url);
+      let coverImg = '';
+      if (type === 'IMG') {
+        coverImg = url;
+      } else if (type === 'SONG') {
+        coverImg = PageEnum.SONG_DEFAULT_COVER;
+      } else if (type === 'VIDEO') {
+        // 获取视频封面图
+        coverImg = await getVideoCoverImg(item.tempFiileBuffer);
+      }
+      emit('update:fileList', url);
+      emit('readFileInputEventAsArrayBuffer', item.tempFiileBuffer);
+      console.log(url, 'url onFinishAfter');
+      emit('finished', {
+        coverImg,
+        content: url
+      });
+
+      options.file.url = url;
+      visiable.value = false;
+      btnLoading.value = false;
+    };
+    const getVideoMsg = (file: any) => {
+      return new Promise(resolve => {
+        // let dataURL = '';
+        const videoElement = document.createElement('video');
+        videoElement.currentTime = 1;
+        videoElement.src = URL.createObjectURL(file);
+        videoElement.addEventListener('loadeddata', function () {
+          const canvas: any = document.createElement('canvas'),
+            width = videoElement.videoWidth, //canvas的尺寸和图片一样
+            height = videoElement.videoHeight;
+          canvas.width = width;
+          canvas.height = height;
+          canvas.getContext('2d').drawImage(videoElement, 0, 0, width, height); //绘制canvas
+          // dataURL = canvas.toDataURL('image/jpeg'); //转换为base64
+          console.log(canvas);
+          canvas.toBlob((blob: any) => {
+            // console.log(blob);
+            resolve(blob);
+          });
+        });
+      });
+    };
+
+    const getVideoCoverImg = async (file: any) => {
+      try {
+        btnLoading.value = true;
+        const imgBlob: any = await getVideoMsg(file || tempFiileBuffer.value);
+        const fileName = `${props.path}${Date.now() + '.png'}`;
+        const obj = {
+          filename: fileName,
+          bucketName: props.bucketName,
+          postData: {
+            filename: fileName,
+            acl: 'public-read',
+            key: fileName,
+            unknowValueField: []
+          }
+        };
+        const { data } = await policy(obj);
+
+        const fileParams = {
+          policy: data.policy,
+          signature: data.signature,
+          key: fileName,
+          acl: 'public-read',
+          KSSAccessKeyId: data.kssAccessKeyId,
+          name: fileName
+        } as any;
+        const formData = new FormData();
+        for (const key in fileParams) {
+          formData.append(key, fileParams[key]);
+        }
+
+        formData.append('file', imgBlob);
+        await axios.post(ossUploadUrl, formData);
+
+        const url = ossUploadUrl + fileName;
+        return url;
+      } finally {
+        btnLoading.value = false;
+      }
+    };
+
+    const onRemove = async () => {
+      emit('update:fileList', '');
+      emit('remove');
+      btnLoading.value = false;
+    };
+
+    // 裁切失败
+    // const cropperNo = () => {}
+    // 裁切成功
+    const cropperOk = async (blob: any) => {
+      try {
+        const fileName = `${props.path}${
+          props.fileName || new Date().getTime() + '.png'
+        }`;
+        const obj = {
+          filename: fileName,
+          bucketName: props.bucketName,
+          postData: {
+            filename: fileName,
+            acl: 'public-read',
+            key: fileName,
+            unknowValueField: []
+          }
+        };
+        const { data } = await policy(obj);
+
+        state.policy = data.policy;
+        state.signature = data.signature;
+        state.key = fileName;
+        state.KSSAccessKeyId = data.kssAccessKeyId;
+        state.name = fileName;
+
+        const formData = new FormData();
+        for (const key in state) {
+          formData.append(key, state[key]);
+        }
+        formData.append('file', blob);
+
+        await axios.post(ossUploadUrl, formData).then(() => {
+          const url = ossUploadUrl + state.key;
+          const splitName = url.split('/');
+          fileListRef.value = [
+            {
+              id: new Date().getTime().toString(),
+              name: splitName[splitName.length - 1],
+              status: 'finished',
+              url: url
+            }
+          ];
+          emit('update:fileList', url);
+          emit('finished', {
+            coverImg: url,
+            content: url
+          });
+          visiable.value = false;
+        });
+      } catch {
+        return false;
+      }
+    };
+
+    return () => (
+      <div class={styles.uploadFile}>
+        <NSpin show={btnLoading.value} description="上传中...">
+          <NUpload
+            ref={uploadRef}
+            action={ossUploadUrl}
+            data={(file: any) => {
+              const item = state.find((c: any) => {
+                return c.id == file.file.id;
+              });
+              const { id, tempFiileBuffer, ...more } = item;
+              return { ...more };
+            }}
+            v-model:fileList={fileListRef.value}
+            accept={props.accept}
+            multiple={props.multiple}
+            max={props.max}
+            disabled={props.disabled}
+            directoryDnd={props.directoryDnd}
+            showFileList={props.showFileList}
+            showPreviewButton
+            onBeforeUpload={(options: any) => onBeforeUpload(options)}
+            onFinish={(options: any) => {
+              onFinish(options);
+            }}
+            onChange={(options: any) => {
+              // console.log(options, 'change');
+            }}
+            onRemove={() => onRemove()}>
+            <NUploadDragger>
+              {props.showType === 'default' && (
+                <div class={styles.uploadBtn}>
+                  <img src={iconUploadAdd} class={styles.iconUploadAdd} />
+                  <p>上传</p>
+                </div>
+              )}
+              {props.showType === 'custom' && slots.custom && slots.custom()}
+            </NUploadDragger>
+          </NUpload>
+        </NSpin>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={visiable.value}
+          preset="dialog"
+          showIcon={false}
+          class={['modalTitle background']}
+          title="上传图片"
+          style={{ width: '800px' }}>
+          {/* @cropper-no="error" @cropper-ok="success" */}
+          <Copper
+            ref={CropperModal}
+            onClose={() => (visiable.value = false)}
+            onCropperOk={cropperOk}
+          />
+        </NModal>
+      </div>
+    );
+  }
+});

+ 2 - 0
src/views/natural-resources/components/share-resources/index.tsx

@@ -9,6 +9,7 @@ import TheEmpty from '/src/components/TheEmpty';
 import CardPreview from '/src/components/card-preview';
 import AddTeaching from '../../model/add-teaching';
 import ShareResourcesGuide from '@/custom-plugins/guide-page/shareResources-guide';
+import { modalClickMask } from '/src/state';
 
 export default defineComponent({
   name: 'share-resources',
@@ -163,6 +164,7 @@ export default defineComponent({
 
         {/* 添加自定义教材 */}
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.teachingStatus}
           preset="card"
           showIcon={false}

+ 132 - 130
src/views/notation/index.tsx

@@ -1,130 +1,132 @@
-import { defineComponent, onMounted, onUnmounted, reactive, ref } from 'vue';
-import { useUserStore } from '/src/store/modules/users';
-import styles from './index.module.less';
-import { state } from '/src/state';
-import { NButton, NModal, NSpace, NSpin } from 'naive-ui';
-import { exitFullscreen, iframeDislableKeyboard } from '/src/utils';
-import { useRouter } from 'vue-router';
-export default defineComponent({
-  name: 'notation-a',
-  setup() {
-    const router = useRouter();
-    const show = ref(false);
-    const previewModal = ref(false);
-    const previewParams = ref({} as any);
-    const removeVisiable = ref(false);
-    const userStore = useUserStore();
-    const Authorization = userStore.getToken || '';
-    const iframeRef = ref();
-    // console.log(Authorization);
-    const notationOpenCreate = sessionStorage.getItem('notation-open-create');
-    const openCreateUrl = notationOpenCreate == '1' ? '&addShow=1' : '';
-    sessionStorage.removeItem('notation-open-create');
-    const data = reactive({
-      src: `${
-        /(192|localhost)/.test(location.origin)
-          ? // ?
-            'https://test.lexiaoya.cn'
-          : // 'http://localhost:3050'
-            location.origin
-      }/notation/?t=${Date.now()}#/create?v=${Date.now()}&Authorization=${Authorization}${openCreateUrl}`
-      //   src: `http://localhost:3050/#/create?Authorization=${Authorization}`
-    });
-    const fscreen = () => {
-      const el = document.documentElement as any;
-      //进入全屏
-      (el.requestFullscreen && el.requestFullscreen()) ||
-        (el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
-        (el.webkitRequestFullscreen && el.webkitRequestFullscreen()) ||
-        (el.msRequestFullscreen && el.msRequestFullscreen());
-    };
-    const handleOpen = (e: MessageEvent) => {
-      console.log(e.data, 'data');
-      if (e.data.api === 'notation_open') {
-        if (state.application) {
-          show.value = true;
-          previewModal.value = true;
-          previewParams.value = {
-            url: e.data.url
-          };
-          fscreen();
-        } else {
-          window.open(e.data.url);
-        }
-      } else if (e.data.api === 'notation_exit') {
-        console.log('进来');
-
-        removeVisiable.value = true;
-      }
-    };
-    onMounted(() => {
-      window.addEventListener('message', handleOpen);
-    });
-    onUnmounted(() => {
-      window.removeEventListener('message', handleOpen);
-    });
-    return () => (
-      <div class={styles.wrap}>
-        <iframe ref={iframeRef} src={data.src} key={Date.now()}></iframe>
-
-        <NModal
-          v-model:show={previewModal.value}
-          class={styles.previewWindow}
-          showIcon={false}
-          displayDirective="show">
-          <NSpin show={show.value} style="--n-opacity-spinning: 1;">
-            <iframe
-              class={styles.previewIframe}
-              onLoad={() => {
-                show.value = false;
-              }}
-              frameborder="0"
-              src={previewParams.value.url}></iframe>
-          </NSpin>
-        </NModal>
-
-        <NModal
-          v-model:show={removeVisiable.value}
-          preset="card"
-          class={['modalTitle', styles.removeVisiable]}
-          title={'退出制谱'}>
-          <div class={styles.studentRemove}>
-            <p>是否退出制谱功能?</p>
-
-            <NSpace class={styles.btnGroupModal} justify="center">
-              <NButton
-                round
-                type="primary"
-                onClick={() => {
-                  previewModal.value = false;
-                  previewParams.value.url = '';
-                  removeVisiable.value = false;
-                  // 给制谱发消息
-                  iframeRef.value.contentWindow.postMessage(
-                    {
-                      api: 'reload'
-                    },
-                    '*'
-                  );
-                  if (state.application) {
-                    document.exitFullscreen
-                      ? document.exitFullscreen()
-                      : document.mozCancelFullScreen
-                      ? document.mozCancelFullScreen()
-                      : document.webkitExitFullscreen
-                      ? document.webkitExitFullscreen()
-                      : '';
-                  }
-                }}>
-                确定
-              </NButton>
-              <NButton round onClick={() => (removeVisiable.value = false)}>
-                取消
-              </NButton>
-            </NSpace>
-          </div>
-        </NModal>
-      </div>
-    );
-  }
-});
+import { defineComponent, onMounted, onUnmounted, reactive, ref } from 'vue';
+import { useUserStore } from '/src/store/modules/users';
+import styles from './index.module.less';
+import { modalClickMask, state } from '/src/state';
+import { NButton, NModal, NSpace, NSpin } from 'naive-ui';
+import { exitFullscreen, iframeDislableKeyboard } from '/src/utils';
+import { useRouter } from 'vue-router';
+export default defineComponent({
+  name: 'notation-a',
+  setup() {
+    const router = useRouter();
+    const show = ref(false);
+    const previewModal = ref(false);
+    const previewParams = ref({} as any);
+    const removeVisiable = ref(false);
+    const userStore = useUserStore();
+    const Authorization = userStore.getToken || '';
+    const iframeRef = ref();
+    // console.log(Authorization);
+    const notationOpenCreate = sessionStorage.getItem('notation-open-create');
+    const openCreateUrl = notationOpenCreate == '1' ? '&addShow=1' : '';
+    sessionStorage.removeItem('notation-open-create');
+    const data = reactive({
+      src: `${
+        /(192|localhost)/.test(location.origin)
+          ? // ?
+            'https://test.lexiaoya.cn'
+          : // 'http://localhost:3050'
+            location.origin
+      }/notation/?t=${Date.now()}#/create?v=${Date.now()}&Authorization=${Authorization}${openCreateUrl}`
+      //   src: `http://localhost:3050/#/create?Authorization=${Authorization}`
+    });
+    const fscreen = () => {
+      const el = document.documentElement as any;
+      //进入全屏
+      (el.requestFullscreen && el.requestFullscreen()) ||
+        (el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
+        (el.webkitRequestFullscreen && el.webkitRequestFullscreen()) ||
+        (el.msRequestFullscreen && el.msRequestFullscreen());
+    };
+    const handleOpen = (e: MessageEvent) => {
+      console.log(e.data, 'data');
+      if (e.data.api === 'notation_open') {
+        if (state.application) {
+          show.value = true;
+          previewModal.value = true;
+          previewParams.value = {
+            url: e.data.url
+          };
+          fscreen();
+        } else {
+          window.open(e.data.url);
+        }
+      } else if (e.data.api === 'notation_exit') {
+        console.log('进来');
+
+        removeVisiable.value = true;
+      }
+    };
+    onMounted(() => {
+      window.addEventListener('message', handleOpen);
+    });
+    onUnmounted(() => {
+      window.removeEventListener('message', handleOpen);
+    });
+    return () => (
+      <div class={styles.wrap}>
+        <iframe ref={iframeRef} src={data.src} key={Date.now()}></iframe>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={previewModal.value}
+          class={styles.previewWindow}
+          showIcon={false}
+          displayDirective="show">
+          <NSpin show={show.value} style="--n-opacity-spinning: 1;">
+            <iframe
+              class={styles.previewIframe}
+              onLoad={() => {
+                show.value = false;
+              }}
+              frameborder="0"
+              src={previewParams.value.url}></iframe>
+          </NSpin>
+        </NModal>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={removeVisiable.value}
+          preset="card"
+          class={['modalTitle', styles.removeVisiable]}
+          title={'退出制谱'}>
+          <div class={styles.studentRemove}>
+            <p>是否退出制谱功能?</p>
+
+            <NSpace class={styles.btnGroupModal} justify="center">
+              <NButton
+                round
+                type="primary"
+                onClick={() => {
+                  previewModal.value = false;
+                  previewParams.value.url = '';
+                  removeVisiable.value = false;
+                  // 给制谱发消息
+                  iframeRef.value.contentWindow.postMessage(
+                    {
+                      api: 'reload'
+                    },
+                    '*'
+                  );
+                  if (state.application) {
+                    document.exitFullscreen
+                      ? document.exitFullscreen()
+                      : document.mozCancelFullScreen
+                      ? document.mozCancelFullScreen()
+                      : document.webkitExitFullscreen
+                      ? document.webkitExitFullscreen()
+                      : '';
+                  }
+                }}>
+                确定
+              </NButton>
+              <NButton round onClick={() => (removeVisiable.value = false)}>
+                取消
+              </NButton>
+            </NSpace>
+          </div>
+        </NModal>
+      </div>
+    );
+  }
+});

+ 2 - 0
src/views/prepare-lessons/components/directory-main/index.tsx

@@ -15,6 +15,7 @@ import { eventGlobal } from '/src/utils';
 import TheNoticeBar from '/src/components/TheNoticeBar';
 import { getSubjectList2 } from '/src/api/user';
 import LessonsGuide from '@/custom-plugins/guide-page/lessons-guide';
+import { modalClickMask } from '/src/state';
 
 export default defineComponent({
   name: 'directory-main',
@@ -402,6 +403,7 @@ export default defineComponent({
 
           {/* 选择教材 */}
           <NModal
+            maskClosable={modalClickMask}
             v-model:show={forms.coursewareStatus}
             preset="card"
             showIcon={false}

+ 3 - 0
src/views/prepare-lessons/components/directory-main/select-lessonware/index.tsx

@@ -26,6 +26,7 @@ import { useCatchStore } from '/src/store/modules/catchData';
 import { useThrottleFn } from '@vueuse/core';
 import iconEdit from '../images/icon-edit.png';
 import iconDelete from '../images/icon-delete.png';
+import { modalClickMask } from '/src/state';
 
 export default defineComponent({
   name: 'select-lessonware',
@@ -269,6 +270,7 @@ export default defineComponent({
 
         {/* 添加自定义教材 */}
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={forms.teachingStatus}
           preset="card"
           showIcon={false}
@@ -286,6 +288,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={forms.removeVisiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable]}

+ 6 - 1
src/views/prepare-lessons/components/lesson-main/courseware-presets/index.tsx

@@ -26,7 +26,7 @@ import add from '@/views/studentList/images/add.png';
 import CoursewareType from '../../../model/courseware-type';
 import TheEmpty from '/src/components/TheEmpty';
 import RelatedClass from '../../../model/related-class';
-import { state } from '/src/state';
+import { modalClickMask, state } from '/src/state';
 import { useResizeObserver } from '@vueuse/core';
 import AttendClass from '/src/views/prepare-lessons/model/attend-class';
 import {
@@ -640,6 +640,7 @@ export default defineComponent({
         {/* )} */}
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={forms.showRelatedClass}
           preset="card"
           showIcon={false}
@@ -693,6 +694,7 @@ export default defineComponent({
         </NModal> */}
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={forms.preRemoveVisiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable1]}
@@ -708,6 +710,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={forms.addVisiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable1]}
@@ -737,6 +740,7 @@ export default defineComponent({
         />
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={forms.showAttendClass}
           preset="card"
           showIcon={false}
@@ -776,6 +780,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={forms.workVisiable}
           preset="card"
           class={['modalTitle background', styles.workVisiable]}

+ 7 - 1
src/views/prepare-lessons/components/lesson-main/courseware/addCourseware.tsx

@@ -56,6 +56,7 @@ import AddOtherSource from '../../../model/add-other-source';
 import deepClone from '/src/helpers/deep-clone';
 import AddCoursewareProtocol from '../../../model/add-courseware-protocol';
 import { useUserStore } from '/src/store/modules/users';
+import { modalClickMask } from '/src/state';
 export default defineComponent({
   name: 'courseware-modal',
   props: {
@@ -954,6 +955,7 @@ export default defineComponent({
         />
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={forms.addCoursewareVisiable}
           preset="card"
           class={['modalTitle', styles.addCourseware]}
@@ -979,6 +981,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={forms.messageOperation.visiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable1]}
@@ -1024,6 +1027,7 @@ export default defineComponent({
 
         {/* 添加其它类型的资源 */}
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={forms.addOtherSource}
           preset="card"
           class={['modalTitle background', styles.addOtherSource]}
@@ -1051,7 +1055,9 @@ export default defineComponent({
           />
         </NModal>
 
-        <NModal v-model:show={showModalMask.value}>
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={showModalMask.value}>
           <AddCoursewareProtocol
             onClose={() => (showModalMask.value = false)}
             onConfirm={async () => {

+ 962 - 959
src/views/prepare-lessons/components/lesson-main/courseware/index.tsx

@@ -1,959 +1,962 @@
-import { defineComponent, nextTick, onMounted, reactive, watch } from 'vue';
-import styles from './index.module.less';
-import {
-  NButton,
-  NModal,
-  NScrollbar,
-  NSelect,
-  NSpace,
-  NSpin,
-  useMessage,
-  useDialog
-} from 'naive-ui';
-import CardType from '/src/components/card-type';
-import AttendClass from '/src/views/prepare-lessons/model/attend-class';
-import { usePrepareStore } from '/src/store/modules/prepareLessons';
-import { useCatchStore } from '/src/store/modules/catchData';
-import TheEmpty from '/src/components/TheEmpty';
-import {
-  courseScheduleStart,
-  queryCourseware,
-  saveCourseware,
-  teacherKnowledgeMaterialDelete
-} from '../../../api';
-import Draggable from 'vuedraggable';
-import iconDelete from '../../../images/icon-delete.png';
-import iconAddMusic from '../../../images/icon-add-music.png';
-import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
-import deepClone from '/src/helpers/deep-clone';
-import CardPreview from '/src/components/card-preview';
-import PreviewWindow from '/src/views/preview-window';
-import { state } from '/src/state';
-import SubjectSync from '../../../model/subject-sync';
-import { eventGlobal } from '/src/utils';
-import iconTips from '../../../images/icon-tips.png';
-import { useElementBounding } from '@vueuse/core';
-export default defineComponent({
-  name: 'courseware-modal',
-  setup() {
-    const catchStore = useCatchStore();
-    const prepareStore = usePrepareStore();
-    const route = useRoute();
-    const router = useRouter();
-    const dialog = useDialog();
-    const message = useMessage();
-
-    const localStorageSubjectId = localStorage.getItem(
-      'prepareLessonSubjectId'
-    );
-    const forms = reactive({
-      tipsStatus: localStorage.getItem('prepare-lesson-courseware-tip')
-        ? true
-        : false,
-      className: route.query.name as any,
-      classGroupId: route.query.classGroupId,
-      preStudentNum: route.query.preStudentNum,
-      courseScheduleSubjectId: route.query.courseScheduleSubjectId,
-      // 选取参数带的,后取缓存
-      subjectId: route.query.subjectId
-        ? Number(route.query.subjectId)
-        : localStorageSubjectId
-        ? Number(localStorageSubjectId)
-        : null,
-      coursewareList: [] as any,
-      loadingStatus: false,
-      showAttendClass: false,
-      attendClassType: 'change', //
-      removeIds: [] as any, // 临时删除的编号
-      drag: false,
-      isEdit: false, // 是否更新数据
-      editSubjectIds: '', // 声部编号
-      removeVisiable: false,
-      removeVisiable1: false,
-      subjectSyncVisiable: false, // 同步声部
-      show: false,
-      item: {} as any,
-      previewModal: false,
-      previewParams: {
-        type: '',
-        subjectId: '',
-        detailId: ''
-      } as any
-    });
-
-    // 获取列表
-    const getList = async () => {
-      forms.loadingStatus = true;
-      try {
-        // 判断是否有选择对应的课件 或声部
-        if (!prepareStore.getSelectKey || !prepareStore.getSubjectId)
-          return (forms.loadingStatus = false);
-        const { data } = await queryCourseware({
-          coursewareDetailKnowledgeId: prepareStore.getSelectKey,
-          subjectId: prepareStore.getSubjectId,
-          page: 1,
-          rows: 99
-        });
-        const tempRows = data.rows || [];
-        const temp: any = [];
-        tempRows.forEach((row: any) => {
-          temp.push({
-            id: row.id,
-            materialId: row.materialId,
-            coverImg: row.coverImg,
-            type: row.materialType,
-            title: row.materialName,
-            isCollect: !!row.favoriteFlag,
-            isSelected: row.source === 'PLATFORM' ? true : false,
-            content: row.content,
-            removeFlag: row.removeFlag
-          });
-        });
-
-        prepareStore.setCoursewareList(temp || []);
-
-        const tempCourse: any = [];
-        temp.forEach((item: any) => {
-          if (!forms.removeIds.includes(item.id)) {
-            tempCourse.push(item);
-          }
-        });
-        forms.coursewareList = tempCourse;
-      } catch {
-        //
-      }
-      forms.loadingStatus = false;
-    };
-
-    // 监听选择的key 左侧选择了其它的课
-    watch(
-      () => prepareStore.getSelectKey,
-      () => {
-        forms.drag = false;
-        prepareStore.setIsEditResource(false);
-        getList();
-      }
-    );
-    // 声部变化时
-    watch(
-      () => prepareStore.getSubjectId,
-      () => {
-        getList();
-      }
-    );
-    watch(
-      () => prepareStore.getIsAddResource,
-      (val: boolean) => {
-        if (val) {
-          getList();
-          prepareStore.setIsAddResource(false);
-        }
-      }
-    );
-    // 监听列表变化,如果变化了,则弹选择声部的
-    watch(
-      () => forms.coursewareList,
-      () => {
-        if (forms.drag) {
-          forms.isEdit = true;
-        }
-      },
-      {
-        deep: true
-      }
-    );
-
-    // 删除
-    const onDelete = (item: any) => {
-      //
-      forms.removeIds.push(item.id);
-      const index = forms.coursewareList.findIndex(
-        (c: any) => c.id === item.id
-      );
-      forms.coursewareList.splice(index, 1);
-      forms.isEdit = true;
-      // prepareStore.setCoursewareList(forms.coursewareList);
-      // console.log(prepareStore.getCoursewareList, 'getCourseware');
-    };
-
-    // 完成编辑
-    const onOverEdit = async () => {
-      try {
-        const temp: any = [];
-        forms.coursewareList.forEach((item: any) => {
-          temp.push({
-            materialName: item.title,
-            materialType: item.type,
-            materialId: item.materialId,
-            id: item.id
-          });
-        });
-        // 保存课件
-        // 判断是否编辑,如果编辑则取选择的声部
-        await saveCourseware({
-          coursewareDetailKnowledgeId: prepareStore.getSelectKey,
-          lessonCoursewareId: prepareStore.getLessonCoursewareId,
-          lessonCoursewareDetailId: prepareStore.getLessonCoursewareDetailId,
-          subjectId: forms.isEdit
-            ? forms.editSubjectIds
-            : prepareStore.getSubjectId,
-          materialList: [...temp]
-        });
-
-        forms.drag = false;
-        message.success('编辑成功');
-        forms.removeVisiable = false;
-        prepareStore.setIsEditResource(false);
-        // 重置临时删除编号
-        forms.removeIds = [];
-        await getList();
-      } catch {
-        //
-      }
-    };
-
-    // 预览上课
-    const onPreviewAttend = () => {
-      // 获取上架的数据
-      let count = 0;
-      forms.coursewareList.forEach((item: any) => {
-        if (!item.removeFlag) {
-          count++;
-        }
-      });
-      if (count <= 0) {
-        message.error('课件不能为空');
-        return;
-      }
-      // 判断是否在应用里面
-      if (window.matchMedia('(display-mode: standalone)').matches) {
-        state.application = window.matchMedia(
-          '(display-mode: standalone)'
-        ).matches;
-        forms.previewModal = true;
-        fscreen();
-        forms.previewParams = {
-          type: 'preview',
-          instrumentId: prepareStore.getInstrumentId,
-          detailId: prepareStore.getSelectKey,
-          lessonCourseId: prepareStore.getBaseCourseware.id
-        };
-      } else {
-        const { href } = router.resolve({
-          path: '/attend-class',
-          query: {
-            type: 'preview',
-            instrumentId: prepareStore.getInstrumentId,
-            detailId: prepareStore.getSelectKey,
-            lessonCourseId: prepareStore.getBaseCourseware.id
-          }
-        });
-        window.open(href, +new Date() + '');
-      }
-    };
-    const fscreen = () => {
-      const el: any = document.documentElement;
-      const documentDom: any = document;
-      const isFullscreen =
-        documentDom.fullScreen ||
-        documentDom.mozFullScreen ||
-        documentDom.webkitIsFullScreen;
-      if (!isFullscreen) {
-        //进入全屏
-        (el.requestFullscreen && el.requestFullscreen()) ||
-          (el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
-          (el.webkitRequestFullscreen && el.webkitRequestFullscreen()) ||
-          (el.msRequestFullscreen && el.msRequestFullscreen());
-      }
-    };
-    // 单个删除
-    const onRemove = async (item: any) => {
-      try {
-        dialog.warning({
-          title: '提示',
-          content: '该资源已下架,是否删除?',
-          positiveText: '确定',
-          negativeText: '取消',
-          onPositiveClick: async () => {
-            forms.removeIds.push(item.id);
-            await teacherKnowledgeMaterialDelete({ ids: item.id });
-            message.success('删除成功');
-            getList();
-          }
-        });
-      } catch {
-        //
-      }
-    };
-
-    watch(
-      () => prepareStore.getSubjectList,
-      () => {
-        checkSubjectIds();
-      }
-    );
-
-    const checkSubjectIds = () => {
-      const subjectList = prepareStore.getSubjectList;
-
-      // 并且没有声部时才会更新
-      if (subjectList.length > 0) {
-        // 并且声部在列表中
-        const localStorageSubjectId = localStorage.getItem(
-          'prepareLessonSubjectId'
-        );
-        // // 先取 上次上课声部,在取班级声部 最后取缓存
-        let subjectId = null;
-        let index = -1;
-        if (forms.courseScheduleSubjectId) {
-          // 判断浏览器上面是否有
-          index = subjectList.findIndex(
-            (subject: any) => subject.id == forms.courseScheduleSubjectId
-          );
-          if (index >= 0) {
-            subjectId = Number(forms.courseScheduleSubjectId);
-          }
-        }
-        // 判断班级上面声部 & 还没有声部
-        if (forms.subjectId && !subjectId) {
-          // 判断浏览器上面是否有
-          index = subjectList.findIndex(
-            (subject: any) => subject.id == forms.subjectId
-          );
-          if (index >= 0) {
-            subjectId = Number(forms.subjectId);
-          }
-        }
-        // 缓存声部 & 还没有声部
-        if (localStorageSubjectId && !subjectId) {
-          // 判断浏览器上面是否有
-          index = subjectList.findIndex(
-            (subject: any) => subject.id == localStorageSubjectId
-          );
-          if (index >= 0) {
-            subjectId = Number(localStorageSubjectId);
-          }
-        }
-        if (subjectId && index >= 0) {
-          prepareStore.setSubjectId(subjectId);
-        } else {
-          // 判断是否有缓存
-          prepareStore.setSubjectId(subjectList[0].id);
-        }
-
-        // 保存
-        localStorage.setItem(
-          'prepareLessonSubjectId',
-          prepareStore.getSubjectId as any
-        );
-      }
-    };
-
-    watch(
-      () => route.query,
-      async () => {
-        forms.className = route.query.name as any;
-        forms.classGroupId = route.query.classGroupId as any;
-        forms.preStudentNum = route.query.preStudentNum as any;
-        forms.subjectId = route.query.subjectId
-          ? Number(route.query.subjectId)
-          : null;
-        prepareStore.setClassGroupId(forms.classGroupId as any);
-
-        checkSubjectIds();
-        await getList();
-      }
-    );
-
-    const isPointInsideElement = (element: any, x: number, y: number) => {
-      const rect = element.getBoundingClientRect();
-      return (
-        x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
-      );
-    };
-    const isPointOnLeft = (element: any, x: number) => {
-      const rect = element.getBoundingClientRect();
-      const elementCenterX = rect.left + rect.width / 2;
-      return x < elementCenterX;
-    };
-    onMounted(async () => {
-      prepareStore.setClassGroupId(route.query.classGroupId as any);
-      // 获取教材分类列表
-      checkSubjectIds();
-
-      await getList();
-
-      // 动态添加数据
-      eventGlobal.on('onPrepareAddItem', (item: any, point?: any) => {
-        forms.drag = true;
-        forms.isEdit = true;
-        nextTick(() => {
-          if (point) {
-            const dom = document.querySelectorAll('.row-nav');
-            let isAdd = false;
-            dom.forEach((child: any, index: number) => {
-              const status = isPointInsideElement(child, point.x, point.y);
-              if (status) {
-                const array: any = forms.coursewareList;
-                const left = isPointOnLeft(child, point.x);
-                if (!left) {
-                  array.splice(index + 1, 0, item);
-                } else {
-                  array.splice(index, 0, item);
-                }
-                isAdd = true;
-                forms.coursewareList = array;
-                prepareStore.setCoursewareList(forms.coursewareList);
-              }
-            });
-            if (!isAdd) {
-              forms.coursewareList.push(item);
-              prepareStore.setCoursewareList(forms.coursewareList);
-            }
-          } else {
-            forms.coursewareList.push(item);
-            prepareStore.setCoursewareList(forms.coursewareList);
-          }
-        });
-      });
-    });
-
-    return () => (
-      <div class={styles.coursewareModal}>
-        <div class={styles.btnGroup}>
-          {forms.drag ? (
-            !forms.tipsStatus ? (
-              <div class={styles.tipsContainer}>
-                <div class={styles.tipsLeft}>
-                  <img src={iconTips} class={styles.iconTips} />
-                  <span class={styles.tips}>可以拖动资源排序哦</span>
-                </div>
-                <span
-                  class={styles.btnNoTips}
-                  onClick={() => {
-                    localStorage.setItem('prepare-lesson-courseware-tip', '1');
-                    forms.tipsStatus = true;
-                  }}>
-                  不再提醒
-                </span>
-              </div>
-            ) : (
-              <span></span>
-            )
-          ) : (
-            <NSpace>
-              {forms.classGroupId && (
-                <div class={styles.btnItem}>
-                  <span class={styles.btnTitle}>上课班级:</span>
-                  <div
-                    onClick={() => {
-                      forms.showAttendClass = true;
-                      forms.attendClassType = 'change';
-                    }}>
-                    <NSelect
-                      placeholder="选择班级"
-                      labelField="name"
-                      valueField="id"
-                      class={styles.btnClassList}
-                      value={forms.className}
-                      disabled
-                    />
-                  </div>
-                </div>
-              )}
-
-              <div class={styles.btnItem}>
-                <span class={styles.btnTitle}>声部:</span>
-                <NSelect
-                  placeholder="选择声部"
-                  class={styles.btnSubjectList}
-                  options={prepareStore.getSubjectList}
-                  labelField="name"
-                  valueField="id"
-                  value={prepareStore.getSubjectId}
-                  onUpdate:value={(val: any) => {
-                    prepareStore.setSubjectId(val);
-                    // 保存
-                    localStorage.setItem('prepareLessonSubjectId', val);
-                    getList();
-                  }}
-                />
-              </div>
-            </NSpace>
-          )}
-
-          {/* 编辑 */}
-          {!forms.drag ? (
-            <NSpace>
-              <NButton
-                type="default"
-                onClick={() => {
-                  forms.drag = true;
-                  prepareStore.setIsEditResource(true);
-
-                  // forms.subjectSyncVisiable = true;
-                }}>
-                编辑
-              </NButton>
-            </NSpace>
-          ) : (
-            <NSpace>
-              <NButton
-                type="error"
-                onClick={() => {
-                  forms.removeVisiable1 = true;
-                }}>
-                清空资源
-              </NButton>
-              <NButton
-                type="error"
-                onClick={() => {
-                  forms.drag = false;
-                  forms.isEdit = false;
-                  prepareStore.setIsEditResource(false);
-                  forms.removeIds = [];
-                  getList();
-                }}>
-                取消编辑
-              </NButton>
-              <NButton
-                type="default"
-                onClick={() => {
-                  if (forms.isEdit) {
-                    forms.subjectSyncVisiable = true;
-                  } else {
-                    forms.removeVisiable = true;
-                  }
-                }}>
-                完成编辑
-              </NButton>
-            </NSpace>
-          )}
-        </div>
-
-        <NScrollbar
-          class={[
-            styles.listContainer,
-            forms.drag ? styles.listContainerDrag : ''
-          ]}
-          {...{ id: 'lessonsIn-1' }}>
-          <NSpin show={forms.loadingStatus}>
-            <div
-              class={[
-                styles.listSection
-                // !forms.loadingStatus && forms.coursewareList.length <= 0
-                //   ? styles.emptySection
-                //   : ''
-              ]}
-              onDragenter={(e: any) => {
-                e.preventDefault();
-              }}
-              onDragover={(e: any) => {
-                e.preventDefault();
-              }}
-              onDrop={(e: any) => {
-                console.log(e, 'event');
-                let dropItem = e.dataTransfer.getData('text');
-                dropItem = dropItem ? JSON.parse(dropItem) : {};
-                // 判断是否有数据
-                if (dropItem.id) {
-                  // 获取拖拽的目标元素
-
-                  eventGlobal.emit(
-                    'onPrepareAddItem',
-                    {
-                      materialId: dropItem.id,
-                      coverImg: dropItem.coverImg,
-                      type: dropItem.type,
-                      title: dropItem.title,
-                      isCollect: dropItem.isCollect,
-                      isSelected: dropItem.isSelected,
-                      content: dropItem.content,
-                      removeFlag: false
-                    },
-                    {
-                      x: e.clientX,
-                      y: e.clientY
-                    }
-                  );
-                }
-              }}>
-              {forms.coursewareList.length > 0 && (
-                <>
-                  {forms.drag ? (
-                    <Draggable
-                      v-model:modelValue={forms.coursewareList}
-                      itemKey="id"
-                      componentData={{
-                        itemKey: 'id',
-                        tag: 'div',
-                        animation: 200,
-                        group: 'description',
-                        disabled: false
-                      }}
-                      class={styles.list}>
-                      {{
-                        item: (element: any) => {
-                          const item = element.element;
-                          return (
-                            <div
-                              data-id={item.id}
-                              class={[
-                                styles.itemWrap,
-                                styles.itemBlock,
-                                'row-nav'
-                              ]}>
-                              <div class={styles.itemWrapBox}>
-                                <CardType
-                                  class={[styles.itemContent]}
-                                  isShowCollect={false}
-                                  offShelf={item.removeFlag ? true : false}
-                                  onOffShelf={() => onRemove(item)}
-                                  item={item}
-                                />
-                                <div class={styles.itemOperation}>
-                                  <img
-                                    src={iconDelete}
-                                    class={styles.iconDelete}
-                                    onClick={(e: MouseEvent) => {
-                                      e.stopPropagation();
-                                      onDelete(item);
-                                    }}
-                                  />
-                                </div>
-                              </div>
-                            </div>
-                          );
-                        }
-                      }}
-                    </Draggable>
-                  ) : (
-                    <div class={styles.list}>
-                      {forms.coursewareList.map((item: any) => (
-                        <div class={styles.itemWrap}>
-                          <div class={styles.itemWrapBox}>
-                            <CardType
-                              class={[styles.itemContent, 'handle']}
-                              isShowCollect={false}
-                              item={item}
-                              offShelf={item.removeFlag ? true : false}
-                              onOffShelf={() => onRemove(item)}
-                              disabledMouseHover={false}
-                              onClick={() => {
-                                if (item.type === 'IMG') return;
-                                forms.show = true;
-                                forms.item = item;
-                              }}
-                            />
-                          </div>
-                        </div>
-                      ))}
-                      <div class={styles.itemWrap}>
-                        <div class={styles.itemWrapBox}>
-                          <div
-                            class={[
-                              styles.itemContent,
-                              styles.addMusicItem,
-                              'handle'
-                            ]}
-                            onClick={() => {
-                              // 直接跳转到制谱页面 (临时存储数据)
-                              sessionStorage.setItem(
-                                'notation-open-create',
-                                '1'
-                              );
-                              router.push('/notation');
-                            }}>
-                            <img src={iconAddMusic} />
-
-                            <p class={styles.addMusicName}>开始制谱</p>
-                          </div>
-                        </div>
-                      </div>
-                    </div>
-                  )}
-                </>
-              )}
-
-              {/* {!forms.loadingStatus && forms.coursewareList.length <= 0 && (
-                <TheEmpty description="暂无课件" />
-              )} */}
-              {forms.coursewareList.length <= 0 && (
-                <div class={styles.list}>
-                  <div class={styles.itemWrap}>
-                    <div class={styles.itemWrapBox}>
-                      <div
-                        class={[
-                          styles.itemContent,
-                          styles.addMusicItem,
-                          'handle'
-                        ]}
-                        onClick={() => {
-                          // 直接跳转到制谱页面 (临时存储数据)
-                          sessionStorage.setItem('notation-open-create', '1');
-                          router.push('/notation');
-                        }}>
-                        <img src={iconAddMusic} />
-
-                        <p class={styles.addMusicName}>开始制谱</p>
-                      </div>
-                    </div>
-                  </div>
-                </div>
-              )}
-            </div>
-          </NSpin>
-        </NScrollbar>
-
-        {!forms.drag ? (
-          <div
-            class={[styles.btnGroup, styles.btnGroupClass]}
-            style={{ justifyContent: 'flex-end' }}>
-            <NSpace justify="end">
-              <NButton type="primary" onClick={onPreviewAttend}>
-                预览课件
-              </NButton>
-              <NButton
-                type="error"
-                class={styles.btnClassStart}
-                onClick={async () => {
-                  let count = 0;
-                  forms.coursewareList.forEach((item: any) => {
-                    if (!item.removeFlag) {
-                      count++;
-                    }
-                  });
-                  if (count <= 0) {
-                    message.error('课件不能为空');
-                    return;
-                  }
-
-                  if (forms.classGroupId) {
-                    // 开始上课
-                    const res = await courseScheduleStart({
-                      lessonCoursewareKnowledgeDetailId: prepareStore.selectKey,
-                      classGroupId: forms.classGroupId,
-                      subjectId: prepareStore.getSubjectId
-                    });
-                    if (
-                      window.matchMedia('(display-mode: standalone)').matches
-                    ) {
-                      state.application = window.matchMedia(
-                        '(display-mode: standalone)'
-                      ).matches;
-                      forms.previewModal = true;
-                      fscreen();
-                      forms.previewParams = {
-                        type: 'class',
-                        classGroupId: forms.classGroupId,
-                        instrumentId: prepareStore.getInstrumentId,
-                        detailId: prepareStore.getSelectKey,
-                        classId: res.data,
-                        lessonCourseId: prepareStore.getBaseCourseware.id,
-                        preStudentNum: forms.preStudentNum
-                      };
-                    } else {
-                      const { href } = router.resolve({
-                        path: '/attend-class',
-                        query: {
-                          type: 'class',
-                          classGroupId: forms.classGroupId,
-                          instrumentId: prepareStore.getInstrumentId,
-                          detailId: prepareStore.getSelectKey,
-                          classId: res.data,
-                          lessonCourseId: prepareStore.getBaseCourseware.id,
-                          preStudentNum: forms.preStudentNum
-                        }
-                      });
-                      window.open(href, +new Date() + '');
-                    }
-                  } else {
-                    forms.showAttendClass = true;
-                    forms.attendClassType = 'change';
-                  }
-                }}>
-                开始上课
-              </NButton>
-            </NSpace>
-          </div>
-        ) : (
-          ''
-        )}
-
-        <NModal
-          v-model:show={forms.showAttendClass}
-          preset="card"
-          showIcon={false}
-          class={['modalTitle background', styles.attendClassModal]}
-          title={'选择班级'}
-          blockScroll={false}>
-          <AttendClass
-            onClose={() => (forms.showAttendClass = false)}
-            type={forms.attendClassType}
-            onPreview={(item: any) => {
-              if (window.matchMedia('(display-mode: standalone)').matches) {
-                state.application = window.matchMedia(
-                  '(display-mode: standalone)'
-                ).matches;
-                forms.previewModal = true;
-                forms.previewParams = {
-                  ...item
-                };
-              } else {
-                const { href } = router.resolve({
-                  path: '/attend-class',
-                  query: {
-                    ...item
-                  }
-                });
-                window.open(href, +new Date() + '');
-              }
-            }}
-            onConfirm={async (item: any) => {
-              if (forms.classGroupId) {
-                forms.className = item.name;
-                forms.classGroupId = item.classGroupId;
-                forms.preStudentNum = item.preStudentNum;
-                forms.subjectId = item.subjectId;
-                forms.courseScheduleSubjectId = item.courseScheduleSubjectId;
-                forms.showAttendClass = false;
-
-                prepareStore.setClassGroupId(item.classGroupId);
-                console.log(forms, 'forms', item);
-                checkSubjectIds();
-                // 声部切换时
-                eventGlobal.emit('onChangeClass', {
-                  lastUseCoursewareId: item.lastUseCoursewareId,
-                  unit: item.unit
-                });
-              } else {
-                const res = await courseScheduleStart({
-                  lessonCoursewareKnowledgeDetailId: prepareStore.selectKey,
-                  classGroupId: item.classGroupId,
-                  subjectId: prepareStore.getSubjectId
-                });
-                forms.showAttendClass = false;
-                if (window.matchMedia('(display-mode: standalone)').matches) {
-                  state.application = window.matchMedia(
-                    '(display-mode: standalone)'
-                  ).matches;
-                  forms.previewModal = true;
-                  forms.previewParams = {
-                    type: 'class',
-                    classId: res.data, // 上课编号
-                    classGroupId: item.classGroupId,
-                    preStudentNum: item.preStudentNum,
-                    subjectId: prepareStore.getSubjectId,
-                    detailId: prepareStore.getSelectKey,
-                    lessonCourseId: prepareStore.getBaseCourseware.id
-                  };
-                  setTimeout(() => {
-                    fscreen();
-                  }, 200);
-                } else {
-                  const { href } = router.resolve({
-                    path: '/attend-class',
-                    query: {
-                      type: 'class',
-                      classId: res.data, // 上课编号
-                      classGroupId: item.classGroupId,
-                      preStudentNum: item.preStudentNum,
-                      subjectId: prepareStore.getSubjectId,
-                      detailId: prepareStore.getSelectKey,
-                      lessonCourseId: prepareStore.getBaseCourseware.id
-                    }
-                  });
-                  window.open(href, +new Date() + '');
-                }
-              }
-            }}
-          />
-        </NModal>
-
-        {/* 弹窗查看 */}
-        <CardPreview v-model:show={forms.show} item={forms.item} />
-
-        <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]}
-          title={'清空资源'}>
-          <div class={styles.studentRemove}>
-            <p>
-              请确认是否要清空资源?
-              <span>点击确认后所有的素材内容 将被清空掉。</span>
-            </p>
-
-            <NSpace class={styles.btnGroupModal} justify="center">
-              <NButton
-                round
-                type="primary"
-                onClick={() => {
-                  forms.coursewareList.forEach((item: any) => {
-                    forms.removeIds.push(item.id);
-                  });
-                  forms.coursewareList = [];
-                  forms.removeVisiable1 = false;
-                  forms.isEdit = true;
-                  // prepareStore.setCoursewareList([]);
-                }}>
-                确定
-              </NButton>
-              <NButton round onClick={() => (forms.removeVisiable1 = false)}>
-                取消
-              </NButton>
-            </NSpace>
-          </div>
-        </NModal>
-
-        <PreviewWindow
-          v-model:show={forms.previewModal}
-          type="attend"
-          params={forms.previewParams}
-        />
-
-        {/* 完成编辑时,选择声部 */}
-        <NModal
-          v-model:show={forms.subjectSyncVisiable}
-          preset="card"
-          class={['modalTitle background', styles.subjectSyncModal]}
-          title={'同步声部'}>
-          <SubjectSync
-            subjectId={prepareStore.getSubjectId as any}
-            onClose={() => (forms.subjectSyncVisiable = false)}
-            onConfirm={async (subjectIds: any) => {
-              //
-              try {
-                forms.editSubjectIds = subjectIds.join(',');
-                await onOverEdit();
-                forms.subjectSyncVisiable = false;
-              } catch {
-                //
-              }
-            }}
-          />
-        </NModal>
-      </div>
-    );
-  }
-});
-
+import { defineComponent, nextTick, onMounted, reactive, watch } from 'vue';
+import styles from './index.module.less';
+import {
+  NButton,
+  NModal,
+  NScrollbar,
+  NSelect,
+  NSpace,
+  NSpin,
+  useMessage,
+  useDialog
+} from 'naive-ui';
+import CardType from '/src/components/card-type';
+import AttendClass from '/src/views/prepare-lessons/model/attend-class';
+import { usePrepareStore } from '/src/store/modules/prepareLessons';
+import { useCatchStore } from '/src/store/modules/catchData';
+import TheEmpty from '/src/components/TheEmpty';
+import {
+  courseScheduleStart,
+  queryCourseware,
+  saveCourseware,
+  teacherKnowledgeMaterialDelete
+} from '../../../api';
+import Draggable from 'vuedraggable';
+import iconDelete from '../../../images/icon-delete.png';
+import iconAddMusic from '../../../images/icon-add-music.png';
+import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
+import deepClone from '/src/helpers/deep-clone';
+import CardPreview from '/src/components/card-preview';
+import PreviewWindow from '/src/views/preview-window';
+import { modalClickMask, state } from '/src/state';
+import SubjectSync from '../../../model/subject-sync';
+import { eventGlobal } from '/src/utils';
+import iconTips from '../../../images/icon-tips.png';
+import { useElementBounding } from '@vueuse/core';
+export default defineComponent({
+  name: 'courseware-modal',
+  setup() {
+    const catchStore = useCatchStore();
+    const prepareStore = usePrepareStore();
+    const route = useRoute();
+    const router = useRouter();
+    const dialog = useDialog();
+    const message = useMessage();
+
+    const localStorageSubjectId = localStorage.getItem(
+      'prepareLessonSubjectId'
+    );
+    const forms = reactive({
+      tipsStatus: localStorage.getItem('prepare-lesson-courseware-tip')
+        ? true
+        : false,
+      className: route.query.name as any,
+      classGroupId: route.query.classGroupId,
+      preStudentNum: route.query.preStudentNum,
+      courseScheduleSubjectId: route.query.courseScheduleSubjectId,
+      // 选取参数带的,后取缓存
+      subjectId: route.query.subjectId
+        ? Number(route.query.subjectId)
+        : localStorageSubjectId
+        ? Number(localStorageSubjectId)
+        : null,
+      coursewareList: [] as any,
+      loadingStatus: false,
+      showAttendClass: false,
+      attendClassType: 'change', //
+      removeIds: [] as any, // 临时删除的编号
+      drag: false,
+      isEdit: false, // 是否更新数据
+      editSubjectIds: '', // 声部编号
+      removeVisiable: false,
+      removeVisiable1: false,
+      subjectSyncVisiable: false, // 同步声部
+      show: false,
+      item: {} as any,
+      previewModal: false,
+      previewParams: {
+        type: '',
+        subjectId: '',
+        detailId: ''
+      } as any
+    });
+
+    // 获取列表
+    const getList = async () => {
+      forms.loadingStatus = true;
+      try {
+        // 判断是否有选择对应的课件 或声部
+        if (!prepareStore.getSelectKey || !prepareStore.getSubjectId)
+          return (forms.loadingStatus = false);
+        const { data } = await queryCourseware({
+          coursewareDetailKnowledgeId: prepareStore.getSelectKey,
+          subjectId: prepareStore.getSubjectId,
+          page: 1,
+          rows: 99
+        });
+        const tempRows = data.rows || [];
+        const temp: any = [];
+        tempRows.forEach((row: any) => {
+          temp.push({
+            id: row.id,
+            materialId: row.materialId,
+            coverImg: row.coverImg,
+            type: row.materialType,
+            title: row.materialName,
+            isCollect: !!row.favoriteFlag,
+            isSelected: row.source === 'PLATFORM' ? true : false,
+            content: row.content,
+            removeFlag: row.removeFlag
+          });
+        });
+
+        prepareStore.setCoursewareList(temp || []);
+
+        const tempCourse: any = [];
+        temp.forEach((item: any) => {
+          if (!forms.removeIds.includes(item.id)) {
+            tempCourse.push(item);
+          }
+        });
+        forms.coursewareList = tempCourse;
+      } catch {
+        //
+      }
+      forms.loadingStatus = false;
+    };
+
+    // 监听选择的key 左侧选择了其它的课
+    watch(
+      () => prepareStore.getSelectKey,
+      () => {
+        forms.drag = false;
+        prepareStore.setIsEditResource(false);
+        getList();
+      }
+    );
+    // 声部变化时
+    watch(
+      () => prepareStore.getSubjectId,
+      () => {
+        getList();
+      }
+    );
+    watch(
+      () => prepareStore.getIsAddResource,
+      (val: boolean) => {
+        if (val) {
+          getList();
+          prepareStore.setIsAddResource(false);
+        }
+      }
+    );
+    // 监听列表变化,如果变化了,则弹选择声部的
+    watch(
+      () => forms.coursewareList,
+      () => {
+        if (forms.drag) {
+          forms.isEdit = true;
+        }
+      },
+      {
+        deep: true
+      }
+    );
+
+    // 删除
+    const onDelete = (item: any) => {
+      //
+      forms.removeIds.push(item.id);
+      const index = forms.coursewareList.findIndex(
+        (c: any) => c.id === item.id
+      );
+      forms.coursewareList.splice(index, 1);
+      forms.isEdit = true;
+      // prepareStore.setCoursewareList(forms.coursewareList);
+      // console.log(prepareStore.getCoursewareList, 'getCourseware');
+    };
+
+    // 完成编辑
+    const onOverEdit = async () => {
+      try {
+        const temp: any = [];
+        forms.coursewareList.forEach((item: any) => {
+          temp.push({
+            materialName: item.title,
+            materialType: item.type,
+            materialId: item.materialId,
+            id: item.id
+          });
+        });
+        // 保存课件
+        // 判断是否编辑,如果编辑则取选择的声部
+        await saveCourseware({
+          coursewareDetailKnowledgeId: prepareStore.getSelectKey,
+          lessonCoursewareId: prepareStore.getLessonCoursewareId,
+          lessonCoursewareDetailId: prepareStore.getLessonCoursewareDetailId,
+          subjectId: forms.isEdit
+            ? forms.editSubjectIds
+            : prepareStore.getSubjectId,
+          materialList: [...temp]
+        });
+
+        forms.drag = false;
+        message.success('编辑成功');
+        forms.removeVisiable = false;
+        prepareStore.setIsEditResource(false);
+        // 重置临时删除编号
+        forms.removeIds = [];
+        await getList();
+      } catch {
+        //
+      }
+    };
+
+    // 预览上课
+    const onPreviewAttend = () => {
+      // 获取上架的数据
+      let count = 0;
+      forms.coursewareList.forEach((item: any) => {
+        if (!item.removeFlag) {
+          count++;
+        }
+      });
+      if (count <= 0) {
+        message.error('课件不能为空');
+        return;
+      }
+      // 判断是否在应用里面
+      if (window.matchMedia('(display-mode: standalone)').matches) {
+        state.application = window.matchMedia(
+          '(display-mode: standalone)'
+        ).matches;
+        forms.previewModal = true;
+        fscreen();
+        forms.previewParams = {
+          type: 'preview',
+          instrumentId: prepareStore.getInstrumentId,
+          detailId: prepareStore.getSelectKey,
+          lessonCourseId: prepareStore.getBaseCourseware.id
+        };
+      } else {
+        const { href } = router.resolve({
+          path: '/attend-class',
+          query: {
+            type: 'preview',
+            instrumentId: prepareStore.getInstrumentId,
+            detailId: prepareStore.getSelectKey,
+            lessonCourseId: prepareStore.getBaseCourseware.id
+          }
+        });
+        window.open(href, +new Date() + '');
+      }
+    };
+    const fscreen = () => {
+      const el: any = document.documentElement;
+      const documentDom: any = document;
+      const isFullscreen =
+        documentDom.fullScreen ||
+        documentDom.mozFullScreen ||
+        documentDom.webkitIsFullScreen;
+      if (!isFullscreen) {
+        //进入全屏
+        (el.requestFullscreen && el.requestFullscreen()) ||
+          (el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
+          (el.webkitRequestFullscreen && el.webkitRequestFullscreen()) ||
+          (el.msRequestFullscreen && el.msRequestFullscreen());
+      }
+    };
+    // 单个删除
+    const onRemove = async (item: any) => {
+      try {
+        dialog.warning({
+          title: '提示',
+          content: '该资源已下架,是否删除?',
+          positiveText: '确定',
+          negativeText: '取消',
+          onPositiveClick: async () => {
+            forms.removeIds.push(item.id);
+            await teacherKnowledgeMaterialDelete({ ids: item.id });
+            message.success('删除成功');
+            getList();
+          }
+        });
+      } catch {
+        //
+      }
+    };
+
+    watch(
+      () => prepareStore.getSubjectList,
+      () => {
+        checkSubjectIds();
+      }
+    );
+
+    const checkSubjectIds = () => {
+      const subjectList = prepareStore.getSubjectList;
+
+      // 并且没有声部时才会更新
+      if (subjectList.length > 0) {
+        // 并且声部在列表中
+        const localStorageSubjectId = localStorage.getItem(
+          'prepareLessonSubjectId'
+        );
+        // // 先取 上次上课声部,在取班级声部 最后取缓存
+        let subjectId = null;
+        let index = -1;
+        if (forms.courseScheduleSubjectId) {
+          // 判断浏览器上面是否有
+          index = subjectList.findIndex(
+            (subject: any) => subject.id == forms.courseScheduleSubjectId
+          );
+          if (index >= 0) {
+            subjectId = Number(forms.courseScheduleSubjectId);
+          }
+        }
+        // 判断班级上面声部 & 还没有声部
+        if (forms.subjectId && !subjectId) {
+          // 判断浏览器上面是否有
+          index = subjectList.findIndex(
+            (subject: any) => subject.id == forms.subjectId
+          );
+          if (index >= 0) {
+            subjectId = Number(forms.subjectId);
+          }
+        }
+        // 缓存声部 & 还没有声部
+        if (localStorageSubjectId && !subjectId) {
+          // 判断浏览器上面是否有
+          index = subjectList.findIndex(
+            (subject: any) => subject.id == localStorageSubjectId
+          );
+          if (index >= 0) {
+            subjectId = Number(localStorageSubjectId);
+          }
+        }
+        if (subjectId && index >= 0) {
+          prepareStore.setSubjectId(subjectId);
+        } else {
+          // 判断是否有缓存
+          prepareStore.setSubjectId(subjectList[0].id);
+        }
+
+        // 保存
+        localStorage.setItem(
+          'prepareLessonSubjectId',
+          prepareStore.getSubjectId as any
+        );
+      }
+    };
+
+    watch(
+      () => route.query,
+      async () => {
+        forms.className = route.query.name as any;
+        forms.classGroupId = route.query.classGroupId as any;
+        forms.preStudentNum = route.query.preStudentNum as any;
+        forms.subjectId = route.query.subjectId
+          ? Number(route.query.subjectId)
+          : null;
+        prepareStore.setClassGroupId(forms.classGroupId as any);
+
+        checkSubjectIds();
+        await getList();
+      }
+    );
+
+    const isPointInsideElement = (element: any, x: number, y: number) => {
+      const rect = element.getBoundingClientRect();
+      return (
+        x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
+      );
+    };
+    const isPointOnLeft = (element: any, x: number) => {
+      const rect = element.getBoundingClientRect();
+      const elementCenterX = rect.left + rect.width / 2;
+      return x < elementCenterX;
+    };
+    onMounted(async () => {
+      prepareStore.setClassGroupId(route.query.classGroupId as any);
+      // 获取教材分类列表
+      checkSubjectIds();
+
+      await getList();
+
+      // 动态添加数据
+      eventGlobal.on('onPrepareAddItem', (item: any, point?: any) => {
+        forms.drag = true;
+        forms.isEdit = true;
+        nextTick(() => {
+          if (point) {
+            const dom = document.querySelectorAll('.row-nav');
+            let isAdd = false;
+            dom.forEach((child: any, index: number) => {
+              const status = isPointInsideElement(child, point.x, point.y);
+              if (status) {
+                const array: any = forms.coursewareList;
+                const left = isPointOnLeft(child, point.x);
+                if (!left) {
+                  array.splice(index + 1, 0, item);
+                } else {
+                  array.splice(index, 0, item);
+                }
+                isAdd = true;
+                forms.coursewareList = array;
+                prepareStore.setCoursewareList(forms.coursewareList);
+              }
+            });
+            if (!isAdd) {
+              forms.coursewareList.push(item);
+              prepareStore.setCoursewareList(forms.coursewareList);
+            }
+          } else {
+            forms.coursewareList.push(item);
+            prepareStore.setCoursewareList(forms.coursewareList);
+          }
+        });
+      });
+    });
+
+    return () => (
+      <div class={styles.coursewareModal}>
+        <div class={styles.btnGroup}>
+          {forms.drag ? (
+            !forms.tipsStatus ? (
+              <div class={styles.tipsContainer}>
+                <div class={styles.tipsLeft}>
+                  <img src={iconTips} class={styles.iconTips} />
+                  <span class={styles.tips}>可以拖动资源排序哦</span>
+                </div>
+                <span
+                  class={styles.btnNoTips}
+                  onClick={() => {
+                    localStorage.setItem('prepare-lesson-courseware-tip', '1');
+                    forms.tipsStatus = true;
+                  }}>
+                  不再提醒
+                </span>
+              </div>
+            ) : (
+              <span></span>
+            )
+          ) : (
+            <NSpace>
+              {forms.classGroupId && (
+                <div class={styles.btnItem}>
+                  <span class={styles.btnTitle}>上课班级:</span>
+                  <div
+                    onClick={() => {
+                      forms.showAttendClass = true;
+                      forms.attendClassType = 'change';
+                    }}>
+                    <NSelect
+                      placeholder="选择班级"
+                      labelField="name"
+                      valueField="id"
+                      class={styles.btnClassList}
+                      value={forms.className}
+                      disabled
+                    />
+                  </div>
+                </div>
+              )}
+
+              <div class={styles.btnItem}>
+                <span class={styles.btnTitle}>声部:</span>
+                <NSelect
+                  placeholder="选择声部"
+                  class={styles.btnSubjectList}
+                  options={prepareStore.getSubjectList}
+                  labelField="name"
+                  valueField="id"
+                  value={prepareStore.getSubjectId}
+                  onUpdate:value={(val: any) => {
+                    prepareStore.setSubjectId(val);
+                    // 保存
+                    localStorage.setItem('prepareLessonSubjectId', val);
+                    getList();
+                  }}
+                />
+              </div>
+            </NSpace>
+          )}
+
+          {/* 编辑 */}
+          {!forms.drag ? (
+            <NSpace>
+              <NButton
+                type="default"
+                onClick={() => {
+                  forms.drag = true;
+                  prepareStore.setIsEditResource(true);
+
+                  // forms.subjectSyncVisiable = true;
+                }}>
+                编辑
+              </NButton>
+            </NSpace>
+          ) : (
+            <NSpace>
+              <NButton
+                type="error"
+                onClick={() => {
+                  forms.removeVisiable1 = true;
+                }}>
+                清空资源
+              </NButton>
+              <NButton
+                type="error"
+                onClick={() => {
+                  forms.drag = false;
+                  forms.isEdit = false;
+                  prepareStore.setIsEditResource(false);
+                  forms.removeIds = [];
+                  getList();
+                }}>
+                取消编辑
+              </NButton>
+              <NButton
+                type="default"
+                onClick={() => {
+                  if (forms.isEdit) {
+                    forms.subjectSyncVisiable = true;
+                  } else {
+                    forms.removeVisiable = true;
+                  }
+                }}>
+                完成编辑
+              </NButton>
+            </NSpace>
+          )}
+        </div>
+
+        <NScrollbar
+          class={[
+            styles.listContainer,
+            forms.drag ? styles.listContainerDrag : ''
+          ]}
+          {...{ id: 'lessonsIn-1' }}>
+          <NSpin show={forms.loadingStatus}>
+            <div
+              class={[
+                styles.listSection
+                // !forms.loadingStatus && forms.coursewareList.length <= 0
+                //   ? styles.emptySection
+                //   : ''
+              ]}
+              onDragenter={(e: any) => {
+                e.preventDefault();
+              }}
+              onDragover={(e: any) => {
+                e.preventDefault();
+              }}
+              onDrop={(e: any) => {
+                console.log(e, 'event');
+                let dropItem = e.dataTransfer.getData('text');
+                dropItem = dropItem ? JSON.parse(dropItem) : {};
+                // 判断是否有数据
+                if (dropItem.id) {
+                  // 获取拖拽的目标元素
+
+                  eventGlobal.emit(
+                    'onPrepareAddItem',
+                    {
+                      materialId: dropItem.id,
+                      coverImg: dropItem.coverImg,
+                      type: dropItem.type,
+                      title: dropItem.title,
+                      isCollect: dropItem.isCollect,
+                      isSelected: dropItem.isSelected,
+                      content: dropItem.content,
+                      removeFlag: false
+                    },
+                    {
+                      x: e.clientX,
+                      y: e.clientY
+                    }
+                  );
+                }
+              }}>
+              {forms.coursewareList.length > 0 && (
+                <>
+                  {forms.drag ? (
+                    <Draggable
+                      v-model:modelValue={forms.coursewareList}
+                      itemKey="id"
+                      componentData={{
+                        itemKey: 'id',
+                        tag: 'div',
+                        animation: 200,
+                        group: 'description',
+                        disabled: false
+                      }}
+                      class={styles.list}>
+                      {{
+                        item: (element: any) => {
+                          const item = element.element;
+                          return (
+                            <div
+                              data-id={item.id}
+                              class={[
+                                styles.itemWrap,
+                                styles.itemBlock,
+                                'row-nav'
+                              ]}>
+                              <div class={styles.itemWrapBox}>
+                                <CardType
+                                  class={[styles.itemContent]}
+                                  isShowCollect={false}
+                                  offShelf={item.removeFlag ? true : false}
+                                  onOffShelf={() => onRemove(item)}
+                                  item={item}
+                                />
+                                <div class={styles.itemOperation}>
+                                  <img
+                                    src={iconDelete}
+                                    class={styles.iconDelete}
+                                    onClick={(e: MouseEvent) => {
+                                      e.stopPropagation();
+                                      onDelete(item);
+                                    }}
+                                  />
+                                </div>
+                              </div>
+                            </div>
+                          );
+                        }
+                      }}
+                    </Draggable>
+                  ) : (
+                    <div class={styles.list}>
+                      {forms.coursewareList.map((item: any) => (
+                        <div class={styles.itemWrap}>
+                          <div class={styles.itemWrapBox}>
+                            <CardType
+                              class={[styles.itemContent, 'handle']}
+                              isShowCollect={false}
+                              item={item}
+                              offShelf={item.removeFlag ? true : false}
+                              onOffShelf={() => onRemove(item)}
+                              disabledMouseHover={false}
+                              onClick={() => {
+                                if (item.type === 'IMG') return;
+                                forms.show = true;
+                                forms.item = item;
+                              }}
+                            />
+                          </div>
+                        </div>
+                      ))}
+                      <div class={styles.itemWrap}>
+                        <div class={styles.itemWrapBox}>
+                          <div
+                            class={[
+                              styles.itemContent,
+                              styles.addMusicItem,
+                              'handle'
+                            ]}
+                            onClick={() => {
+                              // 直接跳转到制谱页面 (临时存储数据)
+                              sessionStorage.setItem(
+                                'notation-open-create',
+                                '1'
+                              );
+                              router.push('/notation');
+                            }}>
+                            <img src={iconAddMusic} />
+
+                            <p class={styles.addMusicName}>开始制谱</p>
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                  )}
+                </>
+              )}
+
+              {/* {!forms.loadingStatus && forms.coursewareList.length <= 0 && (
+                <TheEmpty description="暂无课件" />
+              )} */}
+              {forms.coursewareList.length <= 0 && (
+                <div class={styles.list}>
+                  <div class={styles.itemWrap}>
+                    <div class={styles.itemWrapBox}>
+                      <div
+                        class={[
+                          styles.itemContent,
+                          styles.addMusicItem,
+                          'handle'
+                        ]}
+                        onClick={() => {
+                          // 直接跳转到制谱页面 (临时存储数据)
+                          sessionStorage.setItem('notation-open-create', '1');
+                          router.push('/notation');
+                        }}>
+                        <img src={iconAddMusic} />
+
+                        <p class={styles.addMusicName}>开始制谱</p>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              )}
+            </div>
+          </NSpin>
+        </NScrollbar>
+
+        {!forms.drag ? (
+          <div
+            class={[styles.btnGroup, styles.btnGroupClass]}
+            style={{ justifyContent: 'flex-end' }}>
+            <NSpace justify="end">
+              <NButton type="primary" onClick={onPreviewAttend}>
+                预览课件
+              </NButton>
+              <NButton
+                type="error"
+                class={styles.btnClassStart}
+                onClick={async () => {
+                  let count = 0;
+                  forms.coursewareList.forEach((item: any) => {
+                    if (!item.removeFlag) {
+                      count++;
+                    }
+                  });
+                  if (count <= 0) {
+                    message.error('课件不能为空');
+                    return;
+                  }
+
+                  if (forms.classGroupId) {
+                    // 开始上课
+                    const res = await courseScheduleStart({
+                      lessonCoursewareKnowledgeDetailId: prepareStore.selectKey,
+                      classGroupId: forms.classGroupId,
+                      subjectId: prepareStore.getSubjectId
+                    });
+                    if (
+                      window.matchMedia('(display-mode: standalone)').matches
+                    ) {
+                      state.application = window.matchMedia(
+                        '(display-mode: standalone)'
+                      ).matches;
+                      forms.previewModal = true;
+                      fscreen();
+                      forms.previewParams = {
+                        type: 'class',
+                        classGroupId: forms.classGroupId,
+                        instrumentId: prepareStore.getInstrumentId,
+                        detailId: prepareStore.getSelectKey,
+                        classId: res.data,
+                        lessonCourseId: prepareStore.getBaseCourseware.id,
+                        preStudentNum: forms.preStudentNum
+                      };
+                    } else {
+                      const { href } = router.resolve({
+                        path: '/attend-class',
+                        query: {
+                          type: 'class',
+                          classGroupId: forms.classGroupId,
+                          instrumentId: prepareStore.getInstrumentId,
+                          detailId: prepareStore.getSelectKey,
+                          classId: res.data,
+                          lessonCourseId: prepareStore.getBaseCourseware.id,
+                          preStudentNum: forms.preStudentNum
+                        }
+                      });
+                      window.open(href, +new Date() + '');
+                    }
+                  } else {
+                    forms.showAttendClass = true;
+                    forms.attendClassType = 'change';
+                  }
+                }}>
+                开始上课
+              </NButton>
+            </NSpace>
+          </div>
+        ) : (
+          ''
+        )}
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={forms.showAttendClass}
+          preset="card"
+          showIcon={false}
+          class={['modalTitle background', styles.attendClassModal]}
+          title={'选择班级'}
+          blockScroll={false}>
+          <AttendClass
+            onClose={() => (forms.showAttendClass = false)}
+            type={forms.attendClassType}
+            onPreview={(item: any) => {
+              if (window.matchMedia('(display-mode: standalone)').matches) {
+                state.application = window.matchMedia(
+                  '(display-mode: standalone)'
+                ).matches;
+                forms.previewModal = true;
+                forms.previewParams = {
+                  ...item
+                };
+              } else {
+                const { href } = router.resolve({
+                  path: '/attend-class',
+                  query: {
+                    ...item
+                  }
+                });
+                window.open(href, +new Date() + '');
+              }
+            }}
+            onConfirm={async (item: any) => {
+              if (forms.classGroupId) {
+                forms.className = item.name;
+                forms.classGroupId = item.classGroupId;
+                forms.preStudentNum = item.preStudentNum;
+                forms.subjectId = item.subjectId;
+                forms.courseScheduleSubjectId = item.courseScheduleSubjectId;
+                forms.showAttendClass = false;
+
+                prepareStore.setClassGroupId(item.classGroupId);
+                console.log(forms, 'forms', item);
+                checkSubjectIds();
+                // 声部切换时
+                eventGlobal.emit('onChangeClass', {
+                  lastUseCoursewareId: item.lastUseCoursewareId,
+                  unit: item.unit
+                });
+              } else {
+                const res = await courseScheduleStart({
+                  lessonCoursewareKnowledgeDetailId: prepareStore.selectKey,
+                  classGroupId: item.classGroupId,
+                  subjectId: prepareStore.getSubjectId
+                });
+                forms.showAttendClass = false;
+                if (window.matchMedia('(display-mode: standalone)').matches) {
+                  state.application = window.matchMedia(
+                    '(display-mode: standalone)'
+                  ).matches;
+                  forms.previewModal = true;
+                  forms.previewParams = {
+                    type: 'class',
+                    classId: res.data, // 上课编号
+                    classGroupId: item.classGroupId,
+                    preStudentNum: item.preStudentNum,
+                    subjectId: prepareStore.getSubjectId,
+                    detailId: prepareStore.getSelectKey,
+                    lessonCourseId: prepareStore.getBaseCourseware.id
+                  };
+                  setTimeout(() => {
+                    fscreen();
+                  }, 200);
+                } else {
+                  const { href } = router.resolve({
+                    path: '/attend-class',
+                    query: {
+                      type: 'class',
+                      classId: res.data, // 上课编号
+                      classGroupId: item.classGroupId,
+                      preStudentNum: item.preStudentNum,
+                      subjectId: prepareStore.getSubjectId,
+                      detailId: prepareStore.getSelectKey,
+                      lessonCourseId: prepareStore.getBaseCourseware.id
+                    }
+                  });
+                  window.open(href, +new Date() + '');
+                }
+              }
+            }}
+          />
+        </NModal>
+
+        {/* 弹窗查看 */}
+        <CardPreview v-model:show={forms.show} item={forms.item} />
+
+        <NModal
+          maskClosable={modalClickMask}
+          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
+          maskClosable={modalClickMask}
+          v-model:show={forms.removeVisiable1}
+          preset="card"
+          class={['modalTitle', styles.removeVisiable1]}
+          title={'清空资源'}>
+          <div class={styles.studentRemove}>
+            <p>
+              请确认是否要清空资源?
+              <span>点击确认后所有的素材内容 将被清空掉。</span>
+            </p>
+
+            <NSpace class={styles.btnGroupModal} justify="center">
+              <NButton
+                round
+                type="primary"
+                onClick={() => {
+                  forms.coursewareList.forEach((item: any) => {
+                    forms.removeIds.push(item.id);
+                  });
+                  forms.coursewareList = [];
+                  forms.removeVisiable1 = false;
+                  forms.isEdit = true;
+                  // prepareStore.setCoursewareList([]);
+                }}>
+                确定
+              </NButton>
+              <NButton round onClick={() => (forms.removeVisiable1 = false)}>
+                取消
+              </NButton>
+            </NSpace>
+          </div>
+        </NModal>
+
+        <PreviewWindow
+          v-model:show={forms.previewModal}
+          type="attend"
+          params={forms.previewParams}
+        />
+
+        {/* 完成编辑时,选择声部 */}
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={forms.subjectSyncVisiable}
+          preset="card"
+          class={['modalTitle background', styles.subjectSyncModal]}
+          title={'同步声部'}>
+          <SubjectSync
+            subjectId={prepareStore.getSubjectId as any}
+            onClose={() => (forms.subjectSyncVisiable = false)}
+            onConfirm={async (subjectIds: any) => {
+              //
+              try {
+                forms.editSubjectIds = subjectIds.join(',');
+                await onOverEdit();
+                forms.subjectSyncVisiable = false;
+              } catch {
+                //
+              }
+            }}
+          />
+        </NModal>
+      </div>
+    );
+  }
+});

+ 301 - 297
src/views/prepare-lessons/components/lesson-main/train-presets/index.tsx

@@ -1,297 +1,301 @@
-import { defineComponent, onMounted, reactive, watch } from 'vue';
-import styles from './index.module.less';
-import {
-  NButton,
-  NImage,
-  NInput,
-  NModal,
-  NScrollbar,
-  NSpace,
-  NSpin,
-  useMessage
-} from 'naive-ui';
-import { useUserStore } from '@/store/modules/users';
-import add from '@/views/studentList/images/add.png';
-import WorkSection from '../../../model/work-section';
-import { eventGlobal } from '/src/utils';
-import { usePrepareStore } from '/src/store/modules/prepareLessons';
-import {
-  lessonPreTrainingV2Page,
-  lessonPreTrainingV2Remove,
-  lessonPreTrainingV2Save
-} from '../../../api';
-import { storeToRefs } from 'pinia';
-import AssignHomework from '../train/assign-homework';
-import TheEmpty from '/src/components/TheEmpty';
-
-export default defineComponent({
-  name: 'train-presets',
-  emits: ['change'],
-  setup(props, { emit }) {
-    const prepareStore = usePrepareStore();
-    const users = useUserStore();
-    const { info } = storeToRefs(users);
-    const message = useMessage();
-    const forms = reactive({
-      assignHomeworkStatus: false,
-      editTitleVisiable: false,
-      editTitle: '',
-      editBtnLoading: false,
-      selectItem: {} as any,
-      removeVisiable1: false,
-      loadingStatus: false,
-      tableList: [] as any
-    });
-
-    const getList = async () => {
-      forms.loadingStatus = true;
-      try {
-        // 判断是否有选择对应的课件 或声部
-        if (!prepareStore.getSelectKey) return (forms.loadingStatus = false);
-        const { data } = await lessonPreTrainingV2Page({
-          page: 1,
-          coursewareKnowledgeDetailId: prepareStore.getSelectKey
-        });
-        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;
-    };
-    // 监听选择的key 左侧选择了其它的课
-    watch(
-      () => prepareStore.getSelectKey,
-      () => {
-        // forms.drag = false;
-        // prepareStore.setIsEditResource(false);
-        getList();
-      }
-    );
-
-    /** 修改标题 */
-    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 onRemove = async () => {
-      forms.editBtnLoading = true;
-      try {
-        await lessonPreTrainingV2Remove({ id: forms.selectItem.id });
-        message.success('删除成功');
-        forms.removeVisiable1 = false;
-        getList();
-      } catch {
-        //
-      }
-      forms.editBtnLoading = false;
-    };
-
-    onMounted(() => {
-      getList();
-    });
-    return () => (
-      <div class={styles.trainPresets}>
-        <div class={styles.btnGroup}>
-          <NSpace>
-            <NButton
-              type="primary"
-              class={styles.addPreset}
-              onClick={() => {
-                // 设置右侧栏状态
-                eventGlobal.emit('teacher-slideshow', true);
-                emit('change', { status: true });
-              }}
-              v-slots={{
-                icon: () => (
-                  <>
-                    <NImage
-                      class={styles.addBtnIcon}
-                      previewDisabled
-                      src={add}></NImage>
-                  </>
-                )
-              }}>
-              添加作业预设
-            </NButton>
-          </NSpace>
-        </div>
-
-        <NScrollbar class={[styles.listContainer]}>
-          <NSpin show={forms.loadingStatus}>
-            <div
-              class={[
-                styles.listSection,
-                !forms.loadingStatus && forms.tableList.length <= 0
-                  ? styles.emptySection
-                  : ''
-              ]}>
-              <div class={[styles.list]}>
-                {forms.tableList.map((item: any) => (
-                  <WorkSection
-                    item={item}
-                    onEditTitle={() => {
-                      forms.selectItem = item;
-                      forms.editTitle = item.title;
-                      forms.editTitleVisiable = true;
-                    }}
-                    onEdit={() => {
-                      // 设置右侧栏状态
-                      eventGlobal.emit('teacher-slideshow', true);
-                      emit('change', {
-                        status: true,
-                        lessonPreTraining: item
-                      });
-                    }}
-                    onConfirm={() => {
-                      //
-                      if (
-                        !item.lessonPreTrainingDetails ||
-                        item.lessonPreTrainingDetails.length <= 0
-                      ) {
-                        message.error('作业预设不能为空');
-                        return;
-                      }
-                      let count = 0;
-                      item.lessonPreTrainingDetails?.forEach((item: any) => {
-                        if (!item.removeFlag) {
-                          count++;
-                        }
-                      });
-                      if (count <= 0) {
-                        message.error('作业内容不能为空');
-                        return;
-                      }
-                      forms.assignHomeworkStatus = true;
-                      forms.selectItem = item;
-                    }}
-                    onDelete={() => {
-                      forms.removeVisiable1 = true;
-                      forms.selectItem = item;
-                    }}
-                  />
-                ))}
-              </div>
-              {!forms.loadingStatus && forms.tableList.length <= 0 && (
-                <TheEmpty description="暂无作业" />
-              )}
-            </div>
-          </NSpin>
-        </NScrollbar>
-
-        <NModal
-          v-model:show={forms.removeVisiable1}
-          preset="card"
-          class={['modalTitle', styles.removeVisiable1]}
-          title={'删除作业'}>
-          <div class={styles.studentRemove}>
-            <p>请确认是否删除【{forms.selectItem.title}】,删除后不可恢复</p>
-
-            <NSpace class={styles.btnGroupModal} justify="center">
-              <NButton round onClick={() => (forms.removeVisiable1 = false)}>
-                取消
-              </NButton>
-              <NButton
-                round
-                type="primary"
-                onClick={onRemove}
-                loading={forms.editBtnLoading}>
-                确定
-              </NButton>
-            </NSpace>
-          </div>
-        </NModal>
-
-        <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}
-            />
-
-            <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
-            item={forms.selectItem}
-            trainList={[]}
-            onClose={() => (forms.assignHomeworkStatus = false)}
-          />
-        </NModal>
-      </div>
-    );
-  }
-});
+import { defineComponent, onMounted, reactive, watch } from 'vue';
+import styles from './index.module.less';
+import {
+  NButton,
+  NImage,
+  NInput,
+  NModal,
+  NScrollbar,
+  NSpace,
+  NSpin,
+  useMessage
+} from 'naive-ui';
+import { useUserStore } from '@/store/modules/users';
+import add from '@/views/studentList/images/add.png';
+import WorkSection from '../../../model/work-section';
+import { eventGlobal } from '/src/utils';
+import { usePrepareStore } from '/src/store/modules/prepareLessons';
+import {
+  lessonPreTrainingV2Page,
+  lessonPreTrainingV2Remove,
+  lessonPreTrainingV2Save
+} from '../../../api';
+import { storeToRefs } from 'pinia';
+import AssignHomework from '../train/assign-homework';
+import TheEmpty from '/src/components/TheEmpty';
+import { modalClickMask } from '/src/state';
+
+export default defineComponent({
+  name: 'train-presets',
+  emits: ['change'],
+  setup(props, { emit }) {
+    const prepareStore = usePrepareStore();
+    const users = useUserStore();
+    const { info } = storeToRefs(users);
+    const message = useMessage();
+    const forms = reactive({
+      assignHomeworkStatus: false,
+      editTitleVisiable: false,
+      editTitle: '',
+      editBtnLoading: false,
+      selectItem: {} as any,
+      removeVisiable1: false,
+      loadingStatus: false,
+      tableList: [] as any
+    });
+
+    const getList = async () => {
+      forms.loadingStatus = true;
+      try {
+        // 判断是否有选择对应的课件 或声部
+        if (!prepareStore.getSelectKey) return (forms.loadingStatus = false);
+        const { data } = await lessonPreTrainingV2Page({
+          page: 1,
+          coursewareKnowledgeDetailId: prepareStore.getSelectKey
+        });
+        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;
+    };
+    // 监听选择的key 左侧选择了其它的课
+    watch(
+      () => prepareStore.getSelectKey,
+      () => {
+        // forms.drag = false;
+        // prepareStore.setIsEditResource(false);
+        getList();
+      }
+    );
+
+    /** 修改标题 */
+    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 onRemove = async () => {
+      forms.editBtnLoading = true;
+      try {
+        await lessonPreTrainingV2Remove({ id: forms.selectItem.id });
+        message.success('删除成功');
+        forms.removeVisiable1 = false;
+        getList();
+      } catch {
+        //
+      }
+      forms.editBtnLoading = false;
+    };
+
+    onMounted(() => {
+      getList();
+    });
+    return () => (
+      <div class={styles.trainPresets}>
+        <div class={styles.btnGroup}>
+          <NSpace>
+            <NButton
+              type="primary"
+              class={styles.addPreset}
+              onClick={() => {
+                // 设置右侧栏状态
+                eventGlobal.emit('teacher-slideshow', true);
+                emit('change', { status: true });
+              }}
+              v-slots={{
+                icon: () => (
+                  <>
+                    <NImage
+                      class={styles.addBtnIcon}
+                      previewDisabled
+                      src={add}></NImage>
+                  </>
+                )
+              }}>
+              添加作业预设
+            </NButton>
+          </NSpace>
+        </div>
+
+        <NScrollbar class={[styles.listContainer]}>
+          <NSpin show={forms.loadingStatus}>
+            <div
+              class={[
+                styles.listSection,
+                !forms.loadingStatus && forms.tableList.length <= 0
+                  ? styles.emptySection
+                  : ''
+              ]}>
+              <div class={[styles.list]}>
+                {forms.tableList.map((item: any) => (
+                  <WorkSection
+                    item={item}
+                    onEditTitle={() => {
+                      forms.selectItem = item;
+                      forms.editTitle = item.title;
+                      forms.editTitleVisiable = true;
+                    }}
+                    onEdit={() => {
+                      // 设置右侧栏状态
+                      eventGlobal.emit('teacher-slideshow', true);
+                      emit('change', {
+                        status: true,
+                        lessonPreTraining: item
+                      });
+                    }}
+                    onConfirm={() => {
+                      //
+                      if (
+                        !item.lessonPreTrainingDetails ||
+                        item.lessonPreTrainingDetails.length <= 0
+                      ) {
+                        message.error('作业预设不能为空');
+                        return;
+                      }
+                      let count = 0;
+                      item.lessonPreTrainingDetails?.forEach((item: any) => {
+                        if (!item.removeFlag) {
+                          count++;
+                        }
+                      });
+                      if (count <= 0) {
+                        message.error('作业内容不能为空');
+                        return;
+                      }
+                      forms.assignHomeworkStatus = true;
+                      forms.selectItem = item;
+                    }}
+                    onDelete={() => {
+                      forms.removeVisiable1 = true;
+                      forms.selectItem = item;
+                    }}
+                  />
+                ))}
+              </div>
+              {!forms.loadingStatus && forms.tableList.length <= 0 && (
+                <TheEmpty description="暂无作业" />
+              )}
+            </div>
+          </NSpin>
+        </NScrollbar>
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={forms.removeVisiable1}
+          preset="card"
+          class={['modalTitle', styles.removeVisiable1]}
+          title={'删除作业'}>
+          <div class={styles.studentRemove}>
+            <p>请确认是否删除【{forms.selectItem.title}】,删除后不可恢复</p>
+
+            <NSpace class={styles.btnGroupModal} justify="center">
+              <NButton round onClick={() => (forms.removeVisiable1 = false)}>
+                取消
+              </NButton>
+              <NButton
+                round
+                type="primary"
+                onClick={onRemove}
+                loading={forms.editBtnLoading}>
+                确定
+              </NButton>
+            </NSpace>
+          </div>
+        </NModal>
+
+        <NModal
+          maskClosable={modalClickMask}
+          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}
+            />
+
+            <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
+          maskClosable={modalClickMask}
+          v-model:show={forms.assignHomeworkStatus}
+          preset="card"
+          showIcon={false}
+          class={['modalTitle background', styles.assignHomework]}
+          title={'布置作业'}
+          blockScroll={false}>
+          <AssignHomework
+            item={forms.selectItem}
+            trainList={[]}
+            onClose={() => (forms.assignHomeworkStatus = false)}
+          />
+        </NModal>
+      </div>
+    );
+  }
+});

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

@@ -30,6 +30,7 @@ import Dragbom from '@/hooks/useDrag/dragbom';
 import { useUserStore } from '@/store/modules/users';
 import { api_getCurrentGradeYear } from '/src/views/studentList/api';
 import request from '/src/utils/request';
+import { modalClickMask } from '/src/state';
 
 export default defineComponent({
   name: 'assign-homework',
@@ -492,6 +493,7 @@ export default defineComponent({
         </NForm>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={forms.workVisiable}
           style={
             props.from === 'class'

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

@@ -45,6 +45,7 @@ import useDrag from '@/hooks/useDrag';
 import Dragbom from '@/hooks/useDrag/dragbom';
 import { useUserStore } from '@/store/modules/users';
 import request from '/src/utils/request';
+import { modalClickMask } from '/src/state';
 export default defineComponent({
   name: 'courseware-modal',
   props: {
@@ -528,6 +529,7 @@ export default defineComponent({
 
         {/* 编辑 */}
         <NModal
+          maskClosable={modalClickMask}
           style={
             props.from === 'class' ? workSetingBoxDragData.styleDrag.value : {}
           }
@@ -572,6 +574,7 @@ export default defineComponent({
 
         {/* 添加自定义教材 */}
         <NModal
+          maskClosable={modalClickMask}
           style={
             props.from === 'class'
               ? workArrangeImmediatelyBoxDragData.styleDrag.value
@@ -613,6 +616,7 @@ export default defineComponent({
         {/* {showGuide.value ? <Trainguide></Trainguide> : null} */}
 
         <NModal
+          maskClosable={modalClickMask}
           style={
             props.from === 'class' ? workClearBoxDragData.styleDrag.value : {}
           }
@@ -649,6 +653,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           style={
             props.from === 'class' ? workSaveBoxDragData.styleDrag.value : {}
           }

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

@@ -23,6 +23,7 @@ import { favorite, materialQueryPage } from '/src/views/natural-resources/api';
 import useDrag from '@/hooks/useDrag';
 import Dragbom from '@/hooks/useDrag/dragbom';
 import { useUserStore } from '@/store/modules/users';
+import { modalClickMask } from '/src/state';
 
 const formatType = (type: string) => {
   if (type === 'sahreMusic') {
@@ -319,6 +320,7 @@ export default defineComponent({
         />
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.editStatus}
           style={
             props.from === 'class' ? workSetingBoxDragData.styleDrag.value : {}

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

@@ -21,6 +21,7 @@ import useDrag from '@/hooks/useDrag';
 import Dragbom from '@/hooks/useDrag/dragbom';
 import { useUserStore } from '@/store/modules/users';
 import { useCatchStore } from '/src/store/modules/catchData';
+import { modalClickMask } from '/src/state';
 export default defineComponent({
   name: 'resource-main',
   props: {
@@ -254,6 +255,7 @@ export default defineComponent({
           </NTabs>
         )}
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={forms.selectResourceStatus}
           onUpdate:show={(val: any) => {
             if (!val) {
@@ -267,6 +269,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           style={
             props.from === 'class'
               ? selectResourceStatusAddBoxDragData.styleDrag.value
@@ -292,6 +295,7 @@ export default defineComponent({
           {props.from === 'class' && <Dragbom></Dragbom>}
         </NModal>
         <NModal
+          maskClosable={modalClickMask}
           style={
             props.from === 'class' ? workSetingBoxDragData.styleDrag.value : {}
           }

+ 9 - 0
src/views/prepare-lessons/model/add-other-source/index.tsx

@@ -22,6 +22,7 @@ import UploadModal, {
   formatUrlType
 } from '/src/views/natural-resources/components/my-resources/upload-modal';
 import SaveModal from '/src/views/natural-resources/components/my-resources/save-modal';
+import { modalClickMask } from '/src/state';
 
 export default defineComponent({
   name: 'add-other-source',
@@ -152,6 +153,7 @@ export default defineComponent({
         */}
         {/* 节奏练习 */}
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.rhythmStatus}
           preset="card"
           class={['modalTitle background', styles.addOtherSourceModal]}
@@ -177,6 +179,7 @@ export default defineComponent({
 
         {/* 乐器百科 */}
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.instrumentStatus}
           preset="card"
           class={['modalTitle', styles.instrumentModal]}
@@ -207,6 +210,7 @@ export default defineComponent({
 
         {/* 乐理知识 */}
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.theoryStatus}
           preset="card"
           class={['modalTitle', styles.theoryModal]}
@@ -237,6 +241,7 @@ export default defineComponent({
 
         {/* 音乐家 */}
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.musicianStatus}
           preset="card"
           class={['modalTitle', styles.instrumentModal]}
@@ -267,6 +272,7 @@ export default defineComponent({
 
         {/* 名曲鉴赏 */}
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.musicStatus}
           preset="card"
           class={['modalTitle', styles.musicModal]}
@@ -297,6 +303,7 @@ export default defineComponent({
 
         {/* 听音练习 */}
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.listenStatus}
           preset="card"
           class={['modalTitle background', styles.subjectSyncModal]}
@@ -333,6 +340,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.uploadStatus}
           preset="card"
           showIcon={false}
@@ -378,6 +386,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.saveStatus}
           preset="card"
           showIcon={false}

+ 2 - 1
src/views/prepare-lessons/model/attend-class/index.tsx

@@ -7,7 +7,7 @@ import { classGroupPage, courseScheduleStart } from '../../api';
 import { useThrottleFn } from '@vueuse/core';
 import TheEmpty from '/src/components/TheEmpty';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
-import { state } from '/src/state';
+import { modalClickMask, state } from '/src/state';
 import { nextTick } from 'process';
 import UpdateSubject from '/src/views/classList/modals/updateSubject';
 import { api_getCurrentGradeYear } from '/src/views/studentList/api';
@@ -238,6 +238,7 @@ export default defineComponent({
         </NScrollbar>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={forms.showSubjectClass}
           style={{ width: '500px' }}
           preset="card"

+ 308 - 297
src/views/prepare-lessons/model/the-create/index.tsx

@@ -1,297 +1,308 @@
-import {
-	NButton,
-	NCard,
-	NCheckbox,
-	NGi,
-	NGrid,
-	NIcon,
-	NInputNumber,
-	NLayout,
-	NLayoutContent,
-	NLayoutSider,
-	NModal,
-	NPopover,
-	NRadio,
-	NRadioGroup,
-	NScrollbar,
-	NSpace,
-	NTabPane,
-	NTable,
-	NTabs,
-	useMessage,
-} from "naive-ui";
-import { defineComponent, onMounted, reactive, watch } from "vue";
-import { CheckmarkCircle, Close } from "@vicons/ionicons5";
-import styles from "./index.module.less";
-import { getImage } from "/src/pc/home/images";
-import { ABC_DATA } from "/src/pc/home/runtime";
-import TheIcon from "/src/components/The-icon";
-import TheSpeed from "/src/pc/home/component/the-speed";
-import { api_musicSheetCreationSave, api_subjectList } from "/src/pc/api";
-import { initMusic } from "/src/pc/home";
-import { encodeUrl } from "/src/utils";
-const instruments = [
-	{
-		label: "竖笛",
-		key: "recorder",
-		id: 4,
-		icon: getImage("icon_27_0.png"),
-		range: {
-			min: 48,
-			max: 74,
-		},
-	},
-	{
-		label: "排箫",
-		key: "pan_flute",
-		id: 1,
-		icon: getImage("icon_27_1.png"),
-		range: {
-			min: 43,
-			max: 77,
-		},
-	},
-	{
-		label: "口风琴",
-		key: "piccolo",
-		id: 5,
-		icon: getImage("icon_27_2.png"),
-		range: {
-			min: 41,
-			max: 72,
-		},
-	},
-	{
-		label: "陶笛",
-		key: "blown_bottle",
-		id: 2,
-		icon: getImage("icon_27_3.png"),
-		range: {
-			min: 45,
-			max: 65,
-		},
-	},
-	{
-		label: "葫芦丝",
-		key: "clarinet",
-		id: 3,
-		icon: getImage("icon_27_4.png"),
-		range: {
-			min: 40,
-			max: 57,
-		},
-	},
-];
-export const notationInstruments = instruments;
-export default defineComponent({
-	name: "TheCreate",
-	props: {
-		show: {
-			type: Boolean,
-			default: false,
-		},
-	},
-	emits: ["update:show", "create"],
-	setup(props, { emit }) {
-		const message = useMessage();
-
-		const formsOptions = reactive({
-			loading: false,
-		});
-		const froms = reactive({
-			key: ABC_DATA.key[0],
-			meter: ABC_DATA.meter[0],
-			speed: 80,
-			measure: 30,
-			subjectCode: "recorder",
-		});
-
-		const handleCreate = async () => {
-			if (!froms.speed) {
-				message.warning("请输入开始速度");
-				return;
-			}
-			if (!froms.measure) {
-				message.warning("请输入小节数量");
-				return;
-			}
-			formsOptions.loading = true;
-			handleOpenNotaion({
-				meter: froms.meter.value,
-				speed: `Q:1/4=${froms.speed}`,
-				key: froms.key.value,
-				subjectCode: froms.subjectCode,
-				measure: froms.measure,
-			});
-			emit("create");
-			formsOptions.loading = false;
-		};
-
-		const handleOpenNotaion = (data: any) => {
-			const url = `${location.origin}/notation/#/?v=1.0.3&config=${encodeUrl(data)}`;
-			window.parent.postMessage(
-				{
-					api: "notation_open",
-					url: url,
-				},
-				"*"
-			);
-			// window.open(url, "_blank");
-		};
-
-		return () => (
-			<NModal
-				transformOrigin="center"
-				autoFocus={false}
-				show={props.show}
-				onUpdate:show={(val) => emit("update:show", val)}
-			>
-				<div class={styles.setbox}>
-					<div class={styles.head}>
-						<div>新建乐谱</div>
-						<NButton
-							class={styles.close}
-							quaternary
-							circle
-							size="small"
-							onClick={() => emit("update:show", false)}
-						>
-							<NIcon component={Close} size={18} />
-						</NButton>
-					</div>
-					<div class={styles.content}>
-						<div class={styles.lineTitle}>声部</div>
-						<NSpace size={32} style={{ paddingBottom: "45px" }}>
-							{instruments.map((item) => (
-								<div
-									class={[styles.item, froms.subjectCode === item.key && styles.itemActive]}
-									onClick={() => {
-										froms.subjectCode = item.key;
-									}}
-								>
-									<div class={styles.itemImg}>
-										<img class={styles.icon} src={item.icon} />
-									</div>
-									<div>{item.label}</div>
-									<img class={styles.itemIcon} src={getImage("icon_check.png")} />
-								</div>
-							))}
-						</NSpace>
-						<NSpace style={{ paddingBottom: "45px" }}>
-							<NPopover to="body" trigger="click">
-								{{
-									trigger: () => (
-										<div>
-											<div class={styles.lineTitle}>调号</div>
-											<div class={styles.beatItem}>
-												<div class={[styles.beatIcon]}>
-													<TheIcon iconClassName={froms.key.icon} />
-												</div>
-												<div>{froms.key.name}</div>
-											</div>
-										</div>
-									),
-									default: () => (
-										<NGrid cols={5} xGap={20} yGap={8}>
-											{ABC_DATA.key.map((item) => (
-												<NGi>
-													<div
-														class={[styles.btnItem, froms.key.value === item.value && styles.active]}
-														onClick={() => (froms.key = item)}
-													>
-														<div class={[styles.btnItemIcon]}>
-															<TheIcon iconClassName={item.icon} />
-														</div>
-														<div class={styles.btnItemName}>{item.name}</div>
-													</div>
-												</NGi>
-											))}
-										</NGrid>
-									),
-								}}
-							</NPopover>
-
-							<NPopover to="body" trigger="click">
-								{{
-									trigger: () => (
-										<div>
-											<div class={styles.lineTitle}>拍号</div>
-											<div class={styles.beatItem}>
-												<div class={[styles.beatIcon]}>
-													<TheIcon iconClassName={froms.meter.icon} />
-												</div>
-												<div>{froms.meter.name}</div>
-											</div>
-										</div>
-									),
-									default: () => (
-										<NGrid cols={5} xGap={50} yGap={20}>
-											{ABC_DATA.meter.map((item) => (
-												<NGi>
-													<div
-														class={[styles.btnItem, froms.meter.value === item.value && styles.active]}
-														onClick={() => (froms.meter = item)}
-													>
-														<div class={[styles.btnItemIcon]}>
-															<TheIcon iconClassName={item.icon} />
-														</div>
-														<div class={styles.btnItemName}>{item.name}</div>
-													</div>
-												</NGi>
-											))}
-										</NGrid>
-									),
-								}}
-							</NPopover>
-							<div>
-								<div class={styles.lineTitle}>速度</div>
-								<div class={styles.beatItem}>
-									<NInputNumber
-										size="large"
-										v-model:value={froms.speed}
-										placeholder="开始速度"
-										showButton={false}
-										min={50}
-									>
-										{{
-											prefix: () => (
-												<div class={styles.speedIcon}>
-													<TheIcon iconClassName="icon-a-sudu-4fenyinfu" size={["2em", "1em"]} />
-												</div>
-											),
-										}}
-									</NInputNumber>
-								</div>
-							</div>
-							<div>
-								<div class={styles.lineTitle}>小节</div>
-								<div class={styles.beatItem}>
-									<NInputNumber
-										placeholder="小节数量"
-										size="large"
-										v-model:value={froms.measure}
-										min={4}
-									></NInputNumber>
-								</div>
-							</div>
-						</NSpace>
-
-						<div class={styles.btns}>
-							<NButton round onClick={() => emit("update:show", false)}>
-								取消
-							</NButton>
-							<NButton
-								loading={formsOptions.loading}
-								round
-								type="primary"
-								onClick={() => handleCreate()}
-							>
-								确定
-							</NButton>
-						</div>
-					</div>
-				</div>
-			</NModal>
-		);
-	},
-});
+import {
+  NButton,
+  NCard,
+  NCheckbox,
+  NGi,
+  NGrid,
+  NIcon,
+  NInputNumber,
+  NLayout,
+  NLayoutContent,
+  NLayoutSider,
+  NModal,
+  NPopover,
+  NRadio,
+  NRadioGroup,
+  NScrollbar,
+  NSpace,
+  NTabPane,
+  NTable,
+  NTabs,
+  useMessage
+} from 'naive-ui';
+import { defineComponent, onMounted, reactive, watch } from 'vue';
+import { CheckmarkCircle, Close } from '@vicons/ionicons5';
+import styles from './index.module.less';
+import { getImage } from '/src/pc/home/images';
+import { ABC_DATA } from '/src/pc/home/runtime';
+import TheIcon from '/src/components/The-icon';
+import TheSpeed from '/src/pc/home/component/the-speed';
+import { api_musicSheetCreationSave, api_subjectList } from '/src/pc/api';
+import { initMusic } from '/src/pc/home';
+import { encodeUrl } from '/src/utils';
+import { modalClickMask } from '/src/state';
+const instruments = [
+  {
+    label: '竖笛',
+    key: 'recorder',
+    id: 4,
+    icon: getImage('icon_27_0.png'),
+    range: {
+      min: 48,
+      max: 74
+    }
+  },
+  {
+    label: '排箫',
+    key: 'pan_flute',
+    id: 1,
+    icon: getImage('icon_27_1.png'),
+    range: {
+      min: 43,
+      max: 77
+    }
+  },
+  {
+    label: '口风琴',
+    key: 'piccolo',
+    id: 5,
+    icon: getImage('icon_27_2.png'),
+    range: {
+      min: 41,
+      max: 72
+    }
+  },
+  {
+    label: '陶笛',
+    key: 'blown_bottle',
+    id: 2,
+    icon: getImage('icon_27_3.png'),
+    range: {
+      min: 45,
+      max: 65
+    }
+  },
+  {
+    label: '葫芦丝',
+    key: 'clarinet',
+    id: 3,
+    icon: getImage('icon_27_4.png'),
+    range: {
+      min: 40,
+      max: 57
+    }
+  }
+];
+export const notationInstruments = instruments;
+export default defineComponent({
+  name: 'TheCreate',
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    }
+  },
+  emits: ['update:show', 'create'],
+  setup(props, { emit }) {
+    const message = useMessage();
+
+    const formsOptions = reactive({
+      loading: false
+    });
+    const froms = reactive({
+      key: ABC_DATA.key[0],
+      meter: ABC_DATA.meter[0],
+      speed: 80,
+      measure: 30,
+      subjectCode: 'recorder'
+    });
+
+    const handleCreate = async () => {
+      if (!froms.speed) {
+        message.warning('请输入开始速度');
+        return;
+      }
+      if (!froms.measure) {
+        message.warning('请输入小节数量');
+        return;
+      }
+      formsOptions.loading = true;
+      handleOpenNotaion({
+        meter: froms.meter.value,
+        speed: `Q:1/4=${froms.speed}`,
+        key: froms.key.value,
+        subjectCode: froms.subjectCode,
+        measure: froms.measure
+      });
+      emit('create');
+      formsOptions.loading = false;
+    };
+
+    const handleOpenNotaion = (data: any) => {
+      const url = `${location.origin}/notation/#/?v=1.0.3&config=${encodeUrl(
+        data
+      )}`;
+      window.parent.postMessage(
+        {
+          api: 'notation_open',
+          url: url
+        },
+        '*'
+      );
+      // window.open(url, "_blank");
+    };
+
+    return () => (
+      <NModal
+        maskClosable={modalClickMask}
+        transformOrigin="center"
+        autoFocus={false}
+        show={props.show}
+        onUpdate:show={val => emit('update:show', val)}>
+        <div class={styles.setbox}>
+          <div class={styles.head}>
+            <div>新建乐谱</div>
+            <NButton
+              class={styles.close}
+              quaternary
+              circle
+              size="small"
+              onClick={() => emit('update:show', false)}>
+              <NIcon component={Close} size={18} />
+            </NButton>
+          </div>
+          <div class={styles.content}>
+            <div class={styles.lineTitle}>声部</div>
+            <NSpace size={32} style={{ paddingBottom: '45px' }}>
+              {instruments.map(item => (
+                <div
+                  class={[
+                    styles.item,
+                    froms.subjectCode === item.key && styles.itemActive
+                  ]}
+                  onClick={() => {
+                    froms.subjectCode = item.key;
+                  }}>
+                  <div class={styles.itemImg}>
+                    <img class={styles.icon} src={item.icon} />
+                  </div>
+                  <div>{item.label}</div>
+                  <img
+                    class={styles.itemIcon}
+                    src={getImage('icon_check.png')}
+                  />
+                </div>
+              ))}
+            </NSpace>
+            <NSpace style={{ paddingBottom: '45px' }}>
+              <NPopover to="body" trigger="click">
+                {{
+                  trigger: () => (
+                    <div>
+                      <div class={styles.lineTitle}>调号</div>
+                      <div class={styles.beatItem}>
+                        <div class={[styles.beatIcon]}>
+                          <TheIcon iconClassName={froms.key.icon} />
+                        </div>
+                        <div>{froms.key.name}</div>
+                      </div>
+                    </div>
+                  ),
+                  default: () => (
+                    <NGrid cols={5} xGap={20} yGap={8}>
+                      {ABC_DATA.key.map(item => (
+                        <NGi>
+                          <div
+                            class={[
+                              styles.btnItem,
+                              froms.key.value === item.value && styles.active
+                            ]}
+                            onClick={() => (froms.key = item)}>
+                            <div class={[styles.btnItemIcon]}>
+                              <TheIcon iconClassName={item.icon} />
+                            </div>
+                            <div class={styles.btnItemName}>{item.name}</div>
+                          </div>
+                        </NGi>
+                      ))}
+                    </NGrid>
+                  )
+                }}
+              </NPopover>
+
+              <NPopover to="body" trigger="click">
+                {{
+                  trigger: () => (
+                    <div>
+                      <div class={styles.lineTitle}>拍号</div>
+                      <div class={styles.beatItem}>
+                        <div class={[styles.beatIcon]}>
+                          <TheIcon iconClassName={froms.meter.icon} />
+                        </div>
+                        <div>{froms.meter.name}</div>
+                      </div>
+                    </div>
+                  ),
+                  default: () => (
+                    <NGrid cols={5} xGap={50} yGap={20}>
+                      {ABC_DATA.meter.map(item => (
+                        <NGi>
+                          <div
+                            class={[
+                              styles.btnItem,
+                              froms.meter.value === item.value && styles.active
+                            ]}
+                            onClick={() => (froms.meter = item)}>
+                            <div class={[styles.btnItemIcon]}>
+                              <TheIcon iconClassName={item.icon} />
+                            </div>
+                            <div class={styles.btnItemName}>{item.name}</div>
+                          </div>
+                        </NGi>
+                      ))}
+                    </NGrid>
+                  )
+                }}
+              </NPopover>
+              <div>
+                <div class={styles.lineTitle}>速度</div>
+                <div class={styles.beatItem}>
+                  <NInputNumber
+                    size="large"
+                    v-model:value={froms.speed}
+                    placeholder="开始速度"
+                    showButton={false}
+                    min={50}>
+                    {{
+                      prefix: () => (
+                        <div class={styles.speedIcon}>
+                          <TheIcon
+                            iconClassName="icon-a-sudu-4fenyinfu"
+                            size={['2em', '1em']}
+                          />
+                        </div>
+                      )
+                    }}
+                  </NInputNumber>
+                </div>
+              </div>
+              <div>
+                <div class={styles.lineTitle}>小节</div>
+                <div class={styles.beatItem}>
+                  <NInputNumber
+                    placeholder="小节数量"
+                    size="large"
+                    v-model:value={froms.measure}
+                    min={4}></NInputNumber>
+                </div>
+              </div>
+            </NSpace>
+
+            <div class={styles.btns}>
+              <NButton round onClick={() => emit('update:show', false)}>
+                取消
+              </NButton>
+              <NButton
+                loading={formsOptions.loading}
+                round
+                type="primary"
+                onClick={() => handleCreate()}>
+                确定
+              </NButton>
+            </div>
+          </div>
+        </div>
+      </NModal>
+    );
+  }
+});

+ 117 - 115
src/views/preview-window/index.tsx

@@ -1,115 +1,117 @@
-import {
-  PropType,
-  defineComponent,
-  onMounted,
-  onUnmounted,
-  ref,
-  toRefs,
-  watch
-} from 'vue';
-import styles from './index.module.less';
-import { NModal } from 'naive-ui';
-import AttendClass from '../attend-class';
-import { iframeDislableKeyboard } from '/src/utils';
-
-export default defineComponent({
-  name: 'preview-window',
-  props: {
-    show: {
-      type: Boolean,
-      default: false
-    },
-    type: {
-      type: String,
-      default: ''
-    },
-    params: {
-      type: Object as PropType<any>,
-      default: () => ({})
-    }
-  },
-  emit: ['update:show'],
-  setup(props, { emit }) {
-    const { show, type, params } = toRefs(props);
-    watch(
-      () => props.show,
-      () => {
-        show.value = props.show;
-      }
-    );
-
-    watch(
-      () => props.type,
-      () => {
-        type.value = props.type;
-      }
-    );
-
-    watch(
-      () => props.params,
-      () => {
-        params.value = props.params;
-      }
-    );
-
-    const handleOpen = (e: MessageEvent) => {
-      if (e.data.api === 'iframe_exit') {
-        emit('update:show', false);
-      }
-    };
-
-    onMounted(() => {
-      window.addEventListener('message', handleOpen);
-    });
-    onUnmounted(() => {
-      window.removeEventListener('message', handleOpen);
-    });
-    return () => (
-      <>
-        <NModal
-          v-model:show={show.value}
-          onUpdate:show={() => {
-            emit('update:show', show.value);
-          }}
-          class={styles.previewWindow}
-          showIcon={false}
-          displayDirective="show">
-          <>
-            {show.value ? (
-              type.value == 'attend' ? (
-                <AttendClass
-                  type={params.value.type || ''}
-                  instrumentId={params.value.instrumentId || ''}
-                  courseId={params.value.courseId || ''}
-                  detailId={params.value.detailId || ''}
-                  classGroupId={params.value.classGroupId || ''}
-                  lessonCourseId={params.value.lessonCourseId || ''}
-                  classId={params.value.classId || ''}
-                  preStudentNum={params.value.preStudentNum}
-                  onClose={() => emit('update:show', false)}
-                />
-              ) : type.value == 'notation' ? (
-                <iframe
-                  src={params.value.src}
-                  onLoad={(val: any) => {
-                    iframeDislableKeyboard(val.target);
-                  }}></iframe>
-              ) : type.value == 'music' ? (
-                <iframe
-                  src={params.value.src}
-                  onLoad={(val: any) => {
-                    iframeDislableKeyboard(val.target);
-                  }}
-                  style={{ height: '100vh' }}></iframe>
-              ) : (
-                ''
-              )
-            ) : (
-              ''
-            )}
-          </>
-        </NModal>
-      </>
-    );
-  }
-});
+import {
+  PropType,
+  defineComponent,
+  onMounted,
+  onUnmounted,
+  ref,
+  toRefs,
+  watch
+} from 'vue';
+import styles from './index.module.less';
+import { NModal } from 'naive-ui';
+import AttendClass from '../attend-class';
+import { iframeDislableKeyboard } from '/src/utils';
+import { modalClickMask } from '/src/state';
+
+export default defineComponent({
+  name: 'preview-window',
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    },
+    type: {
+      type: String,
+      default: ''
+    },
+    params: {
+      type: Object as PropType<any>,
+      default: () => ({})
+    }
+  },
+  emit: ['update:show'],
+  setup(props, { emit }) {
+    const { show, type, params } = toRefs(props);
+    watch(
+      () => props.show,
+      () => {
+        show.value = props.show;
+      }
+    );
+
+    watch(
+      () => props.type,
+      () => {
+        type.value = props.type;
+      }
+    );
+
+    watch(
+      () => props.params,
+      () => {
+        params.value = props.params;
+      }
+    );
+
+    const handleOpen = (e: MessageEvent) => {
+      if (e.data.api === 'iframe_exit') {
+        emit('update:show', false);
+      }
+    };
+
+    onMounted(() => {
+      window.addEventListener('message', handleOpen);
+    });
+    onUnmounted(() => {
+      window.removeEventListener('message', handleOpen);
+    });
+    return () => (
+      <>
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={show.value}
+          onUpdate:show={() => {
+            emit('update:show', show.value);
+          }}
+          class={styles.previewWindow}
+          showIcon={false}
+          displayDirective="show">
+          <>
+            {show.value ? (
+              type.value == 'attend' ? (
+                <AttendClass
+                  type={params.value.type || ''}
+                  instrumentId={params.value.instrumentId || ''}
+                  courseId={params.value.courseId || ''}
+                  detailId={params.value.detailId || ''}
+                  classGroupId={params.value.classGroupId || ''}
+                  lessonCourseId={params.value.lessonCourseId || ''}
+                  classId={params.value.classId || ''}
+                  preStudentNum={params.value.preStudentNum}
+                  onClose={() => emit('update:show', false)}
+                />
+              ) : type.value == 'notation' ? (
+                <iframe
+                  src={params.value.src}
+                  onLoad={(val: any) => {
+                    iframeDislableKeyboard(val.target);
+                  }}></iframe>
+              ) : type.value == 'music' ? (
+                <iframe
+                  src={params.value.src}
+                  onLoad={(val: any) => {
+                    iframeDislableKeyboard(val.target);
+                  }}
+                  style={{ height: '100vh' }}></iframe>
+              ) : (
+                ''
+              )
+            ) : (
+              ''
+            )}
+          </>
+        </NModal>
+      </>
+    );
+  }
+});

+ 308 - 306
src/views/setting/components/personInfo.tsx

@@ -1,306 +1,308 @@
-import { defineComponent, nextTick, onMounted, reactive, ref } from 'vue';
-import styles from '../index.module.less';
-import {
-  NImage,
-  NForm,
-  NFormItem,
-  NInput,
-  NGrid,
-  NGi,
-  NButton,
-  NSelect,
-  NSpace,
-  SelectOption,
-  useMessage,
-  NModal,
-  NCalendar,
-  NCascader
-} from 'naive-ui';
-import headerD from '../images/headerD.png';
-import defultHeade from '@/components/layout/images/teacherIcon.png';
-import maleIcon from '../images/maleIcon.png';
-import femaleIcon from '../images/femaleIcon.png';
-import { useUserStore } from '/src/store/modules/users';
-import { api_teacherUpdate } from '/src/api/user';
-import UploadFile from '/src/components/upload-file';
-import ForgotPassword from '../modal/forgotPassword';
-import { api_sysAreaQueryAllProvince } from '../api';
-export default defineComponent({
-  name: 'setting-personInfo',
-  setup() {
-    const message = useMessage();
-    const userStore = useUserStore();
-    const formOptions = reactive({
-      sexs: [
-        { label: '男', value: 1, class: 'option' },
-        { label: '女', value: 0, class: 'option' }
-      ] as SelectOption[],
-      areaList: [] as any[]
-    });
-    const formRef = ref();
-    const teacherForm = reactive({
-      provinceCode: userStore.info.provinceCode, // 省份编码
-      cityCode: userStore.info.cityCode, // 城市编码
-      regionCode: userStore.info.regionCode, // 区域编码
-      nickname: userStore.info.nickname,
-      phone: userStore.info.phone,
-      gender: userStore.info.gender,
-      schoolId: userStore.info.schoolInfos?.[0]?.id,
-      tenantId: userStore.info.schoolInfos?.[0]?.tenantId,
-      id: userStore.info.id,
-      avatar: userStore.info.avatar
-    });
-    const data = reactive({
-      disabled: true,
-      openChangePwd: false,
-      uploadShow: true,
-      loading: false,
-      oldTecherform: {} as any
-    });
-
-    const getAreaList = async () => {
-      const res = await api_sysAreaQueryAllProvince();
-      if (res?.code === 200) {
-        formOptions.areaList = res.data;
-      }
-    };
-    onMounted(() => {
-      getAreaList();
-    });
-
-    const handleSave = () => {
-      formRef.value.validate(async (err: any) => {
-        if (err) {
-          return;
-        }
-        if (!teacherForm.provinceCode || !teacherForm.cityCode) {
-          message.error('请选择城区');
-          return;
-        }
-        await api_teacherUpdate(teacherForm);
-        console.log(teacherForm);
-        userStore.getInfo();
-        data.disabled = true;
-        message.success('修改成功');
-      });
-    };
-    return () => (
-      <div class={styles.infoWrap}>
-        <div class={styles.teacherInfoWrap}>
-          <div class={styles.teacherHeadWrap}>
-            <NImage
-              previewDisabled
-              class={styles.headerD}
-              src={headerD}></NImage>
-            <NImage
-              previewDisabled
-              class={styles.defultHeade}
-              src={teacherForm.avatar || defultHeade}></NImage>
-            <div
-              // style={{ display: data.disabled ? 'none' : '' }}
-              class={[
-                styles.defultHeade,
-                styles.changeHead,
-                data.disabled ? styles.disalbedNone : styles.hoverNone
-              ]}>
-              修改头像
-              {data.uploadShow && (
-                <UploadFile
-                  class={[styles.uploadFile]}
-                  cropper
-                  onUpdate:fileList={val => {
-                    teacherForm.avatar = val;
-                    data.uploadShow = false;
-                    setTimeout(() => {
-                      data.uploadShow = true;
-                    }, 100);
-                  }}
-                />
-              )}
-            </div>
-          </div>
-          <div class={styles.headerInfo}>
-            <p class={styles.headerTitle}>
-              {userStore.info.nickname}
-              {userStore.info.gender !== null && (
-                <NImage
-                  previewDisabled
-                  class={styles.sexIcon}
-                  src={userStore.info.gender ? maleIcon : femaleIcon}></NImage>
-              )}
-            </p>
-            <p class={styles.headerSubTitle}>
-              {userStore.info.schoolInfos?.[0]?.name}
-              {/* | 音乐老师 */}
-            </p>
-          </div>
-        </div>
-        <div class={styles.setInfo}>
-          <NForm ref={formRef} model={teacherForm} disabled={data.disabled}>
-            <NGrid cols={3} x-gap="100">
-              <NGi>
-                <NFormItem
-                  label="姓名"
-                  path="nickname"
-                  rule={[
-                    {
-                      required: true,
-                      message: '请输入老师姓名',
-                      trigger: 'blur'
-                    },
-                    {
-                      pattern: /^(?:[\u4e00-\u9fa5·]{2,16})$/,
-                      message: '请输入中文姓名,不要包含空格,及特殊符号',
-                      trigger: 'blur'
-                    }
-                  ]}>
-                  <NInput
-                    maxlength={14}
-                    bordered={!data.disabled}
-                    placeholder="请填写老师姓名"
-                    v-model:value={teacherForm.nickname}></NInput>
-                </NFormItem>
-              </NGi>
-              <NGi>
-                <NFormItem
-                  label="手机号"
-                  path="phone"
-                  rule={[
-                    {
-                      required: true,
-                      message: '请填写老师手机号',
-                      trigger: 'blur'
-                    },
-                    {
-                      pattern: /^1[3456789]\d{9}$/,
-                      message: '手机号格式不正确',
-                      trigger: 'blur'
-                    }
-                  ]}>
-                  <NInput
-                    bordered={!data.disabled}
-                    placeholder="请填写老师手机号"
-                    v-model:value={teacherForm.phone}></NInput>
-                </NFormItem>
-              </NGi>
-              <NGi>
-                <NFormItem
-                  label="性别"
-                  path="gender"
-                  rule={[
-                    {
-                      required: true,
-                      type: 'number',
-                      message: '请选择性别',
-                      trigger: 'blur'
-                    }
-                  ]}>
-                  <NSelect
-                    bordered={!data.disabled}
-                    class={styles.select}
-                    showArrow={!data.disabled}
-                    placeholder="请选择性别"
-                    options={formOptions.sexs}
-                    v-model:value={teacherForm.gender}
-                  />
-                </NFormItem>
-              </NGi>
-              <NGi>
-                <NFormItem
-                  label="城区"
-                  path="provinceCode"
-                  rule={{
-                    required: true,
-                    type: 'number',
-                    message: '请选择城区',
-                    trigger: 'change'
-                  }}>
-                  {!data.loading && (
-                    <NCascader
-                      bordered={!data.disabled}
-                      options={formOptions.areaList}
-                      labelField="name"
-                      valueField="code"
-                      childrenField="areas"
-                      checkStrategy="child"
-                      expandTrigger="hover"
-                      defaultValue={
-                        userStore.info.regionCode ||
-                        userStore.info.cityCode ||
-                        userStore.info.provinceCode
-                      }
-                      onUpdate:value={(
-                        val: any,
-                        option: any,
-                        pathValues: any
-                      ) => {
-                        teacherForm.provinceCode = pathValues[0]?.code;
-                        teacherForm.cityCode = pathValues[1]?.code;
-                        teacherForm.regionCode = pathValues[2]?.code;
-                      }}
-                    />
-                  )}
-                </NFormItem>
-              </NGi>
-            </NGrid>
-          </NForm>
-        </div>
-        {data.disabled ? (
-          <NSpace class={styles.btnList} align="center" justify="end">
-            <NButton
-              class={styles.btn}
-              color="#198cfe"
-              onClick={() => (data.openChangePwd = true)}>
-              修改密码
-            </NButton>
-            <NButton
-              class={styles.btn}
-              color="#f24433"
-              onClick={() => {
-                data.oldTecherform = Object.assign({}, teacherForm);
-                data.disabled = false;
-              }}>
-              修改信息
-            </NButton>
-          </NSpace>
-        ) : (
-          <NSpace class={styles.btnList} align="center" justify="end">
-            <NButton
-              class={styles.btn}
-              onClick={() => {
-                data.loading = true;
-                userStore.getInfo();
-                formRef.value?.restoreValidation();
-                data.disabled = true;
-                Object.assign(teacherForm, data.oldTecherform);
-                nextTick(() => {
-                  data.loading = false;
-                });
-              }}>
-              取消
-            </NButton>
-            <NButton
-              class={styles.btn}
-              type="primary"
-              onClick={() => handleSave()}>
-              完成
-            </NButton>
-          </NSpace>
-        )}
-        <NModal
-          class={styles.changePwdModal}
-          v-model:show={data.openChangePwd}
-          preset="dialog"
-          showIcon={false}
-          title="修改密码">
-          <ForgotPassword
-            phone={userStore.info.phone}
-            onClose={() => {
-              data.openChangePwd = false;
-            }}
-          />
-        </NModal>
-      </div>
-    );
-  }
-});
+import { defineComponent, nextTick, onMounted, reactive, ref } from 'vue';
+import styles from '../index.module.less';
+import {
+  NImage,
+  NForm,
+  NFormItem,
+  NInput,
+  NGrid,
+  NGi,
+  NButton,
+  NSelect,
+  NSpace,
+  SelectOption,
+  useMessage,
+  NModal,
+  NCalendar,
+  NCascader
+} from 'naive-ui';
+import headerD from '../images/headerD.png';
+import defultHeade from '@/components/layout/images/teacherIcon.png';
+import maleIcon from '../images/maleIcon.png';
+import femaleIcon from '../images/femaleIcon.png';
+import { useUserStore } from '/src/store/modules/users';
+import { api_teacherUpdate } from '/src/api/user';
+import UploadFile from '/src/components/upload-file';
+import ForgotPassword from '../modal/forgotPassword';
+import { api_sysAreaQueryAllProvince } from '../api';
+import { modalClickMask } from '/src/state';
+export default defineComponent({
+  name: 'setting-personInfo',
+  setup() {
+    const message = useMessage();
+    const userStore = useUserStore();
+    const formOptions = reactive({
+      sexs: [
+        { label: '男', value: 1, class: 'option' },
+        { label: '女', value: 0, class: 'option' }
+      ] as SelectOption[],
+      areaList: [] as any[]
+    });
+    const formRef = ref();
+    const teacherForm = reactive({
+      provinceCode: userStore.info.provinceCode, // 省份编码
+      cityCode: userStore.info.cityCode, // 城市编码
+      regionCode: userStore.info.regionCode, // 区域编码
+      nickname: userStore.info.nickname,
+      phone: userStore.info.phone,
+      gender: userStore.info.gender,
+      schoolId: userStore.info.schoolInfos?.[0]?.id,
+      tenantId: userStore.info.schoolInfos?.[0]?.tenantId,
+      id: userStore.info.id,
+      avatar: userStore.info.avatar
+    });
+    const data = reactive({
+      disabled: true,
+      openChangePwd: false,
+      uploadShow: true,
+      loading: false,
+      oldTecherform: {} as any
+    });
+
+    const getAreaList = async () => {
+      const res = await api_sysAreaQueryAllProvince();
+      if (res?.code === 200) {
+        formOptions.areaList = res.data;
+      }
+    };
+    onMounted(() => {
+      getAreaList();
+    });
+
+    const handleSave = () => {
+      formRef.value.validate(async (err: any) => {
+        if (err) {
+          return;
+        }
+        if (!teacherForm.provinceCode || !teacherForm.cityCode) {
+          message.error('请选择城区');
+          return;
+        }
+        await api_teacherUpdate(teacherForm);
+        console.log(teacherForm);
+        userStore.getInfo();
+        data.disabled = true;
+        message.success('修改成功');
+      });
+    };
+    return () => (
+      <div class={styles.infoWrap}>
+        <div class={styles.teacherInfoWrap}>
+          <div class={styles.teacherHeadWrap}>
+            <NImage
+              previewDisabled
+              class={styles.headerD}
+              src={headerD}></NImage>
+            <NImage
+              previewDisabled
+              class={styles.defultHeade}
+              src={teacherForm.avatar || defultHeade}></NImage>
+            <div
+              // style={{ display: data.disabled ? 'none' : '' }}
+              class={[
+                styles.defultHeade,
+                styles.changeHead,
+                data.disabled ? styles.disalbedNone : styles.hoverNone
+              ]}>
+              修改头像
+              {data.uploadShow && (
+                <UploadFile
+                  class={[styles.uploadFile]}
+                  cropper
+                  onUpdate:fileList={val => {
+                    teacherForm.avatar = val;
+                    data.uploadShow = false;
+                    setTimeout(() => {
+                      data.uploadShow = true;
+                    }, 100);
+                  }}
+                />
+              )}
+            </div>
+          </div>
+          <div class={styles.headerInfo}>
+            <p class={styles.headerTitle}>
+              {userStore.info.nickname}
+              {userStore.info.gender !== null && (
+                <NImage
+                  previewDisabled
+                  class={styles.sexIcon}
+                  src={userStore.info.gender ? maleIcon : femaleIcon}></NImage>
+              )}
+            </p>
+            <p class={styles.headerSubTitle}>
+              {userStore.info.schoolInfos?.[0]?.name}
+              {/* | 音乐老师 */}
+            </p>
+          </div>
+        </div>
+        <div class={styles.setInfo}>
+          <NForm ref={formRef} model={teacherForm} disabled={data.disabled}>
+            <NGrid cols={3} x-gap="100">
+              <NGi>
+                <NFormItem
+                  label="姓名"
+                  path="nickname"
+                  rule={[
+                    {
+                      required: true,
+                      message: '请输入老师姓名',
+                      trigger: 'blur'
+                    },
+                    {
+                      pattern: /^(?:[\u4e00-\u9fa5·]{2,16})$/,
+                      message: '请输入中文姓名,不要包含空格,及特殊符号',
+                      trigger: 'blur'
+                    }
+                  ]}>
+                  <NInput
+                    maxlength={14}
+                    bordered={!data.disabled}
+                    placeholder="请填写老师姓名"
+                    v-model:value={teacherForm.nickname}></NInput>
+                </NFormItem>
+              </NGi>
+              <NGi>
+                <NFormItem
+                  label="手机号"
+                  path="phone"
+                  rule={[
+                    {
+                      required: true,
+                      message: '请填写老师手机号',
+                      trigger: 'blur'
+                    },
+                    {
+                      pattern: /^1[3456789]\d{9}$/,
+                      message: '手机号格式不正确',
+                      trigger: 'blur'
+                    }
+                  ]}>
+                  <NInput
+                    bordered={!data.disabled}
+                    placeholder="请填写老师手机号"
+                    v-model:value={teacherForm.phone}></NInput>
+                </NFormItem>
+              </NGi>
+              <NGi>
+                <NFormItem
+                  label="性别"
+                  path="gender"
+                  rule={[
+                    {
+                      required: true,
+                      type: 'number',
+                      message: '请选择性别',
+                      trigger: 'blur'
+                    }
+                  ]}>
+                  <NSelect
+                    bordered={!data.disabled}
+                    class={styles.select}
+                    showArrow={!data.disabled}
+                    placeholder="请选择性别"
+                    options={formOptions.sexs}
+                    v-model:value={teacherForm.gender}
+                  />
+                </NFormItem>
+              </NGi>
+              <NGi>
+                <NFormItem
+                  label="城区"
+                  path="provinceCode"
+                  rule={{
+                    required: true,
+                    type: 'number',
+                    message: '请选择城区',
+                    trigger: 'change'
+                  }}>
+                  {!data.loading && (
+                    <NCascader
+                      bordered={!data.disabled}
+                      options={formOptions.areaList}
+                      labelField="name"
+                      valueField="code"
+                      childrenField="areas"
+                      checkStrategy="child"
+                      expandTrigger="hover"
+                      defaultValue={
+                        userStore.info.regionCode ||
+                        userStore.info.cityCode ||
+                        userStore.info.provinceCode
+                      }
+                      onUpdate:value={(
+                        val: any,
+                        option: any,
+                        pathValues: any
+                      ) => {
+                        teacherForm.provinceCode = pathValues[0]?.code;
+                        teacherForm.cityCode = pathValues[1]?.code;
+                        teacherForm.regionCode = pathValues[2]?.code;
+                      }}
+                    />
+                  )}
+                </NFormItem>
+              </NGi>
+            </NGrid>
+          </NForm>
+        </div>
+        {data.disabled ? (
+          <NSpace class={styles.btnList} align="center" justify="end">
+            <NButton
+              class={styles.btn}
+              color="#198cfe"
+              onClick={() => (data.openChangePwd = true)}>
+              修改密码
+            </NButton>
+            <NButton
+              class={styles.btn}
+              color="#f24433"
+              onClick={() => {
+                data.oldTecherform = Object.assign({}, teacherForm);
+                data.disabled = false;
+              }}>
+              修改信息
+            </NButton>
+          </NSpace>
+        ) : (
+          <NSpace class={styles.btnList} align="center" justify="end">
+            <NButton
+              class={styles.btn}
+              onClick={() => {
+                data.loading = true;
+                userStore.getInfo();
+                formRef.value?.restoreValidation();
+                data.disabled = true;
+                Object.assign(teacherForm, data.oldTecherform);
+                nextTick(() => {
+                  data.loading = false;
+                });
+              }}>
+              取消
+            </NButton>
+            <NButton
+              class={styles.btn}
+              type="primary"
+              onClick={() => handleSave()}>
+              完成
+            </NButton>
+          </NSpace>
+        )}
+        <NModal
+          maskClosable={modalClickMask}
+          class={styles.changePwdModal}
+          v-model:show={data.openChangePwd}
+          preset="dialog"
+          showIcon={false}
+          title="修改密码">
+          <ForgotPassword
+            phone={userStore.info.phone}
+            onClose={() => {
+              data.openChangePwd = false;
+            }}
+          />
+        </NModal>
+      </div>
+    );
+  }
+});

+ 4 - 0
src/views/setting/components/schoolInfo/index.tsx

@@ -34,6 +34,7 @@ import AddteacherModel from '../../modal/addteacherModel';
 import TeacherGuide from '@/custom-plugins/guide-page/teacher-guide';
 import TheEmpty from '/src/components/TheEmpty';
 import TheMessageDialog from '/src/components/TheMessageDialog';
+import { modalClickMask } from '/src/state';
 export default defineComponent({
   name: 'school-info',
   emits: ['changeTab'],
@@ -424,6 +425,7 @@ export default defineComponent({
           data={data.dataList}></NDataTable>
 
         <NModal
+          maskClosable={modalClickMask}
           class={styles.addTeacher}
           v-model:show={data.modal}
           title="添加老师"
@@ -449,6 +451,7 @@ export default defineComponent({
         {showGuide.value ? <TeacherGuide></TeacherGuide> : null}
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={data.changeVisiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable1]}
@@ -464,6 +467,7 @@ export default defineComponent({
         </NModal>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={data.resetVisiable}
           preset="card"
           class={['modalTitle', styles.removeVisiable1]}

+ 254 - 253
src/views/setting/modal/forgotPassword.tsx

@@ -1,253 +1,254 @@
-import { defineComponent, reactive, ref } from 'vue';
-import styles from '../index.module.less';
-import openEye from '../images/openEye.png';
-import closeEye from '../images/closeEye.png';
-import {
-  useMessage,
-  NForm,
-  NFormItem,
-  NInput,
-  NButton,
-  NInputGroup,
-  NSpace,
-  NModal
-} from 'naive-ui';
-import { useRoute, useRouter } from 'vue-router';
-import { PageEnum } from '/src/enums/pageEnum';
-import { storage } from '@/utils/storage';
-import { useUserStore } from '/src/store/modules/users';
-import { sendSms } from '../../login/api';
-import { updatePassword } from '@/views/home/api';
-import SendSms from '../../login/components/sendSms';
-interface FormState {
-  mobile: string;
-  password: string;
-  grant_type: string;
-  loginType: string;
-  client_id: string;
-  client_secret: string;
-}
-
-export default defineComponent({
-  name: 'forgotPassword',
-  emits: ['close'],
-  props: {
-    phone: {
-      type: String,
-      default: ''
-    }
-  },
-  setup(props, { emit }) {
-    const formRef = ref();
-    const message = useMessage();
-    const loading = ref(false);
-    const showPwd = ref(false);
-    const showSmsClass = ref(false);
-    const userStore = useUserStore();
-    const formInline = reactive({
-      mobile: props.phone,
-      password: '',
-      code: ''
-      // isCaptcha: true
-    });
-    const isDisabledCode = ref(false);
-    const starTimer = ref(60);
-    const codeName = '发送短信';
-
-    const handleSubmit = async () => {
-      formRef.value.validate(async (errors: any) => {
-        if (!errors) {
-          message.loading('修改中...');
-          loading.value = true;
-          try {
-            await updatePassword({
-              ...formInline,
-              mobile: null,
-              clientType: 'TEACHER'
-            });
-            message.success('修改成功');
-            loading.value = false;
-            emit('close');
-            setTimeout(() => {
-              userStore.logout();
-              history.go(0);
-            }, 500);
-            return false;
-          } catch (e: any) {
-            loading.value = false;
-            message.error(e.msg);
-            return false;
-          }
-        }
-      });
-      return false;
-    };
-    const sendMessage = () => {
-      formRef.value?.validate(
-        (errors: any) => {
-          if (errors) {
-            return;
-          }
-          // checkTimeOut();
-          // sendSms({
-          //   clientId: 'cooleshow-teacher',
-          //   mobile: formInline.mobile,
-          //   type: 'PASSWORD'
-          // });
-          showSmsClass.value = true;
-        },
-        (rule: any) => {
-          return rule.key === 'a';
-        }
-      );
-    };
-
-    const checkTimeOut = () => {
-      if (isDisabledCode.value) {
-        return;
-      }
-      isDisabledCode.value = true;
-      const tiemr = setInterval(() => {
-        starTimer.value--;
-        console.log(starTimer.value);
-        if (starTimer.value <= 0) {
-          isDisabledCode.value = false;
-          clearInterval(tiemr);
-        }
-      }, 1000);
-    };
-
-    const validatePass = (rule: any, value: any, callback: any): any => {
-      // const reg = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/;
-      if (value === '' || !value) {
-        callback(new Error('请输入新密码'));
-      } else if (value.length < 8 || value.length > 20) {
-        callback(new Error('8~20位含数字、字母、特殊字符(如:%、&、#等)组合'));
-      } else {
-        callback();
-      }
-    };
-    return () => (
-      <>
-        <div class={styles.wrap}>
-          <NForm
-            ref={formRef}
-            label-placement="left"
-            size="large"
-            model={formInline}>
-            <NFormItem
-              path="mobile"
-              rule={[
-                {
-                  key: 'a',
-                  required: true,
-                  message: '请输入手机号',
-                  trigger: 'blur'
-                },
-                {
-                  key: 'a',
-                  pattern: /^1[3456789]\d{9}$/,
-                  message: '手机号格式不正确',
-                  trigger: 'blur'
-                }
-              ]}>
-              <NInput
-                readonly
-                type="text"
-                disabled={true}
-                maxlength={11}
-                v-model:value={formInline.mobile}
-                placeholder="请输入手机号"></NInput>
-            </NFormItem>
-            <NFormItem
-              path="code"
-              rule={[
-                { required: true, message: '请输入验证码', trigger: 'blur' },
-                {
-                  pattern: /^\d+$/,
-                  message: '请输入数字验证码',
-                  trigger: 'blur'
-                }
-              ]}>
-              <NInputGroup>
-                <NInput
-                  v-model:value={formInline.code}
-                  type="text"
-                  maxlength={6}
-                  placeholder="请输入验证码"
-                  class={styles.sendInput}></NInput>
-                <NButton
-                  type="primary"
-                  class={styles.sendMsg}
-                  disabled={isDisabledCode.value}
-                  bordered={false}
-                  onClick={() => sendMessage()}>
-                  {isDisabledCode.value ? starTimer.value : codeName}
-                </NButton>
-              </NInputGroup>
-            </NFormItem>
-            <NFormItem
-              path="password"
-              rule={[
-                {
-                  validator: validatePass,
-                  trigger: 'blur',
-                  required: true
-                }
-              ]}>
-              <NInput
-                v-model:value={formInline.password}
-                showPasswordOn="click"
-                placeholder="请输入密码"
-                inputProps={{ autocomplete: 'off' }}
-                class={[showPwd.value ? '' : styles['no-pwd']]}>
-                {{
-                  'password-visible-icon': () => (
-                    <img src={openEye} class={styles.pwdIcon} />
-                  ),
-                  'password-invisible-icon': () => (
-                    <img src={closeEye} class={styles.pwdIcon} />
-                  )
-                }}
-              </NInput>
-            </NFormItem>
-          </NForm>
-        </div>
-        <NSpace
-          justify="space-around"
-          style={{ width: '100%' }}
-          wrap={false}
-          wrapItem={false}>
-          <NButton
-            class={[styles.submitBtm, styles.submitForgoBtm]}
-            onClick={() => emit('close')}
-            size="large"
-            round
-            disabled={loading.value}>
-            取消
-          </NButton>
-          <NButton
-            class={[styles.submitBtm, styles.submitForgoBtm]}
-            type="primary"
-            onClick={handleSubmit}
-            size="large"
-            round
-            disabled={loading.value}>
-            确认修改
-          </NButton>
-        </NSpace>
-
-        <NModal v-model:show={showSmsClass.value}>
-          <SendSms
-            phone={formInline.mobile}
-            type="PASSWORD"
-            onClose={() => (showSmsClass.value = false)}
-            onSendCode={() => {
-              checkTimeOut();
-            }}
-          />
-        </NModal>
-      </>
-    );
-  }
-});
+import { defineComponent, reactive, ref } from 'vue';
+import styles from '../index.module.less';
+import openEye from '../images/openEye.png';
+import closeEye from '../images/closeEye.png';
+import {
+  useMessage,
+  NForm,
+  NFormItem,
+  NInput,
+  NButton,
+  NInputGroup,
+  NSpace,
+  NModal
+} from 'naive-ui';
+import { useRoute, useRouter } from 'vue-router';
+import { PageEnum } from '/src/enums/pageEnum';
+import { storage } from '@/utils/storage';
+import { useUserStore } from '/src/store/modules/users';
+import { sendSms } from '../../login/api';
+import { updatePassword } from '@/views/home/api';
+import SendSms from '../../login/components/sendSms';
+import { modalClickMask } from '/src/state';
+interface FormState {
+  mobile: string;
+  password: string;
+  grant_type: string;
+  loginType: string;
+  client_id: string;
+  client_secret: string;
+}
+
+export default defineComponent({
+  name: 'forgotPassword',
+  emits: ['close'],
+  props: {
+    phone: {
+      type: String,
+      default: ''
+    }
+  },
+  setup(props, { emit }) {
+    const formRef = ref();
+    const message = useMessage();
+    const loading = ref(false);
+    const showPwd = ref(false);
+    const showSmsClass = ref(false);
+    const userStore = useUserStore();
+    const formInline = reactive({
+      mobile: props.phone,
+      password: '',
+      code: ''
+      // isCaptcha: true
+    });
+    const isDisabledCode = ref(false);
+    const starTimer = ref(60);
+    const codeName = '发送短信';
+
+    const handleSubmit = async () => {
+      formRef.value.validate(async (errors: any) => {
+        if (!errors) {
+          message.loading('修改中...');
+          loading.value = true;
+          try {
+            await updatePassword({
+              ...formInline,
+              mobile: null,
+              clientType: 'TEACHER'
+            });
+            message.success('修改成功');
+            loading.value = false;
+            emit('close');
+            setTimeout(() => {
+              userStore.logout();
+              history.go(0);
+            }, 500);
+            return false;
+          } catch (e: any) {
+            loading.value = false;
+            message.error(e.msg);
+            return false;
+          }
+        }
+      });
+      return false;
+    };
+    const sendMessage = () => {
+      formRef.value?.validate(
+        (errors: any) => {
+          if (errors) {
+            return;
+          }
+          // checkTimeOut();
+          // sendSms({
+          //   clientId: 'cooleshow-teacher',
+          //   mobile: formInline.mobile,
+          //   type: 'PASSWORD'
+          // });
+          showSmsClass.value = true;
+        },
+        (rule: any) => {
+          return rule.key === 'a';
+        }
+      );
+    };
+
+    const checkTimeOut = () => {
+      if (isDisabledCode.value) {
+        return;
+      }
+      isDisabledCode.value = true;
+      const tiemr = setInterval(() => {
+        starTimer.value--;
+        console.log(starTimer.value);
+        if (starTimer.value <= 0) {
+          isDisabledCode.value = false;
+          clearInterval(tiemr);
+        }
+      }, 1000);
+    };
+
+    const validatePass = (rule: any, value: any, callback: any): any => {
+      // const reg = /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/;
+      if (value === '' || !value) {
+        callback(new Error('请输入新密码'));
+      } else if (value.length < 8 || value.length > 20) {
+        callback(new Error('8~20位含数字、字母、特殊字符(如:%、&、#等)组合'));
+      } else {
+        callback();
+      }
+    };
+    return () => (
+      <>
+        <div class={styles.wrap}>
+          <NForm
+            ref={formRef}
+            label-placement="left"
+            size="large"
+            model={formInline}>
+            <NFormItem
+              path="mobile"
+              rule={[
+                {
+                  key: 'a',
+                  required: true,
+                  message: '请输入手机号',
+                  trigger: 'blur'
+                },
+                {
+                  key: 'a',
+                  pattern: /^1[3456789]\d{9}$/,
+                  message: '手机号格式不正确',
+                  trigger: 'blur'
+                }
+              ]}>
+              <NInput
+                readonly
+                type="text"
+                disabled={true}
+                maxlength={11}
+                v-model:value={formInline.mobile}
+                placeholder="请输入手机号"></NInput>
+            </NFormItem>
+            <NFormItem
+              path="code"
+              rule={[
+                { required: true, message: '请输入验证码', trigger: 'blur' },
+                {
+                  pattern: /^\d+$/,
+                  message: '请输入数字验证码',
+                  trigger: 'blur'
+                }
+              ]}>
+              <NInputGroup>
+                <NInput
+                  v-model:value={formInline.code}
+                  type="text"
+                  maxlength={6}
+                  placeholder="请输入验证码"
+                  class={styles.sendInput}></NInput>
+                <NButton
+                  type="primary"
+                  class={styles.sendMsg}
+                  disabled={isDisabledCode.value}
+                  bordered={false}
+                  onClick={() => sendMessage()}>
+                  {isDisabledCode.value ? starTimer.value : codeName}
+                </NButton>
+              </NInputGroup>
+            </NFormItem>
+            <NFormItem
+              path="password"
+              rule={[
+                {
+                  validator: validatePass,
+                  trigger: 'blur',
+                  required: true
+                }
+              ]}>
+              <NInput
+                v-model:value={formInline.password}
+                showPasswordOn="click"
+                placeholder="请输入密码"
+                inputProps={{ autocomplete: 'off' }}
+                class={[showPwd.value ? '' : styles['no-pwd']]}>
+                {{
+                  'password-visible-icon': () => (
+                    <img src={openEye} class={styles.pwdIcon} />
+                  ),
+                  'password-invisible-icon': () => (
+                    <img src={closeEye} class={styles.pwdIcon} />
+                  )
+                }}
+              </NInput>
+            </NFormItem>
+          </NForm>
+        </div>
+        <NSpace
+          justify="space-around"
+          style={{ width: '100%' }}
+          wrap={false}
+          wrapItem={false}>
+          <NButton
+            class={[styles.submitBtm, styles.submitForgoBtm]}
+            onClick={() => emit('close')}
+            size="large"
+            round
+            disabled={loading.value}>
+            取消
+          </NButton>
+          <NButton
+            class={[styles.submitBtm, styles.submitForgoBtm]}
+            type="primary"
+            onClick={handleSubmit}
+            size="large"
+            round
+            disabled={loading.value}>
+            确认修改
+          </NButton>
+        </NSpace>
+
+        <NModal maskClosable={modalClickMask} v-model:show={showSmsClass.value}>
+          <SendSms
+            phone={formInline.mobile}
+            type="PASSWORD"
+            onClose={() => (showSmsClass.value = false)}
+            onSendCode={() => {
+              checkTimeOut();
+            }}
+          />
+        </NModal>
+      </>
+    );
+  }
+});

+ 322 - 320
src/views/studentList/components/baseInfo.tsx

@@ -1,320 +1,322 @@
-import { defineComponent, onMounted, reactive, ref, watch } from 'vue';
-import styles from '../index.module.less';
-import {
-  NImage,
-  NForm,
-  NFormItem,
-  NInput,
-  NGrid,
-  NGi,
-  NButton,
-  NSelect,
-  NSpace,
-  SelectOption,
-  useMessage,
-  NModal
-} from 'naive-ui';
-// import headerD from '../images/headerD.png';
-// import defultHeade from '@/components/layout/images/teacherIcon.png';
-// import maleIcon from '../images/maleIcon.png';
-// import femaleIcon from '../images/femaleIcon.png';
-// import { useUserStore } from '/src/store/modules/users';
-// import { api_teacherUpdate } from '/src/api/user';
-import { api_getCurrentGradeYear, resetStudentInfo } from '../api';
-// import UploadFile from '/src/components/upload-file';
-import { getgradeNumList, classArray } from '@/views/classList/contants';
-import { useRoute } from 'vue-router';
-export default defineComponent({
-  name: 'setting-personInfo',
-  props: {
-    studentInfo: {
-      type: Object,
-      default: () => ({
-        nickname: '',
-        currentGradeNum: '',
-        gender: null,
-        phone: '',
-        id: '',
-        currentClass: ''
-      })
-    }
-  },
-  setup(props) {
-    const route = useRoute();
-    const message = useMessage();
-    const loading = ref();
-    const formOptions = reactive({
-      sexs: [
-        { label: '男', value: 1, class: 'option' },
-        { label: '女', value: 0, class: 'option' }
-      ] as SelectOption[],
-      areaList: [] as any[]
-    });
-    const formRef = ref();
-    const showUpdate = ref(false);
-    const gradeNumList = ref(getgradeNumList());
-    const gradeYearList = ref([] as any);
-    const historyClassStudent = ref(
-      props.studentInfo.historyClassStudent || ''
-    );
-    const formatGradeNum = () => {
-      if (props.studentInfo.currentGradeNum) {
-        const index = gradeNumList.value.findIndex(
-          (item: any) => item.value === props.studentInfo.currentGradeNum
-        );
-        if (index === -1) {
-          return null;
-        } else {
-          return props.studentInfo.currentGradeNum;
-        }
-      } else {
-        return null;
-      }
-    };
-    const initGrade = (gradeYear: any) => {
-      // 判断学年数组里面是否有存在,如果没有则追加数据
-      const hasGradeYear = gradeYearList.value.findIndex(
-        (i: any) => i.value === gradeYear
-      );
-      if (hasGradeYear === -1 && gradeYear) {
-        gradeYearList.value.push({
-          label: gradeYear,
-          value: gradeYear
-        });
-      }
-    };
-    const studentForm = reactive({
-      upgradeFlag: (route.query.upgradeFlag as any) == 0 ? 1 : 0, // 是否为历史班
-      nickname: props.studentInfo.nickname as any,
-      phone: props.studentInfo.phone,
-      gender: props.studentInfo.gender,
-      id: props.studentInfo.id,
-      currentGradeNum: formatGradeNum(),
-      gradeYear: props.studentInfo.gradeYear,
-      currentClass: props.studentInfo.currentClass
-    });
-
-    const classArrayRef = ref([...classArray] as any);
-
-    const data = reactive({
-      disabled: true,
-      openChangePwd: false,
-      uploadShow: true
-    });
-
-    watch(
-      () => props.studentInfo,
-      val => {
-        studentForm.nickname = val.nickname as any;
-        studentForm.phone = val.phone;
-        studentForm.gender = val.gender;
-        studentForm.id = val.id;
-        studentForm.currentGradeNum = formatGradeNum();
-        studentForm.currentClass = val.currentClass;
-        studentForm.gradeYear = val.gradeYear;
-
-        console.log(props.studentInfo, 'props.studentInfo');
-        initGrade(val.gradeYear);
-      }
-    );
-    onMounted(async () => {
-      await getYearList();
-      initGrade(props.studentInfo.gradeYear);
-    });
-
-    const handleSave = () => {
-      loading.value = true;
-      formRef.value.validate(async (err: any) => {
-        if (err) {
-          return;
-        }
-        try {
-          await resetStudentInfo({ ...props.studentInfo, ...studentForm });
-          data.disabled = true;
-          message.success('修改成功');
-          loading.value = false;
-          showUpdate.value = false;
-        } catch (e) {
-          loading.value = false;
-        }
-      });
-    };
-    const cancel = () => {
-      studentForm.nickname = props.studentInfo.nickname;
-      studentForm.phone = props.studentInfo.phone;
-      studentForm.gender = props.studentInfo.gender;
-      studentForm.currentGradeNum = formatGradeNum();
-      studentForm.currentClass = props.studentInfo.currentClass;
-      studentForm.gradeYear = props.studentInfo.gradeYear;
-      data.disabled = true;
-    };
-
-    const getYearList = async () => {
-      try {
-        const { data } = await api_getCurrentGradeYear({});
-        const temp = [
-          {
-            label: data + 1,
-            value: data + 1
-          },
-          {
-            label: data,
-            value: data
-          }
-        ];
-        gradeYearList.value = temp;
-      } catch {
-        //
-      }
-    };
-
-    const onlyAllowNumber = (value: string) => !value || /^\d+$/.test(value);
-    return () => (
-      <div class={styles.infoWrap}>
-        <div class={styles.setInfo}>
-          <NForm ref={formRef} model={studentForm} disabled={data.disabled}>
-            <NGrid cols={4} x-gap="100">
-              <NGi>
-                <NFormItem
-                  label="姓名"
-                  path="nickname"
-                  rule={{
-                    required: true,
-                    message: '请填写学生姓名',
-                    trigger: 'blur'
-                  }}>
-                  <NInput
-                    maxlength={15}
-                    bordered={!data.disabled}
-                    placeholder="请填写学生姓名"
-                    v-model:value={studentForm.nickname}></NInput>
-                </NFormItem>
-              </NGi>
-              <NGi>
-                <NFormItem
-                  label="手机号"
-                  path="phone"
-                  rule={[
-                    {
-                      required: true,
-                      message: '请填写学生手机号',
-                      trigger: 'blur'
-                    },
-                    {
-                      pattern: /^1[3456789]\d{9}$/,
-                      message: '手机号格式不正确',
-                      trigger: 'blur'
-                    }
-                  ]}>
-                  <NInput
-                    maxlength={11}
-                    allowInput={onlyAllowNumber}
-                    bordered={!data.disabled}
-                    placeholder="请填写学生手机号"
-                    v-model:value={studentForm.phone}></NInput>
-                </NFormItem>
-              </NGi>
-              <NGi>
-                <NFormItem label="性别" path="sex">
-                  <NSelect
-                    bordered={!data.disabled}
-                    class={styles.select}
-                    showArrow={!data.disabled}
-                    placeholder="请选择性别"
-                    options={formOptions.sexs}
-                    v-model:value={studentForm.gender}
-                  />
-                </NFormItem>
-              </NGi>
-              <NGi>
-                <NFormItem label="学年" path="">
-                  <NSelect
-                    bordered={!data.disabled}
-                    class={styles.select}
-                    showArrow={!data.disabled}
-                    placeholder="请选择学年"
-                    options={gradeYearList.value as any}
-                    v-model:value={studentForm.gradeYear}
-                  />
-                </NFormItem>
-              </NGi>
-              <NGi>
-                <NFormItem label="年级" path="">
-                  <NSelect
-                    bordered={!data.disabled}
-                    class={styles.select}
-                    showArrow={!data.disabled}
-                    placeholder="请选择年级"
-                    options={gradeNumList.value as any}
-                    v-model:value={studentForm.currentGradeNum}
-                  />
-                </NFormItem>
-              </NGi>
-              <NGi>
-                <NFormItem label="班级" path="">
-                  <NSelect
-                    bordered={!data.disabled}
-                    class={styles.select}
-                    showArrow={!data.disabled}
-                    placeholder="请选择班级"
-                    options={classArrayRef.value}
-                    v-model:value={studentForm.currentClass}
-                  />
-                </NFormItem>
-              </NGi>
-            </NGrid>
-          </NForm>
-        </div>
-        {data.disabled ? (
-          !studentForm.upgradeFlag && (
-            <NSpace class={styles.btnList} align="center" justify="end">
-              <NButton
-                class={styles.btn}
-                strong
-                color="#f24433"
-                disabled={historyClassStudent.value ? true : false}
-                onClick={() => (data.disabled = false)}>
-                修改信息
-              </NButton>
-            </NSpace>
-          )
-        ) : (
-          <NSpace class={styles.btnList} align="center" justify="end">
-            <NButton class={styles.btn} onClick={() => cancel()}>
-              取消
-            </NButton>
-            <NButton
-              class={styles.btn}
-              type="primary"
-              onClick={() => (showUpdate.value = true)}>
-              完成
-            </NButton>
-          </NSpace>
-        )}
-
-        <NModal
-          v-model:show={showUpdate.value}
-          preset="card"
-          class={['modalTitle', styles.removeVisiable]}
-          title={'确认修改'}>
-          <div class={styles.studentRemove}>
-            <p>是否确认修改学员信息</p>
-
-            <NSpace class={styles.btnGroup} justify="center">
-              <NButton
-                round
-                type="primary"
-                onClick={handleSave}
-                loading={loading.value}>
-                确定
-              </NButton>
-              <NButton round onClick={() => (showUpdate.value = false)}>
-                取消
-              </NButton>
-            </NSpace>
-          </div>
-        </NModal>
-      </div>
-    );
-  }
-});
+import { defineComponent, onMounted, reactive, ref, watch } from 'vue';
+import styles from '../index.module.less';
+import {
+  NImage,
+  NForm,
+  NFormItem,
+  NInput,
+  NGrid,
+  NGi,
+  NButton,
+  NSelect,
+  NSpace,
+  SelectOption,
+  useMessage,
+  NModal
+} from 'naive-ui';
+// import headerD from '../images/headerD.png';
+// import defultHeade from '@/components/layout/images/teacherIcon.png';
+// import maleIcon from '../images/maleIcon.png';
+// import femaleIcon from '../images/femaleIcon.png';
+// import { useUserStore } from '/src/store/modules/users';
+// import { api_teacherUpdate } from '/src/api/user';
+import { api_getCurrentGradeYear, resetStudentInfo } from '../api';
+// import UploadFile from '/src/components/upload-file';
+import { getgradeNumList, classArray } from '@/views/classList/contants';
+import { useRoute } from 'vue-router';
+import { modalClickMask } from '/src/state';
+export default defineComponent({
+  name: 'setting-personInfo',
+  props: {
+    studentInfo: {
+      type: Object,
+      default: () => ({
+        nickname: '',
+        currentGradeNum: '',
+        gender: null,
+        phone: '',
+        id: '',
+        currentClass: ''
+      })
+    }
+  },
+  setup(props) {
+    const route = useRoute();
+    const message = useMessage();
+    const loading = ref();
+    const formOptions = reactive({
+      sexs: [
+        { label: '男', value: 1, class: 'option' },
+        { label: '女', value: 0, class: 'option' }
+      ] as SelectOption[],
+      areaList: [] as any[]
+    });
+    const formRef = ref();
+    const showUpdate = ref(false);
+    const gradeNumList = ref(getgradeNumList());
+    const gradeYearList = ref([] as any);
+    const historyClassStudent = ref(
+      props.studentInfo.historyClassStudent || ''
+    );
+    const formatGradeNum = () => {
+      if (props.studentInfo.currentGradeNum) {
+        const index = gradeNumList.value.findIndex(
+          (item: any) => item.value === props.studentInfo.currentGradeNum
+        );
+        if (index === -1) {
+          return null;
+        } else {
+          return props.studentInfo.currentGradeNum;
+        }
+      } else {
+        return null;
+      }
+    };
+    const initGrade = (gradeYear: any) => {
+      // 判断学年数组里面是否有存在,如果没有则追加数据
+      const hasGradeYear = gradeYearList.value.findIndex(
+        (i: any) => i.value === gradeYear
+      );
+      if (hasGradeYear === -1 && gradeYear) {
+        gradeYearList.value.push({
+          label: gradeYear,
+          value: gradeYear
+        });
+      }
+    };
+    const studentForm = reactive({
+      upgradeFlag: (route.query.upgradeFlag as any) == 0 ? 1 : 0, // 是否为历史班
+      nickname: props.studentInfo.nickname as any,
+      phone: props.studentInfo.phone,
+      gender: props.studentInfo.gender,
+      id: props.studentInfo.id,
+      currentGradeNum: formatGradeNum(),
+      gradeYear: props.studentInfo.gradeYear,
+      currentClass: props.studentInfo.currentClass
+    });
+
+    const classArrayRef = ref([...classArray] as any);
+
+    const data = reactive({
+      disabled: true,
+      openChangePwd: false,
+      uploadShow: true
+    });
+
+    watch(
+      () => props.studentInfo,
+      val => {
+        studentForm.nickname = val.nickname as any;
+        studentForm.phone = val.phone;
+        studentForm.gender = val.gender;
+        studentForm.id = val.id;
+        studentForm.currentGradeNum = formatGradeNum();
+        studentForm.currentClass = val.currentClass;
+        studentForm.gradeYear = val.gradeYear;
+
+        console.log(props.studentInfo, 'props.studentInfo');
+        initGrade(val.gradeYear);
+      }
+    );
+    onMounted(async () => {
+      await getYearList();
+      initGrade(props.studentInfo.gradeYear);
+    });
+
+    const handleSave = () => {
+      loading.value = true;
+      formRef.value.validate(async (err: any) => {
+        if (err) {
+          return;
+        }
+        try {
+          await resetStudentInfo({ ...props.studentInfo, ...studentForm });
+          data.disabled = true;
+          message.success('修改成功');
+          loading.value = false;
+          showUpdate.value = false;
+        } catch (e) {
+          loading.value = false;
+        }
+      });
+    };
+    const cancel = () => {
+      studentForm.nickname = props.studentInfo.nickname;
+      studentForm.phone = props.studentInfo.phone;
+      studentForm.gender = props.studentInfo.gender;
+      studentForm.currentGradeNum = formatGradeNum();
+      studentForm.currentClass = props.studentInfo.currentClass;
+      studentForm.gradeYear = props.studentInfo.gradeYear;
+      data.disabled = true;
+    };
+
+    const getYearList = async () => {
+      try {
+        const { data } = await api_getCurrentGradeYear({});
+        const temp = [
+          {
+            label: data + 1,
+            value: data + 1
+          },
+          {
+            label: data,
+            value: data
+          }
+        ];
+        gradeYearList.value = temp;
+      } catch {
+        //
+      }
+    };
+
+    const onlyAllowNumber = (value: string) => !value || /^\d+$/.test(value);
+    return () => (
+      <div class={styles.infoWrap}>
+        <div class={styles.setInfo}>
+          <NForm ref={formRef} model={studentForm} disabled={data.disabled}>
+            <NGrid cols={4} x-gap="100">
+              <NGi>
+                <NFormItem
+                  label="姓名"
+                  path="nickname"
+                  rule={{
+                    required: true,
+                    message: '请填写学生姓名',
+                    trigger: 'blur'
+                  }}>
+                  <NInput
+                    maxlength={15}
+                    bordered={!data.disabled}
+                    placeholder="请填写学生姓名"
+                    v-model:value={studentForm.nickname}></NInput>
+                </NFormItem>
+              </NGi>
+              <NGi>
+                <NFormItem
+                  label="手机号"
+                  path="phone"
+                  rule={[
+                    {
+                      required: true,
+                      message: '请填写学生手机号',
+                      trigger: 'blur'
+                    },
+                    {
+                      pattern: /^1[3456789]\d{9}$/,
+                      message: '手机号格式不正确',
+                      trigger: 'blur'
+                    }
+                  ]}>
+                  <NInput
+                    maxlength={11}
+                    allowInput={onlyAllowNumber}
+                    bordered={!data.disabled}
+                    placeholder="请填写学生手机号"
+                    v-model:value={studentForm.phone}></NInput>
+                </NFormItem>
+              </NGi>
+              <NGi>
+                <NFormItem label="性别" path="sex">
+                  <NSelect
+                    bordered={!data.disabled}
+                    class={styles.select}
+                    showArrow={!data.disabled}
+                    placeholder="请选择性别"
+                    options={formOptions.sexs}
+                    v-model:value={studentForm.gender}
+                  />
+                </NFormItem>
+              </NGi>
+              <NGi>
+                <NFormItem label="学年" path="">
+                  <NSelect
+                    bordered={!data.disabled}
+                    class={styles.select}
+                    showArrow={!data.disabled}
+                    placeholder="请选择学年"
+                    options={gradeYearList.value as any}
+                    v-model:value={studentForm.gradeYear}
+                  />
+                </NFormItem>
+              </NGi>
+              <NGi>
+                <NFormItem label="年级" path="">
+                  <NSelect
+                    bordered={!data.disabled}
+                    class={styles.select}
+                    showArrow={!data.disabled}
+                    placeholder="请选择年级"
+                    options={gradeNumList.value as any}
+                    v-model:value={studentForm.currentGradeNum}
+                  />
+                </NFormItem>
+              </NGi>
+              <NGi>
+                <NFormItem label="班级" path="">
+                  <NSelect
+                    bordered={!data.disabled}
+                    class={styles.select}
+                    showArrow={!data.disabled}
+                    placeholder="请选择班级"
+                    options={classArrayRef.value}
+                    v-model:value={studentForm.currentClass}
+                  />
+                </NFormItem>
+              </NGi>
+            </NGrid>
+          </NForm>
+        </div>
+        {data.disabled ? (
+          !studentForm.upgradeFlag && (
+            <NSpace class={styles.btnList} align="center" justify="end">
+              <NButton
+                class={styles.btn}
+                strong
+                color="#f24433"
+                disabled={historyClassStudent.value ? true : false}
+                onClick={() => (data.disabled = false)}>
+                修改信息
+              </NButton>
+            </NSpace>
+          )
+        ) : (
+          <NSpace class={styles.btnList} align="center" justify="end">
+            <NButton class={styles.btn} onClick={() => cancel()}>
+              取消
+            </NButton>
+            <NButton
+              class={styles.btn}
+              type="primary"
+              onClick={() => (showUpdate.value = true)}>
+              完成
+            </NButton>
+          </NSpace>
+        )}
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={showUpdate.value}
+          preset="card"
+          class={['modalTitle', styles.removeVisiable]}
+          title={'确认修改'}>
+          <div class={styles.studentRemove}>
+            <p>是否确认修改学员信息</p>
+
+            <NSpace class={styles.btnGroup} justify="center">
+              <NButton
+                round
+                type="primary"
+                onClick={handleSave}
+                loading={loading.value}>
+                确定
+              </NButton>
+              <NButton round onClick={() => (showUpdate.value = false)}>
+                取消
+              </NButton>
+            </NSpace>
+          </div>
+        </NModal>
+      </div>
+    );
+  }
+});

+ 2 - 0
src/views/studentList/components/evaluationRecords.tsx

@@ -24,6 +24,7 @@ import { useUserStore } from '/src/store/modules/users';
 import TheEmpty from '/src/components/TheEmpty';
 import { initCache, setCache } from '/src/hooks/use-async';
 import { iframeDislableKeyboard } from '/src/utils';
+import { modalClickMask } from '/src/state';
 export default defineComponent({
   name: 'student-practiceData',
   props: {
@@ -238,6 +239,7 @@ export default defineComponent({
           />
         </div>
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={payForm.detailVisiable}
           preset="card"
           class={['modalTitle background', styles.reportModel]}

+ 303 - 301
src/views/studentList/components/studentAfterWork.tsx

@@ -1,301 +1,303 @@
-import { defineComponent, onMounted, reactive, ref } from 'vue';
-import styles from '../index.module.less';
-import {
-  NButton,
-  NDataTable,
-  NForm,
-  NFormItem,
-  NModal,
-  NSpace
-} from 'naive-ui';
-import CSelect from '@/components/CSelect';
-import Pagination from '@/components/pagination';
-import { getStudentAfterWork } from '../api';
-import { useRoute } from 'vue-router';
-import CDatePicker from '/src/components/CDatePicker';
-import {
-  getNowDateAndMonday,
-  getNowDateAndSunday,
-  getTimes
-} from '/src/utils/dateFormat';
-import { trainingStatusArray } from '@/utils/searchArray';
-import StudentTraomomhDetails from '../modals/studentTraomomhDetails';
-import dayjs from 'dayjs';
-import TheEmpty from '/src/components/TheEmpty';
-import { initCache, setCache } from '/src/hooks/use-async';
-export default defineComponent({
-  name: 'student-studentList',
-  setup() {
-    const state = reactive({
-      searchForm: { keyword: '', trainingStatus: '' as any },
-      loading: false,
-      pagination: {
-        page: 1,
-        rows: 10,
-        pageTotal: 4
-      },
-      tableList: [] as any,
-      workInfo: {
-        createTime: '',
-        expireDate: '',
-        teacherAvatar: '',
-        teacherName: ''
-      },
-      detailVisiable: false,
-      activeRow: null as any,
-      index: 0
-    });
-    const timer = ref<[number, number]>([
-      // new Date('2023-01-01').getTime(),
-      getNowDateAndMonday(new Date().getTime()),
-      getNowDateAndSunday(new Date().getTime())
-    ]);
-    const TrainingDetailsRef = ref();
-    const route = useRoute();
-    // const routerList = ref([
-    //   { name: '班级管理', path: '/classList' },
-    //   { name: route.query.name, path: '/classDetail' },
-    //   { name: route.query.teacherName, path: '/afterWorkDetail' }
-    // ] as any);
-
-    const search = () => {
-      state.pagination.page = 1;
-      getList();
-      setCache({
-        current: { ...state.searchForm, timer: timer.value },
-        saveKey: 'studentDetailAfterWork'
-      });
-    };
-
-    const onReset = () => {
-      state.searchForm = { keyword: '', trainingStatus: '' as any };
-      timer.value = [
-        getNowDateAndMonday(new Date().getTime()),
-        getNowDateAndSunday(new Date().getTime())
-      ];
-      search();
-      setCache({
-        current: { ...state.searchForm, timer: timer.value },
-        saveKey: 'studentDetailAfterWork'
-      });
-    };
-
-    initCache({
-      current: { ...state.searchForm, timer: timer.value },
-      saveKey: 'studentDetailAfterWork',
-      callBack: (active: any) => {
-        state.searchForm = active;
-        timer.value = active.timer;
-      }
-    });
-    const getList = async () => {
-      state.loading = true;
-      try {
-        const res = await getStudentAfterWork({
-          studentId: route.query.studentId,
-          ...state.searchForm,
-          ...state.pagination,
-          ...getTimes(timer.value, ['startTime', 'endTime'], 'YYYY-MM-DD')
-        });
-
-        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 () => {
-    //   console.log(route.query);
-    //   try {
-    //     const res = await getWorkDetail({ trainingId: route.query.trainingId });
-    //     state.workInfo = { ...res.data };
-    //   } 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: 'teacherName'
-        },
-        {
-          title: '布置时间',
-          key: 'createTime',
-          render(row: any) {
-            return row.createTime
-              ? dayjs(row.createTime).format('YYYY-MM-DD HH:mm')
-              : '--';
-          }
-        },
-        {
-          title: '截止时间',
-          key: 'expireDate',
-          render(row: any) {
-            return row.expireDate
-              ? dayjs(row.expireDate).format('YYYY-MM-DD HH:mm')
-              : '--';
-          }
-        },
-        {
-          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} style={{ color: '#aaa' }}>
-                    未提交
-                  </p>
-                ) : null}
-                {row.trainingStatus == 'SUBMITTED' ? (
-                  <p style={{ color: '#EA4132' }} 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>
-        <div>
-          <div class={styles.searchList}>
-            <NForm label-placement="left" inline>
-              <NFormItem>
-                <CDatePicker
-                  v-model:value={timer.value}
-                  separator={'至'}
-                  type="daterange"
-                  timerValue={timer.value}></CDatePicker>
-              </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>
-            <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={'作业详情'}>
-          <StudentTraomomhDetails
-            // onNext={() => goToNext()}
-            // onPre={() => gotoPre()}
-            ref={TrainingDetailsRef}
-            onClose={() => (state.detailVisiable = false)}
-            total={state.tableList.length}
-            current={state.index}
-            activeRow={state.activeRow}></StudentTraomomhDetails>
-        </NModal>
-      </div>
-    );
-  }
-});
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from '../index.module.less';
+import {
+  NButton,
+  NDataTable,
+  NForm,
+  NFormItem,
+  NModal,
+  NSpace
+} from 'naive-ui';
+import CSelect from '@/components/CSelect';
+import Pagination from '@/components/pagination';
+import { getStudentAfterWork } from '../api';
+import { useRoute } from 'vue-router';
+import CDatePicker from '/src/components/CDatePicker';
+import {
+  getNowDateAndMonday,
+  getNowDateAndSunday,
+  getTimes
+} from '/src/utils/dateFormat';
+import { trainingStatusArray } from '@/utils/searchArray';
+import StudentTraomomhDetails from '../modals/studentTraomomhDetails';
+import dayjs from 'dayjs';
+import TheEmpty from '/src/components/TheEmpty';
+import { initCache, setCache } from '/src/hooks/use-async';
+import { modalClickMask } from '/src/state';
+export default defineComponent({
+  name: 'student-studentList',
+  setup() {
+    const state = reactive({
+      searchForm: { keyword: '', trainingStatus: '' as any },
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 4
+      },
+      tableList: [] as any,
+      workInfo: {
+        createTime: '',
+        expireDate: '',
+        teacherAvatar: '',
+        teacherName: ''
+      },
+      detailVisiable: false,
+      activeRow: null as any,
+      index: 0
+    });
+    const timer = ref<[number, number]>([
+      // new Date('2023-01-01').getTime(),
+      getNowDateAndMonday(new Date().getTime()),
+      getNowDateAndSunday(new Date().getTime())
+    ]);
+    const TrainingDetailsRef = ref();
+    const route = useRoute();
+    // const routerList = ref([
+    //   { name: '班级管理', path: '/classList' },
+    //   { name: route.query.name, path: '/classDetail' },
+    //   { name: route.query.teacherName, path: '/afterWorkDetail' }
+    // ] as any);
+
+    const search = () => {
+      state.pagination.page = 1;
+      getList();
+      setCache({
+        current: { ...state.searchForm, timer: timer.value },
+        saveKey: 'studentDetailAfterWork'
+      });
+    };
+
+    const onReset = () => {
+      state.searchForm = { keyword: '', trainingStatus: '' as any };
+      timer.value = [
+        getNowDateAndMonday(new Date().getTime()),
+        getNowDateAndSunday(new Date().getTime())
+      ];
+      search();
+      setCache({
+        current: { ...state.searchForm, timer: timer.value },
+        saveKey: 'studentDetailAfterWork'
+      });
+    };
+
+    initCache({
+      current: { ...state.searchForm, timer: timer.value },
+      saveKey: 'studentDetailAfterWork',
+      callBack: (active: any) => {
+        state.searchForm = active;
+        timer.value = active.timer;
+      }
+    });
+    const getList = async () => {
+      state.loading = true;
+      try {
+        const res = await getStudentAfterWork({
+          studentId: route.query.studentId,
+          ...state.searchForm,
+          ...state.pagination,
+          ...getTimes(timer.value, ['startTime', 'endTime'], 'YYYY-MM-DD')
+        });
+
+        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 () => {
+    //   console.log(route.query);
+    //   try {
+    //     const res = await getWorkDetail({ trainingId: route.query.trainingId });
+    //     state.workInfo = { ...res.data };
+    //   } 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: 'teacherName'
+        },
+        {
+          title: '布置时间',
+          key: 'createTime',
+          render(row: any) {
+            return row.createTime
+              ? dayjs(row.createTime).format('YYYY-MM-DD HH:mm')
+              : '--';
+          }
+        },
+        {
+          title: '截止时间',
+          key: 'expireDate',
+          render(row: any) {
+            return row.expireDate
+              ? dayjs(row.expireDate).format('YYYY-MM-DD HH:mm')
+              : '--';
+          }
+        },
+        {
+          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} style={{ color: '#aaa' }}>
+                    未提交
+                  </p>
+                ) : null}
+                {row.trainingStatus == 'SUBMITTED' ? (
+                  <p style={{ color: '#EA4132' }} 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>
+        <div>
+          <div class={styles.searchList}>
+            <NForm label-placement="left" inline>
+              <NFormItem>
+                <CDatePicker
+                  v-model:value={timer.value}
+                  separator={'至'}
+                  type="daterange"
+                  timerValue={timer.value}></CDatePicker>
+              </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>
+            <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
+          maskClosable={modalClickMask}
+          v-model:show={state.detailVisiable}
+          preset="card"
+          class={['modalTitle background', styles.wordDetailModel]}
+          title={'作业详情'}>
+          <StudentTraomomhDetails
+            // onNext={() => goToNext()}
+            // onPre={() => gotoPre()}
+            ref={TrainingDetailsRef}
+            onClose={() => (state.detailVisiable = false)}
+            total={state.tableList.length}
+            current={state.index}
+            activeRow={state.activeRow}></StudentTraomomhDetails>
+        </NModal>
+      </div>
+    );
+  }
+});

+ 528 - 526
src/views/studentList/index.tsx

@@ -1,526 +1,528 @@
-import { defineComponent, onMounted, reactive, ref } from 'vue';
-import styles from './index.module.less';
-import {
-  NButton,
-  NDataTable,
-  NForm,
-  NFormItem,
-  NImage,
-  NModal,
-  NSelect,
-  NSpace,
-  NTooltip,
-  useMessage
-} from 'naive-ui';
-import SearchInput from '@/components/searchInput';
-import CSelect from '@/components/CSelect';
-import Pagination from '@/components/pagination';
-import add from './images/add.png';
-import { useRoute, useRouter } from 'vue-router';
-import { getStudentList, schoolDetail } from './api';
-import { classGroupList } from '@/views/classList/api';
-import AddStudentModel from './modals/addStudentModel';
-import Studentguide from '@/custom-plugins/guide-page/student-guide';
-import TheEmpty from '/src/components/TheEmpty';
-// import NoticeModal from './modals/noticeModal';
-import { useUserStore } from '/src/store/modules/users';
-import UpdateStudent from './modals/update-student';
-import { initCache, setCache } from '/src/hooks/use-async';
-import { classArray, getgradeNumList } from '../classList/contants';
-import { getGradeLevelList, getGradeYearList } from '../home/api';
-export default defineComponent({
-  name: 'student-studentList',
-  setup(props, { emit }) {
-    const userStore = useUserStore();
-    const state = reactive({
-      searchForm: {
-        keyword: '',
-        gender: '' as any,
-        classGroupId: '' as any,
-        membership: '' as any,
-        currentClass: '' as any,
-        currentGradeNum: '' as any,
-        gradeYear: '' as any,
-        gradeLevel: ''
-      },
-      gradeNumList: [] as any,
-      searchWord: '',
-      orchestraType: null,
-      courseTypeCode: null,
-      subjectId: null,
-      classId: null,
-      studentType: null,
-      loading: false,
-      pagination: {
-        page: 1,
-        rows: 10,
-        pageTotal: 4
-      },
-      tableList: [] as any,
-      classList: [],
-      addStudentVisible: false,
-      editStatus: false,
-      activeRow: {} as any,
-      popSelectYearList: [] as any,
-      popSelectLevelList: [] as any
-    });
-    state.gradeNumList = getgradeNumList();
-    const route = useRoute();
-    const router = useRouter();
-    const showGuide = ref(false);
-    const message = useMessage();
-    const search = () => {
-      state.pagination.page = 1;
-      getList();
-      setCache({ current: state.searchForm, saveKey: route.path });
-    };
-    const getClasslist = async () => {
-      try {
-        const res = await classGroupList({ page: 1, rows: 999 });
-        state.classList = res.data.rows.map((item: any) => {
-          return {
-            label: item.name,
-            value: item.id
-          };
-        });
-      } catch (e) {
-        console.log(e);
-      }
-    };
-
-    const copyTo = (text: string) => {
-      const input = document.createElement('input');
-      input.value = text;
-      document.body.appendChild(input);
-      input.select();
-      input.setSelectionRange(0, input.value.length);
-      document.execCommand('Copy');
-      document.body.removeChild(input);
-      message.success('复制成功');
-    };
-    const onReset = () => {
-      state.searchForm = {
-        keyword: '',
-        gender: '' as any,
-        classGroupId: '' as any,
-        membership: '' as any,
-        currentClass: '' as any,
-        currentGradeNum: '' as any,
-        gradeYear: '' as any,
-        gradeLevel: ''
-      };
-      if (state.popSelectYearList.length > 1) {
-        state.searchForm.gradeYear = state.popSelectYearList[1].id;
-      }
-      search();
-      setCache({ current: state.searchForm, saveKey: route.path });
-    };
-
-    initCache({
-      current: state.searchForm,
-      callBack: (active: any) => {
-        state.searchForm = active;
-      }
-    });
-    const getList = async () => {
-      try {
-        const res = await getStudentList({
-          ...state.searchForm,
-          ...state.pagination
-        });
-        state.tableList = res.data.rows;
-        state.pagination.pageTotal = res.data.total;
-        if (state.tableList.length > 0) {
-          setTimeout(() => {
-            showGuide.value = true;
-          }, 500);
-        }
-      } catch (e) {
-        console.log(e);
-      }
-      console.log('getList');
-    };
-
-    // 获取学年
-    const getYearList = async () => {
-      try {
-        const { data } = await getGradeYearList();
-        const temp = data || [];
-        temp.forEach((i: any) => {
-          i.name = i.name + '学年';
-        });
-        // temp.unshift({
-        //   id: '',
-        //   name: '全部学年'
-        // });
-        state.popSelectYearList = temp || [];
-        if (temp.length > 1 && !state.searchForm.gradeYear) {
-          state.searchForm.gradeYear = temp[1].id;
-        }
-      } catch {
-        //
-      }
-    };
-    // 获取学级
-    const getLevelList = async () => {
-      try {
-        const { data } = await getGradeLevelList();
-        const temp = data || [];
-        temp.forEach((i: any) => {
-          i.name = i.name + '级';
-        });
-        temp.unshift({
-          id: '',
-          name: '全部学级'
-        });
-        state.popSelectLevelList = temp || [];
-        if (temp.length > 0 && !state.searchForm.gradeLevel) {
-          state.searchForm.gradeLevel = temp[0].id;
-        }
-      } catch {
-        //
-      }
-    };
-
-    onMounted(async () => {
-      state.loading = true;
-      await getYearList();
-      await getLevelList();
-      await getList();
-      await getClasslist();
-      state.loading = false;
-    });
-    const columns = () => {
-      return [
-        {
-          title: '学生姓名',
-          key: 'nickname',
-          render: (row: any) => {
-            return (
-              <NTooltip showArrow={false} placement="top-start">
-                {{
-                  trigger: () => (
-                    <div
-                      style={{ userSelect: 'all', cursor: 'pointer' }}
-                      onClick={() => copyTo(row.nickname)}>
-                      {row.nickname}
-                    </div>
-                  ),
-                  default: '点击复制'
-                }}
-              </NTooltip>
-            );
-          }
-        },
-        {
-          title: '手机号',
-          key: 'phone',
-          render: (row: any) => {
-            return (
-              <NTooltip showArrow={false} placement="top-start">
-                {{
-                  trigger: () => (
-                    <div
-                      style={{ userSelect: 'all', cursor: 'pointer' }}
-                      onClick={() => copyTo(row.phone)}>
-                      {row.phone}
-                    </div>
-                  ),
-                  default: '点击复制'
-                }}
-              </NTooltip>
-            );
-          }
-        },
-        {
-          title: '性别',
-          key: 'gender',
-          render(row: any) {
-            return (
-              <>
-                {row.gender + '' != 'null'
-                  ? row.gender == '0'
-                    ? '女'
-                    : '男'
-                  : '--'}
-              </>
-            );
-          }
-        },
-        {
-          title: '年级班级',
-          key: 'classGroupName'
-        },
-
-        {
-          title: '学生类型',
-          key: 'vipMember',
-          render(row: any) {
-            return <>{row.vipMember ? '会员' : '普通'}</>;
-          }
-        },
-        {
-          title: '操作',
-          key: 'id',
-          width: 300,
-          render(row: any, index: number) {
-            return (
-              <NSpace>
-                {index == 0 ? (
-                  <NButton
-                    {...{ id: 'student-1' }}
-                    text
-                    type="primary"
-                    onClick={() => gotoDetail(row)}>
-                    详情
-                  </NButton>
-                ) : (
-                  <NButton text type="primary" onClick={() => gotoDetail(row)}>
-                    详情
-                  </NButton>
-                )}
-                <NButton
-                  text
-                  type="primary"
-                  onClick={() => onUpdate(row)}
-                  disabled={row.historyClassStudent}>
-                  修改
-                </NButton>
-              </NSpace>
-            );
-          }
-        }
-      ];
-    };
-
-    const gotoDetail = (row: any) => {
-      router.push({
-        path: '/studentDetail',
-        query: { ...route.query, studentId: row.id, studentName: row.nickname }
-      });
-    };
-
-    // 修改
-    const onUpdate = (row: any) => {
-      state.editStatus = true;
-      state.activeRow = row;
-    };
-    return () => (
-      <div class={styles.listWrap}>
-        <div class={styles.searchList}>
-          <NForm label-placement="left" inline>
-            <NFormItem>
-              <SearchInput
-                {...{ placeholder: '请输入学生姓名' }}
-                class={styles.searchInput}
-                style={{ width: '160px' }}
-                searchWord={state.searchForm.keyword}
-                onChangeValue={(val: string) =>
-                  (state.searchForm.keyword = val)
-                }></SearchInput>
-            </NFormItem>
-
-            <NFormItem>
-              <CSelect
-                {...({
-                  options: [
-                    {
-                      label: '全部性别',
-                      value: ''
-                    },
-                    {
-                      label: '男',
-                      value: 1
-                    },
-                    {
-                      label: '女',
-                      value: 0
-                    }
-                  ],
-                  placeholder: '性别',
-                  clearable: true,
-                  inline: true
-                } as any)}
-                v-model:value={state.searchForm.gender}></CSelect>
-            </NFormItem>
-            <NFormItem>
-              <CSelect
-                {...({
-                  options: state.popSelectYearList,
-                  placeholder: '选择学年',
-                  clearable: false,
-                  inline: true,
-                  labelField: 'name',
-                  valueField: 'id'
-                } as any)}
-                v-model:value={state.searchForm.gradeYear}></CSelect>
-            </NFormItem>
-            <NFormItem>
-              <CSelect
-                {...({
-                  options: state.popSelectLevelList,
-                  placeholder: '选择学级',
-                  clearable: true,
-                  inline: true,
-                  labelField: 'name',
-                  valueField: 'id'
-                } as any)}
-                v-model:value={state.searchForm.gradeLevel}></CSelect>
-            </NFormItem>
-
-            <NFormItem>
-              <CSelect
-                {...({
-                  options: state.gradeNumList,
-                  placeholder: '选择年级',
-                  clearable: true,
-                  inline: true
-                } as any)}
-                v-model:value={state.searchForm.currentGradeNum}></CSelect>
-            </NFormItem>
-            <NFormItem>
-              <CSelect
-                {...({
-                  options: classArray,
-                  placeholder: '选择班级',
-                  clearable: true,
-                  inline: true
-                } as any)}
-                v-model:value={state.searchForm.currentClass}></CSelect>
-            </NFormItem>
-            {/* <NFormItem>
-              <CSelect
-                {...({
-                  options: [
-                    { label: '全部年级班级', value: '' },
-                    ...state.classList
-                  ],
-                  placeholder: '年级班级',
-                  clearable: true,
-                  inline: true
-                } as any)}
-                v-model:value={state.searchForm.classGroupId}></CSelect>
-            </NFormItem> */}
-            <NFormItem>
-              <CSelect
-                {...({
-                  options: [
-                    {
-                      label: '全部类型',
-                      value: ''
-                    },
-                    {
-                      label: '会员',
-                      value: true
-                    },
-                    {
-                      label: '普通',
-                      value: false
-                    }
-                  ],
-                  placeholder: '学生类型',
-                  clearable: true,
-                  inline: true
-                } as any)}
-                v-model:value={state.searchForm.membership}></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>
-        {/* <NButton
-          {...{ id: 'student-0' }}
-          onClick={async () => {
-            // state.addStudentVisible = true;
-            try {
-              const { schoolInfos } = userStore.getUserInfo;
-              const schoolId =
-                schoolInfos.length > 0 ? schoolInfos[0].id : null;
-              if (schoolId) {
-                const { data } = await schoolDetail({ id: schoolId });
-                state.activeRow = data;
-
-                state.addStudentVisible = true;
-              }
-            } catch {
-              //
-            }
-          }}
-          class={styles.addBtn}
-          type="primary"
-          v-slots={{
-            icon: () => (
-              <>
-                <NImage
-                  class={styles.addBtnIcon}
-                  previewDisabled
-                  src={add}></NImage>
-              </>
-            )
-          }}>
-          邀请学生
-        </NButton> */}
-        <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>
-        {/* {state.addStudentVisible ? (
-          <div v-model:show={state.addStudentVisible} class="n-modal-mask">
-            <AddStudentModel
-              activeRow={state.activeRow}
-              onClose={() => {
-                state.addStudentVisible = false;
-              }}></AddStudentModel>
-          </div>
-        ) : null} */}
-        {/* <NModal
-          v-model:show={state.addStudentVisible}
-          showIcon={false}
-          style={{ width: '400px' }}>
-          <NoticeModal
-            data={state.activeRow}
-            onClose={() => (state.addStudentVisible = false)}
-          />
-        </NModal> */}
-
-        <NModal
-          v-model:show={state.editStatus}
-          class={['modalTitle background', styles.updateStudent]}
-          preset="card"
-          title="修改信息">
-          <UpdateStudent
-            onClose={() => (state.editStatus = false)}
-            onConfirm={() => getList()}
-            row={state.activeRow}
-          />
-        </NModal>
-        {showGuide.value ? <Studentguide></Studentguide> : null}
-      </div>
-    );
-  }
-});
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from './index.module.less';
+import {
+  NButton,
+  NDataTable,
+  NForm,
+  NFormItem,
+  NImage,
+  NModal,
+  NSelect,
+  NSpace,
+  NTooltip,
+  useMessage
+} from 'naive-ui';
+import SearchInput from '@/components/searchInput';
+import CSelect from '@/components/CSelect';
+import Pagination from '@/components/pagination';
+import add from './images/add.png';
+import { useRoute, useRouter } from 'vue-router';
+import { getStudentList, schoolDetail } from './api';
+import { classGroupList } from '@/views/classList/api';
+import AddStudentModel from './modals/addStudentModel';
+import Studentguide from '@/custom-plugins/guide-page/student-guide';
+import TheEmpty from '/src/components/TheEmpty';
+// import NoticeModal from './modals/noticeModal';
+import { useUserStore } from '/src/store/modules/users';
+import UpdateStudent from './modals/update-student';
+import { initCache, setCache } from '/src/hooks/use-async';
+import { classArray, getgradeNumList } from '../classList/contants';
+import { getGradeLevelList, getGradeYearList } from '../home/api';
+import { modalClickMask } from '/src/state';
+export default defineComponent({
+  name: 'student-studentList',
+  setup(props, { emit }) {
+    const userStore = useUserStore();
+    const state = reactive({
+      searchForm: {
+        keyword: '',
+        gender: '' as any,
+        classGroupId: '' as any,
+        membership: '' as any,
+        currentClass: '' as any,
+        currentGradeNum: '' as any,
+        gradeYear: '' as any,
+        gradeLevel: ''
+      },
+      gradeNumList: [] as any,
+      searchWord: '',
+      orchestraType: null,
+      courseTypeCode: null,
+      subjectId: null,
+      classId: null,
+      studentType: null,
+      loading: false,
+      pagination: {
+        page: 1,
+        rows: 10,
+        pageTotal: 4
+      },
+      tableList: [] as any,
+      classList: [],
+      addStudentVisible: false,
+      editStatus: false,
+      activeRow: {} as any,
+      popSelectYearList: [] as any,
+      popSelectLevelList: [] as any
+    });
+    state.gradeNumList = getgradeNumList();
+    const route = useRoute();
+    const router = useRouter();
+    const showGuide = ref(false);
+    const message = useMessage();
+    const search = () => {
+      state.pagination.page = 1;
+      getList();
+      setCache({ current: state.searchForm, saveKey: route.path });
+    };
+    const getClasslist = async () => {
+      try {
+        const res = await classGroupList({ page: 1, rows: 999 });
+        state.classList = res.data.rows.map((item: any) => {
+          return {
+            label: item.name,
+            value: item.id
+          };
+        });
+      } catch (e) {
+        console.log(e);
+      }
+    };
+
+    const copyTo = (text: string) => {
+      const input = document.createElement('input');
+      input.value = text;
+      document.body.appendChild(input);
+      input.select();
+      input.setSelectionRange(0, input.value.length);
+      document.execCommand('Copy');
+      document.body.removeChild(input);
+      message.success('复制成功');
+    };
+    const onReset = () => {
+      state.searchForm = {
+        keyword: '',
+        gender: '' as any,
+        classGroupId: '' as any,
+        membership: '' as any,
+        currentClass: '' as any,
+        currentGradeNum: '' as any,
+        gradeYear: '' as any,
+        gradeLevel: ''
+      };
+      if (state.popSelectYearList.length > 1) {
+        state.searchForm.gradeYear = state.popSelectYearList[1].id;
+      }
+      search();
+      setCache({ current: state.searchForm, saveKey: route.path });
+    };
+
+    initCache({
+      current: state.searchForm,
+      callBack: (active: any) => {
+        state.searchForm = active;
+      }
+    });
+    const getList = async () => {
+      try {
+        const res = await getStudentList({
+          ...state.searchForm,
+          ...state.pagination
+        });
+        state.tableList = res.data.rows;
+        state.pagination.pageTotal = res.data.total;
+        if (state.tableList.length > 0) {
+          setTimeout(() => {
+            showGuide.value = true;
+          }, 500);
+        }
+      } catch (e) {
+        console.log(e);
+      }
+      console.log('getList');
+    };
+
+    // 获取学年
+    const getYearList = async () => {
+      try {
+        const { data } = await getGradeYearList();
+        const temp = data || [];
+        temp.forEach((i: any) => {
+          i.name = i.name + '学年';
+        });
+        // temp.unshift({
+        //   id: '',
+        //   name: '全部学年'
+        // });
+        state.popSelectYearList = temp || [];
+        if (temp.length > 1 && !state.searchForm.gradeYear) {
+          state.searchForm.gradeYear = temp[1].id;
+        }
+      } catch {
+        //
+      }
+    };
+    // 获取学级
+    const getLevelList = async () => {
+      try {
+        const { data } = await getGradeLevelList();
+        const temp = data || [];
+        temp.forEach((i: any) => {
+          i.name = i.name + '级';
+        });
+        temp.unshift({
+          id: '',
+          name: '全部学级'
+        });
+        state.popSelectLevelList = temp || [];
+        if (temp.length > 0 && !state.searchForm.gradeLevel) {
+          state.searchForm.gradeLevel = temp[0].id;
+        }
+      } catch {
+        //
+      }
+    };
+
+    onMounted(async () => {
+      state.loading = true;
+      await getYearList();
+      await getLevelList();
+      await getList();
+      await getClasslist();
+      state.loading = false;
+    });
+    const columns = () => {
+      return [
+        {
+          title: '学生姓名',
+          key: 'nickname',
+          render: (row: any) => {
+            return (
+              <NTooltip showArrow={false} placement="top-start">
+                {{
+                  trigger: () => (
+                    <div
+                      style={{ userSelect: 'all', cursor: 'pointer' }}
+                      onClick={() => copyTo(row.nickname)}>
+                      {row.nickname}
+                    </div>
+                  ),
+                  default: '点击复制'
+                }}
+              </NTooltip>
+            );
+          }
+        },
+        {
+          title: '手机号',
+          key: 'phone',
+          render: (row: any) => {
+            return (
+              <NTooltip showArrow={false} placement="top-start">
+                {{
+                  trigger: () => (
+                    <div
+                      style={{ userSelect: 'all', cursor: 'pointer' }}
+                      onClick={() => copyTo(row.phone)}>
+                      {row.phone}
+                    </div>
+                  ),
+                  default: '点击复制'
+                }}
+              </NTooltip>
+            );
+          }
+        },
+        {
+          title: '性别',
+          key: 'gender',
+          render(row: any) {
+            return (
+              <>
+                {row.gender + '' != 'null'
+                  ? row.gender == '0'
+                    ? '女'
+                    : '男'
+                  : '--'}
+              </>
+            );
+          }
+        },
+        {
+          title: '年级班级',
+          key: 'classGroupName'
+        },
+
+        {
+          title: '学生类型',
+          key: 'vipMember',
+          render(row: any) {
+            return <>{row.vipMember ? '会员' : '普通'}</>;
+          }
+        },
+        {
+          title: '操作',
+          key: 'id',
+          width: 300,
+          render(row: any, index: number) {
+            return (
+              <NSpace>
+                {index == 0 ? (
+                  <NButton
+                    {...{ id: 'student-1' }}
+                    text
+                    type="primary"
+                    onClick={() => gotoDetail(row)}>
+                    详情
+                  </NButton>
+                ) : (
+                  <NButton text type="primary" onClick={() => gotoDetail(row)}>
+                    详情
+                  </NButton>
+                )}
+                <NButton
+                  text
+                  type="primary"
+                  onClick={() => onUpdate(row)}
+                  disabled={row.historyClassStudent}>
+                  修改
+                </NButton>
+              </NSpace>
+            );
+          }
+        }
+      ];
+    };
+
+    const gotoDetail = (row: any) => {
+      router.push({
+        path: '/studentDetail',
+        query: { ...route.query, studentId: row.id, studentName: row.nickname }
+      });
+    };
+
+    // 修改
+    const onUpdate = (row: any) => {
+      state.editStatus = true;
+      state.activeRow = row;
+    };
+    return () => (
+      <div class={styles.listWrap}>
+        <div class={styles.searchList}>
+          <NForm label-placement="left" inline>
+            <NFormItem>
+              <SearchInput
+                {...{ placeholder: '请输入学生姓名' }}
+                class={styles.searchInput}
+                style={{ width: '160px' }}
+                searchWord={state.searchForm.keyword}
+                onChangeValue={(val: string) =>
+                  (state.searchForm.keyword = val)
+                }></SearchInput>
+            </NFormItem>
+
+            <NFormItem>
+              <CSelect
+                {...({
+                  options: [
+                    {
+                      label: '全部性别',
+                      value: ''
+                    },
+                    {
+                      label: '男',
+                      value: 1
+                    },
+                    {
+                      label: '女',
+                      value: 0
+                    }
+                  ],
+                  placeholder: '性别',
+                  clearable: true,
+                  inline: true
+                } as any)}
+                v-model:value={state.searchForm.gender}></CSelect>
+            </NFormItem>
+            <NFormItem>
+              <CSelect
+                {...({
+                  options: state.popSelectYearList,
+                  placeholder: '选择学年',
+                  clearable: false,
+                  inline: true,
+                  labelField: 'name',
+                  valueField: 'id'
+                } as any)}
+                v-model:value={state.searchForm.gradeYear}></CSelect>
+            </NFormItem>
+            <NFormItem>
+              <CSelect
+                {...({
+                  options: state.popSelectLevelList,
+                  placeholder: '选择学级',
+                  clearable: true,
+                  inline: true,
+                  labelField: 'name',
+                  valueField: 'id'
+                } as any)}
+                v-model:value={state.searchForm.gradeLevel}></CSelect>
+            </NFormItem>
+
+            <NFormItem>
+              <CSelect
+                {...({
+                  options: state.gradeNumList,
+                  placeholder: '选择年级',
+                  clearable: true,
+                  inline: true
+                } as any)}
+                v-model:value={state.searchForm.currentGradeNum}></CSelect>
+            </NFormItem>
+            <NFormItem>
+              <CSelect
+                {...({
+                  options: classArray,
+                  placeholder: '选择班级',
+                  clearable: true,
+                  inline: true
+                } as any)}
+                v-model:value={state.searchForm.currentClass}></CSelect>
+            </NFormItem>
+            {/* <NFormItem>
+              <CSelect
+                {...({
+                  options: [
+                    { label: '全部年级班级', value: '' },
+                    ...state.classList
+                  ],
+                  placeholder: '年级班级',
+                  clearable: true,
+                  inline: true
+                } as any)}
+                v-model:value={state.searchForm.classGroupId}></CSelect>
+            </NFormItem> */}
+            <NFormItem>
+              <CSelect
+                {...({
+                  options: [
+                    {
+                      label: '全部类型',
+                      value: ''
+                    },
+                    {
+                      label: '会员',
+                      value: true
+                    },
+                    {
+                      label: '普通',
+                      value: false
+                    }
+                  ],
+                  placeholder: '学生类型',
+                  clearable: true,
+                  inline: true
+                } as any)}
+                v-model:value={state.searchForm.membership}></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>
+        {/* <NButton
+          {...{ id: 'student-0' }}
+          onClick={async () => {
+            // state.addStudentVisible = true;
+            try {
+              const { schoolInfos } = userStore.getUserInfo;
+              const schoolId =
+                schoolInfos.length > 0 ? schoolInfos[0].id : null;
+              if (schoolId) {
+                const { data } = await schoolDetail({ id: schoolId });
+                state.activeRow = data;
+
+                state.addStudentVisible = true;
+              }
+            } catch {
+              //
+            }
+          }}
+          class={styles.addBtn}
+          type="primary"
+          v-slots={{
+            icon: () => (
+              <>
+                <NImage
+                  class={styles.addBtnIcon}
+                  previewDisabled
+                  src={add}></NImage>
+              </>
+            )
+          }}>
+          邀请学生
+        </NButton> */}
+        <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>
+        {/* {state.addStudentVisible ? (
+          <div v-model:show={state.addStudentVisible} class="n-modal-mask">
+            <AddStudentModel
+              activeRow={state.activeRow}
+              onClose={() => {
+                state.addStudentVisible = false;
+              }}></AddStudentModel>
+          </div>
+        ) : null} */}
+        {/* <NModal
+          v-model:show={state.addStudentVisible}
+          showIcon={false}
+          style={{ width: '400px' }}>
+          <NoticeModal
+            data={state.activeRow}
+            onClose={() => (state.addStudentVisible = false)}
+          />
+        </NModal> */}
+
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={state.editStatus}
+          class={['modalTitle background', styles.updateStudent]}
+          preset="card"
+          title="修改信息">
+          <UpdateStudent
+            onClose={() => (state.editStatus = false)}
+            onConfirm={() => getList()}
+            row={state.activeRow}
+          />
+        </NModal>
+        {showGuide.value ? <Studentguide></Studentguide> : null}
+      </div>
+    );
+  }
+});

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

@@ -16,6 +16,7 @@ import defultHeade from '@/components/layout/images/teacherIcon.png';
 import { api_setComment } from '../../api';
 import dayjs from 'dayjs';
 import { storage } from '/src/utils/storage';
+import { modalClickMask } from '/src/state';
 
 export default defineComponent({
   name: 'commit-work',
@@ -132,6 +133,7 @@ export default defineComponent({
         </NSpace>
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={state.removeVisiable1}
           preset="card"
           class={['modalTitle', styles.removeVisiable1]}

+ 4 - 1
src/views/studentList/modals/studentTraomomhDetails.tsx

@@ -18,6 +18,7 @@ import dayjs from 'dayjs';
 import { getTrainingStudentDetail } from '../../classList/api';
 import CommentWork from './comment-work';
 import WorkItem from '../../classList/work-item';
+import { modalClickMask } from '/src/state';
 
 export default defineComponent({
   props: {
@@ -226,7 +227,9 @@ export default defineComponent({
           </NScrollbar>
         </NSpin>
 
-        <NModal v-model:show={showModalMask.value}>
+        <NModal
+          maskClosable={modalClickMask}
+          v-model:show={showModalMask.value}>
           <CommentWork
             comment={teacherInfo.value.comment}
             workInfo={{

+ 2 - 1
src/views/xiaoku-music/index.tsx

@@ -50,7 +50,7 @@ import {
 import { useUserStore } from '/src/store/modules/users';
 import Musicguide from '@/custom-plugins/guide-page/music-guide';
 import TheEmpty from '/src/components/TheEmpty';
-import { state } from '/src/state';
+import { modalClickMask, state } from '/src/state';
 import { useResizeObserver } from '@vueuse/core';
 import { vaildMusicScoreUrl } from '/src/utils/urlUtils';
 import { getInstrumentName, sortMusical, trackToCode } from '/src/utils';
@@ -903,6 +903,7 @@ export default defineComponent({
         {showGuide.value ? <Musicguide></Musicguide> : null}
 
         <NModal
+          maskClosable={modalClickMask}
           v-model:show={data.previewModal}
           onUpdate:show={(val: any) => {
             if (!val) {