修复提取IP表格字段和页面展示 & 前端设置白名单添加数量限制 & 修复长效套餐购买数量和提取数量默认值和递增减

This commit is contained in:
Eamon-meng
2025-12-01 19:21:29 +08:00
parent 3a5f4220ad
commit 591177e7a1
5 changed files with 130 additions and 79 deletions

View File

@@ -155,34 +155,52 @@ export default function ChannelsPage(props: ChannelsPageProps) {
}} }}
columns={[ columns={[
{ {
header: '代理地址', cell: ({row}) => `${row.original.proxy_host}:${row.original.proxy_port}`, header: '代理地址', cell: ({row}) => {
const channel = row.original
const ip = channel.proxy?.ip
const port = channel.port
return `${ip}:${port}`
},
}, },
{ {
header: '认证方式', cell: ({row}) => { header: '认证方式',
cell: ({row}) => {
const channel = row.original
const hasWhitelist = channel.whitelists && channel.whitelists.trim() !== ''
const hasAuth = channel.username && channel.password
return ( return (
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1 min-w-0">
{row.original.auth_ip && ( {hasWhitelist ? (
<> <div className="flex flex-col">
<span className="text-weak">IP </span> <span ></span>
<span>{row.original.whitelists.replaceAll(',', ', ')}</span> <div className="flex flex-wrap gap-1 max-w-[200px]">
</> {channel.whitelists.split(',').map((ip, index) => (
)} <span
{row.original.auth_pass && ( key={index}
<> className="inline-block px-2 py-0.5 bg-gray-100 rounded text-xs text-gray-700 break-all"
<span className="text-weak"></span> >
<span> {ip.trim()}
{row.original.username} </span>
: ))}
{row.original.password} </div>
</span> </div>
</> ) : hasAuth ? (
<div className="flex flex-col">
<span></span>
<div className="px-2 py-1 bg-gray-100 rounded text-sm text-gray-700 font-mono break-all max-w-[120px]">
{channel.username}:{channel.password}
</div>
</div>
) : (
<span className="text-sm text-gray-400"></span>
)} )}
</div> </div>
) )
}, },
}, },
{ {
header: '过期时间', cell: ({row}) => format(row.original.expiration, 'yyyy-MM-dd HH:mm:ss'), header: '过期时间', cell: ({row}) => format(row.original.expired_at, 'yyyy-MM-dd HH:mm:ss'),
}, },
{ {
header: '操作', cell: ({row}) => <span>-</span>, header: '操作', cell: ({row}) => <span>-</span>,

View File

@@ -33,6 +33,8 @@ type SchemaType = z.infer<typeof schema>
export type WhitelistPageProps = {} export type WhitelistPageProps = {}
const MAX_WHITELIST_COUNT = 5
export default function WhitelistPage(props: WhitelistPageProps) { export default function WhitelistPage(props: WhitelistPageProps) {
const [wait, setWait] = useState(false) const [wait, setWait] = useState(false)
@@ -53,6 +55,8 @@ export default function WhitelistPage(props: WhitelistPageProps) {
setWait(true) setWait(true)
try { try {
const resp = await listWhitelist({page, size}) const resp = await listWhitelist({page, size})
console.log(resp, '白名单resp')
if (!resp.success) { if (!resp.success) {
throw new Error(resp.message) throw new Error(resp.message)
} }
@@ -227,9 +231,10 @@ export default function WhitelistPage(props: WhitelistPageProps) {
{/* 全局操作 */} {/* 全局操作 */}
<section> <section>
<Button onClick={() => openDialog('add')} disabled={wait}> <Button onClick={() => openDialog('add')} disabled={wait || data.total >= MAX_WHITELIST_COUNT}>
<Plus/> <Plus/>
{data.total >= MAX_WHITELIST_COUNT && '(已达上限)'}
</Button> </Button>
{/* <Button {/* <Button
theme="fail" theme="fail"
@@ -254,7 +259,7 @@ export default function WhitelistPage(props: WhitelistPageProps) {
}} }}
columns={[ columns={[
{ {
header: `IP 地址`, accessorKey: 'host', header: `IP 地址`, accessorKey: 'ip',
}, },
{ {
header: `备注`, accessorKey: 'remark', header: `备注`, accessorKey: 'remark',
@@ -264,7 +269,7 @@ export default function WhitelistPage(props: WhitelistPageProps) {
}, },
{ {
id: 'actions', header: `操作`, cell: ({row}) => ( id: 'actions', header: `操作`, cell: ({row}) => (
<div className="flex justify-end gap-2"> <div className="flex gap-2">
<Button <Button
className="h-9 w-9" className="h-9 w-9"
theme="outline" theme="outline"

View File

@@ -8,7 +8,7 @@ import {Select, SelectContent, SelectItem, SelectSeparator, SelectTrigger, Selec
import {Button} from '@/components/ui/button' import {Button} from '@/components/ui/button'
import {useForm, useFormContext} from 'react-hook-form' import {useForm, useFormContext} from 'react-hook-form'
import {Alert, AlertTitle} from '@/components/ui/alert' import {Alert, AlertTitle} from '@/components/ui/alert'
import {Box, CircleAlert, CopyIcon, ExternalLinkIcon, Loader, Timer} from 'lucide-react' import {Box, CircleAlert, CopyIcon, ExternalLinkIcon, Loader, Plus, Timer} from 'lucide-react'
import {memo, ReactNode, useEffect, useRef, useState} from 'react' import {memo, ReactNode, useEffect, useRef, useState} from 'react'
import {useStatus} from '@/lib/states' import {useStatus} from '@/lib/states'
import {allResource} from '@/actions/resource' import {allResource} from '@/actions/resource'
@@ -72,9 +72,12 @@ export default function Extract(props: ExtractProps) {
)} )}
> >
<CardSection> <CardSection>
<Alert variant="warn"> <Alert variant="warn" className="flex items-center">
<CircleAlert/> <CircleAlert/>
<AlertTitle>IP前需要将本机IP添加到白名单后才可使用</AlertTitle> <AlertTitle className="flex">IP前需要将本机IP添加到白名单后才可使用</AlertTitle>
<Link href="/admin/whitelist">
<Button ><Plus/></Button>
</Link>
</Alert> </Alert>
<FormFields/> <FormFields/>

View File

@@ -76,33 +76,37 @@ export default function Center() {
className="space-y-4" className="space-y-4"
name="quota" name="quota"
label="IP 购买数量"> label="IP 购买数量">
{({id, field}) => ( {({id, field}) => {
<div className="flex gap-2 items-center"> const value = Number(field.value) || 500
<Button const minValue = 500
theme="outline" const step = 100
type="button" return (
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg" <div className="flex gap-2 items-center">
onClick={() => form.setValue('quota', Math.max(10_000, Number(field.value) - 5_000))} <Button
disabled={Number(field.value) === 10_000}> theme="outline"
<Minus/> type="button"
</Button> className={`h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg ${
<Input value === minValue ? 'opacity-50 cursor-not-allowed' : ''
{...field} }`}
id={id} onClick={() => form.setValue('quota', Math.max(minValue, value - step))}
type="number" disabled={value === minValue}>
className="w-40 h-10 border border-gray-200 rounded-sm text-center" <Minus/>
min={10_000} </Button>
step={5_000}
/> <div className="w-40 h-10 border border-gray-200 rounded-sm flex items-center justify-center">
<Button {value}
theme="outline" </div>
type="button"
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg" <Button
onClick={() => form.setValue('quota', Number(field.value) + 5_000)}> theme="outline"
<Plus/> type="button"
</Button> className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg"
</div> onClick={() => form.setValue('quota', value + step)}>
)} <Plus/>
</Button>
</div>
)
}}
</FormField> </FormField>
) : ( ) : (
<> <>
@@ -133,33 +137,38 @@ export default function Center() {
className="space-y-4" className="space-y-4"
name="daily_limit" name="daily_limit"
label="每日提取上限"> label="每日提取上限">
{({id, field}) => ( {({id, field}) => {
<div className="flex gap-2 items-center"> const value = Number(field.value) || 100
<Button const minValue = 100
theme="outline" const step = 100
type="button"
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg" return (
onClick={() => form.setValue('daily_limit', Math.max(2_000, Number(field.value) - 1_000))} <div className="flex gap-2 items-center">
disabled={Number(field.value) === 2_000}> <Button
<Minus/> theme="outline"
</Button> type="button"
<Input className={`h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg ${
{...field} value === minValue ? 'opacity-50 cursor-not-allowed' : ''
id={id} }`}
type="number" onClick={() => form.setValue('daily_limit', Math.max(minValue, value - step))}
className="w-40 h-10 border border-gray-200 rounded-sm text-center" disabled={value === minValue}>
min={2_000} <Minus/>
step={1_000} </Button>
/>
<Button <div className="w-40 h-10 border border-gray-200 rounded-sm flex items-center justify-center">
theme="outline" {value}
type="button" </div>
className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg"
onClick={() => form.setValue('daily_limit', Number(field.value) + 1_000)}> <Button
<Plus/> theme="outline"
</Button> type="button"
</div> className="h-10 w-10 border border-gray-200 rounded-sm flex items-center justify-center text-lg"
)} onClick={() => form.setValue('daily_limit', value + step)}>
<Plus/>
</Button>
</div>
)
}}
</FormField> </FormField>
</> </>
)} )}

View File

@@ -88,6 +88,22 @@ export type Channel = {
expiration: Date expiration: Date
created_at: Date created_at: Date
updated_at: Date updated_at: Date
proxy: Proxy
port: number
expired_at: Date
}
export type Proxy = {
id: number
ip: string
msc: string
secret: string
type: number
status: number
created_at: Date
updated_at: Date
deleted_at: Date | null
channels: Channel[] | null
version: number
} }
export type Announcement = { export type Announcement = {