feat(ui): refine order chat pages

This commit is contained in:
zetaloop
2026-04-25 21:15:43 +08:00
parent b0cecd58b0
commit 8e02c8ca97
4 changed files with 115 additions and 75 deletions
+36 -16
View File
@@ -4,6 +4,7 @@ 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"
@@ -11,7 +12,7 @@ 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, Send } from "lucide-react"
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"
@@ -55,22 +56,32 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
if (loading) {
return (
<div className="container mx-auto py-8 px-4 text-center text-muted-foreground">...</div>
<div className="container mx-auto max-w-2xl px-4 py-8">
<EmptyState title="加载中" description="正在读取会话内容..." icon={MessageSquare} />
</div>
)
}
if (!session) {
return (
<div className="container mx-auto py-8 px-4 text-center text-muted-foreground">
<div className="container mx-auto max-w-2xl px-4 py-8">
<EmptyState
title="会话不存在"
description="该会话可能已被删除或暂不可访问。"
icon={MessageSquare}
/>
</div>
)
}
if (!user?.id) {
return (
<div className="container mx-auto py-8 px-4 text-center text-muted-foreground">
<div className="container mx-auto max-w-2xl px-4 py-8">
<EmptyState
title="请先登录"
description="登录后可查看会话并发送消息。"
icon={MessageSquare}
/>
</div>
)
}
@@ -79,8 +90,12 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
const isParticipant = session.participants.some((participant) => participant.id === userId)
if (!isParticipant) {
return (
<div className="container mx-auto py-8 px-4 text-center text-muted-foreground">
<div className="container mx-auto max-w-2xl px-4 py-8">
<EmptyState
title="无法查看会话"
description="仅会话参与方可查看并发送消息。"
icon={MessageSquare}
/>
</div>
)
}
@@ -89,8 +104,8 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
return (
<div className="container mx-auto max-w-3xl px-4 py-8 h-[calc(100vh-3.5rem)] flex flex-col">
<Card className="flex-1 flex flex-col overflow-hidden hover:shadow-card-hover">
<div className="border-b px-4 py-3 flex items-center gap-3 bg-muted/30">
<Card className="flex-1 flex flex-col overflow-hidden border-border/80 shadow-sm">
<div className="border-b border-border/60 px-4 py-3 flex items-center gap-3 bg-background">
<Link href="/chat" className="text-muted-foreground hover:text-foreground">
<ArrowLeft className="h-5 w-5" />
</Link>
@@ -101,7 +116,10 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
<div>
<span className="text-sm font-medium">{other.nickname}</span>
<div className="flex items-center gap-1">
<Badge variant="outline" className="text-[10px] px-1.5 py-0">
<Badge
variant={session.type === "order" ? "info" : "neutral"}
className="text-[10px] px-1.5 py-0 font-normal"
>
{session.type === "order" ? "订单会话" : "咨询会话"}
</Badge>
</div>
@@ -114,7 +132,7 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
if (msg.type === "system") {
return (
<div key={msg.id} className="text-center">
<span className="text-xs text-muted-foreground bg-muted px-2 py-1 rounded">
<span className="text-xs text-muted-foreground bg-muted/60 px-2 py-1 rounded-full">
{msg.content}
</span>
</div>
@@ -143,8 +161,10 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
) : (
<p
className={cn(
"inline-block rounded-lg px-3 py-2 text-sm",
isMine ? "bg-primary text-primary-foreground" : "bg-muted",
"inline-block rounded-xl border px-3 py-2 text-sm",
isMine
? "border-primary bg-primary text-primary-foreground"
: "border-border/60 bg-muted/60",
)}
>
{msg.content}
@@ -163,7 +183,7 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
</div>
</ScrollArea>
<div className="border-t p-4 bg-muted/30">
<div className="border-t border-border/60 p-4 bg-background">
<input
ref={imageInputRef}
type="file"
@@ -209,7 +229,7 @@ export default function ChatDetailPage({ params }: { params: Promise<{ id: strin
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="输入消息..."
className="flex-1"
className="flex-1 border-border/60"
/>
<Button
type="button"
+31 -27
View File
@@ -2,7 +2,7 @@
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent } from "@/components/ui/card"
import { EmptyState } from "@/components/ui/empty-state"
import { listChatSessions } from "@/lib/api"
import { useAuthStore } from "@/store/auth"
import { MessageSquare } from "lucide-react"
@@ -34,15 +34,19 @@ export default function ChatListPage() {
<div className="container mx-auto max-w-2xl px-4 py-8 space-y-6">
<h1 className="text-2xl font-bold"></h1>
<div className="flex flex-col gap-3">
{sessions.map((session) => {
const other =
session.participants.find((participant) => participant.id !== userId) ??
session.participants[0]
return (
<Link key={session.id} href={`/chat/${session.id}`} className="block">
<Card className="p-0 hover:bg-muted/50 transition-colors">
<CardContent className="flex items-center gap-3 p-4">
{sessions.length > 0 ? (
<div className="overflow-hidden rounded-xl border border-border/80 bg-card shadow-sm">
{sessions.map((session) => {
const other =
session.participants.find((participant) => participant.id !== userId) ??
session.participants[0]
return (
<Link
key={session.id}
href={`/chat/${session.id}`}
className="block border-b border-border/60 transition-colors last:border-0 hover:bg-muted/10"
>
<div className="flex items-center gap-3 p-4">
<Avatar className="h-10 w-10">
<AvatarImage src={other.avatar} />
<AvatarFallback>{other.nickname[0]}</AvatarFallback>
@@ -50,7 +54,10 @@ export default function ChatListPage() {
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2">
<span className="text-sm font-medium">{other.nickname}</span>
<Badge variant="outline" className="text-[10px] px-1.5 py-0">
<Badge
variant={session.type === "order" ? "info" : "neutral"}
className="text-[10px] px-1.5 py-0 font-normal"
>
{session.type === "order" ? "订单" : "咨询"}
</Badge>
</div>
@@ -58,26 +65,23 @@ export default function ChatListPage() {
</div>
<div className="flex flex-col items-end gap-1 shrink-0">
{session.unreadCount > 0 && (
<Badge className="h-4 min-w-4 px-1 flex items-center justify-center text-[10px]">
<Badge className="h-4 min-w-4 px-1 flex items-center justify-center rounded-full text-[10px]">
{session.unreadCount}
</Badge>
)}
</div>
</CardContent>
</Card>
</Link>
)
})}
{sessions.length === 0 && (
<Card className="hover:shadow-card-hover">
<CardContent className="py-8 text-center text-sm text-muted-foreground">
<MessageSquare className="h-12 w-12 mx-auto mb-2 opacity-50" />
</CardContent>
</Card>
)}
</div>
</div>
</Link>
)
})}
</div>
) : (
<EmptyState
title="暂无消息"
description="订单沟通和咨询会话会显示在这里。"
icon={MessageSquare}
/>
)}
</div>
)
}