diff --git a/app/(order)/dispute/[id]/page.tsx b/app/(order)/dispute/[id]/page.tsx index 6a6fcad..c4250ad 100644 --- a/app/(order)/dispute/[id]/page.tsx +++ b/app/(order)/dispute/[id]/page.tsx @@ -4,14 +4,23 @@ import { AlertTriangle, ArrowLeft, Clock, FileText, Upload, X } from "lucide-rea import Image from "next/image" import Link from "next/link" import { useRouter, useSearchParams } from "next/navigation" -import { type ChangeEvent, use, useEffect, useRef, useState } from "react" +import { + type ChangeEvent, + type Dispatch, + type SetStateAction, + use, + useEffect, + useRef, + useState, +} from "react" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Label } from "@/components/ui/label" import { Separator } from "@/components/ui/separator" import { Textarea } from "@/components/ui/textarea" -import { mockDisputes } from "@/lib/mock" +import { useAuthStore } from "@/store/auth" +import { useDisputeStore } from "@/store/disputes" import { useOrderStore } from "@/store/orders" const disputeStatusLabels: Record = { @@ -26,34 +35,55 @@ export default function DisputePage({ params }: { params: Promise<{ id: string } const router = useRouter() const searchParams = useSearchParams() const order = useOrderStore((state) => state.orders.find((item) => item.id === id)) - const updateOrderStatus = useOrderStore((state) => state.updateOrderStatus) - const existingDispute = mockDisputes.find((d) => d.orderId === id) + const userId = useAuthStore((state) => state.user?.id) + const userName = useAuthStore((state) => state.user?.nickname) + const existingDispute = useDisputeStore((state) => state.getDisputeByOrderId(id)) + const submitDispute = useDisputeStore((state) => state.submitDispute) + const submitResponse = useDisputeStore((state) => state.submitResponse) + const submitAppeal = useDisputeStore((state) => state.submitAppeal) + const [reason, setReason] = useState("") - const [submitted, setSubmitted] = useState(false) const [files, setFiles] = useState([]) + const [responseReason, setResponseReason] = useState("") + const [responseFiles, setResponseFiles] = useState([]) + const [appealReason, setAppealReason] = useState("") + const fileInputRef = useRef(null) + const responseFileInputRef = useRef(null) const filesRef = useRef([]) + const responseFilesRef = useRef([]) useEffect(() => { filesRef.current = files }, [files]) + useEffect(() => { + responseFilesRef.current = responseFiles + }, [responseFiles]) + useEffect( () => () => { filesRef.current.forEach((url) => { URL.revokeObjectURL(url) }) + responseFilesRef.current.forEach((url) => { + URL.revokeObjectURL(url) + }) }, [], ) - const handleFileSelect = (event: ChangeEvent) => { + const handleFileSelect = ( + event: ChangeEvent, + setter: Dispatch>, + ) => { const selectedFiles = event.target.files if (!selectedFiles?.length) return - setFiles((prev) => { + setter((prev) => { const remaining = 5 - prev.length if (remaining <= 0) return prev + const nextUrls = Array.from(selectedFiles) .slice(0, remaining) .map((file) => URL.createObjectURL(file)) @@ -63,21 +93,31 @@ export default function DisputePage({ params }: { params: Promise<{ id: string } event.target.value = "" } - const handleRemoveFile = (index: number) => { - setFiles((prev) => { - const removed = prev[index] - if (removed) URL.revokeObjectURL(removed) - return prev.filter((_, currentIndex) => currentIndex !== index) - }) + const removeFile = ( + index: number, + filesState: string[], + setter: Dispatch>, + ) => { + const removed = filesState[index] + if (removed) { + URL.revokeObjectURL(removed) + } + setter((prev) => prev.filter((_, currentIndex) => currentIndex !== index)) } const handleSubmit = () => { - updateOrderStatus(id, "disputed") - setSubmitted(true) + if (!userId || !userName || !reason.trim()) return + submitDispute({ + orderId: id, + initiatorId: userId, + initiatorName: userName, + reason, + evidence: files, + }) router.replace(`/dispute/${id}?submitted=1`) } - const showSubmitted = submitted || searchParams.get("submitted") === "1" + const showSubmitted = searchParams.get("submitted") === "1" && !existingDispute if (!order) { return ( @@ -88,8 +128,15 @@ export default function DisputePage({ params }: { params: Promise<{ id: string } } if (existingDispute) { + const isInitiator = userId === existingDispute.initiatorId + const canRespond = + !isInitiator && + !existingDispute.respondentReason && + (existingDispute.status === "open" || existingDispute.status === "reviewing") + const canAppeal = existingDispute.status === "resolved" && !existingDispute.appealedAt + return ( -
+
{disputeStatusLabels[existingDispute.status]}
- +
@@ -118,32 +165,124 @@ export default function DisputePage({ params }: { params: Promise<{ id: string } {new Date(existingDispute.createdAt).toLocaleString("zh-CN")}
+ -
- -

{existingDispute.reason}

-
- {existingDispute.evidence.length > 0 && ( -
- -
+ +
+ +

{existingDispute.reason}

+ {existingDispute.evidence.length > 0 && ( +
{existingDispute.evidence.map((url) => (
- 证据截图 + 发起方证据
))}
-
+ )} +
+ + + +
+ + {existingDispute.respondentReason ? ( + <> +

{existingDispute.respondentReason}

+ {existingDispute.respondentEvidence.length > 0 && ( +
+ {existingDispute.respondentEvidence.map((url) => ( +
+ 对方证据 +
+ ))} +
+ )} + + ) : ( +

对方暂未提交回应材料。

+ )} +
+ + {canRespond && ( + <> + +
+ +