|
|
@@ -156,21 +156,76 @@
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 我的作答:writeLine 留痕图片 -->
|
|
|
+ <!-- 我的作答:imgUrlList 有值为非选择题展示作答留痕图片;无值为选择题只读回显选项 -->
|
|
|
<div v-if="detailTab === 'answer'" class="detail_content answer_content">
|
|
|
- <template v-if="selectedQuestion.answerImages.length">
|
|
|
- <el-image
|
|
|
- v-for="(url, idx) in selectedQuestion.answerImages"
|
|
|
- :key="idx"
|
|
|
- class="answer_img"
|
|
|
- :src="url"
|
|
|
- fit="contain"
|
|
|
- :preview-src-list="selectedQuestion.answerImages"
|
|
|
- :z-index="3000">
|
|
|
- </el-image>
|
|
|
- </template>
|
|
|
- <div v-else class="content_empty no_content_data">
|
|
|
- <span>暂无作答内容</span>
|
|
|
+ <!-- 非选择题:容器内嵌图片查看器,居中展示并支持缩放、拖动、多图翻阅 -->
|
|
|
+ <div v-if="answerImgList.length" class="img_viewer">
|
|
|
+ <div
|
|
|
+ class="viewer_stage"
|
|
|
+ ref="viewerStage"
|
|
|
+ :class="{ dragging: answerImgDragging }"
|
|
|
+ @wheel.prevent="onAnswerImgWheel"
|
|
|
+ @pointerdown="startAnswerImgDrag"
|
|
|
+ @dblclick="onStageDblClick">
|
|
|
+ <img
|
|
|
+ class="viewer_img"
|
|
|
+ :src="currentAnswerImg"
|
|
|
+ :alt="`作答图片${answerImgIndex + 1}`"
|
|
|
+ :style="{ transform: `translate(${answerImgX}px, ${answerImgY}px) scale(${answerImgScale})` }"
|
|
|
+ draggable="false">
|
|
|
+ <button
|
|
|
+ v-if="answerImgList.length > 1"
|
|
|
+ type="button"
|
|
|
+ class="viewer_arrow left"
|
|
|
+ @click="prevAnswerImg">
|
|
|
+ <i class="el-icon-arrow-left"></i>
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ v-if="answerImgList.length > 1"
|
|
|
+ type="button"
|
|
|
+ class="viewer_arrow right"
|
|
|
+ @click="nextAnswerImg">
|
|
|
+ <i class="el-icon-arrow-right"></i>
|
|
|
+ </button>
|
|
|
+ <div class="viewer_zoom">
|
|
|
+ <button type="button" class="zoom_btn" @click="zoomAnswerImg(-0.2)">
|
|
|
+ <i class="el-icon-minus"></i>
|
|
|
+ </button>
|
|
|
+ <span class="zoom_value" title="点击恢复100%并居中" @click="resetAnswerImgView">
|
|
|
+ {{ Math.round(answerImgScale * 100) }}%
|
|
|
+ </span>
|
|
|
+ <button type="button" class="zoom_btn" @click="zoomAnswerImg(0.2)">
|
|
|
+ <i class="el-icon-plus"></i>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ <!-- 多图页码:悬浮于舞台底部居中,不占布局空间;容器不拦截指针,仅数字可点 -->
|
|
|
+ <div v-if="answerImgList.length > 1" class="viewer_index">
|
|
|
+ <span
|
|
|
+ v-for="(url, idx) in answerImgList"
|
|
|
+ :key="idx"
|
|
|
+ class="index_num"
|
|
|
+ :class="{ active: idx === answerImgIndex }"
|
|
|
+ @click="goAnswerImg(idx)">
|
|
|
+ {{ idx + 1 }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 无作答图片即为选择题:choiceOptions 决定选项,stuAnswer 决定选中项,只读不可更改 -->
|
|
|
+ <div v-else class="choice_view">
|
|
|
+ <span class="choice_label">我的作答:</span>
|
|
|
+ <el-checkbox-group
|
|
|
+ v-model="selectedQuestion.stuAnswerKeys"
|
|
|
+ disabled
|
|
|
+ class="choice_radio">
|
|
|
+ <el-checkbox
|
|
|
+ v-for="key in selectedQuestion.choiceOptions"
|
|
|
+ :key="key"
|
|
|
+ :label="key">
|
|
|
+ {{ key }}
|
|
|
+ </el-checkbox>
|
|
|
+ </el-checkbox-group>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -214,7 +269,7 @@
|
|
|
<div class="page_table analysis_table_wrap">
|
|
|
<el-table
|
|
|
:data="questionPage"
|
|
|
- height="572"
|
|
|
+ height="575"
|
|
|
stripe
|
|
|
class="el-table--border analysis_table">
|
|
|
<el-table-column prop="name" label="小题名称" min-width="120" show-overflow-tooltip />
|
|
|
@@ -287,7 +342,7 @@
|
|
|
<div class="page_table analysis_table_wrap">
|
|
|
<el-table
|
|
|
:data="knowledgePage"
|
|
|
- height="572"
|
|
|
+ height="575"
|
|
|
stripe
|
|
|
class="el-table--border analysis_table">
|
|
|
<el-table-column prop="name" label="知识点" min-width="200" show-overflow-tooltip />
|
|
|
@@ -377,37 +432,50 @@ function mapKnowledgeList(list, fallback) {
|
|
|
return fallback ? [String(fallback)] : [];
|
|
|
}
|
|
|
|
|
|
-// writeLine 为作答留痕数据(JSON),兼容字符串/数组/对象多种形态,提取作答图片地址
|
|
|
-function parseWriteLine(raw) {
|
|
|
- if (!raw) return [];
|
|
|
- let data = raw;
|
|
|
- if (typeof raw === 'string') {
|
|
|
- const text = raw.trim();
|
|
|
- if (!text) return [];
|
|
|
- try {
|
|
|
- data = JSON.parse(text);
|
|
|
- } catch (e) {
|
|
|
- // 非 JSON 的纯字符串视为单个图片地址
|
|
|
- return /^https?:\/\//.test(text) ? [text] : [];
|
|
|
+// 作答留痕图片:imgUrlList 为图片地址数组,兼容字符串/对象元素及逗号串多种形态
|
|
|
+function normalizeImgList(list) {
|
|
|
+ if (Array.isArray(list)) {
|
|
|
+ return list.map(item => {
|
|
|
+ if (typeof item === 'string') return item;
|
|
|
+ return (item && (item.url || item.imgUrl || item.imageUrl || item.path || item.filePath)) || '';
|
|
|
+ }).filter(Boolean);
|
|
|
+ }
|
|
|
+ if (typeof list === 'string' && list.trim()) {
|
|
|
+ return list.split(',').map(url => url.trim()).filter(Boolean);
|
|
|
+ }
|
|
|
+ return [];
|
|
|
+}
|
|
|
+
|
|
|
+// 选择题选项 key:优先用 optionsNum 生成 A/B/C/D…;缺失时兜底用题干 optionList 自带的 key
|
|
|
+function buildChoiceOptions(optionsNum, optionList) {
|
|
|
+ const count = toNumber(optionsNum);
|
|
|
+ if (count) {
|
|
|
+ const options = [];
|
|
|
+ for (let i = 0; i < count && i < OPTION_KEYS.length; i += 1) {
|
|
|
+ options.push(OPTION_KEYS.charAt(i));
|
|
|
}
|
|
|
+ return options;
|
|
|
}
|
|
|
- const pick = obj => {
|
|
|
- if (!obj || typeof obj !== 'object') return [];
|
|
|
- const urls = obj.writeUrls || obj.urls || obj.imgUrls || obj.images || obj.imgs;
|
|
|
- if (Array.isArray(urls)) return urls;
|
|
|
- return obj.url || obj.imageUrl ? [obj.url || obj.imageUrl] : [];
|
|
|
- };
|
|
|
- if (Array.isArray(data)) {
|
|
|
- return data.reduce((acc, item) => {
|
|
|
- if (typeof item === 'string') {
|
|
|
- if (/^https?:\/\//.test(item)) acc.push(item);
|
|
|
- } else {
|
|
|
- pick(item).forEach(url => acc.push(url));
|
|
|
- }
|
|
|
- return acc;
|
|
|
- }, []).filter(Boolean);
|
|
|
+ if (Array.isArray(optionList) && optionList.length) {
|
|
|
+ return optionList.map(opt => (opt && opt.key) || '').filter(key => /^[A-Z]$/.test(key));
|
|
|
+ }
|
|
|
+ return [];
|
|
|
+}
|
|
|
+
|
|
|
+// stuAnswer 回显:兼容 "A,D"、"AD"、["A"] 等格式,只保留合法选项并去重
|
|
|
+function parseStuAnswerKeys(value, validKeys) {
|
|
|
+ let keys = [];
|
|
|
+ if (Array.isArray(value)) {
|
|
|
+ keys = value;
|
|
|
+ } else if (value !== null && value !== undefined && String(value).trim() !== '') {
|
|
|
+ keys = String(value).toUpperCase().match(/[A-Z]/g) || [];
|
|
|
}
|
|
|
- return pick(data).filter(Boolean);
|
|
|
+ const result = [];
|
|
|
+ keys.forEach(key => {
|
|
|
+ const k = String(key);
|
|
|
+ if (validKeys.indexOf(k) !== -1 && result.indexOf(k) === -1) result.push(k);
|
|
|
+ });
|
|
|
+ return result;
|
|
|
}
|
|
|
|
|
|
// 子题(subQuestions)映射
|
|
|
@@ -426,6 +494,9 @@ function mapSubQuestion(item, index) {
|
|
|
|
|
|
// 学生题目列表(studentQuestionVOList)映射为小题行
|
|
|
function mapQuestionRow(item, index) {
|
|
|
+ const options = mapOptionList(item.optionList);
|
|
|
+ // imgUrlList 有值为非选择题;无值为选择题,选项优先取 optionsNum,缺失时兜底题干选项 key
|
|
|
+ const choiceOptions = buildChoiceOptions(item.optionsNum, options);
|
|
|
return {
|
|
|
id: item.questionId || item.id || index,
|
|
|
name: item.questionName || '-',
|
|
|
@@ -435,8 +506,12 @@ function mapQuestionRow(item, index) {
|
|
|
title: item.title || '',
|
|
|
answer: item.answer || '',
|
|
|
parse: item.parse || '',
|
|
|
- options: mapOptionList(item.optionList),
|
|
|
- answerImages: parseWriteLine(item.writeLine),
|
|
|
+ options,
|
|
|
+ // 选择题只读回显:选项 key 列表 + stuAnswer 选中项
|
|
|
+ choiceOptions,
|
|
|
+ stuAnswerKeys: parseStuAnswerKeys(item.stuAnswer, choiceOptions),
|
|
|
+ // 非选择题作答留痕图片;是否选择题以该数组是否有值为准
|
|
|
+ imgUrlList: normalizeImgList(item.imgUrlList),
|
|
|
modelText: ANSWER_MODEL_MAP[item.model] || '',
|
|
|
model: toNumber(item.model),
|
|
|
score: item.score === '' || item.score == null ? '-' : item.score,
|
|
|
@@ -498,6 +573,12 @@ export default {
|
|
|
],
|
|
|
selectedIndex: 0,
|
|
|
detailTab: 'answer',
|
|
|
+ // 作答图片内嵌查看器:当前图片下标、缩放比例、平移位移、是否拖动中
|
|
|
+ answerImgIndex: 0,
|
|
|
+ answerImgScale: 1,
|
|
|
+ answerImgX: 0,
|
|
|
+ answerImgY: 0,
|
|
|
+ answerImgDragging: false,
|
|
|
questionPageNum: 1,
|
|
|
knowledgePageNum: 1,
|
|
|
// 两个表格前端分页,固定每页 10 条
|
|
|
@@ -556,6 +637,14 @@ export default {
|
|
|
selectedQuestion() {
|
|
|
return this.chartQuestions[this.selectedIndex] || null;
|
|
|
},
|
|
|
+ // 当前题作答留痕图片列表
|
|
|
+ answerImgList() {
|
|
|
+ return this.selectedQuestion ? this.selectedQuestion.imgUrlList : [];
|
|
|
+ },
|
|
|
+ // 查看器当前展示的图片地址
|
|
|
+ currentAnswerImg() {
|
|
|
+ return this.answerImgList[this.answerImgIndex] || '';
|
|
|
+ },
|
|
|
questionPage() {
|
|
|
const start = (this.questionPageNum - 1) * this.questionPageSize;
|
|
|
return this.questionRows.slice(start, start + this.questionPageSize);
|
|
|
@@ -583,11 +672,19 @@ export default {
|
|
|
return [['group', '得分率'], ...this.chartKnowledge.map(item => [item.name, item.rate])];
|
|
|
},
|
|
|
},
|
|
|
+ watch: {
|
|
|
+ // 切换小题时重置图片查看器到第一张、100% 缩放、居中无位移
|
|
|
+ selectedQuestion() {
|
|
|
+ this.answerImgIndex = 0;
|
|
|
+ this.resetAnswerImgView();
|
|
|
+ },
|
|
|
+ },
|
|
|
created() {
|
|
|
this.restoreState();
|
|
|
},
|
|
|
beforeDestroy() {
|
|
|
clearTimeout(this.scrollTimer);
|
|
|
+ this.stopAnswerImgDrag();
|
|
|
},
|
|
|
methods: {
|
|
|
restoreState() {
|
|
|
@@ -712,6 +809,99 @@ export default {
|
|
|
this.selectedIndex = index;
|
|
|
this.detailTab = 'answer';
|
|
|
},
|
|
|
+ // 上一张(循环翻阅),切换后恢复 100% 并居中
|
|
|
+ prevAnswerImg() {
|
|
|
+ const len = this.answerImgList.length;
|
|
|
+ if (!len) return;
|
|
|
+ this.answerImgIndex = (this.answerImgIndex - 1 + len) % len;
|
|
|
+ this.resetAnswerImgView();
|
|
|
+ },
|
|
|
+ // 下一张(循环翻阅),切换后恢复 100% 并居中
|
|
|
+ nextAnswerImg() {
|
|
|
+ const len = this.answerImgList.length;
|
|
|
+ if (!len) return;
|
|
|
+ this.answerImgIndex = (this.answerImgIndex + 1) % len;
|
|
|
+ this.resetAnswerImgView();
|
|
|
+ },
|
|
|
+ // 点击底部数字跳转到指定图片
|
|
|
+ goAnswerImg(index) {
|
|
|
+ if (index === this.answerImgIndex || index < 0 || index >= this.answerImgList.length) return;
|
|
|
+ this.answerImgIndex = index;
|
|
|
+ this.resetAnswerImgView();
|
|
|
+ },
|
|
|
+ // 缩放范围 20% ~ 500%;缩放后把位置夹回可视范围,图片小于舞台的方向自动回正
|
|
|
+ zoomAnswerImg(delta) {
|
|
|
+ this.answerImgScale = Math.min(5, Math.max(0.2, Number((this.answerImgScale + delta).toFixed(2))));
|
|
|
+ const pos = this.clampAnswerImgPos(this.answerImgX, this.answerImgY);
|
|
|
+ this.answerImgX = pos.x;
|
|
|
+ this.answerImgY = pos.y;
|
|
|
+ },
|
|
|
+ // 鼠标滚轮在容器内直接缩放
|
|
|
+ onAnswerImgWheel(e) {
|
|
|
+ this.zoomAnswerImg(e.deltaY < 0 ? 0.15 : -0.15);
|
|
|
+ },
|
|
|
+ // 复位缩放与位移:100% 且居中
|
|
|
+ resetAnswerImgView() {
|
|
|
+ this.answerImgScale = 1;
|
|
|
+ this.answerImgX = 0;
|
|
|
+ this.answerImgY = 0;
|
|
|
+ },
|
|
|
+ // 判断事件目标是否落在翻页箭头、缩放条、页码等控件上
|
|
|
+ isViewerControlTarget(e) {
|
|
|
+ return !!(e.target && e.target.closest && e.target.closest('.viewer_arrow, .viewer_zoom, .viewer_index'));
|
|
|
+ },
|
|
|
+ // 双击舞台空白或图片均复位,双击控件不触发
|
|
|
+ onStageDblClick(e) {
|
|
|
+ if (this.isViewerControlTarget(e)) return;
|
|
|
+ this.resetAnswerImgView();
|
|
|
+ },
|
|
|
+ // 在舞台任意位置按下即可拖动图片(Pointer Events 同时兼容鼠标与触摸)
|
|
|
+ startAnswerImgDrag(e) {
|
|
|
+ // 只响应主键/触摸,避免右键等干扰
|
|
|
+ if (e.button != null && e.button !== 0) return;
|
|
|
+ // 点翻页箭头、缩放条、页码时不启动拖动
|
|
|
+ if (this.isViewerControlTarget(e)) return;
|
|
|
+ this.answerImgDragging = true;
|
|
|
+ this.answerImgDrag = {
|
|
|
+ startX: e.clientX,
|
|
|
+ startY: e.clientY,
|
|
|
+ baseX: this.answerImgX,
|
|
|
+ baseY: this.answerImgY,
|
|
|
+ };
|
|
|
+ window.addEventListener('pointermove', this.onAnswerImgDragMove);
|
|
|
+ window.addEventListener('pointerup', this.stopAnswerImgDrag);
|
|
|
+ window.addEventListener('pointercancel', this.stopAnswerImgDrag);
|
|
|
+ },
|
|
|
+ onAnswerImgDragMove(e) {
|
|
|
+ if (!this.answerImgDragging || !this.answerImgDrag) return;
|
|
|
+ const dx = e.clientX - this.answerImgDrag.startX;
|
|
|
+ const dy = e.clientY - this.answerImgDrag.startY;
|
|
|
+ // transform 为 translate() scale()、origin 居中,位移即屏幕像素增量,任意缩放都可自由拖动
|
|
|
+ this.answerImgX = this.answerImgDrag.baseX + dx;
|
|
|
+ this.answerImgY = this.answerImgDrag.baseY + dy;
|
|
|
+ },
|
|
|
+ // 松手结束拖动,并成对移除 window 监听,防止容器外松手导致状态卡死
|
|
|
+ stopAnswerImgDrag() {
|
|
|
+ if (!this.answerImgDragging) return;
|
|
|
+ this.answerImgDragging = false;
|
|
|
+ this.answerImgDrag = null;
|
|
|
+ window.removeEventListener('pointermove', this.onAnswerImgDragMove);
|
|
|
+ window.removeEventListener('pointerup', this.stopAnswerImgDrag);
|
|
|
+ window.removeEventListener('pointercancel', this.stopAnswerImgDrag);
|
|
|
+ },
|
|
|
+ // 缩放后位置夹取:图片未溢出舞台的方向位移归零(自动回中),溢出方向限制在边缘对齐范围内
|
|
|
+ clampAnswerImgPos(x, y) {
|
|
|
+ const stage = this.$refs.viewerStage;
|
|
|
+ const imgEl = stage && stage.querySelector('.viewer_img');
|
|
|
+ if (!stage || !imgEl) return { x, y };
|
|
|
+ // clientWidth/Height 为缩放前布局尺寸,乘 scale 才是当前渲染尺寸
|
|
|
+ const maxX = Math.max(0, (imgEl.clientWidth * this.answerImgScale - stage.clientWidth) / 2);
|
|
|
+ const maxY = Math.max(0, (imgEl.clientHeight * this.answerImgScale - stage.clientHeight) / 2);
|
|
|
+ return {
|
|
|
+ x: Math.min(maxX, Math.max(-maxX, x)),
|
|
|
+ y: Math.min(maxY, Math.max(-maxY, y)),
|
|
|
+ };
|
|
|
+ },
|
|
|
// 判断富文本是否有实际内容:纯 <p><br></p> 等空标签视为空,含图片或文字视为有内容
|
|
|
hasHtmlContent(content) {
|
|
|
if (!content) return false;
|
|
|
@@ -794,8 +984,7 @@ export default {
|
|
|
return tag.length > 5 ? `${tag.slice(0, 5)}…` : tag;
|
|
|
},
|
|
|
goBack() {
|
|
|
- // 从查看分析返回时,列表定位到「已提交」页签
|
|
|
- this.$router.push({ path: '/teachingHomework/list', query: { status: 1 } });
|
|
|
+ this.$router.push('/teachingHomework/list');
|
|
|
},
|
|
|
},
|
|
|
};
|
|
|
@@ -993,18 +1182,22 @@ export default {
|
|
|
align-items: stretch;
|
|
|
gap: 10px;
|
|
|
margin-bottom: 10px;
|
|
|
+ /* 限制详情区最大高度,文字或图片过多时由内部容器滚动,不撑大整卡 */
|
|
|
+ max-height: 600px;
|
|
|
|
|
|
.detail_card {
|
|
|
min-width: 0;
|
|
|
+ min-height: 0;
|
|
|
margin-bottom: 0;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
|
|
|
|
/* 左题干卡固定宽度 */
|
|
|
.stem_card {
|
|
|
- flex: 0 0 410px;
|
|
|
- width: 410px;
|
|
|
+ flex: 0 0 480px;
|
|
|
+ width: 480px;
|
|
|
padding: 20px;
|
|
|
}
|
|
|
|
|
|
@@ -1012,6 +1205,7 @@ export default {
|
|
|
.answer_col {
|
|
|
flex: 1;
|
|
|
min-width: 0;
|
|
|
+ min-height: 0;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 10px;
|
|
|
@@ -1238,7 +1432,7 @@ export default {
|
|
|
|
|
|
.detail_content {
|
|
|
flex: 1;
|
|
|
- min-height: 360px;
|
|
|
+ min-height: 240px;
|
|
|
overflow-y: auto;
|
|
|
padding: 16px;
|
|
|
background: #F5F7FA;
|
|
|
@@ -1250,25 +1444,237 @@ export default {
|
|
|
}
|
|
|
|
|
|
.answer_content {
|
|
|
- text-align: center;
|
|
|
display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-start;
|
|
|
+ text-align: left;
|
|
|
|
|
|
- .answer_img {
|
|
|
- display: block;
|
|
|
- width: 100%;
|
|
|
- max-width: 560px;
|
|
|
- margin: 0 auto;
|
|
|
- border-radius: 4px;
|
|
|
- background: #fff;
|
|
|
+ /* 选择题只读作答:结构与样式和作答页 answer.vue 保持一致 */
|
|
|
+ .choice_view {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ }
|
|
|
|
|
|
- :deep(img) {
|
|
|
- width: 100%;
|
|
|
- height: auto;
|
|
|
+ .choice_label {
|
|
|
+ flex-shrink: 0;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+
|
|
|
+ .choice_radio {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ min-height: 20px;
|
|
|
+ gap: 20px;
|
|
|
+
|
|
|
+ :deep(.el-checkbox) {
|
|
|
+ margin-right: 0 !important;
|
|
|
+ height: 20px;
|
|
|
+ display: inline-flex !important;
|
|
|
+ align-items: center;
|
|
|
+ cursor: default;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-checkbox__input) {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ height: 16px;
|
|
|
+ line-height: 16px;
|
|
|
+ cursor: default;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-checkbox__inner) {
|
|
|
+ width: 16px;
|
|
|
+ height: 16px;
|
|
|
+ border: 1px solid #DCDFE6;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 2px;
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ top: 1px;
|
|
|
+ left: 5px;
|
|
|
+ width: 3px;
|
|
|
+ height: 8px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-checkbox__input.is-checked .el-checkbox__inner),
|
|
|
+ :deep(.el-checkbox__input.is-focus .el-checkbox__inner) {
|
|
|
+ background-color: #2E64FA;
|
|
|
+ box-shadow: none;
|
|
|
+ border-color: #2E64FA;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 禁用态仅用于展示:未选项白底,选中项仍为蓝底白勾,字母不变灰 */
|
|
|
+ :deep(.el-checkbox__input.is-disabled .el-checkbox__inner) {
|
|
|
+ background-color: #fff;
|
|
|
+ border-color: #DCDFE6;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner) {
|
|
|
+ background-color: #2E64FA;
|
|
|
+ border-color: #2E64FA;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-checkbox.is-disabled .el-checkbox__label) {
|
|
|
+ color: #333;
|
|
|
+ cursor: default;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-checkbox__label) {
|
|
|
+ display:inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ height: 20px;
|
|
|
+ padding-left: 8px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #333;
|
|
|
+ line-height: 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 非选择题作答图片:容器内嵌查看器,图片居中、可缩放、可翻阅 */
|
|
|
+ .img_viewer {
|
|
|
+ align-self: stretch;
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ .viewer_stage {
|
|
|
+ position: relative;
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ overflow: hidden;
|
|
|
+ // background: #fff;
|
|
|
+ border-radius: 6px;
|
|
|
+ /* 整个舞台都是拖拽面:抓手光标 + 触摸手势由舞台接管 */
|
|
|
+ cursor: grab;
|
|
|
+ user-select: none;
|
|
|
+ touch-action: none;
|
|
|
+ /* 提升为独立合成层,保证 overflow 对内部 transform 图片的裁切实时生效,避免缩放残影 */
|
|
|
+ transform: translateZ(0);
|
|
|
+ isolation: isolate;
|
|
|
+
|
|
|
+ &.dragging {
|
|
|
+ cursor: grabbing;
|
|
|
+ }
|
|
|
+
|
|
|
+ .viewer_img {
|
|
|
display: block;
|
|
|
+ max-width: 100%;
|
|
|
+ max-height: 100%;
|
|
|
+ object-fit: contain;
|
|
|
+ transform-origin: center center;
|
|
|
+ -webkit-user-drag: none;
|
|
|
+ pointer-events: none;
|
|
|
+ /* 独立合成层更新 transform,缩放拖动均即时渲染不留残影 */
|
|
|
+ will-change: transform;
|
|
|
+ backface-visibility: hidden;
|
|
|
}
|
|
|
|
|
|
- & + .answer_img {
|
|
|
- margin-top: 12px;
|
|
|
+ .viewer_arrow {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ transform: translateY(-50%);
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ padding: 0;
|
|
|
+ border: none;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: rgba(0, 0, 0, 0.45);
|
|
|
+ color: #fff;
|
|
|
+ font-size: 16px;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background: rgba(0, 0, 0, 0.65);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.left { left: 10px; }
|
|
|
+ &.right { right: 10px; }
|
|
|
+ }
|
|
|
+
|
|
|
+ .viewer_zoom {
|
|
|
+ position: absolute;
|
|
|
+ right: 10px;
|
|
|
+ bottom: 10px;
|
|
|
+ height: 28px;
|
|
|
+ padding: 0 6px;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 2px;
|
|
|
+ background: rgba(0, 0, 0, 0.55);
|
|
|
+ border-radius: 16px;
|
|
|
+
|
|
|
+ .zoom_btn {
|
|
|
+ width: 24px;
|
|
|
+ height: 24px;
|
|
|
+ padding: 0;
|
|
|
+ border: none;
|
|
|
+ background: transparent;
|
|
|
+ color: #fff;
|
|
|
+ font-size: 13px;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ &:hover { color: #B3CDFF; }
|
|
|
+ }
|
|
|
+
|
|
|
+ .zoom_value {
|
|
|
+ min-width: 44px;
|
|
|
+ text-align: center;
|
|
|
+ color: #fff;
|
|
|
+ font-size: 12px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 页码悬浮舞台底部居中,不占据图片区高度;容器透传指针,仅数字本身可点 */
|
|
|
+ .viewer_index {
|
|
|
+ position: absolute;
|
|
|
+ left: 50%;
|
|
|
+ bottom: 12px;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 10px;
|
|
|
+ max-width: calc(100% - 120px);
|
|
|
+ pointer-events: none;
|
|
|
+
|
|
|
+ .index_num {
|
|
|
+ width: 30px;
|
|
|
+ height: 30px;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: rgba(242, 243, 245, 0.92);
|
|
|
+ box-sizing: border-box;
|
|
|
+ color: #4B5563;
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 30px;
|
|
|
+ text-align: center;
|
|
|
+ cursor: pointer;
|
|
|
+ pointer-events: auto;
|
|
|
+ transition: background-color 0.15s, color 0.15s;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: #2E64FA;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ background: #2E64FA;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1341,6 +1747,8 @@ export default {
|
|
|
|
|
|
.content_empty {
|
|
|
flex: 1;
|
|
|
+ align-self: stretch;
|
|
|
+ width: 100%;
|
|
|
min-height: 200px;
|
|
|
}
|
|
|
}
|