From be03cf6440c0ee67ebd6e1bde145177d0b541830 Mon Sep 17 00:00:00 2001 From: Eamon <17516219072@163.com> Date: Tue, 31 Mar 2026 10:56:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AE=A1=E7=90=86=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=BF=AE=E6=94=B9=E9=87=91=E9=A2=9D=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actions/cust.ts | 4 + src/app/(root)/billing/page.tsx | 2 +- src/app/(root)/cust/balanceDialog.tsx | 165 ++++++++++++++++++++++++++ src/app/(root)/cust/page.tsx | 42 +++++-- 4 files changed, 202 insertions(+), 11 deletions(-) create mode 100644 src/app/(root)/cust/balanceDialog.tsx diff --git a/src/actions/cust.ts b/src/actions/cust.ts index a4b04fb..3a0a031 100644 --- a/src/actions/cust.ts +++ b/src/actions/cust.ts @@ -34,3 +34,7 @@ export async function createCust(data: { }) { return callByUser>("/api/admin/user/create", data) } + +export async function getBalance(params: { user_id: number; balance: string }) { + return callByUser>("/api/admin/user/update/balance", params) +} diff --git a/src/app/(root)/billing/page.tsx b/src/app/(root)/billing/page.tsx index 4ea2ca0..3f77907 100644 --- a/src/app/(root)/billing/page.tsx +++ b/src/app/(root)/billing/page.tsx @@ -421,7 +421,7 @@ export default function BillingPage() { ) }, }, - { header: "套餐名称", accessorKey: "info" }, + // { header: "套餐名称", accessorKey: "info" }, { header: "账单号", accessorKey: "bill_no" }, { header: "订单号", accessorKey: "trade.inner_no" }, { diff --git a/src/app/(root)/cust/balanceDialog.tsx b/src/app/(root)/cust/balanceDialog.tsx new file mode 100644 index 0000000..88b00bd --- /dev/null +++ b/src/app/(root)/cust/balanceDialog.tsx @@ -0,0 +1,165 @@ +"use client" + +import { zodResolver } from "@hookform/resolvers/zod" +import { useEffect, useState } from "react" +import { useForm } from "react-hook-form" +import { toast } from "sonner" +import { z } from "zod" +import { getBalance } from "@/actions/cust" +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { Field, FieldError, FieldLabel } from "@/components/ui/field" +import { Input } from "@/components/ui/input" +import type { Cust } from "@/models/cust" + +const balanceSchema = z.object({ + balance: z + .string() + .min(1, "请输入余额") + .refine(val => !Number.isNaN(Number(val)), "请输入有效的数字") + .refine(val => Number(val) >= 0, "余额不能为负数"), +}) + +type BalanceFormValues = z.infer + +interface UpdateBalanceDialogProps { + open: boolean + onOpenChange: (open: boolean) => void + currentUser: Cust | null + onSuccess: () => void +} + +export function BalanceDialog({ + open, + onOpenChange, + currentUser, + onSuccess, +}: UpdateBalanceDialogProps) { + const [isLoading, setIsLoading] = useState(false) + + const { + register, + handleSubmit, + reset, + setValue, + formState: { errors }, + } = useForm({ + resolver: zodResolver(balanceSchema), + defaultValues: { + balance: "", + }, + }) + + useEffect(() => { + if (open && currentUser) { + const currentBalance = currentUser.balance?.toString() || "0" + const formattedBalance = Number(currentBalance).toFixed(2) + setValue("balance", formattedBalance) + } + }, [open, currentUser, setValue]) + + const onSubmit = async (data: BalanceFormValues) => { + if (!currentUser) return + + setIsLoading(true) + + try { + const result = await getBalance({ + user_id: currentUser.id, + balance: data.balance, + }) + + if (result.success) { + toast.success("修改余额成功") + onOpenChange(false) + reset() + onSuccess() + } else { + toast.error(result.message || "修改余额失败") + } + } catch (error) { + const message = + error instanceof Error ? error.message : "网络错误,请稍后重试" + toast.error(message) + } finally { + setIsLoading(false) + } + } + + const handleOpenChange = (open: boolean) => { + if (!open) { + reset() + } + onOpenChange(open) + } + + return ( + + + + 修改余额 + + 修改用户 {currentUser?.name || currentUser?.username} 的余额 + + + +
+
+ + 余额(元) + { + if (!value) return "" + const num = Number(value) + if (Number.isNaN(num)) return value + return num.toFixed(2) + }, + })} + onInput={(e: React.ChangeEvent) => { + let value = e.target.value + if (value.startsWith("-")) { + value = value.replace("-", "") + } + if (value.includes(".")) { + const parts = value.split(".") + if (parts[1] && parts[1].length > 2) { + value = `${parts[0]}.${parts[1].slice(0, 2)}` + } + } + + setValue("balance", value) + }} + /> + {errors.balance?.message} + +
+ + + + + +
+
+
+ ) +} diff --git a/src/app/(root)/cust/page.tsx b/src/app/(root)/cust/page.tsx index e58c5f4..d93c52c 100644 --- a/src/app/(root)/cust/page.tsx +++ b/src/app/(root)/cust/page.tsx @@ -24,6 +24,7 @@ import { SelectValue, } from "@/components/ui/select" import type { Cust } from "@/models/cust" +import { BalanceDialog } from "./balanceDialog" import { AddUserDialog } from "./create" import { UpdateDialog } from "./update" @@ -67,6 +68,8 @@ export default function UserPage() { const [isAddDialogOpen, setIsAddDialogOpen] = useState(false) const [isEditDialogOpen, setIsEditDialogOpen] = useState(false) const [currentEditUser, setCurrentEditUser] = useState(null) + const [balanceDialog, setBalanceDialog] = useState(false) + const [balance, setBalance] = useState(null) const { control, handleSubmit, reset } = useForm({ resolver: zodResolver(filterSchema), defaultValues: { @@ -258,7 +261,7 @@ export default function UserPage() { 2: "代理商注册", 3: "代理商添加", } - return sourceMap[row.original.source] ?? "未知" + return sourceMap[row.original.source] ?? "官网注册" }, }, { @@ -352,15 +355,27 @@ export default function UserPage() { header: "操作", cell: ({ row }) => { return ( - +
+ + +
) }, }, @@ -380,6 +395,13 @@ export default function UserPage() { currentUser={currentEditUser} onSuccess={refreshTable} /> + + ) }