|
@@ -1,19 +1,56 @@
|
|
|
<template>
|
|
|
- <div>
|
|
|
- <header class="tw-flex">
|
|
|
- <div>进度:{{ currentIndex }}/{{ allIds.length }}</div>
|
|
|
- <div>姓名:{{ student?.name }}</div>
|
|
|
- <div>准考证号:{{ student?.examNumber }}</div>
|
|
|
- <div>学号:{{ student?.studentCode }}</div>
|
|
|
- <div>科目:{{ student?.subjectCode }}-{{ student?.subjectName }}</div>
|
|
|
- <div>客观分:{{ student?.objectiveScore }}</div>
|
|
|
- <div>主观分:{{ student?.subjectiveScore }}</div>
|
|
|
- <div></div>
|
|
|
+ <div class="tw-h-screen">
|
|
|
+ <header
|
|
|
+ class="tw-flex tw-gap-2 tw-justify-between tw-items-center header-container"
|
|
|
+ >
|
|
|
+ <div class="tw-ml-2">
|
|
|
+ 进度:<span class="highlight-text">
|
|
|
+ {{ currentIndex }}/{{ allIds.length }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ 姓名:<span class="highlight-text">{{ student?.name }}</span>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ 准考证号:<span class="highlight-text">{{ student?.examNumber }}</span>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ 学号:<span class="highlight-text">{{ student?.studentCode }}</span>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ 科目:<span class="highlight-text">
|
|
|
+ {{ student?.subjectCode }}-{{ student?.subjectName }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ 客观分:<span class="highlight-text">{{
|
|
|
+ student?.objectiveScore
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ 主观分:<span class="highlight-text">{{
|
|
|
+ student?.subjectiveScore
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="tw-flex tw-items-center tw-gap-2 tw-mx-8">
|
|
|
+ <span
|
|
|
+ v-for="(u, index) in student?.sheetUrls"
|
|
|
+ :key="index"
|
|
|
+ class="tw-cursor-pointer"
|
|
|
+ :class="currentImage === index && 'highlight-text'"
|
|
|
+ @click="currentImage = index"
|
|
|
+ >
|
|
|
+ {{ index + 1 }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
</header>
|
|
|
|
|
|
- <div>
|
|
|
- <div>
|
|
|
- <div>
|
|
|
+ <div class="tw-flex" style="height: calc(100% - 56px)">
|
|
|
+ <div
|
|
|
+ style="flex: 0 1 420px"
|
|
|
+ class="tw-flex tw-flex-col tw-justify-between"
|
|
|
+ >
|
|
|
+ <div class="tw-m-2">
|
|
|
<div>
|
|
|
是否缺考:
|
|
|
<a-radio-group v-if="student" v-model:value="student.absent">
|
|
@@ -21,7 +58,7 @@
|
|
|
<a-radio :value="false">否</a-radio>
|
|
|
</a-radio-group>
|
|
|
</div>
|
|
|
- <div>
|
|
|
+ <div class="tw-my-2">
|
|
|
试卷类型:
|
|
|
<a-input
|
|
|
v-if="student"
|
|
@@ -30,51 +67,103 @@
|
|
|
style="width: 40px"
|
|
|
/>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <div v-if="student?.answers">
|
|
|
- <div v-for="group in answersComputed" :key="group.mainNumber">
|
|
|
- <h2>
|
|
|
- {{ group.mainNumber }}、{{ group.mainTitle }} ({{
|
|
|
- group.subs.length
|
|
|
- }})
|
|
|
- </h2>
|
|
|
- <div class="tw-flex tw-gap-4">
|
|
|
- <div v-for="question in group.subs" :key="question.subNumber">
|
|
|
- <span>{{ question.subNumber }}</span>
|
|
|
- <a-input
|
|
|
- :value="question.answer"
|
|
|
- style="width: 40px"
|
|
|
- :maxLength="
|
|
|
- group.mainTitle.match(/多选|多项|不定项/) ? 100 : 1
|
|
|
- "
|
|
|
- @input="($event) => changeAnswer($event, question)"
|
|
|
- @blur="($event) => changeAnswer($event, question, '#')"
|
|
|
- />
|
|
|
+ <div v-if="student?.answers" class="tw-mt-4">
|
|
|
+ <div
|
|
|
+ v-for="group in answersComputed"
|
|
|
+ :key="group.mainNumber"
|
|
|
+ class="tw-mt-2"
|
|
|
+ >
|
|
|
+ <h2>
|
|
|
+ {{ group.mainNumber }}、{{ group.mainTitle }} ({{
|
|
|
+ group.subs.length
|
|
|
+ }})
|
|
|
+ </h2>
|
|
|
+ <div class="tw-flex tw-gap-4">
|
|
|
+ <div v-for="question in group.subs" :key="question.subNumber">
|
|
|
+ <span>{{ question.subNumber }}. </span>
|
|
|
+ <a-input
|
|
|
+ :value="question.answer"
|
|
|
+ style="width: 40px"
|
|
|
+ :maxLength="
|
|
|
+ group.mainTitle.match(/多选|多项|不定项/) ? 100 : 1
|
|
|
+ "
|
|
|
+ @input="changeAnswer($event, question)"
|
|
|
+ @blur="changeAnswer($event, question, '#')"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div>
|
|
|
- <a-button @click="saveStudentAnswer">保存</a-button>
|
|
|
- <a-button :disabled="currentIndex === 1" @click="getPreviousStudent"
|
|
|
- >上一份</a-button
|
|
|
- >
|
|
|
- <a-button
|
|
|
- :disabled="currentIndex === allIds.length"
|
|
|
- @click="getNextStudent"
|
|
|
- >下一份</a-button
|
|
|
- >
|
|
|
+ <div class="tw-flex tw-justify-between tw-bg-white tw-p-4">
|
|
|
+ <a-button type="primary" shape="round" @click="saveStudentAnswer">
|
|
|
+ 保存
|
|
|
+ </a-button>
|
|
|
+ <div>
|
|
|
+ <a-button
|
|
|
+ shape="round"
|
|
|
+ :disabled="currentIndex === 1"
|
|
|
+ class="tw-mr-4"
|
|
|
+ @click="getPreviousStudent"
|
|
|
+ >
|
|
|
+ 上一份
|
|
|
+ </a-button>
|
|
|
+ <a-button
|
|
|
+ shape="round"
|
|
|
+ :disabled="currentIndex === allIds.length"
|
|
|
+ @click="getNextStudent"
|
|
|
+ >
|
|
|
+ 下一份
|
|
|
+ </a-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div>
|
|
|
- <div>
|
|
|
- <img v-for="item in student?.sheetUrls" :key="item" :src="item" />
|
|
|
+ <div style="flex: 1" class="mark-body-container tw-relative">
|
|
|
+ <ArrowLeftOutlined
|
|
|
+ v-if="student"
|
|
|
+ class="tw-cursor-pointer tw-absolute"
|
|
|
+ style="top: 45%; left: 20px; z-index: 1; font-size: 40px"
|
|
|
+ :style="{
|
|
|
+ color: currentImage === 0 ? 'white' : 'blueviolet',
|
|
|
+ }"
|
|
|
+ title="上一张"
|
|
|
+ @click="switchImageArrow({ left: true })"
|
|
|
+ />
|
|
|
+ <ArrowRightOutlined
|
|
|
+ v-if="student"
|
|
|
+ class="tw-cursor-pointer tw-absolute"
|
|
|
+ style="top: 45%; right: 20px; z-index: 1; font-size: 40px"
|
|
|
+ :style="{
|
|
|
+ color:
|
|
|
+ currentImage === student.sheetUrls.length - 1
|
|
|
+ ? 'white'
|
|
|
+ : 'blueviolet',
|
|
|
+ }"
|
|
|
+ title="上一张"
|
|
|
+ @click="switchImageArrow({ right: true })"
|
|
|
+ />
|
|
|
+ <div :style="{ width: answerPaperScale }">
|
|
|
+ <img
|
|
|
+ v-for="(item, index) in student?.sheetUrls"
|
|
|
+ :key="item"
|
|
|
+ class="tw-object-cover"
|
|
|
+ :src="item"
|
|
|
+ :style="{
|
|
|
+ display: index === currentImage ? 'block' : 'none',
|
|
|
+ rotate: rotateDegree + 'deg',
|
|
|
+ translate: rotateDegree ? '0 calc(30vh)' : '',
|
|
|
+ }"
|
|
|
+ @click="switchImage"
|
|
|
+ @contextmenu="showBigImage"
|
|
|
+ />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <ZoomPaper v-if="student" showRotate @rotateRight="rotateRight" />
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
@@ -84,6 +173,14 @@ import { message } from "ant-design-vue";
|
|
|
import { onMounted, reactive } from "vue";
|
|
|
import { useRoute } from "vue-router";
|
|
|
import { CheckSetting, StudentInfo } from "./check";
|
|
|
+import "viewerjs/dist/viewer.css";
|
|
|
+import Viewer from "viewerjs";
|
|
|
+import { store } from "@/store/store";
|
|
|
+import ZoomPaper from "@/components/ZoomPaper.vue";
|
|
|
+import { useTimers } from "@/setups/useTimers";
|
|
|
+import { ArrowLeftOutlined, ArrowRightOutlined } from "@ant-design/icons-vue";
|
|
|
+
|
|
|
+const { addTimeout } = useTimers();
|
|
|
|
|
|
const route = useRoute();
|
|
|
const checkType = route.query.checkType;
|
|
@@ -91,6 +188,8 @@ const queryId = route.query.queryId as string;
|
|
|
let pageType: "DATA_CHECK" | "HAND_CHECK" = "HAND_CHECK";
|
|
|
if (queryId) {
|
|
|
pageType = "DATA_CHECK";
|
|
|
+ sessionStorage.setItem(queryId, localStorage.getItem(queryId) || "[]");
|
|
|
+ localStorage.removeItem(queryId);
|
|
|
}
|
|
|
|
|
|
onMounted(async () => {
|
|
@@ -145,12 +244,12 @@ const answersComputed = $computed(() => {
|
|
|
async function getSetting() {
|
|
|
let res: any;
|
|
|
if (pageType === "DATA_CHECK") {
|
|
|
- const q: Record<string, string> = JSON.parse(
|
|
|
- sessionStorage.getItem(queryId) || "{}"
|
|
|
+ const query: Array<{ name: string; value: string }> = JSON.parse(
|
|
|
+ sessionStorage.getItem(queryId) || "[]"
|
|
|
);
|
|
|
const form = new FormData();
|
|
|
- for (const [k, v] of Object.entries(q)) {
|
|
|
- form.append(k, v + "");
|
|
|
+ for (const v of query) {
|
|
|
+ form.append(v.name, v.value + "");
|
|
|
}
|
|
|
res = await httpApp.post("/admin/exam/check/answer/getSetting", form);
|
|
|
} else {
|
|
@@ -179,14 +278,15 @@ async function getStudent(studentId: number) {
|
|
|
).data;
|
|
|
stu?.sheetUrls.forEach((v, i, a) => (a[i] = setting.fileServer + v));
|
|
|
currentStudentId = stu.id;
|
|
|
+ currentImage = 0;
|
|
|
|
|
|
// for dev
|
|
|
- stu.answers = [
|
|
|
- { mainNumber: 1, subNumber: "1", answer: "A" },
|
|
|
- { mainNumber: 1, subNumber: "2", answer: "B" },
|
|
|
- { mainNumber: 2, subNumber: "1", answer: "#" },
|
|
|
- ];
|
|
|
- stu.titles = { 1: "单选题", 2: "多选题" };
|
|
|
+ // stu.answers = [
|
|
|
+ // { mainNumber: 1, subNumber: "1", answer: "A" },
|
|
|
+ // { mainNumber: 1, subNumber: "2", answer: "B" },
|
|
|
+ // { mainNumber: 2, subNumber: "1", answer: "#" },
|
|
|
+ // ];
|
|
|
+ // stu.titles = { 1: "单选题", 2: "多选题" };
|
|
|
return stu;
|
|
|
}
|
|
|
|
|
@@ -242,4 +342,126 @@ async function saveStudentAnswer() {
|
|
|
void message.success("所有考生已处理完毕。");
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+//#region : 显示大图,供查看和翻转
|
|
|
+let currentImage = $ref(0);
|
|
|
+function switchImageArrow({
|
|
|
+ left = false,
|
|
|
+ right = false,
|
|
|
+}: {
|
|
|
+ left?: boolean;
|
|
|
+ right?: boolean;
|
|
|
+}) {
|
|
|
+ if (left) {
|
|
|
+ if (currentImage > 0) {
|
|
|
+ currentImage--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (right) {
|
|
|
+ if (currentImage < student!.sheetUrls.length - 1) {
|
|
|
+ currentImage++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function switchImage(event: MouseEvent) {
|
|
|
+ const image = event.target as HTMLImageElement;
|
|
|
+ const layerX: number = (event as any).layerX;
|
|
|
+ if (layerX * 2 < image.width) {
|
|
|
+ if (currentImage > 0) {
|
|
|
+ currentImage--;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (currentImage < student!.sheetUrls.length - 1) {
|
|
|
+ currentImage++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const showBigImage = (event: MouseEvent) => {
|
|
|
+ event.preventDefault();
|
|
|
+ // console.log(event);
|
|
|
+ let viewer: Viewer = null as unknown as Viewer;
|
|
|
+ viewer && viewer.destroy();
|
|
|
+ viewer = new Viewer((event.target as HTMLElement).parentElement!, {
|
|
|
+ // inline: true,
|
|
|
+ viewed() {
|
|
|
+ viewer.zoomTo(1);
|
|
|
+ },
|
|
|
+ hidden() {
|
|
|
+ viewer.destroy();
|
|
|
+ },
|
|
|
+ zIndex: 1000000,
|
|
|
+ });
|
|
|
+ viewer.show();
|
|
|
+};
|
|
|
+//#endregion : 显示大图,供查看和翻转
|
|
|
+
|
|
|
+//#region : 放大缩小和之后的滚动
|
|
|
+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 + "%";
|
|
|
+});
|
|
|
+//#endregion : 放大缩小和之后的滚动
|
|
|
+
|
|
|
+//#region rotateRight
|
|
|
+let rotateDegree = $ref(0);
|
|
|
+function rotateRight() {
|
|
|
+ rotateDegree = (rotateDegree + 90) % 360;
|
|
|
+}
|
|
|
+//#endregion
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.header-container {
|
|
|
+ position: relative;
|
|
|
+ height: 56px;
|
|
|
+ line-height: 16px;
|
|
|
+
|
|
|
+ background-color: var(--header-bg-color);
|
|
|
+ color: rgba(255, 255, 255, 0.5);
|
|
|
+}
|
|
|
+.highlight-text {
|
|
|
+ color: white;
|
|
|
+ font-size: var(--app-title-font-size);
|
|
|
+}
|
|
|
+
|
|
|
+.mark-body-container {
|
|
|
+ position: relative;
|
|
|
+ min-height: calc(100vh - 56px);
|
|
|
+ height: calc(100vh - 56px);
|
|
|
+ overflow: auto;
|
|
|
+ /* background-size: 8px 8px;
|
|
|
+ background-image: linear-gradient(to right, #e7e7e7 4px, transparent 4px),
|
|
|
+ linear-gradient(to bottom, transparent 4px, #e7e7e7 4px); */
|
|
|
+ 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;
|
|
|
+}
|
|
|
+</style>
|