+
+
+ {draft.figure && (
+
+ )}
+
+ {draft.figure ? (
+ <>
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+

+ {figError &&
{figError}
}
+ >
+ ) : (
+
+
{
+ uploadFigure(e.target.files?.[0]);
+ e.target.value = "";
+ }}
+ />
+
+ {figError &&
{figError}
}
+
+ )}
+
+
{draft.type === "choice" && (
diff --git a/src/lib/question-validation.test.ts b/src/lib/question-validation.test.ts
index 4bb0370..b88cf59 100644
--- a/src/lib/question-validation.test.ts
+++ b/src/lib/question-validation.test.ts
@@ -33,6 +33,11 @@ describe("validateQuestions 基础", () => {
validateQuestions(JSON.stringify([{ type: "fill", stem: "f?", explain: "e" }])).error,
).toContain("缺少参考答案");
});
+ it("figure 合法/非法文件名", () => {
+ expect(validateQuestions(JSON.stringify([{ ...base, figure: "tri.png" }])).error).toBeUndefined();
+ expect(validateQuestions(JSON.stringify([{ ...base, figure: "a b.png" }])).error).toContain("配图文件名不合法");
+ expect(validateQuestions(JSON.stringify([{ ...base, figure: " " }])).error).toContain("配图文件名不合法");
+ });
});
describe("validateQuestions solve / judgeMode", () => {
diff --git a/src/lib/question-validation.ts b/src/lib/question-validation.ts
index 0b657d2..f8bbfef 100644
--- a/src/lib/question-validation.ts
+++ b/src/lib/question-validation.ts
@@ -21,6 +21,9 @@ export function validateQuestions(raw: string): ValidationResult {
if (typeof q.explain !== "string" || !q.explain.trim()) return { error: `${no}缺少解析(explain)` };
const t = q.type as (typeof EXAM_TYPES)[number];
if (!EXAM_TYPES.includes(t)) return { error: `${no}的 type 非法:${String(q.type)}` };
+ if (typeof q.figure === "string" && (q.figure.trim() === "" || !/^[\w.-]+$/.test(q.figure))) {
+ return { error: `${no}配图文件名不合法(figure)` };
+ }
if ((t === "choice" || t === "judge") && typeof q.answer !== "number") return { error: `${no}(${t})缺少答案(answer 数字)` };
if (t === "choice" && (!Array.isArray(q.options) || q.options.length === 0)) return { error: `${no}(choice)缺少选项(options)` };
if (t === "choice" && (typeof q.answer !== "number" || q.answer < 0 || q.answer >= (q.options as unknown[]).length)) return { error: `${no}(choice)答案下标越界` };