feat: 支持配置微信用户已通关关卡

This commit is contained in:
richarjiang
2026-04-19 14:28:36 +08:00
parent 6e19bfa661
commit f3f27def2b
19 changed files with 7095 additions and 545 deletions

View File

@@ -1,10 +1,9 @@
'use client'
import { useState, useRef, useEffect } from 'react'
import { useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import {
Dialog,
DialogContent,
@@ -16,7 +15,6 @@ import {
import { Spinner } from '@/components/ui/spinner'
import { Level, LevelFormData } from '@/types'
import { ImageUploader } from './image-uploader'
import { Upload } from 'lucide-react'
interface LevelDialogProps {
open: boolean
@@ -26,8 +24,12 @@ interface LevelDialogProps {
}
const defaultFormData: LevelFormData = {
imageUrl: '',
image1Url: '',
image1Description: '',
image2Url: '',
image2Description: '',
answer: '',
punchline: '',
hint1: '',
hint2: '',
hint3: '',
@@ -37,15 +39,18 @@ export function LevelDialog({ open, onOpenChange, level, onSubmit }: LevelDialog
const [formData, setFormData] = useState<LevelFormData>(defaultFormData)
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState('')
const fileInputRef = useRef<HTMLInputElement>(null)
// Reset form when dialog opens/closes or level changes
useEffect(() => {
if (open) {
if (level) {
setFormData({
imageUrl: level.imageUrl,
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 || '',
@@ -57,16 +62,25 @@ export function LevelDialog({ open, onOpenChange, level, onSubmit }: LevelDialog
}
}, [open, level])
const handleImageUpload = (url: string) => {
setFormData((prev) => ({ ...prev, imageUrl: url }))
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.imageUrl) {
setError('请上传关卡图片')
if (!formData.image1Url) {
setError('请上传图片1')
return
}
if (!formData.image2Url) {
setError('请上传图片2')
return
}
@@ -75,6 +89,11 @@ export function LevelDialog({ open, onOpenChange, level, onSubmit }: LevelDialog
return
}
if (formData.answer.trim().length > 4) {
setError('答案最多4个字')
return
}
setIsLoading(true)
try {
await onSubmit(formData)
@@ -88,7 +107,7 @@ export function LevelDialog({ open, onOpenChange, level, onSubmit }: LevelDialog
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[500px]">
<DialogContent className="sm:max-w-2xl max-h-[85vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>{level ? '编辑关卡' : '添加关卡'}</DialogTitle>
<DialogDescription>
@@ -102,12 +121,39 @@ export function LevelDialog({ open, onOpenChange, level, onSubmit }: LevelDialog
</div>
)}
<div className="space-y-2">
<Label> *</Label>
<ImageUploader
value={formData.imageUrl}
onChange={handleImageUpload}
/>
{/* 双图上传区域 */}
<div className="grid grid-cols-2 gap-4">
{/* 图片1 */}
<div className="space-y-2">
<Label>1 *</Label>
<ImageUploader
value={formData.image1Url}
onChange={handleImage1Upload}
/>
<Input
value={formData.image1Description}
onChange={(e) =>
setFormData((prev) => ({ ...prev, image1Description: e.target.value }))
}
placeholder="图片1描述 (可选)"
/>
</div>
{/* 图片2 */}
<div className="space-y-2">
<Label>2 *</Label>
<ImageUploader
value={formData.image2Url}
onChange={handleImage2Upload}
/>
<Input
value={formData.image2Description}
onChange={(e) =>
setFormData((prev) => ({ ...prev, image2Description: e.target.value }))
}
placeholder="图片2描述 (可选)"
/>
</div>
</div>
<div className="space-y-2">
@@ -118,9 +164,25 @@ export function LevelDialog({ open, onOpenChange, level, onSubmit }: LevelDialog
onChange={(e) =>
setFormData((prev) => ({ ...prev, answer: e.target.value }))
}
placeholder="请输入答案"
placeholder="请输入答案最多4个字"
maxLength={4}
required
/>
<p className="text-xs text-muted-foreground text-right">
{formData.answer.length}/4
</p>
</div>
<div className="space-y-2">
<Label htmlFor="punchline"> ()</Label>
<Input
id="punchline"
value={formData.punchline}
onChange={(e) =>
setFormData((prev) => ({ ...prev, punchline: e.target.value }))
}
placeholder="请输入谐音梗说明"
/>
</div>
<div className="space-y-2">