120 lines
3.7 KiB
TypeScript
120 lines
3.7 KiB
TypeScript
'use client'
|
||
|
||
import { Level } from '@/types'
|
||
import { Button } from '@/components/ui/button'
|
||
import { Pencil, Trash2 } from 'lucide-react'
|
||
import Image from 'next/image'
|
||
|
||
// 列宽定义,header 和 row 必须保持一致。用 grid-template-columns 统一控制。
|
||
// 序号 | 图片 | 答案 | 谐音梗 | 提示 | 创建时间 | 操作
|
||
export const GRID_TEMPLATE =
|
||
'minmax(60px,60px) minmax(120px,120px) minmax(80px,1fr) minmax(100px,1fr) minmax(160px,2fr) minmax(100px,100px) minmax(100px,100px)'
|
||
|
||
interface LevelRowProps {
|
||
level: Level
|
||
index: number
|
||
onEdit: (level: Level) => void
|
||
onDelete: (id: string) => void
|
||
}
|
||
|
||
export function LevelRow({ level, index, onEdit, onDelete }: LevelRowProps) {
|
||
return (
|
||
<div
|
||
style={{ gridTemplateColumns: GRID_TEMPLATE }}
|
||
className="grid items-center gap-3 px-3 border-b bg-background text-sm min-h-[64px] hover:bg-muted/30 transition-colors"
|
||
>
|
||
{/* 序号:用数组 index + 1,而不是 DB 里已弃用的 sortOrder */}
|
||
<span className="font-mono tabular-nums text-muted-foreground">
|
||
{index + 1}
|
||
</span>
|
||
|
||
{/* 图片 */}
|
||
<div className="flex gap-1.5">
|
||
<div className="relative w-12 h-12 rounded overflow-hidden bg-gray-100 flex-shrink-0">
|
||
{level.image1Url ? (
|
||
<Image
|
||
src={level.image1Url}
|
||
alt="图片1"
|
||
fill
|
||
className="object-cover"
|
||
sizes="48px"
|
||
/>
|
||
) : (
|
||
<div className="w-full h-full flex items-center justify-center text-gray-400 text-[10px]">
|
||
无
|
||
</div>
|
||
)}
|
||
</div>
|
||
<div className="relative w-12 h-12 rounded overflow-hidden bg-gray-100 flex-shrink-0">
|
||
{level.image2Url ? (
|
||
<Image
|
||
src={level.image2Url}
|
||
alt="图片2"
|
||
fill
|
||
className="object-cover"
|
||
sizes="48px"
|
||
/>
|
||
) : (
|
||
<div className="w-full h-full flex items-center justify-center text-gray-400 text-[10px]">
|
||
无
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 答案 */}
|
||
<span className="font-medium truncate">{level.answer}</span>
|
||
|
||
{/* 谐音梗 */}
|
||
<span className="truncate">
|
||
{level.punchline ? (
|
||
<span className="text-orange-600">{level.punchline}</span>
|
||
) : (
|
||
<span className="text-muted-foreground">—</span>
|
||
)}
|
||
</span>
|
||
|
||
{/* 提示 */}
|
||
<span className="truncate text-muted-foreground">
|
||
{(() => {
|
||
const hints = [level.hint1, level.hint2, level.hint3].filter(Boolean)
|
||
return hints.length > 0 ? hints.join('、') : '—'
|
||
})()}
|
||
</span>
|
||
|
||
{/* 创建时间 */}
|
||
<span className="text-muted-foreground whitespace-nowrap">
|
||
{new Date(level.createdAt).toLocaleDateString('zh-CN', {
|
||
year: 'numeric',
|
||
month: '2-digit',
|
||
day: '2-digit',
|
||
})}
|
||
</span>
|
||
|
||
{/* 操作 */}
|
||
<div className="flex items-center gap-2">
|
||
<Button
|
||
variant="outline"
|
||
size="icon"
|
||
className="h-8 w-8"
|
||
onClick={() => onEdit(level)}
|
||
aria-label="编辑关卡"
|
||
title="编辑"
|
||
>
|
||
<Pencil className="h-3.5 w-3.5" />
|
||
</Button>
|
||
<Button
|
||
variant="outline"
|
||
size="icon"
|
||
className="h-8 w-8 border-red-200 text-red-600 hover:border-red-600 hover:bg-red-600 hover:text-white focus-visible:ring-red-500"
|
||
onClick={() => onDelete(level.id)}
|
||
aria-label="删除关卡"
|
||
title="删除"
|
||
>
|
||
<Trash2 className="h-3.5 w-3.5" />
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|