refactor: 优化阅卷
continuous-integration/drone/push Build is passing Details

This commit is contained in:
AfyerCu 2025-11-18 21:38:44 +08:00
parent 8504c031f6
commit 9e81aa4bec
10 changed files with 410 additions and 496 deletions

View File

@ -117,183 +117,171 @@ const currentHistoryIndexInList = computed(() => {
<!-- 横屏布局 -->
<view v-if="isLandscape" class="h-100vh w-100vw flex flex-grow flex-col bg-gray-100">
<!-- 顶部导航栏 -->
<view class="h-48px flex items-center border-b bg-white pr-16px shadow-sm">
<view class="z-10 h-56px flex flex-shrink-0 items-center border-b border-slate-100 bg-white px-16px shadow-sm">
<!-- 左侧返回按钮 -->
<view class="w-60px flex items-center justify-center border-r">
<view class="mr-16px flex items-center justify-center">
<view
class="size-32px flex cursor-pointer items-center justify-center rounded transition-colors hover:bg-gray-100"
class="size-36px flex cursor-pointer items-center justify-center rounded-full bg-slate-50 text-slate-600 transition-all active:scale-95 active:bg-slate-200 hover:bg-slate-100"
@click="emit('goBack')"
>
<view class="i-carbon-arrow-left size-20px text-gray-700" />
<view class="i-carbon-arrow-left size-20px" />
</view>
</view>
<!-- 右侧导航栏 -->
<view class="flex flex-1 items-center justify-between pl-16px">
<!-- 题号信息 -->
<view class="mr-16px flex items-center">
<text class="text-16px text-gray-800 font-medium">
<!-- 中间题号信息 -->
<view class="flex flex-1 flex-col justify-center">
<view class="flex items-baseline gap-2">
<text class="text-18px text-slate-800 font-bold leading-none">
{{ isViewingHistory ? historyModeText : `${currentTaskSubmit}/${totalQuestions}` }}
</text>
<text class="text-12px text-slate-400 leading-none">
题号
</text>
</view>
</view>
<!-- 右侧操作按钮组 -->
<view class="flex items-center gap-12px">
<view
v-for="(btn, idx) in [
{ text: '设置', icon: 'i-carbon-settings', action: 'openScoreSettings' },
{ text: '均分', icon: 'i-carbon-chart-bar', action: 'viewAvgScore' },
{ text: '答案', icon: 'i-carbon-document', action: 'viewAnswer' },
]"
:key="idx"
class="flex flex-col cursor-pointer items-center justify-center gap-2px rounded-lg px-8px py-4px transition-colors active:bg-slate-100 hover:bg-slate-50"
@click="emit(btn.action as any)"
>
<view :class="btn.icon" class="text-18px text-slate-600" />
<text class="text-10px text-slate-500">{{ btn.text }}</text>
</view>
<!-- 操作按钮组 -->
<view class="flex items-center gap-16px text-14px">
<view
class="cursor-pointer text-gray-700 transition-colors hover:text-blue-500"
@click="emit('openScoreSettings')"
>
打分设置
</view>
<view
class="cursor-pointer text-gray-700 transition-colors hover:text-blue-500"
@click="emit('viewAvgScore')"
>
查看均分
</view>
<view
class="cursor-pointer text-gray-700 transition-colors hover:text-blue-500"
@click="emit('viewAnswer')"
>
查看答案
</view>
<view
class="size-32px flex cursor-pointer items-center justify-center rounded transition-colors hover:bg-gray-100"
@click="emit('toggleOrientation')"
>
<view class="i-carbon:mobile-view-orientation size-18px text-gray-700" />
</view>
<view
class="size-32px flex cursor-pointer items-center justify-center rounded transition-colors hover:bg-gray-100"
@click="emit('toggleFullscreen')"
>
<view
class="i-carbon-image-search size-18px text-gray-700"
/>
</view>
<view class="mx-4px h-24px w-1px bg-slate-200" />
<view
class="size-36px flex cursor-pointer items-center justify-center rounded-full bg-slate-50 text-slate-600 transition-all active:scale-95 active:bg-slate-200 hover:bg-slate-100"
@click="emit('toggleFullscreen')"
>
<view class="i-carbon-image-search size-22px" />
</view>
<view
class="size-36px flex cursor-pointer items-center justify-center rounded-full bg-slate-50 text-slate-600 transition-all active:scale-95 active:bg-slate-200 hover:bg-slate-100"
@click="emit('toggleOrientation')"
>
<view class="i-carbon:mobile-view-orientation size-22px" />
</view>
</view>
</view>
<!-- 内容区域 - 左右分栏 -->
<view class="flex flex-1">
<view class="flex flex-1 overflow-hidden">
<!-- 左侧打分信息面板 -->
<view class="w-60px flex flex-col border-r border-gray-200 bg-white">
<view class="flex flex-col items-center px-4px py-12px text-center">
<view v-if="isViewingHistory" class="mb-2px text-12px text-gray-600">
历史打分
<view class="z-5 w-72px flex flex-col items-center border-r border-slate-100 bg-white py-16px shadow-sm">
<view class="w-full flex flex-col items-center gap-12px">
<view class="flex flex-col items-center gap-2px">
<text class="text-10px text-slate-400">满分</text>
<text class="text-14px text-slate-600 font-semibold font-mono">{{ formatScore(currentQuestionFullScore) }}</text>
</view>
<view v-else class="mb-2px text-12px text-gray-600">
满分/{{ `${formatScore(currentQuestionFullScore)}` }}
</view>
<view class="text-16px text-blue-600 font-semibold">
{{ isViewingHistory ? `${formatScore(currentQuestionFinalScore)}/${formatScore(currentQuestionFullScore)}` : '' }}
<view class="h-1px w-32px bg-slate-100" />
<view class="flex flex-col items-center gap-2px">
<text class="text-10px text-slate-400">得分</text>
<text class="text-20px text-blue-600 font-bold font-mono">
{{ isViewingHistory ? formatScore(currentQuestionFinalScore) : '--' }}
</text>
</view>
</view>
</view>
<!-- 中间图片区域 -->
<view class="relative h-full flex-1 overflow-hidden bg-gray-50">
<!-- 左右箭头按钮 -->
<!-- <view
class="arrow-btn-landscape fixed left-80px z-100 size-32px flex cursor-pointer items-center justify-center rounded transition-colors"
:class="canGoPrev ? 'bg-gray-100 hover:bg-gray-200' : 'bg-gray-50 opacity-50 cursor-not-allowed'"
@click="canGoPrev && emit('prevQuestion')"
>
<view class="i-carbon-chevron-left size-16px text-gray-700" />
</view>
<view
class="arrow-btn-landscape fixed z-100 size-32px flex cursor-pointer items-center justify-center rounded transition-colors"
:class="canGoNext ? 'bg-gray-100 hover:bg-gray-200' : 'bg-gray-50 opacity-50 cursor-not-allowed'"
:style="{ right: '280px' }"
@click="canGoNext && emit('nextQuestion')"
>
<view class="i-carbon-chevron-right size-16px text-gray-700" />
</view> -->
<view class="relative flex-1 overflow-hidden bg-slate-50">
<slot name="content" :current-question="currentQuestion" />
</view>
<!-- 右侧打分区域 -->
<view style="height: calc(100vh - 60px)" class="flex flex-col overflow-auto border-l border-gray-200 bg-white">
<slot name="scoring" />
</view>
<slot name="scoring" />
</view>
</view>
<!-- 竖屏布局 -->
<view v-else class="h-100vh w-100vw flex flex-grow flex-col bg-gray-100">
<view v-else class="h-100vh w-100vw flex flex-grow flex-col bg-slate-50">
<!-- 顶部导航栏 -->
<view class="h-12 flex items-center bg-white px-4 py-2 shadow-sm">
<!-- 左侧 -->
<view class="flex items-center gap-3">
<view
class="size-8 flex cursor-pointer items-center justify-center rounded transition-colors hover:bg-gray-100"
@click="emit('goBack')"
>
<view class="i-carbon-arrow-left size-5 text-gray-700" />
</view>
<!-- 题目选择器弹窗 -->
<wd-picker
:model-value="currentQuestionIndex"
:columns="questionPickerColumns"
:value="String(currentQuestionIndex)"
:title="`${currentQuestion?.question_major}.${currentQuestion?.question_minor}`"
@confirm="({ value }) => emit('selectQuestion', Number(value))"
/>
</view>
</view>
<!-- 二层导航栏 -->
<view class="h-12 flex items-center border-t border-gray-200 bg-white px-4 py-2">
<!-- 题号信息 -->
<view class="flex items-center gap-4">
<view v-if="!isViewingHistory" class="text-base text-gray-800 font-medium">
{{ Math.min(totalQuestions, currentTaskSubmit) }}/{{ totalQuestions }}
</view>
<view v-else class="flex flex-col leading-tight">
<text class="text-base text-gray-800 font-medium">
{{ Math.min(totalQuestions, currentTaskSubmit) }}/{{ totalQuestions }}
</text>
<text class="text-xs text-blue-600">
分值 {{ formatScore(currentQuestionFinalScore) }}/{{ formatScore(currentQuestionFullScore) }}
</text>
</view>
</view>
<!-- 操作按钮组 -->
<view class="flex flex-1 items-center justify-end gap-3">
<view
class="cursor-pointer text-sm text-gray-700 transition-colors hover:text-blue-500"
@click="emit('openScoreSettings')"
>
打分设置
</view>
<view
class="cursor-pointer text-sm text-gray-700 transition-colors hover:text-blue-500"
@click="emit('viewAvgScore')"
>
查看均分
</view>
<view
class="cursor-pointer text-sm text-gray-700 transition-colors hover:text-blue-500"
@click="emit('viewAnswer')"
>
查看答案
</view>
<view
class="size-28px flex cursor-pointer items-center justify-center rounded transition-colors hover:bg-gray-100"
@click="emit('toggleOrientation')"
>
<view class="i-carbon:mobile-view-orientation size-16px text-gray-700" />
</view>
<view
class="size-28px flex cursor-pointer items-center justify-center rounded transition-colors hover:bg-gray-100"
@click="emit('toggleFullscreen')"
>
<view class="z-10 flex flex-col bg-white shadow-sm">
<view class="h-44px flex items-center justify-between px-16px">
<view class="flex items-center gap-8px">
<view
class="i-carbon-image-search size-16px text-gray-700"
/>
class="size-32px flex cursor-pointer items-center justify-center rounded-full bg-slate-50 text-slate-600 active:bg-slate-100"
@click="emit('goBack')"
>
<view class="i-carbon-arrow-left size-18px" />
</view>
<!-- 题目选择器 -->
<wd-picker
:model-value="currentQuestionIndex"
:columns="questionPickerColumns"
:value="String(currentQuestionIndex)"
:title="`${currentQuestion?.question_major}.${currentQuestion?.question_minor}`"
@confirm="({ value }) => emit('selectQuestion', Number(value))"
>
<view class="flex items-center gap-4px rounded-full bg-slate-50 px-12px py-4px active:bg-slate-100">
<text class="text-14px text-slate-800 font-bold">
{{ currentQuestion?.question_major }}.{{ currentQuestion?.question_minor }}
</text>
<view class="i-carbon-chevron-down text-12px text-slate-400" />
</view>
</wd-picker>
</view>
<view class="flex items-center gap-2">
<!-- 题目序号 -->
<text class="mr-2 text-14px text-slate-600 font-medium">
{{ currentTaskSubmit }}/{{ totalQuestions }}
</text>
<view class="flex gap-8px">
<view
class="size-32px flex cursor-pointer items-center justify-center rounded-full bg-slate-50 text-slate-600 active:bg-slate-100"
@click="emit('toggleFullscreen')"
>
<view class="i-carbon-image-search size-18px" />
</view>
<view
class="size-32px flex cursor-pointer items-center justify-center rounded-full bg-slate-50 text-slate-600 active:bg-slate-100"
@click="emit('toggleOrientation')"
>
<view class="i-carbon:mobile-view-orientation size-18px" />
</view>
</view>
</view>
</view>
<!-- 二层信息栏 -->
<view class="h-36px flex items-center justify-between border-t border-slate-100 bg-slate-50 px-16px">
<view class="flex items-baseline gap-2">
<text class="text-12px text-slate-500">满分</text>
<text class="text-12px text-slate-800 font-bold font-mono">{{ formatScore(currentQuestionFullScore) }}</text>
<div class="mx-2 h-10px w-1px bg-slate-300" />
<text class="text-12px text-slate-500">得分</text>
<text class="text-16px text-blue-600 font-bold font-mono">
{{ isViewingHistory ? formatScore(currentQuestionFinalScore) : '--' }}
</text>
</view>
<view class="flex items-center gap-16px">
<text
v-for="(action, idx) in [
{ t: '设置', a: 'openScoreSettings' },
{ t: '均分', a: 'viewAvgScore' },
{ t: '答案', a: 'viewAnswer' },
]"
:key="idx"
class="text-12px text-slate-500 active:text-blue-600"
@click="emit(action.a as any)"
>
{{ action.t }}
</text>
</view>
</view>
</view>
@ -301,14 +289,12 @@ const currentHistoryIndexInList = computed(() => {
<!-- 作答区域和打分区域 - 左右分栏 -->
<view class="relative flex flex-1 overflow-hidden">
<!-- 左侧图片区域 -->
<view class="relative flex-1 overflow-hidden bg-gray-50">
<view class="relative flex-1 overflow-hidden bg-slate-50">
<slot name="content" :current-question="currentQuestion" />
</view>
<!-- 右侧打分区域 -->
<view class="h-full flex flex-col overflow-auto border-l border-gray-200 bg-white">
<slot name="scoring" />
</view>
<slot name="scoring" />
</view>
</view>
</template>

View File

@ -238,6 +238,18 @@ async function submitScore(score: number) {
//
async function submitCurrentScore() {
try {
// -1
if (currentScore.value === -1) {
if (currentMode.value === 'subtract') {
//
currentScore.value = props.fullScore
}
else {
// 0
currentScore.value = 0
}
}
await markingData.submitRecord()
uni.showToast({
title: '提交成功',
@ -261,10 +273,12 @@ async function submitCurrentScore() {
<template>
<!-- 横屏布局 -->
<div
<scroll-view
v-if="isLandscape"
class="h-full flex flex-col overflow-x-hidden overflow-y-auto py-8px"
:class="(isCollapsed && scoreMode === 'onekey') || scoreMode === 'quick' ? 'w-60px px-4px' : 'w-120px px-8px'"
scroll-y
scroll-x
class="z-5 h-full flex flex-grow-0 flex-col py-8px"
:class="(isCollapsed && scoreMode === 'onekey') || scoreMode === 'quick' ? 'w-60px min-w-60px px-4px' : 'w-120px min-w-120px px-8px'"
>
<div class="flex flex-1 flex-col gap-4px">
<!-- 收起时一列布局 -->
@ -290,7 +304,7 @@ async function submitCurrentScore() {
<div
v-for="item in row"
:key="item.label"
class="h-32px flex flex-1 cursor-pointer select-none items-center justify-center border-2 rounded-4px text-12px font-medium transition-all active:scale-95"
class="h-40px flex flex-1 cursor-pointer select-none items-center justify-center border rounded-lg text-14px font-bold shadow-sm transition-all active:scale-95"
:class="item.class"
@click="item.action"
>
@ -300,44 +314,46 @@ async function submitCurrentScore() {
<div v-if="row.length === 1" class="flex-1" />
</div>
</template>
</div>
<!-- 收起/展开按钮 - 仅在一键打分模式显示 -->
<div
v-if="scoreMode === 'onekey'"
class="mt-6px h-32px flex flex-shrink-0 cursor-pointer items-center justify-center border-2 border-blue-200 rounded-4px bg-blue-50 transition-all active:scale-95"
@click="toggleCollapse"
>
<!-- 收起/展开按钮 - 仅在一键打分模式显示 -->
<div
class="size-14px text-blue-500 transition-transform"
:class="!isCollapsed ? 'i-carbon-chevron-right' : 'i-carbon-chevron-left'"
/>
v-if="scoreMode === 'onekey'"
class="mb-40px mt-6px h-40px flex flex-shrink-0 cursor-pointer items-center justify-center border border-blue-100 rounded-lg bg-blue-50 transition-all active:scale-95"
@click="toggleCollapse"
>
<div
class="size-16px text-blue-500 transition-transform"
:class="!isCollapsed ? 'i-carbon-chevron-right' : 'i-carbon-chevron-left'"
/>
</div>
</div>
<!-- 快捷打分模式切换 -->
<div v-if="scoreMode === 'quick'" class="mt-12px flex flex-col gap-6px">
<div v-if="scoreMode === 'quick'" class="mt-12px flex flex-col gap-8px">
<div
class="h-36px flex cursor-pointer items-center justify-center border rounded-4px text-11px transition-all"
:class="currentMode === 'add' ? 'border-green-500 bg-green-50 text-green-600' : 'border-gray-300 bg-white text-gray-600'"
class="h-40px flex cursor-pointer items-center justify-center border rounded-lg text-12px font-bold shadow-sm transition-all"
:class="currentMode === 'add' ? 'border-green-500 bg-green-500 text-white ring-2 ring-green-200' : 'border-slate-200 bg-white text-slate-600 hover:bg-slate-50'"
@click="currentMode = 'add'"
>
加分
</div>
<div
class="h-36px flex cursor-pointer items-center justify-center border rounded-4px text-11px transition-all"
:class="currentMode === 'subtract' ? 'border-red-500 bg-red-50 text-red-600' : 'border-gray-300 bg-white text-gray-600'"
class="h-40px flex cursor-pointer items-center justify-center border rounded-lg text-12px font-bold shadow-sm transition-all"
:class="currentMode === 'subtract' ? 'border-red-500 bg-red-500 text-white ring-2 ring-red-200' : 'border-slate-200 bg-white text-slate-600 hover:bg-slate-50'"
@click="currentMode = 'subtract'"
>
减分
</div>
</div>
</div>
</scroll-view>
<!-- 竖屏布局 -->
<div
<scroll-view
v-else
class="h-full flex flex-col overflow-x-hidden overflow-y-auto py-16rpx"
:class="(isCollapsed && scoreMode === 'onekey') || scoreMode === 'quick' ? 'w-100rpx px-8rpx' : 'w-160rpx px-12rpx'"
scroll-y
scroll-x
class="z-5 h-full flex flex-grow-0 flex-col py-16rpx"
:class="(isCollapsed && scoreMode === 'onekey') || scoreMode === 'quick' ? 'w-100rpx min-w-100rpx px-8rpx' : 'w-160rpx min-w-160rpx px-12rpx'"
>
<div class="flex flex-1 flex-col gap-8rpx">
<!-- 收起时一列布局 -->
@ -363,7 +379,7 @@ async function submitCurrentScore() {
<div
v-for="item in row"
:key="item.label"
class="h-64rpx flex flex-1 cursor-pointer select-none items-center justify-center border-2 rounded-8rpx text-24rpx font-medium transition-all active:scale-95"
class="h-80rpx flex flex-1 cursor-pointer select-none items-center justify-center border rounded-16rpx text-28rpx font-bold shadow-sm transition-all active:scale-95"
:class="item.class"
@click="item.action"
>
@ -373,43 +389,43 @@ async function submitCurrentScore() {
<div v-if="row.length === 1" class="flex-1" />
</div>
</template>
</div>
<!-- 收起/展开按钮 - 仅在一键打分模式显示 -->
<div
v-if="scoreMode === 'onekey'"
class="mt-12rpx h-64rpx flex flex-shrink-0 cursor-pointer items-center justify-center border-2 border-blue-200 rounded-8rpx bg-blue-50 transition-all active:scale-95"
@click="toggleCollapse"
>
<!-- 收起/展开按钮 - 仅在一键打分模式显示 -->
<div
class="size-28rpx text-blue-500 transition-transform"
:class="!isCollapsed ? 'i-carbon-chevron-right' : 'i-carbon-chevron-left'"
/>
v-if="scoreMode === 'onekey'"
class="mb-40px mt-12rpx h-80rpx flex flex-shrink-0 cursor-pointer items-center justify-center border border-blue-100 rounded-16rpx bg-blue-50 transition-all active:scale-95"
@click="toggleCollapse"
>
<div
class="size-32rpx text-blue-500 transition-transform"
:class="!isCollapsed ? 'i-carbon-chevron-right' : 'i-carbon-chevron-left'"
/>
</div>
</div>
<!-- 快捷打分模式切换 -->
<div v-if="scoreMode === 'quick'" class="mt-16rpx flex flex-col gap-8rpx">
<div v-if="scoreMode === 'quick'" class="mt-16rpx flex flex-col gap-12rpx">
<div
class="h-56rpx flex cursor-pointer items-center justify-center border rounded-8rpx text-22rpx transition-all"
:class="currentMode === 'add' ? 'border-green-500 bg-green-50 text-green-600' : 'border-gray-300 bg-white text-gray-600'"
class="h-80rpx flex cursor-pointer items-center justify-center border rounded-16rpx text-26rpx font-bold shadow-sm transition-all"
:class="currentMode === 'add' ? 'border-green-500 bg-green-500 text-white ring-2 ring-green-200' : 'border-slate-200 bg-white text-slate-600 hover:bg-slate-50'"
@click="currentMode = 'add'"
>
加分
</div>
<div
class="h-56rpx flex cursor-pointer items-center justify-center border rounded-8rpx text-22rpx transition-all"
:class="currentMode === 'subtract' ? 'border-red-500 bg-red-50 text-red-600' : 'border-gray-300 bg-white text-gray-600'"
class="h-80rpx flex cursor-pointer items-center justify-center border rounded-16rpx text-26rpx font-bold shadow-sm transition-all"
:class="currentMode === 'subtract' ? 'border-red-500 bg-red-500 text-white ring-2 ring-red-200' : 'border-slate-200 bg-white text-slate-600 hover:bg-slate-50'"
@click="currentMode = 'subtract'"
>
减分
</div>
</div>
</div>
</scroll-view>
<!-- 快捷打分模式的悬浮提交按钮 -->
<div
v-if="scoreMode === 'quick'"
class="fixed z-10 size-48px flex select-none items-center justify-center border-2 border-blue-500 rounded-full bg-blue-500 text-white shadow-xl transition-transform lg:size-56px active:scale-95"
class="fixed z-10 size-48px flex select-none items-center justify-center border-2 border-blue-500 rounded-full bg-blue-500 text-14px text-white shadow-xl transition-transform lg:size-56px active:scale-95"
:class="{
'cursor-move': isDragging,
'cursor-pointer': !isDragging,

View File

@ -66,16 +66,16 @@ const expandedImageUrls = computed(() => {
>
<!-- 顶部工具栏 -->
<view
class="flex items-center justify-between bg-black/80 px-4 py-3"
class="flex items-center justify-between bg-black/80 px-16px py-12px"
:style="{ paddingTop: `${(safeAreaInsets?.top || 0) + 12}px` }"
@click.stop
>
<text class="text-base text-white font-medium">全屏查看</text>
<text class="text-16px text-white font-medium">全屏查看</text>
<view
class="size-8 flex cursor-pointer items-center justify-center rounded transition-colors hover:bg-white/20"
class="size-32px flex cursor-pointer items-center justify-center rounded transition-colors hover:bg-white/20"
@click="visible = false"
>
<view class="i-carbon-close size-5 text-white" />
<view class="i-carbon-close size-20px text-white" />
</view>
</view>
@ -95,7 +95,7 @@ const expandedImageUrls = computed(() => {
</view>
<!-- 多张图片 -->
<view v-else class="flex flex-col gap-4 p-4">
<view v-else class="flex flex-col gap-12px p-16px">
<view
v-for="(imageUrl, index) in expandedImageUrls"
:key="index"
@ -113,8 +113,8 @@ const expandedImageUrls = computed(() => {
</view>
<!-- 底部提示 -->
<view class="bg-black/80 px-4 py-2 text-center" @click.stop>
<text class="text-sm text-white/70">长按图片可保存</text>
<view class="bg-black/80 px-16px py-8px text-center" @click.stop>
<text class="text-12px text-white/70">长按图片可保存</text>
</view>
</view>
</wd-popup>

View File

@ -17,6 +17,9 @@ const emit = defineEmits<{
'marking-change': [questionIndex: number, imageIndex: number, data: DomMarkingData]
'ready': [imageInfo: { width: number, height: number }]
'scroll-edge': [atLeftEdge: boolean, atRightEdge: boolean]
'touch-start': [event: TouchEvent]
'touch-move': [event: TouchEvent]
'touch-end': [event: TouchEvent]
}>()
const scale = defineModel<number>('scale', { default: 1.0 })
@ -31,6 +34,21 @@ const firstImageSize = ref({ width: 0, height: 0 })
// ID uni.createSelectorQuery
const scrollContainerId = `scroll-container-${Math.random().toString(36).slice(2)}`
/**
* 手势事件透传
*/
function handleTouchStart(e: TouchEvent) {
emit('touch-start', e)
}
function handleTouchMove(e: TouchEvent) {
emit('touch-move', e)
}
function handleTouchEnd(e: TouchEvent) {
emit('touch-end', e)
}
/**
* 检查滚动边缘状态使用 uni.createSelectorQuery
*/
@ -39,12 +57,14 @@ function checkScrollEdge() {
query.select(`#${scrollContainerId}`).scrollOffset()
query.select(`#${scrollContainerId}`).boundingClientRect()
query.exec((res) => {
if (!res || res.length < 2) return
if (!res || res.length < 2)
return
const scrollInfo = res[0]
const rectInfo = res[1]
if (!scrollInfo || !rectInfo) return
if (!scrollInfo || !rectInfo)
return
const scrollLeft = scrollInfo.scrollLeft || 0
const scrollWidth = scrollInfo.scrollWidth || 0
@ -153,10 +173,15 @@ defineExpose({
</script>
<template>
<div
<scroll-view
:id="scrollContainerId"
class="h-full w-full overflow-auto"
class="h-full w-full"
scroll-y="true"
scroll-x="true"
@scroll="handleScroll"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
>
<!-- 题目列表 -->
<view
@ -191,8 +216,8 @@ defineExpose({
v-for="(url, index) in nextQuestionImages"
:key="`preload_${index}`"
:src="url"
class="w-0 h-0"
class="h-0 w-0"
/>
</view>
</div>
</scroll-view>
</template>

View File

@ -40,89 +40,44 @@ const emit = defineEmits<{
'toggle-special-mark': [type: 'excellent' | 'typical' | 'problem']
}>()
const currentTool = defineModel<MarkingTool>('currentTool', { required: true })
const showToolOptions = computed(() => {
return [MarkingTool.PEN, MarkingTool.TEXT].includes(currentTool.value)
})
//
const toolbarRef = ref<HTMLElement>()
// 使 SessionStorage
const position = useSessionStorage('marking-toolbar-position', { x: 16, y: 16 })
//
const isCollapsed = ref(uni.getStorageSync('marking-toolbar-collapsed') || false)
watch(isCollapsed, (newVal) => {
uni.setStorageSync('marking-toolbar-collapsed', newVal)
})
//
const isDragging = ref(false)
const dragState = ref({
startX: 0,
startY: 0,
startPositionX: 0,
startPositionY: 0,
})
const { getDictOptionsComputed } = useDict()
const { options: problemTypeOptions } = getDictOptionsComputed(DictCode.SCAN_ANOMALY_STATUS)
const problemTooltipContent = computed(() => {
if (props.currentMarkingData?.isProblem) {
return `当前问题类型:${problemTypeOptions.value.find(item => item.value === props.currentMarkingData?.problemType)?.label}`
}
return '标记为问题卷'
})
/**
* SVG 转换为 data URL用于小程序
*/
function svgToDataURL(svg: string): string {
// dataset
svg = svg.replace(/data-(.*?=(['"]).*?\2)/g, '$1')
// data-xlink-href
svg = svg.replace(/xlink-href=/g, 'xlink:href=')
// dataset kebab-case viewBox
svg = svg.replace(/view-box=/g, 'viewBox=')
// SVG titledescdefs
svg = svg.replace(/<(title|desc|defs)>[\s\S]*?<\/\1>/g, '')
// XML SVG xmlns
if (!/xmlns=/.test(svg))
svg = svg.replace(/<svg/, '<svg xmlns=\'http://www.w3.org/2000/svg\'')
// SVG
svg = svg.replace(/\d+\.\d+/g, match => Number.parseFloat(Number.parseFloat(match).toFixed(2)) as any)
//
svg = svg.replace(/<!--[\s\S]*?-->/g, '')
// HTML white-space
svg = svg.replace(/\s+/g, ' ')
// https://github.com/bhovhannes/svg-url-loader/blob/master/src/loader.js
svg = svg.replace(/[{}|\\^~[\]`"<>#%]/g, (match) => {
return `%${match[0].charCodeAt(0).toString(16).toUpperCase()}`
})
// \' kbone bug outerHTML
// background-image: url( URI
svg = svg.replace(/'/g, '\\\'')
// mime Webview Data URI
return `data:image/svg+xml,${svg.trim()}`
}
/**
* 生成半对标记的 SVG data URL
* 根据当前按钮状态动态生成颜色
*/
function generateHalfMarkSvg(isActive: boolean): string {
//
const color = isActive ? '#ffffff' : '#eab308' //
const color = isActive ? '#ffffff' : '#eab308'
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<line x1="6" y1="12" x2="10.5" y2="16.5" stroke="${color}" stroke-width="2" stroke-linecap="round" fill="none"/>
<line x1="10.5" y1="16.5" x2="18" y2="7.5" stroke="${color}" stroke-width="2" stroke-linecap="round" fill="none"/>
@ -142,9 +97,6 @@ const pendingMarkType = ref<'correct' | 'wrong' | 'half' | null>(null)
//
const PEN_TOOLTIP_KEY = 'marking-pen-tooltip-shown'
/**
* 显示画笔提示仅首次显示
*/
function showPenTooltip() {
const hasShown = uni.getStorageSync(PEN_TOOLTIP_KEY)
if (!hasShown) {
@ -160,91 +112,6 @@ function showPenTooltip() {
}
}
//
const baseButtonClass = computed(() => {
return props.isLandscape
? 'size-32rpx lg:size-8 !p-0 rounded-lg flex items-center justify-center transition-all duration-200 ease-in-out shadow-sm hover:shadow-md active:scale-95 after:border-none'
: 'size-8 lg:size-10 !p-0 rounded-lg flex items-center justify-center transition-all duration-200 ease-in-out shadow-sm hover:shadow-md active:scale-95 after:border-none'
})
const separatorClass = computed(() => {
return props.isLandscape
? 'mx-4rpx lg:mx-1 h-4 lg:h-6 w-px flex-shrink-0 bg-gray-300'
: 'h-5 lg:h-6 w-px bg-gray-300'
})
const iconClass = computed(() => {
return props.isLandscape
? 'size-20rpx lg:size-4'
: 'size-5 lg:size-5 w-4 lg:w-5'
})
/**
* 获取工具按钮样式类
*/
function getToolButtonClass(tool: MarkingTool) {
const isActive = currentTool.value === tool
return [
baseButtonClass.value,
isActive
? 'bg-blue-600 text-white border border-blue-500'
: 'bg-white text-blue-600 border border-blue-200 hover:bg-blue-50 hover:border-blue-300 hover:text-blue-700',
]
}
/**
* 获取标记按钮样式类对错半对
*/
function getMarkButtonClass(type: 'correct' | 'wrong' | 'half') {
const toolMap = {
correct: MarkingTool.CORRECT,
wrong: MarkingTool.WRONG,
half: MarkingTool.HALF,
}
const colorMap = {
correct: {
active: 'bg-green-500 text-white border border-green-400',
inactive: 'bg-white text-green-600 border border-green-200 hover:bg-green-50 hover:border-green-300 hover:text-green-700',
},
wrong: {
active: 'bg-red-500 text-white border border-red-400',
inactive: 'bg-white text-red-600 border border-red-200 hover:bg-red-50 hover:border-red-300 hover:text-red-700',
},
half: {
active: 'bg-yellow-500 text-white border border-yellow-400',
inactive: 'bg-white text-yellow-600 border border-yellow-200 hover:bg-yellow-50 hover:border-yellow-300 hover:text-yellow-700',
},
}
const isActive = currentTool.value === toolMap[type]
const colors = colorMap[type]
return [baseButtonClass.value, isActive ? colors.active : colors.inactive]
}
/**
* 获取特殊标记按钮样式类
*/
function getSpecialButtonClass(type: 'excellent' | 'typical' | 'problem') {
const isActive = props.specialMarks[type]
const colorMap = {
excellent: {
active: 'bg-yellow-500 text-white border border-yellow-400',
inactive: 'bg-white text-yellow-600 border border-yellow-200 hover:bg-yellow-50 hover:border-yellow-300 hover:text-yellow-700',
},
typical: {
active: 'bg-blue-600 text-white border border-blue-500',
inactive: 'bg-white text-blue-600 border border-blue-200 hover:bg-blue-50 hover:border-blue-300 hover:text-blue-700',
},
problem: {
active: 'bg-red-500 text-white border border-red-400',
inactive: 'bg-white text-red-600 border border-red-200 hover:bg-red-50 hover:border-red-300 hover:text-red-700',
},
}
const colors = colorMap[type]
return [baseButtonClass.value, props.isLandscape ? 'text-12rpx lg:text-sm' : 'text-sm lg:text-base', 'font-medium', isActive ? colors.active : colors.inactive]
}
/**
* 切换工具支持取消激活
*/
@ -253,17 +120,15 @@ function switchTool(tool: MarkingTool) {
currentTool.value = wasDeactivated ? MarkingTool.SELECT : tool
pendingMarkType.value = null
//
if (!wasDeactivated && tool === MarkingTool.PEN) {
showPenTooltip()
}
// /
settings.value.showToolOptions = currentTool.value === 'pen' || currentTool.value === 'text'
}
/**
* 处理标记工具切换正确/错误/半对
* 处理标记工具切换
*/
function handleMarkTool(type: 'correct' | 'wrong' | 'half') {
const toolMap = {
@ -277,7 +142,7 @@ function handleMarkTool(type: 'correct' | 'wrong' | 'half') {
}
/**
* 处理图片点击用于放置标记
* 处理图片点击
*/
function handleImageClick(position: { x: number, y: number }) {
if (pendingMarkType.value) {
@ -292,46 +157,10 @@ function handleImageClick(position: { x: number, y: number }) {
emit('add-half-mark', position)
break
}
pendingMarkType.value = null
}
}
/**
* 处理撤销
*/
function handleUndo() {
if (props.canUndo) {
emit('undo')
}
}
/**
* 处理重做
*/
function handleRedo() {
if (props.canRedo) {
emit('redo')
}
}
/**
* 处理清除所有
*/
function handleClearAll() {
if (props.hasMarks) {
uni.showModal({
confirmButtonText: '确认清除',
cancelButtonText: '取消',
success: (result) => {
if (result.confirm) {
emit('clear-all')
}
},
})
}
}
/**
* 切换特殊标记
*/
@ -339,9 +168,6 @@ function toggleSpecialMark(type: 'excellent' | 'typical' | 'problem') {
emit('toggle-special-mark', type)
}
/**
* 切换收起/展开状态
*/
function toggleCollapse() {
isCollapsed.value = !isCollapsed.value
}
@ -355,135 +181,193 @@ defineExpose({
<template>
<div
ref="toolbarRef"
class="rounded-tr-base fixed bottom-0 left-0 z-10 select-none border border-white/20 shadow-xl backdrop-blur-sm transition-all duration-300"
class="fixed z-5 flex flex-col items-start transition-all duration-300 ease-out"
:class="[
{
'px-2 py-1 w-fit cursor-move lg:px-3 lg:py-2': !isLandscape && !isCollapsed,
'px-4rpx py-2rpx': isLandscape && !isCollapsed,
'bg-slate-800/95': !isCollapsed,
'backdrop-saturate-150': !isCollapsed,
},
isLandscape
? 'bottom-12px left-1/2 -translate-x-1/2 items-center'
: 'bottom-48rpx left-12rpx items-start max-w-[75vw]',
]"
@touchend.stop
@touchmove.stop
@touchstart.stop
>
<!-- 收起按钮 -->
<!-- 收起状态 (FAB) -->
<div
v-if="isCollapsed"
class="h-full w-8 flex transform cursor-pointer items-center justify-center border border-gray-200 rounded-tr-2xl bg-white shadow-inner"
:class="{
'w-64px': isLandscape,
}"
class="flex cursor-pointer items-center justify-center rounded-full bg-white shadow-lg transition-all active:scale-95"
:class="isLandscape ? 'size-48px' : 'size-80rpx'"
@click="toggleCollapse"
@mousedown.stop
>
<div class="i-carbon:chevron-right text-base text-blue-600 transition-colors lg:text-lg hover:text-blue-700" />
<div class="i-fluent:edit-24-regular text-blue-600" :class="isLandscape ? 'size-24px' : 'size-40rpx'" />
</div>
<!-- 工具栏内容 -->
<!-- 展开状态 -->
<div
v-show="!isCollapsed"
class="flex items-center" :class="{
'gap-4rpx lg:gap-2': !isLandscape,
'gap-4rpx lg:gap-1': isLandscape,
}"
v-else
class="flex items-center rounded-full bg-white shadow-xl ring-1 ring-slate-900 ring-opacity-5 transition-all"
:class="[
isLandscape ? 'px-8px py-4px gap-4px' : 'pl-12rpx pr-6rpx py-10rpx gap-4rpx',
{ 'animate-fade-in-up': !isCollapsed },
]"
>
<!-- 收起按钮展开状态时显示 -->
<button
class="border border-gray-200 bg-white text-slate-600 transition-all hover:border-gray-300 hover:bg-gray-50 hover:text-slate-700"
:class="baseButtonClass"
<!-- 收起按钮 -->
<div
class="flex flex-shrink-0 cursor-pointer items-center justify-center rounded-full text-slate-400 active:scale-95 hover:bg-slate-100"
:class="isLandscape ? 'size-32px' : 'size-60rpx'"
@click="toggleCollapse"
>
<div :class="iconClass" class="i-carbon:chevron-left" />
</button>
<div
:class="[
isLandscape ? 'i-carbon:chevron-down size-20px' : 'i-carbon:chevron-left size-36rpx',
]"
/>
</div>
<div :class="separatorClass" />
<!-- 对错半对标记 -->
<wd-tooltip content="正确标记" placement="top">
<button :class="getMarkButtonClass('correct')" @click="handleMarkTool('correct')">
<div :class="iconClass" class="i-fluent:checkmark-24-regular" />
</button>
</wd-tooltip>
<!-- 分隔线 -->
<div class="h-4/5 w-1px flex-shrink-0 bg-slate-200" />
<wd-tooltip content="错误标记" placement="top">
<button :class="getMarkButtonClass('wrong')" @click="handleMarkTool('wrong')">
<div :class="iconClass" class="i-fluent:dismiss-24-regular" />
</button>
</wd-tooltip>
<!-- 工具内容区域 (竖屏时可滚动) -->
<scroll-view
:scroll-x="true"
:show-scrollbar="false"
class="flex whitespace-nowrap"
:class="isLandscape ? 'w-auto' : 'max-w-[65vw]'"
>
<div class="flex items-center" :class="isLandscape ? 'gap-6px' : 'gap-16rpx'">
<!-- 标记组 -->
<div class="flex items-center rounded-full bg-slate-50" :class="isLandscape ? 'p-4px gap-4px' : 'p-6rpx gap-8rpx'">
<wd-tooltip :content="isLandscape ? '正确' : ''" placement="top">
<div
class="flex cursor-pointer items-center justify-center rounded-full transition-all active:scale-95"
:class="[
currentTool === MarkingTool.CORRECT ? 'bg-green-500 text-white shadow-md' : 'text-slate-500 hover:bg-white hover:text-green-600',
isLandscape ? 'size-32px' : 'size-64rpx',
]"
@click="handleMarkTool('correct')"
>
<div class="i-fluent:checkmark-24-regular" :class="isLandscape ? 'size-20px' : 'size-40rpx'" />
</div>
</wd-tooltip>
<wd-tooltip content="半对标记" placement="top">
<button :class="getMarkButtonClass('half')" @click="handleMarkTool('half')">
<view
class="h-48rpx w-48rpx" :class="iconClass"
:style="{
backgroundImage: `url('${halfMarkSvg}')`,
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
}"
/>
</button>
</wd-tooltip>
<wd-tooltip :content="isLandscape ? '错误' : ''" placement="top">
<div
class="flex cursor-pointer items-center justify-center rounded-full transition-all active:scale-95"
:class="[
currentTool === MarkingTool.WRONG ? 'bg-red-500 text-white shadow-md' : 'text-slate-500 hover:bg-white hover:text-red-600',
isLandscape ? 'size-32px' : 'size-64rpx',
]"
@click="handleMarkTool('wrong')"
>
<div class="i-fluent:dismiss-24-regular" :class="isLandscape ? 'size-20px' : 'size-40rpx'" />
</div>
</wd-tooltip>
<div :class="separatorClass" />
<!-- 橡皮擦 -->
<wd-tooltip content="擦除" placement="top">
<button
:class="getToolButtonClass(MarkingTool.ERASER)"
@click="switchTool(MarkingTool.ERASER)"
>
<div :class="iconClass" class="i-fluent:eraser-24-regular" />
</button>
</wd-tooltip>
<wd-tooltip :content="isLandscape ? '半对' : ''" placement="top">
<div
class="flex cursor-pointer items-center justify-center rounded-full transition-all active:scale-95"
:class="[
currentTool === MarkingTool.HALF ? 'bg-yellow-500 text-white shadow-md' : 'text-slate-500 hover:bg-white hover:text-yellow-600',
isLandscape ? 'size-32px' : 'size-64rpx',
]"
@click="handleMarkTool('half')"
>
<view
:class="isLandscape ? 'size-20px' : 'size-40rpx'"
:style="{
backgroundImage: `url('${halfMarkSvg}')`,
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
}"
/>
</div>
</wd-tooltip>
</div>
<!-- 文字工具 -->
<wd-tooltip content="添加文字" placement="top">
<button :class="getToolButtonClass(MarkingTool.TEXT)" @click="switchTool(MarkingTool.TEXT)">
<div :class="iconClass" class="i-fluent:text-24-regular" />
</button>
</wd-tooltip>
<!-- 分隔线 -->
<div class="h-20px w-1px flex-shrink-0 bg-slate-200" />
<!-- 任意笔画 -->
<wd-tooltip content="自由绘制" placement="top">
<button :class="getToolButtonClass(MarkingTool.PEN)" @click="switchTool(MarkingTool.PEN)">
<div :class="iconClass" class="i-fluent:pen-24-regular" />
</button>
</wd-tooltip>
<!-- 绘图组 -->
<div class="flex items-center rounded-full bg-slate-50" :class="isLandscape ? 'p-4px gap-4px' : 'p-6rpx gap-8rpx'">
<wd-tooltip :content="isLandscape ? '画笔' : ''" placement="top">
<div
class="flex cursor-pointer items-center justify-center rounded-full transition-all active:scale-95"
:class="[
currentTool === MarkingTool.PEN ? 'bg-blue-600 text-white shadow-md' : 'text-slate-500 hover:bg-white hover:text-blue-600',
isLandscape ? 'size-32px' : 'size-64rpx',
]"
@click="switchTool(MarkingTool.PEN)"
>
<div class="i-fluent:pen-24-regular" :class="isLandscape ? 'size-20px' : 'size-40rpx'" />
</div>
</wd-tooltip>
<div :class="separatorClass" />
<wd-tooltip :content="isLandscape ? '文字' : ''" placement="top">
<div
class="flex cursor-pointer items-center justify-center rounded-full transition-all active:scale-95"
:class="[
currentTool === MarkingTool.TEXT ? 'bg-blue-600 text-white shadow-md' : 'text-slate-500 hover:bg-white hover:text-blue-600',
isLandscape ? 'size-32px' : 'size-64rpx',
]"
@click="switchTool(MarkingTool.TEXT)"
>
<div class="i-fluent:text-24-regular" :class="isLandscape ? 'size-20px' : 'size-40rpx'" />
</div>
</wd-tooltip>
<!-- 撤回 -->
<!-- <wd-tooltip content="撤回一步" placement="top">
<button :class="getActionButtonClass('undo')" :disabled="!canUndo" @click="handleUndo">
<div class="i-fluent:arrow-undo-24-regular h-4 w-4" />
</button>
</wd-tooltip> -->
<wd-tooltip :content="isLandscape ? '擦除' : ''" placement="top">
<div
class="flex cursor-pointer items-center justify-center rounded-full transition-all active:scale-95"
:class="[
currentTool === MarkingTool.ERASER ? 'bg-slate-600 text-white shadow-md' : 'text-slate-500 hover:bg-white hover:text-slate-700',
isLandscape ? 'size-32px' : 'size-64rpx',
]"
@click="switchTool(MarkingTool.ERASER)"
>
<div class="i-fluent:eraser-24-regular" :class="isLandscape ? 'size-20px' : 'size-40rpx'" />
</div>
</wd-tooltip>
</div>
<!-- 特殊标记 -->
<wd-tooltip content="标记为优秀卷" placement="top">
<button
:class="getSpecialButtonClass('excellent')"
@click="toggleSpecialMark('excellent')"
>
优秀
</button>
</wd-tooltip>
<!-- 分隔线 -->
<div class="h-20px w-1px flex-shrink-0 bg-slate-200" />
<wd-tooltip content="标记为典例卷" placement="top">
<button
:class="getSpecialButtonClass('typical')"
@click="toggleSpecialMark('typical')"
>
典例
</button>
</wd-tooltip>
<wd-tooltip :content="problemTooltipContent" placement="top">
<button
:class="getSpecialButtonClass('problem')"
@click="toggleSpecialMark('problem')"
>
问题
</button>
</wd-tooltip>
<!-- 特殊标记组 -->
<div class="flex items-center" :class="isLandscape ? 'gap-6px' : 'gap-12rpx'">
<div
v-for="type in ['excellent', 'typical', 'problem']"
:key="type"
class="flex flex-shrink-0 cursor-pointer items-center justify-center rounded-full font-medium transition-all active:scale-95"
:class="[
specialMarks[type as keyof typeof specialMarks]
? (type === 'excellent' ? 'bg-yellow-100 text-yellow-700' : type === 'typical' ? 'bg-blue-100 text-blue-700' : 'bg-red-100 text-red-700')
: 'bg-slate-50 text-slate-500 hover:bg-slate-100',
isLandscape ? 'h-32px px-12px text-12px' : 'h-64rpx px-20rpx text-24rpx',
]"
@click="toggleSpecialMark(type as any)"
>
{{ type === 'excellent' ? '优秀' : type === 'typical' ? '典例' : '问题' }}
</div>
</div>
</div>
</scroll-view>
</div>
</div>
</template>
<style scoped>
.animate-fade-in-up {
animation: fadeInUp 0.2s ease-out;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>

View File

@ -52,7 +52,8 @@ function createMarkingData(options: UseMarkingDataOptions) {
const currentScore = computed({
get: () => {
return currentMarkingSubmitData.value[firstNotScoredIndex.value]?.score || -1
const score = currentMarkingSubmitData.value[firstNotScoredIndex.value]?.score
return score !== undefined ? score : -1
},
set: (value) => {
if (currentMarkingSubmitData.value[firstNotScoredIndex.value]) {

View File

@ -88,7 +88,7 @@ const defaultSettings: MarkingSettings = {
// 快捷打分默认值
quickScoreMode: 'onekey',
quickScoreScores: [1, 2],
quickScoreScores: [0.5, 1, 2, 3],
quickScoreLayout: 'single',
// 一键打分默认值

View File

@ -104,6 +104,8 @@ export interface MarkingContext {
defaultPosition?: 'first' | 'last' // 默认定位first-第一个last-最后一个
}
export const cachedImages = ref<string[]>([])
/**
* 使API
*
@ -115,8 +117,6 @@ export class DefaultMarkingDataProvider implements MarkingDataProvider {
private hasSubmitted = false
private fetchingPromise: Promise<ExamStudentMarkingQuestionsResponse | null> | null = null
cachedImages = ref<string[]>([])
async getSingleQuestion(taskId: number): Promise<ExamStudentMarkingQuestionResponse | null> {
// 如果任务ID变化清空缓存
if (this.lastTaskId !== taskId) {
@ -176,7 +176,8 @@ export class DefaultMarkingDataProvider implements MarkingDataProvider {
// 预加载图片资源
if (this.cachedQuestion.questions?.[0]?.image_urls) {
this.cachedImages.value = this.cachedQuestion.questions?.[0]?.image_urls || []
cachedImages.value = this.cachedQuestion.questions?.[0]?.image_urls || []
console.log('cachedImages', cachedImages.value)
}
}
else {

View File

@ -231,7 +231,7 @@ function handleExamChange() {
uni.showActionSheet({
itemList: examNames,
success: (res) => {
const selectedOption = homeStore.examOptions[res.tapIndex].filter(item => item.)
const selectedOption = homeStore.examOptions[res.tapIndex]
homeStore.selectedExamId = selectedOption.value as number
//
fetchExamStats()

View File

@ -19,7 +19,7 @@ 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, useMarkingContext } from '@/composables/marking/MarkingContext'
import { cachedImages, DefaultMarkingDataProvider, DefaultMarkingHistoryProvider, provideMarkingContext, useMarkingContext } from '@/composables/marking/MarkingContext'
import { provideMarkingHistory } from '@/composables/marking/useMarkingHistory'
import { provideMarkingNavigation } from '@/composables/marking/useMarkingNavigation'
import { useSafeArea } from '@/composables/useSafeArea'
@ -473,7 +473,7 @@ function handleGlobalTouchEnd(e: TouchEvent) {
//
const nextQuestionImages = computed(() => {
return markingDataProvider.cachedImages.value
return cachedImages.value
})
</script>
@ -481,9 +481,6 @@ const nextQuestionImages = computed(() => {
<div
class="relative h-screen w-100vw flex flex-col touch-pan-x touch-pan-y overflow-hidden overscroll-none"
:style="{ paddingTop: `${(safeAreaInsets?.top || 0)}px` }"
@touchstart.capture="handleGlobalTouchStart"
@touchmove.capture="handleGlobalTouchMove"
@touchend.capture="handleGlobalTouchEnd"
>
<!-- 缩放提示 -->
<view
@ -496,6 +493,7 @@ const nextQuestionImages = computed(() => {
>
{{ Math.round(imageScale * 100) }}%
</view>
<MarkingLayout
:is-landscape="isLandscape"
:is-fullscreen="false"
@ -533,6 +531,9 @@ const nextQuestionImages = computed(() => {
:next-question-images="nextQuestionImages"
@ready="handleImageReady"
@scroll-edge="handleScrollEdge"
@touch-start="handleGlobalTouchStart"
@touch-move="handleGlobalTouchMove"
@touch-end="handleGlobalTouchEnd"
/>
</div>
</template>