"use client" import { useEffect, useMemo, useState } from "react" import type { KefuWidgetHostConfig } from "@/lib/kefu-widget-config" import { generateUUID } from "@/lib/utils" const STORAGE_KEY = "cs-agent-web-widget-test-config" const INITIAL_CONFIG: KefuWidgetHostConfig = { channelId: "", baseUrl: "", apiBaseUrl: "", externalSource: "web_chat", title: "在线客服", subtitle: "欢迎咨询", position: "right", themeColor: "#2563eb", width: "680px", subject: "", } declare global { interface Window { CSAgentWidget?: { mount: (config: KefuWidgetHostConfig) => void destroy: () => void open: () => void close: () => void } } } function generateRandomSubject() { return `访客-${generateUUID().replace(/-/g, "").slice(0, 8)}` } function getDefaultConfig(): KefuWidgetHostConfig { if (typeof window === "undefined") { return INITIAL_CONFIG } const savedText = window.localStorage.getItem(STORAGE_KEY) const savedConfig = savedText ? (JSON.parse(savedText) as Partial) : {} const query = new URLSearchParams(window.location.search) const origin = window.location.origin return { channelId: query.get("channelId") ?? savedConfig.channelId ?? "", baseUrl: query.get("baseUrl") ?? savedConfig.baseUrl ?? origin, apiBaseUrl: query.get("apiBaseUrl") ?? savedConfig.apiBaseUrl ?? savedConfig.baseUrl ?? origin, externalSource: query.get("externalSource") ?? savedConfig.externalSource ?? "web_chat", title: query.get("title") ?? savedConfig.title ?? "在线客服", subtitle: query.get("subtitle") ?? savedConfig.subtitle ?? "欢迎咨询", position: (query.get("position") as "left" | "right" | null) ?? savedConfig.position ?? "right", themeColor: query.get("themeColor") ?? savedConfig.themeColor ?? "#2563eb", width: query.get("width") ?? savedConfig.width ?? "680px", subject: query.get("subject") ?? savedConfig.subject ?? generateRandomSubject(), } } function removeMountedWidget() { if (typeof window === "undefined") { return } window.CSAgentWidget?.destroy() document .querySelectorAll( '[data-cs-agent-widget="launcher"], [data-cs-agent-widget="frame"], [data-cs-agent-widget="script"]' ) .forEach((node) => node.remove()) delete window.CSAgentConfig delete window.__CS_AGENT_WIDGET_CONFIG__ delete window.__CS_AGENT_WIDGET_STATE__ delete window.CSAgentWidget } function injectWidget(config: KefuWidgetHostConfig) { removeMountedWidget() window.CSAgentConfig = config const script = document.createElement("script") script.async = true script.src = `${window.location.origin}/sdk/cs-agent-widget.js` script.dataset.csAgentWidget = "script" document.body.appendChild(script) } export function KefuWidgetDemo() { const [config, setConfig] = useState(INITIAL_CONFIG) const [status, setStatus] = useState("请填写 channelId") useEffect(() => { const initialConfig = getDefaultConfig() setConfig(initialConfig) setStatus(initialConfig.channelId ? "Widget 已挂载" : "请填写 channelId") if (initialConfig.channelId) { injectWidget(initialConfig) } return () => { removeMountedWidget() } }, []) const snippet = useMemo(() => { const scriptSrc = config.baseUrl ? `${config.baseUrl.replace(/\/$/, "")}/sdk/cs-agent-widget.js` : "/sdk/cs-agent-widget.js" return ` ` }, [config]) function updateField( key: K, value: KefuWidgetHostConfig[K] ) { setConfig((current) => ({ ...current, [key]: value })) } function handleMount() { const nextConfig: KefuWidgetHostConfig = { ...config, channelId: config.channelId.trim(), baseUrl: config.baseUrl.trim() || window.location.origin, apiBaseUrl: config.apiBaseUrl?.trim() || config.baseUrl.trim() || window.location.origin, externalSource: config.externalSource?.trim() || "web_chat", title: config.title?.trim() || "在线客服", subtitle: config.subtitle?.trim() || "", themeColor: config.themeColor?.trim() || "#2563eb", width: config.width?.trim() || "380px", subject: config.subject?.trim() || generateRandomSubject(), } setConfig(nextConfig) window.localStorage.setItem(STORAGE_KEY, JSON.stringify(nextConfig)) if (!nextConfig.channelId) { removeMountedWidget() setStatus("请填写 channelId") return } injectWidget(nextConfig) setStatus("Widget 已挂载") } return (
Widget 挂载测试
{status}
updateField("channelId", value)} /> updateField("baseUrl", value)} /> updateField("apiBaseUrl", value)} /> updateField("title", value)} /> updateField("subtitle", value)} /> updateField("themeColor", value)} /> updateField("width", value)} /> updateField("subject", value)} />
接入代码
            {snippet}
          
) } function TextField({ label, value, onChange, }: { label: string value?: string onChange: (value: string) => void }) { return ( ) }