diff --git a/web/app/dashboard/tickets/_components/ticket-detail-dialog.tsx b/web/app/dashboard/tickets/_components/ticket-detail-dialog.tsx index 8e70d1f..7024577 100644 --- a/web/app/dashboard/tickets/_components/ticket-detail-dialog.tsx +++ b/web/app/dashboard/tickets/_components/ticket-detail-dialog.tsx @@ -19,10 +19,14 @@ import { changeTicketStatus, createTicketProgress, fetchTicketDetail, + type CreateTicketPayload, type TicketDetail, type TicketStatus, + type UpdateTicketPayload, + updateTicket, } from "@/lib/api/ticket" import { cn, formatDateTime } from "@/lib/utils" +import { EditDialog } from "./edit" import { TicketAssignDialog } from "./ticket-assign-dialog" import { TicketStatusBadge } from "./ticket-status-badge" @@ -69,6 +73,8 @@ export function TicketDetailDialog({ const [progressSaving, setProgressSaving] = useState(false) const [progressContent, setProgressContent] = useState("") const [assignOpen, setAssignOpen] = useState(false) + const [editOpen, setEditOpen] = useState(false) + const [editSaving, setEditSaving] = useState(false) const loadDetail = useCallback(async () => { if (!open || !ticketId) { @@ -138,6 +144,25 @@ export function TicketDetailDialog({ onChanged() } + async function handleUpdateTicket(payload: CreateTicketPayload | UpdateTicketPayload) { + if (!("ticketId" in payload) || payload.ticketId <= 0) { + toast.error("请选择工单") + return + } + setEditSaving(true) + try { + await updateTicket(payload) + toast.success("工单已更新") + setEditOpen(false) + await loadDetail() + onChanged() + } catch (error) { + toast.error(error instanceof Error ? error.message : "更新工单失败") + } finally { + setEditSaving(false) + } + } + const ticket = detail?.ticket return ( @@ -206,7 +231,12 @@ export function TicketDetailDialog({
-
标签
+
+
标签
+ +
{ticket.tags && ticket.tags.length > 0 ? (
{ticket.tags.map((tag) => ( @@ -295,6 +325,13 @@ export function TicketDetailDialog({ onOpenChange={setAssignOpen} onSuccess={handleAssigned} /> + ) } diff --git a/web/app/dashboard/tickets/page.tsx b/web/app/dashboard/tickets/page.tsx index 656a79f..12ce243 100644 --- a/web/app/dashboard/tickets/page.tsx +++ b/web/app/dashboard/tickets/page.tsx @@ -1,6 +1,6 @@ "use client" -import { RefreshCcwIcon, SearchXIcon } from "lucide-react" +import { PlusIcon, RefreshCcwIcon, SearchXIcon } from "lucide-react" import { useCallback, useEffect, useMemo, useState } from "react" import { toast } from "sonner" @@ -24,14 +24,17 @@ import { type TagTree, } from "@/lib/api/admin" import { + createTicket, fetchTicketSummary, fetchTickets, + type CreateTicketPayload, type TicketItem, type TicketListQuery, type TicketStatus, type TicketSummary, } from "@/lib/api/ticket" import { cn, formatDateTime } from "@/lib/utils" +import { EditDialog } from "./_components/edit" import { TicketAssignDialog } from "./_components/ticket-assign-dialog" import { TicketDetailDialog } from "./_components/ticket-detail-dialog" import { TicketStatusBadge } from "./_components/ticket-status-badge" @@ -114,6 +117,8 @@ export default function TicketsPage() { const [batchAssignOpen, setBatchAssignOpen] = useState(false) const [selectedTicketId, setSelectedTicketId] = useState(null) const [detailOpen, setDetailOpen] = useState(false) + const [createOpen, setCreateOpen] = useState(false) + const [savingCreate, setSavingCreate] = useState(false) const quickViews = useMemo( () => @@ -237,6 +242,20 @@ export default function TicketsPage() { setStaleHours("24") } + async function handleCreateTicket(payload: CreateTicketPayload) { + setSavingCreate(true) + try { + await createTicket(payload) + toast.success("工单已创建") + setCreateOpen(false) + await loadData() + } catch (error) { + toast.error(error instanceof Error ? error.message : "创建工单失败") + } finally { + setSavingCreate(false) + } + } + return (
@@ -244,10 +263,16 @@ export default function TicketsPage() {

工单

按状态、负责人和标签快速处理轻量工单

- +
+ + +
@@ -431,6 +456,13 @@ export default function TicketsPage() { onOpenChange={setBatchAssignOpen} onSuccess={loadData} /> +