page.tsx 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. "use client";
  2. import { useEffect, useState, useSyncExternalStore } from "react";
  3. import { ChatWidget } from "@/components/visitor/ChatWidget";
  4. import { FloatingButton } from "@/components/visitor/FloatingButton";
  5. import { isChatEmbedMode } from "@/lib/chat-embed";
  6. import { getOrCreateVisitorId } from "@/lib/visitor-id";
  7. /**
  8. * 访客聊天页面
  9. * - 独立打开:默认展开聊天小窗(仍保留右下角浮动按钮)
  10. * - iframe / ?embed=1:直接铺满,供宿主站点一次点击打开 iframe 即可使用
  11. */
  12. export default function ChatPage() {
  13. const [visitorId, setVisitorId] = useState<number | null>(null);
  14. const [isOpen, setIsOpen] = useState(false);
  15. const embedded = useSyncExternalStore(
  16. () => () => {},
  17. () => isChatEmbedMode(),
  18. () => false
  19. );
  20. useEffect(() => {
  21. setVisitorId(getOrCreateVisitorId());
  22. }, []);
  23. useEffect(() => {
  24. if (!embedded && visitorId !== null) {
  25. setIsOpen(true);
  26. }
  27. }, [embedded, visitorId]);
  28. const handleToggle = () => {
  29. setIsOpen((prev) => !prev);
  30. };
  31. const loadingShell = embedded ? "h-[100dvh] w-full" : "min-h-screen";
  32. if (visitorId === null) {
  33. return (
  34. <div className={`flex items-center justify-center ${loadingShell} bg-muted/30 text-muted-foreground`}>
  35. 正在初始化...
  36. </div>
  37. );
  38. }
  39. if (embedded) {
  40. return (
  41. <div className="h-[100dvh] w-full overflow-hidden bg-white">
  42. <ChatWidget
  43. visitorId={visitorId}
  44. isOpen
  45. embedded
  46. onToggle={() => {}}
  47. />
  48. </div>
  49. );
  50. }
  51. return (
  52. <>
  53. <FloatingButton onClick={handleToggle} isOpen={isOpen} />
  54. {isOpen && (
  55. <ChatWidget visitorId={visitorId} isOpen={isOpen} onToggle={handleToggle} />
  56. )}
  57. </>
  58. );
  59. }