diff --git a/src/app/(root)/admin/page.tsx b/src/app/(root)/admin/page.tsx index 3701912..2c1fc32 100644 --- a/src/app/(root)/admin/page.tsx +++ b/src/app/(root)/admin/page.tsx @@ -4,6 +4,7 @@ import { Suspense, useState } from "react" import { toast } from "sonner" import { deleteAdmin, getPageAdmin, updateAdmin } from "@/actions/admin" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { AlertDialog, AlertDialogAction, @@ -38,7 +39,7 @@ export default function AdminPage() { console.log(table, "table") return ( -
+ {/* 操作栏 */}
@@ -129,7 +130,7 @@ export default function AdminPage() { ]} /> -
+ ) } diff --git a/src/app/(root)/appbar.tsx b/src/app/(root)/appbar.tsx index 5bebbbb..3a5cff9 100644 --- a/src/app/(root)/appbar.tsx +++ b/src/app/(root)/appbar.tsx @@ -103,7 +103,7 @@ export default function Appbar(props: { admin: Admin }) { } return ( -
+
{/* 面包屑导航 */}
{breadcrumbs.map((crumb, index) => ( diff --git a/src/app/(root)/balance/page.tsx b/src/app/(root)/balance/page.tsx index 5dd5b10..2237efb 100644 --- a/src/app/(root)/balance/page.tsx +++ b/src/app/(root)/balance/page.tsx @@ -6,6 +6,7 @@ import { Controller, useForm } from "react-hook-form" import z from "zod" import { getPageBalance } from "@/actions/balance" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Button } from "@/components/ui/button" import { Field, @@ -14,7 +15,6 @@ import { FieldLabel, } from "@/components/ui/field" import { Input } from "@/components/ui/input" - import type { Balance } from "@/models/balance" type FilterValues = { @@ -84,8 +84,8 @@ export default function BalancePage() { }) return ( -
-
+ +
-
+
) } diff --git a/src/app/(root)/batch/page.tsx b/src/app/(root)/batch/page.tsx index fdfe423..8849845 100644 --- a/src/app/(root)/batch/page.tsx +++ b/src/app/(root)/batch/page.tsx @@ -6,6 +6,7 @@ import { Controller, useForm } from "react-hook-form" import { z } from "zod" import { getPageBatch } from "@/actions/batch" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Button } from "@/components/ui/button" import { Field, @@ -100,9 +101,9 @@ export default function BatchPage() { }) return ( -
+ {/* 搜索表单 */} - +
-
+
) } diff --git a/src/app/(root)/billing/page.tsx b/src/app/(root)/billing/page.tsx index a8048be..deed950 100644 --- a/src/app/(root)/billing/page.tsx +++ b/src/app/(root)/billing/page.tsx @@ -8,6 +8,7 @@ import { toast } from "sonner" import { z } from "zod" import { getPageBill, getSkuList } from "@/actions/bill" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Button } from "@/components/ui/button" import { Field, @@ -150,8 +151,8 @@ export default function BillingPage() { }) return ( -
- + +
-
+
) } diff --git a/src/app/(root)/channel/page.tsx b/src/app/(root)/channel/page.tsx index 8d83934..491ff5e 100644 --- a/src/app/(root)/channel/page.tsx +++ b/src/app/(root)/channel/page.tsx @@ -6,6 +6,7 @@ import { Controller, useForm } from "react-hook-form" import { z } from "zod" import { getPageChannel } from "@/actions/channel" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { @@ -101,8 +102,8 @@ export default function ChannelPage() { }) return ( -
- + +
-
+
) } diff --git a/src/app/(root)/client/balance/page.tsx b/src/app/(root)/client/balance/page.tsx index 6825a66..1ac16cd 100644 --- a/src/app/(root)/client/balance/page.tsx +++ b/src/app/(root)/client/balance/page.tsx @@ -7,6 +7,7 @@ import { Controller, useForm } from "react-hook-form" import z from "zod" import { getBalance } from "@/actions/balance" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Button } from "@/components/ui/button" import { Field, @@ -86,7 +87,7 @@ export default function BalancePage() { }) return ( -
+ 用户会员号: {userPhone} - +
-
+
) } diff --git a/src/app/(root)/client/batch/page.tsx b/src/app/(root)/client/batch/page.tsx index ddcbf47..5b1f0c4 100644 --- a/src/app/(root)/client/batch/page.tsx +++ b/src/app/(root)/client/batch/page.tsx @@ -7,6 +7,7 @@ import { Controller, useForm } from "react-hook-form" import { z } from "zod" import { getBatch } from "@/actions/batch" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Button } from "@/components/ui/button" import { Field, @@ -105,18 +106,9 @@ export default function BatchPage() { }) return ( -
- - 用户会员号: {userPhone} - - + + {/* 筛选表单 */} +
-
+
) } diff --git a/src/app/(root)/client/billing/page.tsx b/src/app/(root)/client/billing/page.tsx index 3f02cb1..7140d62 100644 --- a/src/app/(root)/client/billing/page.tsx +++ b/src/app/(root)/client/billing/page.tsx @@ -9,6 +9,7 @@ import { toast } from "sonner" import { z } from "zod" import { getBill, getSkuList } from "@/actions/bill" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Button } from "@/components/ui/button" import { Field, @@ -155,17 +156,8 @@ export default function BillingPage() { }) return ( -
- - 用户会员号: {userPhone} - + +
-
+
) } diff --git a/src/app/(root)/client/channel/page.tsx b/src/app/(root)/client/channel/page.tsx index fe00881..6b88169 100644 --- a/src/app/(root)/client/channel/page.tsx +++ b/src/app/(root)/client/channel/page.tsx @@ -7,6 +7,7 @@ import { Controller, useForm } from "react-hook-form" import { z } from "zod" import { getChannel } from "@/actions/channel" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { @@ -105,18 +106,8 @@ export default function ChannelPage() { }) return ( -
- - 用户会员号: {userPhone} - - + +
-
+
) } diff --git a/src/app/(root)/client/cust/page.tsx b/src/app/(root)/client/cust/page.tsx index 178845d..3113f77 100644 --- a/src/app/(root)/client/cust/page.tsx +++ b/src/app/(root)/client/cust/page.tsx @@ -10,6 +10,7 @@ import { getPageUser } from "@/actions/user" import { UpdateDialog } from "@/app/(root)/cust/update" import { Auth } from "@/components/auth" import { DataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { @@ -53,10 +54,7 @@ type FormValues = z.infer export default function UserQueryPage() { const [userList, setUserList] = useState([]) const [loading, setLoading] = useState(false) - const [isEditDialogOpen, setIsEditDialogOpen] = useState(false) - const [currentEditUser, setCurrentEditUser] = useState(null) const [currentFilters, setCurrentFilters] = useState({}) - const [isAddDialogOpen, setIsAddDialogOpen] = useState(false) const router = useRouter() const { control, handleSubmit, reset } = useForm({ @@ -111,8 +109,8 @@ export default function UserQueryPage() { } return ( -
- + +
- + + @@ -343,17 +334,6 @@ export default function UserQueryPage() { ]} /> - - -
+
) } diff --git a/src/app/(root)/client/resources/page.tsx b/src/app/(root)/client/resources/page.tsx index 8e7bb11..bef6cd3 100644 --- a/src/app/(root)/client/resources/page.tsx +++ b/src/app/(root)/client/resources/page.tsx @@ -13,6 +13,7 @@ import { updateResource, } from "@/actions/resources" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { @@ -170,7 +171,7 @@ function getTodayUsage(lastAt: Date | null | undefined, daily: number) { export default function ResourcesPage() { return ( -
+ -
+ ) } @@ -425,17 +426,7 @@ function ResourceList({ resourceType }: ResourceListProps) { const userPhone = searchParams.get("phone") return (
- - 用户会员号: {userPhone} - - +
- - 用户会员号: {userPhone} - - + + {/* 筛选表单 */} +
-
+
) } diff --git a/src/app/(root)/coupon/page.tsx b/src/app/(root)/coupon/page.tsx index d02f894..84aa6fe 100644 --- a/src/app/(root)/coupon/page.tsx +++ b/src/app/(root)/coupon/page.tsx @@ -4,6 +4,7 @@ import { Suspense, useState } from "react" import { toast } from "sonner" import { deleteCoupon, getPagCoupon } from "@/actions/coupon" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { AlertDialog, AlertDialogAction, @@ -24,7 +25,7 @@ export default function CouponPage() { const table = useDataTable((page, size) => getPagCoupon({ page, size })) return ( -
+
@@ -92,7 +93,7 @@ export default function CouponPage() { ]} /> -
+ ) } diff --git a/src/app/(root)/cust/create.tsx b/src/app/(root)/cust/create.tsx index 6d0ed1f..c5abcba 100644 --- a/src/app/(root)/cust/create.tsx +++ b/src/app/(root)/cust/create.tsx @@ -14,6 +14,7 @@ import { DialogContent, DialogHeader, DialogTitle, + DialogTrigger, } from "@/components/ui/dialog" import { Field, @@ -72,16 +73,11 @@ const addUserSchema = z export type AddUserFormValues = z.infer interface AddUserDialogProps { - open: boolean - onOpenChange: (open: boolean) => void onSuccess?: () => void } -export function AddUserDialog({ - open, - onOpenChange, - onSuccess, -}: AddUserDialogProps) { +export function AddUserDialog({ onSuccess }: AddUserDialogProps) { + const [open, setOpen] = useState(false) const [isAdding, setIsAdding] = useState(false) const [discountList, setDiscountList] = useState([]) const [isLoadingDiscount, setIsLoadingDiscount] = useState(false) @@ -176,7 +172,7 @@ export function AddUserDialog({ const result = await createCust(payload) if (result?.success) { toast.success("添加用户成功") - onOpenChange(false) + setOpen(false) resetAddForm() onSuccess?.() } else { @@ -194,11 +190,14 @@ export function AddUserDialog({ if (!open) { resetAddForm() } - onOpenChange(open) + setOpen(open) } return ( + + + 添加用户 @@ -396,7 +395,7 @@ export function AddUserDialog({ diff --git a/src/app/(root)/cust/deduction.tsx b/src/app/(root)/cust/deduction.tsx index 8799b89..69496a8 100644 --- a/src/app/(root)/cust/deduction.tsx +++ b/src/app/(root)/cust/deduction.tsx @@ -14,6 +14,7 @@ import { DialogFooter, DialogHeader, DialogTitle, + DialogTrigger, } from "@/components/ui/dialog" import { Field, FieldError, FieldLabel } from "@/components/ui/field" import { Input } from "@/components/ui/input" @@ -30,18 +31,15 @@ const Schema = z.object({ type FormValues = z.infer interface UpdateDeductionDialogProps { - open: boolean - onOpenChange: (open: boolean) => void - currentUser: User | null + user: User onSuccess: () => void } export function DeductionDialog({ - open, - onOpenChange, - currentUser, + user, onSuccess, }: UpdateDeductionDialogProps) { + const [open, setOpen] = useState(false) const [isLoading, setIsLoading] = useState(false) const { @@ -58,17 +56,16 @@ export function DeductionDialog({ }) const onSubmit = async (data: FormValues) => { - if (!currentUser) return setIsLoading(true) try { const result = await getDeduction({ - user_id: currentUser.id, + user_id: user.id, amount: data.deduction, }) if (result.success) { toast.success("扣款成功") - onOpenChange(false) + setOpen(false) reset() onSuccess() } else { @@ -86,16 +83,19 @@ export function DeductionDialog({ if (!open) { reset() } - onOpenChange(open) + setOpen(open) } return ( + + + 扣款 - 扣减用户 {currentUser?.name || currentUser?.username} 的余额 + 扣减用户 {user.name || user.username} 的余额 diff --git a/src/app/(root)/cust/deposit.tsx b/src/app/(root)/cust/deposit.tsx index a94a597..ac2f80b 100644 --- a/src/app/(root)/cust/deposit.tsx +++ b/src/app/(root)/cust/deposit.tsx @@ -14,6 +14,7 @@ import { DialogFooter, DialogHeader, DialogTitle, + DialogTrigger, } from "@/components/ui/dialog" import { Field, FieldError, FieldLabel } from "@/components/ui/field" import { Input } from "@/components/ui/input" @@ -30,18 +31,12 @@ const Schema = z.object({ type FormValues = z.infer interface UpdateDepositDialogProps { - open: boolean - onOpenChange: (open: boolean) => void - currentUser: User | null + user: User onSuccess: () => void } -export function DepositDialog({ - open, - onOpenChange, - currentUser, - onSuccess, -}: UpdateDepositDialogProps) { +export function DepositDialog({ user, onSuccess }: UpdateDepositDialogProps) { + const [open, setOpen] = useState(false) const [isLoading, setIsLoading] = useState(false) const { @@ -58,17 +53,16 @@ export function DepositDialog({ }) const onSubmit = async (data: FormValues) => { - if (!currentUser) return setIsLoading(true) try { const result = await getDeposit({ - user_id: currentUser.id, + user_id: user.id, amount: data.deposit, }) if (result.success) { toast.success("充值成功") - onOpenChange(false) + setOpen(false) reset() onSuccess() } else { @@ -86,16 +80,19 @@ export function DepositDialog({ if (!open) { reset() } - onOpenChange(open) + setOpen(open) } return ( + + + 充值 - 充值用户 {currentUser?.name || currentUser?.username} 的余额 + 充值用户 {user?.name || user?.username} 的余额 diff --git a/src/app/(root)/cust/page.tsx b/src/app/(root)/cust/page.tsx index fbab54b..aa07586 100644 --- a/src/app/(root)/cust/page.tsx +++ b/src/app/(root)/cust/page.tsx @@ -9,6 +9,7 @@ import { z } from "zod" import { getPageCusts } from "@/actions/cust" import { Auth } from "@/components/auth" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { @@ -86,14 +87,7 @@ type FormValues = z.infer export default function CustPage() { const [filters, setFilters] = useState({}) - const [isAddDialogOpen, setIsAddDialogOpen] = useState(false) - const [isEditDialogOpen, setIsEditDialogOpen] = useState(false) - const [currentEditUser, setCurrentEditUser] = useState(null) - const [depositDialog, setDepositDialog] = useState(false) - const [deposit, setDeposit] = useState(null) const router = useRouter() - const [deductionDialog, setDeductionDialog] = useState(false) - const [deduction, setDeduction] = useState(null) const { control, handleSubmit, reset } = useForm({ resolver: zodResolver(filterSchema), defaultValues: { @@ -130,8 +124,8 @@ export default function CustPage() { }, [table]) return ( -
- + +
)} /> + - + + - + - + @@ -492,32 +471,6 @@ export default function CustPage() { ]} /> - - - - - - - -
+
) } diff --git a/src/app/(root)/cust/update.tsx b/src/app/(root)/cust/update.tsx index 458146b..b5da524 100644 --- a/src/app/(root)/cust/update.tsx +++ b/src/app/(root)/cust/update.tsx @@ -14,6 +14,7 @@ import { DialogContent, DialogHeader, DialogTitle, + DialogTrigger, } from "@/components/ui/dialog" import { Field, @@ -30,8 +31,8 @@ import { SelectValue, } from "@/components/ui/select" import type { Admin } from "@/models/admin" -import type { User } from "@/models/user" import type { ProductDiscount } from "@/models/product_discount" +import type { User } from "@/models/user" // 表单验证规则 const editUserSchema = z @@ -67,18 +68,12 @@ const editUserSchema = z export type EditUserFormValues = z.infer interface EditUserDialogProps { - open: boolean - onOpenChange: (open: boolean) => void - currentUser: User | null + user: User onSuccess: () => void } -export function UpdateDialog({ - open, - onOpenChange, - currentUser, - onSuccess, -}: EditUserDialogProps) { +export function UpdateDialog({ user, onSuccess }: EditUserDialogProps) { + const [open, setOpen] = useState(false) const [admins, setAdmins] = useState([]) const [discounts, setDiscounts] = useState([]) const [isSubmitting, setIsSubmitting] = useState(false) @@ -133,24 +128,21 @@ export function UpdateDialog({ }, [open]) useEffect(() => { - if (currentUser) { + if (open) { reset({ - id: currentUser.id, - username: currentUser.username, - email: currentUser.email || "", + id: user.id, + username: user.username, + email: user.email || "", password: "", confirmPassword: "", - admin_id: currentUser.admin_id ? String(currentUser.admin_id) : "", - discount_id: currentUser.discount_id - ? String(currentUser.discount_id) - : "", - status: - currentUser.status !== undefined ? String(currentUser.status) : "", - contact_qq: currentUser.contact_qq || "", - contact_wechat: currentUser.contact_wechat || "", + admin_id: user.admin_id ? String(user.admin_id) : "", + discount_id: user.discount_id ? String(user.discount_id) : "", + status: user.status !== undefined ? String(user.status) : "", + contact_qq: user.contact_qq || "", + contact_wechat: user.contact_wechat || "", }) } - }, [currentUser, reset]) + }, [open, user, reset]) const onSubmit = async (data: EditUserFormValues) => { setIsSubmitting(true) @@ -194,7 +186,7 @@ export function UpdateDialog({ const result = await updateCust(updateData) if (result?.success) { toast.success("修改成功") - onOpenChange(false) + setOpen(false) onSuccess() } else { toast.error(result?.message || "修改失败") @@ -213,11 +205,14 @@ export function UpdateDialog({ if (!open) { reset() } - onOpenChange(open) + setOpen(open) } return ( + + + 修改用户 diff --git a/src/app/(root)/discount/page.tsx b/src/app/(root)/discount/page.tsx index 601703f..cf04579 100644 --- a/src/app/(root)/discount/page.tsx +++ b/src/app/(root)/discount/page.tsx @@ -7,6 +7,7 @@ import { getPageProductDiscount, } from "@/actions/product_discount" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { AlertDialog, AlertDialogAction, @@ -29,7 +30,7 @@ export default function DiscountPage() { ) return ( -
+ {/* 操作栏 */}
@@ -76,7 +77,7 @@ export default function DiscountPage() { ]} /> -
+ ) } diff --git a/src/app/(root)/layout.tsx b/src/app/(root)/layout.tsx index cb5e819..bb20f3d 100644 --- a/src/app/(root)/layout.tsx +++ b/src/app/(root)/layout.tsx @@ -32,7 +32,7 @@ async function Layout(props: { children: ReactNode }) { {/* 内容区域 */} -
{props.children}
+
{props.children}
) diff --git a/src/app/(root)/permissions/page.tsx b/src/app/(root)/permissions/page.tsx index 179df15..8dc4a55 100644 --- a/src/app/(root)/permissions/page.tsx +++ b/src/app/(root)/permissions/page.tsx @@ -9,6 +9,7 @@ import { Copy } from "lucide-react" import { Suspense, useCallback, useEffect, useState } from "react" import { toast } from "sonner" import { getAllPermissions } from "@/actions/permission" +import { Page } from "@/components/page" import { Button } from "@/components/ui/button" import { Table, @@ -57,9 +58,11 @@ function generateScopeCode(roots: Node[]): string { export default function PermissionsPage() { return ( - - - + + + + + ) } @@ -145,7 +148,7 @@ function PermissionTable() {
)} - +
编码 diff --git a/src/app/(root)/product/page.tsx b/src/app/(root)/product/page.tsx index 7e051e4..82e854c 100644 --- a/src/app/(root)/product/page.tsx +++ b/src/app/(root)/product/page.tsx @@ -10,6 +10,7 @@ import { getPageProductSku, } from "@/actions/product" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { SkuCodeBadge } from "@/components/products/format" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" @@ -28,10 +29,10 @@ export default function ProductPage() { ) return ( -
+ -
+ ) } diff --git a/src/app/(root)/proxy/nodes/page.tsx b/src/app/(root)/proxy/nodes/page.tsx deleted file mode 100644 index 042f051..0000000 --- a/src/app/(root)/proxy/nodes/page.tsx +++ /dev/null @@ -1,625 +0,0 @@ -"use client" -import Link from "next/link" -import { useEffect, useState } from "react" - -// 定义节点数据接口 -interface ProxyNode { - id: string - ipAddress: string - location: { - country: string - region: string - } - type: string - status: "online" | "offline" | "warning" - responseTime: number - lastCheckTime: string - pool: string - isStatic: boolean -} - -export type ProxyNodesPageProps = {} - -export default function ProxyNodesPage(props: ProxyNodesPageProps) { - const [loading, setLoading] = useState(true) - - const [nodes, setNodes] = useState([]) - const [searchTerm, setSearchTerm] = useState("") - const [filterStatus, setFilterStatus] = useState("all") - const [filterType, setFilterType] = useState("all") - const [filterPool, setFilterPool] = useState("all") - - // 模拟数据加载 - useEffect(() => { - setTimeout(() => { - setNodes([ - { - id: "ip-1", - ipAddress: "203.45.167.82", - location: { country: "美国", region: "纽约" }, - type: "数据中心", - status: "online", - responseTime: 126, - lastCheckTime: "2024-05-10 15:30:22", - pool: "北美专用池", - isStatic: true, - }, - { - id: "ip-2", - ipAddress: "185.72.193.54", - location: { country: "德国", region: "法兰克福" }, - type: "住宅", - status: "online", - responseTime: 158, - lastCheckTime: "2024-05-10 15:28:45", - pool: "欧洲高速池", - isStatic: false, - }, - { - id: "ip-3", - ipAddress: "118.96.244.105", - location: { country: "新加坡", region: "中心区" }, - type: "移动", - status: "warning", - responseTime: 312, - lastCheckTime: "2024-05-10 15:25:12", - pool: "亚太地区池", - isStatic: false, - }, - { - id: "ip-4", - ipAddress: "45.178.29.6", - location: { country: "加拿大", region: "多伦多" }, - type: "数据中心", - status: "online", - responseTime: 143, - lastCheckTime: "2024-05-10 15:23:08", - pool: "北美专用池", - isStatic: false, - }, - { - id: "ip-5", - ipAddress: "79.114.83.201", - location: { country: "英国", region: "伦敦" }, - type: "住宅", - status: "offline", - responseTime: 0, - lastCheckTime: "2024-05-10 15:18:33", - pool: "欧洲高速池", - isStatic: false, - }, - { - id: "ip-6", - ipAddress: "164.83.219.47", - location: { country: "日本", region: "东京" }, - type: "住宅", - status: "online", - responseTime: 87, - lastCheckTime: "2024-05-10 15:15:21", - pool: "亚太地区池", - isStatic: true, - }, - { - id: "ip-7", - ipAddress: "221.67.93.143", - location: { country: "中国", region: "上海" }, - type: "移动", - status: "online", - responseTime: 104, - lastCheckTime: "2024-05-10 15:10:46", - pool: "亚太地区池", - isStatic: false, - }, - { - id: "ip-8", - ipAddress: "37.209.148.72", - location: { country: "法国", region: "巴黎" }, - type: "数据中心", - status: "warning", - responseTime: 276, - lastCheckTime: "2024-05-10 15:05:19", - pool: "欧洲高速池", - isStatic: false, - }, - ]) - setLoading(false) - }, 800) - }, []) - - // 过滤节点数据 - const filteredNodes = nodes.filter(node => { - return ( - (searchTerm === "" || - node.ipAddress.includes(searchTerm) || - node.location.country.includes(searchTerm) || - node.pool.includes(searchTerm)) && - (filterStatus === "all" || - (filterStatus === "online" && node.status === "online") || - (filterStatus === "offline" && node.status === "offline") || - (filterStatus === "warning" && node.status === "warning")) && - (filterType === "all" || node.type === filterType) && - (filterPool === "all" || node.pool === filterPool) - ) - }) - - return ( -
- {/* 概览区域 - 使用色块和留白风格 */} -
- {/* 标题区域 - 简洁风格 */} -
-
-

节点列表

-
- - -
-
-

- 查看和管理所有代理IP资源,支持多维度筛选 -

-
- - {/* 统计信息区域 - 色块风格 */} -
- {/* 总IP数量 */} -
-
-
- - - -
-
-

总IP数量

-
- 152,487 -
-
-
-
- - {/* 在线IP */} -
-
-
- - - -
-
-
-

在线IP

- - 91% - -
-
- 138,954 -
-
-
-
- - {/* IP池分布 */} -
-
-
- - - -
-
-
-

IP池分布

- - 5个地区 - -
-
12
-
-
-
- - {/* 异常IP */} -
-
-
- - - -
-
-
-

异常IP

- - 需检查 - -
-
1,205
-
-
-
-
-
- - {/* 数据展示 */} -
- {/* 筛选搜索区域 */} -
-
- {/* 搜索框 */} -
- - - - -
- - {/* 筛选区域 */} -
- - - - - -
-
-
- - {/* IP表格区域 */} - {loading ? ( -
- - - - -
- ) : ( -
-
- - - - - - - - - - - - - {filteredNodes.map((node, index) => ( - - - - - - - - - - - - - - - - ))} - -
- IP地址 - - 位置 - - 类型 - - 状态 - - 响应时间 - - 所属池 - - 操作 -
-
- - - {node.ipAddress} - - {node.isStatic && ( - - 静态 - - )} -
-
-
- {node.location.country} -
-
- {node.location.region} -
-
- - {node.type} - - - - {node.status === "online" - ? "在线" - : node.status === "offline" - ? "离线" - : "异常"} - - - {node.responseTime > 0 ? ( -
- - {node.responseTime} ms - -
- ) : ( - - - )} -
- {node.pool} - - - 详情 - - -
-
- )} - - {/* 分页控制 */} -
-
-
- 显示 1 至{" "} - 8 条,共{" "} - 152,487 条结果 -
-
- -
-
-
-
-
- ) -} diff --git a/src/app/(root)/proxy/pools/page.tsx b/src/app/(root)/proxy/pools/page.tsx deleted file mode 100644 index 0bb1d89..0000000 --- a/src/app/(root)/proxy/pools/page.tsx +++ /dev/null @@ -1,527 +0,0 @@ -"use client" -import Link from "next/link" -import { useEffect, useState } from "react" - -export type ProxyPoolsPageProps = {} - -// 定义IP池接口 -interface ProxyPool { - id: string - name: string - description: string - ips: number - activeIps: number - region: string - type: string - createdAt: string - status: "active" | "inactive" | "maintenance" -} - -export default function ProxyPoolsPage(props: ProxyPoolsPageProps) { - const [pools, setPools] = useState([]) - const [loading, setLoading] = useState(true) - const [searchTerm, setSearchTerm] = useState("") - const [filterStatus, setFilterStatus] = useState("all") - const [filterRegion, setFilterRegion] = useState("all") - - // 模拟数据加载 - useEffect(() => { - // 实际项目中替换为API调用 - setTimeout(() => { - setPools([ - { - id: "pool-1", - name: "全球通用池", - description: "包含全球多个地区的高质量IP", - ips: 5000, - activeIps: 4328, - region: "全球", - type: "住宅IP", - createdAt: "2023-10-15", - status: "active", - }, - { - id: "pool-2", - name: "北美专用池", - description: "美国和加拿大地区专用IP池", - ips: 3200, - activeIps: 2950, - region: "北美", - type: "数据中心IP", - createdAt: "2023-11-02", - status: "active", - }, - { - id: "pool-3", - name: "欧洲高速池", - description: "欧洲地区高速稳定IP", - ips: 2800, - activeIps: 2180, - region: "欧洲", - type: "住宅IP", - createdAt: "2023-09-28", - status: "active", - }, - { - id: "pool-4", - name: "亚太地区池", - description: "亚洲和太平洋地区IP", - ips: 4200, - activeIps: 3890, - region: "亚太", - type: "移动IP", - createdAt: "2023-12-05", - status: "maintenance", - }, - { - id: "pool-5", - name: "电商专用池", - description: "适用于电商平台的IP池", - ips: 1500, - activeIps: 1200, - region: "全球", - type: "住宅IP", - createdAt: "2024-01-10", - status: "inactive", - }, - ]) - setLoading(false) - }, 800) - }, []) - - // 过滤IP池 - const filteredPools = pools.filter(pool => { - return ( - (searchTerm === "" || - pool.name.toLowerCase().includes(searchTerm.toLowerCase()) || - pool.description.toLowerCase().includes(searchTerm.toLowerCase())) && - (filterStatus === "all" || pool.status === filterStatus) && - (filterRegion === "all" || pool.region === filterRegion) - ) - }) - - // 获取状态颜色和文本 - const getStatusInfo = (status: string) => { - switch (status) { - case "active": - return { color: "bg-green-100 text-green-800", text: "运行中" } - case "inactive": - return { color: "bg-gray-100 text-gray-800", text: "未启用" } - case "maintenance": - return { color: "bg-yellow-100 text-yellow-800", text: "维护中" } - default: - return { color: "bg-gray-100 text-gray-800", text: "未知" } - } - } - - return ( -
- {/* 页面标题 */} -
-
-

IP池管理

-

管理和配置代理IP池

-
- -
- - {/* 筛选和搜索工具栏 */} -
-
-
- setSearchTerm(e.target.value)} - /> - - - -
- -
-
- - -
-
- -
- - -
-
-
- - {/* IP池列表 */} -
- {loading ? ( -
- - - - -
- ) : filteredPools.length === 0 ? ( -
- - - -

没有找到匹配的IP池

-
- ) : ( -
- - - - - - - - - - - - - - {filteredPools.map(pool => { - const statusInfo = getStatusInfo(pool.status) - return ( - - - - - - - - - - ) - })} - -
- IP池名称 - - IP概况 - - 地区 - - IP类型 - - 状态 - - 创建日期 - - 操作 -
-
-
-
- {pool.name} -
-
- {pool.description} -
-
-
-
-
- 总IP: {pool.ips.toLocaleString()} -
-
- 活跃IP: {pool.activeIps.toLocaleString()} -
-
-
-
-
-
- {pool.region} -
-
-
{pool.type}
-
- - {statusInfo.text} - - - {pool.createdAt} - - - 详情 - - - 编辑 - - -
-
- )} -
- - {/* 数据卡片概览 */} -
-
-
-
- - - -
-
-

总IP池

-
{pools.length}
-
-
-
-
- 活跃IP池 - - {pools.filter(p => p.status === "active").length} - -
-
-
- -
-
-
- - - -
-
-

总IP数量

-
- {pools - .reduce((sum, pool) => sum + pool.ips, 0) - .toLocaleString()} -
-
-
-
-
- 活跃IP - - {pools - .reduce((sum, pool) => sum + pool.activeIps, 0) - .toLocaleString()} - -
-
-
- -
-
-
- - - -
-
-

IP利用率

-
- {Math.round( - (pools.reduce((sum, pool) => sum + pool.activeIps, 0) / - pools.reduce((sum, pool) => sum + pool.ips, 0)) * - 100, - )} - % -
-
-
-
-
-
sum + pool.activeIps, 0) / - pools.reduce((sum, pool) => sum + pool.ips, 0)) * - 100 - }%`, - }} - >
-
-
-
-
- - {/* 操作指南 */} -
-
-
- - - -
-
-

- IP池是管理IP资源的基础单位。您可以创建不同用途的IP池,并为客户分配相应的访问权限。 -

-

- - 查看文档 - -

-
-
-
-
- ) -} diff --git a/src/app/(root)/resources/page.tsx b/src/app/(root)/resources/page.tsx index 7d55e19..de53f65 100644 --- a/src/app/(root)/resources/page.tsx +++ b/src/app/(root)/resources/page.tsx @@ -12,6 +12,7 @@ import { updateResource, } from "@/actions/resources" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { @@ -169,22 +170,17 @@ function getTodayUsage(lastAt: Date | null | undefined, daily: number) { export default function ResourcesPage() { return ( -
+ - - + + 短效套餐 - + 长效套餐 + @@ -192,7 +188,7 @@ export default function ResourcesPage() { -
+ ) } @@ -422,7 +418,7 @@ function ResourceList({ resourceType }: ResourceListProps) { return (
- +
getPageRole({ page, size })) return ( -
+ {/* 操作栏 */}
@@ -78,7 +79,7 @@ export default function RolesPage() { ]} /> -
+ ) } diff --git a/src/app/(root)/trade/page.tsx b/src/app/(root)/trade/page.tsx index cd15178..9a0ef6d 100644 --- a/src/app/(root)/trade/page.tsx +++ b/src/app/(root)/trade/page.tsx @@ -7,6 +7,7 @@ import { Controller, useForm } from "react-hook-form" import { z } from "zod" import { getPageTrade } from "@/actions/trade" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Button } from "@/components/ui/button" import { Field, @@ -111,8 +112,9 @@ export default function TradePage() { }) return ( -
- + + {/* 筛选表单 */} +
-
+
) } diff --git a/src/app/(root)/user/page.tsx b/src/app/(root)/user/page.tsx index 0b86b99..f27c917 100644 --- a/src/app/(root)/user/page.tsx +++ b/src/app/(root)/user/page.tsx @@ -7,6 +7,7 @@ import { z } from "zod" import { bindAdmin, getPageUserPage } from "@/actions/user" import { Auth } from "@/components/auth" import { DataTable, useDataTable } from "@/components/data-table" +import { Page } from "@/components/page" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { @@ -60,8 +61,8 @@ export default function UserPage() { }) return ( -
- + +
-
+
) } diff --git a/src/components/data-table/component.tsx b/src/components/data-table/component.tsx index a775744..35a537e 100644 --- a/src/components/data-table/component.tsx +++ b/src/components/data-table/component.tsx @@ -10,11 +10,11 @@ import { Loader } from "lucide-react" import type { CSSProperties } from "react" import { Pagination, type PaginationProps } from "@/components/ui/pagination" import { + Table, TableBody, TableCell, TableHead, TableHeader, - Table as TableRoot, TableRow, } from "@/components/ui/table" import { cn } from "@/lib/utils" @@ -75,31 +75,32 @@ export function DataTable>( }, }) - const pinStyle = (column: Column) => { + const pinStyle = (column: Column, header?: boolean) => { const pinned = column.getIsPinned() if (!pinned) return {} return { - position: pinned ? ("sticky" as const) : undefined, - backgroundColor: "white", - zIndex: 1, - ...{ - left: { - left: column.getStart(pinned), - boxShadow: "inset 1px 0 var(--border)", - }, - right: { - right: column.getAfter(pinned), - boxShadow: "inset 1px 0 var(--border)", - }, - }[pinned], - } as CSSProperties + left: { + left: column.getStart(pinned), + boxShadow: header + ? "inset -1px -1px var(--border)" + : "inset -1px 0 var(--border)", + } as CSSProperties, + right: { + right: column.getAfter(pinned), + boxShadow: header + ? "inset 1px -1px var(--border)" + : "inset 1px 0 var(--border)", + } as CSSProperties, + }[pinned] } return ( -
+
{/* 数据表 */} -
- +
+ {table.getHeaderGroups().map(group => ( @@ -107,7 +108,8 @@ export function DataTable>( {header.isPlaceholder ? null @@ -147,7 +149,11 @@ export function DataTable>( className={cn("h-14", props.classNames?.dataRow)} > {row.getVisibleCells().map(cell => ( - + {flexRender( cell.column.columnDef.cell, cell.getContext(), @@ -158,9 +164,9 @@ export function DataTable>( )) )} - +
{props.status === "load" && ( -
+
加载中
@@ -168,7 +174,9 @@ export function DataTable>(
{/* 分页器 */} - {props.pagination && } + {props.pagination && ( + + )}
) } diff --git a/src/components/page.tsx b/src/components/page.tsx index b75452a..fbe1d58 100644 --- a/src/components/page.tsx +++ b/src/components/page.tsx @@ -1,5 +1,10 @@ import type { ReactNode } from "react" +import { cn } from "@/lib/utils" -export function Page(props: { children: ReactNode }) { - return
{props.children}
+export function Page(props: { className?: string; children: ReactNode }) { + return ( +
+ {props.children} +
+ ) } diff --git a/src/components/ui/table.tsx b/src/components/ui/table.tsx index 450c7c6..6680518 100644 --- a/src/components/ui/table.tsx +++ b/src/components/ui/table.tsx @@ -8,11 +8,11 @@ function Table({ className, ...props }: React.ComponentProps<"table">) { return (
@@ -23,7 +23,7 @@ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) { return ( ) diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx index a233bbc..70e78d9 100644 --- a/src/components/ui/tabs.tsx +++ b/src/components/ui/tabs.tsx @@ -1,34 +1,56 @@ "use client" -import * as TabsPrimitive from "@radix-ui/react-tabs" +import { cva, type VariantProps } from "class-variance-authority" +import { Tabs as TabsPrimitive } from "radix-ui" import type * as React from "react" import { cn } from "@/lib/utils" function Tabs({ className, + orientation = "horizontal", ...props }: React.ComponentProps) { return ( ) } +const tabsListVariants = cva( + "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none", + { + variants: { + variant: { + default: "bg-muted", + line: "gap-1 bg-transparent", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +) + function TabsList({ className, + variant = "default", ...props -}: React.ComponentProps) { +}: React.ComponentProps & + VariantProps) { return ( ) @@ -42,7 +64,10 @@ function TabsTrigger({