Explorar o código

引入livechat

fogwind hai 4 días
pai
achega
03f738eb4d

+ 1 - 0
package.json

@@ -32,6 +32,7 @@
     "@heroui/tooltip": "^2.2.29",
     "@heroui/use-disclosure": "^2.2.19",
     "@internationalized/date": "3.12.0",
+    "@livechat/widget-react": "^1.4.1",
     "@paypal/react-paypal-js": "^10.1.2",
     "@react-aria/visually-hidden": "^3.8.31",
     "@react-types/shared": "^3.33.1",

+ 25 - 0
pnpm-lock.yaml

@@ -75,6 +75,9 @@ importers:
       '@internationalized/date':
         specifier: 3.12.0
         version: 3.12.0
+      '@livechat/widget-react':
+        specifier: ^1.4.1
+        version: 1.4.1(react@19.2.5)
       '@paypal/react-paypal-js':
         specifier: ^10.1.2
         version: 10.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
@@ -906,6 +909,14 @@ packages:
   '@jridgewell/trace-mapping@0.3.9':
     resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
 
+  '@livechat/widget-core@1.4.1':
+    resolution: {integrity: sha512-ZD7Q5nhCImmyCmcu87g1emgnp7Ta/gM9bwWlc/ZhjPJvO4gOMzycEu97xtAY+w3X4Cw1KJ59WVi22xN1tQ9fYQ==}
+
+  '@livechat/widget-react@1.4.1':
+    resolution: {integrity: sha512-DjpbDGGLem4n5EFjavh/piJWuUACI7Rb+3mtuhJgqhRVCTnx8tWyrPQ/WGzfmRagv+LT7FJ9d6m2x6+b5koPvg==}
+    peerDependencies:
+      react: ^16.8 || 17 || 18 || 19
+
   '@napi-rs/wasm-runtime@1.1.4':
     resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==}
     peerDependencies:
@@ -2840,6 +2851,9 @@ packages:
   minimist@1.2.8:
     resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
 
+  mitt@3.0.0:
+    resolution: {integrity: sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==}
+
   motion-dom@12.39.0:
     resolution: {integrity: sha512-Xn7aAcGDhco/JZTXOub64UmaYn73C6J1Po7Fk+8EvkJsNGTqfhon6UJY53vJKXW5v5Zl8HrYsVxv6oPXeGoGLQ==}
 
@@ -4511,6 +4525,15 @@ snapshots:
       '@jridgewell/resolve-uri': 3.1.2
       '@jridgewell/sourcemap-codec': 1.5.5
 
+  '@livechat/widget-core@1.4.1':
+    dependencies:
+      mitt: 3.0.0
+
+  '@livechat/widget-react@1.4.1(react@19.2.5)':
+    dependencies:
+      '@livechat/widget-core': 1.4.1
+      react: 19.2.5
+
   '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
     dependencies:
       '@emnapi/core': 1.10.0
@@ -6773,6 +6796,8 @@ snapshots:
 
   minimist@1.2.8: {}
 
+  mitt@3.0.0: {}
+
   motion-dom@12.39.0:
     dependencies:
       motion-utils: 12.39.0

+ 5 - 3
src/app/(public)/_components/HomeContent.tsx

@@ -12,7 +12,8 @@ import RecommendedProducts from "./RecommendedProducts";
 import CategoryTabSwiper  from "./CategoryTabSwiper ";
 import "swiper/css";
 import "swiper/css/navigation";
