- {progressCards.map((progress) => (
-
-
-
-
-
-
-
- {progress.status === 'student-failed' ? '生成失败' : '生成中'}
-
-
-
-
- {progress.studentName || '学生'} 正在生成报告
-
-
- {progress.className} · {progress.current}/{progress.total}
-
-
-
-
-
- {progress.error ? (
- {progress.error}
- ) : null}
-
-
- ))}
-
{filteredReports.map((report) => {
const ReportIcon = getReportIcon(report.format)
@@ -555,6 +646,57 @@ export function ReportPage(): React.JSX.Element {
+
+
+
+ 生成班级报告
+
+ 选择一个班级,系统会使用当前选中的 PPTX 模板为该班级幼儿生成报告。
+
+
+
+
+
+
+
+
+ 当前模板:
+ {templates.find((template) => template.id === selectedTemplateId)?.name ||
+ '未选择 PPTX 模板'}
+
+
+
+
+
+
+
+
+
>
)
diff --git a/src/renderer/src/pages/StudentPage.tsx b/src/renderer/src/pages/StudentPage.tsx
index 1dc6fd4..2ba5588 100644
--- a/src/renderer/src/pages/StudentPage.tsx
+++ b/src/renderer/src/pages/StudentPage.tsx
@@ -75,6 +75,7 @@ import {
} from '@renderer/components/ui/table'
import { getClassTypeLabel } from '@renderer/student/classes'
import type { ChildProfile, ClassProfile } from '@renderer/types/app'
+import type { CommentGenerationProgress } from '@renderer/types/app'
function formatList(items: string[]): string {
return items.length > 0 ? items.join('、') : '未填写'
@@ -120,6 +121,7 @@ export function StudentPage(): React.JSX.Element {
const [selectedId, setSelectedId] = useState
('')
const [selectedProfileIds, setSelectedProfileIds] = useState([])
const [generatingIds, setGeneratingIds] = useState([])
+ const [commentProgress, setCommentProgress] = useState(null)
const [editingProfile, setEditingProfile] = useState(null)
const [query, setQuery] = useState('')
const [selectedClassFilter, setSelectedClassFilter] = useState('all')
@@ -167,6 +169,33 @@ export function StudentPage(): React.JSX.Element {
loadPagedStudentProfiles()
}, [page, pageSize, query, selectedClassFilter, studentDataReady])
+ useEffect(() => {
+ return window.api.onCommentGenerationProgress((progress) => {
+ setCommentProgress(progress.status === 'finished' ? null : progress)
+
+ if (progress.status === 'student-started' && progress.studentId) {
+ setGeneratingIds((currentIds) =>
+ currentIds.includes(progress.studentId!)
+ ? currentIds
+ : [...currentIds, progress.studentId!]
+ )
+ }
+
+ if (progress.status === 'student-finished' && progress.profile) {
+ updateProfileInPage(progress.profile)
+ }
+
+ if (
+ (progress.status === 'student-finished' || progress.status === 'student-failed') &&
+ progress.studentId
+ ) {
+ setGeneratingIds((currentIds) =>
+ currentIds.filter((profileId) => profileId !== progress.studentId)
+ )
+ }
+ })
+ }, [])
+
const classOptions = useMemo(() => {
const classMap = new Map()
@@ -282,28 +311,26 @@ export function StudentPage(): React.JSX.Element {
}
async function handleBatchGenerateComments(): Promise {
- const selectedProfiles = profiles.filter((profile) => selectedProfileIds.includes(profile.id))
-
- if (selectedProfiles.length === 0) {
+ if (selectedProfileIds.length === 0) {
toast.error('请先选择要生成评语的学生')
return
}
- let successCount = 0
- let failedCount = 0
+ const response = await window.api.generateStudentComments({
+ profileIds: selectedProfileIds
+ })
- for (const profile of selectedProfiles) {
- const ok = await handleGenerateComment(profile)
+ if (!response.ok) {
+ toast.error('批量生成评语失败', { description: response.message })
+ return
+ }
- if (ok) {
- successCount += 1
- } else {
- failedCount += 1
- }
+ for (const profile of response.profiles) {
+ updateProfileInPage(profile)
}
toast.success('批量生成完成', {
- description: `成功 ${successCount} 个,失败 ${failedCount} 个`
+ description: `成功 ${response.profiles.length} 个,失败 ${response.skipped.length} 个`
})
}
@@ -312,9 +339,10 @@ export function StudentPage(): React.JSX.Element {
}
function handleGenerateReport(profile: ChildProfile): void {
- navigate('/tools/reports', {
+ navigate('/student/reports', {
state: {
- profileId: profile.id
+ profileId: profile.id,
+ autoGenerate: true
}
})
}
@@ -324,7 +352,7 @@ export function StudentPage(): React.JSX.Element {
return
}
- navigate('/tools/reports', {
+ navigate('/student/reports', {
state: {
profileId: profile.id,
mode: 'view'
@@ -425,7 +453,7 @@ export function StudentPage(): React.JSX.Element {
+ {commentProgress ? (
+