diff --git a/web/components/kefu/chat-shell.tsx b/web/components/kefu/chat-shell.tsx index c6c3327..b16f975 100644 --- a/web/components/kefu/chat-shell.tsx +++ b/web/components/kefu/chat-shell.tsx @@ -7,7 +7,7 @@ import { RotateCwIcon, XIcon, } from "lucide-react" -import { useCallback, useEffect, useRef, useState } from "react" +import { useCallback, useEffect, useRef, useState, type CSSProperties } from "react" import { useShallow } from "zustand/react/shallow" import { KefuConnectionStatus } from "@/components/kefu/connection-status" @@ -23,6 +23,15 @@ import { requestKefuHostToggleMaximize, } from "@/lib/kefu-host-bridge" import { useKefuChatStore } from "@/lib/stores/kefu-chat" +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" export function KefuChatShell() { const messageListRef = useRef(null) @@ -191,74 +200,81 @@ export function KefuChatShell() { return (
-
-
-
-
+
+
+
-
+
{title}
-
{subtitle}
+
{subtitle}
{status !== "connected" ? ( ) : null} -
- + + {showHostActions ? ( - + + ) : null} {showHostActions ? ( - + ) : null} - + +
-
+
- {isCloseDialogOpen ? ( -
-
-
-
-
- 结束当前对话? -
-
- 结束会话,客服将无法再查看您的消息记录,如需再次联系请重新发起对话。 -
-
-
- -
- - -
-
-
- ) : null} + { + if (!isClosingConversation) { + setIsCloseDialogOpen(open) + } + }} + > + + + 结束当前对话? + + 结束会话,客服将无法再查看您的消息记录,如需再次联系请重新发起对话。 + + + + + + + +
) } diff --git a/web/components/kefu/connection-status.tsx b/web/components/kefu/connection-status.tsx index 02dd3f7..a27811f 100644 --- a/web/components/kefu/connection-status.tsx +++ b/web/components/kefu/connection-status.tsx @@ -1,5 +1,6 @@ "use client" +import { Badge } from "@/components/ui/badge" import { cn } from "@/lib/utils" type KefuConnectionStatusProps = { @@ -15,17 +16,15 @@ const statusText: Record = { export function KefuConnectionStatus({ status }: KefuConnectionStatusProps) { const toneClass = status === "connected" - ? "border-emerald-200/80 bg-emerald-50 text-emerald-700" + ? "border-emerald-200 bg-emerald-50 text-emerald-700" : status === "connecting" - ? "border-amber-200/80 bg-amber-50 text-amber-700" - : "border-slate-200/80 bg-slate-100 text-slate-600" + ? "border-amber-200 bg-amber-50 text-amber-700" + : "border-slate-200 bg-slate-100 text-slate-600" return ( -
{statusText[status]} -
+ ) } - diff --git a/web/components/kefu/message-editor.tsx b/web/components/kefu/message-editor.tsx index e8a7379..d40b077 100644 --- a/web/components/kefu/message-editor.tsx +++ b/web/components/kefu/message-editor.tsx @@ -7,6 +7,7 @@ import Placeholder from "@tiptap/extension-placeholder" import StarterKit from "@tiptap/starter-kit" import { ImageIcon, PaperclipIcon, SendHorizonalIcon } from "lucide-react" +import { Button } from "@/components/ui/button" import { generateUUID } from "@/lib/utils" type UploadedImage = { @@ -99,7 +100,7 @@ export function KefuMessageEditor({ editorProps: { attributes: { class: - "min-h-12 max-h-40 overflow-y-auto px-1.5 py-1 text-[13px] leading-6 text-slate-900 outline-none [&_p]:m-0 [&_p+*]:mt-2 [&_img]:my-2 [&_img]:max-h-64 [&_img]:rounded-xl [&_img]:object-contain", + "min-h-12 max-h-40 overflow-y-auto px-1.5 py-1 text-sm leading-6 text-slate-900 outline-none [&_p]:m-0 [&_p+*]:mt-2 [&_img]:my-2 [&_img]:max-h-64 [&_img]:rounded-lg [&_img]:object-contain", }, handleKeyDown: (_view, event) => { if (event.key === "Enter" && !event.shiftKey) { @@ -232,8 +233,8 @@ export function KefuMessageEditor({ } return ( -
-
+
+
-
+
- - + + +
-
+

Enter 发送

- + +
diff --git a/web/components/kefu/message-list.tsx b/web/components/kefu/message-list.tsx index 819caab..645e991 100644 --- a/web/components/kefu/message-list.tsx +++ b/web/components/kefu/message-list.tsx @@ -1,6 +1,5 @@ "use client" -import Image from "next/image" import { forwardRef, memo, @@ -13,6 +12,9 @@ import { import { ImMessageHTML } from "@/components/im-message-html" import { useImageLightbox } from "@/components/image-lightbox" +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" import type { ImMessage } from "@/lib/api/im" import { renderIMMessageHTML } from "@/lib/im-message" import { cn, formatDateTime } from "@/lib/utils" @@ -211,14 +213,16 @@ export const KefuMessageList = forwardRef {hasMoreOlder && onLoadOlder ? (
- +
) : null} @@ -257,35 +261,38 @@ const MessageItem = memo( const avatarSrc = !isCustomer && message.senderAvatar?.trim() ? message.senderAvatar.trim() : undefined const htmlContent = renderIMMessageHTML(message) + const fallbackName = senderName.slice(0, 1).toUpperCase() return ( -
+
{showTimeline ? (
-
+ {getTimelineLabel(message.sentAt)} -
+
) : null} -
- {!isCustomer && avatarSrc ? ( - +
+ {!isCustomer ? ( + + {avatarSrc ? : null} + + {fallbackName || "客"} + + ) : null}
-
+
{senderName} {formatDateTime(message.sentAt)} {isCustomer ? ( @@ -294,10 +301,10 @@ const MessageItem = memo(