From 616901acddf6705877249335ae7b5b0a03852bf0 Mon Sep 17 00:00:00 2001 From: Eamon <17516219072@163.com> Date: Thu, 14 May 2026 16:04:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=A1=86=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E4=B8=80=E9=94=AE=E6=B8=85=E9=99=A4=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(root)/balance/page.tsx | 24 +++++++-- src/app/(root)/batch/page.tsx | 36 +++++++++---- src/app/(root)/billing/page.tsx | 71 ++++++++++++------------ src/app/(root)/channel/page.tsx | 39 ++++++++++---- src/app/(root)/client/cust/page.tsx | 12 ++--- src/app/(root)/cust/create.tsx | 15 ++++-- src/app/(root)/cust/page.tsx | 22 +++++--- src/app/(root)/cust/update.tsx | 10 ++-- src/app/(root)/resources/page.tsx | 57 +++++++++++++------- src/app/(root)/trade/page.tsx | 10 ++-- src/app/(root)/user/page.tsx | 2 +- src/components/ui/input.tsx | 83 ++++++++++++++++++++++++----- 12 files changed, 263 insertions(+), 118 deletions(-) diff --git a/src/app/(root)/balance/page.tsx b/src/app/(root)/balance/page.tsx index b2fc9c0..f689e5e 100644 --- a/src/app/(root)/balance/page.tsx +++ b/src/app/(root)/balance/page.tsx @@ -1,6 +1,7 @@ "use client" import { zodResolver } from "@hookform/resolvers/zod" import { format } from "date-fns" +import Link from "next/link" import { useRouter, useSearchParams } from "next/navigation" import { Suspense, useCallback } from "react" import { Controller, useForm } from "react-hook-form" @@ -95,7 +96,7 @@ export default function BalancePage() { className="w-40 flex-none" > 会员号 - + {fieldState.error?.message} )} @@ -109,7 +110,7 @@ export default function BalancePage() { className="w-40 flex-none" > 账单号 - + {fieldState.error?.message} )} @@ -123,7 +124,7 @@ export default function BalancePage() { className="w-40 flex-none" > 开始时间 - + {fieldState.error?.message} )} @@ -137,7 +138,7 @@ export default function BalancePage() { className="w-40 flex-none" > 结束时间 - + {fieldState.error?.message} )} @@ -174,7 +175,20 @@ export default function BalancePage() { }, { header: "账单编号", - accessorFn: row => row.bill?.bill_no || "", + accessorKey: "bill?.bill_no", + cell: ({ row }) => { + const bill_no = row.original.bill?.bill_no + return ( + + {bill_no} + + ) + }, }, { header: "管理员", diff --git a/src/app/(root)/batch/page.tsx b/src/app/(root)/batch/page.tsx index 350bf20..25d6941 100644 --- a/src/app/(root)/batch/page.tsx +++ b/src/app/(root)/batch/page.tsx @@ -120,7 +120,7 @@ export default function BatchPage() { className="w-40 flex-none" > 提取编号 - + {fieldState.error?.message} )} @@ -134,7 +134,7 @@ export default function BatchPage() { className="w-40 flex-none" > 套餐号 - + {fieldState.error?.message} )} @@ -148,7 +148,7 @@ export default function BatchPage() { className="w-40 flex-none" > 会员号 - + {fieldState.error?.message} )} @@ -163,7 +163,7 @@ export default function BatchPage() { className="w-32 flex-none" > 省份 - + {fieldState.error?.message} )} @@ -178,7 +178,7 @@ export default function BatchPage() { className="w-32 flex-none" > 城市 - + {fieldState.error?.message} )} @@ -215,7 +215,7 @@ export default function BatchPage() { className="w-40 flex-none" > 开始时间 - + {fieldState.error?.message} )} @@ -230,7 +230,7 @@ export default function BatchPage() { className="w-40 flex-none" > 结束时间 - + {fieldState.error?.message} )} @@ -277,17 +277,33 @@ export default function BatchPage() { const resourceNo = row.original.resource?.resource_no return ( {resourceNo} ) }, }, - { header: "提取编号", accessorKey: "batch_no" }, + { + header: "提取编号", + accessorKey: "batch_no", + cell: ({ row }) => { + const batch_no = row.original.batch_no + return ( + + {batch_no} + + ) + }, + }, { header: "省份", accessorKey: "prov" }, { header: "城市", accessorKey: "city" }, { header: "用户IP", accessorKey: "ip" }, diff --git a/src/app/(root)/billing/page.tsx b/src/app/(root)/billing/page.tsx index 5403485..2731e00 100644 --- a/src/app/(root)/billing/page.tsx +++ b/src/app/(root)/billing/page.tsx @@ -81,7 +81,8 @@ type FilterSchema = z.infer export default function BillingPage() { const searchParams = useSearchParams() const innerNo = searchParams.get("inner_no") - console.log(innerNo, "innerNo") + const billNo = searchParams.get("bill_no") + const resourceNo = searchParams.get("resource_no") const [skuOptions, setSkuOptions] = useState([]) const [loading, setLoading] = useState(true) const [skuProductCode, setSkuProductCode] = useState( @@ -92,12 +93,12 @@ export default function BillingPage() { const { control, handleSubmit, reset, getValues } = useForm({ resolver: zodResolver(filterSchema), defaultValues: { - bill_no: "", + bill_no: billNo || "", inner_no: innerNo || "", created_at_start: "", created_at_end: "", phone: "", - resource_no: "", + resource_no: resourceNo || "", sku_code: "all", product_code: "", }, @@ -133,28 +134,23 @@ export default function BillingPage() { const loadData = (page: number, size: number) => { const result: FilterValues = {} - - if (innerNo) { - result.trade_inner_no = innerNo - } else { - const filters = getValues() - if (filters.phone?.trim()) result.user_phone = filters.phone.trim() - if (filters.inner_no?.trim()) - result.trade_inner_no = filters.inner_no.trim() - if (filters.bill_no?.trim()) result.bill_no = filters.bill_no.trim() - if (filters.resource_no?.trim()) - result.resource_no = filters.resource_no.trim() - if (filters.product_code && filters.product_code !== ProductCode.All) { - result.product_code = filters.product_code - } - if (filters.sku_code && filters.sku_code !== "all") { - result.sku_code = filters.sku_code - } - if (filters.created_at_start) - result.created_at_start = new Date(filters.created_at_start) - if (filters.created_at_end) - result.created_at_end = new Date(filters.created_at_end) + const filters = getValues() + if (filters.phone?.trim()) result.user_phone = filters.phone.trim() + if (filters.inner_no?.trim()) + result.trade_inner_no = filters.inner_no.trim() + if (filters.bill_no?.trim()) result.bill_no = filters.bill_no.trim() + if (filters.resource_no?.trim()) + result.resource_no = filters.resource_no.trim() + if (filters.product_code && filters.product_code !== ProductCode.All) { + result.product_code = filters.product_code } + if (filters.sku_code && filters.sku_code !== "all") { + result.sku_code = filters.sku_code + } + if (filters.created_at_start) + result.created_at_start = new Date(filters.created_at_start) + if (filters.created_at_end) + result.created_at_end = new Date(filters.created_at_end) return getPageBill({ page, size, @@ -196,7 +192,7 @@ export default function BillingPage() { className="w-40 flex-none" > 会员号 - + {fieldState.error?.message} )} @@ -210,7 +206,7 @@ export default function BillingPage() { className="w-40 flex-none" > 套餐号 - + {fieldState.error?.message} )} @@ -224,7 +220,7 @@ export default function BillingPage() { className="w-40 flex-none" > 账单号 - + {fieldState.error?.message} )} @@ -238,7 +234,7 @@ export default function BillingPage() { className="w-40 flex-none" > 订单号 - + {fieldState.error?.message} )} @@ -319,7 +315,7 @@ export default function BillingPage() { className="w-40 flex-none" > 开始时间 - + {fieldState.error?.message} )} @@ -334,7 +330,7 @@ export default function BillingPage() { className="w-40 flex-none" > 结束时间 - + {fieldState.error?.message} )} @@ -373,7 +369,7 @@ export default function BillingPage() { href={`/resources?resource_no=${resource_no}`} target="_blank" rel="noopener noreferrer" - className="text-blue-600 hover:underline cursor-pointer" + className="text-blue-600" > {resource_no} @@ -468,7 +464,7 @@ export default function BillingPage() { href={`./balance?bill_no=${billNo}`} target="_blank" rel="noopener noreferrer" - className="text-blue-600 hover:underline cursor-pointer" + className="text-blue-600" > {billNo} @@ -509,7 +505,16 @@ export default function BillingPage() { )} -
{bill.trade?.inner_no}
+
+ + {bill.trade?.inner_no} + +
) }, diff --git a/src/app/(root)/channel/page.tsx b/src/app/(root)/channel/page.tsx index c0caa4b..003be2d 100644 --- a/src/app/(root)/channel/page.tsx +++ b/src/app/(root)/channel/page.tsx @@ -68,11 +68,12 @@ const ispMap: Record = { export default function ChannelPage() { const searchParams = useSearchParams() const resourceNo = searchParams.get("resource_no") + const batch_no = searchParams.get("batch_no") const router = useRouter() const { control, handleSubmit, reset, getValues } = useForm({ resolver: zodResolver(filterSchema), defaultValues: { - batch_no: "", + batch_no: batch_no || "", user_phone: "", resource_no: resourceNo || "", proxy_port: "", @@ -119,7 +120,7 @@ export default function ChannelPage() { className="w-40 flex-none" > 提取编号 - + {fieldState.error?.message} )} @@ -133,7 +134,7 @@ export default function ChannelPage() { className="w-40 flex-none" > 会员号 - + {fieldState.error?.message} )} @@ -147,7 +148,7 @@ export default function ChannelPage() { className="w-40 flex-none" > 套餐号 - + {fieldState.error?.message} )} @@ -161,7 +162,7 @@ export default function ChannelPage() { className="w-40 flex-none" > 代理IP - + {fieldState.error?.message} )} @@ -175,7 +176,7 @@ export default function ChannelPage() { className="w-32 flex-none" > 代理端口 - + {fieldState.error?.message} )} @@ -189,7 +190,7 @@ export default function ChannelPage() { className="w-40 flex-none" > 节点 - + {fieldState.error?.message} )} @@ -203,7 +204,7 @@ export default function ChannelPage() { className="w-40 flex-none" > 开始时间 - + {fieldState.error?.message} )} @@ -217,7 +218,7 @@ export default function ChannelPage() { className="w-40 flex-none" > 结束时间 - + {fieldState.error?.message} )} @@ -257,7 +258,23 @@ export default function ChannelPage() { header: "会员号", accessorFn: row => row.user?.phone || "-", }, - { header: "套餐号", accessorKey: "resource.resource_no" }, + { + header: "套餐号", + accessorKey: "resource.resource_no", + cell: ({ row }) => { + const resource_no = row.original.resource?.resource_no + return ( + + {resource_no} + + ) + }, + }, { header: "提取编号", accessorKey: "batch_no", @@ -268,7 +285,7 @@ export default function ChannelPage() { href={`./batch?batch_no=${batchNo}`} target="_blank" rel="noopener noreferrer" - className="text-blue-600 hover:underline cursor-pointer" + className="text-blue-600" > {batchNo} diff --git a/src/app/(root)/client/cust/page.tsx b/src/app/(root)/client/cust/page.tsx index 47c3830..151b544 100644 --- a/src/app/(root)/client/cust/page.tsx +++ b/src/app/(root)/client/cust/page.tsx @@ -113,7 +113,7 @@ export default function UserQueryPage() { render={({ field, fieldState }) => ( 手机号 - + {fieldState.error?.message} )} @@ -125,18 +125,18 @@ export default function UserQueryPage() { render={({ field, fieldState }) => ( 姓名 - + {fieldState.error?.message} )} /> - - - + - + + + diff --git a/src/app/(root)/cust/create.tsx b/src/app/(root)/cust/create.tsx index c5abcba..10c555a 100644 --- a/src/app/(root)/cust/create.tsx +++ b/src/app/(root)/cust/create.tsx @@ -214,6 +214,7 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) { {...field} placeholder="请输入用户名" autoComplete="off" + clearable /> {fieldState.error?.message} @@ -225,7 +226,7 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) { render={({ field, fieldState }) => ( 手机号 * - + {fieldState.error?.message} )} @@ -246,6 +247,7 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) { {...field} type="password" placeholder="请输入密码(至少6位)" + clearable /> {fieldState.error?.message} @@ -261,6 +263,7 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) { {...field} type="password" placeholder="请再次输入密码" + clearable /> {fieldState.error?.message} @@ -275,7 +278,7 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) { render={({ field, fieldState }) => ( 邮箱 - + {fieldState.error?.message} )} @@ -311,7 +314,7 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) { render={({ field, fieldState }) => ( QQ联系方式 - + {fieldState.error?.message} )} @@ -322,7 +325,11 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) { render={({ field, fieldState }) => ( 微信/联系方式 - + {fieldState.error?.message} )} diff --git a/src/app/(root)/cust/page.tsx b/src/app/(root)/cust/page.tsx index fc7f86e..0ef51f7 100644 --- a/src/app/(root)/cust/page.tsx +++ b/src/app/(root)/cust/page.tsx @@ -99,6 +99,8 @@ export default function CustPage() { (page: number, size: number) => { const result: FilterValues = {} const filters = getValues() + console.log(filters, "filters") + if (filters.account?.trim()) result.account = filters.account.trim() if (filters.name?.trim()) result.name = filters.name.trim() if (filters.identified && filters.identified !== "all") @@ -134,7 +136,11 @@ export default function CustPage() { render={({ field, fieldState }) => ( 账号/手机号/邮箱 - + {fieldState.error?.message} )} @@ -146,7 +152,7 @@ export default function CustPage() { render={({ field, fieldState }) => ( 姓名 - + {fieldState.error?.message} )} @@ -200,7 +206,7 @@ export default function CustPage() { render={({ field, fieldState }) => ( 开始时间 - + {fieldState.error?.message} )} @@ -212,14 +218,12 @@ export default function CustPage() { render={({ field, fieldState }) => ( 结束时间 - + {fieldState.error?.message} )} /> - - - + - + + + diff --git a/src/app/(root)/cust/update.tsx b/src/app/(root)/cust/update.tsx index b5da524..88108ce 100644 --- a/src/app/(root)/cust/update.tsx +++ b/src/app/(root)/cust/update.tsx @@ -226,7 +226,7 @@ export function UpdateDialog({ user, onSuccess }: EditUserDialogProps) { render={({ field, fieldState }) => ( 用户名 - + {fieldState.error?.message} )} @@ -238,7 +238,7 @@ export function UpdateDialog({ user, onSuccess }: EditUserDialogProps) { render={({ field, fieldState }) => ( 邮箱 - + {fieldState.error?.message} )} @@ -254,6 +254,7 @@ export function UpdateDialog({ user, onSuccess }: EditUserDialogProps) { {...field} type="password" placeholder="选填,修改请输入新密码(至少6位)" + clearable /> {fieldState.error?.message} @@ -270,6 +271,7 @@ export function UpdateDialog({ user, onSuccess }: EditUserDialogProps) { {...field} type="password" placeholder="请再次输入密码" + clearable /> {fieldState.error?.message} @@ -348,7 +350,7 @@ export function UpdateDialog({ user, onSuccess }: EditUserDialogProps) { render={({ field }) => ( QQ - + )} /> @@ -359,7 +361,7 @@ export function UpdateDialog({ user, onSuccess }: EditUserDialogProps) { render={({ field }) => ( 微信 - + )} /> diff --git a/src/app/(root)/resources/page.tsx b/src/app/(root)/resources/page.tsx index 74b8b63..44d3c0f 100644 --- a/src/app/(root)/resources/page.tsx +++ b/src/app/(root)/resources/page.tsx @@ -2,7 +2,6 @@ import { zodResolver } from "@hookform/resolvers/zod" import { format, isBefore, isSameDay } from "date-fns" import { Box, Loader2, Timer } from "lucide-react" -import Link from "next/link" import { useRouter, useSearchParams } from "next/navigation" import { Suspense, useCallback, useMemo, useState } from "react" import { Controller, useForm } from "react-hook-form" @@ -17,6 +16,12 @@ 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 { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" import { Field, FieldError, @@ -249,10 +254,6 @@ function ResourceList({ resourceType }: ResourceListProps) { const table = useDataTable(fetchResources) - // const refreshTable = useCallback(() => { - // setFilters(prev => ({ ...prev })) - // }, []) - const handleStatusChange = useCallback( async (resource: Resources, newStatusValue: string) => { const newActive = newStatusValue === "0" @@ -322,14 +323,34 @@ function ResourceList({ resourceType }: ResourceListProps) {
{name}
- - {resourceNo} - + + + {resourceNo} + + + { + router.push(`/billing?resource_no=${resourceNo}`) + }} + > + 账单详情 + + { + router.push(`/batch?resource_no=${resourceNo}`) + }} + > + 提取记录 + + { + router.push(`/channel?resource_no=${resourceNo}`) + }} + > + IP管理 + + +
@@ -458,7 +479,7 @@ function ResourceList({ resourceType }: ResourceListProps) { }, }, ], - [isLong, updatingId, handleStatusChange, handleCheckipChange], + [isLong, updatingId, handleStatusChange, handleCheckipChange, router], ) return ( @@ -474,7 +495,7 @@ function ResourceList({ resourceType }: ResourceListProps) { className="w-40 flex-none" > 会员号 - + {fieldState.error?.message} )} @@ -488,7 +509,7 @@ function ResourceList({ resourceType }: ResourceListProps) { className="w-40 flex-none" > 套餐号 - + {fieldState.error?.message} )} @@ -562,7 +583,7 @@ function ResourceList({ resourceType }: ResourceListProps) { className="w-40 flex-none" > 开始时间 - + {fieldState.error?.message} )} @@ -576,7 +597,7 @@ function ResourceList({ resourceType }: ResourceListProps) { className="w-40 flex-none" > 结束时间 - + {fieldState.error?.message} )} diff --git a/src/app/(root)/trade/page.tsx b/src/app/(root)/trade/page.tsx index fb706c7..93cc8a2 100644 --- a/src/app/(root)/trade/page.tsx +++ b/src/app/(root)/trade/page.tsx @@ -117,7 +117,7 @@ export default function TradePage() { render={({ field, fieldState }) => ( 会员号 - + {fieldState.error?.message} )} @@ -129,7 +129,7 @@ export default function TradePage() { render={({ field, fieldState }) => ( 订单号 - + {fieldState.error?.message} )} @@ -208,7 +208,7 @@ export default function TradePage() { render={({ field, fieldState }) => ( 开始时间 - + {fieldState.error?.message} )} @@ -220,7 +220,7 @@ export default function TradePage() { render={({ field, fieldState }) => ( 结束时间 - + {fieldState.error?.message} )} @@ -265,7 +265,7 @@ export default function TradePage() { href={`/billing?inner_no=${innerNo}`} target="_blank" rel="noopener noreferrer" - className="text-blue-600 hover:underline cursor-pointer" + className="text-blue-600" > {innerNo} diff --git a/src/app/(root)/user/page.tsx b/src/app/(root)/user/page.tsx index fa5aca3..bd480c5 100644 --- a/src/app/(root)/user/page.tsx +++ b/src/app/(root)/user/page.tsx @@ -65,7 +65,7 @@ export default function UserPage() { render={({ field, fieldState }) => ( 手机号 - + {fieldState.error?.message} )} diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx index 3d7abfa..8ccf51c 100644 --- a/src/components/ui/input.tsx +++ b/src/components/ui/input.tsx @@ -1,20 +1,77 @@ -import type * as React from "react" - +import { X } from "lucide-react" +import * as React from "react" import { cn } from "@/lib/utils" -function Input({ className, type, ...props }: React.ComponentProps<"input">) { +interface InputProps extends React.ComponentProps<"input"> { + clearable?: boolean + onClear?: () => void +} + +function Input({ + className, + type, + clearable, + onClear, + value, + onChange, + ...props +}: InputProps) { + const [showClear, setShowClear] = React.useState(false) + const inputRef = React.useRef(null) + + const hasValue = + value !== undefined && value !== null && String(value).length > 0 + + // 监听输入框焦点状态 + const handleFocus = () => setShowClear(true) + const handleBlur = () => setShowClear(false) + + const handleClear = () => { + if (onClear) { + onClear() + } else if (onChange) { + // 触发 React 的 change 事件 + const event = { + target: { value: "" }, + currentTarget: { value: "" }, + } as React.ChangeEvent + onChange(event) + } + // 清空后重新聚焦 + inputRef.current?.focus() + } + return ( - + + {clearable && hasValue && showClear && ( + )} - {...props} - /> + ) }