mirror of
https://github.com/cfngc4594/monaco-editor-lsp-next.git
synced 2025-07-04 09:20:53 +00:00
feat(problem-editor): realize backend for save problem edit
- 在编辑面板组件中添加 onUpdate属性,用于处理数据更新 - 新增 updateProblem 函数,实现问题数据的更新逻辑 - 在问题编辑页面中集成更新功能,实现即时保存
This commit is contained in:
parent
20f4cc01da
commit
f63d869403
@ -1,27 +1,116 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ProblemFlexLayout } from "@/features/problems/components/problem-flexlayout";
|
import { ProblemFlexLayout } from '@/features/problems/components/problem-flexlayout';
|
||||||
import { EditDescriptionPanel } from "@/components/creater/edit-description-panel";
|
import { EditDescriptionPanel } from '@/components/creater/edit-description-panel';
|
||||||
import { EditSolutionPanel } from "@/components/creater/edit-solution-panel";
|
import { EditSolutionPanel } from '@/components/creater/edit-solution-panel';
|
||||||
import { EditTestcasePanel } from "@/components/creater/edit-testcase-panel";
|
import { EditTestcasePanel } from '@/components/creater/edit-testcase-panel';
|
||||||
import { EditDetailPanel } from "@/components/creater/edit-detail-panel";
|
import { EditDetailPanel } from '@/components/creater/edit-detail-panel';
|
||||||
import { EditCodePanel } from "@/components/creater/edit-code-panel";
|
import { EditCodePanel } from '@/components/creater/edit-code-panel';
|
||||||
|
import { updateProblem } from '@/app/actions/updateProblem';
|
||||||
|
|
||||||
interface ProblemEditorPageProps {
|
interface ProblemEditorPageProps {
|
||||||
params: Promise<{ problemId: string }>;
|
params: Promise<{ problemId: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface UpdateData {
|
||||||
|
content: string;
|
||||||
|
language?: 'c' | 'cpp';
|
||||||
|
inputs?: Array<{ index: number; name: string; value: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUpdate = async (
|
||||||
|
updateFn: (data: UpdateData) => Promise<{ success: boolean }>,
|
||||||
|
data: UpdateData
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const result = await updateFn(data);
|
||||||
|
if (!result.success) {
|
||||||
|
// 这里可以添加更具体的错误处理
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新失败:', error);
|
||||||
|
return { success: false };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default async function ProblemEditorPage({
|
export default async function ProblemEditorPage({
|
||||||
params,
|
params,
|
||||||
}: ProblemEditorPageProps) {
|
}: ProblemEditorPageProps) {
|
||||||
const { problemId } = await params;
|
const { problemId } = await params;
|
||||||
|
|
||||||
const components: Record<string, React.ReactNode> = {
|
const components: Record<string, React.ReactNode> = {
|
||||||
description: <EditDescriptionPanel problemId={problemId} />,
|
description: <EditDescriptionPanel
|
||||||
solution: <EditSolutionPanel problemId={problemId} />,
|
problemId={problemId}
|
||||||
detail: <EditDetailPanel problemId={problemId} />,
|
onUpdate={async (data) => {
|
||||||
code: <EditCodePanel problemId={problemId} />,
|
await handleUpdate(
|
||||||
testcase: <EditTestcasePanel problemId={problemId} />,
|
(descriptionData) => updateProblem({
|
||||||
|
problemId,
|
||||||
|
displayId: 0,
|
||||||
|
description: descriptionData.content
|
||||||
|
}),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
solution: <EditSolutionPanel
|
||||||
|
problemId={problemId}
|
||||||
|
onUpdate={async (data) => {
|
||||||
|
await handleUpdate(
|
||||||
|
(solutionData) => updateProblem({
|
||||||
|
problemId,
|
||||||
|
displayId: 0,
|
||||||
|
solution: solutionData.content
|
||||||
|
}),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
detail: <EditDetailPanel
|
||||||
|
problemId={problemId}
|
||||||
|
onUpdate={async (data) => {
|
||||||
|
await handleUpdate(
|
||||||
|
(detailData) => updateProblem({
|
||||||
|
problemId,
|
||||||
|
displayId: 0,
|
||||||
|
detail: detailData.content
|
||||||
|
}),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
code: <EditCodePanel
|
||||||
|
problemId={problemId}
|
||||||
|
onUpdate={async (data) => {
|
||||||
|
await handleUpdate(
|
||||||
|
(codeData) => updateProblem({
|
||||||
|
problemId,
|
||||||
|
displayId: 0,
|
||||||
|
templates: [{
|
||||||
|
language: codeData.language || 'c', // 添加默认值
|
||||||
|
content: codeData.content
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
testcase: <EditTestcasePanel
|
||||||
|
problemId={problemId}
|
||||||
|
onUpdate={async (data) => {
|
||||||
|
await handleUpdate(
|
||||||
|
(testcaseData) => updateProblem({
|
||||||
|
problemId,
|
||||||
|
displayId: 0,
|
||||||
|
testcases: [{
|
||||||
|
expectedOutput: testcaseData.content,
|
||||||
|
inputs: testcaseData.inputs || [] // 添加默认空数组
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
data
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
127
src/app/actions/updateProblem.ts
Normal file
127
src/app/actions/updateProblem.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
'use server';
|
||||||
|
|
||||||
|
import prisma from '@/lib/prisma';
|
||||||
|
import { revalidatePath } from 'next/cache';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const ProblemUpdateSchema = z.object({
|
||||||
|
problemId: z.string(),
|
||||||
|
displayId: z.number().optional(), // 改回可选字段
|
||||||
|
difficulty: z.enum(['EASY', 'MEDIUM', 'HARD']).optional(),
|
||||||
|
isPublished: z.boolean().optional(),
|
||||||
|
timeLimit: z.number().optional(),
|
||||||
|
memoryLimit: z.number().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
solution: z.string().optional(),
|
||||||
|
detail: z.string().optional(),
|
||||||
|
templates: z.array(z.object({
|
||||||
|
language: z.enum(['c', 'cpp']),
|
||||||
|
content: z.string()
|
||||||
|
})).optional(),
|
||||||
|
testcases: z.array(z.object({
|
||||||
|
expectedOutput: z.string(),
|
||||||
|
inputs: z.array(z.object({
|
||||||
|
index: z.number(),
|
||||||
|
name: z.string(),
|
||||||
|
value: z.string()
|
||||||
|
}))
|
||||||
|
})).optional()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type UpdateProblemData = z.infer<typeof ProblemUpdateSchema>;
|
||||||
|
|
||||||
|
export async function updateProblem(data: z.infer<typeof ProblemUpdateSchema>) {
|
||||||
|
try {
|
||||||
|
const validatedData = ProblemUpdateSchema.parse(data);
|
||||||
|
|
||||||
|
// 使用upsert代替update实现存在时更新,不存在时创建
|
||||||
|
const problem = await prisma.problem.upsert({
|
||||||
|
where: { id: validatedData.problemId },
|
||||||
|
create: {
|
||||||
|
id: validatedData.problemId, // 需要显式指定ID
|
||||||
|
displayId: validatedData.displayId || 0,
|
||||||
|
difficulty: validatedData.difficulty || 'EASY',
|
||||||
|
isPublished: validatedData.isPublished || false,
|
||||||
|
timeLimit: validatedData.timeLimit || 1000,
|
||||||
|
memoryLimit: validatedData.memoryLimit || 134217728,
|
||||||
|
// 初始化关联数据
|
||||||
|
localizations: validatedData.description ? {
|
||||||
|
create: [{
|
||||||
|
locale: 'en',
|
||||||
|
type: 'DESCRIPTION',
|
||||||
|
content: validatedData.description
|
||||||
|
}]
|
||||||
|
} : undefined,
|
||||||
|
templates: validatedData.templates ? {
|
||||||
|
create: validatedData.templates.map(t => ({
|
||||||
|
language: t.language,
|
||||||
|
content: t.content
|
||||||
|
}))
|
||||||
|
} : undefined,
|
||||||
|
testcases: validatedData.testcases ? {
|
||||||
|
create: validatedData.testcases.map(t => ({
|
||||||
|
expectedOutput: t.expectedOutput,
|
||||||
|
inputs: {
|
||||||
|
create: t.inputs.map(i => ({
|
||||||
|
index: i.index,
|
||||||
|
name: i.name,
|
||||||
|
value: i.value
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
} : undefined
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
displayId: validatedData.displayId,
|
||||||
|
difficulty: validatedData.difficulty,
|
||||||
|
isPublished: validatedData.isPublished,
|
||||||
|
timeLimit: validatedData.timeLimit,
|
||||||
|
memoryLimit: validatedData.memoryLimit,
|
||||||
|
// 更新关联数据
|
||||||
|
localizations: validatedData.description ? {
|
||||||
|
upsert: {
|
||||||
|
where: { problemId_locale_type: {
|
||||||
|
problemId: validatedData.problemId,
|
||||||
|
locale: 'en',
|
||||||
|
type: 'DESCRIPTION'
|
||||||
|
}},
|
||||||
|
create: {
|
||||||
|
locale: 'en',
|
||||||
|
type: 'DESCRIPTION',
|
||||||
|
content: validatedData.description
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
content: validatedData.description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} : undefined,
|
||||||
|
templates: validatedData.templates ? {
|
||||||
|
deleteMany: {},
|
||||||
|
create: validatedData.templates.map(t => ({
|
||||||
|
language: t.language,
|
||||||
|
content: t.content
|
||||||
|
}))
|
||||||
|
} : undefined,
|
||||||
|
testcases: validatedData.testcases ? {
|
||||||
|
deleteMany: {},
|
||||||
|
create: validatedData.testcases.map(t => ({
|
||||||
|
expectedOutput: t.expectedOutput,
|
||||||
|
inputs: {
|
||||||
|
create: t.inputs.map(i => ({
|
||||||
|
index: i.index,
|
||||||
|
name: i.name,
|
||||||
|
value: i.value
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
} : undefined
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
revalidatePath(`/problem-editor/${validatedData.problemId}`);
|
||||||
|
return { success: true, problem };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to update problem:', error);
|
||||||
|
return { success: false, error: 'Failed to update problem' };
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,10 @@ interface Template {
|
|||||||
|
|
||||||
interface EditCodePanelProps {
|
interface EditCodePanelProps {
|
||||||
problemId: string;
|
problemId: string;
|
||||||
|
onUpdate?: (data: {
|
||||||
|
content: string;
|
||||||
|
language: 'c' | 'cpp'; // 移除可选标记
|
||||||
|
}) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EditCodePanel = ({
|
export const EditCodePanel = ({
|
||||||
|
@ -10,6 +10,7 @@ import {CoreEditor} from "@/components/core-editor";
|
|||||||
|
|
||||||
interface EditDescriptionPanelProps {
|
interface EditDescriptionPanelProps {
|
||||||
problemId: string;
|
problemId: string;
|
||||||
|
onUpdate?: (data: { content: string }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EditDescriptionPanel = ({
|
export const EditDescriptionPanel = ({
|
||||||
@ -65,7 +66,7 @@ export const EditDescriptionPanel = ({
|
|||||||
<CoreEditor
|
<CoreEditor
|
||||||
value={content}
|
value={content}
|
||||||
onChange={setContent}
|
onChange={setContent}
|
||||||
language="markdown"
|
language="``"
|
||||||
className="absolute inset-0 rounded-md border border-input"
|
className="absolute inset-0 rounded-md border border-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,6 +8,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
|
|
||||||
interface EditDetailPanelProps {
|
interface EditDetailPanelProps {
|
||||||
problemId: string;
|
problemId: string;
|
||||||
|
onUpdate?: (data: { content: string }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EditDetailPanel = ({
|
export const EditDetailPanel = ({
|
||||||
|
@ -10,6 +10,7 @@ import {CoreEditor} from "@/components/core-editor";
|
|||||||
|
|
||||||
interface EditSolutionPanelProps {
|
interface EditSolutionPanelProps {
|
||||||
problemId: string;
|
problemId: string;
|
||||||
|
onUpdate?: (data: { content: string }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EditSolutionPanel = ({
|
export const EditSolutionPanel = ({
|
||||||
@ -65,7 +66,7 @@ export const EditSolutionPanel = ({
|
|||||||
<CoreEditor
|
<CoreEditor
|
||||||
value={content}
|
value={content}
|
||||||
onChange={setContent}
|
onChange={setContent}
|
||||||
language="markdown"
|
language="``"
|
||||||
className="absolute inset-0 rounded-md border border-input"
|
className="absolute inset-0 rounded-md border border-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,6 +21,10 @@ interface TestCase {
|
|||||||
|
|
||||||
interface EditTestcasePanelProps {
|
interface EditTestcasePanelProps {
|
||||||
problemId: string;
|
problemId: string;
|
||||||
|
onUpdate?: (data: {
|
||||||
|
content: string;
|
||||||
|
inputs: Array<{ index: number; name: string; value: string }>
|
||||||
|
}) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EditTestcasePanel = ({
|
export const EditTestcasePanel = ({
|
||||||
|
Loading…
Reference in New Issue
Block a user