123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- <template>
- <div class="mark-body-container tw-flex-auto tw-p-2" ref="dragContainer">
- <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"
- :style="{
- width: item.width,
- }"
- >
- <img :src="item.url" draggable="false" />
- <MarkDrawTrack
- :track-list="item.trackList"
- :special-tag-list="item.tagList"
- :original-image-height="item.originalImageHeight"
- :original-image-width="item.originalImageWidth"
- />
- <hr class="image-seperator" />
- </div>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { reactive, watch } from "vue";
- import { store } from "@/store/store";
- import MarkDrawTrack from "./MarkDrawTrack.vue";
- import type { 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;
- trackList: Array<Track>;
- tagList: Array<SpecialTag>;
- originalImageWidth: number;
- originalImageHeight: number;
- width: string; // 图片在整个图片列表里面的宽度比例
- }
- const { usingImage = "sliceUrls" } = withDefaults(
- defineProps<{
- usingImage?: "sheetUrls" | "sliceUrls";
- }>(),
- {
- usingImage: "sliceUrls",
- }
- );
- const emit = defineEmits(["error"]);
- const { dragContainer } = dragImage();
- const { addTimeout } = useTimers();
- let sliceImagesWithTrackList: Array<SliceImage> = reactive([]);
- let maxImageWidth = 0;
- async function processImage() {
- if (!store.currentTask) return;
- const images = [];
- const urls = store.currentTask[usingImage] || [];
- 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)
- .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,
- 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);
- let 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>
- <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>
|