-import {HomeApiData,HomeProduct } from "@/types/home/type"
+import type {HomeApiData,HomeProduct } from "@/types/home/type"
+import {CustomerService} from "@/components/common/CustomerService/CustomerService";
 // 定义组件入参类型
 interface HomeContentProps {
   homeData: HomeApiData;
@@ -125,7 +126,7 @@ export default function HomeContent({ homeData }: HomeContentProps) {
    // 4. Recommended
   const recommendedSection = sections.find(sec => sec.name === "Recommended_For_You");
   const recommended: HomeProduct[] = recommendedSection?.products ?? [];
-  return (
+  return (<>
     <div className="min-h-screen bg-white font-sans">
       {/* 顶部Banner */}
       <div className="w-full relative">
@@ -152,5 +153,6 @@ export default function HomeContent({ homeData }: HomeContentProps) {
       <RecommendedProducts recommended={recommended} />
       <CategoryTabSwiper />
     </div>
-  );
+    <CustomerService />
+  </>);
 }

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 38 - 0
src/components/common/CustomerService/CustomerService.tsx


+ 42 - 0
src/components/common/CustomerService/LiveChat/LiveChat.tsx

@@ -0,0 +1,42 @@
+"use client";
+
+import { 
+    LiveChatWidget, 
+    type EventHandlerPayload, 
+    type WidgetState 
+} from "@livechat/widget-react";
+
+export type LiveChatVisibilityType = WidgetState['visibility'];
+
+export function LiveChat({
+    visibility,
+    readyHandler,
+    visibilityChangeHandler
+}: {
+    visibility: LiveChatVisibilityType;
+    readyHandler: (event: EventHandlerPayload<"onReady">) => void;
+    visibilityChangeHandler: (str: LiveChatVisibilityType) => void;
+}) {
+
+
+    function handleReadyEvent(event: EventHandlerPayload<"onReady">) {
+        readyHandler(event);
+    }
+    function handleVisibilityChangedEvent(event: EventHandlerPayload<"onVisibilityChanged">) {
+        // console.log("LiveChatWidget.onVisibilityChanged", event)
+        // if(event.visibility === "minimized") {
+        //     setVisibility('hidden');
+        // }
+        visibilityChangeHandler(event.visibility);
+    }
+
+    return (<>
+
+        <LiveChatWidget
+            license="8982890"
+            visibility={visibility}
+            onReady={handleReadyEvent}
+            onVisibilityChanged={handleVisibilityChangedEvent}
+        />
+    </>)
+}

+ 4 - 1
src/providers/GlobalProviders.tsx

@@ -1,12 +1,13 @@
 "use client";
 
-import { ReactNode } from "react";
+import { type ReactNode } from "react";
 import { SessionProvider } from "next-auth/react";
 import { ThemeProvider } from "./ThemeProvider";
 import { ToastProvider } from "./ToastProvider";
 import { ApolloWrapper } from "./ApolloWrapper";
 import { PaymentSDKProvider } from "./PaymentSDKProvider";
 import { LoginModalProvider } from "./LoginModalProvider";
+import { LiveChatProvider } from "./LiveChatProvider";
 
 export function GlobalProviders({ children }: { children: ReactNode }) {
   return (
@@ -15,9 +16,11 @@ export function GlobalProviders({ children }: { children: ReactNode }) {
         <ToastProvider>
           <ApolloWrapper>
             <PaymentSDKProvider>
+              <LiveChatProvider>
               <LoginModalProvider>
                 {children}
               </LoginModalProvider>
+              </LiveChatProvider>
             </PaymentSDKProvider>
           </ApolloWrapper>
         </ToastProvider>

+ 97 - 0
src/providers/LiveChatProvider.tsx

@@ -0,0 +1,97 @@
+"use client";
+
+import {
+    createContext,
+    useContext,
+    useRef,
+    useState,
+    useCallback,
+    type ReactNode,
+} from "react";
+import { LiveChat, type LiveChatVisibilityType } from "@/components/common/CustomerService/LiveChat/LiveChat";
+
+type LiveChatContextType = {
+    visibility: LiveChatVisibilityType;
+    show: () => void;
+    hide: () => void;
+    openMiniMode: () => void;
+    closeMiniMode: () => void;
+};
+
+export const LiveChatContext = createContext<LiveChatContextType | null>(null);
+
+
+export function LiveChatProvider({
+    children
+}:{
+    children:ReactNode;
+}){
+
+    const [visibility, setVisibility]=useState<LiveChatVisibilityType>('hidden');
+    const isUseMiniRef = useRef(false);
+
+    const openMiniMode = useCallback(() => {
+        isUseMiniRef.current = true;
+    },[]);
+    const closeMiniMode = useCallback(() => {
+        isUseMiniRef.current = false;
+    },[]);
+
+    const show = useCallback(()=>{
+
+        setVisibility('maximized');
+
+    },[]);
+
+
+    const hide = useCallback(()=>{
+
+        setVisibility('hidden');
+
+    },[]);
+
+    const visibilityChanged = (str: LiveChatVisibilityType) => {
+        if(str === 'minimized' && !isUseMiniRef.current) {
+            hide();
+        }
+    };
+
+    const ready = () => {};
+    return (
+        <LiveChatContext.Provider
+            value={{
+                visibility,
+                show,
+                hide,
+                closeMiniMode,
+                openMiniMode
+            }}
+        >
+            {children}
+            <LiveChat 
+                visibility={visibility}
+                readyHandler={ready}
+                visibilityChangeHandler={visibilityChanged}
+            />
+        </LiveChatContext.Provider>
+        
+
+    );
+
+}
+
+export function useLiveChat(){
+
+    const context = useContext(LiveChatContext);
+
+
+    if(!context){
+        throw new Error(
+            "useLiveChat must be used within LiveChatProvider."
+        );
+    }
+
+
+    return context;
+
+}