370 lines
9.9 KiB
Vue
370 lines
9.9 KiB
Vue
<!-- 阅卷页面 -->
|
|
<route lang="jsonc">
|
|
{
|
|
"style": {
|
|
"navigationStyle": "custom"
|
|
}
|
|
}
|
|
</route>
|
|
|
|
<script lang="ts" setup>
|
|
import { useQueryClient } from '@tanstack/vue-query'
|
|
import { whenever } from '@vueuse/core'
|
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
|
import AnswerDialog from '@/components/marking/components/dialog/AnswerDialog.vue'
|
|
import AvgScoreDialog from '@/components/marking/components/dialog/AvgScoreDialog.vue'
|
|
import FullscreenImageDialog from '@/components/marking/components/dialog/FullscreenImageDialog.vue'
|
|
import ScoreSettingsDialog from '@/components/marking/components/dialog/ScoreSettingsDialog.vue'
|
|
import QuickScorePanel from '@/components/marking/components/QuickScorePanel.vue'
|
|
import MarkingImageViewerNew from '@/components/marking/components/renderer/MarkingImageViewerNew.vue'
|
|
import { provideMarkingData } from '@/components/marking/composables/useMarkingData'
|
|
import MarkingLayout from '@/components/marking/MarkingLayout.vue'
|
|
import { DefaultMarkingDataProvider, DefaultMarkingHistoryProvider, provideMarkingContext } from '@/composables/marking/MarkingContext'
|
|
import { provideMarkingHistory } from '@/composables/marking/useMarkingHistory'
|
|
import { provideMarkingNavigation } from '@/composables/marking/useMarkingNavigation'
|
|
|
|
defineOptions({
|
|
name: 'GradingPage',
|
|
})
|
|
|
|
// 获取路由参数
|
|
const examId = ref<number>()
|
|
const subjectId = ref<number>()
|
|
const questionId = ref<number>()
|
|
const taskId = ref<number>()
|
|
const evaluationType = ref<'single' | 'double'>('single')
|
|
const taskType = ref<'initial' | 'final' | 'arbitration'>('initial')
|
|
|
|
// 屏幕方向状态
|
|
const isLandscape = ref(false)
|
|
|
|
// 图片缩放比例
|
|
const imageScale = ref(1.0)
|
|
|
|
// 提供阅卷上下文(使用默认实现)
|
|
provideMarkingContext({
|
|
dataProvider: new DefaultMarkingDataProvider(),
|
|
historyProvider: new DefaultMarkingHistoryProvider(),
|
|
isHistory: false,
|
|
defaultPosition: 'last',
|
|
})
|
|
|
|
// 提供历史记录管理
|
|
provideMarkingHistory({ taskId })
|
|
|
|
// 提供导航管理
|
|
const markingNavigation = provideMarkingNavigation({ taskId })
|
|
const {
|
|
currentIndex: navCurrentIndex,
|
|
totalCount: navTotalCount,
|
|
isViewingHistory,
|
|
canGoPrev,
|
|
canGoNext,
|
|
goToPrevQuestion,
|
|
goToNextQuestion,
|
|
initNavigation,
|
|
createSwipeHandler,
|
|
} = markingNavigation
|
|
|
|
// 提供数据管理
|
|
const markingData = provideMarkingData({
|
|
taskId,
|
|
questionId,
|
|
examId,
|
|
subjectId,
|
|
isLandscape,
|
|
taskType,
|
|
})
|
|
const { questionData: questions, questionsList, totalQuestions: totalQuestionsCount } = markingData
|
|
|
|
// 全屏弹窗状态
|
|
const showFullscreenImage = ref(false)
|
|
|
|
const currentQuestionIndex = ref(0)
|
|
const totalQuestions = computed(() => totalQuestionsCount.value)
|
|
|
|
// 分数数据
|
|
const myScore = computed(() => {
|
|
const data = markingData.avgScoreData.value
|
|
return data?.teacher_score.average_score || 0
|
|
})
|
|
const avgScore = computed(() => {
|
|
const data = markingData.avgScoreData.value
|
|
return data?.all_teachers_score.average_score || 0
|
|
})
|
|
|
|
// 页面加载时获取参数
|
|
onLoad((options) => {
|
|
examId.value = Number(options.examId)
|
|
subjectId.value = Number(options.subjectId)
|
|
questionId.value = Number(options.questionId)
|
|
taskId.value = Number(options.taskId) // 从路由参数获取taskId
|
|
evaluationType.value = options.type as 'single' | 'double' || 'single'
|
|
taskType.value = options.taskType
|
|
})
|
|
|
|
// 监听屏幕方向变化
|
|
function handleOrientationChange() {
|
|
// #ifdef H5
|
|
const orientation = window.screen.orientation?.angle || 0
|
|
isLandscape.value = orientation === 90 || orientation === 270
|
|
// #endif
|
|
|
|
// #ifdef APP-PLUS
|
|
// plus.screen.lockOrientation('portrait-primary')
|
|
// #endif
|
|
}
|
|
|
|
onMounted(async () => {
|
|
// #ifdef H5
|
|
window.addEventListener('orientationchange', handleOrientationChange)
|
|
handleOrientationChange()
|
|
// #endif
|
|
|
|
// 初始化导航
|
|
await initNavigation()
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
// #ifdef H5
|
|
window.removeEventListener('orientationchange', handleOrientationChange)
|
|
// #endif
|
|
})
|
|
|
|
// 切换屏幕方向
|
|
function toggleOrientation() {
|
|
// #ifdef APP-PLUS
|
|
if (isLandscape.value) {
|
|
plus.screen.lockOrientation('portrait-primary')
|
|
}
|
|
else {
|
|
plus.screen.lockOrientation('landscape-primary')
|
|
}
|
|
// #endif
|
|
|
|
isLandscape.value = !isLandscape.value
|
|
}
|
|
|
|
// 打开全屏图片弹窗
|
|
function toggleFullscreen() {
|
|
showFullscreenImage.value = true
|
|
}
|
|
|
|
// 返回上一页
|
|
function goBack() {
|
|
uni.navigateBack()
|
|
}
|
|
|
|
// 题目选择
|
|
function selectQuestion(index: number) {
|
|
currentQuestionIndex.value = index
|
|
questions.value = []
|
|
}
|
|
|
|
// 弹窗状态
|
|
const showScoreSettings = ref(false)
|
|
const showAvgScore = ref(false)
|
|
const showAnswer = ref(false)
|
|
|
|
// 打分设置
|
|
function openScoreSettings() {
|
|
showScoreSettings.value = true
|
|
}
|
|
|
|
// 打分设置确认
|
|
function handleScoreSettingsConfirm(data: { stepSize: number, commonScores: number[] }) {
|
|
console.log('打分设置已确认:', data)
|
|
uni.showToast({
|
|
title: '设置已保存',
|
|
icon: 'success',
|
|
})
|
|
}
|
|
|
|
// 查看均分
|
|
function viewAvgScore() {
|
|
showAvgScore.value = true
|
|
}
|
|
|
|
// 查看答案
|
|
function viewAnswer() {
|
|
showAnswer.value = true
|
|
}
|
|
|
|
// 当前题目信息
|
|
const currentQuestions = computed(() => questionsList.value[currentQuestionIndex.value])
|
|
// 如果在历史查看模式,使用历史记录数据,否则使用当前题目数据
|
|
const currentQuestion = computed(() => {
|
|
if (isViewingHistory.value && markingNavigation.currentHistoryQuestion.value) {
|
|
// 历史模式:返回历史记录
|
|
return markingNavigation.currentHistoryQuestion.value
|
|
}
|
|
// 正常模式:返回当前题目
|
|
return questions.value[0]
|
|
})
|
|
const currentTask = computed(() => currentQuestions.value?.tasks?.[taskType.value])
|
|
|
|
// 当前答题卡图片列表
|
|
const currentImageUrls = computed(() => currentQuestion.value?.image_urls || [])
|
|
|
|
let isFirst = true
|
|
whenever(questionsList, () => {
|
|
if (isFirst) {
|
|
isFirst = false
|
|
currentQuestionIndex.value = questionsList.value.findIndex(q => q.question_id === questionId.value)
|
|
}
|
|
})
|
|
|
|
const queryClient = useQueryClient()
|
|
whenever(currentTask, (task, oldTask) => {
|
|
taskId.value = task?.id
|
|
questionId.value = task?.question_id
|
|
|
|
queryClient.invalidateQueries({ queryKey: ['marking-question', oldTask?.id] })
|
|
queryClient.invalidateQueries({ queryKey: ['marking-question', task.id] })
|
|
})
|
|
|
|
// 快捷打分选择
|
|
function handleQuickScoreSelect(score: number) {
|
|
console.log('选择分数:', score)
|
|
}
|
|
|
|
// 创建手势处理器
|
|
const swipeHandler = createSwipeHandler()
|
|
|
|
// 历史查看模式提示
|
|
const historyModeText = computed(() => {
|
|
if (isViewingHistory.value) {
|
|
return `${navCurrentIndex.value}/${totalQuestionsCount.value}`
|
|
}
|
|
return ''
|
|
})
|
|
|
|
// 上一题
|
|
async function handlePrevQuestion() {
|
|
await goToPrevQuestion()
|
|
}
|
|
|
|
// 下一题
|
|
async function handleNextQuestion() {
|
|
await goToNextQuestion()
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="relative h-screen w-100vw flex flex-col touch-pan-x touch-pan-y overflow-hidden overscroll-none pt-safe"
|
|
@touchstart="swipeHandler.onTouchStart"
|
|
@touchend="swipeHandler.onTouchEnd"
|
|
>
|
|
<MarkingLayout
|
|
:is-landscape="isLandscape"
|
|
:is-fullscreen="false"
|
|
:current-question-index="currentQuestionIndex"
|
|
:current-task-submit="currentTask?.marked_quantity || 0"
|
|
:total-questions="totalQuestions"
|
|
:questions="questionsList"
|
|
:my-score="myScore"
|
|
:avg-score="avgScore"
|
|
:is-viewing-history="isViewingHistory"
|
|
:can-go-prev="canGoPrev"
|
|
:can-go-next="canGoNext"
|
|
:history-mode-text="historyModeText"
|
|
:task-id="taskId"
|
|
@go-back="goBack"
|
|
@select-question="selectQuestion"
|
|
@open-score-settings="openScoreSettings"
|
|
@view-avg-score="viewAvgScore"
|
|
@view-answer="viewAnswer"
|
|
@toggle-orientation="toggleOrientation"
|
|
@toggle-fullscreen="toggleFullscreen"
|
|
@prev-question="handlePrevQuestion"
|
|
@next-question="handleNextQuestion"
|
|
>
|
|
<template #content>
|
|
<MarkingImageViewerNew
|
|
v-if="questions[0]?.image_urls?.length"
|
|
v-model:scale="imageScale"
|
|
:question-data="[currentQuestion]"
|
|
:image-size="100"
|
|
/>
|
|
|
|
<!-- 快捷打分面板 - 固定定位在右侧 -->
|
|
<QuickScorePanel
|
|
v-if="currentQuestion && !isViewingHistory"
|
|
:is-landscape="isLandscape"
|
|
:full-score="currentQuestion.full_score"
|
|
@score-selected="handleQuickScoreSelect"
|
|
/>
|
|
</template>
|
|
</MarkingLayout>
|
|
|
|
<!-- 打分设置弹窗 -->
|
|
<ScoreSettingsDialog
|
|
v-model="showScoreSettings"
|
|
:full-score="currentQuestions?.full_score"
|
|
@confirm="handleScoreSettingsConfirm"
|
|
/>
|
|
|
|
<!-- 查看均分弹窗 -->
|
|
<AvgScoreDialog
|
|
v-model="showAvgScore"
|
|
:my-score="myScore"
|
|
:avg-score="avgScore"
|
|
:question-title="`${currentQuestions?.question_major}.${currentQuestions?.question_minor}`"
|
|
:full-score="currentQuestions?.full_score"
|
|
:avg-score-data="markingData.avgScoreData.value"
|
|
/>
|
|
|
|
<!-- 查看答案弹窗 -->
|
|
<AnswerDialog
|
|
v-if="currentQuestion"
|
|
v-model="showAnswer"
|
|
:question-title="`${currentQuestion?.question_major}.${currentQuestion?.question_minor}`"
|
|
:full-score="currentQuestion?.full_score"
|
|
:standard-answer="currentQuestion?.standard_answer || ''"
|
|
/>
|
|
|
|
<!-- 全屏图片弹窗 -->
|
|
<FullscreenImageDialog
|
|
v-model="showFullscreenImage"
|
|
:image-urls="currentImageUrls"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
/* 禁用橡皮筋效果 */
|
|
.h-screen {
|
|
position: fixed;
|
|
overscroll-behavior: none;
|
|
-webkit-overflow-scrolling: touch;
|
|
}
|
|
|
|
/* H5端额外的橡皮筋禁用 */
|
|
/* #ifdef H5 */
|
|
body,
|
|
html {
|
|
overscroll-behavior: none;
|
|
overflow: hidden;
|
|
position: fixed;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
/* #endif */
|
|
|
|
/* 微信小程序端橡皮筋禁用 */
|
|
/* #ifdef MP-WEIXIN */
|
|
page {
|
|
overflow: hidden;
|
|
overscroll-behavior: none;
|
|
}
|
|
/* #endif */
|
|
|
|
/* APP端橡皮筋禁用 */
|
|
/* #ifdef APP-PLUS */
|
|
page {
|
|
overflow: hidden;
|
|
}
|
|
/* #endif */
|
|
</style>
|