ソースを参照

给分板分隔线可拖动

Michael Wang 4 年 前
コミット
8db974da13
2 ファイル変更164 行追加29 行削除
  1. 62 29
      src/features/mark/MarkBoardTrack.vue
  2. 102 0
      src/features/mark/use/splitPane.ts

+ 62 - 29
src/features/mark/MarkBoardTrack.vue

@@ -16,44 +16,71 @@
     </div>
 
     <div
-      v-if="store.currentTask && store.currentTask.questionList"
-      class="tw-flex tw-gap-1 tw-flex-wrap tw-justify-between"
+      style="
+        height: calc(100vh - 41px - 160px);
+        overflow: hidden;
+        user-select: none;
+      "
     >
-      <template
-        v-for="(question, index) in store.currentTask?.questionList"
-        :key="index"
+      <div
+        v-if="store.currentTask && store.currentTask.questionList"
+        class="tw-flex tw-gap-1 tw-flex-wrap tw-justify-between tw-overflow-scroll tw-content-start"
+        style="min-height: 20% !important"
+        :style="{ height: `${topPercent}%` }"
       >
-        <div
-          @click="chooseQuestion(question)"
-          class="question tw-rounded tw-p-1"
-          :class="isCurrentQuestion(question) && 'current-question'"
+        <template
+          v-for="(question, index) in store.currentTask?.questionList"
+          :key="index"
         >
-          <div style="border-bottom: 1px solid grey">
-            {{ question.title }} {{ question.mainNumber }}-{{
-              question.subNumber
-            }}
+          <div
+            @click="chooseQuestion(question)"
+            class="question tw-rounded tw-p-1"
+            style="height: 70px"
+            :class="isCurrentQuestion(question) && 'current-question'"
+          >
+            <div style="border-bottom: 1px solid grey">
+              {{ question.title }} {{ question.mainNumber }}-{{
+                question.subNumber
+              }}
+            </div>
+            <div class="tw-text-center tw-font-medium tw-text-3xl">
+              <!-- 特殊的空格符号 -->
+              {{ question.score ?? " " }}
+            </div>
           </div>
-          <div class="tw-text-center tw-font-medium tw-text-2xl">
-            <!-- 特殊的空格符号 -->
-            {{ question.score ?? " " }}
-          </div>
-        </div>
-      </template>
-    </div>
+        </template>
+      </div>
 
-    <div class="tw-flex tw-gap-1 tw-flex-wrap tw-mt-5">
       <div
-        v-for="(s, i) in questionScoreSteps"
-        :key="i"
-        @click="chooseScore(s)"
-        class="single-score"
-        :class="isCurrentScore(s) && 'current-score'"
+        style="
+          width: 100%;
+          height: 4px;
+          border: 2px solid grey;
+          cursor: row-resize;
+        "
+        ref="dragSpliter"
+      ></div>
+      <div
+        class="tw-flex tw-gap-1 tw-flex-wrap tw-mt-5 tw-overflow-scroll tw-content-start"
+        style="padding-bottom: 40px"
+        :style="{ height: `${100 - topPercent}%` }"
       >
-        {{ s }}
+        <div
+          v-for="(s, i) in questionScoreSteps"
+          :key="i"
+          @click="chooseScore(s)"
+          class="single-score"
+          :class="isCurrentScore(s) && 'current-score'"
+        >
+          {{ s }}
+        </div>
       </div>
     </div>
-
-    <div class="tw-flex tw-justify-between tw-mt-4">
+    <div
+      class="tw-flex tw-justify-between tw-mt-4"
+      draggable
+      style="position: absolute; bottom: 10px; right: 10px; width: 230px"
+    >
       <qm-button
         type="primary"
         shape="round"
@@ -63,6 +90,7 @@
       >
         回退
       </qm-button>
+
       <qm-button
         type="primary"
         shape="round"
@@ -83,11 +111,14 @@ import { computed, defineComponent, onMounted, onUnmounted, watch } from "vue";
 import { store } from "./store";
 import { autoChooseFirstQuestion } from "./use/autoChooseFirstQuestion";
 import { message } from "ant-design-vue";
