xlx_teacher_app/src/pages/marking/grading.vue

305 lines
7.6 KiB
Vue
Raw Normal View History

2025-08-16 16:42:40 +08:00
<!-- 阅卷页面 -->
<route lang="jsonc">
{
"style": {
"navigationStyle": "custom"
}
}
</route>
<script lang="ts" setup>
import { computed, onMounted, onUnmounted, ref } from 'vue'
2025-08-16 18:30:38 +08:00
import AnswerDialog from '@/components/marking/components/dialog/AnswerDialog.vue'
import AvgScoreDialog from '@/components/marking/components/dialog/AvgScoreDialog.vue'
2025-08-16 16:42:40 +08:00
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'
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 isLandscape = ref(false)
const markingData = provideMarkingData({
taskId,
questionId,
isLandscape,
})
const { questionData: questions } = markingData
// 全屏状态
const isFullscreen = ref(false)
const currentQuestionIndex = ref(0)
const totalQuestions = computed(() => questions.value.length)
// 分数数据
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'
})
// 监听屏幕方向变化
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
}
// 监听全屏状态变化
function handleFullscreenChange() {
// #ifdef H5
isFullscreen.value = !!document.fullscreenElement
// #endif
}
onMounted(() => {
// #ifdef H5
window.addEventListener('orientationchange', handleOrientationChange)
document.addEventListener('fullscreenchange', handleFullscreenChange)
handleOrientationChange()
// #endif
})
onUnmounted(() => {
// #ifdef H5
window.removeEventListener('orientationchange', handleOrientationChange)
document.removeEventListener('fullscreenchange', handleFullscreenChange)
// #endif
})
// 切换屏幕方向
function toggleOrientation() {
// #ifdef H5
if (document.fullscreenElement) {
if (screen.orientation && 'lock' in screen.orientation) {
if (isLandscape.value) {
(screen.orientation as any).lock('portrait-primary')
}
else {
(screen.orientation as any).lock('landscape-primary')
}
}
}
// #endif
// #ifdef APP-PLUS
if (isLandscape.value) {
plus.screen.lockOrientation('portrait-primary')
}
else {
plus.screen.lockOrientation('landscape-primary')
}
// #endif
isLandscape.value = !isLandscape.value
}
// 全屏切换
function toggleFullscreen() {
// #ifdef H5
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen().then(() => {
isFullscreen.value = true
}).catch((err) => {
console.error('进入全屏失败:', err)
uni.showToast({
title: '全屏功能不支持',
icon: 'none',
})
})
}
else {
document.exitFullscreen().then(() => {
isFullscreen.value = false
})
}
// #endif
// #ifdef APP-PLUS
// App端可以通过设置状态栏来模拟全屏
if (isFullscreen.value) {
plus.navigator.setStatusBarStyle('dark')
plus.navigator.setStatusBarBackground('#000000')
}
else {
plus.navigator.setStatusBarStyle('light')
plus.navigator.setStatusBarBackground('#ffffff')
}
isFullscreen.value = !isFullscreen.value
// #endif
}
// 返回上一页
function goBack() {
uni.navigateBack()
}
// 题目选择
function selectQuestion(index: number) {
currentQuestionIndex.value = index
}
2025-08-16 18:30:38 +08:00
// 弹窗状态
2025-08-16 16:42:40 +08:00
const showScoreSettings = ref(false)
2025-08-16 18:30:38 +08:00
const showAvgScore = ref(false)
const showAnswer = ref(false)
2025-08-16 16:42:40 +08:00
// 打分设置
function openScoreSettings() {
showScoreSettings.value = true
}
// 打分设置确认
function handleScoreSettingsConfirm(data: { stepSize: number, commonScores: number[] }) {
console.log('打分设置已确认:', data)
uni.showToast({
title: '设置已保存',
icon: 'success',
})
}
// 查看均分
function viewAvgScore() {
2025-08-16 18:30:38 +08:00
showAvgScore.value = true
2025-08-16 16:42:40 +08:00
}
// 查看答案
function viewAnswer() {
2025-08-16 18:30:38 +08:00
showAnswer.value = true
2025-08-16 16:42:40 +08:00
}
// 当前题目信息
const currentQuestion = computed(() => questions.value[currentQuestionIndex.value])
// 快捷打分选择
function handleQuickScoreSelect(score: number) {
console.log('选择分数:', score)
}
</script>
<template>
2025-08-16 20:12:10 +08:00
<div class="relative h-screen w-100vw flex flex-col touch-pan-x touch-pan-y overflow-hidden overscroll-none pt-safe">
2025-08-16 16:42:40 +08:00
<MarkingLayout
:is-landscape="isLandscape"
:is-fullscreen="isFullscreen"
:current-question-index="currentQuestionIndex"
:total-questions="totalQuestions"
:questions="questions"
:my-score="myScore"
:avg-score="avgScore"
@go-back="goBack"
@select-question="selectQuestion"
@open-score-settings="openScoreSettings"
@view-avg-score="viewAvgScore"
@view-answer="viewAnswer"
@toggle-orientation="toggleOrientation"
@toggle-fullscreen="toggleFullscreen"
>
<template #content>
<MarkingImageViewerNew
v-if="questions[0]?.image_urls?.length"
v-model:question-data="questions"
:image-size="100"
/>
</template>
</MarkingLayout>
<!-- 快捷打分面板 - 固定定位在右侧 -->
2025-08-16 18:30:38 +08:00
<QuickScorePanel
:is-landscape="isLandscape"
:full-score="currentQuestion.full_score"
@score-selected="handleQuickScoreSelect"
/>
2025-08-16 16:42:40 +08:00
<!-- 打分设置弹窗 -->
<ScoreSettingsDialog
v-model="showScoreSettings"
2025-08-16 18:30:38 +08:00
:full-score="currentQuestion.full_score"
2025-08-16 16:42:40 +08:00
@confirm="handleScoreSettingsConfirm"
/>
2025-08-16 18:30:38 +08:00
<!-- 查看均分弹窗 -->
<AvgScoreDialog
v-model="showAvgScore"
:my-score="myScore"
:avg-score="avgScore"
:question-title="`${currentQuestion?.question_major}.${currentQuestion?.question_minor}`"
:full-score="currentQuestion?.full_score"
:avg-score-data="markingData.avgScoreData.value"
/>
<!-- 查看答案弹窗 -->
<AnswerDialog
v-model="showAnswer"
:question-title="`${currentQuestion?.question_major}.${currentQuestion?.question_minor}`"
:full-score="currentQuestion?.full_score"
/>
2025-08-16 16:42:40 +08:00
</div>
</template>
2025-08-16 20:12:10 +08:00
<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>