"use client" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card } from "@/components/ui/card" import { EmptyState } from "@/components/ui/empty-state" import { Input } from "@/components/ui/input" import { ScrollArea } from "@/components/ui/scroll-area" import { getChatSessionById, listChatMessages } from "@/lib/api" import { sendImageMessage, sendTextMessage } from "@/lib/api/chat" import { notifyInfo } from "@/lib/toast" import { cn } from "@/lib/utils" import { useAuthStore } from "@/store/auth" import { ArrowLeft, ImagePlus, MessageSquare, Send } from "lucide-react" import Image from "next/image" import Link from "next/link" import { use, useEffect, useRef, useState } from "react" export default function ChatDetailPage({ params }: { params: Promise<{ id: string }> }) { const { id } = use(params) const [loading, setLoading] = useState(true) const [session, setSession] = useState< Awaited> | undefined >(undefined) const [messages, setMessages] = useState>>([]) const [input, setInput] = useState("") const imageInputRef = useRef(null) const { user } = useAuthStore() useEffect(() => { let cancelled = false void Promise.all([ Promise.resolve(getChatSessionById(id)), Promise.resolve(listChatMessages(id)), ]) .then(([nextSession, nextMessages]) => { if (cancelled) return setSession(nextSession) setMessages(nextMessages) }) .catch(() => { if (cancelled) return setSession(undefined) setMessages([]) }) .finally(() => { if (cancelled) return setLoading(false) }) return () => { cancelled = true } }, [id]) if (loading) { return (
) } if (!session) { return (
) } if (!user?.id) { return (
) } const userId = user.id const isParticipant = session.participants.some((participant) => participant.id === userId) if (!isParticipant) { return (
) } const other = session.participants.find((p) => p.id !== userId) ?? session.participants[0] return (
{other.nickname[0]}
{other.nickname}
{session.type === "order" ? "订单会话" : "咨询会话"}
{messages.map((msg) => { if (msg.type === "system") { return (
{msg.content}
) } const isMine = msg.senderId === userId const sender = session.participants.find( (participant) => participant.id === msg.senderId, ) return (
{(sender?.nickname ?? "?")[0]}
{msg.type === "image" ? ( 聊天图片 ) : (

{msg.content}

)}

{new Date(msg.createdAt).toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit", })}

) })}
{ const target = event.currentTarget const file = target.files?.[0] if (!file) return const imageUrl = URL.createObjectURL(file) void Promise.resolve(sendImageMessage(session.id, imageUrl)) .then((result) => { if (!result.ok) { notifyInfo(result.error.msg) return } return Promise.resolve(listChatMessages(session.id)).then(setMessages) }) .finally(() => { target.value = "" }) }} />
{ e.preventDefault() const text = input.trim() if (!text) return void Promise.resolve(sendTextMessage(session.id, text)).then((result) => { if (!result.ok) { notifyInfo(result.error.msg) return } setInput("") return Promise.resolve(listChatMessages(session.id)).then(setMessages) }) }} > setInput(e.target.value)} placeholder="输入消息..." className="flex-1 border-border/60" />
) }