Files
web/src/app/admin/record/page.tsx
2026-04-13 10:23:19 +08:00

194 lines
5.4 KiB
TypeScript

'use client'
import {useCallback, useEffect, useState} from 'react'
import {useStatus} from '@/lib/states'
import {PageRecord} from '@/lib/api'
import Page from '@/components/page'
import DataTable from '@/components/data-table'
import {toast} from 'sonner'
import {Batch} from '@/lib/models/batch'
import {format} from 'date-fns'
import {Form, FormField} from '@/components/ui/form'
import {z} from 'zod'
import {useForm} from 'react-hook-form'
import {zodResolver} from '@hookform/resolvers/zod'
import DatePicker from '@/components/date-picker'
import {Button} from '@/components/ui/button'
import {EraserIcon, SearchIcon} from 'lucide-react'
import {pageBatch} from '@/actions/batch'
export type RecordPageProps = {}
export default function RecordPage(props: RecordPageProps) {
const [status, setStatus] = useStatus()
const [data, setData] = useState<PageRecord<Batch>>({
page: 1,
size: 10,
total: 0,
list: [],
})
// ======================
// filter
// ======================
const filterSchema = z.object({
time_start: z.date().optional(),
time_end: z.date().optional(),
})
type FilterSchema = z.infer<typeof filterSchema>
const filterForm = useForm<FilterSchema>({
resolver: zodResolver(filterSchema),
defaultValues: {
time_start: undefined,
time_end: undefined,
},
})
const fetchRecords = useCallback(async (page: number, size: number) => {
try {
setStatus('load')
// 获取筛选条件
const filter = filterForm.getValues()
const result = await pageBatch({
page,
size,
...filter,
})
if (result.success && result.data) {
setData(result.data)
}
else {
throw new Error('获取数据失败')
}
setStatus('done')
}
catch (error) {
setStatus('fail')
console.error(error)
toast.error('获取提取结果失败', {
description: (error as Error).message,
})
}
}, [filterForm, setStatus])
const filterHandler = filterForm.handleSubmit(async () => {
// 重置到第一页进行筛选
await fetchRecords(1, data.size)
})
useEffect(() => {
fetchRecords(data.page, data.size).then()
}, [data.page, data.size, fetchRecords])
return (
<Page>
{/* 筛选表单 */}
<section className="flex justify-between">
<div></div>
<Form form={filterForm} handler={filterHandler} className="flex-auto flex flex-wrap gap-4 items-end">
<fieldset className="flex flex-col gap-2 items-start">
<div>
<legend className="block text-sm"></legend>
</div>
<div className="flex gap-1 items-center">
<FormField<FilterSchema, 'time_start'> name="time_start">
{({field}) => (
<DatePicker
placeholder="选择开始时间"
{...field}
format="yyyy-MM-dd"
/>
)}
</FormField>
<span>-</span>
<FormField<FilterSchema, 'time_end'> name="time_end">
{({field}) => (
<DatePicker
placeholder="选择结束时间"
{...field}
format="yyyy-MM-dd"
/>
)}
</FormField>
</div>
</fieldset>
<Button className="h-9" type="submit">
<SearchIcon/>
</Button>
<Button
theme="outline"
className="h-9"
onClick={() => {
filterForm.reset()
fetchRecords(1, data.size)
}}>
<EraserIcon/>
</Button>
</Form>
</section>
<DataTable
status={status}
data={data.list}
pagination={{
page: data.page,
size: data.size,
total: data.total,
onPageChange: page => fetchRecords(page, data.size),
onSizeChange: size => fetchRecords(1, size),
}}
columns={[
{
header: '批次号',
cell: ({row}) => <div>{row.original.batch_no}</div>,
accessorKey: 'batch_no',
},
{
header: 'IP地址',
cell: ({row}) => <div>{row.original.ip}</div>,
accessorKey: 'ip',
},
{
header: '运营商',
cell: ({row}) => <div>{row.original.isp}</div>,
accessorKey: 'isp',
},
{
header: '地区',
cell: ({row}) => <div>{row.original.prov}</div>,
accessorKey: 'prov',
},
{
header: '城市',
cell: ({row}) => <div>{row.original.city}</div>,
accessorKey: 'city',
},
{
header: '提取数量',
cell: ({row}) => <div>{row.original.count}</div>,
accessorKey: 'count',
},
// {
// header: '资源数量',
// cell: ({row}) => <div>{row.original.resource_id}</div>,
// accessorKey: 'resource_id',
// },
{
header: '提取时间',
cell: ({row}) => {
return <div>{format(new Date(row.original.time), 'yyyy-MM-dd HH:mm:ss')}</div>
},
accessorKey: 'time',
},
]}
/>
</Page>
)
}