feat: initial project setup for Meme Studio
Next.js 14 App Router application for managing homophone pun game levels: - Better Auth with Prisma adapter for authentication - MySQL database with Prisma ORM - Level CRUD operations with drag-and-drop reordering - Tencent COS integration for image uploads - shadcn/ui components with Tailwind CSS - TanStack Query for server state management
This commit is contained in:
78
components/levels/level-card.tsx
Normal file
78
components/levels/level-card.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
'use client'
|
||||
|
||||
import { Level } from '@/types'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { GripVertical, Pencil, Trash2 } from 'lucide-react'
|
||||
import Image from 'next/image'
|
||||
|
||||
interface LevelCardProps {
|
||||
level: Level
|
||||
onEdit: (level: Level) => void
|
||||
onDelete: (id: string) => void
|
||||
isDragging?: boolean
|
||||
}
|
||||
|
||||
export function LevelCard({ level, onEdit, onDelete, isDragging }: LevelCardProps) {
|
||||
return (
|
||||
<Card
|
||||
className={`cursor-grab transition-shadow ${
|
||||
isDragging ? 'shadow-lg opacity-90' : 'hover:shadow-md'
|
||||
}`}
|
||||
>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex items-center justify-center w-8 h-8 rounded bg-gray-100 text-gray-500 cursor-grab">
|
||||
<GripVertical className="h-4 w-4" />
|
||||
</div>
|
||||
<div className="relative w-20 h-20 rounded-md overflow-hidden bg-gray-100 flex-shrink-0">
|
||||
{level.imageUrl ? (
|
||||
<Image
|
||||
src={level.imageUrl}
|
||||
alt="关卡图片"
|
||||
fill
|
||||
className="object-cover"
|
||||
sizes="80px"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-gray-400 text-xs">
|
||||
无图片
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-medium text-lg truncate">{level.answer}</p>
|
||||
<div className="mt-1 space-y-0.5">
|
||||
{level.hint1 && (
|
||||
<p className="text-sm text-gray-500 truncate">提示1: {level.hint1}</p>
|
||||
)}
|
||||
{level.hint2 && (
|
||||
<p className="text-sm text-gray-500 truncate">提示2: {level.hint2}</p>
|
||||
)}
|
||||
{level.hint3 && (
|
||||
<p className="text-sm text-gray-500 truncate">提示3: {level.hint3}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => onEdit(level)}
|
||||
>
|
||||
<Pencil className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => onDelete(level.id)}
|
||||
className="text-red-600 hover:text-red-700 hover:bg-red-50"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user