|
|
@@ -1,14 +1,15 @@
|
|
|
<template>
|
|
|
<el-dialog
|
|
|
title=""
|
|
|
- v-model="showDialog"
|
|
|
+ :model-value="showDialog"
|
|
|
+ @update:model-value="showDialog = $event"
|
|
|
class="preview_dialog"
|
|
|
:modal-append-to-body="false"
|
|
|
append-to-body
|
|
|
fullscreen
|
|
|
- height="100%"
|
|
|
>
|
|
|
<div class="preview_image">
|
|
|
+ <!-- 绘图模式:带答题卡标注 -->
|
|
|
<div v-if="showDraw && showDialog" class="preview_canvas">
|
|
|
<ActionImage
|
|
|
ref="ActionCanvasRef"
|
|
|
@@ -17,14 +18,21 @@
|
|
|
:drawData="currentDrawData"
|
|
|
></ActionImage>
|
|
|
</div>
|
|
|
- <div v-else>
|
|
|
+ <!-- 纯图片预览模式:支持滚轮缩放 + 拖拽 -->
|
|
|
+ <div
|
|
|
+ v-else
|
|
|
+ class="preview_image_wrapper"
|
|
|
+ @wheel.prevent="HandleWheel"
|
|
|
+ @mousedown="HandleMouseDown"
|
|
|
+ @mousemove="HandleMouseMove"
|
|
|
+ @mouseup="HandleMouseUp"
|
|
|
+ @mouseleave="HandleMouseUp"
|
|
|
+ >
|
|
|
<img
|
|
|
:src="currenImageUrl"
|
|
|
alt=""
|
|
|
- :style="{
|
|
|
- transform: `scale(${zoomLevel}) rotate(${rotateAngle}deg)`,
|
|
|
- transition: 'transform 0.2s'
|
|
|
- }"
|
|
|
+ :style="imageStyle"
|
|
|
+ draggable="false"
|
|
|
/>
|
|
|
</div>
|
|
|
|
|
|
@@ -48,7 +56,8 @@
|
|
|
<el-icon><ArrowRight /></el-icon>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div class="preview_tool" v-if="showTool">
|
|
|
+ <!-- 工具栏仅在绘图模式下显示 -->
|
|
|
+ <div class="preview_tool" v-if="showTool && showDraw">
|
|
|
<div
|
|
|
class="tool_item"
|
|
|
v-for="(item, index) in toolList"
|
|
|
@@ -66,100 +75,121 @@
|
|
|
</el-dialog>
|
|
|
</template>
|
|
|
|
|
|
-<script setup>
|
|
|
-import { ref, watch, onBeforeUnmount } from 'vue'
|
|
|
+<script lang="ts" setup>
|
|
|
+import { ref, watch, computed } from 'vue'
|
|
|
import { Close, ArrowLeft, ArrowRight } from '@element-plus/icons-vue'
|
|
|
import ActionImage from '@/components/ActionImage.vue'
|
|
|
|
|
|
-// 定义 Props
|
|
|
-const props = defineProps({
|
|
|
- modelValue: {
|
|
|
- type: Boolean,
|
|
|
- required: true,
|
|
|
- },
|
|
|
- imageList: {
|
|
|
- type: Array,
|
|
|
- default: () => [],
|
|
|
- },
|
|
|
- index: {
|
|
|
- type: Number,
|
|
|
- default: 0,
|
|
|
- },
|
|
|
- showTool: {
|
|
|
- type: Boolean,
|
|
|
- default: false,
|
|
|
- },
|
|
|
- showDraw: {
|
|
|
- type: Boolean,
|
|
|
- default: false,
|
|
|
- },
|
|
|
- answerData: {
|
|
|
- type: Object,
|
|
|
- default: () => ({}),
|
|
|
- },
|
|
|
+// ==================== 类型定义 ====================
|
|
|
+
|
|
|
+/** 图片项接口 */
|
|
|
+interface ImageItem {
|
|
|
+ url: string
|
|
|
+ pageNo?: number
|
|
|
+ rotateAngle?: number
|
|
|
+}
|
|
|
+
|
|
|
+/** 工具栏项接口 */
|
|
|
+interface ToolItem {
|
|
|
+ title: string
|
|
|
+ icon: string
|
|
|
+ value: string
|
|
|
+}
|
|
|
+
|
|
|
+/** 组件 Props 接口 */
|
|
|
+interface Props {
|
|
|
+ modelValue: boolean
|
|
|
+ imageList: ImageItem[]
|
|
|
+ index?: number
|
|
|
+ showTool?: boolean
|
|
|
+ showDraw?: boolean
|
|
|
+ answerData?: Record<string, any>
|
|
|
+}
|
|
|
+
|
|
|
+/** 组件 Emits 接口 */
|
|
|
+interface Emits {
|
|
|
+ (e: 'update:modelValue', value: boolean): void
|
|
|
+ (e: 'GetRotateAngle', data: { currentIndex: number; rotateAngle: number }): void
|
|
|
+}
|
|
|
+
|
|
|
+// ==================== Props & Emits ====================
|
|
|
+
|
|
|
+const props = withDefaults(defineProps<Props>(), {
|
|
|
+ imageList: () => [],
|
|
|
+ index: 0,
|
|
|
+ showTool: false,
|
|
|
+ showDraw: false,
|
|
|
+ answerData: () => ({}),
|
|
|
})
|
|
|
|
|
|
-// 定义 Emits
|
|
|
-const emit = defineEmits(['update:modelValue', 'GetRotateAngle'])
|
|
|
+const emit = defineEmits<Emits>()
|
|
|
|
|
|
-// 响应式数据
|
|
|
+// ==================== 响应式状态 ====================
|
|
|
+
|
|
|
+/** 当前图片URL */
|
|
|
const currenImageUrl = ref('')
|
|
|
+/** 当前页码 */
|
|
|
const currentPageNo = ref(1)
|
|
|
+/** 当前图片索引 */
|
|
|
const currentIndex = ref(0)
|
|
|
|
|
|
-const toolList = ref([
|
|
|
- {
|
|
|
- title: '放大',
|
|
|
- icon: 'icon_fangda',
|
|
|
- value: '1',
|
|
|
- },
|
|
|
- {
|
|
|
- title: '缩小',
|
|
|
- icon: 'icon_suoxiao',
|
|
|
- value: '2',
|
|
|
- },
|
|
|
- {
|
|
|
- title: '逆时针1°',
|
|
|
- icon: 'icon_nishizhen1',
|
|
|
- value: '3',
|
|
|
- },
|
|
|
- {
|
|
|
- title: '顺时针1°',
|
|
|
- icon: 'icon_shunshizhen1',
|
|
|
- value: '4',
|
|
|
- },
|
|
|
- {
|
|
|
- title: '逆时针90°',
|
|
|
- icon: 'icon_nishizhen90',
|
|
|
- value: '5',
|
|
|
- },
|
|
|
- {
|
|
|
- title: '顺时针90°',
|
|
|
- icon: 'icon_shunshizhen90',
|
|
|
- value: '6',
|
|
|
- },
|
|
|
+/** 工具栏配置 */
|
|
|
+const toolList = ref<ToolItem[]>([
|
|
|
+ { title: '放大', icon: 'icon_fangda', value: '1' },
|
|
|
+ { title: '缩小', icon: 'icon_suoxiao', value: '2' },
|
|
|
+ { title: '逆时针1°', icon: 'icon_nishizhen1', value: '3' },
|
|
|
+ { title: '顺时针1°', icon: 'icon_shunshizhen1', value: '4' },
|
|
|
+ { title: '逆时针90°', icon: 'icon_nishizhen90', value: '5' },
|
|
|
+ { title: '顺时针90°', icon: 'icon_shunshizhen90', value: '6' },
|
|
|
])
|
|
|
|
|
|
+/** 缩放比例 */
|
|
|
const zoomLevel = ref(1)
|
|
|
-const maxZoom = ref(3)
|
|
|
-const minZoom = ref(0.5)
|
|
|
-const zoomStep = ref(0.1)
|
|
|
+/** 缩放范围常量 */
|
|
|
+const MAX_ZOOM = 3
|
|
|
+const MIN_ZOOM = 0.3
|
|
|
+const ZOOM_STEP = 0.1
|
|
|
+/** 旋转角度 */
|
|
|
const rotateAngle = ref(0)
|
|
|
|
|
|
-const currentPageInfo = ref({})
|
|
|
-const currentDrawData = ref([])
|
|
|
+/** 绘图数据 */
|
|
|
+const currentDrawData = ref<any[]>([])
|
|
|
+
|
|
|
+/** 拖拽相关状态 */
|
|
|
+const translateX = ref(0)
|
|
|
+const translateY = ref(0)
|
|
|
+const isDragging = ref(false)
|
|
|
+const dragStartX = ref(0)
|
|
|
+const dragStartY = ref(0)
|
|
|
|
|
|
-// Ref 引用
|
|
|
-const ActionCanvasRef = ref(null)
|
|
|
+/** ActionImage 组件引用 */
|
|
|
+const ActionCanvasRef = ref<InstanceType<typeof ActionImage> | null>(null)
|
|
|
|
|
|
-// 计算属性:showDialog
|
|
|
-const showDialog = ref(props.modelValue)
|
|
|
+// ==================== 计算属性 ====================
|
|
|
|
|
|
-// 监听 props.modelValue 变化
|
|
|
+/** 弹窗显示状态(双向绑定) */
|
|
|
+const showDialog = computed({
|
|
|
+ get() {
|
|
|
+ return props.modelValue
|
|
|
+ },
|
|
|
+ set(val) {
|
|
|
+ emit('update:modelValue', val)
|
|
|
+ },
|
|
|
+})
|
|
|
+
|
|
|
+/** 图片预览模式下的内联样式(缩放 + 旋转 + 拖拽位移) */
|
|
|
+const imageStyle = computed(() => ({
|
|
|
+ transform: `translate(${translateX.value}px, ${translateY.value}px) scale(${zoomLevel.value}) rotate(${rotateAngle.value}deg)`,
|
|
|
+ transition: isDragging.value ? 'none' : 'transform 0.15s',
|
|
|
+ cursor: isDragging.value ? 'grabbing' : 'grab',
|
|
|
+}))
|
|
|
+
|
|
|
+// ==================== 监听器 ====================
|
|
|
+
|
|
|
+/** 监听弹窗打开,加载当前索引图片信息 */
|
|
|
watch(
|
|
|
() => props.modelValue,
|
|
|
(newVal) => {
|
|
|
- showDialog.value = newVal
|
|
|
if (newVal) {
|
|
|
currentIndex.value = props.index
|
|
|
LoadPaperInfo()
|
|
|
@@ -167,50 +197,54 @@ watch(
|
|
|
}
|
|
|
)
|
|
|
|
|
|
-// 监听 showDialog 内部变化同步回父组件
|
|
|
-watch(showDialog, (newVal) => {
|
|
|
- emit('update:modelValue', newVal)
|
|
|
-})
|
|
|
+// ==================== 弹窗控制 ====================
|
|
|
|
|
|
-// 关闭弹窗
|
|
|
+/** 关闭弹窗并回传旋转角度信息 */
|
|
|
const CloseDialog = () => {
|
|
|
showDialog.value = false
|
|
|
- const obj = {
|
|
|
+ emit('GetRotateAngle', {
|
|
|
currentIndex: currentIndex.value,
|
|
|
rotateAngle: rotateAngle.value,
|
|
|
- }
|
|
|
- emit('GetRotateAngle', obj)
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
-// 返回角度信息
|
|
|
-const GetReturn = () => {
|
|
|
- const obj = {
|
|
|
+/** 回传当前角度信息(绘图模式旋转时调用) */
|
|
|
+const EmitRotateAngle = () => {
|
|
|
+ emit('GetRotateAngle', {
|
|
|
currentIndex: currentIndex.value,
|
|
|
rotateAngle: rotateAngle.value,
|
|
|
- }
|
|
|
- emit('GetRotateAngle', obj)
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
-// 加载页面相关信息
|
|
|
-const LoadPaperInfo = () => {
|
|
|
+// ==================== 图片加载与切换 ====================
|
|
|
|
|
|
- console.log('打印当前图片列表', props.imageList)
|
|
|
+/** 加载当前索引对应的图片及绘图数据 */
|
|
|
+const LoadPaperInfo = () => {
|
|
|
if (!props.imageList || props.imageList.length === 0) return
|
|
|
-
|
|
|
+
|
|
|
const currentItem = props.imageList[currentIndex.value]
|
|
|
if (!currentItem) return
|
|
|
|
|
|
currenImageUrl.value = currentItem.url
|
|
|
- currentPageNo.value = currentItem.pageNo
|
|
|
+ currentPageNo.value = currentItem.pageNo || 1
|
|
|
rotateAngle.value = currentItem.rotateAngle || 0
|
|
|
- console.log('打印当前图片信息', currentItem)
|
|
|
-
|
|
|
+
|
|
|
+ // 切换图片时重置缩放和位移
|
|
|
+ ResetTransform()
|
|
|
+
|
|
|
if (props.showDraw) {
|
|
|
LoadDrawData()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 上一个图片
|
|
|
+/** 重置缩放和位移状态 */
|
|
|
+const ResetTransform = () => {
|
|
|
+ zoomLevel.value = 1
|
|
|
+ translateX.value = 0
|
|
|
+ translateY.value = 0
|
|
|
+}
|
|
|
+
|
|
|
+/** 切换到上一张图片 */
|
|
|
const LastImage = () => {
|
|
|
if (currentIndex.value > 0) {
|
|
|
currentIndex.value--
|
|
|
@@ -218,7 +252,7 @@ const LastImage = () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 下一个图片
|
|
|
+/** 切换到下一张图片 */
|
|
|
const NextImage = () => {
|
|
|
if (currentIndex.value < props.imageList.length - 1) {
|
|
|
currentIndex.value++
|
|
|
@@ -226,239 +260,244 @@ const NextImage = () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 工具栏点击事件
|
|
|
-const ToolClick = (item) => {
|
|
|
+// ==================== 工具栏(绘图模式) ====================
|
|
|
+
|
|
|
+/** 工具栏点击事件 */
|
|
|
+const ToolClick = (item: ToolItem) => {
|
|
|
switch (item.value) {
|
|
|
- case '1':
|
|
|
- ZoomIn()
|
|
|
- break
|
|
|
- case '2':
|
|
|
- ZoomOut()
|
|
|
- break
|
|
|
- case '3':
|
|
|
- RotateCounterClockwiseOne()
|
|
|
- break
|
|
|
- case '4':
|
|
|
- RotateClockwiseOne()
|
|
|
- break
|
|
|
- case '5':
|
|
|
- RotateCounterClockwise90()
|
|
|
- break
|
|
|
- case '6':
|
|
|
- RotateClockwise90()
|
|
|
- break
|
|
|
+ case '1': ZoomIn(); break
|
|
|
+ case '2': ZoomOut(); break
|
|
|
+ case '3': RotateCounterClockwiseOne(); break
|
|
|
+ case '4': RotateClockwiseOne(); break
|
|
|
+ case '5': RotateCounterClockwise90(); break
|
|
|
+ case '6': RotateClockwise90(); break
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 放大图片
|
|
|
+// ==================== 缩放 ====================
|
|
|
+
|
|
|
+/** 放大图片 */
|
|
|
const ZoomIn = () => {
|
|
|
- if (zoomLevel.value < maxZoom.value) {
|
|
|
- zoomLevel.value += zoomStep.value
|
|
|
+ if (zoomLevel.value < MAX_ZOOM) {
|
|
|
+ zoomLevel.value = Math.round((zoomLevel.value + ZOOM_STEP) * 100) / 100
|
|
|
}
|
|
|
- console.log('打印当前缩放的级别', zoomLevel.value)
|
|
|
}
|
|
|
|
|
|
-// 缩小图片
|
|
|
+/** 缩小图片 */
|
|
|
const ZoomOut = () => {
|
|
|
- if (zoomLevel.value > minZoom.value) {
|
|
|
- zoomLevel.value -= zoomStep.value
|
|
|
+ if (zoomLevel.value > MIN_ZOOM) {
|
|
|
+ zoomLevel.value = Math.round((zoomLevel.value - ZOOM_STEP) * 100) / 100
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 逆时针旋转1度
|
|
|
+// ==================== 鼠标滚轮缩放(图片预览模式) ====================
|
|
|
+
|
|
|
+/** 滚轮事件:向上滚放大,向下滚缩小 */
|
|
|
+const HandleWheel = (e: WheelEvent) => {
|
|
|
+ if (e.deltaY < 0) {
|
|
|
+ ZoomIn()
|
|
|
+ } else {
|
|
|
+ ZoomOut()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// ==================== 鼠标拖拽移动(图片预览模式) ====================
|
|
|
+
|
|
|
+/** 鼠标按下:记录起始位置,进入拖拽状态(仅左键) */
|
|
|
+const HandleMouseDown = (e: MouseEvent) => {
|
|
|
+ if (e.button !== 0) return
|
|
|
+ isDragging.value = true
|
|
|
+ dragStartX.value = e.clientX - translateX.value
|
|
|
+ dragStartY.value = e.clientY - translateY.value
|
|
|
+}
|
|
|
+
|
|
|
+/** 鼠标移动:计算拖拽偏移量 */
|
|
|
+const HandleMouseMove = (e: MouseEvent) => {
|
|
|
+ if (!isDragging.value) return
|
|
|
+ translateX.value = e.clientX - dragStartX.value
|
|
|
+ translateY.value = e.clientY - dragStartY.value
|
|
|
+}
|
|
|
+
|
|
|
+/** 鼠标松开 / 移出区域:结束拖拽 */
|
|
|
+const HandleMouseUp = () => {
|
|
|
+ isDragging.value = false
|
|
|
+}
|
|
|
+
|
|
|
+// ==================== 旋转(绘图模式工具栏) ====================
|
|
|
+
|
|
|
+/** 逆时针旋转1度 */
|
|
|
const RotateCounterClockwiseOne = () => {
|
|
|
rotateAngle.value -= 1
|
|
|
- GetReturn()
|
|
|
+ EmitRotateAngle()
|
|
|
}
|
|
|
|
|
|
-// 顺时针旋转1度
|
|
|
+/** 顺时针旋转1度 */
|
|
|
const RotateClockwiseOne = () => {
|
|
|
rotateAngle.value += 1
|
|
|
- GetReturn()
|
|
|
+ EmitRotateAngle()
|
|
|
}
|
|
|
|
|
|
-// 逆时针旋转90度
|
|
|
+/** 逆时针旋转90度 */
|
|
|
const RotateCounterClockwise90 = () => {
|
|
|
rotateAngle.value -= 90
|
|
|
- GetReturn()
|
|
|
+ EmitRotateAngle()
|
|
|
}
|
|
|
|
|
|
-// 顺时针旋转90度
|
|
|
+/** 顺时针旋转90度 */
|
|
|
const RotateClockwise90 = () => {
|
|
|
rotateAngle.value += 90
|
|
|
- GetReturn()
|
|
|
+ EmitRotateAngle()
|
|
|
}
|
|
|
|
|
|
-// 加载绘图数据
|
|
|
-const LoadDrawData = () => {
|
|
|
+// ==================== 绘图数据加载 ====================
|
|
|
|
|
|
- console.log("加载模板绘图数据");
|
|
|
+/** 加载当前页的答题卡绘图数据 */
|
|
|
+const LoadDrawData = () => {
|
|
|
try {
|
|
|
-
|
|
|
-
|
|
|
const templateDataStr = localStorage.getItem('templateData')
|
|
|
-
|
|
|
- // const usedCardTypeStr = localStorage.getItem('usedCardType')
|
|
|
-
|
|
|
if (!templateDataStr) return
|
|
|
-
|
|
|
- const templateData = JSON.parse(templateDataStr);
|
|
|
-
|
|
|
- const usedCardType = 2;//默认三方卡
|
|
|
-
|
|
|
- console.log('打印templateData模板信息', templateData)
|
|
|
- console.log('打印currentPageNo', currentPageNo)
|
|
|
- console.log('打印answerData', props.answerData)
|
|
|
+
|
|
|
+ const templateData = JSON.parse(templateDataStr)
|
|
|
+ const usedCardType = 2 // 默认三方卡
|
|
|
+
|
|
|
const currentTemplateItem = templateData.find(
|
|
|
- (item) => item.pageNum == currentPageNo.value
|
|
|
+ (item: any) => item.pageNum == currentPageNo.value
|
|
|
)
|
|
|
-
|
|
|
+
|
|
|
if (!currentTemplateItem) {
|
|
|
console.warn('未找到当前页码对应的模板信息', currentPageNo.value)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- console.log('打印当前页的模板信息', currentTemplateItem)
|
|
|
-
|
|
|
- let drawList = []
|
|
|
+ let drawList: any[] = []
|
|
|
currentDrawData.value = []
|
|
|
-
|
|
|
- let paperInfo = {}
|
|
|
+
|
|
|
+ // 解析 paperInfo(保留以备后续使用)
|
|
|
+ let paperInfo: Record<string, any> = {}
|
|
|
try {
|
|
|
paperInfo = JSON.parse(currentTemplateItem.paperInfo)
|
|
|
} catch (e) {
|
|
|
console.error('解析 paperInfo 失败', e)
|
|
|
}
|
|
|
|
|
|
- //选择题判分暂时没有系统卡 注释掉
|
|
|
- // if (usedCardType == 1) {
|
|
|
- // currentPageInfo.value = {
|
|
|
- // width: paperInfo.width - 30,
|
|
|
- // height: paperInfo.height - 25,
|
|
|
- // }
|
|
|
- // }
|
|
|
-
|
|
|
if (currentTemplateItem.examCardGroupVOS) {
|
|
|
- // const list = currentTemplateItem.examCardGroupVOS.filter(
|
|
|
- // (item) => item.blockArea == 8
|
|
|
- // )
|
|
|
- const list = currentTemplateItem.examCardGroupVOS;//无需过滤 都是客观题的数据
|
|
|
-
|
|
|
- list.forEach((item) => {
|
|
|
- if (item.position) {
|
|
|
- let position = {}
|
|
|
- try {
|
|
|
- position = JSON.parse(item.position)
|
|
|
- } catch (e) {
|
|
|
- console.error('解析 position 失败', e)
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- const unit = 'px'
|
|
|
- let topiclist = []
|
|
|
-
|
|
|
- if (item.groupTopicList) {
|
|
|
- item.groupTopicList.forEach((topic) => {
|
|
|
- const answerValue = props.answerData[topic.topicName];//答案
|
|
|
- console.log('answerValue',answerValue)
|
|
|
- // 关键修改:使用深拷贝确保是纯JavaScript对象
|
|
|
- let answerList = JSON.parse(JSON.stringify(JSON.parse(topic.optionJsons)));
|
|
|
-
|
|
|
- // 为每个答案项显式添加 isCheck 属性
|
|
|
- answerList.forEach(answer => {
|
|
|
- answer.isCheck = false;
|
|
|
- });
|
|
|
- console.log("打印answerList",answerList);
|
|
|
- if (answerValue) {
|
|
|
- const answerLetters = answerValue.match(/[A-Za-z]/g) || []
|
|
|
- console.log('answerLetters',answerLetters)
|
|
|
- answerLetters.forEach((letter) => {
|
|
|
- let answerIndex = GetAnswerIndexToLetter(letter)
|
|
|
-
|
|
|
- if (answerIndex !== -1 && answerList[answerIndex]) {
|
|
|
- answerList[answerIndex].isCheck = true;
|
|
|
-
|
|
|
- } else {
|
|
|
- console.log("❌ 设置失败! answerIndex:", answerIndex, "answerList长度:", answerList.length);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- })
|
|
|
- }
|
|
|
- let topiclistObj={
|
|
|
- answerList:answerList,
|
|
|
- index:topic.index,
|
|
|
- topicName:topic.topicName,
|
|
|
- topicType:1,//1单选 2多选 3判断
|
|
|
- };
|
|
|
- topiclist.push(topiclistObj);
|
|
|
+ const list = currentTemplateItem.examCardGroupVOS
|
|
|
+
|
|
|
+ list.forEach((item: any) => {
|
|
|
+ if (!item.position) return
|
|
|
+
|
|
|
+ let position: Record<string, any> = {}
|
|
|
+ try {
|
|
|
+ position = JSON.parse(item.position)
|
|
|
+ } catch (e) {
|
|
|
+ console.error('解析 position 失败', e)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let topicList: any[] = []
|
|
|
+
|
|
|
+ if (item.groupTopicList) {
|
|
|
+ item.groupTopicList.forEach((topic: any) => {
|
|
|
+ const answerValue = props.answerData[topic.topicName]
|
|
|
+ let answerList = JSON.parse(JSON.stringify(JSON.parse(topic.optionJsons)))
|
|
|
+
|
|
|
+ // 初始化所有选项为未选中
|
|
|
+ answerList.forEach((answer: any) => {
|
|
|
+ answer.isCheck = false
|
|
|
+ })
|
|
|
+
|
|
|
+ // 根据答案标记选中项
|
|
|
+ if (answerValue) {
|
|
|
+ const answerLetters = answerValue.match(/[A-Za-z]/g) || []
|
|
|
+ answerLetters.forEach((letter: string) => {
|
|
|
+ const answerIndex = GetAnswerIndexToLetter(letter)
|
|
|
+ if (answerIndex !== -1 && answerList[answerIndex]) {
|
|
|
+ answerList[answerIndex].isCheck = true
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ topicList.push({
|
|
|
+ answerList,
|
|
|
+ index: topic.index,
|
|
|
+ topicName: topic.topicName,
|
|
|
+ topicType: 1, // 1单选 2多选 3判断
|
|
|
})
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- const obj = {
|
|
|
- x: position.x,
|
|
|
- y: position.y,
|
|
|
- w: position.w,
|
|
|
- h: position.h,
|
|
|
- position: item.position,
|
|
|
- blockArea: item.blockArea,
|
|
|
- page: currentPageNo.value,
|
|
|
- blockName: item.name,
|
|
|
- id: item.groupId,
|
|
|
- unit: unit,
|
|
|
- usedCardType: usedCardType,
|
|
|
- areaOption: item.groupConfig,
|
|
|
- topiclist: topiclist,
|
|
|
- }
|
|
|
- drawList.push(obj)
|
|
|
+ })
|
|
|
}
|
|
|
+
|
|
|
+ drawList.push({
|
|
|
+ x: position.x,
|
|
|
+ y: position.y,
|
|
|
+ w: position.w,
|
|
|
+ h: position.h,
|
|
|
+ position: item.position,
|
|
|
+ blockArea: item.blockArea,
|
|
|
+ page: currentPageNo.value,
|
|
|
+ blockName: item.name,
|
|
|
+ id: item.groupId,
|
|
|
+ unit: 'px',
|
|
|
+ usedCardType,
|
|
|
+ areaOption: item.groupConfig,
|
|
|
+ topiclist: topicList,
|
|
|
+ })
|
|
|
})
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
currentDrawData.value = drawList
|
|
|
} catch (error) {
|
|
|
console.error('LoadDrawData 错误:', error)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 根据字母转换成索引
|
|
|
-const GetAnswerIndexToLetter = (value) => {
|
|
|
- const letters = [
|
|
|
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
|
|
- 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
|
|
- ]
|
|
|
- return letters.findIndex((letter) => letter == value)
|
|
|
+// ==================== 工具函数 ====================
|
|
|
+
|
|
|
+/** 字母答案转数组索引(A->0, B->1, ...) */
|
|
|
+const GetAnswerIndexToLetter = (value: string): number => {
|
|
|
+ return value.toUpperCase().charCodeAt(0) - 'A'.charCodeAt(0)
|
|
|
}
|
|
|
|
|
|
-// 判断题的
|
|
|
-const GetAnswerIndexToLetterTF = (value) => {
|
|
|
+/** 判断题答案转索引(T->0, F->1) */
|
|
|
+const GetAnswerIndexToLetterTF = (value: string): number => {
|
|
|
const letters = ['T', 'F']
|
|
|
- return letters.findIndex((letter) => letter == value)
|
|
|
+ return letters.findIndex((letter) => letter === value)
|
|
|
}
|
|
|
-
|
|
|
-// 生命周期
|
|
|
-onBeforeUnmount(() => {
|
|
|
- // 清理工作
|
|
|
-})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.preview_image {
|
|
|
width: 100%;
|
|
|
- height: calc(100% - 20px);
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
display: flex;
|
|
|
justify-content: center;
|
|
|
align-items: center;
|
|
|
- margin-top: 0;
|
|
|
position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.preview_image_wrapper {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ overflow: hidden;
|
|
|
+ user-select: none;
|
|
|
+
|
|
|
+ img {
|
|
|
+ max-width: 100%;
|
|
|
+ max-height: 100%;
|
|
|
+ object-fit: contain;
|
|
|
+ user-select: none;
|
|
|
+ -webkit-user-drag: none;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.preview_canvas {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
-
|
|
|
+
|
|
|
.main_container {
|
|
|
width: calc(100% - 240px);
|
|
|
margin: auto;
|