diff --git a/src/actions/cust.ts b/src/actions/cust.ts index 066d94b..13e9fb5 100644 --- a/src/actions/cust.ts +++ b/src/actions/cust.ts @@ -2,7 +2,16 @@ import type { PageRecord } from "@/lib/api" import type { User } from "@/models/user" import { callByUser } from "./base" -export async function getPageCusts(params: { page: number; size: number }) { +export async function getPageCusts(params: { + page: number + size: number + account?: string + name?: string + identified?: boolean + enabled?: boolean + created_at_start?: Date + created_at_end?: Date +}) { return callByUser>("/api/admin/user/page", params) } export async function updateCust(data: { diff --git a/src/app/(root)/_navigation/index.tsx b/src/app/(root)/_navigation/index.tsx index 9045a70..adfbfaf 100644 --- a/src/app/(root)/_navigation/index.tsx +++ b/src/app/(root)/_navigation/index.tsx @@ -6,7 +6,6 @@ import { ChevronsRight, CircleDollarSign, ClipboardList, - ComputerIcon, ContactRound, DollarSign, DoorClosedIcon, @@ -24,7 +23,7 @@ import { } from "lucide-react" import Link from "next/link" import { usePathname } from "next/navigation" -import { createContext, type ReactNode, Suspense, useContext, useState } from "react" +import { createContext, type ReactNode, useContext, useState } from "react" import { twJoin } from "tailwind-merge" import { Auth } from "@/components/auth" import { Button } from "@/components/ui/button" @@ -307,7 +306,7 @@ export default function Navigation() { )} > {/*Logo 区域 */} - + {/* Navigation Menu */} diff --git a/src/app/(root)/_navigation/logo.tsx b/src/app/(root)/_navigation/logo.tsx index c194b14..d7c3a81 100644 --- a/src/app/(root)/_navigation/logo.tsx +++ b/src/app/(root)/_navigation/logo.tsx @@ -1,10 +1,15 @@ - +"use client" import { ComputerIcon } from "lucide-react" +import { useEffect, useState } from "react" import { getNodeEnv } from "@/actions/env" import { cn } from "@/lib/utils" -export default async function Logo(props: { collapsed: boolean }) { - const env = await getNodeEnv() +export default function Logo(props: { collapsed: boolean }) { + const [env, setEnv] = useState("") + + useEffect(() => { + getNodeEnv().then(setEnv) + }, []) return (
(null) - const notificationRef = useRef(null) // 处理点击外部关闭下拉菜单 useEffect(() => { @@ -33,12 +31,6 @@ export default function Appbar(props: { admin: Admin }) { ) { setShowDropdown(false) } - if ( - notificationRef.current && - !notificationRef.current.contains(event.target as Node) - ) { - setShowNotifications(false) - } } document.addEventListener("mousedown", handleClickOutside) @@ -51,7 +43,7 @@ export default function Appbar(props: { admin: Admin }) { const filteredPaths = paths.filter(path => !hiddenSegments.includes(path)) const breadcrumbs = [ { path: "/", label: "首页" }, - ...filteredPaths.map((path, index) => { + ...filteredPaths.map(path => { const originalIndex = paths.findIndex(p => p === path) const url = `/${paths.slice(0, originalIndex + 1).join("/")}` const label = getBreadcrumbLabel(path) diff --git a/src/app/(root)/balance/page.tsx b/src/app/(root)/balance/page.tsx index 0751584..b2fc9c0 100644 --- a/src/app/(root)/balance/page.tsx +++ b/src/app/(root)/balance/page.tsx @@ -1,19 +1,15 @@ "use client" import { zodResolver } from "@hookform/resolvers/zod" import { format } from "date-fns" -import { Suspense, useCallback, useState } from "react" +import { useRouter, useSearchParams } from "next/navigation" +import { Suspense, useCallback } from "react" 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, - FieldError, - FieldGroup, - FieldLabel, -} from "@/components/ui/field" +import { Field, FieldError, FieldLabel } from "@/components/ui/field" import { Input } from "@/components/ui/input" import type { Balance } from "@/models/balance" @@ -51,12 +47,14 @@ const filterSchema = z type FormValues = z.infer export default function BalancePage() { - const [filters, setFilters] = useState({}) - const { control, handleSubmit, reset } = useForm({ + const searchParams = useSearchParams() + const billNo = searchParams.get("bill_no") + const router = useRouter() + const { control, handleSubmit, reset, getValues } = useForm({ resolver: zodResolver(filterSchema), defaultValues: { phone: "", - bill_no: "", + bill_no: billNo || "", admin_id: "", created_at_start: "", created_at_end: "", @@ -64,21 +62,23 @@ export default function BalancePage() { }) const fetchUsers = useCallback( - (page: number, size: number) => getPageBalance({ page, size, ...filters }), - [filters], + (page: number, size: number) => { + const result: FilterValues = {} + const filters = getValues() + if (filters.phone?.trim()) result.user_phone = filters.phone.trim() + if (filters.bill_no?.trim()) result.bill_no = filters.bill_no.trim() + 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 getPageBalance({ page, size, ...result }) + }, + [getValues], ) const table = useDataTable(fetchUsers) - const onFilter = handleSubmit(data => { - const result: FilterValues = {} - if (data.phone?.trim()) result.user_phone = data.phone.trim() - if (data.bill_no?.trim()) result.bill_no = data.bill_no.trim() - if (data.created_at_start) - result.created_at_start = new Date(data.created_at_start) - if (data.created_at_end) - result.created_at_end = new Date(data.created_at_end) - setFilters(result) + const onFilter = handleSubmit(() => { table.pagination.onPageChange(1) }) @@ -148,8 +148,14 @@ export default function BalancePage() { type="button" variant="outline" onClick={() => { - reset() - setFilters({}) + reset({ + phone: "", + bill_no: "", + admin_id: "", + created_at_start: "", + created_at_end: "", + }) + router.replace("./balance") table.pagination.onPageChange(1) }} > @@ -218,7 +224,10 @@ export default function BalancePage() { header: "创建时间", accessorKey: "created_at", cell: ({ row }) => - format(new Date(row.original.created_at), "yyyy-MM-dd HH:mm"), + format( + new Date(row.original.created_at), + "yyyy-MM-dd HH:mm:ss", + ), }, ]} /> diff --git a/src/app/(root)/batch/page.tsx b/src/app/(root)/batch/page.tsx index 4632cb5..350bf20 100644 --- a/src/app/(root)/batch/page.tsx +++ b/src/app/(root)/batch/page.tsx @@ -1,7 +1,9 @@ "use client" import { zodResolver } from "@hookform/resolvers/zod" import { format } from "date-fns" -import { Suspense, useState } from "react" +import Link from "next/link" +import { useRouter, useSearchParams } from "next/navigation" +import { Suspense } from "react" import { Controller, useForm } from "react-hook-form" import { z } from "zod" import { getPageBatch } from "@/actions/batch" @@ -64,14 +66,17 @@ const filterSchema = z type FilterSchema = z.infer export default function BatchPage() { - const [filters, setFilters] = useState({}) - - const { control, handleSubmit, reset } = useForm({ + const searchParams = useSearchParams() + const resourceNo = searchParams.get("resource_no") + const batchNo = searchParams.get("batch_no") + const router = useRouter() + const { control, handleSubmit, reset, getValues } = useForm({ resolver: zodResolver(filterSchema), defaultValues: { user_phone: "", - batch_no: "", + batch_no: batchNo || "", prov: "", + resource_no: resourceNo || "", city: "", isp: "all", created_at_start: "", @@ -79,24 +84,25 @@ export default function BatchPage() { }, }) - const table = useDataTable((page, size) => - getPageBatch({ page, size, ...filters }), - ) - - const onFilter = handleSubmit(data => { + const table = useDataTable((page, size) => { const result: APIFilterParams = {} - if (data.user_phone?.trim()) result.user_phone = data.user_phone.trim() - if (data.batch_no?.trim()) result.batch_no = data.batch_no.trim() - if (data.resource_no?.trim()) result.resource_no = data.resource_no.trim() - if (data.prov?.trim()) result.prov = data.prov.trim() - if (data.city?.trim()) result.city = data.city.trim() - if (data.isp && data.isp !== "all") result.isp = data.isp - if (data.created_at_start) - result.created_at_start = new Date(data.created_at_start) - if (data.created_at_end) - result.created_at_end = new Date(data.created_at_end) + const filters = getValues() + if (filters.user_phone?.trim()) + result.user_phone = filters.user_phone.trim() + if (filters.batch_no?.trim()) result.batch_no = filters.batch_no.trim() + if (filters.resource_no?.trim()) + result.resource_no = filters.resource_no.trim() + if (filters.prov?.trim()) result.prov = filters.prov.trim() + if (filters.city?.trim()) result.city = filters.city.trim() + if (filters.isp && filters.isp !== "all") result.isp = filters.isp + 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 getPageBatch({ page, size, ...result }) + }) - setFilters(result) + const onFilter = handleSubmit(() => { table.pagination.onPageChange(1) }) @@ -237,8 +243,17 @@ export default function BatchPage() { type="button" variant="outline" onClick={() => { - reset() - setFilters({}) + reset({ + user_phone: "", + batch_no: "", + prov: "", + resource_no: "", + city: "", + isp: "all", + created_at_start: "", + created_at_end: "", + }) + router.replace("./batch") table.pagination.onPageChange(1) }} > @@ -255,7 +270,23 @@ export default function BatchPage() { header: "会员号", accessorFn: row => row.user?.phone || "", }, - { header: "套餐号", accessorKey: "resource.resource_no" }, + { + header: "套餐号", + accessorKey: "resource.resource_no", + cell: ({ row }) => { + const resourceNo = row.original.resource?.resource_no + return ( + + {resourceNo} + + ) + }, + }, { header: "提取编号", accessorKey: "batch_no" }, { header: "省份", accessorKey: "prov" }, { header: "城市", accessorKey: "city" }, @@ -266,7 +297,7 @@ export default function BatchPage() { header: "提取时间", accessorKey: "time", cell: ({ row }) => - format(new Date(row.original.time), "yyyy-MM-dd HH:mm"), + format(new Date(row.original.time), "yyyy-MM-dd HH:mm:ss"), }, ]} /> diff --git a/src/app/(root)/billing/page.tsx b/src/app/(root)/billing/page.tsx index deed950..5403485 100644 --- a/src/app/(root)/billing/page.tsx +++ b/src/app/(root)/billing/page.tsx @@ -2,6 +2,8 @@ import { zodResolver } from "@hookform/resolvers/zod" import { format } from "date-fns" import { CreditCard, Wallet } from "lucide-react" +import Link from "next/link" +import { useRouter, useSearchParams } from "next/navigation" import { Suspense, useEffect, useState } from "react" import { Controller, useForm } from "react-hook-form" import { toast } from "sonner" @@ -77,17 +79,21 @@ const filterSchema = z type FilterSchema = z.infer export default function BillingPage() { - const [filters, setFilters] = useState({}) + const searchParams = useSearchParams() + const innerNo = searchParams.get("inner_no") + console.log(innerNo, "innerNo") const [skuOptions, setSkuOptions] = useState([]) const [loading, setLoading] = useState(true) const [skuProductCode, setSkuProductCode] = useState( ProductCode.All, ) - const { control, handleSubmit, reset } = useForm({ + const router = useRouter() + + const { control, handleSubmit, reset, getValues } = useForm({ resolver: zodResolver(filterSchema), defaultValues: { bill_no: "", - inner_no: "", + inner_no: innerNo || "", created_at_start: "", created_at_end: "", phone: "", @@ -125,31 +131,58 @@ export default function BillingPage() { }) }, [skuProductCode]) - const table = useDataTable((page, size) => - getPageBill({ page, size, ...filters }), - ) - - const onFilter = handleSubmit(data => { + const loadData = (page: number, size: number) => { const result: FilterValues = {} - if (data.phone?.trim()) result.user_phone = data.phone.trim() - if (data.inner_no?.trim()) result.trade_inner_no = data.inner_no.trim() - if (data.bill_no?.trim()) result.bill_no = data.bill_no.trim() - if (data.resource_no?.trim()) result.resource_no = data.resource_no.trim() - if (data.product_code && data.product_code !== ProductCode.All) { - result.product_code = data.product_code - } - if (data.sku_code && data.sku_code !== "all") { - result.sku_code = data.sku_code - } - if (data.created_at_start) - result.created_at_start = new Date(data.created_at_start) - if (data.created_at_end) - result.created_at_end = new Date(data.created_at_end) - setFilters(result) + 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) + } + return getPageBill({ + page, + size, + ...result, + }) + } + + const clearFilter = () => { + router.replace("/billing") + reset({ + bill_no: "", + inner_no: "", + created_at_start: "", + created_at_end: "", + phone: "", + resource_no: "", + sku_code: "all", + product_code: "", + }) + setSkuProductCode(ProductCode.All) + table.pagination.onPageChange(1) + } + + const table = useDataTable(loadData) + + const onFilter = handleSubmit(() => { table.pagination.onPageChange(1) }) - return (
@@ -310,16 +343,7 @@ export default function BillingPage() { - @@ -333,9 +357,31 @@ export default function BillingPage() { header: "创建时间", accessorKey: "created_at", cell: ({ row }) => - format(new Date(row.original.created_at), "yyyy-MM-dd HH:mm"), + format( + new Date(row.original.created_at), + "yyyy-MM-dd HH:mm:ss", + ), + }, + { + header: "套餐号", + accessorKey: "resource.resource_no", + + cell: ({ row }) => { + const resource_no = row.original.resource?.resource_no + return resource_no ? ( + + {resource_no} + + ) : ( + + ) + }, }, - { header: "套餐号", accessorKey: "resource.resource_no" }, { header: "会员号", accessorFn: row => row.user?.phone || "" }, { header: "账单详情", @@ -412,7 +458,23 @@ export default function BillingPage() { ) }, }, - { header: "账单号", accessorKey: "bill_no" }, + { + header: "账单号", + accessorKey: "bill_no", + cell: ({ row }) => { + const billNo = row.original.bill_no + return ( + + {billNo} + + ) + }, + }, { header: "订单号", accessorKey: "trade.inner_no", diff --git a/src/app/(root)/channel/page.tsx b/src/app/(root)/channel/page.tsx index 1dd84f3..c0caa4b 100644 --- a/src/app/(root)/channel/page.tsx +++ b/src/app/(root)/channel/page.tsx @@ -1,7 +1,9 @@ "use client" import { zodResolver } from "@hookform/resolvers/zod" import { format } from "date-fns" -import { Suspense, useState } from "react" +import Link from "next/link" +import { useRouter, useSearchParams } from "next/navigation" +import { Suspense } from "react" import { Controller, useForm } from "react-hook-form" import { z } from "zod" import { getPageChannel } from "@/actions/channel" @@ -64,14 +66,15 @@ const ispMap: Record = { } export default function ChannelPage() { - const [filters, setFilters] = useState({}) - - const { control, handleSubmit, reset } = useForm({ + const searchParams = useSearchParams() + const resourceNo = searchParams.get("resource_no") + const router = useRouter() + const { control, handleSubmit, reset, getValues } = useForm({ resolver: zodResolver(filterSchema), defaultValues: { batch_no: "", user_phone: "", - resource_no: "", + resource_no: resourceNo || "", proxy_port: "", proxy_host: "", node_ip: "", @@ -80,24 +83,26 @@ export default function ChannelPage() { }, }) - const table = useDataTable((page, size) => - getPageChannel({ page, size, ...filters }), - ) - - const onFilter = handleSubmit(data => { + const table = useDataTable((page, size) => { const result: FilterValues = {} - if (data.batch_no?.trim()) result.batch_no = data.batch_no.trim() - if (data.user_phone?.trim()) result.user_phone = data.user_phone.trim() - if (data.resource_no?.trim()) result.resource_no = data.resource_no.trim() - if (data.proxy_host?.trim()) result.proxy_host = data.proxy_host.trim() - if (data.proxy_port?.trim()) - result.proxy_port = Number(data.proxy_port.trim()) - if (data.node_ip?.trim()) result.node_ip = data.node_ip.trim() - if (data.expired_at_start) - result.expired_at_start = new Date(data.expired_at_start) - if (data.expired_at_end) - result.expired_at_end = new Date(data.expired_at_end) - setFilters(result) + const filters = getValues() + if (filters.batch_no?.trim()) result.batch_no = filters.batch_no.trim() + if (filters.user_phone?.trim()) + result.user_phone = filters.user_phone.trim() + if (filters.resource_no?.trim()) + result.resource_no = filters.resource_no.trim() + if (filters.proxy_host?.trim()) + result.proxy_host = filters.proxy_host.trim() + if (filters.proxy_port) result.proxy_port = Number(filters.proxy_port) + if (filters.node_ip?.trim()) result.node_ip = filters.node_ip.trim() + if (filters.expired_at_start) + result.expired_at_start = new Date(filters.expired_at_start) + if (filters.expired_at_end) + result.expired_at_end = new Date(filters.expired_at_end) + return getPageChannel({ page, size, ...result }) + }) + + const onFilter = handleSubmit(() => { table.pagination.onPageChange(1) }) @@ -225,8 +230,17 @@ export default function ChannelPage() { type="button" variant="outline" onClick={() => { - reset() - setFilters({}) + reset({ + batch_no: "", + user_phone: "", + resource_no: "", + proxy_port: "", + proxy_host: "", + node_ip: "", + expired_at_start: "", + expired_at_end: "", + }) + router.replace("./channel") table.pagination.onPageChange(1) }} > @@ -244,7 +258,23 @@ export default function ChannelPage() { accessorFn: row => row.user?.phone || "-", }, { header: "套餐号", accessorKey: "resource.resource_no" }, - { header: "提取编号", accessorKey: "batch_no" }, + { + header: "提取编号", + accessorKey: "batch_no", + cell: ({ row }) => { + const batchNo = row.original.batch_no + return ( + + {batchNo} + + ) + }, + }, { header: "节点", accessorFn: row => row.ip || row.edge_ref || row.edge_id, @@ -318,7 +348,10 @@ export default function ChannelPage() { header: "过期时间", accessorKey: "expired_at", cell: ({ row }) => - format(new Date(row.original.expired_at), "yyyy-MM-dd HH:mm"), + format( + new Date(row.original.expired_at), + "yyyy-MM-dd HH:mm:ss", + ), }, ]} /> diff --git a/src/app/(root)/client/balance/page.tsx b/src/app/(root)/client/balance/page.tsx index 3615a81..5d66981 100644 --- a/src/app/(root)/client/balance/page.tsx +++ b/src/app/(root)/client/balance/page.tsx @@ -141,7 +141,7 @@ export default function BalancePage() { )} /> - +