Selaa lähdekoodia

三评颜色加入

刘洋 1 vuosi sitten
vanhempi
commit
888fc99fd1

+ 178 - 178
src/features/mark/MarkDrawTrack.vue

@@ -1,178 +1,178 @@
-<template>
-  <transition-group name="track-score" tag="div">
-    <template v-for="track in trackList">
-      <div
-        v-if="store.shouldShowTrack && (doubleTrack || !track.isByMultMark)"
-        :key="`key-${track.mainNumber}-${track.subNumber}-${track.offsetY}-${track.offsetX}`"
-        class="score-container"
-        :class="[focusedTrack(track) && 'score-animation']"
-        :style="computeTopAndLeft(track)"
-      >
-        <span
-          :id="`a-${track.mainNumber}-${track.subNumber}-${track.offsetY}-${track.offsetX}`"
-          class="tw-m-auto"
-        >
-          {{ track.unanswered ? "空" : track.score }}
-        </span>
-      </div>
-    </template>
-  </transition-group>
-  <template v-for="(tag, index) in specialTagList" :key="index">
-    <div class="score-container" :style="computeTopAndLeft(tag)">
-      <span class="tw-m-auto">
-        {{ tag.tagName }}
-      </span>
-    </div>
-  </template>
-</template>
-
-<script setup lang="ts">
-import type { SpecialTag, Track } from "@/types";
-import { toRefs, watch, nextTick, computed } from "vue";
-import { store } from "@/store/store";
-import { message } from "ant-design-vue";
-import { useRoute } from "vue-router";
-const route = useRoute();
-
-const doubleTrack = computed(() => {
-  return !!store.setting?.doubleTrack;
-});
-const props = defineProps<{
-  trackList: Array<Track>;
-  specialTagList: Array<SpecialTag>;
-  sliceImageWidth: number;
-  sliceImageHeight: number;
-  dx: number;
-  dy: number;
-}>();
-const { trackList } = toRefs(props);
-
-const computeTopAndLeft = (track: Track | SpecialTag) => {
-  const topInsideSlice = track.offsetY - props.dy;
-  const leftInsideSlice = track.offsetX - props.dx;
-  const topInsideSliceRatio = topInsideSlice / props.sliceImageHeight;
-  const leftInsideSliceRatio = leftInsideSlice / props.sliceImageWidth;
-  if (
-    topInsideSliceRatio < 0 ||
-    topInsideSliceRatio > 1 ||
-    leftInsideSliceRatio < 0 ||
-    leftInsideSliceRatio > 1
-  ) {
-    /** 解决message提示死循环的问题 */
-    void nextTick(() => {
-      void message.error("轨迹坐标有误,可能是图片被修改过,请联系管理员!");
-    });
-  }
-
-  return {
-    color:
-      route.path === "/admin/exam/arbitrate/start"
-        ? "green"
-        : track.color || "red",
-    top: topInsideSliceRatio * 100 + "%",
-    left: leftInsideSliceRatio * 100 + "%",
-    "font-size":
-      (store.setting.uiSetting["score.fontSize.scale"] || 1) *
-        store.setting.uiSetting["answer.paper.scale"] *
-        2.2 +
-      "em",
-  };
-};
-const hasMember = (track: any) => {
-  return (
-    // (store.getMarkStatus === "正评" || store.getMarkStatus === "试评") &&
-    store.focusTracks.find((item: any) => {
-      return (
-        item.mainNumber == track.mainNumber && item.subNumber == track.subNumber
-      );
-    })
-  );
-};
-const focusedTrack = (track: Track) => {
-  return store.focusTracks.includes(track) || hasMember(track);
-};
-
-watch(
-  () => store.focusTracks,
-  () => {
-    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) {
-      let allHeaderTracks = store.currentTask.questionList
-        .map((item: any) => item.headerTrack || [])
-        .flat();
-      console.log("allHeaderTracks", allHeaderTracks);
-      let find = allHeaderTracks.find(
-        (item: any) =>
-          item.mainNumber == topTrack.mainNumber &&
-          item.subNumber == topTrack.subNumber
-      );
-      document
-        .querySelector(
-          `#a-${topTrack.mainNumber}-${topTrack.subNumber}-${
-            find?.offsetY || topTrack.offsetY
-          }-${find?.offsetX || topTrack.offsetX}`
-        )
-        ?.scrollIntoView({ behavior: "smooth" });
-    }
-  },
-  {
-    deep: true,
-  }
-);
-</script>
-
-<style scoped>
-.score-container {
-  position: absolute;
-  display: flex;
-  place-content: center;
-  /* color: red; */
-
-  /* to center score */
-  width: 200px;
-  height: 200px;
-  margin-top: -100px;
-  margin-left: -100px;
-
-  /* to click through div */
-  pointer-events: none;
-}
-.score-animation {
-  animation: 2s ease-in-out 0s infinite alternate change_size;
-}
-
-@keyframes change_size {
-  from {
-    font-size: 2em;
-    margin-top: -100px;
-    margin-left: -100px;
-  }
-  to {
-    font-size: 4em;
-    margin-top: -80px;
-    margin-left: -80px;
-  }
-}
-.track-score-enter-active {
-  transition: opacity 0.3s ease;
-}
-.track-score-leave-active {
-  transition: opacity 0.6s ease;
-}
-
-.track-score-enter-from,
-.track-score-leave-to {
-  opacity: 0;
-}
-</style>
+<template>
+  <transition-group name="track-score" tag="div">
+    <template v-for="track in trackList">
+      <div
+        v-if="store.shouldShowTrack && (doubleTrack || !track.isByMultMark)"
+        :key="`key-${track.mainNumber}-${track.subNumber}-${track.offsetY}-${track.offsetX}`"
+        class="score-container"
+        :class="[focusedTrack(track) && 'score-animation']"
+        :style="computeTopAndLeft(track)"
+      >
+        <span
+          :id="`a-${track.mainNumber}-${track.subNumber}-${track.offsetY}-${track.offsetX}`"
+          class="tw-m-auto"
+        >
+          {{ track.unanswered ? "空" : track.score }}
+        </span>
+      </div>
+    </template>
+  </transition-group>
+  <template v-for="(tag, index) in specialTagList" :key="index">
+    <div class="score-container" :style="computeTopAndLeft(tag)">
+      <span class="tw-m-auto">
+        {{ tag.tagName }}
+      </span>
+    </div>
+  </template>
+</template>
+
+<script setup lang="ts">
+import type { SpecialTag, Track } from "@/types";
+import { toRefs, watch, nextTick, computed } from "vue";
+import { store } from "@/store/store";
+import { message } from "ant-design-vue";
+import { useRoute } from "vue-router";
+const route = useRoute();
+
+const doubleTrack = computed(() => {
+  return !!store.setting?.doubleTrack;
+});
+const props = defineProps<{
+  trackList: Array<Track>;
+  specialTagList: Array<SpecialTag>;
+  sliceImageWidth: number;
+  sliceImageHeight: number;
+  dx: number;
+  dy: number;
+}>();
+const { trackList } = toRefs(props);
+
+const computeTopAndLeft = (track: Track | SpecialTag) => {
+  const topInsideSlice = track.offsetY - props.dy;
+  const leftInsideSlice = track.offsetX - props.dx;
+  const topInsideSliceRatio = topInsideSlice / props.sliceImageHeight;
+  const leftInsideSliceRatio = leftInsideSlice / props.sliceImageWidth;
+  if (
+    topInsideSliceRatio < 0 ||
+    topInsideSliceRatio > 1 ||
+    leftInsideSliceRatio < 0 ||
+    leftInsideSliceRatio > 1
+  ) {
+    /** 解决message提示死循环的问题 */
+    void nextTick(() => {
+      void message.error("轨迹坐标有误,可能是图片被修改过,请联系管理员!");
+    });
+  }
+
+  return {
+    color:
+      route.path === "/admin/exam/arbitrate/start"
+        ? "#00B42A"
+        : track.color || "#F53F3F",
+    top: topInsideSliceRatio * 100 + "%",
+    left: leftInsideSliceRatio * 100 + "%",
+    "font-size":
+      (store.setting.uiSetting["score.fontSize.scale"] || 1) *
+        store.setting.uiSetting["answer.paper.scale"] *
+        2.2 +
+      "em",
+  };
+};
+const hasMember = (track: any) => {
+  return (
+    // (store.getMarkStatus === "正评" || store.getMarkStatus === "试评") &&
+    store.focusTracks.find((item: any) => {
+      return (
+        item.mainNumber == track.mainNumber && item.subNumber == track.subNumber
+      );
+    })
+  );
+};
+const focusedTrack = (track: Track) => {
+  return store.focusTracks.includes(track) || hasMember(track);
+};
+
+watch(
+  () => store.focusTracks,
+  () => {
+    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) {
+      let allHeaderTracks = store.currentTask.questionList
+        .map((item: any) => item.headerTrack || [])
+        .flat();
+      console.log("allHeaderTracks", allHeaderTracks);
+      let find = allHeaderTracks.find(
+        (item: any) =>
+          item.mainNumber == topTrack.mainNumber &&
+          item.subNumber == topTrack.subNumber
+      );
+      document
+        .querySelector(
+          `#a-${topTrack.mainNumber}-${topTrack.subNumber}-${
+            find?.offsetY || topTrack.offsetY
+          }-${find?.offsetX || topTrack.offsetX}`
+        )
+        ?.scrollIntoView({ behavior: "smooth" });
+    }
+  },
+  {
+    deep: true,
+  }
+);
+</script>
+
+<style scoped>
+.score-container {
+  position: absolute;
+  display: flex;
+  place-content: center;
+  /* color: red; */
+
+  /* to center score */
+  width: 200px;
+  height: 200px;
+  margin-top: -100px;
+  margin-left: -100px;
+
+  /* to click through div */
+  pointer-events: none;
+}
+.score-animation {
+  animation: 2s ease-in-out 0s infinite alternate change_size;
+}
+
+@keyframes change_size {
+  from {
+    font-size: 2em;
+    margin-top: -100px;
+    margin-left: -100px;
+  }
+  to {
+    font-size: 4em;
+    margin-top: -80px;
+    margin-left: -80px;
+  }
+}
+.track-score-enter-active {
+  transition: opacity 0.3s ease;
+}
+.track-score-leave-active {
+  transition: opacity 0.6s ease;
+}
+
+.track-score-enter-from,
+.track-score-leave-to {
+  opacity: 0;
+}
+</style>