+import { dragSplitPane } from "./use/splitPane";
 
 export default defineComponent({
   name: "MarkBoardTrack",
   emits: ["submit"],
   setup(props, { emit }) {
+    const { dragSpliter, topPercent } = dragSplitPane();
+
     const { chooseQuestion } = autoChooseFirstQuestion();
 
     const questionScoreSteps = computed(() => {
@@ -255,6 +286,8 @@ export default defineComponent({
       questionScoreSteps,
       clearLatestMarkOfCurrentQuetion,
       clearAllMarksOfCurrentQuetion,
+      dragSpliter,
+      topPercent,
       submit,
     };
   },

+ 102 - 0
src/features/mark/use/splitPane.ts

@@ -0,0 +1,102 @@
+import { onMounted, onUnmounted, ref, watchEffect } from "vue";
+
+export function dragSplitPane() {
+  let pos = { y: 0 };
+  const dragSpliter = ref((null as unknown) as HTMLDivElement);
+  let topPercent = ref(30);
+
+  const mouseDownHandler = function (e: MouseEvent) {
+    // console.log(e);
+    if (e.target !== dragSpliter.value) return;
+    e.preventDefault();
+    pos = {
+      // Get the current mouse position
+      y: e.clientY,
+    };
+    // topPercent.value = 100 * e.offsetTop / e.target.parentElement.style.height;
+    if (dragSpliter.value) {
+      // dragSpliter.value.style.cursor = "row-resize";
+      // console.log("add");
+
+      document.addEventListener("mousemove", mouseMoveHandler);
+      document.addEventListener("mouseup", mouseUpHandler);
+    }
+    return false;
+  };
+
+  const mouseMoveHandler = function (e: MouseEvent) {
+    e.preventDefault();
+    const dy = e.clientY - pos.y;
+
+    // // Scroll the element
+    // // dragSpliter.value.style.marginTop =
+    // //   (parseFloat(dragSpliter.value.style.marginTop) || 0) + dy + "px";
+    dragSpliter.value.style.marginTop = dy + "px";
+    // console.log(dragSpliter.value.style.marginTop);
+    const target = e.target as HTMLElement;
+    const parent = target.parentElement;
+    // console.log(getComputedStyle(e.target.parentElement).height);
+    // console.log({
+    //   offsetTop: target.offsetTop,
+    //   dy: dy,
+    //   movementY: e.movementY,
+    // });
+    // if (parent)
+    //   topPercent.value =
+    //     (100 * target.offsetTop) / parseFloat(getComputedStyle(parent).height);
+    // console.log("topPercent", topPercent.value);
+    return false;
+  };
+
+  const mouseUpHandler = function (e: MouseEvent) {
+    const dy = e.clientY - pos.y;
+
+    // Scroll the element
+    // dragSpliter.value.style.marginTop =
+    //   (parseFloat(dragSpliter.value.style.marginTop) || 0) + dy + "px";
+    // dragSpliter.value.style.marginTop = dy + "px";
+    // console.log(dragSpliter.value.style.marginTop);
+    const target = e.target as HTMLElement;
+    const parent = target.parentElement;
+    // console.log(getComputedStyle(e.target.parentElement).height);
+    // console.log("offsetTop", target.offsetTop);
+    // TODO: 暂时不知道为啥有 140
+    if (parent)
+      topPercent.value =
+        (100 * (target.offsetTop - 140)) /
+        parseFloat(getComputedStyle(parent).height);
+    // console.log({
+    //   topPercent: topPercent.value,
+    //   "parseFloat(getComputedStyle(parent).height)": parseFloat(
+    //     getComputedStyle(parent).height
+    //   ),
+    //   "target.offsetTop": target.offsetTop,
+    // });
+
+    // console.log("removed");
+    if (dragSpliter.value) {
+      document.removeEventListener("mousemove", mouseMoveHandler);
+      document.removeEventListener("mouseup", mouseUpHandler);
+      // dragSpliter.value.style.cursor = "auto";
+      dragSpliter.value.style.marginTop = "0";
+    }
+  };
+
+  onMounted(() => {
+    watchEffect(() => {
+      if (dragSpliter.value) {
+        // console.log("watchEffect");
+        document.addEventListener("mousedown", mouseDownHandler);
+      }
+    });
+  });
+  onUnmounted(() => {
+    if (dragSpliter.value) {
+      document.removeEventListener("mousedown", mouseDownHandler);
+      document.removeEventListener("mousemove", mouseMoveHandler);
+      document.removeEventListener("mouseup", mouseUpHandler);
+    }
+  });
+
+  return { dragSpliter, topPercent };
+}