Эх сурвалжийг харах

script setup refactor step => student track

Michael Wang 4 жил өмнө
parent
commit
331ca1f001

+ 105 - 126
src/features/student/studentTrack/MarkBody.vue

@@ -28,11 +28,11 @@
   </div>
 </template>
 
-<script lang="ts">
-import { computed, defineComponent, reactive, ref, watchEffect } from "vue";
+<script setup lang="ts">
+import { computed, defineEmit, reactive, ref, watchEffect } from "vue";
 import { store } from "./store";
 import MarkDrawTrack from "./MarkDrawTrack.vue";
-import { SpecialTag, Track } from "@/types";
+import type { SpecialTag, Track } from "@/types";
 import { useTimers } from "@/setups/useTimers";
 import { loadImage } from "@/utils/utils";
 import { dragImage } from "@/features/mark/use/draggable";
@@ -44,128 +44,110 @@ interface SliceImage {
   tagList: Array<SpecialTag>;
   originalImage: HTMLImageElement;
 }
-// should not render twice at the same time
-let __lock = false;
-let __currentStudentId = -1; // save __currentStudentIdof lock
-export default defineComponent({
-  name: "MarkBody",
-  components: { MarkDrawTrack },
-  emits: ["error"],
-  setup(props, { emit }) {
-    const { dragContainer } = dragImage();
-
-    const { addTimeout } = useTimers();
-
-    let rendering = ref(false);
-    let sliceImagesWithTrackList: Array<SliceImage> = reactive([]);
-
-    async function processImage() {
-      if (!store.currentTask) return;
-
-      const images = [];
-      for (const url of store.currentTask.sliceUrls) {
-        const image = await loadImage(url);
-        images.push(image);
-      }
-
-      for (const url of store.currentTask.sliceUrls) {
-        const completeUrl = url;
-
-        const indexInSliceUrls = store.currentTask.sliceUrls.indexOf(url) + 1;
-        const image = images[indexInSliceUrls - 1];
-
-        const trackLists = store.currentTask.questionList
-          .map((q) => q.trackList)
-          .reduce((acc, t) => {
-            acc = acc.concat(t);
-            return acc;
-          }, [] as Array<Track>);
-        const thisImageTrackList = trackLists.filter(
-          (t) => t.offsetIndex === indexInSliceUrls
-        );
-        const thisImageTagList = store.currentTask.specialTagList.filter(
-          (t) => t.offsetIndex === indexInSliceUrls
-        );
-
-        sliceImagesWithTrackList.push({
-          url: completeUrl,
-          indexInSliceUrls,
-          trackList: thisImageTrackList,
-          tagList: thisImageTagList,
-          originalImage: image,
-        });
-      }
-    }
-    const renderPaperAndMark = async () => {
-      if (__lock) {
-        if (store.currentTask?.studentId === __currentStudentId) {
-          console.log("重复渲染,返回");
-          return;
-        }
-        console.log("上个任务还未渲染完毕,稍等一秒再尝试渲染");
-        await new Promise((res) => setTimeout(res, 1000));
-        await renderPaperAndMark();
-        return;
-      }
-      __lock = true;
-      __currentStudentId = store.currentTask?.studentId ?? -1;
-      sliceImagesWithTrackList.splice(0);
-
-      if (!store.currentTask) {
-        __lock = false;
-        return;
-      }
-
-      try {
-        rendering.value = true;
-        await processImage();
-      } catch (error) {
-        sliceImagesWithTrackList.splice(0);
-        console.log("render error ", error);
-        // 图片加载出错,自动加载下一个任务
-        emit("error");
-      } finally {
-        __lock = false;
-        rendering.value = false;
-      }
-    };
-
-    watchEffect(renderPaperAndMark);
-
-    const answerPaperScale = computed(() => {
-      // 放大、缩小不影响页面之前的滚动条定位
-      let percentWidth = 0;
-      let percentTop = 0;
-      const container = document.querySelector(
-        ".mark-body-container"
-      ) as HTMLDivElement;
-      if (container) {
-        const { scrollLeft, scrollTop, scrollWidth, scrollHeight } = container;
-        percentWidth = scrollLeft / scrollWidth;
-        percentTop = scrollTop / scrollHeight;
-      }
-
-      addTimeout(() => {
-        if (container) {
-          const { scrollWidth, scrollHeight } = container;
-          container.scrollTo({
-            left: scrollWidth * percentWidth,
-            top: scrollHeight * percentTop,
-          });
-        }
-      }, 10);
-      const scale = store.setting.uiSetting["answer.paper.scale"];
-      return scale * 100 + "%";
+
+const emit = defineEmit(["error"]);
+const { dragContainer } = dragImage();
+
+const { addTimeout } = useTimers();
+
+let rendering = ref(false);
+let sliceImagesWithTrackList: Array<SliceImage> = reactive([]);
+
+async function processImage() {
+  if (!store.currentTask) return;
+
+  const images = [];
+  for (const url of store.currentTask.sliceUrls) {
+    const image = await loadImage(url);
+    images.push(image);
+  }
+
+  for (const url of store.currentTask.sliceUrls) {
+    const completeUrl = url;
+
+    const indexInSliceUrls = store.currentTask.sliceUrls.indexOf(url) + 1;
+    const image = images[indexInSliceUrls - 1];
+
+    const trackLists = store.currentTask.questionList
+      .map((q) => q.trackList)
+      .reduce((acc, t) => {
+        acc = acc.concat(t);
+        return acc;
+      }, [] as Array<Track>);
+    const thisImageTrackList = trackLists.filter(
+      (t) => t.offsetIndex === indexInSliceUrls
+    );
+    const thisImageTagList = store.currentTask.specialTagList.filter(
+      (t) => t.offsetIndex === indexInSliceUrls
+    );
+
+    sliceImagesWithTrackList.push({
+      url: completeUrl,
+      indexInSliceUrls,
+      trackList: thisImageTrackList,
+      tagList: thisImageTagList,
+      originalImage: image,
     });
+  }
+}
 
-    return {
-      dragContainer,
-      store,
-      rendering,
-      sliceImagesWithTrackList,
-      answerPaperScale,
-    };
-  },
+// should not render twice at the same time
+let renderLock = false;
+const renderPaperAndMark = async () => {
+  if (renderLock) {
+    console.log("上个任务还未渲染完毕,稍等一秒再尝试渲染");
+    await new Promise((res) => setTimeout(res, 1000));
+    await renderPaperAndMark();
+    return;
+  }
+  renderLock = true;
+  sliceImagesWithTrackList.splice(0);
+
+  if (!store.currentTask) {
+    renderLock = false;
+    return;
+  }
+
+  try {
+    rendering.value = true;
+    await processImage();
+  } catch (error) {
+    sliceImagesWithTrackList.splice(0);
+    console.log("render error ", error);
+    // 图片加载出错,自动加载下一个任务
+    emit("error");
+  } finally {
+    renderLock = false;
+    rendering.value = false;
+  }
+};
+
+watchEffect(renderPaperAndMark);
+
+const answerPaperScale = computed(() => {
+  // 放大、缩小不影响页面之前的滚动条定位
+  let percentWidth = 0;
+  let percentTop = 0;
+  const container = document.querySelector(
+    ".mark-body-container"
+  ) as HTMLDivElement;
+  if (container) {
+    const { scrollLeft, scrollTop, scrollWidth, scrollHeight } = container;
+    percentWidth = scrollLeft / scrollWidth;
+    percentTop = scrollTop / scrollHeight;
+  }
+
+  addTimeout(() => {
+    if (container) {
+      const { scrollWidth, scrollHeight } = container;
+      container.scrollTo({
+        left: scrollWidth * percentWidth,
+        top: scrollHeight * percentTop,
+      });
+    }
+  }, 10);
+  const scale = store.setting.uiSetting["answer.paper.scale"];
+  return scale * 100 + "%";
 });
 </script>
 
@@ -180,9 +162,6 @@ export default defineComponent({
   cursor: grab;
   user-select: none;
 }
-.grabbing {
-  cursor: grabbing;
-}
 .mark-body-container img {
   width: 100%;
 }

+ 46 - 58
src/features/student/studentTrack/MarkDrawTrack.vue

@@ -22,68 +22,56 @@
   </template>
 </template>
 
-<script lang="ts">
-import { SpecialTag, Track } from "@/types";
-import { defineComponent, PropType, watch } from "vue";
+<script setup lang="ts">
+import type { SpecialTag, Track } from "@/types";
+import { defineProps, watch } from "vue";
 import { store } from "./store";
 
-export default defineComponent({
-  name: "MarkDrawTrack",
-  props: {
-    trackList: {
-      type: Array as PropType<Array<Track>>,
-    },
-    specialTagList: {
-      type: Array as PropType<Array<SpecialTag>>,
-    },
-    originalImage: {
-      type: Object as PropType<HTMLImageElement>,
-      required: true,
-    },
-  },
-  setup({ trackList, specialTagList, originalImage }) {
-    const focusedTrack = (track: Track) => {
-      return store.focusTracks.includes(track);
-    };
-    const computeTopAndLeft = (track: Track | SpecialTag) => {
-      const topInsideSlice = track.offsetY;
-      const leftInsideSlice = track.offsetX;
-      return {
-        top: (topInsideSlice / originalImage.naturalHeight) * 100 + "%",
-        left: (leftInsideSlice / originalImage.naturalWidth) * 100 + "%",
-        "font-size": store.setting.uiSetting["answer.paper.scale"] * 2.2 + "em",
-      };
-    };
+const props =
+  defineProps<{
+    trackList: Array<Track>;
+    specialTagList: Array<SpecialTag>;
+    originalImage: HTMLImageElement;
+  }>();
+const { originalImage } = props;
 
-    watch(
-      () => store.focusTracks.length,
-      () => {
-        if (store.focusTracks.length === 0) return;
-        const minImageIndex = Math.min(
-          ...store.focusTracks.map((t) => t.offsetIndex)
-        );
-        const minImageOffsetY = Math.min(
-          ...store.focusTracks
-            .filter((t) => t.offsetIndex === minImageIndex)
-            .map((t) => t.offsetY)
-        );
-        const topTrack = store.focusTracks.find(
-          (t) =>
-            t.offsetIndex === minImageIndex && t.offsetY === minImageOffsetY
-        );
-        if (topTrack) {
-          document
-            .querySelector(
-              `#a${topTrack.mainNumber + topTrack.subNumber + topTrack.offsetY}`
-            )
-            ?.scrollIntoView({ behavior: "smooth" });
-        }
-      }
-    );
+const focusedTrack = (track: Track) => {
+  return store.focusTracks.includes(track);
+};
+const computeTopAndLeft = (track: Track | SpecialTag) => {
+  const topInsideSlice = track.offsetY;
+  const leftInsideSlice = track.offsetX;
+  return {
+    top: (topInsideSlice / originalImage.naturalHeight) * 100 + "%",
+    left: (leftInsideSlice / originalImage.naturalWidth) * 100 + "%",
+    "font-size": store.setting.uiSetting["answer.paper.scale"] * 2.2 + "em",
+  };
+};
 
-    return { store, focusedTrack, computeTopAndLeft };
-  },
-});
+watch(
+  () => store.focusTracks.length,
+  () => {
+    if (store.focusTracks.length === 0) return;
+    const minImageIndex = Math.min(
+      ...store.focusTracks.map((t) => t.offsetIndex)
+    );
+    const minImageOffsetY = Math.min(
+      ...store.focusTracks
+        .filter((t) => t.offsetIndex === minImageIndex)
+        .map((t) => t.offsetY)
+    );
+    const topTrack = store.focusTracks.find(
+      (t) => t.offsetIndex === minImageIndex && t.offsetY === minImageOffsetY
+    );
+    if (topTrack) {
+      document
+        .querySelector(
+          `#a${topTrack.mainNumber + topTrack.subNumber + topTrack.offsetY}`
+        )
+        ?.scrollIntoView({ behavior: "smooth" });
+    }
+  }
+);
 </script>
 
 <style scoped>

+ 25 - 54
src/features/student/studentTrack/MarkHeader.vue

@@ -46,71 +46,42 @@
   </div>
 </template>
 
-<script lang="ts">
-import { computed, defineComponent } from "vue";
+<script setup lang="ts">
+import { computed } from "vue";
 import { store } from "./store";
 import {
   ZoomInOutlined,
   ZoomOutOutlined,
   FullscreenOutlined,
-  SnippetsOutlined,
-  UserOutlined,
   PoweroffOutlined,
-  ClockCircleOutlined,
-  QuestionCircleOutlined,
 } from "@ant-design/icons-vue";
 import { useRoute } from "vue-router";
 
-export default defineComponent({
-  name: "MarkHeader",
-  components: {
-    ZoomInOutlined,
-    ZoomOutOutlined,
-    FullscreenOutlined,
-    SnippetsOutlined,
-    UserOutlined,
-    PoweroffOutlined,
-    ClockCircleOutlined,
-    QuestionCircleOutlined,
-  },
-  setup() {
-    const route = useRoute();
+const route = useRoute();
 
-    const upScale = () => {
-      const s = store.setting.uiSetting["answer.paper.scale"];
-      if (s < 3)
-        store.setting.uiSetting["answer.paper.scale"] = +(s + 0.2).toFixed(1);
-    };
-    const downScale = () => {
-      const s = store.setting.uiSetting["answer.paper.scale"];
-      if (s > 0.2)
-        store.setting.uiSetting["answer.paper.scale"] = +(s - 0.2).toFixed(1);
-    };
-    const normalScale = () => {
-      store.setting.uiSetting["answer.paper.scale"] = 1;
-    };
-    const greaterThanOneScale = computed(() => {
-      return store.setting.uiSetting["answer.paper.scale"] > 1;
-    });
-    const lessThanOneScale = computed(() => {
-      return store.setting.uiSetting["answer.paper.scale"] < 1;
-    });
-
-    const closeWindow = async () => {
-      window.close();
-    };
-
-    return {
-      store,
-      upScale,
-      downScale,
-      normalScale,
-      greaterThanOneScale,
-      lessThanOneScale,
-      closeWindow,
-    };
-  },
+const upScale = () => {
+  const s = store.setting.uiSetting["answer.paper.scale"];
+  if (s < 3)
+    store.setting.uiSetting["answer.paper.scale"] = +(s + 0.2).toFixed(1);
+};
+const downScale = () => {
+  const s = store.setting.uiSetting["answer.paper.scale"];
+  if (s > 0.2)
+    store.setting.uiSetting["answer.paper.scale"] = +(s - 0.2).toFixed(1);
+};
+const normalScale = () => {
+  store.setting.uiSetting["answer.paper.scale"] = 1;
+};
+const greaterThanOneScale = computed(() => {
+  return store.setting.uiSetting["answer.paper.scale"] > 1;
 });
+const lessThanOneScale = computed(() => {
+  return store.setting.uiSetting["answer.paper.scale"] < 1;
+});
+
+const closeWindow = async () => {
+  window.close();
+};
 </script>
 
 <style scoped>

+ 47 - 63
src/features/student/studentTrack/StudentTrack.vue

@@ -7,85 +7,69 @@
   </div>
 </template>
 
-<script lang="ts">
-import { defineComponent, onMounted } from "vue";
+<script setup lang="ts">
+import { onMounted } from "vue";
 import { store } from "./store";
 import MarkHeader from "./MarkHeader.vue";
 import { useRoute } from "vue-router";
 import MarkBody from "./MarkBody.vue";
-import { Task } from "@/types";
+import type { Task } from "@/types";
 import { message } from "ant-design-vue";
 import { getSingleStudentTask } from "@/api/studentTrackPage";
 
-export default defineComponent({
-  name: "StudentTrack",
-  components: {
-    MarkHeader,
-    MarkBody,
-  },
-  setup: () => {
-    const route = useRoute();
-    let studentId = route.query.studentId;
+const route = useRoute();
+let studentId = route.query.studentId;
 
-    async function updateTask() {
-      // const mkey = "fetch_task_key";
-      message.info({ content: "获取任务中...", duration: 2 });
-      let res = await getSingleStuTask();
-      // message.success({ content: "获取成功", key: mkey });
+async function updateTask() {
+  // const mkey = "fetch_task_key";
+  message.info({ content: "获取任务中...", duration: 2 });
+  let res = await getSingleStuTask();
+  // message.success({ content: "获取成功", key: mkey });
 
-      if (res.data.sliceUrls) {
-        store.setting.fileServer = res.data.fileServer;
-        store.setting.uiSetting = {
-          "answer.paper.scale": 1,
-          "score.board.collapse": false,
-        };
-
-        let task = {} as Task;
-        task.examNumber = res.data.examNumber;
+  if (res.data.sliceUrls) {
+    store.setting.fileServer = res.data.fileServer;
+    store.setting.uiSetting = {
+      "answer.paper.scale": 1,
+      "score.board.collapse": false,
+    };
 
-        task.sliceUrls = res.data.sliceUrls.map(
-          (s: string) => store.setting.fileServer + s
-        );
-        // 目前api并没有区分score和tag
-        task.questionList = [
-          // @ts-ignore
-          // { trackList: res.data.tagList.filter((q) => !q.tagName) },
-        ];
-        // @ts-ignore
-        task.specialTagList = res.data.tagList.filter((q) => q.tagName);
+    let task = {} as Task;
+    task.examNumber = res.data.examNumber;
 
-        store.currentTask = task;
-        if (store.currentTask)
-          store.setting.subject = store.currentTask.subject;
-      } else {
-        store.message = res.data.message;
-      }
-    }
+    task.sliceUrls = res.data.sliceUrls.map(
+      (s: string) => store.setting.fileServer + s
+    );
+    // 目前api并没有区分score和tag
+    task.questionList = [
+      // @ts-ignore
+      // { trackList: res.data.tagList.filter((q) => !q.tagName) },
+    ];
+    // @ts-ignore
+    task.specialTagList = res.data.tagList.filter((q) => q.tagName);
 
-    async function fetchTask() {
-      await updateTask();
-    }
+    store.currentTask = task;
+    if (store.currentTask) store.setting.subject = store.currentTask.subject;
+  } else {
+    store.message = res.data.message;
+  }
+}
 
-    onMounted(async () => {
-      await fetchTask();
-    });
+async function fetchTask() {
+  await updateTask();
+}
 
-    async function getSingleStuTask() {
-      return getSingleStudentTask(studentId as string);
-    }
+onMounted(async () => {
+  await fetchTask();
+});
 
-    const renderError = () => {
-      store.currentTask = undefined;
-      store.message = "加载失败,请重新加载。";
-    };
+async function getSingleStuTask() {
+  return getSingleStudentTask(studentId as string);
+}
 
-    return {
-      store,
-      fetchTask,
-      renderError,
-    };
-  },
-});
+const renderError = () => {
+  store.currentTask = undefined;
+  store.message = "加载失败,请重新加载。";
+};
 </script>
 
 <style scoped>