| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- <template>
- <el-dialog
- title="预览答题卡"
- append-to-body
- v-model="showDialog"
- class="page_full_dialog"
- fullscreen
- >
- <div class="page_header" ref="pageHeaderRef">
- <div class="back_button" @click="GoBack">
- <i class="iconfont icon_return"></i>返回
- </div>
- <div class="header_title">
- {{ pageTitle }}<span class="header_title_tip">(右键点击图片另存为可保存图片到本地)</span>
- </div>
- </div>
- <div class="dialog_paper">
- <div class="paper_content">
- <div class="canvas_button">
- <div :class="currentIndex === 0 ? 'disable_button_item' : 'button_item'" @click="LastPaper">
- <el-icon><ArrowLeft /></el-icon>
- </div>
- </div>
- <div
- class="canvas_image"
- v-loading="isLoading"
- element-loading-text="加载中……"
- element-loading-spinner="el-icon-loading"
- element-loading-background="#ffffff"
- >
- <PaperImage
- :paperImgUrl="currentPaperUrl"
- :currentPage="currentPage"
- :usedCardType="usedCardType"
- :drawData="currentDrawData"
- :downLoadName="currentDownLoadName"
- />
- </div>
- <div class="canvas_button">
- <div :class="currentIndex === paperImageList.length - 1 ? 'disable_button_item' : 'button_item'" @click="NextPaper">
- <el-icon><ArrowRight /></el-icon>
- </div>
- </div>
- </div>
- <div class="paper_question" ref="paperQuestionRef">
- <div class="area_table">
- <el-table :data="questionList" border ref="questionTableRef" :key="pageKey" :max-height="questionTableHeight">
- <el-table-column label="小题名称" prop="questionName" align="center">
- <template #default="scope">
- {{ scope.row.questionName }}
- </template>
- </el-table-column>
- <el-table-column label="满分/答案" align="center">
- <template #default="scope">
- {{ scope.row.fullScore }}
- <span v-if="scope.row.questionAnswer">/ {{ scope.row.questionAnswer }}</span>
- </template>
- </el-table-column>
- <el-table-column label="得分/答案" align="center">
- <template #default="scope">
- <div :class="scope.row.fullScore == scope.row.score ? '' : 'question_score'">
- {{ scope.row.score }}
- <span v-if="scope.row.answer">/ {{ scope.row.answer }}</span>
- </div>
- </template>
- </el-table-column>
- </el-table>
- </div>
- </div>
- </div>
- </el-dialog>
- </template>
- <script lang="ts" setup>
- import { ref, watch, onMounted, onBeforeUnmount, nextTick } from "vue";
- import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
- import PaperImage from "@/components/PaperImage.vue"; // 学生试卷组件
- import { getStudentPaperCardInfo } from "@/api/analysis";
- // ================= 类型定义 =================
- interface PaperInfo {
- examPaperId: string;
- platformNumber: string | number | null;
- [key: string]: any;
- }
- interface QuestionItem {
- questionName: string;
- fullScore: number | string;
- score: number | string;
- questionAnswer?: string;
- answer?: string;
- samplingPosition?: string;
- pagePaintingVOS?: any[];
- questionId?: string | number;
- [key: string]: any;
- }
- interface CrossDrawItem {
- page: number | string;
- item: QuestionItem;
- }
- // ================= Props & Emits =================
- const props = withDefaults(
- defineProps<{
- paperInfo?: PaperInfo;
- pageTitle?: string;
- modelValue?: boolean; // 替代原有的 value
- examLevel?: number | string;
- }>(),
- {
- paperInfo: () => ({}) as PaperInfo,
- pageTitle: "",
- modelValue: false,
- examLevel: 2,
- }
- );
- const emit = defineEmits<{
- (e: "updateModelValue", value: boolean): void;
- }>();
- const showDialog = ref(false);
- // ================= 响应式数据 (替代 data) =================
- const paperImageList = ref<any[]>([]); // 学生试卷图片列表
- const currentIndex = ref(0); // 当前学生试卷图片索引
- const currentPage = ref(1); // 当前学生试卷页码 默认第一页
- const currentPaperUrl = ref(""); // 当前学生试卷图片地址
- const currentDownLoadName = ref(""); // 当前学生试卷图片下载名称
- const currentDrawData = ref<any[]>([]); // 当前学生试卷答题标记数据
- const questionList = ref<QuestionItem[]>([]); // 学生试卷题目列表
- const questionTableHeight = ref<number | string>(0); // 学生试卷题目列表高度
- const usedCardType = ref<number | null>(null); // 1系统卡 2 三方卡
- const isLoading = ref(false); // 是否正在加载中
- const studentName = ref(""); // 学生姓名
- const crossDrawData = ref<CrossDrawItem[]>([]); // 跨页的批注数据
- const pageKey = ref(1);
- // DOM Refs
- const pageHeaderRef = ref<HTMLElement | null>(null);
- const paperQuestionRef = ref<HTMLElement | null>(null);
- const questionTableRef = ref();
- // ================= 监听器 =================
- watch(
- () => props.modelValue,
- (newVal) => {
- if (newVal) {
- showDialog.value = true;
- currentIndex.value = 0;
- pageKey.value += 1;
- GetStudentPaperInfo();
- }
- }
- );
- // ================= 方法 (替代 methods) =================
- // 禁用右键菜单的方法
- const DisableRightClick = (event: MouseEvent) => {
- event.preventDefault();
- return false;
- };
- // 返回
- const GoBack = () => {
- currentPaperUrl.value = "";
- currentDrawData.value = [];
- isLoading.value = true;
- showDialog.value = false;
- emit("updateModelValue", false);
- };
- // 上一个试卷
- const LastPaper = () => {
- if (currentIndex.value > 0) {
- currentIndex.value--;
- UpdateCurrentPaperData();
- }
- };
- // 下一个试卷
- const NextPaper = () => {
- if (currentIndex.value < paperImageList.value.length - 1) {
- currentIndex.value++;
- UpdateCurrentPaperData();
- }
- };
- // 获取学生试卷详情信息
- const GetStudentPaperInfo = () => {
- currentPaperUrl.value = "";
- if (props.paperInfo?.examPaperId && props.paperInfo?.platformNumber != null) {
- isLoading.value = true;
-
- getStudentPaperCardInfo(props.paperInfo).then((res: any) => {
- if (res.code == 200 && res.data) {
- paperImageList.value = res.data.pageVOS || [];
- usedCardType.value = res.data.usedCardType;
-
- // 重置索引并更新当前试卷数据
- currentIndex.value = 0;
- UpdateCurrentPaperData();
- // 合并所有试卷图片中的题目列表
- let allQuestions: QuestionItem[] = [];
- // 先添加总分数据
- const totalScore: QuestionItem = {
- questionName: "总分",
- fullScore: res.data.fullScore || 150,
- score: res.data.totalScore,
- questionAnswer: "",
- answer: "",
- samplingPosition: '{"x":195,"y":147,"page":1}',
- };
-
- if (paperImageList.value.length > 0 && paperImageList.value[0].questionVOS) {
- paperImageList.value[0].questionVOS.unshift(totalScore);
- }
-
- if (props.examLevel == 1) {
- // 联考
- allQuestions = res?.data?.studentAnswerBOS || [];
- } else {
- // 校考
- paperImageList.value.forEach((item) => {
- if (item.questionVOS && item.questionVOS.length > 0) {
- allQuestions = allQuestions.concat(item.questionVOS);
- }
- });
- }
-
- questionList.value = allQuestions;
- CalculateTableHeight(); // 计算表格高度
-
- nextTick(() => {
- setTimeout(() => {
- isLoading.value = false;
- }, 500);
- });
- } else {
- currentPaperUrl.value = "";
- currentDrawData.value = [];
- paperImageList.value = [];
- currentIndex.value = 0;
- questionList.value = [];
- questionTableHeight.value = "";
- nextTick(() => {
- isLoading.value = false;
- });
- }
- });
- }
- };
- // 计算表格高度(更精确的方式)
- const CalculateTableHeight = () => {
- nextTick(() => {
- if (paperQuestionRef.value && pageHeaderRef.value) {
- const availableHeight = window.innerHeight - 65 - 80;
- questionTableHeight.value = availableHeight;
- } else {
- const availableHeight = window.innerHeight - 65 - 40 - 40;
- questionTableHeight.value = availableHeight;
- }
- });
- };
- // 是否重复
- const IsRepeat = (obj: CrossDrawItem): boolean => {
- return crossDrawData.value.some(
- (item) => item.page == obj.page && item.item.questionId == obj.item.questionId
- );
- };
- // 更新当前试卷数据的公共方法
- const UpdateCurrentPaperData = () => {
- if (paperImageList.value.length > 0 && currentIndex.value < paperImageList.value.length) {
- const currentItem = paperImageList.value[currentIndex.value];
- currentPaperUrl.value = currentItem.picUrl;
-
- // 兼容旧的数据
- if (currentItem.useType) {
- usedCardType.value = currentItem.useType;
- }
- let drawData = currentItem.questionVOS || [];
- currentPage.value = currentItem.page; // 打印当前的页码
- if (drawData.length > 0) {
- drawData.forEach((item: QuestionItem) => {
- // 如果有批阅块 且大于1个批阅块 代表跨页的
- if (item.pagePaintingVOS && item.pagePaintingVOS.length > 1) {
- item.pagePaintingVOS.forEach((block) => {
- if (block.page != currentPage.value) {
- const obj: CrossDrawItem = {
- page: block.page,
- item: item,
- };
- // 插入之前 根据page和题目id判断是否重复
- if (!IsRepeat(obj)) {
- crossDrawData.value.push(obj);
- }
- }
- });
- }
- });
- }
- currentDrawData.value = currentItem.questionVOS || [];
- currentDownLoadName.value = `${props.pageTitle}答题卡第${currentIndex.value + 1}页`; // 重置下载名称
- } else {
- // 处理边界情况
- currentPaperUrl.value = "";
- currentDrawData.value = [];
- paperImageList.value = [];
- currentIndex.value = 0;
- currentDownLoadName.value = "";
- }
- };
- // ================= 生命周期 =================
- onMounted(() => {
- // 禁用鼠标右键
- document.addEventListener("contextmenu", DisableRightClick);
- });
- onBeforeUnmount(() => {
- document.removeEventListener("contextmenu", DisableRightClick);
- });
- </script>
- <style lang="scss" scoped>
- .dialog_paper {
- width: 100%;
- height: calc(100vh - 65px);
- background: #f0f4fb;
- overflow: hidden;
- display: flex;
- justify-content: space-between;
- padding: 20px;
- box-sizing: border-box;
- .paper_content {
- width: calc(100% - 340px);
- height: 100%;
- display: flex;
- justify-content: flex-start;
- .canvas_button {
- width: 48px;
- height: 100%;
- display: flex;
- justify-content: center;
- align-items: center;
- .button_item {
- width: 48px;
- height: 48px;
- border-radius: 50%;
- background: rgba(0, 0, 0, 0.1);
- color: #999999;
- font-size: 24px;
- line-height: 48px;
- text-align: center;
- cursor: pointer;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- .disable_button_item {
- width: 48px;
- height: 48px;
- border-radius: 50%;
- background: rgba(0, 0, 0, 0.1);
- color: #c0c4cc;
- font-size: 24px;
- line-height: 48px;
- text-align: center;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- }
- .canvas_image {
- width: calc(100% - 88px - 40px);
- height: 100%;
- margin: auto;
- }
- }
- .paper_question {
- width: 320px;
- height: 100%;
- background: #ffffff;
- border-radius: 10px;
- border: 1px solid #ebeef5;
- padding: 20px;
- box-sizing: border-box;
- .question_score {
- color: #f56c6c;
- }
- }
- }
- </style>
|