From 5b1207783be65ac2ecb287486ab8abf8fc09fb07 Mon Sep 17 00:00:00 2001 From: Eamon-meng <17516219072@163.com> Date: Sat, 20 Dec 2025 15:08:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A8=E9=80=81=E8=BF=9C=E7=A8=8B=E5=88=86?= =?UTF-8?q?=E6=94=AF=E5=89=8D=E7=9A=84=E6=8E=92=E6=9F=A5=E5=92=8C=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eslint.config.mjs | 1 + src/actions/batch.ts | 8 + src/actions/channel.ts | 6 +- src/actions/resource.ts | 6 +- src/app/(home)/footer.tsx | 2 +- src/app/admin/_components/addr.tsx | 22 +++ src/app/admin/channels/layout.tsx | 2 +- src/app/admin/channels/page.tsx | 154 +++++++----------- src/app/admin/record/page.tsx | 10 +- .../composites/purchase/long/right.tsx | 45 +++-- .../composites/purchase/short/right.tsx | 45 +++-- src/lib/models/batch.ts | 2 +- 12 files changed, 143 insertions(+), 160 deletions(-) create mode 100644 src/actions/batch.ts create mode 100644 src/app/admin/_components/addr.tsx diff --git a/eslint.config.mjs b/eslint.config.mjs index c72afb4..2dc564c 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -32,6 +32,7 @@ const eslintConfig = defineConfig([ '@stylistic/block-spacing': 'off', '@typescript-eslint/no-empty-object-type': 'off', '@typescript-eslint/no-unused-vars': 'off', + '@react-hooks/set-state-in-effect': 'off', }, }, ]) diff --git a/src/actions/batch.ts b/src/actions/batch.ts new file mode 100644 index 0000000..6007310 --- /dev/null +++ b/src/actions/batch.ts @@ -0,0 +1,8 @@ +'use server' +import {PageRecord} from '@/lib/api' +import {Batch} from '@/lib/models/batch' +import {callByUser} from './base' + +export async function pageBatch(props: {page: number, size: number}) { + return callByUser>('/api/batch/page', props) +} diff --git a/src/actions/channel.ts b/src/actions/channel.ts index fad8c7d..3f66f0a 100644 --- a/src/actions/channel.ts +++ b/src/actions/channel.ts @@ -2,7 +2,7 @@ import {callByUser, callPublic} from '@/actions/base' import {Channel} from '@/lib/models' import {PageRecord} from '@/lib/api' -import {BatchRecord} from '@/lib/models/batch' +import {Batch} from '@/lib/models/batch' export async function listChannels(props: { page: number @@ -16,10 +16,6 @@ export async function listChannels(props: { return callByUser>('/api/channel/list', props) } -export async function extractRecord(props: {page: number, size: number}) { - return callByUser>('/api/batch/page', props) -} - type CreateChannelsResp = { host: string port: string diff --git a/src/actions/resource.ts b/src/actions/resource.ts index 5245223..e92fc9d 100644 --- a/src/actions/resource.ts +++ b/src/actions/resource.ts @@ -87,5 +87,9 @@ export async function payClose(props: { } export async function getPrice(props: CreateResourceReq) { - return callByDevice<{price: string}>('/api/resource/price', props) + return callByDevice<{ + price: string + discounted_price?: string + discounted?: number + }>('/api/resource/price', props) } diff --git a/src/app/(home)/footer.tsx b/src/app/(home)/footer.tsx index ac10f68..9b78e0f 100644 --- a/src/app/(home)/footer.tsx +++ b/src/app/(home)/footer.tsx @@ -76,7 +76,7 @@ export default function Footer(props: FooterProps) { /> -
+

蓝狐代理仅提供IP服务,用户使用蓝狐代理IP从事的任何行为均不代表蓝狐代理IP的意志和观点,与蓝狐代理的立场无关。
diff --git a/src/app/admin/_components/addr.tsx b/src/app/admin/_components/addr.tsx new file mode 100644 index 0000000..fba0804 --- /dev/null +++ b/src/app/admin/_components/addr.tsx @@ -0,0 +1,22 @@ +import {Badge} from '@/components/ui/badge' +import {Channel} from '@/lib/models' +import {isBefore} from 'date-fns' + +export default function Addr({channel}: { + channel: Channel +}) { + const ip = channel.host + const port = channel.port + const expired = isBefore(channel.expired_at, new Date()) + + return ( +

+ {ip}:{port} + {expired && ( + + 已过期 + + )} +
+ ) +} diff --git a/src/app/admin/channels/layout.tsx b/src/app/admin/channels/layout.tsx index 1750c59..811e656 100644 --- a/src/app/admin/channels/layout.tsx +++ b/src/app/admin/channels/layout.tsx @@ -12,5 +12,5 @@ export type ChannelsLayoutProps = { } export default async function ChannelsLayout(props: ChannelsLayoutProps) { - return {props.children} + return props.children } diff --git a/src/app/admin/channels/page.tsx b/src/app/admin/channels/page.tsx index a76ba34..60b16e8 100644 --- a/src/app/admin/channels/page.tsx +++ b/src/app/admin/channels/page.tsx @@ -1,5 +1,5 @@ 'use client' -import {useCallback, useEffect, useState} from 'react' +import {Suspense, useCallback, useEffect, useState} from 'react' import {useStatus} from '@/lib/states' import {PageRecord} from '@/lib/api' import {Channel} from '@/lib/models' @@ -17,6 +17,7 @@ import {Button} from '@/components/ui/button' import {EraserIcon, SearchIcon} from 'lucide-react' import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/components/ui/select' import {Badge} from '@/components/ui/badge' +import Addr from '../_components/addr' export type ChannelsPageProps = {} export default function ChannelsPage(props: ChannelsPageProps) { @@ -50,11 +51,6 @@ export default function ChannelsPage(props: ChannelsPageProps) { expire_before: undefined, }, }) - // 检查是否过期 - const isExpired = (expiredAt: string | Date) => { - const date = typeof expiredAt === 'string' ? new Date(expiredAt) : expiredAt - return isBefore(date, new Date()) - } const refresh = useCallback(async (page: number, size: number) => { try { @@ -63,12 +59,8 @@ export default function ChannelsPage(props: ChannelsPageProps) { // 筛选条件 const filter = filterForm.getValues() const auth_type = filter.auth_type ? parseInt(filter.auth_type) : undefined - const expired_status = filter.expired_status // 请求数据 - console.log({ - page, size, ...filter, auth_type, - }) const resp = await listChannels({ page, size, ...filter, auth_type, }) @@ -77,20 +69,8 @@ export default function ChannelsPage(props: ChannelsPageProps) { throw new Error(resp.message) } - let filteredList = resp.data.list - if (expired_status !== undefined && expired_status !== 'all') { - filteredList = resp.data.list.filter((channel) => { - const expired = isExpired(channel.expired_at) - return !expired - }) - resp.data.total = filteredList.length - } - // 更新数据 - setData({ - ...resp.data, - list: filteredList, - }) + setData(resp.data) setStatus('done') } catch (e) { @@ -182,81 +162,67 @@ export default function ChannelsPage(props: ChannelsPageProps) { - refresh(page, data.size), - onSizeChange: size => refresh(1, size), - }} - columns={[ - { - header: '代理地址', - cell: ({row}) => { - const channel = row.original - const ip = channel.host - const port = channel.port - const expired = isExpired(channel.expired_at) - - return ( -
- {ip}:{port} - {expired && ( - - 已过期 - - )} -
- ) + + refresh(page, data.size), + onSizeChange: size => refresh(1, size), + }} + columns={[ + { + header: '代理地址', + cell: ({row}) => , }, - }, - { - header: '认证方式', - cell: ({row}) => { - const channel = row.original - const hasWhitelist = channel.whitelists && channel.whitelists.trim() !== '' - const hasAuth = channel.username && channel.password + { + header: '认证方式', + cell: ({row}) => { + const channel = row.original + const hasWhitelist = channel.whitelists && channel.whitelists.trim() !== '' + const hasAuth = channel.username && channel.password - return ( -
- {hasWhitelist ? ( -
- 白名单 -
- {channel.whitelists.split(',').map((ip, index) => ( - - {ip.trim()} - - ))} + return ( +
+ {hasWhitelist ? ( +
+ 白名单 +
+ {channel.whitelists.split(',').map((ip, index) => ( + + {ip.trim()} + + ))} +
-
- ) : hasAuth ? ( -
- 账号密码 - - {channel.username}:{channel.password} - -
- ) : ( - 无认证 - )} -
- ) + ) : hasAuth ? ( +
+ 账号密码 + + {channel.username}:{channel.password} + +
+ ) : ( + 无认证 + )} +
+ ) + }, }, - }, - { - header: '提取时间', - cell: ({row}) => format(row.original.created_at, 'yyyy-MM-dd HH:mm'), - }, - { - header: '过期时间', - cell: ({row}) => format(row.original.expired_at, 'yyyy-MM-dd HH:mm:ss'), - }, - ]} - /> + { + header: '提取时间', + cell: ({row}) => format(row.original.created_at, 'yyyy-MM-dd HH:mm'), + }, + { + header: '过期时间', + cell: ({row}) => format(row.original.expired_at, 'yyyy-MM-dd HH:mm:ss'), + }, + ]} + /> + ) } diff --git a/src/app/admin/record/page.tsx b/src/app/admin/record/page.tsx index 7c57617..8b40126 100644 --- a/src/app/admin/record/page.tsx +++ b/src/app/admin/record/page.tsx @@ -5,8 +5,7 @@ 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 {Batch} from '@/lib/models/batch' import {format} from 'date-fns' import {Form, FormField} from '@/components/ui/form' import {z} from 'zod' @@ -15,12 +14,13 @@ 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>({ + const [data, setData] = useState>({ page: 1, size: 10, total: 0, @@ -50,9 +50,7 @@ export default function RecordPage(props: RecordPageProps) { setStatus('load') // 获取筛选条件 const filter = filterForm.getValues() - console.log(filter, 'filter') - - const result = await extractRecord({ + const result = await pageBatch({ page, size, ...filter, diff --git a/src/components/composites/purchase/long/right.tsx b/src/components/composites/purchase/long/right.tsx index f2bbb0d..6dd1d8a 100644 --- a/src/components/composites/purchase/long/right.tsx +++ b/src/components/composites/purchase/long/right.tsx @@ -18,12 +18,7 @@ import {useFormContext, useWatch} from 'react-hook-form' import {Schema} from '@/components/composites/purchase/long/form' import {Card} from '@/components/ui/card' import {getPrice, CreateResourceReq} from '@/actions/resource' - -interface PriceData { - price: string - discounted_price?: string - discounted?: number -} +import {ExtraResp} from '@/lib/api' export default function Right() { const {control} = useFormContext() @@ -33,7 +28,7 @@ export default function Right() { const quota = useWatch({control, name: 'quota'}) const expire = useWatch({control, name: 'expire'}) const dailyLimit = useWatch({control, name: 'daily_limit'}) - const [priceData, setPriceData] = useState({ + const [priceData, setPriceData] = useState>({ price: '0.00', discounted_price: '0.00', discounted: 0, @@ -41,29 +36,27 @@ export default function Right() { useEffect(() => { const price = async () => { - const params: CreateResourceReq = { - type: 2, - long: { - live: Number(live), - mode: Number(mode), - quota: Number(mode) === 1 ? Number(dailyLimit) : Number(quota), - expire: Number(mode) === 1 ? Number(expire) : undefined, - }, - } try { - const priceValue = await getPrice(params) - - if (priceValue.success && priceValue.data?.price) { - const data: PriceData = priceValue.data - setPriceData({ - price: data.price, - discounted_price: data.discounted_price ?? data.price ?? '', - discounted: data.discounted, - }) + const resp = await getPrice({ + type: 2, + long: { + live: Number(live), + mode: Number(mode), + quota: mode === '1' ? Number(dailyLimit) : Number(quota), + expire: mode === '1' ? Number(expire) : undefined, + }, + }) + if (!resp.success) { + throw new Error('获取价格失败') } + + setPriceData({ + price: resp.data.price, + discounted_price: resp.data.discounted_price ?? resp.data.price ?? '', + discounted: resp.data.discounted, + }) } catch (error) { - console.error('获取价格失败:', error) setPriceData({ price: '0.00', discounted_price: '0.00', diff --git a/src/components/composites/purchase/short/right.tsx b/src/components/composites/purchase/short/right.tsx index d35f695..947e6b8 100644 --- a/src/components/composites/purchase/short/right.tsx +++ b/src/components/composites/purchase/short/right.tsx @@ -17,12 +17,7 @@ import Pay from '@/components/composites/purchase/pay' import {useFormContext, useWatch} from 'react-hook-form' import {Card} from '@/components/ui/card' import {CreateResourceReq, getPrice} from '@/actions/resource' - -interface PriceData { - price: string - discounted_price?: string - discounted?: number -} +import {ExtraResp} from '@/lib/api' export default function Right() { const {control} = useFormContext() @@ -32,7 +27,7 @@ export default function Right() { const expire = useWatch({control, name: 'expire'}) const quota = useWatch({control, name: 'quota'}) const dailyLimit = useWatch({control, name: 'daily_limit'}) - const [priceData, setPriceData] = useState({ + const [priceData, setPriceData] = useState>({ price: '0.00', discounted_price: '0.00', discounted: 0, @@ -40,26 +35,26 @@ export default function Right() { useEffect(() => { const price = async () => { - const params: CreateResourceReq = { - type: 1, - short: { - live: Number(live), - mode: Number(mode), - quota: Number(mode) === 1 ? Number(dailyLimit) : Number(quota), - expire: Number(mode) === 1 ? Number(expire) : undefined, - }, - } try { - const priceResponse = await getPrice(params) - - if (priceResponse.success && priceResponse.data) { - const data: PriceData = priceResponse.data - setPriceData({ - price: data.price, - discounted_price: data.discounted_price ?? data.price ?? '', - discounted: data.discounted, - }) + const priceResponse = await getPrice({ + type: 1, + short: { + live: Number(live), + mode: Number(mode), + quota: mode === '1' ? Number(dailyLimit) : Number(quota), + expire: mode === '1' ? Number(expire) : undefined, + }, + }) + if (!priceResponse.success) { + throw new Error('获取价格失败') } + + const data = priceResponse.data + setPriceData({ + price: data.price, + discounted_price: data.discounted_price ?? data.price ?? '', + discounted: data.discounted, + }) } catch (error) { console.error('获取价格失败:', error) diff --git a/src/lib/models/batch.ts b/src/lib/models/batch.ts index 89a1f7e..d653107 100644 --- a/src/lib/models/batch.ts +++ b/src/lib/models/batch.ts @@ -1,4 +1,4 @@ -export type BatchRecord = { +export type Batch = { batch_no: string ip: string id: number