"use client" import { zodResolver } from "@hookform/resolvers/zod" import { format, isBefore, isSameDay } from "date-fns" import { Box, Loader2, Timer } from "lucide-react" import { Suspense, useCallback, useMemo, useState } from "react" import { Controller, useForm } from "react-hook-form" import { toast } from "sonner" import { z } from "zod" import { listResourceLong, listResourceShort, updateResource, } from "@/actions/resources" import { DataTable, useDataTable } from "@/components/data-table" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Field, FieldError, FieldGroup, FieldLabel, } from "@/components/ui/field" import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import type { Resources } from "@/models/resources" const filterSchema = z .object({ user_phone: z.string().optional(), resource_no: z.string().optional(), status: z.string().optional(), type: z.string().optional(), created_at_start: z.string().optional(), created_at_end: z.string().optional(), expired: z.string().optional(), }) .superRefine((data, ctx) => { if (data.created_at_start && data.created_at_end) { const start = new Date(data.created_at_start) const end = new Date(data.created_at_end) if (end < start) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "结束时间不能早于开始时间", path: ["created_at_end"], }) } } }) type FilterFormValues = z.infer interface FilterParams { user_phone?: string resource_no?: string active?: boolean mode?: number created_at_start?: Date created_at_end?: Date expired?: boolean } // 获取资源类型(从内部对象获取) function getResourceType(resource: Resources): number { if ("short" in resource && resource.short) { return resource.short.type } if ("long" in resource && resource.long) { return resource.long.type } return resource.type } // 获取资源详情对象 function getResourceDetail(resource: Resources) { if ("short" in resource && resource.short) { return resource.short } if ("long" in resource && resource.long) { return resource.long } return null } // 获取过期时间 function getExpireAt(resource: Resources): Date | null | undefined { if ("short" in resource && resource.short) { return resource.short.expire_at } if ("long" in resource && resource.long) { return resource.long.expire_at } return undefined } function getName(resource: Resources): string | null | undefined { if ("short" in resource && resource.short) { return resource.short.sku?.name } if ("long" in resource && resource.long) { return resource.long.sku?.name } return undefined } // 获取最近使用时间 function getLastAt(resource: Resources): Date | null | undefined { if ("short" in resource && resource.short) { return resource.short.last_at } if ("long" in resource && resource.long) { return resource.long.last_at } return undefined } // 资源类型徽章 function ResourceTypeBadge({ resource }: { resource: Resources }) { const type = getResourceType(resource) if (type === 1) { return (
包时
) } if (type === 2) { return (
包量
) } return null } // 过期徽章 function ExpireBadge({ expireAt }: { expireAt: Date | null | undefined }) { if (!expireAt) return null if (isBefore(expireAt, new Date())) { return 过期 } return null } // 格式化日期 function formatDateTime(date: Date | null | undefined) { if (!date) return "-" return format(date, "yyyy-MM-dd HH:mm") } // 计算今日使用量 function getTodayUsage(lastAt: Date | null | undefined, daily: number) { if (lastAt && isSameDay(lastAt, new Date())) { return daily } return 0 } export default function ResourcesPage() { return (
短效套餐 长效套餐
) } interface ResourceListProps { resourceType: "long" | "short" } function ResourceList({ resourceType }: ResourceListProps) { const isLong = resourceType === "long" const listFn = isLong ? listResourceLong : listResourceShort const [filters, setFilters] = useState({}) const [updatingId, setUpdatingId] = useState(null) const { control, handleSubmit, reset } = useForm({ resolver: zodResolver(filterSchema), defaultValues: { user_phone: "", resource_no: "", status: "all", type: "all", created_at_start: "", created_at_end: "", expired: "all", }, }) const fetchResources = useCallback( (page: number, size: number) => { return listFn({ page, size, ...filters }) }, [listFn, filters], ) const table = useDataTable(fetchResources) console.log(table, "我的套餐的table") const refreshTable = useCallback(() => { setFilters(prev => ({ ...prev })) }, []) const handleStatusChange = useCallback( async (resource: Resources, newStatusValue: string) => { const newActive = newStatusValue === "0" if (newActive === resource.active) return setUpdatingId(resource.id) try { await updateResource({ id: resource.id, active: newActive, }) toast.success("更新成功", { description: `资源状态已更新为${newActive ? "启用" : "禁用"}`, }) refreshTable() } catch (error) { console.error("更新状态失败:", error) toast.error("更新失败", { description: error instanceof Error ? error.message : "请稍后重试", }) } finally { setUpdatingId(null) } }, [refreshTable], ) const onFilter = handleSubmit(data => { const result: FilterParams = {} if (data.user_phone?.trim()) result.user_phone = data.user_phone.trim() if (data.resource_no?.trim()) result.resource_no = data.resource_no.trim() if (data.status && data.status !== "all") { result.active = data.status === "0" } if (data.type && data.type !== "all") { result.mode = Number(data.type) } if (data.expired && data.expired !== "all") { result.expired = data.expired === "1" } if (data.created_at_start) result.created_at_start = new Date(data.created_at_start) if (data.created_at_end) result.created_at_end = new Date(data.created_at_end) setFilters(result) table.pagination.onPageChange(1) }) const columns = useMemo( () => [ { header: "ID", accessorKey: "id" }, { header: "会员号", accessorFn: (row: Resources) => row.user?.phone || "-", }, { header: "套餐", cell: ({ row }: { row: { original: Resources } }) => { const resourceNo = row.original.resource_no const name = getName(row.original) const expireAt = getExpireAt(row.original) return (
{name}
{resourceNo}
) }, }, { header: "类型", cell: ({ row }: { row: { original: Resources } }) => { return }, }, { header: "IP时效", cell: ({ row }: { row: { original: Resources } }) => { const detail = getResourceDetail(row.original) const live = detail?.live if (live === undefined) return "-" return {isLong ? `${live}小时` : `${live}分钟`} }, }, { header: "使用情况", cell: ({ row }: { row: { original: Resources } }) => { const detail = getResourceDetail(row.original) const type = getResourceType(row.original) if (!detail) return - if (type === 1) { // 包时 const todayUsage = getTodayUsage(detail.last_at, detail.daily || 0) return (
{todayUsage}/{detail.quota}
) } else { // 包量 if (isLong) { return (
{detail.used < detail.quota ? ( 正常 ) : ( 已用完 )} | {detail.used}/{detail.quota}
) } else { return (
{detail.used}/{detail.quota}
) } } }, }, { header: "最近使用时间", cell: ({ row }: { row: { original: Resources } }) => { const lastAt = getLastAt(row.original) return lastAt ? formatDateTime(lastAt) : "暂未使用" }, }, { header: "开通时间", cell: ({ row }: { row: { original: Resources } }) => { return formatDateTime(row.original.created_at) }, }, ...(!isLong ? [ { header: "到期时间", cell: ({ row }: { row: { original: Resources } }) => { return formatDateTime(getExpireAt(row.original)) }, }, ] : []), { header: "状态", cell: ({ row }: { row: { original: Resources } }) => { const resource = row.original const isLoading = updatingId === resource.id const currentActive = resource.active return (
{isLoading && ( )}
) }, }, ], [isLong, updatingId, handleStatusChange], ) return (
( 会员号 {fieldState.error?.message} )} /> ( 套餐号 {fieldState.error?.message} )} /> ( 状态 {fieldState.error?.message} )} /> ( 类型 {fieldState.error?.message} )} /> ( 是否过期 {fieldState.error?.message} )} /> ( 开始时间 {fieldState.error?.message} )} /> ( 结束时间 {fieldState.error?.message} )} />
加载中...
}> {...table} columns={columns} /> ) }