fix(testcase): fix testcase add data failure due to index did not refresh index after save testcase

- 为测试用例添加唯一的临时 ID,避免重复
- 新增测试用例时检查输入和输出不能为空
- 保存测试用例时显示具体的成功和失败信息
- 保存完成后刷新最新测试用例数据
- 优化代码结构,提高可读性和可维护性
This commit is contained in:
fly6516 2025-06-20 17:31:33 +08:00
parent 5e89d67190
commit 0247053160

View File

@ -24,7 +24,7 @@ export default function EditTestcasePanel({ problemId }: { problemId: string })
const [testcases, setTestcases] = useState<Testcase[]>([]); const [testcases, setTestcases] = useState<Testcase[]>([]);
const [isGenerating, setIsGenerating] = useState(false); const [isGenerating, setIsGenerating] = useState(false);
// 1) Load existing testcases // 加载测试用例
useEffect(() => { useEffect(() => {
async function fetch() { async function fetch() {
try { try {
@ -38,21 +38,21 @@ export default function EditTestcasePanel({ problemId }: { problemId: string })
fetch(); fetch();
}, [problemId]); }, [problemId]);
// 2) Local add // 本地添加测试用例
const handleAddTestcase = () => const handleAddTestcase = () =>
setTestcases((prev) => [ setTestcases((prev) => [
...prev, ...prev,
{ id: `new-${Date.now()}`, expectedOutput: "", inputs: [{ name: "input1", value: "" }] }, { id: `new-${Date.now()}-${Math.random()}`, expectedOutput: "", inputs: [{ name: "input1", value: "" }] },
]); ]);
// 3) AI generation // AI 生成测试用例
const handleAITestcase = async () => { const handleAITestcase = async () => {
setIsGenerating(true); setIsGenerating(true);
try { try {
const ai = await generateAITestcase({ problemId }); const ai = await generateAITestcase({ problemId });
setTestcases((prev) => [ setTestcases((prev) => [
...prev, ...prev,
{ id: `new-${Date.now()}`, expectedOutput: ai.expectedOutput, inputs: ai.inputs }, { id: `new-${Date.now()}-${Math.random()}`, expectedOutput: ai.expectedOutput, inputs: ai.inputs },
]); ]);
window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" }); window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" });
} catch (err) { } catch (err) {
@ -63,7 +63,7 @@ export default function EditTestcasePanel({ problemId }: { problemId: string })
} }
}; };
// 4) Remove (local + remote if existing) // 删除测试用例(本地 + 服务器)
const handleRemoveTestcase = async (idx: number) => { const handleRemoveTestcase = async (idx: number) => {
const tc = testcases[idx]; const tc = testcases[idx];
if (!tc.id.startsWith("new-")) { if (!tc.id.startsWith("new-")) {
@ -79,14 +79,15 @@ export default function EditTestcasePanel({ problemId }: { problemId: string })
setTestcases((prev) => prev.filter((_, i) => i !== idx)); setTestcases((prev) => prev.filter((_, i) => i !== idx));
}; };
// 5) Field updates // 修改预期输出
const handleExpectedOutputChange = (idx: number, val: string) => const handleExpectedOutputChange = (idx: number, val: string) =>
setTestcases((prev) => { setTestcases((prev) => {
const c = [...prev]; const c = [...prev];
c[idx].expectedOutput = val; c[idx] = { ...c[idx], expectedOutput: val };
return c; return c;
}); });
// 修改输入参数
const handleInputChange = ( const handleInputChange = (
tIdx: number, tIdx: number,
iIdx: number, iIdx: number,
@ -95,43 +96,58 @@ export default function EditTestcasePanel({ problemId }: { problemId: string })
) => ) =>
setTestcases((prev) => { setTestcases((prev) => {
const c = [...prev]; const c = [...prev];
c[tIdx].inputs[iIdx][field] = val; const newInputs = [...c[tIdx].inputs];
newInputs[iIdx] = { ...newInputs[iIdx], [field]: val };
c[tIdx] = { ...c[tIdx], inputs: newInputs };
return c; return c;
}); });
// 添加输入参数
const handleAddInput = (tIdx: number) => const handleAddInput = (tIdx: number) =>
setTestcases((prev) => { setTestcases((prev) => {
const c = [...prev]; const c = [...prev];
c[tIdx].inputs.push({ name: `input${c[tIdx].inputs.length + 1}`, value: "" }); const inputs = [...c[tIdx].inputs, { name: `input${c[tIdx].inputs.length + 1}`, value: "" }];
c[tIdx] = { ...c[tIdx], inputs };
return c; return c;
}); });
// 删除输入参数
const handleRemoveInput = (tIdx: number, iIdx: number) => const handleRemoveInput = (tIdx: number, iIdx: number) =>
setTestcases((prev) => { setTestcases((prev) => {
const c = [...prev]; const c = [...prev];
c[tIdx].inputs.splice(iIdx, 1); const inputs = c[tIdx].inputs.filter((_, i) => i !== iIdx);
c[tIdx] = { ...c[tIdx], inputs };
return c; return c;
}); });
// 6) Persist all changes // 保存所有测试用例,并刷新最新数据
const handleSaveAll = async () => { const handleSaveAll = async () => {
try { try {
for (const tc of testcases) { for (let i = 0; i < testcases.length; i++) {
const tc = testcases[i];
if (tc.expectedOutput.trim() === "" || tc.inputs.some(inp => !inp.name.trim() || !inp.value.trim())) {
toast.error(`${i + 1} 个测试用例存在空的输入或输出,保存失败`);
return;
}
if (tc.id.startsWith("new-")) { if (tc.id.startsWith("new-")) {
const res = await addProblemTestcase(problemId, tc.expectedOutput, tc.inputs); const res = await addProblemTestcase(problemId, tc.expectedOutput, tc.inputs);
if (res.success) toast.success("新增测试用例成功"); if (res.success) {
else toast.error("新增测试用例失败"); toast.success(`新增测试用例 ${i + 1} 成功`);
} else {
toast.error(`新增测试用例 ${i + 1} 失败`);
}
} else { } else {
const res = await updateProblemTestcase( const res = await updateProblemTestcase(problemId, tc.id, tc.expectedOutput, tc.inputs);
problemId, if (res.success) toast.success(`更新测试用例 ${i + 1} 成功`);
tc.id, else toast.error(`更新测试用例 ${i + 1} 失败`);
tc.expectedOutput,
tc.inputs
);
if (res.success) toast.success("更新测试用例成功");
else toast.error("更新测试用例失败");
} }
} }
// 保存完成后刷新最新测试用例
const data = await getProblemData(problemId);
setTestcases(data.testcases || []);
toast.success("测试用例保存并刷新成功");
} catch (err) { } catch (err) {
console.error(err); console.error(err);
toast.error("保存测试用例异常"); toast.error("保存测试用例异常");