diff --git a/README.md b/README.md index 068f17b..5f9fbfa 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ ## TODO +检查时间范围选择,限定到一定范围内 +将翻页操作反映在路由历史中,可以通过后退返回到上一个翻页状态 使用 pure js 的包代替 canvas,加快编译速度 diff --git a/src/actions/channel.ts b/src/actions/channel.ts index 2c357f5..7b88348 100644 --- a/src/actions/channel.ts +++ b/src/actions/channel.ts @@ -1,7 +1,28 @@ 'use server' import {callByUser} from '@/actions/base' +import {Channel} from '@/lib/models' +import {PageRecord} from '@/lib/api' -async function createChannels(params: { +export async function listChannels(props: { + page: number + size: number + auth_type?: number + prov?: string + city?: string + isp?: string + expiration?: Date +}) { + return callByUser>('/api/channel/list', props) +} + +type CreateChannelsResp = { + host: string + port: string + username?: string + password?: string +} + +export async function createChannels(params: { resource_id: number protocol: number auth_type: number @@ -12,14 +33,3 @@ async function createChannels(params: { }) { return callByUser('/api/channel/create', params) } - -type CreateChannelsResp = { - host: string - port: string - username?: string - password?: string -} - -export { - createChannels, -} diff --git a/src/app/admin/_client/navbar.tsx b/src/app/admin/_client/navbar.tsx index 6abbd80..04e24dd 100644 --- a/src/app/admin/_client/navbar.tsx +++ b/src/app/admin/_client/navbar.tsx @@ -52,7 +52,7 @@ export default function Navbar(props: NavbarProps) { - + diff --git a/src/app/admin/_client/profile.tsx b/src/app/admin/_client/profile.tsx index ebca97e..bbda2d7 100644 --- a/src/app/admin/_client/profile.tsx +++ b/src/app/admin/_client/profile.tsx @@ -9,19 +9,10 @@ export type ProfileProps = {} export default function Profile(props: ProfileProps) { const refreshProfile = useProfileStore(store => store.refreshProfile) - const router = useRouter() const doLogout = async () => { - try { - const resp = await logout() - if (resp.success) { - await refreshProfile() - router.push('/') - } - } - catch (e) { - toast.error('退出登录失败', { - description: (e as Error).message, - }) + const resp = await logout() + if (resp.success) { + await refreshProfile() } } diff --git a/src/app/admin/channels/page.tsx b/src/app/admin/channels/page.tsx new file mode 100644 index 0000000..6fe0bb2 --- /dev/null +++ b/src/app/admin/channels/page.tsx @@ -0,0 +1,184 @@ +'use client' +import {useEffect, useState} from 'react' +import {useStatus} from '@/lib/states' +import {PageRecord} from '@/lib/api' +import {Channel} from '@/lib/models' +import Page from '@/components/page' +import DataTable from '@/components/data-table' +import {toast} from 'sonner' +import {listChannels} from '@/actions/channel' +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 {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/components/ui/select' + +export type ChannelsPageProps = {} + +export default function ChannelsPage(props: ChannelsPageProps) { + + // ====================== + // data + // ====================== + + const [status, setStatus] = useStatus() + const [data, setData] = useState>({ + page: 1, + size: 10, + total: 0, + list: [], + }) + + const refresh = async (page: number, size: number) => { + try { + setStatus('load') + + // 筛选条件 + const filter = filterForm.getValues() + const auth_type = filter.auth_type ? parseInt(filter.auth_type) : undefined + + // 请求数据 + console.log({ + page, size, ...filter, auth_type, + }) + const resp = await listChannels({ + page, size, ...filter, auth_type, + }) + if (!resp.success) { + throw new Error(resp.message) + } + + // 更新数据 + setData(resp.data) + setStatus('done') + } + catch (e) { + setStatus('fail') + console.error(e) + toast.error('获取提取结果失败', { + description: (e as Error).message, + }) + } + } + + useEffect(() => { + refresh(data.page, data.size).then() + }, []) + + // ====================== + // filter + // ====================== + + const filterSchema = z.object({ + auth_type: z.enum(['0', '1', '2']), + expire_after: z.date().optional(), + expire_before: z.date().optional(), + }) + type FilterSchema = z.infer + + const filterForm = useForm({ + resolver: zodResolver(filterSchema), + defaultValues: { + auth_type: '0', + expire_after: undefined, + expire_before: undefined, + }, + }) + const filterHandler = filterForm.handleSubmit(async value => { + await refresh(data.page, data.size) + }) + + // ====================== + // render + // ====================== + + return ( + +
+
+
+ name={`auth_type`} label={认证方式}> + {({field}) => ( + + )} + +
+
+ 过期时间 +
+
+ name={`expire_after`}> + {({field}) => ( + + )} + + - + name={`expire_before`}> + {({field}) => ( + + )} + +
+
+ + + +
+ + refresh(page, data.size), + onSizeChange: (size) => refresh(1, size), + }} + columns={[ + { + header: '代理地址', cell: ({row}) => `${row.original.proxy_host}:${row.original.proxy_port}`, + }, + { + header: '认证方式', cell: ({row}) => { + return
+ {row.original.auth_ip && (<> + IP 白名单 + {row.original.user_host} + )} + {row.original.auth_pass && (<> + 账号密码 + {row.original.username}:{row.original.password} + )} +
+ }, + }, + { + header: '过期时间', cell: ({row}) => format(row.original.expiration, 'yyyy-MM-dd HH:mm:ss'), + }, + { + header: '操作', cell: ({row}) => -, + }, + ]} + /> +
+ ) +} + diff --git a/src/app/admin/profile/page.tsx b/src/app/admin/profile/page.tsx index 6db9776..e853db0 100644 --- a/src/app/admin/profile/page.tsx +++ b/src/app/admin/profile/page.tsx @@ -18,6 +18,7 @@ import * as qrcode from 'qrcode' import Link from 'next/link' import RechargeModal from '@/components/composites/purchase/_client/recharge' import {User} from '@/lib/models' +import { Label } from '@/components/ui/label' export type ProfilePageProps = {} @@ -454,7 +455,7 @@ function ChangePhoneDialog(props: {
{currentPhone && (
- 当前手机号 +
)} diff --git a/src/app/admin/resources/page.tsx b/src/app/admin/resources/page.tsx index ecf8cbc..6bc25a1 100644 --- a/src/app/admin/resources/page.tsx +++ b/src/app/admin/resources/page.tsx @@ -136,7 +136,7 @@ export default function ResourcesPage(props: ResourcesPageProps) { 类型}> {({id, field}) => (