Bladeren bron

考试详情页面

刘洋 9 maanden geleden
bovenliggende
commit
f170e66848
3 gewijzigde bestanden met toevoegingen van 613 en 377 verwijderingen
  1. 383 364
      src/render/components/MyModal/index.vue
  2. 7 0
      src/render/views/CurExam/AddExamDialog.vue
  3. 223 13
      src/render/views/CurExam/index.vue

+ 383 - 364
src/render/components/MyModal/index.vue

@@ -1,392 +1,411 @@
 <template>
-    <div
-      ref="modalWrapRef"
-      class="draggable-modal"
-      :class="{ fullscreen: fullscreenModel }"
+  <div
+    ref="modalWrapRef"
+    class="draggable-modal"
+    :class="{ fullscreen: fullscreenModel }"
+  >
+    <Modal
+      v-bind="omit(props, ['open', 'onCancel', 'onOk', 'onUpdate:open'])"
+      v-model:open="openModel"
+      :mask-closable="false"
+      :get-container="() => modalWrapRef"
+      :width="innerWidth || width"
+      @ok="emit('ok')"
+      @cancel="emit('cancel')"
     >
-      <Modal
-        v-bind="omit(props, ['open', 'onCancel', 'onOk', 'onUpdate:open'])"
-        v-model:open="openModel"
-        :mask-closable="false"
-        :get-container="() => modalWrapRef"
-        :width="innerWidth || width"
-        @ok="emit('ok')"
-        @cancel="emit('cancel')"
-      >
-        <template #title>
-          <slot name="title">{{ $attrs.title || "&nbsp;" }}</slot>
-        </template>
-        <template #closeIcon>
-          <slot name="closeIcon">
-            <Space v-if="showFullScreen" class="ant-modal-operate" @click.stop>
-              <FullscreenOutlined
-                v-if="!fullscreenModel"
-                @click="fullscreenModel = true"
-              />
-              <FullscreenExitOutlined v-else @click="restore" />
-              <CloseOutlined @click="closeModal" />
-            </Space>
-            <span v-else @click.stop>
-              <CloseOutlined @click="closeModal" />
-            </span>
-          </slot>
-        </template>
-        <slot>
-          ① 窗口可以拖动;<br />
-          ② 窗口可以通过八个方向改变大小;<br />
-          ③ 窗口可以最小化、最大化、还原、关闭;<br />
-          ④ 限制窗口最小宽度/高度。
+      <template #title>
+        <slot name="title">{{ $attrs.title || "&nbsp;" }}</slot>
+      </template>
+      <template #closeIcon>
+        <slot name="closeIcon">
+          <Space v-if="showFullScreen" class="ant-modal-operate" @click.stop>
+            <FullscreenOutlined
+              v-if="!fullscreenModel"
+              @click="fullscreenModel = true"
+            />
+            <FullscreenExitOutlined v-else @click="restore" />
+            <CloseOutlined @click="closeModal" />
+          </Space>
+          <span v-else @click.stop>
+            <CloseOutlined @click="closeModal" />
+          </span>
         </slot>
