|
@@ -1,10 +1,21 @@
|
|
|
<template>
|
|
|
<div class="mark-body-container flex-auto" ref="container">
|
|
|
<div :style="{ width: answerPaperScale }">
|
|
|
- <template v-for="(item, index) in imageWithStyles" :key="index">
|
|
|
+ <div
|
|
|
+ v-for="(item, index) in sliceImagesWithTrackList"
|
|
|
+ :key="index"
|
|
|
+ class="single-image-container"
|
|
|
+ >
|
|
|
<img :src="item.url" />
|
|
|
+ <MarkDrawTrack
|
|
|
+ :track-list="item.trackList"
|
|
|
+ :original-image="item.originalImage"
|
|
|
+ :slice-image="item.sliceImage"
|
|
|
+ :dx="item.dx"
|
|
|
+ :dy="item.dy"
|
|
|
+ />
|
|
|
<hr style="border: 2px solid grey" />
|
|
|
- </template>
|
|
|
+ </div>
|
|
|
<!-- style="border: 1px solid black; background: black" -->
|
|
|
</div>
|
|
|
<div v-if="!store.currentTask" class="text-center">暂无评卷任务</div>
|
|
@@ -15,15 +26,24 @@
|
|
|
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";
|
|
|
|
|
|
-interface ImageStyle {
|
|
|
- width: number; // or string?
|
|
|
+interface SliceImage {
|
|
|
+ url: string;
|
|
|
+ indexInSliceUrls: number;
|
|
|
+ trackList: Array<Track>;
|
|
|
+ originalImage: HTMLImageElement;
|
|
|
+ sliceImage: HTMLImageElement;
|
|
|
+ dx: number;
|
|
|
+ dy: number;
|
|
|
}
|
|
|
export default defineComponent({
|
|
|
name: "MarkBody",
|
|
|
+ components: { MarkDrawTrack },
|
|
|
setup() {
|
|
|
const container = ref(null);
|
|
|
- let imageWithStyles: Array<any> = reactive([]);
|
|
|
+ let sliceImagesWithTrackList: Array<SliceImage> = reactive([]);
|
|
|
watchEffect(async () => {
|
|
|
async function loadImage(url: string): Promise<HTMLImageElement> {
|
|
|
return new Promise((resolve, reject) => {
|
|
@@ -35,12 +55,25 @@ export default defineComponent({
|
|
|
});
|
|
|
}
|
|
|
if (!store.currentTask?.libraryId) return;
|
|
|
- imageWithStyles.splice(0);
|
|
|
+
|
|
|
+ // reset sliceImagesWithTrackList
|
|
|
+ sliceImagesWithTrackList.splice(0);
|
|
|
+ const allTrackList = store.currentTask.questionList.reduce(
|
|
|
+ (all, c) => all.concat(c.trackList),
|
|
|
+ [] as Array<Track>
|
|
|
+ );
|
|
|
+
|
|
|
if (store.currentTask.sliceConfig?.length) {
|
|
|
for (const url of store.currentTask.sliceUrls) {
|
|
|
await loadImage(filters.toCompleteUrl(url));
|
|
|
}
|
|
|
+ const theFinalheight = store.currentTask.sliceConfig
|
|
|
+ .map((v) => v.h)
|
|
|
+ .reduce((acc, v) => (acc += v));
|
|
|
+ let accumTopHeight = 0;
|
|
|
+ let accumBottomHeight = 0;
|
|
|
for (const sliceConfig of store.currentTask.sliceConfig) {
|
|
|
+ accumBottomHeight += sliceConfig.h;
|
|
|
const url = filters.toCompleteUrl(
|
|
|
store.currentTask.sliceUrls[sliceConfig.i - 1]
|
|
|
);
|
|
@@ -70,17 +103,38 @@ export default defineComponent({
|
|
|
);
|
|
|
// console.log(image, canvas.height, sliceConfig, ctx);
|
|
|
// console.log(canvas.toDataURL());
|
|
|
- imageWithStyles.push({
|
|
|
- url: canvas.toDataURL(),
|
|
|
+ const thisImageTrackList = allTrackList.filter(
|
|
|
+ (v) => v.offsetIndex === sliceConfig.i
|
|
|
+ );
|
|
|
+
|
|
|
+ const dataUrl = canvas.toDataURL();
|
|
|
+ const sliceImage = new Image();
|
|
|
+ sliceImage.src = dataUrl;
|
|
|
+ // sliceConfig.x + sliceConfig.w
|
|
|
+ sliceImagesWithTrackList.push({
|
|
|
+ url: dataUrl,
|
|
|
+ indexInSliceUrls: sliceConfig.i,
|
|
|
+ // 通过positionY来定位是第几张slice的还原,并过滤出相应的track
|
|
|
+ trackList: thisImageTrackList.filter(
|
|
|
+ (t) =>
|
|
|
+ t.positionY >= accumTopHeight / theFinalheight &&
|
|
|
+ t.positionY < accumBottomHeight / theFinalheight
|
|
|
+ ),
|
|
|
+ originalImage: image,
|
|
|
+ sliceImage,
|
|
|
+ dx: sliceConfig.x,
|
|
|
+ dy: sliceConfig.y,
|
|
|
});
|
|
|
+ accumTopHeight = accumBottomHeight;
|
|
|
}
|
|
|
} else {
|
|
|
const images = [];
|
|
|
- for (const url of store.currentTask.sheetUrls) {
|
|
|
+ for (const url of store.currentTask.sliceUrls) {
|
|
|
const image = await loadImage(filters.toCompleteUrl(url));
|
|
|
images.push(image);
|
|
|
}
|
|
|
|
|
|
+ // TODO: add loading
|
|
|
const newConfig = (store.setting.splitConfig
|
|
|
.map((v, index, ary) =>
|
|
|
index % 2 === 0 ? [v, ary[index + 1]] : false
|
|
@@ -91,12 +145,19 @@ export default defineComponent({
|
|
|
const maxSliceWidth =
|
|
|
Math.max(...images.map((v) => v.naturalWidth)) * maxSplitConfig;
|
|
|
|
|
|
- for (const url of store.currentTask.sheetUrls) {
|
|
|
+ const theFinalheight =
|
|
|
+ newConfig.length *
|
|
|
+ images.reduce((acc, v) => (acc += v.naturalHeight), 0);
|
|
|
+
|
|
|
+ let accumTopHeight = 0;
|
|
|
+ let accumBottomHeight = 0;
|
|
|
+ for (const url of store.currentTask.sliceUrls) {
|
|
|
const completeUrl = filters.toCompleteUrl(url);
|
|
|
|
|
|
for (const config of newConfig) {
|
|
|
const image = await loadImage(completeUrl);
|
|
|
|
|
|
+ accumBottomHeight += image.naturalHeight;
|
|
|
const div = (container.value as unknown) as HTMLDivElement;
|
|
|
|
|
|
const width = image.naturalWidth * (config[1] - config[0]);
|
|
@@ -118,9 +179,29 @@ export default defineComponent({
|
|
|
);
|
|
|
// console.log(image, canvas.height, sliceConfig, ctx);
|
|
|
// console.log(canvas.toDataURL());
|
|
|
- imageWithStyles.push({
|
|
|
+
|
|
|
+ const thisImageTrackList = allTrackList.filter(
|
|
|
+ (t) =>
|
|
|
+ t.offsetIndex === store.currentTask.sliceUrls.indexOf(url) + 1
|
|
|
+ );
|
|
|
+
|
|
|
+ const dataUrl = canvas.toDataURL();
|
|
|
+ const sliceImage = new Image();
|
|
|
+ sliceImage.src = dataUrl;
|
|
|
+ sliceImagesWithTrackList.push({
|
|
|
url: canvas.toDataURL(),
|
|
|
+ indexInSliceUrls: store.currentTask.sliceUrls.indexOf(url),
|
|
|
+ trackList: thisImageTrackList.filter(
|
|
|
+ (t) =>
|
|
|
+ t.positionY >= accumTopHeight / theFinalheight &&
|
|
|
+ t.positionY < accumBottomHeight / theFinalheight
|
|
|
+ ),
|
|
|
+ originalImage: image,
|
|
|
+ sliceImage,
|
|
|
+ dx: image.naturalWidth * config[0],
|
|
|
+ dy: 0,
|
|
|
});
|
|
|
+ accumTopHeight = accumBottomHeight;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -130,7 +211,7 @@ export default defineComponent({
|
|
|
const scale = store.setting.uiSetting["answer.paper.scale"] || 1;
|
|
|
return scale * 100 + "%";
|
|
|
});
|
|
|
- return { container, store, imageWithStyles, answerPaperScale };
|
|
|
+ return { container, store, sliceImagesWithTrackList, answerPaperScale };
|
|
|
},
|
|
|
});
|
|
|
</script>
|
|
@@ -139,8 +220,14 @@ export default defineComponent({
|
|
|
.mark-body-container {
|
|
|
height: calc(100vh - 21px);
|
|
|
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;
|
|
|
+}
|
|
|
</style>
|