时间显示到秒 & 表格之间的跳转页面
This commit is contained in:
@@ -2,7 +2,16 @@ import type { PageRecord } from "@/lib/api"
|
|||||||
import type { User } from "@/models/user"
|
import type { User } from "@/models/user"
|
||||||
import { callByUser } from "./base"
|
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<PageRecord<User>>("/api/admin/user/page", params)
|
return callByUser<PageRecord<User>>("/api/admin/user/page", params)
|
||||||
}
|
}
|
||||||
export async function updateCust(data: {
|
export async function updateCust(data: {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
ChevronsRight,
|
ChevronsRight,
|
||||||
CircleDollarSign,
|
CircleDollarSign,
|
||||||
ClipboardList,
|
ClipboardList,
|
||||||
ComputerIcon,
|
|
||||||
ContactRound,
|
ContactRound,
|
||||||
DollarSign,
|
DollarSign,
|
||||||
DoorClosedIcon,
|
DoorClosedIcon,
|
||||||
@@ -24,7 +23,7 @@ import {
|
|||||||
} from "lucide-react"
|
} from "lucide-react"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { usePathname } from "next/navigation"
|
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 { twJoin } from "tailwind-merge"
|
||||||
import { Auth } from "@/components/auth"
|
import { Auth } from "@/components/auth"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
@@ -307,7 +306,7 @@ export default function Navigation() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/*Logo 区域 */}
|
{/*Logo 区域 */}
|
||||||
<Suspense><Logo collapsed={collapsed} /></Suspense>
|
<Logo collapsed={collapsed} />
|
||||||
|
|
||||||
{/* Navigation Menu */}
|
{/* Navigation Menu */}
|
||||||
<ScrollArea className="flex-1 py-3 overflow-hidden">
|
<ScrollArea className="flex-1 py-3 overflow-hidden">
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
|
"use client"
|
||||||
import { ComputerIcon } from "lucide-react"
|
import { ComputerIcon } from "lucide-react"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
import { getNodeEnv } from "@/actions/env"
|
import { getNodeEnv } from "@/actions/env"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
export default async function Logo(props: { collapsed: boolean }) {
|
export default function Logo(props: { collapsed: boolean }) {
|
||||||
const env = await getNodeEnv()
|
const [env, setEnv] = useState<string>("")
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getNodeEnv().then(setEnv)
|
||||||
|
}, [])
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|||||||
@@ -18,11 +18,9 @@ import type { Admin } from "@/models/admin"
|
|||||||
export default function Appbar(props: { admin: Admin }) {
|
export default function Appbar(props: { admin: Admin }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [showDropdown, setShowDropdown] = useState(false)
|
const [showDropdown, setShowDropdown] = useState(false)
|
||||||
const [showNotifications, setShowNotifications] = useState(false)
|
|
||||||
|
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const dropdownRef = useRef<HTMLDivElement>(null)
|
const dropdownRef = useRef<HTMLDivElement>(null)
|
||||||
const notificationRef = useRef<HTMLDivElement>(null)
|
|
||||||
|
|
||||||
// 处理点击外部关闭下拉菜单
|
// 处理点击外部关闭下拉菜单
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -33,12 +31,6 @@ export default function Appbar(props: { admin: Admin }) {
|
|||||||
) {
|
) {
|
||||||
setShowDropdown(false)
|
setShowDropdown(false)
|
||||||
}
|
}
|
||||||
if (
|
|
||||||
notificationRef.current &&
|
|
||||||
!notificationRef.current.contains(event.target as Node)
|
|
||||||
) {
|
|
||||||
setShowNotifications(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("mousedown", handleClickOutside)
|
document.addEventListener("mousedown", handleClickOutside)
|
||||||
@@ -51,7 +43,7 @@ export default function Appbar(props: { admin: Admin }) {
|
|||||||
const filteredPaths = paths.filter(path => !hiddenSegments.includes(path))
|
const filteredPaths = paths.filter(path => !hiddenSegments.includes(path))
|
||||||
const breadcrumbs = [
|
const breadcrumbs = [
|
||||||
{ path: "/", label: "首页" },
|
{ path: "/", label: "首页" },
|
||||||
...filteredPaths.map((path, index) => {
|
...filteredPaths.map(path => {
|
||||||
const originalIndex = paths.findIndex(p => p === path)
|
const originalIndex = paths.findIndex(p => p === path)
|
||||||
const url = `/${paths.slice(0, originalIndex + 1).join("/")}`
|
const url = `/${paths.slice(0, originalIndex + 1).join("/")}`
|
||||||
const label = getBreadcrumbLabel(path)
|
const label = getBreadcrumbLabel(path)
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { format } from "date-fns"
|
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 { Controller, useForm } from "react-hook-form"
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { getPageBalance } from "@/actions/balance"
|
import { getPageBalance } from "@/actions/balance"
|
||||||
import { DataTable, useDataTable } from "@/components/data-table"
|
import { DataTable, useDataTable } from "@/components/data-table"
|
||||||
import { Page } from "@/components/page"
|
import { Page } from "@/components/page"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import { Field, FieldError, FieldLabel } from "@/components/ui/field"
|
||||||
Field,
|
|
||||||
FieldError,
|
|
||||||
FieldGroup,
|
|
||||||
FieldLabel,
|
|
||||||
} from "@/components/ui/field"
|
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import type { Balance } from "@/models/balance"
|
import type { Balance } from "@/models/balance"
|
||||||
|
|
||||||
@@ -51,12 +47,14 @@ const filterSchema = z
|
|||||||
type FormValues = z.infer<typeof filterSchema>
|
type FormValues = z.infer<typeof filterSchema>
|
||||||
|
|
||||||
export default function BalancePage() {
|
export default function BalancePage() {
|
||||||
const [filters, setFilters] = useState<FilterValues>({})
|
const searchParams = useSearchParams()
|
||||||
const { control, handleSubmit, reset } = useForm<FormValues>({
|
const billNo = searchParams.get("bill_no")
|
||||||
|
const router = useRouter()
|
||||||
|
const { control, handleSubmit, reset, getValues } = useForm<FormValues>({
|
||||||
resolver: zodResolver(filterSchema),
|
resolver: zodResolver(filterSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
phone: "",
|
phone: "",
|
||||||
bill_no: "",
|
bill_no: billNo || "",
|
||||||
admin_id: "",
|
admin_id: "",
|
||||||
created_at_start: "",
|
created_at_start: "",
|
||||||
created_at_end: "",
|
created_at_end: "",
|
||||||
@@ -64,21 +62,23 @@ export default function BalancePage() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const fetchUsers = useCallback(
|
const fetchUsers = useCallback(
|
||||||
(page: number, size: number) => getPageBalance({ page, size, ...filters }),
|
(page: number, size: number) => {
|
||||||
[filters],
|
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<Balance>(fetchUsers)
|
const table = useDataTable<Balance>(fetchUsers)
|
||||||
|
|
||||||
const onFilter = handleSubmit(data => {
|
const onFilter = handleSubmit(() => {
|
||||||
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)
|
|
||||||
table.pagination.onPageChange(1)
|
table.pagination.onPageChange(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -148,8 +148,14 @@ export default function BalancePage() {
|
|||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reset()
|
reset({
|
||||||
setFilters({})
|
phone: "",
|
||||||
|
bill_no: "",
|
||||||
|
admin_id: "",
|
||||||
|
created_at_start: "",
|
||||||
|
created_at_end: "",
|
||||||
|
})
|
||||||
|
router.replace("./balance")
|
||||||
table.pagination.onPageChange(1)
|
table.pagination.onPageChange(1)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -218,7 +224,10 @@ export default function BalancePage() {
|
|||||||
header: "创建时间",
|
header: "创建时间",
|
||||||
accessorKey: "created_at",
|
accessorKey: "created_at",
|
||||||
cell: ({ row }) =>
|
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",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { format } from "date-fns"
|
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 { Controller, useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { getPageBatch } from "@/actions/batch"
|
import { getPageBatch } from "@/actions/batch"
|
||||||
@@ -64,14 +66,17 @@ const filterSchema = z
|
|||||||
type FilterSchema = z.infer<typeof filterSchema>
|
type FilterSchema = z.infer<typeof filterSchema>
|
||||||
|
|
||||||
export default function BatchPage() {
|
export default function BatchPage() {
|
||||||
const [filters, setFilters] = useState<APIFilterParams>({})
|
const searchParams = useSearchParams()
|
||||||
|
const resourceNo = searchParams.get("resource_no")
|
||||||
const { control, handleSubmit, reset } = useForm<FilterSchema>({
|
const batchNo = searchParams.get("batch_no")
|
||||||
|
const router = useRouter()
|
||||||
|
const { control, handleSubmit, reset, getValues } = useForm<FilterSchema>({
|
||||||
resolver: zodResolver(filterSchema),
|
resolver: zodResolver(filterSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
user_phone: "",
|
user_phone: "",
|
||||||
batch_no: "",
|
batch_no: batchNo || "",
|
||||||
prov: "",
|
prov: "",
|
||||||
|
resource_no: resourceNo || "",
|
||||||
city: "",
|
city: "",
|
||||||
isp: "all",
|
isp: "all",
|
||||||
created_at_start: "",
|
created_at_start: "",
|
||||||
@@ -79,24 +84,25 @@ export default function BatchPage() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const table = useDataTable<Batch>((page, size) =>
|
const table = useDataTable<Batch>((page, size) => {
|
||||||
getPageBatch({ page, size, ...filters }),
|
|
||||||
)
|
|
||||||
|
|
||||||
const onFilter = handleSubmit(data => {
|
|
||||||
const result: APIFilterParams = {}
|
const result: APIFilterParams = {}
|
||||||
if (data.user_phone?.trim()) result.user_phone = data.user_phone.trim()
|
const filters = getValues()
|
||||||
if (data.batch_no?.trim()) result.batch_no = data.batch_no.trim()
|
if (filters.user_phone?.trim())
|
||||||
if (data.resource_no?.trim()) result.resource_no = data.resource_no.trim()
|
result.user_phone = filters.user_phone.trim()
|
||||||
if (data.prov?.trim()) result.prov = data.prov.trim()
|
if (filters.batch_no?.trim()) result.batch_no = filters.batch_no.trim()
|
||||||
if (data.city?.trim()) result.city = data.city.trim()
|
if (filters.resource_no?.trim())
|
||||||
if (data.isp && data.isp !== "all") result.isp = data.isp
|
result.resource_no = filters.resource_no.trim()
|
||||||
if (data.created_at_start)
|
if (filters.prov?.trim()) result.prov = filters.prov.trim()
|
||||||
result.created_at_start = new Date(data.created_at_start)
|
if (filters.city?.trim()) result.city = filters.city.trim()
|
||||||
if (data.created_at_end)
|
if (filters.isp && filters.isp !== "all") result.isp = filters.isp
|
||||||
result.created_at_end = new Date(data.created_at_end)
|
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)
|
table.pagination.onPageChange(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -237,8 +243,17 @@ export default function BatchPage() {
|
|||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reset()
|
reset({
|
||||||
setFilters({})
|
user_phone: "",
|
||||||
|
batch_no: "",
|
||||||
|
prov: "",
|
||||||
|
resource_no: "",
|
||||||
|
city: "",
|
||||||
|
isp: "all",
|
||||||
|
created_at_start: "",
|
||||||
|
created_at_end: "",
|
||||||
|
})
|
||||||
|
router.replace("./batch")
|
||||||
table.pagination.onPageChange(1)
|
table.pagination.onPageChange(1)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -255,7 +270,23 @@ export default function BatchPage() {
|
|||||||
header: "会员号",
|
header: "会员号",
|
||||||
accessorFn: row => row.user?.phone || "",
|
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 (
|
||||||
|
<Link
|
||||||
|
href={`/channel?resource_no=${resourceNo}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-blue-600 hover:underline cursor-pointer"
|
||||||
|
>
|
||||||
|
{resourceNo}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
{ header: "提取编号", accessorKey: "batch_no" },
|
{ header: "提取编号", accessorKey: "batch_no" },
|
||||||
{ header: "省份", accessorKey: "prov" },
|
{ header: "省份", accessorKey: "prov" },
|
||||||
{ header: "城市", accessorKey: "city" },
|
{ header: "城市", accessorKey: "city" },
|
||||||
@@ -266,7 +297,7 @@ export default function BatchPage() {
|
|||||||
header: "提取时间",
|
header: "提取时间",
|
||||||
accessorKey: "time",
|
accessorKey: "time",
|
||||||
cell: ({ row }) =>
|
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"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { format } from "date-fns"
|
import { format } from "date-fns"
|
||||||
import { CreditCard, Wallet } from "lucide-react"
|
import { CreditCard, Wallet } from "lucide-react"
|
||||||
|
import Link from "next/link"
|
||||||
|
import { useRouter, useSearchParams } from "next/navigation"
|
||||||
import { Suspense, useEffect, useState } from "react"
|
import { Suspense, useEffect, useState } from "react"
|
||||||
import { Controller, useForm } from "react-hook-form"
|
import { Controller, useForm } from "react-hook-form"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
@@ -77,17 +79,21 @@ const filterSchema = z
|
|||||||
type FilterSchema = z.infer<typeof filterSchema>
|
type FilterSchema = z.infer<typeof filterSchema>
|
||||||
|
|
||||||
export default function BillingPage() {
|
export default function BillingPage() {
|
||||||
const [filters, setFilters] = useState<FilterValues>({})
|
const searchParams = useSearchParams()
|
||||||
|
const innerNo = searchParams.get("inner_no")
|
||||||
|
console.log(innerNo, "innerNo")
|
||||||
const [skuOptions, setSkuOptions] = useState<SkuOption[]>([])
|
const [skuOptions, setSkuOptions] = useState<SkuOption[]>([])
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [skuProductCode, setSkuProductCode] = useState<ProductCode>(
|
const [skuProductCode, setSkuProductCode] = useState<ProductCode>(
|
||||||
ProductCode.All,
|
ProductCode.All,
|
||||||
)
|
)
|
||||||
const { control, handleSubmit, reset } = useForm<FilterSchema>({
|
const router = useRouter()
|
||||||
|
|
||||||
|
const { control, handleSubmit, reset, getValues } = useForm<FilterSchema>({
|
||||||
resolver: zodResolver(filterSchema),
|
resolver: zodResolver(filterSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
bill_no: "",
|
bill_no: "",
|
||||||
inner_no: "",
|
inner_no: innerNo || "",
|
||||||
created_at_start: "",
|
created_at_start: "",
|
||||||
created_at_end: "",
|
created_at_end: "",
|
||||||
phone: "",
|
phone: "",
|
||||||
@@ -125,31 +131,58 @@ export default function BillingPage() {
|
|||||||
})
|
})
|
||||||
}, [skuProductCode])
|
}, [skuProductCode])
|
||||||
|
|
||||||
const table = useDataTable<Billing>((page, size) =>
|
const loadData = (page: number, size: number) => {
|
||||||
getPageBill({ page, size, ...filters }),
|
|
||||||
)
|
|
||||||
|
|
||||||
const onFilter = handleSubmit(data => {
|
|
||||||
const result: FilterValues = {}
|
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<Billing>(loadData)
|
||||||
|
|
||||||
|
const onFilter = handleSubmit(() => {
|
||||||
table.pagination.onPageChange(1)
|
table.pagination.onPageChange(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<form onSubmit={onFilter} className="bg-card p-4 rounded-lg">
|
<form onSubmit={onFilter} className="bg-card p-4 rounded-lg">
|
||||||
@@ -310,16 +343,7 @@ export default function BillingPage() {
|
|||||||
|
|
||||||
<FieldGroup className="flex-row justify-start mt-4 gap-2">
|
<FieldGroup className="flex-row justify-start mt-4 gap-2">
|
||||||
<Button type="submit">搜索</Button>
|
<Button type="submit">搜索</Button>
|
||||||
<Button
|
<Button type="button" variant="outline" onClick={clearFilter}>
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => {
|
|
||||||
reset()
|
|
||||||
setSkuProductCode(ProductCode.All)
|
|
||||||
setFilters({})
|
|
||||||
table.pagination.onPageChange(1)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
重置
|
重置
|
||||||
</Button>
|
</Button>
|
||||||
</FieldGroup>
|
</FieldGroup>
|
||||||
@@ -333,9 +357,31 @@ export default function BillingPage() {
|
|||||||
header: "创建时间",
|
header: "创建时间",
|
||||||
accessorKey: "created_at",
|
accessorKey: "created_at",
|
||||||
cell: ({ row }) =>
|
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 ? (
|
||||||
|
<Link
|
||||||
|
href={`/resources?resource_no=${resource_no}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-blue-600 hover:underline cursor-pointer"
|
||||||
|
>
|
||||||
|
{resource_no}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span></span>
|
||||||
|
)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{ header: "套餐号", accessorKey: "resource.resource_no" },
|
|
||||||
{ header: "会员号", accessorFn: row => row.user?.phone || "" },
|
{ header: "会员号", accessorFn: row => row.user?.phone || "" },
|
||||||
{
|
{
|
||||||
header: "账单详情",
|
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 (
|
||||||
|
<Link
|
||||||
|
href={`./balance?bill_no=${billNo}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-blue-600 hover:underline cursor-pointer"
|
||||||
|
>
|
||||||
|
{billNo}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
header: "订单号",
|
header: "订单号",
|
||||||
accessorKey: "trade.inner_no",
|
accessorKey: "trade.inner_no",
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { format } from "date-fns"
|
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 { Controller, useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { getPageChannel } from "@/actions/channel"
|
import { getPageChannel } from "@/actions/channel"
|
||||||
@@ -64,14 +66,15 @@ const ispMap: Record<number, string> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function ChannelPage() {
|
export default function ChannelPage() {
|
||||||
const [filters, setFilters] = useState<FilterValues>({})
|
const searchParams = useSearchParams()
|
||||||
|
const resourceNo = searchParams.get("resource_no")
|
||||||
const { control, handleSubmit, reset } = useForm<FilterSchema>({
|
const router = useRouter()
|
||||||
|
const { control, handleSubmit, reset, getValues } = useForm<FilterSchema>({
|
||||||
resolver: zodResolver(filterSchema),
|
resolver: zodResolver(filterSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
batch_no: "",
|
batch_no: "",
|
||||||
user_phone: "",
|
user_phone: "",
|
||||||
resource_no: "",
|
resource_no: resourceNo || "",
|
||||||
proxy_port: "",
|
proxy_port: "",
|
||||||
proxy_host: "",
|
proxy_host: "",
|
||||||
node_ip: "",
|
node_ip: "",
|
||||||
@@ -80,24 +83,26 @@ export default function ChannelPage() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const table = useDataTable<Channel>((page, size) =>
|
const table = useDataTable<Channel>((page, size) => {
|
||||||
getPageChannel({ page, size, ...filters }),
|
|
||||||
)
|
|
||||||
|
|
||||||
const onFilter = handleSubmit(data => {
|
|
||||||
const result: FilterValues = {}
|
const result: FilterValues = {}
|
||||||
if (data.batch_no?.trim()) result.batch_no = data.batch_no.trim()
|
const filters = getValues()
|
||||||
if (data.user_phone?.trim()) result.user_phone = data.user_phone.trim()
|
if (filters.batch_no?.trim()) result.batch_no = filters.batch_no.trim()
|
||||||
if (data.resource_no?.trim()) result.resource_no = data.resource_no.trim()
|
if (filters.user_phone?.trim())
|
||||||
if (data.proxy_host?.trim()) result.proxy_host = data.proxy_host.trim()
|
result.user_phone = filters.user_phone.trim()
|
||||||
if (data.proxy_port?.trim())
|
if (filters.resource_no?.trim())
|
||||||
result.proxy_port = Number(data.proxy_port.trim())
|
result.resource_no = filters.resource_no.trim()
|
||||||
if (data.node_ip?.trim()) result.node_ip = data.node_ip.trim()
|
if (filters.proxy_host?.trim())
|
||||||
if (data.expired_at_start)
|
result.proxy_host = filters.proxy_host.trim()
|
||||||
result.expired_at_start = new Date(data.expired_at_start)
|
if (filters.proxy_port) result.proxy_port = Number(filters.proxy_port)
|
||||||
if (data.expired_at_end)
|
if (filters.node_ip?.trim()) result.node_ip = filters.node_ip.trim()
|
||||||
result.expired_at_end = new Date(data.expired_at_end)
|
if (filters.expired_at_start)
|
||||||
setFilters(result)
|
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)
|
table.pagination.onPageChange(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -225,8 +230,17 @@ export default function ChannelPage() {
|
|||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reset()
|
reset({
|
||||||
setFilters({})
|
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)
|
table.pagination.onPageChange(1)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -244,7 +258,23 @@ export default function ChannelPage() {
|
|||||||
accessorFn: row => row.user?.phone || "-",
|
accessorFn: row => row.user?.phone || "-",
|
||||||
},
|
},
|
||||||
{ header: "套餐号", accessorKey: "resource.resource_no" },
|
{ header: "套餐号", accessorKey: "resource.resource_no" },
|
||||||
{ header: "提取编号", accessorKey: "batch_no" },
|
{
|
||||||
|
header: "提取编号",
|
||||||
|
accessorKey: "batch_no",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const batchNo = row.original.batch_no
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={`./batch?batch_no=${batchNo}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-blue-600 hover:underline cursor-pointer"
|
||||||
|
>
|
||||||
|
{batchNo}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
header: "节点",
|
header: "节点",
|
||||||
accessorFn: row => row.ip || row.edge_ref || row.edge_id,
|
accessorFn: row => row.ip || row.edge_ref || row.edge_id,
|
||||||
@@ -318,7 +348,10 @@ export default function ChannelPage() {
|
|||||||
header: "过期时间",
|
header: "过期时间",
|
||||||
accessorKey: "expired_at",
|
accessorKey: "expired_at",
|
||||||
cell: ({ row }) =>
|
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",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ export default function BalancePage() {
|
|||||||
</Field>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button type="submit">搜索</Button>
|
<Button type="submit">搜索</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -213,7 +213,10 @@ export default function BalancePage() {
|
|||||||
header: "创建时间",
|
header: "创建时间",
|
||||||
accessorKey: "created_at",
|
accessorKey: "created_at",
|
||||||
cell: ({ row }) =>
|
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",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ export default function BatchPage() {
|
|||||||
header: "提取时间",
|
header: "提取时间",
|
||||||
accessorKey: "time",
|
accessorKey: "time",
|
||||||
cell: ({ row }) =>
|
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"),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -335,7 +335,10 @@ export default function BillingPage() {
|
|||||||
header: "创建时间",
|
header: "创建时间",
|
||||||
accessorKey: "created_at",
|
accessorKey: "created_at",
|
||||||
cell: ({ row }) =>
|
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" },
|
{ header: "套餐号", accessorKey: "resource.resource_no" },
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -319,7 +319,10 @@ export default function ChannelPage() {
|
|||||||
header: "过期时间",
|
header: "过期时间",
|
||||||
accessorKey: "expired_at",
|
accessorKey: "expired_at",
|
||||||
cell: ({ row }) =>
|
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",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ export default function CouponPage() {
|
|||||||
header: "发放时间",
|
header: "发放时间",
|
||||||
accessorKey: "issued_at",
|
accessorKey: "issued_at",
|
||||||
// cell: ({ row }) =>
|
// cell: ({ row }) =>
|
||||||
// format(new Date(row.original.issued_at), "yyyy-MM-dd HH:mm"),
|
// format(new Date(row.original.issued_at), "yyyy-MM-dd HH:mm:ss"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: "备注",
|
header: "备注",
|
||||||
|
|||||||
@@ -150,7 +150,10 @@ export default function UserQueryPage() {
|
|||||||
header: "创建时间",
|
header: "创建时间",
|
||||||
accessorKey: "created_at",
|
accessorKey: "created_at",
|
||||||
cell: ({ row }) =>
|
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: "客户来源",
|
header: "客户来源",
|
||||||
@@ -215,7 +218,7 @@ export default function UserQueryPage() {
|
|||||||
row.original.last_login
|
row.original.last_login
|
||||||
? format(
|
? format(
|
||||||
new Date(row.original.last_login),
|
new Date(row.original.last_login),
|
||||||
"yyyy-MM-dd HH:mm",
|
"yyyy-MM-dd HH:mm:ss",
|
||||||
)
|
)
|
||||||
: "",
|
: "",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ function ExpireBadge({ expireAt }: { expireAt: Date | null | undefined }) {
|
|||||||
// 格式化日期
|
// 格式化日期
|
||||||
function formatDateTime(date: Date | null | undefined) {
|
function formatDateTime(date: Date | null | undefined) {
|
||||||
if (!date) return "-"
|
if (!date) return "-"
|
||||||
return format(date, "yyyy-MM-dd HH:mm")
|
return format(date, "yyyy-MM-dd HH:mm:ss")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算今日使用量
|
// 计算今日使用量
|
||||||
|
|||||||
@@ -252,7 +252,10 @@ export default function TradePage() {
|
|||||||
header: "创建时间",
|
header: "创建时间",
|
||||||
accessorKey: "created_at",
|
accessorKey: "created_at",
|
||||||
cell: ({ row }) =>
|
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: "订单号",
|
header: "订单号",
|
||||||
|
|||||||
@@ -84,11 +84,14 @@ export default function CouponPage() {
|
|||||||
if (coupon.expire_type === 2 && coupon.expire_in) {
|
if (coupon.expire_type === 2 && coupon.expire_in) {
|
||||||
const expireDate = new Date(coupon.created_at)
|
const expireDate = new Date(coupon.created_at)
|
||||||
expireDate.setDate(expireDate.getDate() + coupon.expire_in)
|
expireDate.setDate(expireDate.getDate() + coupon.expire_in)
|
||||||
return format(expireDate, "yyyy-MM-dd HH:mm")
|
return format(expireDate, "yyyy-MM-dd HH:mm:ss")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coupon.expire_type === 1 && coupon.expire_at) {
|
if (coupon.expire_type === 1 && coupon.expire_at) {
|
||||||
return format(new Date(coupon.expire_at), "yyyy-MM-dd HH:mm")
|
return format(
|
||||||
|
new Date(coupon.expire_at),
|
||||||
|
"yyyy-MM-dd HH:mm:ss",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return <span>永久有效</span>
|
return <span>永久有效</span>
|
||||||
},
|
},
|
||||||
@@ -97,7 +100,10 @@ export default function CouponPage() {
|
|||||||
header: "创建时间",
|
header: "创建时间",
|
||||||
accessorKey: "created_at",
|
accessorKey: "created_at",
|
||||||
cell: ({ row }) =>
|
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",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "action",
|
id: "action",
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ export function ReleaseCoupon(props: {
|
|||||||
{user.last_login
|
{user.last_login
|
||||||
? format(
|
? format(
|
||||||
new Date(user.last_login),
|
new Date(user.last_login),
|
||||||
"yyyy-MM-dd HH:mm",
|
"yyyy-MM-dd HH:mm:ss",
|
||||||
)
|
)
|
||||||
: "-"}
|
: "-"}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { format } from "date-fns"
|
import { format } from "date-fns"
|
||||||
import { useRouter } from "next/navigation"
|
import { useRouter } from "next/navigation"
|
||||||
import { Suspense, useCallback, useState } from "react"
|
import { Suspense, useCallback } from "react"
|
||||||
import { Controller, useForm } from "react-hook-form"
|
import { Controller, useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { getPageCusts } from "@/actions/cust"
|
import { getPageCusts } from "@/actions/cust"
|
||||||
@@ -82,9 +82,8 @@ const filterSchema = z
|
|||||||
type FormValues = z.infer<typeof filterSchema>
|
type FormValues = z.infer<typeof filterSchema>
|
||||||
|
|
||||||
export default function CustPage() {
|
export default function CustPage() {
|
||||||
const [filters, setFilters] = useState<FilterValues>({})
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { control, handleSubmit, reset } = useForm<FormValues>({
|
const { control, handleSubmit, reset, getValues } = useForm<FormValues>({
|
||||||
resolver: zodResolver(filterSchema),
|
resolver: zodResolver(filterSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
account: "",
|
account: "",
|
||||||
@@ -97,21 +96,27 @@ export default function CustPage() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const fetchUsers = useCallback(
|
const fetchUsers = useCallback(
|
||||||
(page: number, size: number) => getPageCusts({ page, size, ...filters }),
|
(page: number, size: number) => {
|
||||||
[filters],
|
const result: FilterValues = {}
|
||||||
|
const filters = getValues()
|
||||||
|
if (filters.account?.trim()) result.account = filters.account.trim()
|
||||||
|
if (filters.name?.trim()) result.name = filters.name.trim()
|
||||||
|
if (filters.identified && filters.identified !== "all")
|
||||||
|
result.identified = filters.identified === "1"
|
||||||
|
if (filters.enabled && filters.enabled !== "all")
|
||||||
|
result.enabled = filters.enabled === "1"
|
||||||
|
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 getPageCusts({ page, size, ...result })
|
||||||
|
},
|
||||||
|
[getValues],
|
||||||
)
|
)
|
||||||
|
|
||||||
const table = useDataTable<User>(fetchUsers)
|
const table = useDataTable<User>(fetchUsers)
|
||||||
|
|
||||||
const onFilter = handleSubmit(data => {
|
const onFilter = handleSubmit(() => {
|
||||||
const result: FilterValues = {}
|
|
||||||
if (data.account?.trim()) result.account = data.account.trim()
|
|
||||||
if (data.name?.trim()) result.name = data.name.trim()
|
|
||||||
if (data.identified && data.identified !== "all")
|
|
||||||
result.identified = data.identified === "1"
|
|
||||||
if (data.enabled && data.enabled !== "all")
|
|
||||||
result.enabled = data.enabled === "1"
|
|
||||||
setFilters(result)
|
|
||||||
table.pagination.onPageChange(1)
|
table.pagination.onPageChange(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -219,8 +224,14 @@ export default function CustPage() {
|
|||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reset()
|
reset({
|
||||||
setFilters({})
|
account: "",
|
||||||
|
name: "",
|
||||||
|
identified: "all",
|
||||||
|
enabled: "all",
|
||||||
|
created_at_start: "",
|
||||||
|
created_at_end: "",
|
||||||
|
})
|
||||||
table.pagination.onPageChange(1)
|
table.pagination.onPageChange(1)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -240,7 +251,10 @@ export default function CustPage() {
|
|||||||
header: "创建时间",
|
header: "创建时间",
|
||||||
accessorKey: "created_at",
|
accessorKey: "created_at",
|
||||||
cell: ({ row }) =>
|
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: "email" },
|
// { header: "邮箱", accessorKey: "email" },
|
||||||
{
|
{
|
||||||
@@ -311,7 +325,7 @@ export default function CustPage() {
|
|||||||
row.original.last_login
|
row.original.last_login
|
||||||
? format(
|
? format(
|
||||||
new Date(row.original.last_login),
|
new Date(row.original.last_login),
|
||||||
"yyyy-MM-dd HH:mm",
|
"yyyy-MM-dd HH:mm:ss",
|
||||||
)
|
)
|
||||||
: "",
|
: "",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -49,13 +49,19 @@ export default function DiscountPage() {
|
|||||||
header: "创建时间",
|
header: "创建时间",
|
||||||
accessorKey: "created_at",
|
accessorKey: "created_at",
|
||||||
cell: ({ row }) =>
|
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: "更新时间",
|
header: "更新时间",
|
||||||
accessorKey: "updated_at",
|
accessorKey: "updated_at",
|
||||||
cell: ({ row }) =>
|
cell: ({ row }) =>
|
||||||
format(new Date(row.original.updated_at), "yyyy-MM-dd HH:mm"),
|
format(
|
||||||
|
new Date(row.original.updated_at),
|
||||||
|
"yyyy-MM-dd HH:mm:ss",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "action",
|
id: "action",
|
||||||
|
|||||||
@@ -105,7 +105,10 @@ export default function GatewayPage() {
|
|||||||
header: "创建时间",
|
header: "创建时间",
|
||||||
accessorKey: "created_at",
|
accessorKey: "created_at",
|
||||||
cell: ({ row }) =>
|
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",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "action",
|
id: "action",
|
||||||
|
|||||||
@@ -144,11 +144,11 @@ function ProductSkus(props: {
|
|||||||
{ header: "最低购买数量", accessorKey: "count_min" },
|
{ header: "最低购买数量", accessorKey: "count_min" },
|
||||||
{
|
{
|
||||||
header: "创建时间",
|
header: "创建时间",
|
||||||
accessorFn: row => format(row.created_at, "yyyy-MM-dd HH:mm"),
|
accessorFn: row => format(row.created_at, "yyyy-MM-dd HH:mm:ss"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: "更新时间",
|
header: "更新时间",
|
||||||
accessorFn: row => format(row.updated_at, "yyyy-MM-dd HH:mm"),
|
accessorFn: row => format(row.updated_at, "yyyy-MM-dd HH:mm:ss"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "action",
|
id: "action",
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { format, isBefore, isSameDay } from "date-fns"
|
import { format, isBefore, isSameDay } from "date-fns"
|
||||||
import { Box, Loader2, Timer } from "lucide-react"
|
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 { Suspense, useCallback, useMemo, useState } from "react"
|
||||||
import { Controller, useForm } from "react-hook-form"
|
import { Controller, useForm } from "react-hook-form"
|
||||||
import { toast } from "sonner"
|
import { toast } from "sonner"
|
||||||
@@ -156,8 +158,8 @@ function ExpireBadge({ expireAt }: { expireAt: Date | null | undefined }) {
|
|||||||
|
|
||||||
// 格式化日期
|
// 格式化日期
|
||||||
function formatDateTime(date: Date | null | undefined) {
|
function formatDateTime(date: Date | null | undefined) {
|
||||||
if (!date) return "-"
|
if (!date) return ""
|
||||||
return format(date, "yyyy-MM-dd HH:mm")
|
return format(date, "yyyy-MM-dd HH:mm:ss")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算今日使用量
|
// 计算今日使用量
|
||||||
@@ -200,15 +202,17 @@ interface ResourceListProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ResourceList({ resourceType }: ResourceListProps) {
|
function ResourceList({ resourceType }: ResourceListProps) {
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
const resourceNo = searchParams.get("resource_no")
|
||||||
const isLong = resourceType === "long"
|
const isLong = resourceType === "long"
|
||||||
const listFn = isLong ? listResourceLong : listResourceShort
|
const listFn = isLong ? listResourceLong : listResourceShort
|
||||||
const [filters, setFilters] = useState<FilterParams>({})
|
|
||||||
const [updatingId, setUpdatingId] = useState<number | null>(null)
|
const [updatingId, setUpdatingId] = useState<number | null>(null)
|
||||||
const { control, handleSubmit, reset } = useForm<FormValues>({
|
const router = useRouter()
|
||||||
|
const { control, handleSubmit, reset, getValues } = useForm<FormValues>({
|
||||||
resolver: zodResolver(filterSchema),
|
resolver: zodResolver(filterSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
user_phone: "",
|
user_phone: "",
|
||||||
resource_no: "",
|
resource_no: resourceNo || "",
|
||||||
status: "all",
|
status: "all",
|
||||||
type: "all",
|
type: "all",
|
||||||
created_at_start: "",
|
created_at_start: "",
|
||||||
@@ -219,16 +223,35 @@ function ResourceList({ resourceType }: ResourceListProps) {
|
|||||||
|
|
||||||
const fetchResources = useCallback(
|
const fetchResources = useCallback(
|
||||||
(page: number, size: number) => {
|
(page: number, size: number) => {
|
||||||
return listFn({ page, size, ...filters })
|
const result: FilterParams = {}
|
||||||
|
const filters = getValues()
|
||||||
|
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.status && filters.status !== "all") {
|
||||||
|
result.active = filters.status === "0"
|
||||||
|
}
|
||||||
|
if (filters.type && filters.type !== "all") {
|
||||||
|
result.mode = Number(filters.type)
|
||||||
|
}
|
||||||
|
if (filters.expired && filters.expired !== "all") {
|
||||||
|
result.expired = filters.expired === "1"
|
||||||
|
}
|
||||||
|
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 listFn({ page, size, ...result })
|
||||||
},
|
},
|
||||||
[listFn, filters],
|
[listFn, getValues],
|
||||||
)
|
)
|
||||||
|
|
||||||
const table = useDataTable<Resources>(fetchResources)
|
const table = useDataTable<Resources>(fetchResources)
|
||||||
|
|
||||||
const refreshTable = useCallback(() => {
|
// const refreshTable = useCallback(() => {
|
||||||
setFilters(prev => ({ ...prev }))
|
// setFilters(prev => ({ ...prev }))
|
||||||
}, [])
|
// }, [])
|
||||||
|
|
||||||
const handleStatusChange = useCallback(
|
const handleStatusChange = useCallback(
|
||||||
async (resource: Resources, newStatusValue: string) => {
|
async (resource: Resources, newStatusValue: string) => {
|
||||||
@@ -243,7 +266,7 @@ function ResourceList({ resourceType }: ResourceListProps) {
|
|||||||
toast.success("更新成功", {
|
toast.success("更新成功", {
|
||||||
description: `资源状态已更新为${newActive ? "启用" : "禁用"}`,
|
description: `资源状态已更新为${newActive ? "启用" : "禁用"}`,
|
||||||
})
|
})
|
||||||
refreshTable()
|
table.refresh()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("更新状态失败:", error)
|
console.error("更新状态失败:", error)
|
||||||
toast.error("更新失败", {
|
toast.error("更新失败", {
|
||||||
@@ -253,7 +276,7 @@ function ResourceList({ resourceType }: ResourceListProps) {
|
|||||||
setUpdatingId(null)
|
setUpdatingId(null)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[refreshTable],
|
[table],
|
||||||
)
|
)
|
||||||
const handleCheckipChange = useCallback(
|
const handleCheckipChange = useCallback(
|
||||||
async (resource: Resources) => {
|
async (resource: Resources) => {
|
||||||
@@ -267,7 +290,7 @@ function ResourceList({ resourceType }: ResourceListProps) {
|
|||||||
toast.success("更新成功", {
|
toast.success("更新成功", {
|
||||||
description: `IP检查已${newCheckip ? "启用IP检查" : "停用IP检查"}`,
|
description: `IP检查已${newCheckip ? "启用IP检查" : "停用IP检查"}`,
|
||||||
})
|
})
|
||||||
refreshTable()
|
table.refresh()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("更新IP检查状态失败:", error)
|
console.error("更新IP检查状态失败:", error)
|
||||||
toast.error("更新失败", {
|
toast.error("更新失败", {
|
||||||
@@ -277,27 +300,9 @@ function ResourceList({ resourceType }: ResourceListProps) {
|
|||||||
setUpdatingId(null)
|
setUpdatingId(null)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[refreshTable],
|
[table],
|
||||||
)
|
)
|
||||||
const onFilter = handleSubmit(data => {
|
const onFilter = handleSubmit(() => {
|
||||||
const result: FilterParams = {}
|
|
||||||
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.status && data.status !== "all") {
|
|
||||||
result.active = data.status === "0"
|
|
||||||
}
|
|
||||||
if (data.type && data.type !== "all") {
|
|
||||||
result.mode = Number(data.type)
|
|
||||||
}
|
|
||||||
if (data.expired && data.expired !== "all") {
|
|
||||||
result.expired = data.expired === "1"
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
table.pagination.onPageChange(1)
|
table.pagination.onPageChange(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -317,7 +322,14 @@ function ResourceList({ resourceType }: ResourceListProps) {
|
|||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<div>{name}</div>
|
<div>{name}</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-xs text-gray-500">{resourceNo}</span>
|
<Link
|
||||||
|
href={`/batch?resource_no=${resourceNo}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-xs text-gray-500"
|
||||||
|
>
|
||||||
|
{resourceNo}
|
||||||
|
</Link>
|
||||||
<ExpireBadge expireAt={expireAt} />
|
<ExpireBadge expireAt={expireAt} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -576,8 +588,16 @@ function ResourceList({ resourceType }: ResourceListProps) {
|
|||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reset()
|
router.replace("./resources")
|
||||||
setFilters({})
|
reset({
|
||||||
|
user_phone: "",
|
||||||
|
resource_no: "",
|
||||||
|
status: "all",
|
||||||
|
type: "all",
|
||||||
|
created_at_start: "",
|
||||||
|
created_at_end: "",
|
||||||
|
expired: "all",
|
||||||
|
})
|
||||||
table.pagination.onPageChange(1)
|
table.pagination.onPageChange(1)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { format } from "date-fns"
|
import { format } from "date-fns"
|
||||||
import { CheckCircle, Clock, XCircle } from "lucide-react"
|
import { CheckCircle, Clock, XCircle } from "lucide-react"
|
||||||
|
import Link from "next/link"
|
||||||
import { Suspense, useCallback, useState } from "react"
|
import { Suspense, useCallback, useState } from "react"
|
||||||
import { Controller, useForm } from "react-hook-form"
|
import { Controller, useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
@@ -9,12 +10,7 @@ import { getPageTrade } from "@/actions/trade"
|
|||||||
import { DataTable, useDataTable } from "@/components/data-table"
|
import { DataTable, useDataTable } from "@/components/data-table"
|
||||||
import { Page } from "@/components/page"
|
import { Page } from "@/components/page"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import { Field, FieldError, FieldLabel } from "@/components/ui/field"
|
||||||
Field,
|
|
||||||
FieldError,
|
|
||||||
FieldGroup,
|
|
||||||
FieldLabel,
|
|
||||||
} from "@/components/ui/field"
|
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
@@ -69,7 +65,6 @@ type FilterSchema = z.infer<typeof filterSchema>
|
|||||||
|
|
||||||
export default function TradePage() {
|
export default function TradePage() {
|
||||||
const [filters, setFilters] = useState<FilterValues>({})
|
const [filters, setFilters] = useState<FilterValues>({})
|
||||||
|
|
||||||
const { control, handleSubmit, reset } = useForm<FilterSchema>({
|
const { control, handleSubmit, reset } = useForm<FilterSchema>({
|
||||||
resolver: zodResolver(filterSchema),
|
resolver: zodResolver(filterSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
@@ -120,10 +115,7 @@ export default function TradePage() {
|
|||||||
name="user_phone"
|
name="user_phone"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field, fieldState }) => (
|
render={({ field, fieldState }) => (
|
||||||
<Field
|
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||||
data-invalid={fieldState.invalid}
|
|
||||||
className="w-40 flex"
|
|
||||||
>
|
|
||||||
<FieldLabel>会员号</FieldLabel>
|
<FieldLabel>会员号</FieldLabel>
|
||||||
<Input {...field} placeholder="请输入会员号" />
|
<Input {...field} placeholder="请输入会员号" />
|
||||||
<FieldError>{fieldState.error?.message}</FieldError>
|
<FieldError>{fieldState.error?.message}</FieldError>
|
||||||
@@ -135,10 +127,7 @@ export default function TradePage() {
|
|||||||
name="inner_no"
|
name="inner_no"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field, fieldState }) => (
|
render={({ field, fieldState }) => (
|
||||||
<Field
|
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||||
data-invalid={fieldState.invalid}
|
|
||||||
className="w-40 flex"
|
|
||||||
>
|
|
||||||
<FieldLabel>订单号</FieldLabel>
|
<FieldLabel>订单号</FieldLabel>
|
||||||
<Input {...field} placeholder="请输入订单号" />
|
<Input {...field} placeholder="请输入订单号" />
|
||||||
<FieldError>{fieldState.error?.message}</FieldError>
|
<FieldError>{fieldState.error?.message}</FieldError>
|
||||||
@@ -217,10 +206,7 @@ export default function TradePage() {
|
|||||||
name="created_at_start"
|
name="created_at_start"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field, fieldState }) => (
|
render={({ field, fieldState }) => (
|
||||||
<Field
|
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||||
data-invalid={fieldState.invalid}
|
|
||||||
className="w-40 flex"
|
|
||||||
>
|
|
||||||
<FieldLabel>开始时间</FieldLabel>
|
<FieldLabel>开始时间</FieldLabel>
|
||||||
<Input type="date" {...field} />
|
<Input type="date" {...field} />
|
||||||
<FieldError>{fieldState.error?.message}</FieldError>
|
<FieldError>{fieldState.error?.message}</FieldError>
|
||||||
@@ -232,17 +218,14 @@ export default function TradePage() {
|
|||||||
name="created_at_end"
|
name="created_at_end"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field, fieldState }) => (
|
render={({ field, fieldState }) => (
|
||||||
<Field
|
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||||
data-invalid={fieldState.invalid}
|
|
||||||
className="w-40 flex"
|
|
||||||
>
|
|
||||||
<FieldLabel>结束时间</FieldLabel>
|
<FieldLabel>结束时间</FieldLabel>
|
||||||
<Input type="date" {...field} />
|
<Input type="date" {...field} />
|
||||||
<FieldError>{fieldState.error?.message}</FieldError>
|
<FieldError>{fieldState.error?.message}</FieldError>
|
||||||
</Field>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button type="submit">搜索</Button>
|
<Button type="submit">搜索</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -266,10 +249,29 @@ export default function TradePage() {
|
|||||||
header: "创建时间",
|
header: "创建时间",
|
||||||
accessorKey: "created_at",
|
accessorKey: "created_at",
|
||||||
cell: ({ row }) =>
|
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: "会员号", accessorFn: row => row.user?.phone || "" },
|
{ header: "会员号", accessorFn: row => row.user?.phone || "" },
|
||||||
{ header: "订单号", accessorKey: "inner_no" },
|
{
|
||||||
|
header: "订单号",
|
||||||
|
accessorKey: "inner_no",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const innerNo = row.original.inner_no
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={`/billing?inner_no=${innerNo}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-blue-600 hover:underline cursor-pointer"
|
||||||
|
>
|
||||||
|
{innerNo}
|
||||||
|
</Link>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
{ header: "购买套餐", accessorKey: "subject" },
|
{ header: "购买套餐", accessorKey: "subject" },
|
||||||
{
|
{
|
||||||
header: "支付金额",
|
header: "支付金额",
|
||||||
|
|||||||
@@ -10,11 +10,7 @@ import { DataTable, useDataTable } from "@/components/data-table"
|
|||||||
import { Page } from "@/components/page"
|
import { Page } from "@/components/page"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import { Field, FieldError, FieldLabel } from "@/components/ui/field"
|
||||||
Field,
|
|
||||||
FieldError,
|
|
||||||
FieldLabel,
|
|
||||||
} from "@/components/ui/field"
|
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
import { useFetch } from "@/hooks/data"
|
import { useFetch } from "@/hooks/data"
|
||||||
import { ScopeUserWriteBind } from "@/lib/scopes"
|
import { ScopeUserWriteBind } from "@/lib/scopes"
|
||||||
@@ -67,17 +63,14 @@ export default function UserPage() {
|
|||||||
name="phone"
|
name="phone"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field, fieldState }) => (
|
render={({ field, fieldState }) => (
|
||||||
<Field
|
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||||
data-invalid={fieldState.invalid}
|
|
||||||
className="w-40 flex"
|
|
||||||
>
|
|
||||||
<FieldLabel>手机号</FieldLabel>
|
<FieldLabel>手机号</FieldLabel>
|
||||||
<Input {...field} placeholder="请输入手机号" />
|
<Input {...field} placeholder="请输入手机号" />
|
||||||
<FieldError>{fieldState.error?.message}</FieldError>
|
<FieldError>{fieldState.error?.message}</FieldError>
|
||||||
</Field>
|
</Field>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button type="submit">搜索</Button>
|
<Button type="submit">搜索</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -161,7 +154,7 @@ export default function UserPage() {
|
|||||||
row.original.last_login
|
row.original.last_login
|
||||||
? format(
|
? format(
|
||||||
new Date(row.original.last_login),
|
new Date(row.original.last_login),
|
||||||
"yyyy-MM-dd HH:mm",
|
"yyyy-MM-dd HH:mm:ss",
|
||||||
)
|
)
|
||||||
: "",
|
: "",
|
||||||
},
|
},
|
||||||
@@ -174,7 +167,10 @@ export default function UserPage() {
|
|||||||
header: "创建时间",
|
header: "创建时间",
|
||||||
accessorKey: "created_at",
|
accessorKey: "created_at",
|
||||||
cell: ({ row }) =>
|
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",
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "action",
|
id: "action",
|
||||||
@@ -197,4 +193,4 @@ export default function UserPage() {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
</Page>
|
</Page>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user