修复错题一直显示处理中:超时、自动刷新与状态更新。

This commit is contained in:
dekun
2026-06-28 14:09:10 +08:00
parent c42cd0b46d
commit 6200dbb596
7 changed files with 163 additions and 46 deletions
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -9,7 +9,7 @@
<meta name="author" content="马建军" />
<meta name="copyright" content="Copyright (c) 马建军. All rights reserved." />
<title>中学成绩档案</title>
<script type="module" crossorigin src="/assets/index-C3DkjkK0.js"></script>
<script type="module" crossorigin src="/assets/index-_1CtLpiP.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-GY2etMYN.css">
</head>
<body>
+15 -3
View File
@@ -1,7 +1,9 @@
import { DeleteOutlined } from '@ant-design/icons'
import { Button, Popconfirm, Space, Tag, Typography, message } from 'antd'
import { useEffect } from 'react'
import { wrongQuestionApi } from '../api/client'
import type { WrongQuestion } from '../types'
import { isWrongQuestionProcessing, processingHint } from '../utils/wqProcessing'
import { STATUS_LABELS } from '../types'
import AuthenticatedImage from './AuthenticatedImage'
import WrongQuestionDetail from '../pages/WrongQuestionDetail'
@@ -12,14 +14,15 @@ interface Props {
onSelect: (id: string | null) => void
onRefresh: () => void
emptyText?: string
pollWhenProcessing?: boolean
}
function cardSummary(wq: WrongQuestion): { tone: 'error' | 'pending' | 'normal'; text: string } {
if (wq.error_message) {
return { tone: 'error', text: wq.error_message }
}
if (wq.status === 'pending') {
return { tone: 'pending', text: '正在识别、标注并生成解题思路…' }
if (isWrongQuestionProcessing(wq)) {
return { tone: 'pending', text: processingHint(wq) }
}
return {
tone: 'normal',
@@ -33,7 +36,16 @@ export default function WrongQuestionList({
onSelect,
onRefresh,
emptyText = '暂无记录',
pollWhenProcessing = true,
}: Props) {
const hasProcessing = pollWhenProcessing && items.some(isWrongQuestionProcessing)
useEffect(() => {
if (!hasProcessing) return
const timer = window.setInterval(() => onRefresh(), 4000)
return () => window.clearInterval(timer)
}, [hasProcessing, onRefresh])
const handleDelete = async (id: string) => {
try {
await wrongQuestionApi.remove(id)
@@ -58,7 +70,7 @@ export default function WrongQuestionList({
<Space size={4} wrap>
<Typography.Text strong>{wq.subject_name}</Typography.Text>
{wq.category === 'olympiad' && <Tag color="gold"></Tag>}
<Tag color={wq.error_message || wq.status === 'failed' ? 'error' : wq.status === 'pending' ? 'processing' : 'default'}>
<Tag color={wq.error_message || wq.status === 'failed' ? 'error' : isWrongQuestionProcessing(wq) ? 'processing' : 'default'}>
{wq.error_message ? '失败' : STATUS_LABELS[wq.status]}
</Tag>
</Space>
+23 -1
View File
@@ -4,6 +4,7 @@ import ReactMarkdown from 'react-markdown'
import AuthenticatedImage from '../components/AuthenticatedImage'
import { wrongQuestionApi } from '../api/client'
import type { WrongQuestion } from '../types'
import { isWrongQuestionProcessing, processingHint } from '../utils/wqProcessing'
import { STATUS_LABELS } from '../types'
interface Props {
@@ -49,6 +50,24 @@ export default function WrongQuestionDetail({
if (open && questionId) load()
}, [open, questionId])
useEffect(() => {
if (!open || !wq || !isWrongQuestionProcessing(wq)) return
const timer = window.setInterval(async () => {
try {
const { data } = await wrongQuestionApi.get(questionId)
setWq(data)
setQuestionText(data.question_text || '')
setApproachText(data.solution_approach || '')
setSolutionText(data.solution_text || '')
if (data.has_annotated_image) setImageMode('annotated')
if (!isWrongQuestionProcessing(data)) onUpdated()
} catch {
/* ignore poll errors */
}
}, 4000)
return () => window.clearInterval(timer)
}, [open, questionId, wq?.status, wq?.question_text, wq?.error_message])
const handleSave = async () => {
setSaving(true)
try {
@@ -149,7 +168,10 @@ export default function WrongQuestionDetail({
/>
)}
{wq.status === 'pending' && !wq.error_message && (
<Alert message="正在识别、标注并生成解题思路,请稍候…" type="info" showIcon style={{ marginBottom: 12 }} />
<Alert message={processingHint(wq)} type="info" showIcon style={{ marginBottom: 12 }} />
)}
{wq.status === 'ocr_done' && !wq.question_text && !wq.error_message && (
<Alert message={processingHint(wq)} type="info" showIcon style={{ marginBottom: 12 }} />
)}
{(wq.solution_approach || wq.solution_text) && (
<Alert
+18
View File
@@ -0,0 +1,18 @@
import type { WrongQuestion } from '../types'
export function isWrongQuestionProcessing(wq: WrongQuestion): boolean {
if (wq.error_message) return false
if (wq.status === 'pending') return true
if (wq.status === 'ocr_done' && !wq.question_text) return true
return false
}
export function processingHint(wq: WrongQuestion): string {
if (wq.status === 'pending') {
return '正在 OCR 识别(首次约 1–5 分钟,请稍候)…'
}
if (wq.status === 'ocr_done') {
return '正在标注错题并生成解题思路…'
}
return '正在识别、标注并生成解题思路…'
}