123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- <template>
- <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 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, reactive, ref, watchEffect } from "vue";
- import { store } from "./store";
- import filters from "@/filters";
- import MarkDrawTrack from "./MarkDrawTrack.vue";
- import { SpecialTag, Track } from "@/types";
- import { useTimers } from "@/setups/useTimers";
- import { 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 __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(
- filters.toCompleteUrlWithFileServer(store.setting.fileServer, url)
- );
- images.push(image);
- }
- for (const url of store.currentTask.sliceUrls) {
- const completeUrl = filters.toCompleteUrlWithFileServer(
- store.setting.fileServer,
- 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 + "%";
- });
- return {
- dragContainer,
- store,
- rendering,
- 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);
- cursor: grab;
- user-select: none;
- }
- .grabbing {
- cursor: grabbing;
- }
- .mark-body-container img {
- width: 100%;
- }
- .single-image-container {
- position: relative;
- }
- .image-seperator {
- border: 2px solid rgba(120, 120, 120, 0.1);
- }
- </style>
|