Files
admin/src/app/(root)/product/create.tsx

260 lines
7.2 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.
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 { createProductSku } from "@/actions/product"
import { getAllProductDiscount } from "@/actions/product_discount"
import { ProductCodeField } from "@/components/products"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import {
Field,
FieldError,
FieldGroup,
FieldLabel,
FieldSeparator,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import type { ProductDiscount } from "@/models/product_discount"
import type { SelectedProduct } from "./type"
const schema = z.object({
code: z.string().min(1, "请输入套餐编码"),
name: z.string().min(1, "请输入套餐名称"),
price: z
.string()
.min(1, "请输入单价")
.refine(
v => !Number.isNaN(Number(v)) && Number(v) > 0,
"请输入有效的正数单价",
),
discount_id: z.string().optional(),
price_min: z
.string()
.optional()
.or(z.literal(""))
.refine(
v => !v || (!Number.isNaN(Number(v)) && Number(v) > 0),
"请输入有效的正数价格",
),
})
export function CreateProductSku(props: {
product?: SelectedProduct
onSuccess?: () => void
}) {
const [open, setOpen] = useState(false)
const [discounts, setDiscounts] = useState<ProductDiscount[]>([])
const form = useForm({
resolver: zodResolver(schema),
defaultValues: {
code: "",
name: "",
price: "",
discount_id: "",
price_min: "",
},
})
useEffect(() => {
if (open) {
getAllProductDiscount()
.then(resp => {
if (resp.success) {
setDiscounts(resp.data)
}
})
.catch(e => toast.error(e.message))
}
}, [open])
const onSubmit = async (data: z.infer<typeof schema>) => {
if (!props.product) return
try {
const resp = await createProductSku({
product_id: props.product.id,
code: data.code,
name: data.name,
price: data.price,
discount_id:
data.discount_id && data.discount_id !== ""
? Number(data.discount_id)
: undefined,
price_min: data.price_min,
})
console.log({
product_id: props.productId,
code: data.code,
name: data.name,
price: data.price,
discount_id:
data.discount_id && data.discount_id !== ""
? Number(data.discount_id)
: undefined,
price_min: data.price_min,
})
console.log(resp, "resp")
if (resp.success) {
form.reset()
toast.success("套餐创建成功")
props.onSuccess?.()
setOpen(false)
} else {
toast.error(resp.message)
}
} catch (error) {
const message = error instanceof Error ? error.message : error
toast.error(`接口请求错误: ${message}`)
}
}
const handleOpenChange = (value: boolean) => {
if (!value) {
form.reset()
}
setOpen(value)
}
return (
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogTrigger asChild>
<Button disabled={!props.product}></Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle></DialogTitle>
</DialogHeader>
<form id="sku-create" onSubmit={form.handleSubmit(onSubmit)}>
<FieldGroup>
<Controller
control={form.control}
name="name"
render={({ field, fieldState }) => (
<Field>
<FieldLabel htmlFor="sku-create-name"></FieldLabel>
<Input
id="sku-create-name"
placeholder="请输入套餐名称"
{...field}
aria-invalid={fieldState.invalid}
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
<Controller
control={form.control}
name="price"
render={({ field, fieldState }) => (
<Field>
<FieldLabel htmlFor="sku-create-price"></FieldLabel>
<Input
id="sku-create-price"
placeholder="请输入单价"
{...field}
aria-invalid={fieldState.invalid}
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
<Controller
control={form.control}
name="price_min"
render={({ field, fieldState }) => (
<Field>
<FieldLabel htmlFor="sku-create-price"></FieldLabel>
<Input
id="sku-create-price"
placeholder="请输入单价"
{...field}
aria-invalid={fieldState.invalid}
/>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
<Controller
control={form.control}
name="discount_id"
render={({ field, fieldState }) => (
<Field>
<FieldLabel></FieldLabel>
<Select value={field.value} onValueChange={field.onChange}>
<SelectTrigger
className="w-full"
aria-invalid={fieldState.invalid}
>
<SelectValue placeholder="选择折扣" />
</SelectTrigger>
<SelectContent>
{discounts.map(d => (
<SelectItem key={d.id} value={String(d.id)}>
{d.name}{d.discount}%
</SelectItem>
))}
</SelectContent>
</Select>
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
<FieldSeparator />
{props.product && (
<ProductCodeField
control={form.control}
name="code"
code={props.product.code}
/>
)}
</FieldGroup>
</form>
<DialogFooter>
<DialogClose asChild>
<Button variant="ghost"></Button>
</DialogClose>
<Button type="submit" form="sku-create">
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}