feat: 讲解模式新增展开收起
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled

This commit is contained in:
shawko 2026-05-10 14:48:59 +08:00
parent adbfc847ec
commit 08eb7248ec

View File

@ -2,10 +2,9 @@
import { computed, onActivated, onBeforeUnmount, onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import hud from '#/utils/hud'
import { taskMgrService } from '#/api/service/question-bank/task-mgr'
import PaperQuestionItem from '#/components/paper-question-item/index.vue'
import hud from '#/utils/hud'
import ViewStudents from './components/viewStudents.vue'
@ -89,10 +88,10 @@ onActivated(() => {
})
// ======================== ========================
type ExplainPhase = 'detail' | 'countdown' | 'explaining'
type ExplainPhase = 'countdown' | 'detail' | 'explaining'
const phase = ref<ExplainPhase>('detail')
const countdown = ref(3)
let countdownTimer: ReturnType<typeof setInterval> | null = null
let countdownTimer: null | ReturnType<typeof setInterval> = null
function startCountdown() {
document.documentElement.requestFullscreen?.().catch(() => {})
@ -214,15 +213,23 @@ function resetZoom() {
const currentIndex = ref(0)
const answerExpanded = ref(false)
const explanationExpanded = ref(false)
const answerPanelCollapsed = ref(false)
const currentQuestion = computed(() => {
return paperDetail.value[currentIndex.value] || null
})
const hasAnswer = computed(() => Boolean(currentQuestion.value?.xkwTitle?.answer))
const hasAnswer = computed(() =>
Boolean(currentQuestion.value?.xkwTitle?.answer),
)
const hasExplanation = computed(() =>
Boolean(currentQuestion.value?.xkwTitle?.explanation),
)
const leftPanelStyle = computed(() => {
return answerPanelCollapsed.value
? { flex: '1 1 auto' }
: { flex: `0 0 ${leftWidth.value}px` }
})
function getTitleKey(item: any) {
return (
@ -243,6 +250,10 @@ function toggleExplanation() {
explanationExpanded.value = !explanationExpanded.value
}
function toggleAnswerPanel() {
answerPanelCollapsed.value = !answerPanelCollapsed.value
}
async function enterExplaining() {
initLeftWidth()
resetZoom()
@ -294,7 +305,7 @@ function endExplaining() {
hud.confirm({
content: '确定要结束讲解吗?',
title: '提示',
zIndex: 999999,
zIndex: 999_999,
onConfirm: async () => {
const lastQuestion = paperDetail.value[paperDetail.value.length - 1]
const lastQuestionId = getTitleKey(lastQuestion)
@ -422,7 +433,7 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
@wheel="onZoomWheel"
>
<!-- 左侧题目 -->
<div class="explain-left" :style="{ flex: `0 0 ${leftWidth}px` }">
<div class="explain-left" :style="leftPanelStyle">
<div class="explain-panel-header">
<span class="panel-title"> {{ currentIndex + 1 }} </span>
</div>
@ -457,10 +468,34 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
</div>
<!-- 拖动分隔条 -->
<div class="resize-handle" @mousedown="onResizeStart"></div>
<div
class="resize-handle"
:class="{ collapsed: answerPanelCollapsed }"
@mousedown="!answerPanelCollapsed && onResizeStart($event)"
>
<button
type="button"
class="answer-panel-toggle"
:aria-label="answerPanelCollapsed ? '展开答案解析' : '收起答案解析'"
:title="answerPanelCollapsed ? '展开答案解析' : '收起答案解析'"
@mousedown.stop
@click.stop="toggleAnswerPanel"
>
<span class="toggle-arrow">
{{ answerPanelCollapsed ? '<' : '>' }}
</span>
<span class="toggle-text">
{{ answerPanelCollapsed ? '展开' : '收起' }}
</span>
</button>
</div>
<!-- 右侧答案解析统计 -->
<div class="explain-right" style="flex: 1; width: 0">
<div
v-show="!answerPanelCollapsed"
class="explain-right"
style="flex: 1; width: 0"
>
<div class="explain-panel-header">
<span class="panel-title">答案与解析</span>
</div>
@ -475,7 +510,9 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
<latex-text :text="currentQuestion.xkwTitle.answer" />
</div>
<div v-else class="answer-analysis-collapsed">
{{ hasAnswer ? '答案已收起,点击底部“展开答案”查看' : '暂无答案' }}
{{
hasAnswer ? '答案已收起,点击底部“展开答案”查看' : '暂无答案'
}}
</div>
</div>
@ -491,7 +528,11 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
<latex-text :text="currentQuestion.xkwTitle.explanation" />
</div>
<div v-else class="answer-analysis-collapsed">
{{ hasExplanation ? '解析已收起,点击底部“展开解析”查看' : '暂无解析' }}
{{
hasExplanation
? '解析已收起,点击底部“展开解析”查看'
: '暂无解析'
}}
</div>
</div>
@ -714,8 +755,12 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
}
.resize-handle {
position: relative;
display: flex;
flex-shrink: 0;
width: 6px;
align-items: center;
justify-content: center;
width: 28px;
cursor: col-resize;
background: transparent;
transition: background 0.2s;
@ -723,8 +768,72 @@ async function getTitleStudentInfo(titleId: string, markType: number) {
.resize-handle:hover,
.resize-handle:active {
background: #ecf5ff;
}
.resize-handle::before {
width: 2px;
height: 100%;
content: '';
background: #dcdfe6;
border-radius: 2px;
transition: background 0.2s;
}
.resize-handle:hover::before,
.resize-handle:active::before {
background: #409eff;
border-radius: 3px;
}
.resize-handle.collapsed {
cursor: default;
}
.answer-panel-toggle {
position: absolute;
top: 50%;
left: 50%;
z-index: 2;
display: inline-flex;
flex-direction: column;
gap: 2px;
align-items: center;
justify-content: center;
width: 42px;
height: 72px;
padding: 0;
font-size: 13px;
font-weight: 500;
color: #409eff;
cursor: pointer;
background: #fff;
border: 1px solid #d9ecff;
border-radius: 21px;
box-shadow: 0 2px 10px rgb(64 158 255 / 18%);
transform: translate(-50%, -50%);
transition:
color 0.2s,
background 0.2s,
border-color 0.2s,
box-shadow 0.2s;
}
.answer-panel-toggle:hover,
.answer-panel-toggle:focus-visible {
color: #fff;
background: #409eff;
border-color: #409eff;
box-shadow: 0 4px 14px rgb(64 158 255 / 28%);
outline: none;
}
.toggle-arrow {
font-size: 18px;
line-height: 1;
}
.toggle-text {
line-height: 1.2;
}
.explain-panel-header {