/* 匯入 & AI 學習分析畫面 — screens2.jsx */
const { useState: useState2, useMemo: useMemo2 } = React;
const {
Icon: Icon2, Card: Card2, StatCard: StatCard2, TypeBadge: TypeBadge2,
ScoreChip: ScoreChip2, RiskLight: RiskLight2, Button: Button2,
SectionTitle: SectionTitle2, TrendLine: TrendLine2
} = window;
const D2 = window.APPDATA;
const fmt2 = window.fmtDate;
/* ============================================================
匯入成績(老師)— 三步驟流程
============================================================ */
function ImportScreen({ commitImport }) {
const [step, setStep] = useState2(1);
const [source, setSource] = useState2(null); // "excel" | "sheet"
const [done, setDone] = useState2(false);
const rows = D2.importSampleRows;
const okRows = rows.filter(r => r.status !== "error");
const errRows = rows.filter(r => r.status === "error");
const steps = ["上傳來源", "預覽與對應", "確認匯入"];
function reset() { setStep(1); setSource(null); setDone(false); }
return (
匯入成績
支援 Excel 檔上傳,或連結 Google 試算表批次匯入。
{/* 步驟列 */}
{steps.map((s, i) => {
const n = i + 1;
const state = done ? "done" : n < step ? "done" : n === step ? "active" : "idle";
return (
{state === "done" ? : n}
{s}
{i < steps.length - 1 && }
);
})}
{done ? (
匯入完成
已成功匯入 {okRows.length} 筆成績{errRows.length ? <>,略過 {errRows.length} 筆有問題的資料。> : "。"}
再匯入一批
完成
) : (
<>
{/* Step 1 — 上傳來源 */}
{step === 1 && (
選擇匯入來源
{source === "excel" && (
將 Excel 檔拖曳到這裡,或點擊選擇
已選擇範例檔:七年三班_第5次小考.xlsx(8 列)
)}
{source === "sheet" && (
)}
setStep(2)}>下一步:預覽
)}
{/* Step 2 — 預覽與對應 */}
{step === 2 && (
{okRows.length} 筆可匯入
{errRows.length > 0 && {errRows.length} 筆需檢查}
}>預覽資料・欄位對應
{[["座號", "seat"], ["姓名", "name"], ["評量類型", "type"], ["評量項目", "title"], ["日期", "date"], ["得分", "score"]].map(([cn]) => (
{cn}
))}
{rows.map((r, i) => (
{String(r.seat).padStart(2, "0")}
{r.name}
{r.title}
{fmt2(r.date)}
{r.score == null
? 缺漏
: }
))}
{errRows.length > 0 && (
有 {errRows.length} 列得分缺漏,匯入時將自動略過。請確認後再繼續。
)}
setStep(1)}>上一步
setStep(3)}>下一步:確認
)}
{/* Step 3 — 確認 */}
{step === 3 && (
確認匯入
來源
{source === "excel" ? "Excel 檔・七年三班_第5次小考.xlsx" : "Google 試算表"}
評量項目
第 5 次小考・統計圖表
可匯入筆數
{okRows.length} 筆
略過筆數
{errRows.length} 筆(缺漏)
匯入後,這些成績會立即出現在「全班成績」與學生的「我的成績」中。
setStep(2)}>上一步
{ commitImport && commitImport(okRows); setDone(true); }}>
確認匯入 {okRows.length} 筆
)}
>
)}
);
}
/* ============================================================
AI 學習分析(學生 / 小老師)
============================================================ */
function AnalysisScreen({ studentId, grades }) {
const student = D2.students.find(s => s.id === studentId);
const myGrades = useMemo2(
() => grades.filter(g => g.studentId === studentId).slice().sort((a, b) => a.date < b.date ? -1 : 1),
[grades, studentId]
);
const risk = useMemo2(() => D2.riskFor(studentId, grades), [grades, studentId]);
const trend = myGrades.map(g => g.score);
// 各類型平均
const byType = useMemo2(() => {
return D2.assessmentTypes.map(t => {
const sc = myGrades.filter(g => g.type === t).map(g => g.score);
return { type: t, avg: sc.length ? Math.round(sc.reduce((a, b) => a + b, 0) / sc.length) : null };
}).filter(x => x.avg != null);
}, [myGrades]);
const weakest = byType.slice().sort((a, b) => a.avg - b.avg)[0];
if (!risk) return