|
@@ -0,0 +1,155 @@
|
|
|
+<template>
|
|
|
+ <div class="mark-body-container tw-flex-auto">
|
|
|
+ <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>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts">
|
|
|
+import { computed, defineComponent, reactive, watchEffect } from "vue";
|
|
|
+import { store } from "./store";
|
|
|
+import filters from "@/filters";
|
|
|
+import MarkDrawTrack from "./MarkDrawTrack.vue";
|
|
|
+import { Track } from "@/types";
|
|
|
+import { useTimers } from "@/setups/useTimers";
|
|
|
+import { loadImage } from "@/utils/utils";
|
|
|
+
|
|
|
+interface SliceImage {
|
|
|
+ url: string;
|
|
|
+ indexInSliceUrls: number;
|
|
|
+ trackList: Array<Track>;
|
|
|
+ originalImage: HTMLImageElement;
|
|
|
+}
|
|
|
+export default defineComponent({
|
|
|
+ name: "MarkBody",
|
|
|
+ components: { MarkDrawTrack },
|
|
|
+ setup() {
|
|
|
+ const { addTimeout } = useTimers();
|
|
|
+
|
|
|
+ let sliceImagesWithTrackList: Array<SliceImage> = reactive([]);
|
|
|
+ let _studentId = -1; // 判断是否改变了任务
|
|
|
+
|
|
|
+ async function processImage() {
|
|
|
+ if (!store.currentTask) return;
|
|
|
+
|
|
|
+ const images = [];
|
|
|
+ for (const url of store.currentTask.sliceUrls) {
|
|
|
+ console.log(url);
|
|
|
+ const image = await loadImage(
|
|
|
+ filters.toCompleteUrlWithFileServer(store.setting.fileServer, url)
|
|
|
+ );
|
|
|
+ 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 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 ===
|
|
|
+ (store.currentTask && store.currentTask.sliceUrls.indexOf(url) + 1)
|
|
|
+ );
|
|
|
+
|
|
|
+ sliceImagesWithTrackList.push({
|
|
|
+ url: completeUrl,
|
|
|
+ indexInSliceUrls: store.currentTask.sliceUrls.indexOf(url) + 1,
|
|
|
+ trackList: thisImageTrackList,
|
|
|
+ originalImage: image,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 供回退和清除使用
|
|
|
+ // let trackLen = store.currentMarkResult?.trackList.length;
|
|
|
+ const renderPaperAndMark = async () => {
|
|
|
+ if (!store.currentTask) return;
|
|
|
+
|
|
|
+ // reset sliceImagesWithTrackList ,当切换任务时,要重新绘制图片和轨迹
|
|
|
+ if (_studentId !== store.currentTask.studentId) {
|
|
|
+ // 还原轨迹用得上
|
|
|
+ sliceImagesWithTrackList.splice(0);
|
|
|
+ _studentId = store.currentTask.studentId;
|
|
|
+ }
|
|
|
+
|
|
|
+ await processImage();
|
|
|
+ };
|
|
|
+
|
|
|
+ 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 + "%";
|
|
|
+ });
|
|
|
+
|
|
|
+ return {
|
|
|
+ store,
|
|
|
+ sliceImagesWithTrackList,
|
|
|
+ answerPaperScale,
|
|
|
+ };
|
|
|
+ },
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.mark-body-container {
|
|
|
+ height: calc(100vh - 41px);
|
|
|
+ overflow: scroll;
|
|
|
+ background-size: 8px 8px;
|
|
|
+ background-image: linear-gradient(to right, #e7e7e7 4px, transparent 4px),
|
|
|
+ linear-gradient(to bottom, transparent 4px, #e7e7e7 4px);
|
|
|
+}
|
|
|
+.mark-body-container img {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+.single-image-container {
|
|
|
+ position: relative;
|
|
|
+}
|
|
|
+.image-seperator {
|
|
|
+ border: 2px solid rgba(120, 120, 120, 0.1);
|
|
|
+}
|
|
|
+</style>
|