|
@@ -1,60 +1,68 @@
|
|
|
<template>
|
|
|
- <div
|
|
|
- class="mark-body-container tw-flex-auto tw-p-2"
|
|
|
- :class="isGrabbing && 'grabbing'"
|
|
|
- ref="container"
|
|
|
- @mousedown="mouseDownHandler"
|
|
|
- @mouseup="mouseUpHandler"
|
|
|
- @mousemove="mouseMoveHandler"
|
|
|
- >
|
|
|
- <div v-if="!store.currentTask" class="tw-text-center">暂无待复核任务</div>
|
|
|
- <div v-else :style="{ width: answerPaperScale }">
|
|
|
- <div
|
|
|
- v-for="(item, index) in sliceImagesWithTrackList"
|
|
|
- :key="index"
|
|
|
- class="single-image-container"
|
|
|
- >
|
|
|
- <img :src="item.url" draggable="false" />
|
|
|
- <MarkDrawTrack
|
|
|
- :track-list="item.trackList"
|
|
|
- :original-image="item.originalImage"
|
|
|
- />
|
|
|
- <hr class="image-seperator" />
|
|
|
+ <div class="mark-body-container tw-flex-auto tw-p-2" ref="dragContainer">
|
|
|
+ <a-spin
|
|
|
+ :spinning="rendering"
|
|
|
+ size="large"
|
|
|
+ tip="Loading..."
|
|
|
+ style="margin-top: 50px"
|
|
|
+ >
|
|
|
+ <div v-if="!store.currentTask" class="tw-text-center">
|
|
|
+ {{ store.message }}
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ <div v-else :style="{ width: answerPaperScale }">
|
|
|
+ <div
|
|
|
+ v-for="(item, index) in sliceImagesWithTrackList"
|
|
|
+ :key="index"
|
|
|
+ class="single-image-container"
|
|
|
+ >
|
|
|
+ <img :src="item.url" draggable="false" />
|
|
|
+ <MarkDrawTrack
|
|
|
+ :track-list="item.trackList"
|
|
|
+ :special-tag-list="item.tagList"
|
|
|
+ :original-image="item.originalImage"
|
|
|
+ />
|
|
|
+ <hr class="image-seperator" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </a-spin>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts">
|
|
|
-import {
|
|
|
- computed,
|
|
|
- defineComponent,
|
|
|
- onUnmounted,
|
|
|
- reactive,
|
|
|
- ref,
|
|
|
- watchEffect,
|
|
|
-} from "vue";
|
|
|
+import { computed, defineComponent, reactive, ref, watchEffect } from "vue";
|
|
|
import { store } from "./store";
|
|
|
import filters from "@/filters";
|
|
|
import MarkDrawTrack from "./MarkDrawTrack.vue";
|
|
|
-import { Track } from "@/types";
|
|
|
+import { SpecialTag, Track } from "@/types";
|
|
|
import { useTimers } from "@/setups/useTimers";
|
|
|
-import { loadImage } from "@/utils/utils";
|
|
|
+import {
|
|
|
+ getDataUrlForSliceConfig,
|
|
|
+ getDataUrlForSplitConfig,
|
|
|
+ loadImage,
|
|
|
+} from "@/utils/utils";
|
|
|
+import { dragImage } from "@/features/mark/use/draggable";
|
|
|
|
|
|
interface SliceImage {
|
|
|
url: string;
|
|
|
indexInSliceUrls: number;
|
|
|
trackList: Array<Track>;
|
|
|
+ tagList: Array<SpecialTag>;
|
|
|
originalImage: HTMLImageElement;
|
|
|
}
|
|
|
+// should not render twice at the same time
|
|
|
+let __lock = false;
|
|
|
+let __currentLibraryId = -1; // save __currentLibraryId of lock
|
|
|
export default defineComponent({
|
|
|
name: "MarkBody",
|
|
|
components: { MarkDrawTrack },
|
|
|
- setup() {
|
|
|
+ emits: ["error"],
|
|
|
+ setup(props, { emit }) {
|
|
|
+ const { dragContainer } = dragImage();
|
|
|
+
|
|
|
const { addTimeout } = useTimers();
|
|
|
|
|
|
+ let rendering = ref(false);
|
|
|
let sliceImagesWithTrackList: Array<SliceImage> = reactive([]);
|
|
|
- let _studentId = -1; // 判断是否改变了任务
|
|
|
|
|
|
async function processImage() {
|
|
|
if (!store.currentTask) return;
|
|
@@ -67,14 +75,14 @@ export default defineComponent({
|
|
|
images.push(image);
|
|
|
}
|
|
|
|
|
|
- // TODO: add loading
|
|
|
for (const url of store.currentTask.sliceUrls) {
|
|
|
const completeUrl = filters.toCompleteUrlWithFileServer(
|
|
|
store.setting.fileServer,
|
|
|
url
|
|
|
);
|
|
|
|
|
|
- const image = await loadImage(completeUrl);
|
|
|
+ const indexInSliceUrls = store.currentTask.sliceUrls.indexOf(url) + 1;
|
|
|
+ const image = images[indexInSliceUrls - 1];
|
|
|
|
|
|
const trackLists = store.currentTask.questionList
|
|
|
.map((q) => q.trackList)
|
|
@@ -83,30 +91,53 @@ export default defineComponent({
|
|
|
return acc;
|
|
|
}, [] as Array<Track>);
|
|
|
const thisImageTrackList = trackLists.filter(
|
|
|
- (t) =>
|
|
|
- t.offsetIndex ===
|
|
|
- (store.currentTask && store.currentTask.sliceUrls.indexOf(url) + 1)
|
|
|
+ (t) => t.offsetIndex === indexInSliceUrls
|
|
|
+ );
|
|
|
+ const thisImageTagList = store.currentTask.specialTagList.filter(
|
|
|
+ (t) => t.offsetIndex === indexInSliceUrls
|
|
|
);
|
|
|
|
|
|
sliceImagesWithTrackList.push({
|
|
|
url: completeUrl,
|
|
|
- indexInSliceUrls: store.currentTask.sliceUrls.indexOf(url) + 1,
|
|
|
+ indexInSliceUrls,
|
|
|
trackList: thisImageTrackList,
|
|
|
+ tagList: thisImageTagList,
|
|
|
originalImage: image,
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
const renderPaperAndMark = async () => {
|
|
|
- if (!store.currentTask) return;
|
|
|
+ if (__lock) {
|
|
|
+ if (store.currentTask?.libraryId === __currentLibraryId) {
|
|
|
+ console.log("重复渲染,返回");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ console.log("上个任务还未渲染完毕,稍等一秒再尝试渲染");
|
|
|
+ await new Promise((res) => setTimeout(res, 1000));
|
|
|
+ await renderPaperAndMark();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ __lock = true;
|
|
|
+ __currentLibraryId = store.currentTask?.libraryId ?? -1;
|
|
|
+ sliceImagesWithTrackList.splice(0);
|
|
|
|
|
|
- // reset sliceImagesWithTrackList ,当切换任务时,要重新绘制图片和轨迹
|
|
|
- if (_studentId !== store.currentTask.studentId) {
|
|
|
- // 还原轨迹用得上
|
|
|
- sliceImagesWithTrackList.splice(0);
|
|
|
- _studentId = store.currentTask.studentId;
|
|
|
+ if (!store.currentTask) {
|
|
|
+ __lock = false;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- await processImage();
|
|
|
+ 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);
|
|
@@ -137,46 +168,12 @@ export default defineComponent({
|
|
|
return scale * 100 + "%";
|
|
|
});
|
|
|
|
|
|
- // grab moving
|
|
|
- let pos = { top: 0, left: 0, x: 0, y: 0 };
|
|
|
- const container = ref((null as unknown) as HTMLDivElement);
|
|
|
- const isGrabbing = ref(false);
|
|
|
-
|
|
|
- const mouseDownHandler = function (e: MouseEvent) {
|
|
|
- pos = {
|
|
|
- // The current scroll
|
|
|
- left: container.value.scrollLeft,
|
|
|
- top: container.value.scrollTop,
|
|
|
- // Get the current mouse position
|
|
|
- x: e.clientX,
|
|
|
- y: e.clientY,
|
|
|
- };
|
|
|
- isGrabbing.value = true;
|
|
|
- };
|
|
|
-
|
|
|
- const mouseMoveHandler = function (e: MouseEvent) {
|
|
|
- if (!isGrabbing.value) return;
|
|
|
- // How far the mouse has been moved
|
|
|
- const dx = e.clientX - pos.x;
|
|
|
- const dy = e.clientY - pos.y;
|
|
|
-
|
|
|
- // Scroll the element
|
|
|
- container.value.scrollTop = pos.top - dy;
|
|
|
- container.value.scrollLeft = pos.left - dx;
|
|
|
- };
|
|
|
- const mouseUpHandler = function () {
|
|
|
- isGrabbing.value = false;
|
|
|
- };
|
|
|
-
|
|
|
return {
|
|
|
+ dragContainer,
|
|
|
store,
|
|
|
+ rendering,
|
|
|
sliceImagesWithTrackList,
|
|
|
answerPaperScale,
|
|
|
- container,
|
|
|
- isGrabbing,
|
|
|
- mouseDownHandler,
|
|
|
- mouseMoveHandler,
|
|
|
- mouseUpHandler,
|
|
|
};
|
|
|
},
|
|
|
});
|