'use client' import { useState, useEffect, useMemo } from 'react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Spinner } from '@/components/ui/spinner' import { Level, LevelFormData } from '@/types' import { ImageUploader } from './image-uploader' interface LevelDialogProps { open: boolean onOpenChange: (open: boolean) => void level?: Level | null /** 当前关卡总数(含正在编辑的这条) */ totalCount: number /** 编辑时当前行的 0-based index;创建时传 null */ currentIndex: number | null onSubmit: (data: LevelFormData) => Promise } type FormState = Omit & { /** 位置字段以字符串保存,便于处理"空输入"状态;提交时解析为数字 */ position: string } const defaultFormState: FormState = { image1Url: '', image1Description: '', image2Url: '', image2Description: '', answer: '', punchline: '', hint1: '', hint2: '', hint3: '', position: '', } export function LevelDialog({ open, onOpenChange, level, totalCount, currentIndex, onSubmit, }: LevelDialogProps) { const [formData, setFormData] = useState(defaultFormState) const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState('') const isEdit = !!level // 位置的合法范围与默认值 const { minPos, maxPos, defaultPos } = useMemo(() => { if (isEdit) { const min = 1 const max = Math.max(totalCount, 1) const def = typeof currentIndex === 'number' ? currentIndex + 1 : max return { minPos: min, maxPos: max, defaultPos: def } } const max = totalCount + 1 return { minPos: 1, maxPos: max, defaultPos: max } }, [isEdit, totalCount, currentIndex]) // Reset form when dialog opens/closes or level changes useEffect(() => { if (open) { if (level) { setFormData({ image1Url: level.image1Url, image1Description: level.image1Description || '', image2Url: level.image2Url, image2Description: level.image2Description || '', answer: level.answer, punchline: level.punchline || '', hint1: level.hint1 || '', hint2: level.hint2 || '', hint3: level.hint3 || '', position: String(defaultPos), }) } else { setFormData({ ...defaultFormState, position: String(defaultPos) }) } setError('') } }, [open, level, defaultPos]) const handleImage1Upload = (url: string) => { setFormData((prev) => ({ ...prev, image1Url: url })) } const handleImage2Upload = (url: string) => { setFormData((prev) => ({ ...prev, image2Url: url })) } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setError('') if (!formData.image1Url) { setError('请上传图片1') return } if (!formData.image2Url) { setError('请上传图片2') return } if (!formData.answer.trim()) { setError('请输入答案') return } if (formData.answer.trim().length > 4) { setError('答案最多4个字') return } // 位置:留空视为"不改位置"(编辑)/ "追加末尾"(创建) let position: number | undefined const raw = formData.position.trim() if (raw !== '') { const n = Number(raw) if (!Number.isInteger(n) || n < minPos || n > maxPos) { setError(`位置必须是 ${minPos}-${maxPos} 之间的整数`) return } // 编辑场景:值没变则不上传 position,避免后端无谓重算 sortKey if (isEdit && typeof currentIndex === 'number' && n === currentIndex + 1) { position = undefined } else { position = n } } const payload: LevelFormData = { image1Url: formData.image1Url, image1Description: formData.image1Description, image2Url: formData.image2Url, image2Description: formData.image2Description, answer: formData.answer, punchline: formData.punchline, hint1: formData.hint1, hint2: formData.hint2, hint3: formData.hint3, ...(position !== undefined ? { position } : {}), } setIsLoading(true) try { await onSubmit(payload) onOpenChange(false) } catch (err) { setError(err instanceof Error ? err.message : '操作失败,请稍后重试') } finally { setIsLoading(false) } } return ( {level ? '编辑关卡' : '添加关卡'} {level ? '修改关卡信息' : '创建新的关卡'}
{error && (
{error}
)} {/* 双图上传区域 */}
{/* 图片1 */}
setFormData((prev) => ({ ...prev, image1Description: e.target.value })) } placeholder="图片1描述 (可选)" />
{/* 图片2 */}
setFormData((prev) => ({ ...prev, image2Description: e.target.value })) } placeholder="图片2描述 (可选)" />
setFormData((prev) => ({ ...prev, answer: e.target.value })) } placeholder="请输入答案(最多4个字)" maxLength={4} required />

{formData.answer.length}/4

setFormData((prev) => ({ ...prev, position: e.target.value })) } className="w-32 font-mono tabular-nums" placeholder={String(defaultPos)} />

可填 {minPos} 到{' '} {maxPos} {isEdit && typeof currentIndex === 'number' && ( <> {' · 当前第 '} {currentIndex + 1} 位 )} {!isEdit && ' · 留空则追加到末尾'}

setFormData((prev) => ({ ...prev, punchline: e.target.value })) } placeholder="请输入谐音梗说明" />
setFormData((prev) => ({ ...prev, hint1: e.target.value })) } placeholder="请输入提示1" />
setFormData((prev) => ({ ...prev, hint2: e.target.value })) } placeholder="请输入提示2" />
setFormData((prev) => ({ ...prev, hint3: e.target.value })) } placeholder="请输入提示3" />
) }