| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- <template>
- <div
- class="canvas_image"
- v-loading="isLoading"
- element-loading-text="加载中..."
- :element-loading-spinner="loadingSvgIcon"
- element-loading-background="#ffffff"
- >
- <!-- PointCanvas 组件需要确保也是 Vue 3 版本 -->
- <PointCanvas
- ref="pointCanvasRef"
- :usedCardType="usedCardType"
- :drawData="currentDrawData"
- :paperImage="paperImage"
- type="question"
- ></PointCanvas>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, watch, onMounted, nextTick } from "vue";
- import PointCanvas from "@/components/QuestionPoint.vue"; // 小题的画布版本
- import { getStudentPaperCardInfo } from "@/api/analysis";
- import { loadingSvgIcon } from "@/utils/common";
- // --- 类型定义 ---
- interface PagePaintingVO {
- x: number;
- y: number;
- w: number;
- h: number;
- page: number;
- [key: string]: any;
- }
- interface QuestionVO {
- questionName: string;
- fullScore: number;
- score: number;
- titleType: number; // 1: 客观题, 2: 主观题, etc.
- samplingPosition: string; // JSON string
- drawLineData?: any;
- model?: any;
- pagePaintingVOS?: PagePaintingVO[];
- [key: string]: any;
- }
- interface PageVO {
- picUrl: string;
- page: number;
- questionVOS?: QuestionVO[];
- [key: string]: any;
- }
- interface PaperDataResult {
- pageVOS: PageVO[];
- usedCardType: number; // 1: 系统卡, 2: 三方卡
- [key: string]: any;
- }
- interface Props {
- paperInfo?: Record<string, any>; // 试卷信息
- paperData?: Record<string, any>; // 试卷分析批量查看答题卡数据
- isBatch?: boolean; // 试卷分析批量查看答题卡
- }
- const props = withDefaults(defineProps<Props>(), {
- paperInfo: () => ({}),
- paperData: () => ({}),
- isBatch: false,
- });
- // --- 响应式数据 ---
- const pointCanvasRef = ref<InstanceType<typeof PointCanvas> | null>(null);
- const paperImageList = ref<PageVO[]>([]); // 学生试卷图片列表
- const currentIndex = ref<number>(0); // 当前学生试卷图片索引
- const currentPaperUrl = ref<string>(""); // 当前学生试卷图片地址
- const paperImage = ref<{ url: string; page: number }[]>([]); // 学生试卷图片列表(用于显示)
- const currentDownLoadName = ref<string>(""); // 当前学生试卷图片下载名称
- const currentDrawData = ref<any[]>([]); // 当前学生试卷答题标记数据
- const questionList = ref<QuestionVO[]>([]); // 学生试卷题目列表
- const usedCardType = ref<number | null>(null); // 1系统卡 2 三方卡
- const isLoading = ref<boolean>(false); // 是否正在加载中
- // --- 方法 ---
- // 获取图片信息 (宽高)
- const GetImageInfo = async (
- imageUrl: string,
- ): Promise<{ width: number; height: number }> => {
- try {
- // 注意:fetch 可能在某些环境下需要配置代理或处理 CORS
- const response = await fetch(imageUrl + "?x-oss-process=image/info");
- if (!response.ok) {
- throw new Error("Network response was not ok");
- }
- const data = await response.json();
- const imageWidth = Number(data.ImageWidth?.value || 0);
- const imageHeight = Number(data.ImageHeight?.value || 0);
- return {
- width: imageWidth,
- height: imageHeight,
- };
- } catch (error) {
- console.error("获取图片信息失败:", error);
- // 返回默认值或抛出错误,视业务需求而定
- return { width: 0, height: 0 };
- }
- };
- // 获取切块图片的地址 (OSS Crop)
- const GetQuestionImgUrl = (
- url: string,
- x: number,
- y: number,
- w: number,
- h: number,
- ): string => {
- const ossProcessParam = "x-oss-process=image";
- const cropParams = `/crop,x_${Math.round(x)},y_${Math.round(y)},w_${Math.round(w)},h_${Math.round(h)}`;
- if (url.includes(ossProcessParam)) {
- return url + cropParams;
- } else {
- const separator = url.includes("?") ? "&" : "?";
- return url + `${separator}${ossProcessParam}${cropParams}`;
- }
- };
- // 更新当前试卷数据的公共方法
- const UpdateCurrentPaperData = async () => {
- if (questionList.value.length === 0) return;
- const currentQuestionItem = questionList.value[0];
- console.log("打印当前的题目数据", currentQuestionItem);
- // 获取第一张图片的尺寸用于坐标转换
- // 注意:如果 paperImageList 为空,这里需要保护
- if (paperImageList.value.length === 0) return;
- const firstPageUrl = paperImageList.value[0].picUrl;
- let imageInfo = { width: 0, height: 0 };
- if (firstPageUrl) {
- imageInfo = await GetImageInfo(firstPageUrl);
- }
- //兼容旧的数据
- if (paperImageList.value[currentIndex.value].useType) {
- usedCardType.value = paperImageList.value[currentIndex.value].useType; //卡类型需要调整为从此页获取
- }
- // 1. 处理显示的图片 (paperImage)
- if (currentQuestionItem.titleType == 1) {
- // 如果是客观题,显示整张试卷
- currentPaperUrl.value = paperImageList.value[currentIndex.value].picUrl;
- paperImage.value = [
- {
- url: currentPaperUrl.value,
- page: paperImageList.value[currentIndex.value].page,
- },
- ];
- } else {
- // 否则显示对应的切块图片
- const list = currentQuestionItem.pagePaintingVOS || [];
- paperImage.value = [];
- for (const item of list) {
- const pageItem = paperImageList.value.find((p) => p.page == item.page);
- if (!pageItem) continue;
- let obj: { url: string; page: number } = {
- url: "",
- page: pageItem.page,
- };
- if (usedCardType.value == 1) {
- // 系统卡:计算相对坐标
- let templateInfo = {
- width: 794 - 30 * 2, // A4 减去边距
- height: 1123 - 25 * 2,
- };
- // 如果长大于宽 就是A3
- if (imageInfo.width > imageInfo.height) {
- templateInfo = {
- width: 1588 - 30 * 2, // A3 减去边距
- height: 1123 - 25 * 2,
- };
- }
- const newBlockPoint = {
- x: ((item.x - 30) / templateInfo.width) * imageInfo.width,
- y: ((item.y - 25) / templateInfo.height) * imageInfo.height,
- w: (item.w / templateInfo.width) * imageInfo.width,
- h: (item.h / templateInfo.height) * imageInfo.height,
- page: item.page,
- };
- obj.url = GetQuestionImgUrl(
- pageItem.picUrl,
- newBlockPoint.x,
- newBlockPoint.y,
- newBlockPoint.w,
- newBlockPoint.h,
- );
- } else {
- // 三方卡
- obj.url = GetQuestionImgUrl(
- pageItem.picUrl,
- item.x,
- item.y,
- item.w,
- item.h,
- );
- }
- paperImage.value.push(obj);
- }
- }
- // 2. 处理采分点坐标 (currentDrawData)
- let positionX = 0;
- let positionY = 0;
- try {
- const point = JSON.parse(currentQuestionItem.samplingPosition || "{}");
- positionX = point.x || 0;
- positionY = point.y || 0;
- } catch (e) {
- console.error("解析采样位置失败", e);
- }
- if (usedCardType.value == 1) {
- // 系统卡:需要根据模板的坐标进行转换
- let templateInfo = {
- width: 794,
- height: 1123,
- };
- if (imageInfo.width > imageInfo.height) {
- templateInfo = {
- width: 1588,
- height: 1123,
- };
- }
- // 计算试卷相对于模板的倍率
- const offsetScale = imageInfo.width / templateInfo.width;
- positionX = parseFloat((offsetScale * positionX).toFixed(2));
- positionY = parseFloat((offsetScale * positionY).toFixed(2));
- }
- const drawDataItem = {
- id: "",
- name: currentQuestionItem.questionName,
- fullScore: currentQuestionItem.fullScore, // 满分
- score: currentQuestionItem.score, // 学生得分
- drawLineData: currentQuestionItem.drawLineData, // 划线标记的数据
- x: positionX,
- y: positionY,
- titleType: currentQuestionItem.titleType, // 题目类型
- pagePaintingVOS: currentQuestionItem.pagePaintingVOS, // 切块图片数据
- model: currentQuestionItem.model, // 优秀 典型错误数据
- samplingPosition: currentQuestionItem.samplingPosition, // 采分点数据位置
- };
- currentDrawData.value = [drawDataItem];
- currentDownLoadName.value = "答题卡";
- };
- // 处理批量查看的数据
- const StudentPaperData = (res: PaperDataResult | any) => {
- paperImageList.value = res?.pageVOS || [];
- usedCardType.value = res?.usedCardType || 2; // 默认三方卡
- // 合并所有试卷图片中的题目列表
- let allQuestions: QuestionVO[] = [];
- paperImageList.value.forEach((item) => {
- if (item.questionVOS && item.questionVOS.length > 0) {
- allQuestions = allQuestions.concat(item.questionVOS);
- }
- });
- questionList.value = allQuestions;
- currentIndex.value = 0;
- if (questionList.value.length > 0) {
- UpdateCurrentPaperData();
- }
- nextTick(() => {
- isLoading.value = false;
- });
- };
- // 获取学生试卷详情信息
- const GetStudentPaperInfo = () => {
- console.log("加载学生小题试卷信息参数", props.paperInfo);
- if (props.paperInfo?.examPaperId && props.paperInfo?.platformNumber != null) {
- isLoading.value = true;
- getStudentPaperCardInfo(props.paperInfo)
- .then((res) => {
- console.log("打印学生试卷详情信息", res);
- if (res.code == 200 && res.data) {
- const data = res.data as PaperDataResult;
- paperImageList.value = data.pageVOS || [];
- usedCardType.value = data.usedCardType || 2;
- // 合并所有试卷图片中的题目列表
- let allQuestions: QuestionVO[] = [];
- paperImageList.value.forEach((item) => {
- if (item.questionVOS && item.questionVOS.length > 0) {
- allQuestions = allQuestions.concat(item.questionVOS);
- }
- });
- questionList.value = allQuestions;
- // 重置索引并更新当前试卷数据
- currentIndex.value = 0;
- if (questionList.value.length > 0) {
- UpdateCurrentPaperData();
- }
- nextTick(() => {
- isLoading.value = false;
- });
- } else {
- nextTick(() => {
- isLoading.value = false;
- });
- }
- })
- .catch((err) => {
- console.error(err);
- nextTick(() => {
- isLoading.value = false;
- });
- });
- } else {
- if (props.isBatch) {
- StudentPaperData(props.paperData as PaperDataResult);
- } else {
- currentDrawData.value = [];
- isLoading.value = false;
- }
- }
- };
- // --- 监听器 ---
- watch(
- () => props.paperInfo,
- (newVal) => {
- if (newVal && Object.keys(newVal).length > 0) {
- currentIndex.value = 0;
- currentDrawData.value = [];
- paperImage.value = [];
- console.log("学生试卷信息更新了", newVal);
- GetStudentPaperInfo();
- }
- },
- { deep: true },
- );
- watch(
- () => props.paperData,
- (newVal) => {
- if (newVal && Object.keys(newVal).length > 0) {
- currentIndex.value = 0;
- currentDrawData.value = [];
- paperImage.value = [];
- StudentPaperData(newVal as PaperDataResult);
- }
- },
- { deep: true },
- );
- // --- 生命周期 ---
- onMounted(() => {
- // 初始加载
- GetStudentPaperInfo();
- });
- </script>
- <style lang="scss" scoped>
- .canvas_image {
- width: 100%;
- height: 100%;
- margin: auto;
- // background-color: green;
- .main_container {
- width: 100%;
- height: 100%;
- position: relative;
- }
- :deep(.el-loading-spinner) {
- .el-loading-text {
- font-size: 14px;
- margin: 0 auto;
- }
- .circular {
- width: 20px;
- height: 20px;
- color: #2e64fa;
- }
- }
- }
- </style>
|