Spaces:
Running
Running
Update components/ai/CommentGeneratorPanel.tsx
Browse files
components/ai/CommentGeneratorPanel.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
|
| 2 |
import React, { useState, useEffect, useRef } from 'react';
|
| 3 |
import { User, StudentProfile, SavedRecord, Zodiac, Personality, Hobby } from '../../types';
|
|
|
|
| 4 |
import { FileSpreadsheet, Loader2, Sparkles, Copy, Save, Trash2, Download, RefreshCw, PenTool, Edit, Box, ChevronLeft, Settings2, Check, X } from 'lucide-react';
|
| 5 |
import { Toast, ToastState } from '../Toast';
|
| 6 |
import { ConfirmModal } from '../ConfirmModal';
|
|
@@ -67,6 +68,30 @@ export const CommentGeneratorPanel: React.FC<CommentGeneratorPanelProps> = ({ cu
|
|
| 67 |
}
|
| 68 |
}, [savedRecords, currentUser]);
|
| 69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
// --- Excel Import Logic ---
|
| 71 |
const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
| 72 |
const file = e.target.files?.[0];
|
|
@@ -396,39 +421,39 @@ export const CommentGeneratorPanel: React.FC<CommentGeneratorPanelProps> = ({ cu
|
|
| 396 |
{/* 1. Header (Name Input) */}
|
| 397 |
<div className="p-4 border-b border-gray-100 bg-gray-50/50 shrink-0">
|
| 398 |
<label className="text-xs font-bold text-gray-500 uppercase block mb-2">当前学生</label>
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
<
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
{
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
<input
|
| 417 |
className="flex-1 border border-gray-300 rounded-xl p-3 text-sm focus:ring-2 focus:ring-amber-400 outline-none shadow-sm"
|
| 418 |
placeholder="输入姓名..."
|
| 419 |
value={profile.name}
|
| 420 |
onChange={e => setProfile({...profile, name: e.target.value})}
|
| 421 |
/>
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
</
|
| 429 |
-
|
| 430 |
-
</
|
| 431 |
-
|
| 432 |
{isImporting && <div className="text-xs text-gray-400 mt-2 flex items-center gap-1"><Loader2 size={10} className="animate-spin"/> 正在解析名单...</div>}
|
| 433 |
</div>
|
| 434 |
|
|
|
|
| 1 |
|
| 2 |
import React, { useState, useEffect, useRef } from 'react';
|
| 3 |
import { User, StudentProfile, SavedRecord, Zodiac, Personality, Hobby } from '../../types';
|
| 4 |
+
import { api } from '../../services/api';
|
| 5 |
import { FileSpreadsheet, Loader2, Sparkles, Copy, Save, Trash2, Download, RefreshCw, PenTool, Edit, Box, ChevronLeft, Settings2, Check, X } from 'lucide-react';
|
| 6 |
import { Toast, ToastState } from '../Toast';
|
| 7 |
import { ConfirmModal } from '../ConfirmModal';
|
|
|
|
| 68 |
}
|
| 69 |
}, [savedRecords, currentUser]);
|
| 70 |
|
| 71 |
+
// Auto-load class students
|
| 72 |
+
useEffect(() => {
|
| 73 |
+
const fetchClassStudents = async () => {
|
| 74 |
+
// If list is empty and user has a class, try to load from API
|
| 75 |
+
if (importedNames.length === 0 && currentUser?.homeroomClass) {
|
| 76 |
+
try {
|
| 77 |
+
const allStudents = await api.students.getAll();
|
| 78 |
+
const classStudents = allStudents
|
| 79 |
+
.filter((s: any) => s.className === currentUser.homeroomClass)
|
| 80 |
+
.map((s: any) => s.name);
|
| 81 |
+
|
| 82 |
+
if (classStudents.length > 0) {
|
| 83 |
+
setImportedNames(classStudents);
|
| 84 |
+
setProfile(prev => ({ ...prev, name: classStudents[0] }));
|
| 85 |
+
setToast({ show: true, message: `已加载 ${currentUser.homeroomClass} 学生名单`, type: 'success' });
|
| 86 |
+
}
|
| 87 |
+
} catch (e) {
|
| 88 |
+
console.error("Failed to load students", e);
|
| 89 |
+
}
|
| 90 |
+
}
|
| 91 |
+
};
|
| 92 |
+
fetchClassStudents();
|
| 93 |
+
}, [currentUser]);
|
| 94 |
+
|
| 95 |
// --- Excel Import Logic ---
|
| 96 |
const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
| 97 |
const file = e.target.files?.[0];
|
|
|
|
| 421 |
{/* 1. Header (Name Input) */}
|
| 422 |
<div className="p-4 border-b border-gray-100 bg-gray-50/50 shrink-0">
|
| 423 |
<label className="text-xs font-bold text-gray-500 uppercase block mb-2">当前学生</label>
|
| 424 |
+
<div className="flex gap-2">
|
| 425 |
+
{importedNames.length > 0 ? (
|
| 426 |
+
<div className="relative flex-1">
|
| 427 |
+
<select
|
| 428 |
+
className="w-full border border-amber-300 rounded-xl p-3 bg-white text-base font-bold text-gray-800 shadow-sm focus:ring-2 focus:ring-amber-400 outline-none appearance-none"
|
| 429 |
+
value={profile.name}
|
| 430 |
+
onChange={e => setProfile({...profile, name: e.target.value})}
|
| 431 |
+
>
|
| 432 |
+
{importedNames.map(name => (
|
| 433 |
+
<option key={name} value={name}>
|
| 434 |
+
{isStudentDone(name) ? '✅' : '⬜'} {name}
|
| 435 |
+
</option>
|
| 436 |
+
))}
|
| 437 |
+
</select>
|
| 438 |
+
<div className="absolute right-3 top-3.5 text-gray-400 pointer-events-none text-xs">▼</div>
|
| 439 |
+
</div>
|
| 440 |
+
) : (
|
| 441 |
<input
|
| 442 |
className="flex-1 border border-gray-300 rounded-xl p-3 text-sm focus:ring-2 focus:ring-amber-400 outline-none shadow-sm"
|
| 443 |
placeholder="输入姓名..."
|
| 444 |
value={profile.name}
|
| 445 |
onChange={e => setProfile({...profile, name: e.target.value})}
|
| 446 |
/>
|
| 447 |
+
)}
|
| 448 |
+
<button
|
| 449 |
+
onClick={() => fileInputRef.current?.click()}
|
| 450 |
+
className="px-3 bg-white text-green-600 rounded-xl border border-green-200 hover:bg-green-50 flex items-center justify-center shadow-sm shrink-0"
|
| 451 |
+
title="Excel 导入 / 覆盖名单"
|
| 452 |
+
>
|
| 453 |
+
<FileSpreadsheet size={20}/>
|
| 454 |
+
</button>
|
| 455 |
+
<input type="file" accept=".xlsx,.xls,.csv" ref={fileInputRef} className="hidden" onChange={handleFileUpload} />
|
| 456 |
+
</div>
|
| 457 |
{isImporting && <div className="text-xs text-gray-400 mt-2 flex items-center gap-1"><Loader2 size={10} className="animate-spin"/> 正在解析名单...</div>}
|
| 458 |
</div>
|
| 459 |
|