修复错题一直显示处理中:超时、自动刷新与状态更新。
This commit is contained in:
+33
-33
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 '正在识别、标注并生成解题思路…'
|
||||
}
|
||||
Reference in New Issue
Block a user