-        <template v-if="$slots.footer" #footer>
-          <slot name="footer" />
-        </template>
-      </Modal>
-    </div>
-  </template>
-  
-  <script lang="ts" name="QmModal" setup>
-  import { ref, watch, nextTick, onMounted } from "vue";
-  import { modalProps } from "ant-design-vue/es/modal/Modal";
-  import {
-    CloseOutlined,
-    FullscreenOutlined,
-    FullscreenExitOutlined,
-  } from "@ant-design/icons-vue";
-  import { throttle, omit } from "lodash-es";
-  import { Modal, Space } from "ant-design-vue";
-  
-  const props = defineProps({
-    ...modalProps(),
-    fullscreen: {
-      type: Boolean,
-      default: false,
-    },
-    showFullScreen: {
-      type: Boolean,
-      default: false,
-    },
-  });
-  
-  const emit = defineEmits(["update:open", "update:fullscreen", "ok", "cancel"]);
-  
-  const openModel = defineModel<boolean>("open");
-  const fullscreenModel = ref(props.fullscreen);
-  const innerWidth = ref("");
-  
-  const cursorStyle = {
-    top: "n-resize",
-    left: "w-resize",
-    right: "e-resize",
-    bottom: "s-resize",
-    topLeft: "nw-resize",
-    topright: "ne-resize",
-    bottomLeft: "sw-resize",
-    bottomRight: "se-resize",
-    auto: "auto",
-  } as const;
-  
-  // 是否已经初始化过了
-  let inited = false;
-  const modalWrapRef = ref<HTMLDivElement>();
-  
-  const closeModal = () => {
-    openModel.value = false;
-    emit("cancel");
-  };
-  
-  // 居中弹窗
-  const centerModal = async () => {
-    await nextTick();
-    const modalEl =
-      modalWrapRef.value?.querySelector<HTMLDivElement>(".ant-modal");
-  
-    if (modalEl && modalEl.getBoundingClientRect().left < 1) {
-      modalEl.style.left = `${
-        (document.documentElement.clientWidth - modalEl.offsetWidth) / 2
-      }px`;
-    }
-  };
-  
-  const restore = async () => {
-    fullscreenModel.value = false;
+      </template>
+      <slot>
+        ① 窗口可以拖动;<br />
+        ② 窗口可以通过八个方向改变大小;<br />
+        ③ 窗口可以最小化、最大化、还原、关闭;<br />
+        ④ 限制窗口最小宽度/高度。
+      </slot>
+      <template v-if="$slots.footer" #footer>
+        <slot name="footer" />
+      </template>
+    </Modal>
+  </div>
+</template>
+
+<script lang="ts" name="QmModal" setup>
+import { ref, watch, nextTick, onMounted } from "vue";
+import { modalProps } from "ant-design-vue/es/modal/Modal";
+import {
+  CloseOutlined,
+  FullscreenOutlined,
+  FullscreenExitOutlined,
+} from "@ant-design/icons-vue";
+import { throttle, omit } from "lodash-es";
+import { Modal, Space } from "ant-design-vue";
+
+const props = defineProps({
+  ...modalProps(),
+  fullscreen: {
+    type: Boolean,
+    default: false,
+  },
+  showFullScreen: {
+    type: Boolean,
+    default: false,
+  },
+  canDrag: {
+    type: Boolean,
+    default: false,
+  },
+});
+
+const emit = defineEmits(["update:open", "update:fullscreen", "ok", "cancel"]);
+
+const openModel = defineModel<boolean>("open");
+const fullscreenModel = ref(props.fullscreen);
+const innerWidth = ref("");
+
+const cursorStyle = {
+  top: "n-resize",
+  left: "w-resize",
+  right: "e-resize",
+  bottom: "s-resize",
+  topLeft: "nw-resize",
+  topright: "ne-resize",
+  bottomLeft: "sw-resize",
+  bottomRight: "se-resize",
+  auto: "auto",
+} as const;
+
+// 是否已经初始化过了
+let inited = false;
+const modalWrapRef = ref<HTMLDivElement>();
+
+const closeModal = () => {
+  openModel.value = false;
+  emit("cancel");
+};
+
+// 居中弹窗
+const centerModal = async () => {
+  await nextTick();
+  const modalEl =
+    modalWrapRef.value?.querySelector<HTMLDivElement>(".ant-modal");
+
+  if (modalEl && modalEl.getBoundingClientRect().left < 1) {
+    modalEl.style.left = `${
+      (document.documentElement.clientWidth - modalEl.offsetWidth) / 2
+    }px`;
+  }
+};
+
+const restore = async () => {
+  fullscreenModel.value = false;
+  centerModal();
+};
+
+const registerDragTitle = (
+  dragEl: HTMLDivElement,
+  handleEl: HTMLDivElement
+) => {
+  handleEl.style.cursor = "move";
+  handleEl.onmousedown = throttle((e: MouseEvent) => {
+    if (fullscreenModel.value) return;
+    document.body.style.userSelect = "none";
+    const disX = e.clientX - dragEl.getBoundingClientRect().left;
+    const disY = e.clientY - dragEl.getBoundingClientRect().top;
+    const mousemove = (event: MouseEvent) => {
+      if (fullscreenModel.value) return;
+      let iL = event.clientX - disX;
+      let iT = event.clientY - disY;
+      const maxL = document.documentElement.clientWidth - dragEl.offsetWidth;
+      const maxT = document.documentElement.clientHeight - dragEl.offsetHeight;
+
+      iL <= 0 && (iL = 0);
+      iT <= 0 && (iT = 0);
+      iL >= maxL && (iL = maxL);
+      iT >= maxT && (iT = maxT);
+
+      dragEl.style.left = `${Math.max(iL, 0)}px`;
+      dragEl.style.top = `${Math.max(iT, 0)}px`;
+    };
+    const mouseup = () => {
+      document.removeEventListener("mousemove", mousemove);
+      document.removeEventListener("mouseup", mouseup);
+      document.body.style.userSelect = "auto";
+    };
+
+    document.addEventListener("mousemove", mousemove);
+    document.addEventListener("mouseup", mouseup);
+  }, 20);
+};
+
+const initDrag = async () => {
+  const modalWrapRefEl = modalWrapRef.value!;
+  const modalWrapEl =
+    modalWrapRefEl.querySelector<HTMLDivElement>(".ant-modal-wrap");
+  const modalEl = modalWrapRefEl.querySelector<HTMLDivElement>(".ant-modal");
+  if (modalWrapEl && modalEl) {
     centerModal();
-  };
-  
-  const registerDragTitle = (
-    dragEl: HTMLDivElement,
-    handleEl: HTMLDivElement
-  ) => {
-    handleEl.style.cursor = "move";
-    handleEl.onmousedown = throttle((e: MouseEvent) => {
+    const headerEl = modalEl.querySelector<HTMLDivElement>(".ant-modal-header");
+    headerEl && registerDragTitle(modalEl, headerEl);
+
+    modalWrapEl.onmousemove = throttle((event: MouseEvent) => {
       if (fullscreenModel.value) return;
-      document.body.style.userSelect = "none";
-      const disX = e.clientX - dragEl.getBoundingClientRect().left;
-      const disY = e.clientY - dragEl.getBoundingClientRect().top;
-      const mousemove = (event: MouseEvent) => {
+      const left = event.clientX - modalEl.offsetLeft;
+      const top = event.clientY - modalEl.offsetTop;
+      const right = event.clientX - modalEl.offsetWidth - modalEl.offsetLeft;
+      const bottom = event.clientY - modalEl.offsetHeight - modalEl.offsetTop;
+      const isLeft = left <= 0 && left > -8;
+      const isTop = top < 5 && top > -8;
+      const isRight = right >= 0 && right < 8;
+      const isBottom = bottom > -5 && bottom < 8;
+      // 向左
+      if (isLeft && top > 5 && bottom < -5) {
+        modalWrapEl.style.cursor = cursorStyle.left;
+        // 向上
+      } else if (isTop && left > 5 && right < -5) {
+        modalWrapEl.style.cursor = cursorStyle.top;
+        // 向右
+      } else if (isRight && top > 5 && bottom < -5) {
+        modalWrapEl.style.cursor = cursorStyle.right;
+        // 向下
+      } else if (isBottom && left > 5 && right < -5) {
+        modalWrapEl.style.cursor = cursorStyle.bottom;
+        // 左上角
+      } else if (left > -8 && left <= 5 && top <= 5 && top > -8) {
+        modalWrapEl.style.cursor = cursorStyle.topLeft;
+        // 左下角
+      } else if (left > -8 && left <= 5 && bottom <= 5 && bottom > -8) {
+        modalWrapEl.style.cursor = cursorStyle.bottomLeft;
+        // 右上角
+      } else if (right < 8 && right >= -5 && top <= 5 && top > -8) {
+        modalWrapEl.style.cursor = cursorStyle.topright;
+        // 右下角
+      } else if (right < 8 && right >= -5 && bottom <= 5 && bottom > -8) {
+        modalWrapEl.style.cursor = cursorStyle.bottomRight;
+      } else {
+        modalWrapEl.style.cursor = cursorStyle.auto;
+      }
+    }, 20);
+    modalWrapEl.onmousedown = (e: MouseEvent) => {
+      if (fullscreenModel.value) return;
+      const {
+        top: iParentTop,
+        bottom: iParentBottom,
+        left: iParentLeft,
+        right: iParentRight,
+      } = modalEl.getBoundingClientRect();
+
+      const disX = e.clientX - iParentLeft;
+      const disY = e.clientY - iParentTop;
+      const iParentWidth = modalEl.offsetWidth;
+      const iParentHeight = modalEl.offsetHeight;
+
+      const cursor = modalWrapEl.style.cursor;
+
+      const mousemove = throttle((event: MouseEvent) => {
         if (fullscreenModel.value) return;
-        let iL = event.clientX - disX;
-        let iT = event.clientY - disY;
-        const maxL = document.documentElement.clientWidth - dragEl.offsetWidth;
-        const maxT = document.documentElement.clientHeight - dragEl.offsetHeight;
-  
-        iL <= 0 && (iL = 0);
-        iT <= 0 && (iT = 0);
-        iL >= maxL && (iL = maxL);
-        iT >= maxT && (iT = maxT);
-  
-        dragEl.style.left = `${Math.max(iL, 0)}px`;
-        dragEl.style.top = `${Math.max(iT, 0)}px`;
-      };
+        if (cursor !== cursorStyle.auto) {
+          document.body.style.userSelect = "none";
+        }
+        const mLeft = `${Math.max(0, event.clientX - disX)}px`;
+        const mTop = `${Math.max(0, event.clientY - disY)}px`;
+        const mLeftWidth = `${Math.min(
+          iParentRight,
+          iParentWidth + iParentLeft - event.clientX
+        )}px`;
+        const mRightWidth = `${Math.min(
+          window.innerWidth - iParentLeft,
+          event.clientX - iParentLeft
+        )}px`;
+        const mTopHeight = `${Math.min(
+          iParentBottom,
+          iParentHeight + iParentTop - event.clientY
+        )}px`;
+        const mBottomHeight = `${Math.min(
+          window.innerHeight - iParentTop,
+          event.clientY - iParentTop
+        )}px`;
+
+        // 向左边拖拽
+        if (cursor === cursorStyle.left) {
+          modalEl.style.left = mLeft;
+          modalEl.style.width = mLeftWidth;
+          // 向上边拖拽
+        } else if (cursor === cursorStyle.top) {
+          modalEl.style.top = mTop;
+          modalEl.style.height = mTopHeight;
+          // 向右边拖拽
+        } else if (cursor === cursorStyle.right) {
+          modalEl.style.width = mRightWidth;
+          // 向下拖拽
+        } else if (cursor === cursorStyle.bottom) {
+          modalEl.style.height = mBottomHeight;
+          // 左上角拖拽
+        } else if (cursor === cursorStyle.topLeft) {
+          modalEl.style.left = mLeft;
+          modalEl.style.top = mTop;
+          modalEl.style.height = mTopHeight;
+          modalEl.style.width = mLeftWidth;
+          // 右上角拖拽
+        } else if (cursor === cursorStyle.topright) {
+          modalEl.style.top = mTop;
+          modalEl.style.width = mRightWidth;
+          modalEl.style.height = mTopHeight;
+          // 左下角拖拽
+        } else if (cursor === cursorStyle.bottomLeft) {
+          modalEl.style.left = mLeft;
+          modalEl.style.width = mLeftWidth;
+          modalEl.style.height = mBottomHeight;
+          // 右下角拖拽
+        } else if (cursor === cursorStyle.bottomRight) {
+          modalEl.style.width = mRightWidth;
+          modalEl.style.height = mBottomHeight;
+        }
+        innerWidth.value = modalEl.style.width;
+      }, 20);
+
       const mouseup = () => {
         document.removeEventListener("mousemove", mousemove);
         document.removeEventListener("mouseup", mouseup);
         document.body.style.userSelect = "auto";
+        modalWrapEl.style.cursor = cursorStyle.auto;
       };
-  
+
       document.addEventListener("mousemove", mousemove);
       document.addEventListener("mouseup", mouseup);
-    }, 20);
-  };
-  
-  const initDrag = async () => {
-    await nextTick();
-    const modalWrapRefEl = modalWrapRef.value!;
-    const modalWrapEl =
-      modalWrapRefEl.querySelector<HTMLDivElement>(".ant-modal-wrap");
-    const modalEl = modalWrapRefEl.querySelector<HTMLDivElement>(".ant-modal");
-    if (modalWrapEl && modalEl) {
+    };
+  }
+  inited = true;
+};
+
+const init = async (timesObj = { num: 0 }) => {
+  await nextTick();
+  const modalWrapRefEl = modalWrapRef.value!;
+  const modalWrapEl =
+    modalWrapRefEl.querySelector<HTMLDivElement>(".ant-modal-wrap");
+  const modalEl = modalWrapRefEl.querySelector<HTMLDivElement>(".ant-modal");
+  if (timesObj.num >= 10) {
+    return;
+  }
+  if (!(modalWrapEl && modalEl)) {
+    timesObj.num++;
+    setTimeout(() => {
+      init(timesObj);
+    }, 10);
+    return;
+  }
+  if ((openModel.value && Object.is(inited, false)) || props.destroyOnClose) {
+    if (props.canDrag) {
+      initDrag();
+    } else {
       centerModal();
-      const headerEl = modalEl.querySelector<HTMLDivElement>(".ant-modal-header");
-      headerEl && registerDragTitle(modalEl, headerEl);
-  
-      modalWrapEl.onmousemove = throttle((event: MouseEvent) => {
-        if (fullscreenModel.value) return;
-        const left = event.clientX - modalEl.offsetLeft;
-        const top = event.clientY - modalEl.offsetTop;
-        const right = event.clientX - modalEl.offsetWidth - modalEl.offsetLeft;
-        const bottom = event.clientY - modalEl.offsetHeight - modalEl.offsetTop;
-        const isLeft = left <= 0 && left > -8;
-        const isTop = top < 5 && top > -8;
-        const isRight = right >= 0 && right < 8;
-        const isBottom = bottom > -5 && bottom < 8;
-        // 向左
-        if (isLeft && top > 5 && bottom < -5) {
-          modalWrapEl.style.cursor = cursorStyle.left;
-          // 向上
-        } else if (isTop && left > 5 && right < -5) {
-          modalWrapEl.style.cursor = cursorStyle.top;
-          // 向右
-        } else if (isRight && top > 5 && bottom < -5) {
-          modalWrapEl.style.cursor = cursorStyle.right;
-          // 向下
-        } else if (isBottom && left > 5 && right < -5) {
-          modalWrapEl.style.cursor = cursorStyle.bottom;
-          // 左上角
-        } else if (left > -8 && left <= 5 && top <= 5 && top > -8) {
-          modalWrapEl.style.cursor = cursorStyle.topLeft;
-          // 左下角
-        } else if (left > -8 && left <= 5 && bottom <= 5 && bottom > -8) {
-          modalWrapEl.style.cursor = cursorStyle.bottomLeft;
-          // 右上角
-        } else if (right < 8 && right >= -5 && top <= 5 && top > -8) {
-          modalWrapEl.style.cursor = cursorStyle.topright;
-          // 右下角
-        } else if (right < 8 && right >= -5 && bottom <= 5 && bottom > -8) {
-          modalWrapEl.style.cursor = cursorStyle.bottomRight;
-        } else {
-          modalWrapEl.style.cursor = cursorStyle.auto;
-        }
-      }, 20);
-      modalWrapEl.onmousedown = (e: MouseEvent) => {
-        if (fullscreenModel.value) return;
-        const {
-          top: iParentTop,
-          bottom: iParentBottom,
-          left: iParentLeft,
-          right: iParentRight,
-        } = modalEl.getBoundingClientRect();
-  
-        const disX = e.clientX - iParentLeft;
-        const disY = e.clientY - iParentTop;
-        const iParentWidth = modalEl.offsetWidth;
-        const iParentHeight = modalEl.offsetHeight;
-  
-        const cursor = modalWrapEl.style.cursor;
-  
-        const mousemove = throttle((event: MouseEvent) => {
-          if (fullscreenModel.value) return;
-          if (cursor !== cursorStyle.auto) {
-            document.body.style.userSelect = "none";
-          }
-          const mLeft = `${Math.max(0, event.clientX - disX)}px`;
-          const mTop = `${Math.max(0, event.clientY - disY)}px`;
-          const mLeftWidth = `${Math.min(
-            iParentRight,
-            iParentWidth + iParentLeft - event.clientX
-          )}px`;
-          const mRightWidth = `${Math.min(
-            window.innerWidth - iParentLeft,
-            event.clientX - iParentLeft
-          )}px`;
-          const mTopHeight = `${Math.min(
-            iParentBottom,
-            iParentHeight + iParentTop - event.clientY
-          )}px`;
-          const mBottomHeight = `${Math.min(
-            window.innerHeight - iParentTop,
-            event.clientY - iParentTop
-          )}px`;
-  
-          // 向左边拖拽
-          if (cursor === cursorStyle.left) {
-            modalEl.style.left = mLeft;
-            modalEl.style.width = mLeftWidth;
-            // 向上边拖拽
-          } else if (cursor === cursorStyle.top) {
-            modalEl.style.top = mTop;
-            modalEl.style.height = mTopHeight;
-            // 向右边拖拽
-          } else if (cursor === cursorStyle.right) {
-            modalEl.style.width = mRightWidth;
-            // 向下拖拽
-          } else if (cursor === cursorStyle.bottom) {
-            modalEl.style.height = mBottomHeight;
-            // 左上角拖拽
-          } else if (cursor === cursorStyle.topLeft) {
-            modalEl.style.left = mLeft;
-            modalEl.style.top = mTop;
-            modalEl.style.height = mTopHeight;
-            modalEl.style.width = mLeftWidth;
-            // 右上角拖拽
-          } else if (cursor === cursorStyle.topright) {
-            modalEl.style.top = mTop;
-            modalEl.style.width = mRightWidth;
-            modalEl.style.height = mTopHeight;
-            // 左下角拖拽
-          } else if (cursor === cursorStyle.bottomLeft) {
-            modalEl.style.left = mLeft;
-            modalEl.style.width = mLeftWidth;
-            modalEl.style.height = mBottomHeight;
-            // 右下角拖拽
-          } else if (cursor === cursorStyle.bottomRight) {
-            modalEl.style.width = mRightWidth;
-            modalEl.style.height = mBottomHeight;
-          }
-          innerWidth.value = modalEl.style.width;
-        }, 20);
-  
-        const mouseup = () => {
-          document.removeEventListener("mousemove", mousemove);
-          document.removeEventListener("mouseup", mouseup);
-          document.body.style.userSelect = "auto";
-          modalWrapEl.style.cursor = cursorStyle.auto;
-        };
-  
-        document.addEventListener("mousemove", mousemove);
-        document.addEventListener("mouseup", mouseup);
-      };
     }
-    inited = true;
-  };
-  
-  watch(openModel, async (val) => {
-    if ((val && Object.is(inited, false)) || props.destroyOnClose) {
-      setTimeout(() => {
-        initDrag();
-      });
+  }
+};
+
+watch(openModel, () => {
+  init();
+});
+onMounted(() => {
+  init();
+});
+</script>
+
+<style lang="less">
+.draggable-modal {
+  &.fullscreen {
+    .ant-modal {
+      inset: 0 !important;
+      width: 100% !important;
+      max-width: 100vw !important;
+      height: 100% !important;
     }
-  });
-  onMounted(() => {
-    if ((openModel.value && Object.is(inited, false)) || props.destroyOnClose) {
-      setTimeout(() => {
-        initDrag();
-      });
+
+    .ant-modal-content {
+      width: 100% !important;
+      height: 100% !important;
     }
-  });
-  </script>
-  
-  <style lang="less">
-  .draggable-modal {
-    &.fullscreen {
-      .ant-modal {
-        inset: 0 !important;
-        width: 100% !important;
-        max-width: 100vw !important;
-        height: 100% !important;
+  }
+
+  .ant-modal-wrap {
+    overflow-x: hidden;
+  }
+
+  .ant-modal {
+    position: relative;
+    min-width: 200px;
+    min-height: 200px;
+    margin: 0;
+    padding: 0;
+
+    .ant-modal-header {
+      user-select: none;
+    }
+
+    .ant-modal-close {
+      top: 6px;
+      right: 30px;
+      background-color: transparent;
+      cursor: inherit;
+
+      &:hover,
+      &:focus {
+        color: rgb(0 0 0 / 45%);
       }
-  
-      .ant-modal-content {
-        width: 100% !important;
-        height: 100% !important;
+
+      .ant-space-item:hover .anticon,
+      .ant-space-item:focus .anticon {
+        color: rgb(0 0 0 / 75%);
+        text-decoration: none;
+      }
+
+      .ant-modal-close-x {
+        width: 50px;
+        height: 50px;
+        line-height: 44px;
+
+        .ant-space {
+          width: 100%;
+          height: 100%;
+        }
       }
     }
-  
-    .ant-modal-wrap {
-      overflow-x: hidden;
-    }
-  
-    .ant-modal {
-      position: relative;
+
+    .ant-modal-content {
+      /* width: ~'v-bind("props.width")px'; */
+      display: flex;
+      flex-direction: column;
+      width: 100%;
       min-width: 200px;
+      height: 100%;
       min-height: 200px;
-      margin: 0;
-      padding: 0;
-  
+      padding-top: 0;
+      overflow: hidden;
+
       .ant-modal-header {
-        user-select: none;
+        padding-top: 20px;
       }
-  
-      .ant-modal-close {
-        top: 6px;
-        right: 30px;
-        background-color: transparent;
-        cursor: inherit;
-  
-        &:hover,
-        &:focus {
-          color: rgb(0 0 0 / 45%);
-        }
-  
-        .ant-space-item:hover .anticon,
-        .ant-space-item:focus .anticon {
-          color: rgb(0 0 0 / 75%);
-          text-decoration: none;
-        }
-  
-        .ant-modal-close-x {
-          width: 50px;
-          height: 50px;
-          line-height: 44px;
-  
-          .ant-space {
-            width: 100%;
-            height: 100%;
-          }
-        }
-      }
-  
-      .ant-modal-content {
-        /* width: ~'v-bind("props.width")px'; */
-        display: flex;
-        flex-direction: column;
-        width: 100%;
-        min-width: 200px;
+
+      .ant-modal-body {
+        flex: auto;
         height: 100%;
-        min-height: 200px;
-        padding-top: 0;
-        overflow: hidden;
-  
-        .ant-modal-header {
-          padding-top: 20px;
-        }
-  
-        .ant-modal-body {
-          flex: auto;
-          height: 100%;
-          overflow: auto;
-        }
+        overflow: auto;
       }
     }
   }
-  </style>
-  
+}
+</style>

+ 7 - 0
src/render/views/CurExam/AddExamDialog.vue

@@ -0,0 +1,7 @@
+<template>
+  <my-modal v-model:open="visible" title="新建考试"></my-modal>
+</template>
+<script name="AddExam" lang="ts" setup>
+const visible = defineModel();
+</script>
+<style lang="less" scoped></style>

+ 223 - 13
src/render/views/CurExam/index.vue

@@ -2,7 +2,7 @@
   <div class="cur-exam h-full">
     <div class="operate-box flex items-center justify-between">
       <div class="lf h-full flex items-center" v-if="curExam">
-        <span class="no">{{ curExam?.id }}</span>
+        <span class="no">No.{{ curExam?.id }}</span>
         <qm-button type="primary" ghost size="small"
           >{{ curExam?.mode }}模式</qm-button
         >
@@ -13,8 +13,18 @@
         <qm-button :icon="h(SwapOutlined)" @click="showExamListModal = true"
           >切换考试</qm-button
         >
-        <qm-button class="ml-10px" :icon="h(EditOutlined)">编辑</qm-button>
-        <qm-button class="ml-10px" :icon="h(PlusCircleOutlined)" type="primary"
+        <qm-button
+          class="ml-10px"
+          :icon="h(EditOutlined)"
+          @click="editExam"
+          :disabled="!curExam"
+          >编辑</qm-button
+        >
+        <qm-button
+          class="ml-10px"
+          :icon="h(PlusCircleOutlined)"
+          type="primary"
+          @click="addExam"
           >新建</qm-button
         >
       </div>
@@ -40,23 +50,176 @@
                 <div class="title">基础数据配置</div>
               </div>
               <div
-                class="flex items-center"
+                class="flex items-center cursor-pointer"
+                :style="{ color: token.colorPrimary }"
+              >
+                <span>进入</span>
+                <RightOutlined />
+              </div>
+            </div>
+            <div class="body">
+              <div class="option">科目数量:</div>
+              <div class="option">卡格式:</div>
+              <div class="option">考生人数:</div>
+            </div>
+          </div>
+          <div class="module">
+            <div class="head">
+              <div class="flex items-center">
+                <div class="tag" :style="{ color: token.colorPrimary }">
+                  步骤二
+                </div>
+                <div class="title">扫描管理</div>
+              </div>
+              <div
+                class="flex items-center cursor-pointer"
                 :style="{ color: token.colorPrimary }"
               >
                 <span>进入</span>
                 <RightOutlined />
               </div>
             </div>
+            <div class="body">
+              <div class="option">
+                扫描进度:
+                <p>已扫描:,未扫描:,完成比:0%</p>
+              </div>
+              <div class="option">
+                图片审核:
+                <p>已扫描:,未扫描:,完成比:0%</p>
+              </div>
+            </div>
+          </div>
+          <div class="module">
+            <div class="head">
+              <div class="flex items-center">
+                <div class="tag" :style="{ color: token.colorPrimary }">
+                  步骤三
+                </div>
+                <div class="title">复核校验</div>
+              </div>
+              <div
+                class="flex items-center cursor-pointer"
+                :style="{ color: token.colorPrimary }"
+              >
+                <span>进入</span>
+                <RightOutlined />
+              </div>
+            </div>
+            <div class="body">
+              <div class="option">
+                审核员
+                <p>已完成:,待完成:,完成比:0%</p>
+              </div>
+              <div class="option">
+                管理员
+                <p>已完成:,待完成:,完成比:0%</p>
+              </div>
+            </div>
+          </div>
+          <div class="module">
+            <div class="head">
+              <div class="flex items-center">
+                <div class="tag" :style="{ color: token.colorPrimary }">
+                  步骤四
+                </div>
+                <div class="title">识别对照</div>
+              </div>
+              <div
+                class="flex items-center cursor-pointer"
+                :style="{ color: token.colorPrimary }"
+              >
+                <span>进入</span>
+                <RightOutlined />
+              </div>
+            </div>
+            <div class="body">
+              <div class="option">
+                识别嫌疑
+                <p>待处理:,已处理:,完成比:0%</p>
+              </div>
+              <div class="option">
+                自定义
+                <p>待处理:,已处理:,完成比:0%</p>
+                <p>待生成:</p>
+              </div>
+            </div>
           </div>
-          <div class="module"></div>
-          <div class="module"></div>
-          <div class="module"></div>
         </div>
         <div class="row row2">
-          <div class="module"></div>
-          <div class="module"></div>
-          <div class="module"></div>
-          <div class="module"></div>
+          <div class="module">
+            <div class="head">
+              <div class="flex items-center">
+                <div class="tag" :style="{ color: token.colorPrimary }">
+                  步骤五
+                </div>
+                <div class="title">缺考校验</div>
+              </div>
+              <div
+                class="flex items-center cursor-pointer"
+                :style="{ color: token.colorPrimary }"
+              >
+                <span>进入</span>
+                <RightOutlined />
+              </div>
+            </div>
+            <div class="body">
+              <div class="option">正常数量:</div>
+              <div class="option">缺考:</div>
+              <div class="option">缺考待确认数量:</div>
+            </div>
+          </div>
+          <div class="module">
+            <div class="head">
+              <div class="flex items-center">
+                <div class="tag" :style="{ color: token.colorPrimary }">
+                  步骤六
+                </div>
+                <div class="title">数据检查</div>
+              </div>
+              <div
+                class="flex items-center cursor-pointer"
+                :style="{ color: token.colorPrimary }"
+              >
+                <span>进入</span>
+                <RightOutlined />
+              </div>
+            </div>
+          </div>
+          <div class="module">
+            <div class="head">
+              <div class="flex items-center">
+                <div class="tag" :style="{ color: token.colorPrimary }">
+                  步骤七
+                </div>
+                <div class="title">图片检查</div>
+              </div>
+              <div
+                class="flex items-center cursor-pointer"
+                :style="{ color: token.colorPrimary }"
+              >
+                <span>进入</span>
+                <RightOutlined />
+              </div>
+            </div>
+          </div>
+          <div class="module">
+            <div class="head">
+              <div class="flex items-center">
+                <div class="tag" :style="{ color: token.colorPrimary }">
+                  步骤八
+                </div>
+                <div class="title">结果导出</div>
+              </div>
+              <div
+                class="flex items-center cursor-pointer"
+                :style="{ color: token.colorPrimary }"
+              >
+                <span>进入</span>
+                <RightOutlined />
+              </div>
+            </div>
+          </div>
         </div>
       </div>
     </div>
@@ -82,12 +245,18 @@
         </a-radio>
       </a-radio-group>
     </my-modal>
+    <AddExamDialog
+      v-model="showAddDialog"
+      v-if="showAddDialog"
+      :curRow="curRow"
+    ></AddExamDialog>
   </div>
 </template>
 <script name="CurExam" lang="ts" setup>
 import { onMounted, ref, computed, h, watch, reactive } from "vue";
 import { getExamList } from "@/ap/exam";
 import { useUserStore } from "@/store";
+import AddExamDialog from "./AddExamDialog.vue";
 import {
   SwapOutlined,
   EditOutlined,
@@ -98,6 +267,8 @@ import useToken from "@/hooks/useToken";
 
 const { token } = useToken();
 
+const showAddDialog = ref(false);
+
 const radioStyle = reactive({
   display: "flex",
   alignItems: "center",
@@ -135,7 +306,14 @@ const chooseExamHandler = () => {
   !!exam && userStore.setCurExam(exam);
   showExamListModal.value = false;
 };
-
+const curRow = ref();
+const addExam = () => {
+  curRow.value = null;
+  showAddDialog.value = true;
+};
+const editExam = () => {
+  curRow.value = curExam.value;
+};
 onMounted(() => {
   _getExamList();
 });
@@ -192,6 +370,35 @@ watch(showExamListModal, (val: boolean) => {
           border-radius: 8px;
           border: 1px solid #e5e6eb;
           padding: 15px;
+          .body {
+            padding: 20px 4px;
+            .option {
+              position: relative;
+              color: @text-color1;
+              font-weight: bold;
+              padding-left: 14px;
+              font-size: 14px;
+              line-height: 24px;
+              & > p {
+                color: @text-color2;
+                font-weight: normal;
+              }
+              &:not(:first-child) {
+                margin-top: 10px;
+              }
+              &:after {
+                content: "";
+                position: absolute;
+                z-index: 1;
+                width: 6px;
+                height: 6px;
+                border-radius: 3px;
+                top: 8px;
+                left: 0;
+                background: #4080ff;
+              }
+            }
+          }
           &:not(:first-child) {
             margin-left: 20px;
           }
@@ -229,13 +436,16 @@ watch(showExamListModal, (val: boolean) => {
     padding: 0 16px;
     .lf {
       .no {
-        width: 70px;
+        min-width: 70px;
+        padding: 0 5px;
         height: 24px;
         background: linear-gradient(135deg, #fdd62d 0%, #faad14 100%);
         border-radius: 4px;
         border: 1px solid #ffc53d;
         color: #fff;
         margin-right: 6px;
+        text-align: center;
+        line-height: 22px;
       }
       .exam-name {
         color: @text-color1;