"use client" import Link from "next/link" import { useEffect, useState } from "react" import { zodResolver } from "@hookform/resolvers/zod" import { Settings2Icon } from "lucide-react" import { Controller, Resolver, useForm } from "react-hook-form" import { toast } from "sonner" import { z } from "zod/v4" import { OptionCombobox } from "@/components/option-combobox" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Field, FieldContent, FieldError, FieldLabel } from "@/components/ui/field" import { Textarea } from "@/components/ui/textarea" import { fetchTicketResolutionCodesAll, type TicketResolutionCode, } from "@/lib/api/ticket-config" import { batchChangeTicketStatus, changeTicketStatus } from "@/lib/api/ticket" const schema = z.object({ status: z.string().trim().min(1, "请选择状态"), pendingReason: z.string().trim(), closeReason: z.string().trim(), resolutionCode: z.string().trim(), resolutionSummary: z.string().trim(), reason: z.string().trim(), }) type FormValues = z.infer const resolver = zodResolver(schema as never) as Resolver< z.input, undefined, z.output > type TicketStatusDialogProps = { open: boolean ticketId: number | null ticketIds?: number[] currentStatus?: string onOpenChange: (open: boolean) => void onSuccess?: () => Promise | void } export function TicketStatusDialog({ open, ticketId, ticketIds, currentStatus, onOpenChange, onSuccess, }: TicketStatusDialogProps) { const form = useForm< z.input, undefined, z.output >({ resolver, defaultValues: { status: "", pendingReason: "", closeReason: "", resolutionCode: "", resolutionSummary: "", reason: "", }, }) const { control, watch, register, reset, handleSubmit, formState: { errors, isSubmitting }, } = form const [resolutionCodes, setResolutionCodes] = useState([]) const targetStatus = watch("status") useEffect(() => { reset({ status: currentStatus || "", pendingReason: "", closeReason: "", resolutionCode: "", resolutionSummary: "", reason: "", }) }, [currentStatus, reset, ticketId]) useEffect(() => { if (!open) { return } void (async () => { try { const data = await fetchTicketResolutionCodesAll() setResolutionCodes(Array.isArray(data) ? data : []) } catch (error) { toast.error(error instanceof Error ? error.message : "加载解决码失败") } })() }, [open]) const resolutionCodeOptions = resolutionCodes.map((item) => ({ value: item.code, label: item.name, })) async function onFormSubmit(values: FormValues) { const validTicketIds = (ticketIds ?? []).filter((item) => item > 0) if (!ticketId && validTicketIds.length === 0) { toast.error("请选择工单") return } try { if (validTicketIds.length > 0) { await batchChangeTicketStatus({ ticketIds: validTicketIds, status: values.status, pendingReason: values.pendingReason || undefined, closeReason: values.status === "closed" ? values.closeReason || undefined : undefined, resolutionCode: values.resolutionCode || undefined, resolutionSummary: values.resolutionSummary || undefined, reason: values.reason || undefined, }) toast.success(`已批量更新 ${validTicketIds.length} 张工单`) } else { await changeTicketStatus({ ticketId: ticketId!, status: values.status, pendingReason: values.pendingReason || undefined, closeReason: values.status === "closed" ? values.closeReason || undefined : undefined, resolutionCode: values.resolutionCode || undefined, resolutionSummary: values.resolutionSummary || undefined, reason: values.reason || undefined, }) toast.success("状态已更新") } onOpenChange(false) await onSuccess?.() } catch (error) { toast.error(error instanceof Error ? error.message : "更新状态失败") } } return ( {ticketIds?.length ? `批量变更状态(${ticketIds.length})` : "变更工单状态"}
目标状态 ( )} /> {(targetStatus === "pending_customer" || targetStatus === "pending_internal") && ( 挂起原因