+ 313 - 309
src/features/student/scoreVerify/markBody.vue

@@ -1,309 +1,313 @@
-<template>
-  <div
-    ref="dragContainer"
-    class="mark-body-container tw-flex-auto tw-p-2 tw-pt-0"
-    @scroll="viewScroll"
-  >
-    <div v-if="!store.currentTask" class="tw-text-center none-tip">
-      {{ store.message }}
-    </div>
-    <div
-      v-else-if="!sliceImagesWithTrackList.length"
-      class="tw-text-center none-tip"
-      style="color: red"
-    >
-      考生答卷未上传
-    </div>
-    <div v-else :style="{ width: answerPaperScale }" class="tw-pt-2">
-      <div
-        v-for="(item, index) in sliceImagesWithTrackList"
-        :key="index"
-        class="single-image-container"
-        :style="{
-          width: item.width,
-        }"
-      >
-        <img :src="item.url" draggable="false" />
-        <MarkDrawTrack
-          :trackList="item.trackList"
-          :specialTagList="item.tagList"
-          :sliceImageHeight="item.originalImageHeight"
-          :sliceImageWidth="item.originalImageWidth"
-          :dx="0"
-          :dy="0"
-        />
-        <hr class="image-seperator" />
-      </div>
-    </div>
-    <ZoomPaper v-if="store.isScanImage && sliceImagesWithTrackList.length" />
-  </div>
-</template>
-
-<script setup lang="ts">
-import { reactive, watch } from "vue";
-import { store } from "@/store/store";
-import MarkDrawTrack from "@/features/mark/MarkDrawTrack.vue";
-import type { SpecialTag, Track, ColorMap } from "@/types";
-import { useTimers } from "@/setups/useTimers";
-import { loadImage, addHeaderTrackColorAttr } from "@/utils/utils";
-import { dragImage } from "@/features/mark/use/draggable";
-import ZoomPaper from "@/components/ZoomPaper.vue";
-
-interface SliceImage {
-  url: string;
-  trackList: Array<Track>;
-  tagList: Array<SpecialTag>;
-  originalImageWidth: number;
-  originalImageHeight: number;
-  width: string; // 图片在整个图片列表里面的宽度比例
-}
-
-const { origImageUrls = "sliceUrls" } = defineProps<{
-  origImageUrls?: "sheetUrls" | "sliceUrls";
-}>();
-const emit = defineEmits(["error", "getIsMultComments", "getScrollStatus"]);
-
-const { dragContainer } = dragImage();
-const viewScroll = () => {
-  if (
-    dragContainer.value.scrollTop + dragContainer.value.offsetHeight + 50 >=
-    dragContainer.value.scrollHeight
-  ) {
-    emit("getScrollStatus");
-  }
-};
-const { addTimeout } = useTimers();
-
-let sliceImagesWithTrackList: SliceImage[] = reactive([]);
-let maxImageWidth = 0;
-
-function addTrackColorAttr(tList: Track[]): Track[] {
-  let markerIds: (number | undefined)[] = tList
-    .map((v) => v.markerId)
-    .filter((x) => !!x);
-  markerIds = Array.from(new Set(markerIds));
-  // markerIds.sort();
-  let colorMap: ColorMap = {};
-  for (let i = 0; i < markerIds.length; i++) {
-    const mId: any = markerIds[i];
-    if (i == 0) {
-      colorMap[mId + ""] = "red";
-    } else if (i == 1) {
-      colorMap[mId + ""] = "blue";
-    } else if (i > 1) {
-      colorMap[mId + ""] = "gray";
-    }
-  }
-  if (Object.keys(colorMap).length > 1) {
-    emit("getIsMultComments", true);
-  }
-  tList = tList.map((item: Track) => {
-    item.color = colorMap[item.markerId + ""] || "gray";
-    item.isByMultMark = markerIds.length > 1;
-    return item;
-  });
-  return tList;
-}
-
-function addTagColorAttr(tList: SpecialTag[]): SpecialTag[] {
-  let markerIds: (number | undefined)[] = tList
-    .map((v) => v.markerId)
-    .filter((x) => !!x);
-  markerIds = Array.from(new Set(markerIds));
-  // markerIds.sort();
-  let colorMap: ColorMap = {};
-  for (let i = 0; i < markerIds.length; i++) {
-    const mId: any = markerIds[i];
-    if (i == 0) {
-      colorMap[mId + ""] = "red";
-    } else if (i == 1) {
-      colorMap[mId + ""] = "blue";
-    } else if (i > 1) {
-      colorMap[mId + ""] = "gray";
-    }
-  }
-  tList = tList.map((item: SpecialTag) => {
-    item.color = colorMap[item.markerId + ""] || "gray";
-    item.isByMultMark = markerIds.length > 1;
-    return item;
-  });
-  return tList;
-}
-
-async function processImage() {
-  if (!store.currentTask) return;
-
-  const images = [];
-  const urls = store.currentTask[origImageUrls] || [];
-  for (const url of urls) {
-    const image = await loadImage(url);
-    images.push(image);
-  }
-
-  maxImageWidth = Math.max(...images.map((i) => i.naturalWidth));
-
-  for (const url of urls) {
-    const indexInSliceUrls = urls.indexOf(url) + 1;
-    const image = images[indexInSliceUrls - 1];
-
-    const trackLists = (store.currentTask.questionList || [])
-      // .map((q) => q.trackList)
-      .map((q) => {
-        let tList = q.trackList;
-
-        return q.headerTrack?.length
-          ? addHeaderTrackColorAttr(q.headerTrack)
-          : addTrackColorAttr(tList);
-      })
-      .flat();
-    const thisImageTrackList = trackLists.filter(
-      (t) => t.offsetIndex === indexInSliceUrls
-    );
-
-    const thisImageTagList = store.currentTask.headerTagList?.length
-      ? addHeaderTrackColorAttr(
-          (store.currentTask.headerTagList || []).filter(
-            (t) => t.offsetIndex === indexInSliceUrls
-          )
-        )
-      : addTagColorAttr(
-          (store.currentTask.specialTagList || []).filter(
-            (t) => t.offsetIndex === indexInSliceUrls
-          )
-        );
-
-    // const thisImageTagList = addTagColorAttr(
-    //   (store.currentTask.specialTagList || []).filter(
-    //     (t) => t.offsetIndex === indexInSliceUrls
-    //   )
-    // );
-
-    sliceImagesWithTrackList.push({
-      url,
-      trackList: thisImageTrackList,
-      tagList: thisImageTagList,
-      originalImageWidth: image.naturalWidth,
-      originalImageHeight: image.naturalHeight,
-      width: (image.naturalWidth / maxImageWidth) * 100 + "%",
-    });
-  }
-}
-
-// 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 {
-    store.globalMask = true;
-    await processImage();
-  } catch (error) {
-    sliceImagesWithTrackList.splice(0);
-    console.log("render error ", error);
-    // 图片加载出错,自动加载下一个任务
-    emit("error");
-  } finally {
-    await new Promise((res) => setTimeout(res, 500));
-    store.globalMask = false;
-    renderLock = false;
-  }
-};
-
-watch(() => store.currentTask, renderPaperAndMark);
-
-watch(
-  (): (number | undefined)[] => [
-    store.minimapScrollToX,
-    store.minimapScrollToY,
-  ],
-  () => {
-    const container = document.querySelector<HTMLDivElement>(
-      ".mark-body-container"
-    );
-    addTimeout(() => {
-      if (
-        container &&
-        typeof store.minimapScrollToX === "number" &&
-        typeof store.minimapScrollToY === "number"
-      ) {
-        const { scrollWidth, scrollHeight } = container;
-        container.scrollTo({
-          top: scrollHeight * store.minimapScrollToY,
-          left: scrollWidth * store.minimapScrollToX,
-          behavior: "smooth",
-        });
-      }
-    }, 10);
-  }
-);
-
-const answerPaperScale = $computed(() => {
-  // 放大、缩小不影响页面之前的滚动条定位
-  let percentWidth = 0;
-  let percentTop = 0;
-  const container = document.querySelector(".mark-body-container");
-  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>
-
-<style scoped>
-.mark-body-container .none-tip {
-  height: 100%;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  font-size: 28px;
-}
-.mark-body-container {
-  height: calc(100vh - 56px);
-  overflow: auto;
-  background-color: var(--app-container-bg-color);
-  background-image: linear-gradient(45deg, #e0e0e0 25%, transparent 25%),
-    linear-gradient(-45deg, #e0e0e0 25%, transparent 25%),
-    linear-gradient(45deg, transparent 75%, #e0e0e0 75%),
-    linear-gradient(-45deg, transparent 75%, #e0e0e0 75%);
-  background-size: 20px 20px;
-  background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
-  transform: inherit;
-
-  cursor: grab;
-  user-select: none;
-}
-.mark-body-container img {
-  width: 100%;
-}
-.single-image-container {
-  position: relative;
-}
-.image-seperator {
-  border: 2px solid rgba(120, 120, 120, 0.1);
-}
-</style>
+<template>
+  <div
+    ref="dragContainer"
+    class="mark-body-container tw-flex-auto tw-p-2 tw-pt-0"
+    @scroll="viewScroll"
+  >
+    <div v-if="!store.currentTask" class="tw-text-center none-tip">
+      {{ store.message }}
+    </div>
+    <div
+      v-else-if="!sliceImagesWithTrackList.length"
+      class="tw-text-center none-tip"
+      style="color: red"
+    >
+      考生答卷未上传
+    </div>
+    <div v-else :style="{ width: answerPaperScale }" class="tw-pt-2">
+      <div
+        v-for="(item, index) in sliceImagesWithTrackList"
+        :key="index"
+        class="single-image-container"
+        :style="{
+          width: item.width,
+        }"
+      >
+        <img :src="item.url" draggable="false" />
+        <MarkDrawTrack
+          :trackList="item.trackList"
+          :specialTagList="item.tagList"
+          :sliceImageHeight="item.originalImageHeight"
+          :sliceImageWidth="item.originalImageWidth"
+          :dx="0"
+          :dy="0"
+        />
+        <hr class="image-seperator" />
+      </div>
+    </div>
+    <ZoomPaper v-if="store.isScanImage && sliceImagesWithTrackList.length" />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, watch } from "vue";
+import { store } from "@/store/store";
+import MarkDrawTrack from "@/features/mark/MarkDrawTrack.vue";
+import type { SpecialTag, Track, ColorMap } from "@/types";
+import { useTimers } from "@/setups/useTimers";
+import { loadImage, addHeaderTrackColorAttr } from "@/utils/utils";
+import { dragImage } from "@/features/mark/use/draggable";
+import ZoomPaper from "@/components/ZoomPaper.vue";
+
+interface SliceImage {
+  url: string;
+  trackList: Array<Track>;
+  tagList: Array<SpecialTag>;
+  originalImageWidth: number;
+  originalImageHeight: number;
+  width: string; // 图片在整个图片列表里面的宽度比例
+}
+
+const { origImageUrls = "sliceUrls" } = defineProps<{
+  origImageUrls?: "sheetUrls" | "sliceUrls";
+}>();
+const emit = defineEmits(["error", "getIsMultComments", "getScrollStatus"]);
+
+const { dragContainer } = dragImage();
+const viewScroll = () => {
+  if (
+    dragContainer.value.scrollTop + dragContainer.value.offsetHeight + 50 >=
+    dragContainer.value.scrollHeight
+  ) {
+    emit("getScrollStatus");
+  }
+};
+const { addTimeout } = useTimers();
+
+let sliceImagesWithTrackList: SliceImage[] = reactive([]);
+let maxImageWidth = 0;
+
+function addTrackColorAttr(tList: Track[]): Track[] {
+  let markerIds: (number | undefined)[] = tList
+    .map((v) => v.markerId)
+    .filter((x) => !!x);
+  markerIds = Array.from(new Set(markerIds));
+  // markerIds.sort();
+  let colorMap: ColorMap = {};
+  for (let i = 0; i < markerIds.length; i++) {
+    const mId: any = markerIds[i];
+    if (i == 0) {
+      colorMap[mId + ""] = "#F53F3F";
+    } else if (i == 1) {
+      colorMap[mId + ""] = "#165DFF";
+    } else if (i == 2) {
+      colorMap[mId + ""] = "#FAAD14";
+    } else if (i > 2) {
+      colorMap[mId + ""] = "gray";
+    }
+  }
+  if (Object.keys(colorMap).length > 1) {
+    emit("getIsMultComments", true);
+  }
+  tList = tList.map((item: Track) => {
+    item.color = colorMap[item.markerId + ""] || "gray";
+    item.isByMultMark = markerIds.length > 1;
+    return item;
+  });
+  return tList;
+}
+
+function addTagColorAttr(tList: SpecialTag[]): SpecialTag[] {
+  let markerIds: (number | undefined)[] = tList
+    .map((v) => v.markerId)
+    .filter((x) => !!x);
+  markerIds = Array.from(new Set(markerIds));
+  // markerIds.sort();
+  let colorMap: ColorMap = {};
+  for (let i = 0; i < markerIds.length; i++) {
+    const mId: any = markerIds[i];
+    if (i == 0) {
+      colorMap[mId + ""] = "#F53F3F";
+    } else if (i == 1) {
+      colorMap[mId + ""] = "#165DFF";
+    } else if (i == 2) {
+      colorMap[mId + ""] = "#FAAD14";
+    } else if (i > 2) {
+      colorMap[mId + ""] = "gray";
+    }
+  }
+  tList = tList.map((item: SpecialTag) => {
+    item.color = colorMap[item.markerId + ""] || "gray";
+    item.isByMultMark = markerIds.length > 1;
+    return item;
+  });
+  return tList;
+}
+
+async function processImage() {
+  if (!store.currentTask) return;
+
+  const images = [];
+  const urls = store.currentTask[origImageUrls] || [];
+  for (const url of urls) {
+    const image = await loadImage(url);
+    images.push(image);
+  }
+
+  maxImageWidth = Math.max(...images.map((i) => i.naturalWidth));
+
+  for (const url of urls) {
+    const indexInSliceUrls = urls.indexOf(url) + 1;
+    const image = images[indexInSliceUrls - 1];
+
+    const trackLists = (store.currentTask.questionList || [])
+      // .map((q) => q.trackList)
+      .map((q) => {
+        let tList = q.trackList;
+
+        return q.headerTrack?.length
+          ? addHeaderTrackColorAttr(q.headerTrack)
+          : addTrackColorAttr(tList);
+      })
+      .flat();
+    const thisImageTrackList = trackLists.filter(
+      (t) => t.offsetIndex === indexInSliceUrls
+    );
+
+    const thisImageTagList = store.currentTask.headerTagList?.length
+      ? addHeaderTrackColorAttr(
+          (store.currentTask.headerTagList || []).filter(
+            (t) => t.offsetIndex === indexInSliceUrls
+          )
+        )
+      : addTagColorAttr(
+          (store.currentTask.specialTagList || []).filter(
+            (t) => t.offsetIndex === indexInSliceUrls
+          )
+        );
+
+    // const thisImageTagList = addTagColorAttr(
+    //   (store.currentTask.specialTagList || []).filter(
+    //     (t) => t.offsetIndex === indexInSliceUrls
+    //   )
+    // );
+
+    sliceImagesWithTrackList.push({
+      url,
+      trackList: thisImageTrackList,
+      tagList: thisImageTagList,
+      originalImageWidth: image.naturalWidth,
+      originalImageHeight: image.naturalHeight,
+      width: (image.naturalWidth / maxImageWidth) * 100 + "%",
+    });
+  }
+}
+
+// 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 {
+    store.globalMask = true;
+    await processImage();
+  } catch (error) {
+    sliceImagesWithTrackList.splice(0);
+    console.log("render error ", error);
+    // 图片加载出错,自动加载下一个任务
+    emit("error");
+  } finally {
+    await new Promise((res) => setTimeout(res, 500));
+    store.globalMask = false;
+    renderLock = false;
+  }
+};
+
+watch(() => store.currentTask, renderPaperAndMark);
+
+watch(
+  (): (number | undefined)[] => [
+    store.minimapScrollToX,
+    store.minimapScrollToY,
+  ],
+  () => {
+    const container = document.querySelector<HTMLDivElement>(
+      ".mark-body-container"
+    );
+    addTimeout(() => {
+      if (
+        container &&
+        typeof store.minimapScrollToX === "number" &&
+        typeof store.minimapScrollToY === "number"
+      ) {
+        const { scrollWidth, scrollHeight } = container;
+        container.scrollTo({
+          top: scrollHeight * store.minimapScrollToY,
+          left: scrollWidth * store.minimapScrollToX,
+          behavior: "smooth",
+        });
+      }
+    }, 10);
+  }
+);
+
+const answerPaperScale = $computed(() => {
+  // 放大、缩小不影响页面之前的滚动条定位
+  let percentWidth = 0;
+  let percentTop = 0;
+  const container = document.querySelector(".mark-body-container");
+  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>
+
+<style scoped>
+.mark-body-container .none-tip {
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  font-size: 28px;
+}
+.mark-body-container {
+  height: calc(100vh - 56px);
+  overflow: auto;
+  background-color: var(--app-container-bg-color);
+  background-image: linear-gradient(45deg, #e0e0e0 25%, transparent 25%),
+    linear-gradient(-45deg, #e0e0e0 25%, transparent 25%),
+    linear-gradient(45deg, transparent 75%, #e0e0e0 75%),
+    linear-gradient(-45deg, transparent 75%, #e0e0e0 75%);
+  background-size: 20px 20px;
+  background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
+  transform: inherit;
+
+  cursor: grab;
+  user-select: none;
+}
+.mark-body-container img {
+  width: 100%;
+}
+.single-image-container {
+  position: relative;
+}
+.image-seperator {
+  border: 2px solid rgba(120, 120, 120, 0.1);
+}
+</style>

+ 291 - 287
src/features/student/studentInspect/MarkBody.vue

@@ -1,287 +1,291 @@
-<template>
-  <div
-    ref="dragContainer"
-    class="mark-body-container tw-flex-auto tw-p-2 tw-pt-0"
-    @scroll="viewScroll"
-  >
-    <div v-if="!store.currentTask" class="tw-text-center">
-      {{ store.message }}
-    </div>
-    <div v-else :style="{ width: answerPaperScale }" class="tw-pt-2">
-      <div
-        v-for="(item, index) in sliceImagesWithTrackList"
-        :key="index"
-        class="single-image-container"
-        :style="{
-          width: item.width,
-        }"
-      >
-        <img :src="item.url" draggable="false" />
-        <MarkDrawTrack
-          :trackList="item.trackList"
-          :specialTagList="item.tagList"
-          :sliceImageHeight="item.originalImageHeight"
-          :sliceImageWidth="item.originalImageWidth"
-          :dx="0"
-          :dy="0"
-        />
-        <hr class="image-seperator" />
-      </div>
-    </div>
-    <ZoomPaper v-if="store.isScanImage && sliceImagesWithTrackList.length" />
-  </div>
-</template>
-
-<script setup lang="ts">
-import { reactive, watch } from "vue";
-import { store } from "@/store/store";
-import MarkDrawTrack from "@/features/mark/MarkDrawTrack.vue";
-import type { SpecialTag, Track, ColorMap } from "@/types";
-import { useTimers } from "@/setups/useTimers";
-import { loadImage, addHeaderTrackColorAttr } from "@/utils/utils";
-import { dragImage } from "@/features/mark/use/draggable";
-import ZoomPaper from "@/components/ZoomPaper.vue";
-
-interface SliceImage {
-  url: string;
-  trackList: Array<Track>;
-  tagList: Array<SpecialTag>;
-  originalImageWidth: number;
-  originalImageHeight: number;
-  width: string; // 图片在整个图片列表里面的宽度比例
-}
-
-const { origImageUrls = "sliceUrls" } = defineProps<{
-  origImageUrls?: "sheetUrls" | "sliceUrls";
-}>();
-const emit = defineEmits(["error", "getIsMultComments", "getScrollStatus"]);
-
-const { dragContainer } = dragImage();
-const viewScroll = () => {
-  if (
-    dragContainer.value.scrollTop + dragContainer.value.offsetHeight + 50 >=
-    dragContainer.value.scrollHeight
-  ) {
-    emit("getScrollStatus");
-  }
-};
-const { addTimeout } = useTimers();
-
-let sliceImagesWithTrackList: SliceImage[] = reactive([]);
-let maxImageWidth = 0;
-
-function addTrackColorAttr(tList: Track[]): Track[] {
-  let markerIds: (number | undefined)[] = tList
-    .map((v) => v.markerId)
-    .filter((x) => !!x);
-  markerIds = Array.from(new Set(markerIds));
-  // markerIds.sort();
-  let colorMap: ColorMap = {};
-  for (let i = 0; i < markerIds.length; i++) {
-    const mId: any = markerIds[i];
-    if (i == 0) {
-      colorMap[mId + ""] = "red";
-    } else if (i == 1) {
-      colorMap[mId + ""] = "blue";
-    } else if (i > 1) {
-      colorMap[mId + ""] = "gray";
-    }
-  }
-  if (Object.keys(colorMap).length > 1) {
-    emit("getIsMultComments", true);
-  }
-  tList = tList.map((item: Track) => {
-    item.color = colorMap[item.markerId + ""] || "gray";
-    item.isByMultMark = markerIds.length > 1;
-    return item;
-  });
-  return tList;
-}
-
-function addTagColorAttr(tList: SpecialTag[]): SpecialTag[] {
-  let markerIds: (number | undefined)[] = tList
-    .map((v) => v.markerId)
-    .filter((x) => !!x);
-  markerIds = Array.from(new Set(markerIds));
-  // markerIds.sort();
-  let colorMap: ColorMap = {};
-  for (let i = 0; i < markerIds.length; i++) {
-    const mId: any = markerIds[i];
-    if (i == 0) {
-      colorMap[mId + ""] = "red";
-    } else if (i == 1) {
-      colorMap[mId + ""] = "blue";
-    } else if (i > 1) {
-      colorMap[mId + ""] = "gray";
-    }
-  }
-  tList = tList.map((item: SpecialTag) => {
-    item.color = colorMap[item.markerId + ""] || "gray";
-    item.isByMultMark = markerIds.length > 1;
-    return item;
-  });
-  return tList;
-}
-
-async function processImage() {
-  if (!store.currentTask) return;
-
-  const images = [];
-  const urls = store.currentTask[origImageUrls] || [];
-  for (const url of urls) {
-    const image = await loadImage(url);
-    images.push(image);
-  }
-
-  maxImageWidth = Math.max(...images.map((i) => i.naturalWidth));
-
-  for (const url of urls) {
-    const indexInSliceUrls = urls.indexOf(url) + 1;
-    const image = images[indexInSliceUrls - 1];
-
-    const trackLists = (store.currentTask.questionList || [])
-      // .map((q) => q.trackList)
-      .map((q) => {
-        let tList = q.trackList;
-        return q.headerTrack?.length
-          ? addHeaderTrackColorAttr(q.headerTrack)
-          : addTrackColorAttr(tList);
-      })
-      .flat();
-    const thisImageTrackList = trackLists.filter(
-      (t) => t.offsetIndex === indexInSliceUrls
-    );
-    const thisImageTagList = store.currentTask.headerTagList?.length
-      ? addHeaderTrackColorAttr(
-          (store.currentTask.headerTagList || []).filter(
-            (t) => t.offsetIndex === indexInSliceUrls
-          )
-        )
-      : addTagColorAttr(
-          (store.currentTask.specialTagList || []).filter(
-            (t) => t.offsetIndex === indexInSliceUrls
-          )
-        );
-
-    sliceImagesWithTrackList.push({
-      url,
-      trackList: thisImageTrackList,
-      tagList: thisImageTagList,
-      originalImageWidth: image.naturalWidth,
-      originalImageHeight: image.naturalHeight,
-      width: (image.naturalWidth / maxImageWidth) * 100 + "%",
-    });
-  }
-}
-
-// 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 {
-    store.globalMask = true;
-    await processImage();
-  } catch (error) {
-    sliceImagesWithTrackList.splice(0);
-    console.log("render error ", error);
-    // 图片加载出错,自动加载下一个任务
-    emit("error");
-  } finally {
-    await new Promise((res) => setTimeout(res, 500));
-    store.globalMask = false;
-    renderLock = false;
-  }
-};
-
-watch(() => store.currentTask, renderPaperAndMark);
-
-watch(
-  (): (number | undefined)[] => [
-    store.minimapScrollToX,
-    store.minimapScrollToY,
-  ],
-  () => {
-    const container = document.querySelector<HTMLDivElement>(
-      ".mark-body-container"
-    );
-    addTimeout(() => {
-      if (
-        container &&
-        typeof store.minimapScrollToX === "number" &&
-        typeof store.minimapScrollToY === "number"
-      ) {
-        const { scrollWidth, scrollHeight } = container;
-        container.scrollTo({
-          top: scrollHeight * store.minimapScrollToY,
-          left: scrollWidth * store.minimapScrollToX,
-          behavior: "smooth",
-        });
-      }
-    }, 10);
-  }
-);
-
-const answerPaperScale = $computed(() => {
-  // 放大、缩小不影响页面之前的滚动条定位
-  let percentWidth = 0;
-  let percentTop = 0;
-  const container = document.querySelector(".mark-body-container");
-  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>
-
-<style scoped>
-.mark-body-container {
-  height: calc(100vh - 56px);
-  overflow: auto;
-  background-color: var(--app-container-bg-color);
-  background-image: linear-gradient(45deg, #e0e0e0 25%, transparent 25%),
-    linear-gradient(-45deg, #e0e0e0 25%, transparent 25%),
-    linear-gradient(45deg, transparent 75%, #e0e0e0 75%),
-    linear-gradient(-45deg, transparent 75%, #e0e0e0 75%);
-  background-size: 20px 20px;
-  background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
-  transform: inherit;
-
-  cursor: grab;
-  user-select: none;
-}
-.mark-body-container img {
-  width: 100%;
-}
-.single-image-container {
-  position: relative;
-}
-.image-seperator {
-  border: 2px solid rgba(120, 120, 120, 0.1);
-}
-</style>
+<template>
+  <div
+    ref="dragContainer"
+    class="mark-body-container tw-flex-auto tw-p-2 tw-pt-0"
+    @scroll="viewScroll"
+  >
+    <div v-if="!store.currentTask" class="tw-text-center">
+      {{ store.message }}
+    </div>
+    <div v-else :style="{ width: answerPaperScale }" class="tw-pt-2">
+      <div
+        v-for="(item, index) in sliceImagesWithTrackList"
+        :key="index"
+        class="single-image-container"
+        :style="{
+          width: item.width,
+        }"
+      >
+        <img :src="item.url" draggable="false" />
+        <MarkDrawTrack
+          :trackList="item.trackList"
+          :specialTagList="item.tagList"
+          :sliceImageHeight="item.originalImageHeight"
+          :sliceImageWidth="item.originalImageWidth"
+          :dx="0"
+          :dy="0"
+        />
+        <hr class="image-seperator" />
+      </div>
+    </div>
+    <ZoomPaper v-if="store.isScanImage && sliceImagesWithTrackList.length" />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, watch } from "vue";
+import { store } from "@/store/store";
+import MarkDrawTrack from "@/features/mark/MarkDrawTrack.vue";
+import type { SpecialTag, Track, ColorMap } from "@/types";
+import { useTimers } from "@/setups/useTimers";
+import { loadImage, addHeaderTrackColorAttr } from "@/utils/utils";
+import { dragImage } from "@/features/mark/use/draggable";
+import ZoomPaper from "@/components/ZoomPaper.vue";
+
+interface SliceImage {
+  url: string;
+  trackList: Array<Track>;
+  tagList: Array<SpecialTag>;
+  originalImageWidth: number;
+  originalImageHeight: number;
+  width: string; // 图片在整个图片列表里面的宽度比例
+}
+
+const { origImageUrls = "sliceUrls" } = defineProps<{
+  origImageUrls?: "sheetUrls" | "sliceUrls";
+}>();
+const emit = defineEmits(["error", "getIsMultComments", "getScrollStatus"]);
+
+const { dragContainer } = dragImage();
+const viewScroll = () => {
+  if (
+    dragContainer.value.scrollTop + dragContainer.value.offsetHeight + 50 >=
+    dragContainer.value.scrollHeight
+  ) {
+    emit("getScrollStatus");
+  }
+};
+const { addTimeout } = useTimers();
+
+let sliceImagesWithTrackList: SliceImage[] = reactive([]);
+let maxImageWidth = 0;
+
+function addTrackColorAttr(tList: Track[]): Track[] {
+  let markerIds: (number | undefined)[] = tList
+    .map((v) => v.markerId)
+    .filter((x) => !!x);
+  markerIds = Array.from(new Set(markerIds));
+  // markerIds.sort();
+  let colorMap: ColorMap = {};
+  for (let i = 0; i < markerIds.length; i++) {
+    const mId: any = markerIds[i];
+    if (i == 0) {
+      colorMap[mId + ""] = "#F53F3F";
+    } else if (i == 1) {
+      colorMap[mId + ""] = "#165DFF";
+    } else if (i == 2) {
+      colorMap[mId + ""] = "#FAAD14";
+    } else if (i > 2) {
+      colorMap[mId + ""] = "gray";
+    }
+  }
+  if (Object.keys(colorMap).length > 1) {
+    emit("getIsMultComments", true);
+  }
+  tList = tList.map((item: Track) => {
+    item.color = colorMap[item.markerId + ""] || "gray";
+    item.isByMultMark = markerIds.length > 1;
+    return item;
+  });
+  return tList;
+}
+
+function addTagColorAttr(tList: SpecialTag[]): SpecialTag[] {
+  let markerIds: (number | undefined)[] = tList
+    .map((v) => v.markerId)
+    .filter((x) => !!x);
+  markerIds = Array.from(new Set(markerIds));
+  // markerIds.sort();
+  let colorMap: ColorMap = {};
+  for (let i = 0; i < markerIds.length; i++) {
+    const mId: any = markerIds[i];
+    if (i == 0) {
+      colorMap[mId + ""] = "#F53F3F";
+    } else if (i == 1) {
+      colorMap[mId + ""] = "#165DFF";
+    } else if (i == 2) {
+      colorMap[mId + ""] = "#FAAD14";
+    } else if (i > 2) {
+      colorMap[mId + ""] = "gray";
+    }
+  }
+  tList = tList.map((item: SpecialTag) => {
+    item.color = colorMap[item.markerId + ""] || "gray";
+    item.isByMultMark = markerIds.length > 1;
+    return item;
+  });
+  return tList;
+}
+
+async function processImage() {
+  if (!store.currentTask) return;
+
+  const images = [];
+  const urls = store.currentTask[origImageUrls] || [];
+  for (const url of urls) {
+    const image = await loadImage(url);
+    images.push(image);
+  }
+
+  maxImageWidth = Math.max(...images.map((i) => i.naturalWidth));
+
+  for (const url of urls) {
+    const indexInSliceUrls = urls.indexOf(url) + 1;
+    const image = images[indexInSliceUrls - 1];
+
+    const trackLists = (store.currentTask.questionList || [])
+      // .map((q) => q.trackList)
+      .map((q) => {
+        let tList = q.trackList;
+        return q.headerTrack?.length
+          ? addHeaderTrackColorAttr(q.headerTrack)
+          : addTrackColorAttr(tList);
+      })
+      .flat();
+    const thisImageTrackList = trackLists.filter(
+      (t) => t.offsetIndex === indexInSliceUrls
+    );
+    const thisImageTagList = store.currentTask.headerTagList?.length
+      ? addHeaderTrackColorAttr(
+          (store.currentTask.headerTagList || []).filter(
+            (t) => t.offsetIndex === indexInSliceUrls
+          )
+        )
+      : addTagColorAttr(
+          (store.currentTask.specialTagList || []).filter(
+            (t) => t.offsetIndex === indexInSliceUrls
+          )
+        );
+
+    sliceImagesWithTrackList.push({
+      url,
+      trackList: thisImageTrackList,
+      tagList: thisImageTagList,
+      originalImageWidth: image.naturalWidth,
+      originalImageHeight: image.naturalHeight,
+      width: (image.naturalWidth / maxImageWidth) * 100 + "%",
+    });
+  }
+}
+
+// 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 {
+    store.globalMask = true;
+    await processImage();
+  } catch (error) {
+    sliceImagesWithTrackList.splice(0);
+    console.log("render error ", error);
+    // 图片加载出错,自动加载下一个任务
+    emit("error");
+  } finally {
+    await new Promise((res) => setTimeout(res, 500));
+    store.globalMask = false;
+    renderLock = false;
+  }
+};
+
+watch(() => store.currentTask, renderPaperAndMark);
+
+watch(
+  (): (number | undefined)[] => [
+    store.minimapScrollToX,
+    store.minimapScrollToY,
+  ],
+  () => {
+    const container = document.querySelector<HTMLDivElement>(
+      ".mark-body-container"
+    );
+    addTimeout(() => {
+      if (
+        container &&
+        typeof store.minimapScrollToX === "number" &&
+        typeof store.minimapScrollToY === "number"
+      ) {
+        const { scrollWidth, scrollHeight } = container;
+        container.scrollTo({
+          top: scrollHeight * store.minimapScrollToY,
+          left: scrollWidth * store.minimapScrollToX,
+          behavior: "smooth",
+        });
+      }
+    }, 10);
+  }
+);
+
+const answerPaperScale = $computed(() => {
+  // 放大、缩小不影响页面之前的滚动条定位
+  let percentWidth = 0;
+  let percentTop = 0;
+  const container = document.querySelector(".mark-body-container");
+  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>
+
+<style scoped>
+.mark-body-container {
+  height: calc(100vh - 56px);
+  overflow: auto;
+  background-color: var(--app-container-bg-color);
+  background-image: linear-gradient(45deg, #e0e0e0 25%, transparent 25%),
+    linear-gradient(-45deg, #e0e0e0 25%, transparent 25%),
+    linear-gradient(45deg, transparent 75%, #e0e0e0 75%),
+    linear-gradient(-45deg, transparent 75%, #e0e0e0 75%);
+  background-size: 20px 20px;
+  background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
+  transform: inherit;
+
+  cursor: grab;
+  user-select: none;
+}
+.mark-body-container img {
+  width: 100%;
+}
+.single-image-container {
+  position: relative;
+}
+.image-seperator {
+  border: 2px solid rgba(120, 120, 120, 0.1);
+}
+</style>

+ 1 - 1
src/utils/utils.ts

@@ -351,7 +351,7 @@ export function addFileServerPrefixToTask(rawTask: Task): Task {
 
 export function addHeaderTrackColorAttr(headerTrack: any): any {
   return headerTrack.map((item: any) => {
-    item.color = "green";
+    item.color = "#00B42A";
     return item;
   });
 }