Files
MemeStudio/components/levels/level-columns.tsx
2026-04-19 14:28:36 +08:00

157 lines
4.3 KiB
TypeScript

'use client'
import { ColumnDef } from '@tanstack/react-table'
import { Level } from '@/types'
import { Button } from '@/components/ui/button'
import { Pencil, Trash2 } from 'lucide-react'
import Image from 'next/image'
interface ColumnCallbacks {
onEdit: (level: Level) => void
onDelete: (id: string) => void
deleteConfirmId: string | null
}
export function createColumns({
onEdit,
onDelete,
deleteConfirmId,
}: ColumnCallbacks): ColumnDef<Level>[] {
return [
{
accessorKey: 'sortOrder',
header: '序号',
cell: ({ row }) => (
<span className="text-muted-foreground">{row.original.sortOrder + 1}</span>
),
size: 60,
},
{
id: 'images',
header: '图片',
cell: ({ row }) => {
const { image1Url, image2Url } = row.original
return (
<div className="flex gap-1.5">
<div className="relative w-12 h-12 rounded overflow-hidden bg-gray-100 flex-shrink-0">
{image1Url ? (
<Image
src={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">
{image2Url ? (
<Image
src={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>
)
},
size: 120,
},
{
accessorKey: 'answer',
header: '答案',
cell: ({ row }) => (
<span className="font-medium">{row.original.answer}</span>
),
},
{
accessorKey: 'punchline',
header: '谐音梗',
cell: ({ row }) => {
const punchline = row.original.punchline
return punchline ? (
<span className="text-orange-600">{punchline}</span>
) : (
<span className="text-muted-foreground"></span>
)
},
},
{
id: 'hints',
header: '提示',
cell: ({ row }) => {
const { hint1, hint2, hint3 } = row.original
const hints = [hint1, hint2, hint3].filter(Boolean)
return hints.length > 0 ? (
<span className="text-sm text-muted-foreground truncate max-w-[200px] block">
{hints.join('、')}
</span>
) : (
<span className="text-muted-foreground"></span>
)
},
},
{
accessorKey: 'createdAt',
header: '创建时间',
cell: ({ row }) => {
const date = new Date(row.original.createdAt)
return (
<span className="text-sm text-muted-foreground whitespace-nowrap">
{date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
})}
</span>
)
},
size: 120,
},
{
id: 'actions',
header: '操作',
cell: ({ row }) => {
const level = row.original
const isConfirming = deleteConfirmId === level.id
return (
<div className="flex items-center gap-2">
<Button
variant="outline"
size="icon"
className="h-8 w-8"
onClick={() => onEdit(level)}
>
<Pencil className="h-3.5 w-3.5" />
</Button>
<Button
variant="outline"
size="icon"
className={`h-8 w-8 ${
isConfirming
? 'bg-red-600 text-white hover:bg-red-700 border-red-600'
: 'text-red-600 hover:text-red-700 hover:bg-red-50'
}`}
onClick={() => onDelete(level.id)}
>
<Trash2 className="h-3.5 w-3.5" />
</Button>
</div>
)
},
size: 100,
},
]
}