Files
admin/src/app/(root)/cust/update.tsx
2026-04-02 13:13:59 +08:00

391 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import { zodResolver } from "@hookform/resolvers/zod"
import { useEffect, useState } from "react"
import { Controller, useForm } from "react-hook-form"
import { toast } from "sonner"
import { z } from "zod"
import { getAllAdmin } from "@/actions/admin"
import { updateCust } from "@/actions/cust"
import { getAllProductDiscount } from "@/actions/product_discount"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import {
Field,
FieldError,
FieldGroup,
FieldLabel,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import type { Admin } from "@/models/admin"
import type { Cust } from "@/models/cust"
import type { ProductDiscount } from "@/models/product_discount"
// 表单验证规则
const editUserSchema = z
.object({
id: z.number(),
username: z.string().optional(),
email: z.string().email("邮箱格式不正确").optional().or(z.literal("")),
password: z.string().optional(),
confirmPassword: z.string().optional(),
admin_id: z.string().optional(),
discount_id: z.string().optional(),
status: z.string().optional(),
contact_qq: z.string().optional(),
contact_wechat: z.string().optional(),
})
.superRefine((data, ctx) => {
if (data.password && data.password.length < 6) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "密码至少6位",
path: ["password"],
})
}
if (data.password && data.password !== data.confirmPassword) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "两次输入的密码不一致",
path: ["confirmPassword"],
})
}
})
export type EditUserFormValues = z.infer<typeof editUserSchema>
interface EditUserDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
currentUser: Cust | null
onSuccess: () => void
}
export function UpdateDialog({
open,
onOpenChange,
currentUser,
onSuccess,
}: EditUserDialogProps) {
const [admins, setAdmins] = useState<Admin[]>([])
const [discounts, setDiscounts] = useState<ProductDiscount[]>([])
const [isSubmitting, setIsSubmitting] = useState(false)
// 表单
const { control, handleSubmit, reset } = useForm<EditUserFormValues>({
resolver: zodResolver(editUserSchema),
defaultValues: {
id: 0,
username: "",
email: "",
password: "",
confirmPassword: "",
admin_id: "",
discount_id: "",
status: "",
contact_qq: "",
contact_wechat: "",
},
})
useEffect(() => {
const loadOptions = async () => {
try {
const [adminRes, discountRes] = await Promise.all([
getAllAdmin(),
getAllProductDiscount(),
])
if (adminRes.success) {
setAdmins(adminRes.data)
} else {
toast.error(adminRes.message || "获取管理员失败")
setAdmins([])
}
if (discountRes.success) {
setDiscounts(discountRes.data)
} else {
toast.error(discountRes.message || "获取折扣方案失败")
setDiscounts([])
}
} catch (error) {
const message = error instanceof Error ? error.message : "加载数据失败"
toast.error(message)
}
}
if (open) {
loadOptions()
}
}, [open])
useEffect(() => {
if (currentUser) {
reset({
id: currentUser.id,
username: currentUser.username,
email: currentUser.email || "",
password: "",
confirmPassword: "",
admin_id: currentUser.admin_id ? String(currentUser.admin_id) : "",
discount_id: currentUser.discount_id
? String(currentUser.discount_id)
: "",
status:
currentUser.status !== undefined ? String(currentUser.status) : "",
contact_qq: currentUser.contact_qq || "",
contact_wechat: currentUser.contact_wechat || "",
})
}
}, [currentUser, reset])
const onSubmit = async (data: EditUserFormValues) => {
setIsSubmitting(true)
try {
const updateData: {
id: number
username?: string
email?: string
password?: string
admin_id?: number
discount_id?: number
status?: number
contact_qq?: string
contact_wechat?: string
} = { id: data.id }
if (data.username && data.username.trim() !== "") {
updateData.username = data.username.trim()
}
if (data.email && data.email.trim() !== "") {
updateData.email = data.email.trim()
}
if (data.password && data.password.trim() !== "") {
updateData.password = data.password
}
if (data.admin_id && data.admin_id !== "") {
updateData.admin_id = Number(data.admin_id)
}
if (data.discount_id && data.discount_id !== "") {
updateData.discount_id = Number(data.discount_id)
}
if (data.status && data.status !== "") {
updateData.status = Number(data.status)
}
if (data.contact_qq !== undefined) {
updateData.contact_qq = data.contact_qq
}
if (data.contact_wechat !== undefined) {
updateData.contact_wechat = data.contact_wechat
}
const result = await updateCust(updateData)
if (result?.success) {
toast.success("修改成功")
onOpenChange(false)
onSuccess()
} else {
toast.error(result?.message || "修改失败")
}
} catch (error) {
console.error("修改用户失败:", error)
const message =
error instanceof Error ? error.message : "修改失败,请稍后重试"
toast.error(`修改失败:${message}`)
} finally {
setIsSubmitting(false)
}
}
const handleOpenChange = (open: boolean) => {
if (!open) {
reset()
}
onOpenChange(open)
}
return (
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle></DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4 py-2">
<div className="grid grid-cols-2 gap-4">
<Controller
name="username"
control={control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel></FieldLabel>
<Input {...field} placeholder="请输入用户名" />
<FieldError>{fieldState.error?.message}</FieldError>
</Field>
)}
/>
<Controller
name="email"
control={control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel></FieldLabel>
<Input {...field} placeholder="请输入邮箱" />
<FieldError>{fieldState.error?.message}</FieldError>
</Field>
)}
/>
<Controller
name="password"
control={control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel></FieldLabel>
<Input
{...field}
type="password"
placeholder="选填,修改请输入新密码至少6位"
/>
<FieldError>{fieldState.error?.message}</FieldError>
</Field>
)}
/>
<Controller
name="confirmPassword"
control={control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel></FieldLabel>
<Input
{...field}
type="password"
placeholder="请再次输入密码"
/>
<FieldError>{fieldState.error?.message}</FieldError>
</Field>
)}
/>
<Controller
name="status"
control={control}
render={({ field }) => (
<Field>
<FieldLabel></FieldLabel>
<Select value={field.value} onValueChange={field.onChange}>
<SelectTrigger>
<SelectValue placeholder="请选择账号状态" />
</SelectTrigger>
<SelectContent>
<SelectItem value="1"></SelectItem>
<SelectItem value="0"></SelectItem>
</SelectContent>
</Select>
</Field>
)}
/>
<Controller
name="admin_id"
control={control}
render={({ field }) => (
<Field>
<FieldLabel></FieldLabel>
<Select value={field.value} onValueChange={field.onChange}>
<SelectTrigger>
<SelectValue placeholder="请选择客户经理" />
</SelectTrigger>
<SelectContent>
{admins.map(admin => (
<SelectItem key={admin.id} value={admin.id.toString()}>
{admin.name}
</SelectItem>
))}
</SelectContent>
</Select>
</Field>
)}
/>
<Controller
name="discount_id"
control={control}
render={({ field }) => (
<Field>
<FieldLabel></FieldLabel>
<Select value={field.value} onValueChange={field.onChange}>
<SelectTrigger>
<SelectValue placeholder="请选择折扣方案" />
</SelectTrigger>
<SelectContent>
{discounts.map(discount => (
<SelectItem
key={discount.id}
value={discount.id.toString()}
>
{discount.name}{discount.discount}%
</SelectItem>
))}
</SelectContent>
</Select>
</Field>
)}
/>
<Controller
name="contact_qq"
control={control}
render={({ field }) => (
<Field>
<FieldLabel>QQ</FieldLabel>
<Input {...field} placeholder="QQ号" />
</Field>
)}
/>
<Controller
name="contact_wechat"
control={control}
render={({ field }) => (
<Field>
<FieldLabel></FieldLabel>
<Input {...field} placeholder="微信号" />
</Field>
)}
/>
</div>
<FieldGroup className="flex-row justify-end gap-2 pt-2">
<Button
type="button"
variant="outline"
onClick={() => handleOpenChange(false)}
disabled={isSubmitting}
>
</Button>
<Button type="submit" disabled={isSubmitting}>
{isSubmitting ? "提交中..." : "确认修改"}
</Button>
</FieldGroup>
</form>
</DialogContent>
</Dialog>
)
}