Files
admin/src/app/(root)/cust/deduction.tsx

145 lines
3.9 KiB
TypeScript
Raw Normal View History

2026-03-31 10:56:47 +08:00
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useState } from "react"
2026-03-31 10:56:47 +08:00
import { useForm } from "react-hook-form"
import { toast } from "sonner"
import { z } from "zod"
import { getDeduction } from "@/actions/cust"
2026-03-31 10:56:47 +08:00
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
2026-04-11 17:12:16 +08:00
DialogTrigger,
2026-03-31 10:56:47 +08:00
} from "@/components/ui/dialog"
import { Field, FieldError, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
2026-04-09 17:08:59 +08:00
import type { User } from "@/models/user"
2026-03-31 10:56:47 +08:00
const Schema = z.object({
deduction: z
2026-03-31 10:56:47 +08:00
.string()
.min(1, "请输入扣款金额")
2026-03-31 10:56:47 +08:00
.refine(val => !Number.isNaN(Number(val)), "请输入有效的数字")
.refine(val => Number(val) >= 0, "金额不能为负数"),
2026-03-31 10:56:47 +08:00
})
type FormValues = z.infer<typeof Schema>
2026-03-31 10:56:47 +08:00
interface UpdateDeductionDialogProps {
2026-04-11 17:12:16 +08:00
user: User
2026-03-31 10:56:47 +08:00
onSuccess: () => void
}
export function DeductionDialog({
2026-04-11 17:12:16 +08:00
user,
2026-03-31 10:56:47 +08:00
onSuccess,
}: UpdateDeductionDialogProps) {
2026-04-11 17:12:16 +08:00
const [open, setOpen] = useState(false)
2026-03-31 10:56:47 +08:00
const [isLoading, setIsLoading] = useState(false)
const {
register,
handleSubmit,
reset,
setValue,
formState: { errors },
} = useForm<FormValues>({
resolver: zodResolver(Schema),
2026-03-31 10:56:47 +08:00
defaultValues: {
deduction: "",
2026-03-31 10:56:47 +08:00
},
})
const onSubmit = async (data: FormValues) => {
2026-03-31 10:56:47 +08:00
setIsLoading(true)
try {
const result = await getDeduction({
2026-04-11 17:12:16 +08:00
user_id: user.id,
amount: data.deduction,
2026-03-31 10:56:47 +08:00
})
if (result.success) {
toast.success("扣款成功")
2026-04-11 17:12:16 +08:00
setOpen(false)
2026-03-31 10:56:47 +08:00
reset()
onSuccess()
} else {
toast.error(result.message || "扣款失败")
2026-03-31 10:56:47 +08:00
}
} catch (error) {
2026-04-02 13:13:59 +08:00
const message = error instanceof Error ? error.message : error
toast.error(`网络错误,请稍后重试: ${message}`)
2026-03-31 10:56:47 +08:00
} finally {
setIsLoading(false)
}
}
const handleOpenChange = (open: boolean) => {
if (!open) {
reset()
}
2026-04-11 17:12:16 +08:00
setOpen(open)
2026-03-31 10:56:47 +08:00
}
return (
<Dialog open={open} onOpenChange={handleOpenChange}>
2026-04-11 17:12:16 +08:00
<DialogTrigger asChild>
<Button size="sm"></Button>
</DialogTrigger>
2026-03-31 10:56:47 +08:00
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle></DialogTitle>
2026-03-31 10:56:47 +08:00
<DialogDescription>
2026-04-11 17:12:16 +08:00
{user.name || user.username}
2026-03-31 10:56:47 +08:00
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="grid gap-4 py-4">
<Field data-invalid={!!errors.deduction}>
<FieldLabel></FieldLabel>
2026-03-31 10:56:47 +08:00
<Input
type="text"
placeholder="请输入扣款金额"
{...register("deduction")}
2026-03-31 10:56:47 +08:00
onInput={(e: React.ChangeEvent<HTMLInputElement>) => {
let value = e.target.value
value = value.replace(/[^\d.]/g, "")
const dotCount = (value.match(/\./g) || []).length
if (dotCount > 1) {
value = value.slice(0, value.lastIndexOf("."))
2026-03-31 10:56:47 +08:00
}
if (value.includes(".")) {
const [int, dec] = value.split(".")
value = `${int}.${dec.slice(0, 2)}`
2026-03-31 10:56:47 +08:00
}
setValue("deduction", value)
2026-03-31 10:56:47 +08:00
}}
/>
<FieldError>{errors.deduction?.message}</FieldError>
2026-03-31 10:56:47 +08:00
</Field>
</div>
<DialogFooter>
<Button
type="button"
variant="outline"
onClick={() => handleOpenChange(false)}
>
</Button>
<Button type="submit" disabled={isLoading}>
2026-04-02 13:13:59 +08:00
{isLoading ? "保存中" : "保存"}
2026-03-31 10:56:47 +08:00
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
)
}