MarkBody.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <template>
  2. <div class="mark-body-container tw-flex-auto tw-p-2" ref="dragContainer">
  3. <a-spin
  4. :spinning="rendering"
  5. size="large"
  6. tip="Loading..."
  7. style="margin-top: 50px"
  8. >
  9. <div v-if="!store.currentTask" class="tw-text-center">
  10. {{ store.message }}
  11. </div>
  12. <div v-else :style="{ width: answerPaperScale }">
  13. <div
  14. v-for="(item, index) in sliceImagesWithTrackList"
  15. :key="index"
  16. class="single-image-container"
  17. >
  18. <img :src="item.url" draggable="false" />
  19. <MarkDrawTrack
  20. :track-list="item.trackList"
  21. :special-tag-list="item.tagList"
  22. :original-image="item.originalImage"
  23. />
  24. <hr class="image-seperator" />
  25. </div>
  26. </div>
  27. </a-spin>
  28. </div>
  29. </template>
  30. <script lang="ts">
  31. import { computed, defineComponent, reactive, ref, watchEffect } from "vue";
  32. import { store } from "./store";
  33. import filters from "@/filters";
  34. import MarkDrawTrack from "./MarkDrawTrack.vue";
  35. import { SpecialTag, Track } from "@/types";
  36. import { useTimers } from "@/setups/useTimers";
  37. import {
  38. getDataUrlForSliceConfig,
  39. getDataUrlForSplitConfig,
  40. loadImage,
  41. } from "@/utils/utils";
  42. import { dragImage } from "@/features/mark/use/draggable";
  43. interface SliceImage {
  44. url: string;
  45. indexInSliceUrls: number;
  46. trackList: Array<Track>;
  47. tagList: Array<SpecialTag>;
  48. originalImage: HTMLImageElement;
  49. }
  50. // should not render twice at the same time
  51. let __lock = false;
  52. let __currentLibraryId = -1; // save __currentLibraryId of lock
  53. export default defineComponent({
  54. name: "MarkBody",
  55. components: { MarkDrawTrack },
  56. emits: ["error"],
  57. setup(props, { emit }) {
  58. const { dragContainer } = dragImage();
  59. const { addTimeout } = useTimers();
  60. let rendering = ref(false);
  61. let sliceImagesWithTrackList: Array<SliceImage> = reactive([]);
  62. async function processImage() {
  63. if (!store.currentTask) return;
  64. const images = [];
  65. for (const url of store.currentTask.sliceUrls) {
  66. const image = await loadImage(
  67. filters.toCompleteUrlWithFileServer(store.setting.fileServer, url)
  68. );
  69. images.push(image);
  70. }
  71. for (const url of store.currentTask.sliceUrls) {
  72. const completeUrl = filters.toCompleteUrlWithFileServer(
  73. store.setting.fileServer,
  74. url
  75. );
  76. const indexInSliceUrls = store.currentTask.sliceUrls.indexOf(url) + 1;
  77. const image = images[indexInSliceUrls - 1];
  78. const trackLists = store.currentTask.questionList
  79. .map((q) => q.trackList)
  80. .reduce((acc, t) => {
  81. acc = acc.concat(t);
  82. return acc;
  83. }, [] as Array<Track>);
  84. const thisImageTrackList = trackLists.filter(
  85. (t) => t.offsetIndex === indexInSliceUrls
  86. );
  87. const thisImageTagList = store.currentTask.specialTagList.filter(
  88. (t) => t.offsetIndex === indexInSliceUrls
  89. );
  90. sliceImagesWithTrackList.push({
  91. url: completeUrl,
  92. indexInSliceUrls,
  93. trackList: thisImageTrackList,
  94. tagList: thisImageTagList,
  95. originalImage: image,
  96. });
  97. }
  98. }
  99. const renderPaperAndMark = async () => {
  100. if (__lock) {
  101. if (store.currentTask?.libraryId === __currentLibraryId) {
  102. console.log("重复渲染,返回");
  103. return;
  104. }
  105. console.log("上个任务还未渲染完毕,稍等一秒再尝试渲染");
  106. await new Promise((res) => setTimeout(res, 1000));
  107. await renderPaperAndMark();
  108. return;
  109. }
  110. __lock = true;
  111. __currentLibraryId = store.currentTask?.libraryId ?? -1;
  112. sliceImagesWithTrackList.splice(0);
  113. if (!store.currentTask) {
  114. __lock = false;
  115. return;
  116. }
  117. try {
  118. rendering.value = true;
  119. await processImage();
  120. } catch (error) {
  121. sliceImagesWithTrackList.splice(0);
  122. console.log("render error ", error);
  123. // 图片加载出错,自动加载下一个任务
  124. emit("error");
  125. } finally {
  126. __lock = false;
  127. rendering.value = false;
  128. }
  129. };
  130. watchEffect(renderPaperAndMark);
  131. const answerPaperScale = computed(() => {
  132. // 放大、缩小不影响页面之前的滚动条定位
  133. let percentWidth = 0;
  134. let percentTop = 0;
  135. const container = document.querySelector(
  136. ".mark-body-container"
  137. ) as HTMLDivElement;
  138. if (container) {
  139. const { scrollLeft, scrollTop, scrollWidth, scrollHeight } = container;
  140. percentWidth = scrollLeft / scrollWidth;
  141. percentTop = scrollTop / scrollHeight;
  142. }
  143. addTimeout(() => {
  144. if (container) {
  145. const { scrollWidth, scrollHeight } = container;
  146. container.scrollTo({
  147. left: scrollWidth * percentWidth,
  148. top: scrollHeight * percentTop,
  149. });
  150. }
  151. }, 10);
  152. const scale = store.setting.uiSetting["answer.paper.scale"];
  153. return scale * 100 + "%";
  154. });
  155. return {
  156. dragContainer,
  157. store,
  158. rendering,
  159. sliceImagesWithTrackList,
  160. answerPaperScale,
  161. };
  162. },
  163. });
  164. </script>
  165. <style scoped>
  166. .mark-body-container {
  167. height: calc(100vh - 41px);
  168. overflow: scroll;
  169. background-size: 8px 8px;
  170. background-image: linear-gradient(to right, #e7e7e7 4px, transparent 4px),
  171. linear-gradient(to bottom, transparent 4px, #e7e7e7 4px);
  172. cursor: grab;
  173. user-select: none;
  174. }
  175. .grabbing {
  176. cursor: grabbing;
  177. }
  178. .mark-body-container img {
  179. width: 100%;
  180. }
  181. .single-image-container {
  182. position: relative;
  183. }
  184. .image-seperator {
  185. border: 2px solid rgba(120, 120, 120, 0.1);
  186. }
  187. </style>