搜索框添加一键清除功能
This commit is contained in:
@@ -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"
|
||||
>
|
||||
<FieldLabel>会员号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入会员号" />
|
||||
<Input {...field} placeholder="请输入会员号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -109,7 +110,7 @@ export default function BalancePage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>账单号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入账单号" />
|
||||
<Input {...field} placeholder="请输入账单号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -123,7 +124,7 @@ export default function BalancePage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>开始时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -137,7 +138,7 @@ export default function BalancePage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>结束时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -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 (
|
||||
<Link
|
||||
href={`/billing?bill_no=${bill_no}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600"
|
||||
>
|
||||
{bill_no}
|
||||
</Link>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "管理员",
|
||||
|
||||
@@ -120,7 +120,7 @@ export default function BatchPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>提取编号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入提取编号" />
|
||||
<Input {...field} placeholder="请输入提取编号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -134,7 +134,7 @@ export default function BatchPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>套餐号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入套餐号" />
|
||||
<Input {...field} placeholder="请输入套餐号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -148,7 +148,7 @@ export default function BatchPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>会员号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入会员号" />
|
||||
<Input {...field} placeholder="请输入会员号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -163,7 +163,7 @@ export default function BatchPage() {
|
||||
className="w-32 flex-none"
|
||||
>
|
||||
<FieldLabel>省份</FieldLabel>
|
||||
<Input {...field} placeholder="请输入省份" />
|
||||
<Input {...field} placeholder="请输入省份" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -178,7 +178,7 @@ export default function BatchPage() {
|
||||
className="w-32 flex-none"
|
||||
>
|
||||
<FieldLabel>城市</FieldLabel>
|
||||
<Input {...field} placeholder="请输入城市" />
|
||||
<Input {...field} placeholder="请输入城市" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -215,7 +215,7 @@ export default function BatchPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>开始时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -230,7 +230,7 @@ export default function BatchPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>结束时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -277,17 +277,33 @@ export default function BatchPage() {
|
||||
const resourceNo = row.original.resource?.resource_no
|
||||
return (
|
||||
<Link
|
||||
href={`/channel?resource_no=${resourceNo}`}
|
||||
href={`/resources?resource_no=${resourceNo}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 hover:underline cursor-pointer"
|
||||
className="text-blue-600"
|
||||
>
|
||||
{resourceNo}
|
||||
</Link>
|
||||
)
|
||||
},
|
||||
},
|
||||
{ header: "提取编号", accessorKey: "batch_no" },
|
||||
{
|
||||
header: "提取编号",
|
||||
accessorKey: "batch_no",
|
||||
cell: ({ row }) => {
|
||||
const batch_no = row.original.batch_no
|
||||
return (
|
||||
<Link
|
||||
href={`./channel?batch_no=${batch_no}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600"
|
||||
>
|
||||
{batch_no}
|
||||
</Link>
|
||||
)
|
||||
},
|
||||
},
|
||||
{ header: "省份", accessorKey: "prov" },
|
||||
{ header: "城市", accessorKey: "city" },
|
||||
{ header: "用户IP", accessorKey: "ip" },
|
||||
|
||||
@@ -81,7 +81,8 @@ type FilterSchema = z.infer<typeof filterSchema>
|
||||
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<SkuOption[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [skuProductCode, setSkuProductCode] = useState<ProductCode>(
|
||||
@@ -92,12 +93,12 @@ export default function BillingPage() {
|
||||
const { control, handleSubmit, reset, getValues } = useForm<FilterSchema>({
|
||||
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,10 +134,6 @@ 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())
|
||||
@@ -154,7 +151,6 @@ export default function BillingPage() {
|
||||
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"
|
||||
>
|
||||
<FieldLabel>会员号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入会员号" />
|
||||
<Input {...field} placeholder="请输入会员号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -210,7 +206,7 @@ export default function BillingPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>套餐号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入套餐号" />
|
||||
<Input {...field} placeholder="请输入套餐号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -224,7 +220,7 @@ export default function BillingPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>账单号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入账单号" />
|
||||
<Input {...field} placeholder="请输入账单号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -238,7 +234,7 @@ export default function BillingPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>订单号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入订单号" />
|
||||
<Input {...field} placeholder="请输入订单号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -319,7 +315,7 @@ export default function BillingPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>开始时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -334,7 +330,7 @@ export default function BillingPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>结束时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -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}
|
||||
</Link>
|
||||
@@ -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}
|
||||
</Link>
|
||||
@@ -509,7 +505,16 @@ export default function BillingPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-sm">{bill.trade?.inner_no}</div>
|
||||
<div className="text-sm">
|
||||
<Link
|
||||
href={`/trade?inner_no=${bill.trade?.inner_no}`}
|
||||
target="_blak"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600"
|
||||
>
|
||||
{bill.trade?.inner_no}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
@@ -68,11 +68,12 @@ const ispMap: Record<number, string> = {
|
||||
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<FilterSchema>({
|
||||
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"
|
||||
>
|
||||
<FieldLabel>提取编号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入提取编号" />
|
||||
<Input {...field} placeholder="请输入提取编号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -133,7 +134,7 @@ export default function ChannelPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>会员号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入会员号" />
|
||||
<Input {...field} placeholder="请输入会员号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -147,7 +148,7 @@ export default function ChannelPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>套餐号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入套餐号" />
|
||||
<Input {...field} placeholder="请输入套餐号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -161,7 +162,7 @@ export default function ChannelPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>代理IP</FieldLabel>
|
||||
<Input {...field} placeholder="请输入代理IP" />
|
||||
<Input {...field} placeholder="请输入代理IP" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -175,7 +176,7 @@ export default function ChannelPage() {
|
||||
className="w-32 flex-none"
|
||||
>
|
||||
<FieldLabel>代理端口</FieldLabel>
|
||||
<Input {...field} placeholder="请输入代理端口" />
|
||||
<Input {...field} placeholder="请输入代理端口" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -189,7 +190,7 @@ export default function ChannelPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>节点</FieldLabel>
|
||||
<Input {...field} placeholder="请输入节点" />
|
||||
<Input {...field} placeholder="请输入节点" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -203,7 +204,7 @@ export default function ChannelPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>开始时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -217,7 +218,7 @@ export default function ChannelPage() {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>结束时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -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 (
|
||||
<Link
|
||||
href={`./resources?resource_no=${resource_no}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600"
|
||||
>
|
||||
{resource_no}
|
||||
</Link>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
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}
|
||||
</Link>
|
||||
|
||||
@@ -113,7 +113,7 @@ export default function UserQueryPage() {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||
<FieldLabel>手机号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入手机号" />
|
||||
<Input {...field} placeholder="请输入手机号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -125,18 +125,18 @@ export default function UserQueryPage() {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||
<FieldLabel>姓名</FieldLabel>
|
||||
<Input {...field} placeholder="请输入姓名" />
|
||||
<Input {...field} placeholder="请输入姓名" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
/>
|
||||
<Auth scope={ScopeUserWrite}>
|
||||
<AddUserDialog onSuccess={refreshTable} />
|
||||
</Auth>
|
||||
<Button type="submit">搜索</Button>
|
||||
<Button type="button" variant="outline" onClick={handleReset}>
|
||||
重置
|
||||
</Button>
|
||||
<Button type="submit">搜索</Button>
|
||||
<Auth scope={ScopeUserWrite}>
|
||||
<AddUserDialog onSuccess={refreshTable} />
|
||||
</Auth>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
@@ -214,6 +214,7 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) {
|
||||
{...field}
|
||||
placeholder="请输入用户名"
|
||||
autoComplete="off"
|
||||
clearable
|
||||
/>
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
@@ -225,7 +226,7 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid}>
|
||||
<FieldLabel>手机号 *</FieldLabel>
|
||||
<Input {...field} placeholder="请输入手机号" />
|
||||
<Input {...field} placeholder="请输入手机号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -246,6 +247,7 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) {
|
||||
{...field}
|
||||
type="password"
|
||||
placeholder="请输入密码(至少6位)"
|
||||
clearable
|
||||
/>
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
@@ -261,6 +263,7 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) {
|
||||
{...field}
|
||||
type="password"
|
||||
placeholder="请再次输入密码"
|
||||
clearable
|
||||
/>
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
@@ -275,7 +278,7 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid}>
|
||||
<FieldLabel>邮箱</FieldLabel>
|
||||
<Input {...field} placeholder="请输入邮箱" />
|
||||
<Input {...field} placeholder="请输入邮箱" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -311,7 +314,7 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid}>
|
||||
<FieldLabel>QQ联系方式</FieldLabel>
|
||||
<Input {...field} placeholder="请输入QQ联系方式" />
|
||||
<Input {...field} placeholder="请输入QQ联系方式" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -322,7 +325,11 @@ export function AddUserDialog({ onSuccess }: AddUserDialogProps) {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid}>
|
||||
<FieldLabel>微信/联系方式</FieldLabel>
|
||||
<Input {...field} placeholder="请输入微信或联系方式" />
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="请输入微信或联系方式"
|
||||
clearable
|
||||
/>
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
|
||||
@@ -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 }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-80 flex">
|
||||
<FieldLabel>账号/手机号/邮箱</FieldLabel>
|
||||
<Input {...field} placeholder="请输入账号/手机号/邮箱" />
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="请输入账号/手机号/邮箱"
|
||||
clearable
|
||||
/>
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -146,7 +152,7 @@ export default function CustPage() {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||
<FieldLabel>姓名</FieldLabel>
|
||||
<Input {...field} placeholder="请输入姓名" />
|
||||
<Input {...field} placeholder="请输入姓名" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -200,7 +206,7 @@ export default function CustPage() {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||
<FieldLabel>开始时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -212,14 +218,12 @@ export default function CustPage() {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||
<FieldLabel>结束时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
/>
|
||||
<Auth scope={ScopeUserWrite}>
|
||||
<AddUserDialog onSuccess={refreshTable} />
|
||||
</Auth>
|
||||
<Button type="submit">搜索</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
@@ -237,7 +241,9 @@ export default function CustPage() {
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
<Button type="submit">搜索</Button>
|
||||
<Auth scope={ScopeUserWrite}>
|
||||
<AddUserDialog onSuccess={refreshTable} />
|
||||
</Auth>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ export function UpdateDialog({ user, onSuccess }: EditUserDialogProps) {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid}>
|
||||
<FieldLabel>用户名</FieldLabel>
|
||||
<Input {...field} placeholder="请输入用户名" />
|
||||
<Input {...field} placeholder="请输入用户名" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -238,7 +238,7 @@ export function UpdateDialog({ user, onSuccess }: EditUserDialogProps) {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid}>
|
||||
<FieldLabel>邮箱</FieldLabel>
|
||||
<Input {...field} placeholder="请输入邮箱" />
|
||||
<Input {...field} placeholder="请输入邮箱" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -254,6 +254,7 @@ export function UpdateDialog({ user, onSuccess }: EditUserDialogProps) {
|
||||
{...field}
|
||||
type="password"
|
||||
placeholder="选填,修改请输入新密码(至少6位)"
|
||||
clearable
|
||||
/>
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
@@ -270,6 +271,7 @@ export function UpdateDialog({ user, onSuccess }: EditUserDialogProps) {
|
||||
{...field}
|
||||
type="password"
|
||||
placeholder="请再次输入密码"
|
||||
clearable
|
||||
/>
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
@@ -348,7 +350,7 @@ export function UpdateDialog({ user, onSuccess }: EditUserDialogProps) {
|
||||
render={({ field }) => (
|
||||
<Field>
|
||||
<FieldLabel>QQ</FieldLabel>
|
||||
<Input {...field} placeholder="QQ号" />
|
||||
<Input {...field} placeholder="QQ号" clearable />
|
||||
</Field>
|
||||
)}
|
||||
/>
|
||||
@@ -359,7 +361,7 @@ export function UpdateDialog({ user, onSuccess }: EditUserDialogProps) {
|
||||
render={({ field }) => (
|
||||
<Field>
|
||||
<FieldLabel>微信</FieldLabel>
|
||||
<Input {...field} placeholder="微信号" />
|
||||
<Input {...field} placeholder="微信号" clearable />
|
||||
</Field>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -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<Resources>(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) {
|
||||
<div className="flex flex-col gap-1">
|
||||
<div>{name}</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Link
|
||||
href={`/batch?resource_no=${resourceNo}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-xs text-gray-500"
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<span className="text-xs text-gray-500">{resourceNo}</span>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-40">
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
router.push(`/billing?resource_no=${resourceNo}`)
|
||||
}}
|
||||
>
|
||||
{resourceNo}
|
||||
</Link>
|
||||
账单详情
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
router.push(`/batch?resource_no=${resourceNo}`)
|
||||
}}
|
||||
>
|
||||
提取记录
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
router.push(`/channel?resource_no=${resourceNo}`)
|
||||
}}
|
||||
>
|
||||
IP管理
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<ExpireBadge expireAt={expireAt} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -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"
|
||||
>
|
||||
<FieldLabel>会员号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入会员号" />
|
||||
<Input {...field} placeholder="请输入会员号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -488,7 +509,7 @@ function ResourceList({ resourceType }: ResourceListProps) {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>套餐号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入套餐号" />
|
||||
<Input {...field} placeholder="请输入套餐号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -562,7 +583,7 @@ function ResourceList({ resourceType }: ResourceListProps) {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>开始时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -576,7 +597,7 @@ function ResourceList({ resourceType }: ResourceListProps) {
|
||||
className="w-40 flex-none"
|
||||
>
|
||||
<FieldLabel>结束时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
|
||||
@@ -117,7 +117,7 @@ export default function TradePage() {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||
<FieldLabel>会员号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入会员号" />
|
||||
<Input {...field} placeholder="请输入会员号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -129,7 +129,7 @@ export default function TradePage() {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||
<FieldLabel>订单号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入订单号" />
|
||||
<Input {...field} placeholder="请输入订单号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -208,7 +208,7 @@ export default function TradePage() {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||
<FieldLabel>开始时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -220,7 +220,7 @@ export default function TradePage() {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||
<FieldLabel>结束时间</FieldLabel>
|
||||
<Input type="date" {...field} />
|
||||
<Input type="date" {...field} clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
@@ -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}
|
||||
</Link>
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function UserPage() {
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid} className="w-40 flex">
|
||||
<FieldLabel>手机号</FieldLabel>
|
||||
<Input {...field} placeholder="请输入手机号" />
|
||||
<Input {...field} placeholder="请输入手机号" clearable />
|
||||
<FieldError>{fieldState.error?.message}</FieldError>
|
||||
</Field>
|
||||
)}
|
||||
|
||||
@@ -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<HTMLInputElement>(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<HTMLInputElement>
|
||||
onChange(event)
|
||||
}
|
||||
// 清空后重新聚焦
|
||||
inputRef.current?.focus()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative inline-block w-full">
|
||||
<input
|
||||
ref={inputRef}
|
||||
type={type}
|
||||
data-slot="input"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
className={cn(
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
// 当有清空按钮时,右侧留出空间
|
||||
clearable && hasValue && showClear && "pr-7",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
{clearable && hasValue && showClear && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClear}
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
|
||||
aria-label="清空"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user