2025-12-19 15:15:54 +08:00
|
|
|
'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 {extractRecord} from '@/actions/channel'
|
|
|
|
|
import {BatchRecord} 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'
|
|
|
|
|
|
|
|
|
|
export type RecordPageProps = {}
|
|
|
|
|
|
|
|
|
|
export default function RecordPage(props: RecordPageProps) {
|
|
|
|
|
const [status, setStatus] = useStatus()
|
|
|
|
|
const [data, setData] = useState<PageRecord<BatchRecord>>({
|
|
|
|
|
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()
|
2025-12-20 10:09:38 +08:00
|
|
|
console.log(filter, 'filter')
|
|
|
|
|
|
2025-12-19 15:15:54 +08:00
|
|
|
const result = await extractRecord({
|
|
|
|
|
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.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>
|
|
|
|
|
)
|
|
|
|
|
}
|