page.tsx 1.9 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. /**
  7. * 访客聊天页面
  8. * - 独立打开:右下角浮动按钮 + 聊天小窗
  9. * - iframe / ?embed=1:直接铺满,供宿主站点一次点击打开 iframe 即可使用
  10. */
  11. export default function ChatPage() {
  12. const [visitorId, setVisitorId] = useState<number | null>(null);
  13. const [isOpen, setIsOpen] = useState(false);
  14. const embedded = useSyncExternalStore(
  15. () => () => {},
  16. () => isChatEmbedMode(),
  17. () => false
  18. );
  19. // 初始化访客 ID(使用 localStorage 保持连续性)
  20. useEffect(() => {
  21. let stored = window.localStorage.getItem("visitor_id");
  22. if (!stored) {
  23. stored = `${Date.now()}${Math.floor(Math.random() * 100000)}`;
  24. window.localStorage.setItem("visitor_id", stored);
  25. }
  26. const parsed = Number.parseInt(stored, 10);
  27. setVisitorId(Number.isNaN(parsed) ? null : parsed);
  28. }, []);
  29. const handleToggle = () => {
  30. setIsOpen((prev) => !prev);
  31. };
  32. const loadingShell = embedded ? "h-[100dvh] w-full" : "min-h-screen";
  33. if (visitorId === null) {
  34. return (
  35. <div className={`flex items-center justify-center ${loadingShell} bg-muted/30 text-muted-foreground`}>
  36. 正在初始化...
  37. </div>
  38. );
  39. }
  40. if (embedded) {
  41. return (
  42. <div className="h-[100dvh] w-full overflow-hidden bg-white">
  43. <ChatWidget
  44. visitorId={visitorId}
  45. isOpen
  46. embedded
  47. onToggle={() => {}}
  48. />
  49. </div>
  50. );
  51. }
  52. return (
  53. <>
  54. <FloatingButton onClick={handleToggle} isOpen={isOpen} />
  55. {isOpen && (
  56. <ChatWidget visitorId={visitorId} isOpen={isOpen} onToggle={handleToggle} />
  57. )}
  58. </>
  59. );
  60. }