lty/qy-lty-admin/components/dances/add-dance-dialog.tsx
2026-03-17 13:17:02 +08:00

352 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import type React from "react"
import { useState, useEffect } from "react"
import type { Dance } from "@/lib/api/types"
import { useToast } from "@/hooks/use-toast"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Loader2, Upload, Video } from "lucide-react"
interface AddDanceDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
onDanceAdded: (dance: Dance) => void
editDance?: Dance
}
export function AddDanceDialog({ open, onOpenChange, onDanceAdded, editDance }: AddDanceDialogProps) {
const { toast } = useToast()
const [isSubmitting, setIsSubmitting] = useState(false)
// 表单状态
const [formData, setFormData] = useState<Partial<Dance>>({
name: "",
choreographer: "",
duration: "",
difficulty: "中等",
description: "",
category: "流行",
tags: [],
motionFile: "",
videoUrl: "",
})
// 预览ID
const [previewId, setPreviewId] = useState(
"DNC" +
Math.floor(Math.random() * 1000)
.toString()
.padStart(3, "0"),
)
// 当编辑模式且有舞蹈数据时,初始化表单
useEffect(() => {
if (editDance) {
setFormData({
name: editDance.name || "",
choreographer: editDance.choreographer || "",
duration: editDance.duration || "",
difficulty: editDance.difficulty || "中等",
description: editDance.description || "",
category: editDance.category || "流行",
tags: editDance.tags || [],
motionFile: editDance.motionFile || "",
videoUrl: editDance.videoUrl || "",
})
} else {
// 重置表单
setFormData({
name: "",
choreographer: "",
duration: "",
difficulty: "中等",
description: "",
category: "流行",
tags: [],
motionFile: "",
videoUrl: "",
})
// 生成新的预览ID
setPreviewId(
"DNC" +
Math.floor(Math.random() * 1000)
.toString()
.padStart(3, "0"),
)
}
}, [editDance, open])
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target
setFormData((prev) => ({ ...prev, [name]: value }))
}
const handleSelectChange = (name: string, value: string) => {
setFormData((prev) => ({ ...prev, [name]: value }))
}
const handleTagsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const tagsString = e.target.value
const tagsArray = tagsString
.split(",")
.map((tag) => tag.trim())
.filter((tag) => tag !== "")
setFormData((prev) => ({ ...prev, tags: tagsArray }))
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
// 表单验证
if (!formData.name) {
toast({
title: "表单不完整",
description: "请填写舞蹈名称",
variant: "destructive",
})
return
}
setIsSubmitting(true)
try {
// 模拟API请求延迟
await new Promise((resolve) => setTimeout(resolve, 1000))
const newDance: Dance = {
id: editDance?.id || previewId,
name: formData.name || "",
choreographer: formData.choreographer || "",
duration: formData.duration || "",
difficulty: formData.difficulty || "中等",
description: formData.description || "",
category: formData.category || "流行",
tags: formData.tags || [],
motionFile: formData.motionFile || "",
videoUrl: formData.videoUrl || "",
coverUrl: editDance?.coverUrl || "/placeholder.svg?height=300&width=400",
createdAt: editDance?.createdAt || new Date().toISOString(),
updatedAt: new Date().toISOString(),
}
onDanceAdded(newDance)
onOpenChange(false)
} catch (error) {
toast({
title: "操作失败",
description: "添加或更新舞蹈时出现错误,请重试。",
variant: "destructive",
})
} finally {
setIsSubmitting(false)
}
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="text-xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-purple-600 to-pink-600">
{editDance ? "编辑舞蹈" : "添加新舞蹈"}
</DialogTitle>
<DialogDescription>
{editDance ? "修改舞蹈信息,完成后点击保存。" : "填写舞蹈信息,完成后点击添加。"}
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="name">
<span className="text-red-500">*</span>
</Label>
<Input
id="name"
name="name"
value={formData.name || ""}
onChange={handleChange}
className="border-gray-300 focus-visible:ring-purple-500"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="choreographer"></Label>
<Input
id="choreographer"
name="choreographer"
value={formData.choreographer || ""}
onChange={handleChange}
className="border-gray-300 focus-visible:ring-purple-500"
/>
</div>
<div className="space-y-2">
<Label htmlFor="duration"></Label>
<Input
id="duration"
name="duration"
value={formData.duration || ""}
onChange={handleChange}
placeholder="例如: 3:45"
className="border-gray-300 focus-visible:ring-purple-500"
/>
</div>
<div className="space-y-2">
<Label htmlFor="difficulty"></Label>
<Select
value={formData.difficulty || "中等"}
onValueChange={(value) => handleSelectChange("difficulty", value)}
>
<SelectTrigger className="border-gray-300 focus:ring-purple-500">
<SelectValue placeholder="选择难度" />
</SelectTrigger>
<SelectContent>
<SelectItem value="初级"></SelectItem>
<SelectItem value="中等"></SelectItem>
<SelectItem value="中高级"></SelectItem>
<SelectItem value="高级"></SelectItem>
<SelectItem value="专业"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="category"></Label>
<Select
value={formData.category || "流行"}
onValueChange={(value) => handleSelectChange("category", value)}
>
<SelectTrigger className="border-gray-300 focus:ring-purple-500">
<SelectValue placeholder="选择分类" />
</SelectTrigger>
<SelectContent>
<SelectItem value="流行"></SelectItem>
<SelectItem value="中国风"></SelectItem>
<SelectItem value="日式"></SelectItem>
<SelectItem value="现代"></SelectItem>
<SelectItem value="古典"></SelectItem>
<SelectItem value="街舞"></SelectItem>
<SelectItem value="其他"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="tags"></Label>
<Input
id="tags"
name="tags"
value={formData.tags?.join(", ") || ""}
onChange={handleTagsChange}
placeholder="用逗号分隔多个标签"
className="border-gray-300 focus-visible:ring-purple-500"
/>
</div>
<div className="space-y-2">
<Label htmlFor="motionFile"></Label>
<Input
id="motionFile"
name="motionFile"
value={formData.motionFile || ""}
onChange={handleChange}
placeholder="动作文件名称,例如: dance_motion.fbx"
className="border-gray-300 focus-visible:ring-purple-500"
/>
</div>
<div className="space-y-2">
<Label htmlFor="videoUrl"></Label>
<Input
id="videoUrl"
name="videoUrl"
value={formData.videoUrl || ""}
onChange={handleChange}
placeholder="舞蹈视频链接"
className="border-gray-300 focus-visible:ring-purple-500"
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="description"></Label>
<Textarea
id="description"
name="description"
value={formData.description || ""}
onChange={handleChange}
rows={4}
placeholder="请输入舞蹈的详细描述..."
className="min-h-[100px] border-gray-300 focus-visible:ring-purple-500"
/>
</div>
<div className="space-y-2">
<Label className="text-right"></Label>
<div className="border-2 border-dashed border-gray-300 rounded-lg p-6 flex flex-col items-center justify-center hover:border-purple-500 transition-colors cursor-pointer">
<Video className="h-8 w-8 text-gray-400 mb-2" />
<p className="text-sm text-gray-500"></p>
<p className="text-xs text-gray-400 mt-1"> PNG, JPG, JPEG 5MB</p>
</div>
</div>
<div className="space-y-2">
<Label className="text-right"></Label>
<div className="border-2 border-dashed border-gray-300 rounded-lg p-6 flex flex-col items-center justify-center hover:border-purple-500 transition-colors cursor-pointer">
<Upload className="h-8 w-8 text-gray-400 mb-2" />
<p className="text-sm text-gray-500"></p>
<p className="text-xs text-gray-400 mt-1"> FBX, BVH 20MB</p>
</div>
</div>
{!editDance && (
<div className="p-4 bg-gray-50 rounded-lg">
<h3 className="text-sm font-medium mb-2"></h3>
<p className="text-sm text-gray-600">
ID: <span className="font-medium text-purple-600">{previewId}</span>
</p>
</div>
)}
<DialogFooter>
<Button type="button" variant="outline" onClick={() => onOpenChange(false)} disabled={isSubmitting}>
</Button>
<Button
type="submit"
className="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600"
disabled={isSubmitting}
>
{isSubmitting ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
{editDance ? "更新中..." : "创建中..."}
</>
) : editDance ? (
"保存修改"
) : (
"添加舞蹈"
)}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
)
}