271 lines
8.0 KiB
JavaScript
271 lines
8.0 KiB
JavaScript
(function () {
|
|
if (window.__CS_AGENT_WIDGET_LOADED__) {
|
|
return;
|
|
}
|
|
window.__CS_AGENT_WIDGET_LOADED__ = true;
|
|
|
|
var config = window.CSAgentConfig || {};
|
|
var baseUrl = String(config.baseUrl || "").replace(/\/$/, "");
|
|
if (!config.channelId || !baseUrl) {
|
|
console.error("[cs-agent-widget] channelId and baseUrl are required");
|
|
return;
|
|
}
|
|
|
|
function resolveWidgetBaseUrl() {
|
|
var currentScript = document.currentScript;
|
|
if (currentScript && currentScript.src) {
|
|
return currentScript.src.replace(/\/sdk\/cs-agent-widget\.js(?:\?.*)?$/, "");
|
|
}
|
|
if (/\/widget$/.test(baseUrl)) {
|
|
return baseUrl;
|
|
}
|
|
return baseUrl + "/widget";
|
|
}
|
|
|
|
var button = document.createElement("button");
|
|
button.type = "button";
|
|
button.dataset.csAgentWidget = "launcher";
|
|
button.setAttribute("aria-label", config.title || "在线客服");
|
|
button.textContent = config.title || "在线客服";
|
|
button.style.position = "fixed";
|
|
button.style.bottom = "24px";
|
|
button.style.right = config.position === "left" ? "" : "24px";
|
|
button.style.left = config.position === "left" ? "24px" : "";
|
|
button.style.zIndex = "2147483000";
|
|
button.style.border = "0";
|
|
button.style.borderRadius = "999px";
|
|
button.style.padding = "14px 18px";
|
|
button.style.background = config.themeColor || "#0f6cbd";
|
|
button.style.color = "#fff";
|
|
button.style.font = "600 14px/1 sans-serif";
|
|
button.style.boxShadow = "0 18px 40px rgba(15, 35, 65, 0.24)";
|
|
button.style.cursor = "pointer";
|
|
|
|
var widgetBaseUrl = resolveWidgetBaseUrl();
|
|
var frameUrl = new URL(widgetBaseUrl + "/frame/");
|
|
frameUrl.searchParams.set("channelId", config.channelId);
|
|
frameUrl.searchParams.set("baseUrl", baseUrl);
|
|
if (config.apiBaseUrl) frameUrl.searchParams.set("apiBaseUrl", config.apiBaseUrl);
|
|
if (config.title) frameUrl.searchParams.set("title", config.title);
|
|
if (config.subtitle) frameUrl.searchParams.set("subtitle", config.subtitle);
|
|
if (config.position) frameUrl.searchParams.set("position", config.position);
|
|
if (config.themeColor) frameUrl.searchParams.set("themeColor", config.themeColor);
|
|
if (config.width) frameUrl.searchParams.set("width", config.width);
|
|
var frame = null;
|
|
var frameLoaded = false;
|
|
var frameReady = false;
|
|
var initSent = false;
|
|
var isOpen = false;
|
|
var isMaximized = false;
|
|
var frameHideTimer = null;
|
|
var frameDestroyTimer = null;
|
|
var animationDuration = 260;
|
|
|
|
function clearFrameTimers() {
|
|
if (frameHideTimer) {
|
|
window.clearTimeout(frameHideTimer);
|
|
frameHideTimer = null;
|
|
}
|
|
if (frameDestroyTimer) {
|
|
window.clearTimeout(frameDestroyTimer);
|
|
frameDestroyTimer = null;
|
|
}
|
|
}
|
|
|
|
function applyFrameLayout() {
|
|
if (!frame) {
|
|
return;
|
|
}
|
|
|
|
frame.style.position = "fixed";
|
|
frame.style.border = "0";
|
|
frame.style.overflow = "hidden";
|
|
frame.style.background = "#fff";
|
|
frame.style.zIndex = "2147483000";
|
|
frame.style.boxShadow = "0 28px 80px rgba(15, 35, 65, 0.28)";
|
|
frame.style.willChange = "top,right,bottom,left,width,height,opacity,transform,border-radius";
|
|
frame.style.transition =
|
|
"top 260ms cubic-bezier(0.22, 1, 0.36, 1), right 260ms cubic-bezier(0.22, 1, 0.36, 1), bottom 260ms cubic-bezier(0.22, 1, 0.36, 1), left 260ms cubic-bezier(0.22, 1, 0.36, 1), width 260ms cubic-bezier(0.22, 1, 0.36, 1), height 260ms cubic-bezier(0.22, 1, 0.36, 1), opacity 220ms ease, transform 260ms cubic-bezier(0.22, 1, 0.36, 1), border-radius 260ms cubic-bezier(0.22, 1, 0.36, 1), box-shadow 260ms ease";
|
|
frame.style.transformOrigin =
|
|
config.position === "left" ? "left bottom" : "right bottom";
|
|
|
|
if (isMaximized) {
|
|
frame.style.top = "20px";
|
|
frame.style.right = "20px";
|
|
frame.style.bottom = "20px";
|
|
frame.style.left = "20px";
|
|
frame.style.width = "calc(100vw - 40px)";
|
|
frame.style.maxWidth = "none";
|
|
frame.style.height = "calc(100vh - 40px)";
|
|
frame.style.borderRadius = "24px";
|
|
return;
|
|
}
|
|
|
|
frame.style.top = "";
|
|
frame.style.bottom = "88px";
|
|
frame.style.right = config.position === "left" ? "" : "24px";
|
|
frame.style.left = config.position === "left" ? "24px" : "";
|
|
frame.style.width = config.width || "380px";
|
|
frame.style.maxWidth = "calc(100vw - 24px)";
|
|
frame.style.height = "min(760px, calc(100vh - 112px))";
|
|
frame.style.borderRadius = "28px";
|
|
}
|
|
|
|
function flushFrameState() {
|
|
if (!frame || !frameLoaded || !frameReady) {
|
|
return;
|
|
}
|
|
|
|
if (!initSent) {
|
|
initSent = true;
|
|
postToFrame({
|
|
type: "cs-agent:init",
|
|
payload: config,
|
|
});
|
|
}
|
|
|
|
postToFrame({ type: isOpen ? "cs-agent:open" : "cs-agent:minimize" });
|
|
postToFrame({
|
|
type: "cs-agent:maximized",
|
|
payload: { isMaximized: isMaximized },
|
|
});
|
|
}
|
|
|
|
function postToFrame(message) {
|
|
if (!frame || !frame.contentWindow) {
|
|
return;
|
|
}
|
|
try {
|
|
frame.contentWindow.postMessage(message, frameUrl.origin);
|
|
} catch (error) {
|
|
console.error("[cs-agent-widget] postMessage failed", error);
|
|
}
|
|
}
|
|
|
|
function syncFrameVisibility() {
|
|
if (!frame) {
|
|
return;
|
|
}
|
|
clearFrameTimers();
|
|
applyFrameLayout();
|
|
frame.style.display = "block";
|
|
|
|
if (isOpen) {
|
|
frame.style.visibility = "visible";
|
|
frame.style.pointerEvents = "auto";
|
|
frameHideTimer = window.setTimeout(function () {
|
|
if (!frame) {
|
|
return;
|
|
}
|
|
frame.style.opacity = "1";
|
|
frame.style.transform = "translate3d(0, 0, 0) scale(1)";
|
|
}, 16);
|
|
flushFrameState();
|
|
return;
|
|
}
|
|
|
|
frame.style.pointerEvents = "none";
|
|
frame.style.opacity = "0";
|
|
frame.style.transform = isMaximized
|
|
? "translate3d(0, 10px, 0) scale(0.985)"
|
|
: "translate3d(0, 16px, 0) scale(0.96)";
|
|
frameHideTimer = window.setTimeout(function () {
|
|
if (!frame || isOpen) {
|
|
return;
|
|
}
|
|
frame.style.visibility = "hidden";
|
|
}, animationDuration);
|
|
flushFrameState();
|
|
}
|
|
|
|
function handleWindowMessage(event) {
|
|
if (!frame || event.source !== frame.contentWindow) {
|
|
return;
|
|
}
|
|
|
|
var data = event.data || {};
|
|
if (data.type === "cs-agent:ready") {
|
|
frameReady = true;
|
|
flushFrameState();
|
|
return;
|
|
}
|
|
|
|
if (data.type === "cs-agent:request-minimize") {
|
|
isOpen = false;
|
|
syncFrameVisibility();
|
|
return;
|
|
}
|
|
|
|
if (data.type === "cs-agent:request-close") {
|
|
destroyFrame();
|
|
return;
|
|
}
|
|
|
|
if (data.type === "cs-agent:request-toggle-maximize") {
|
|
isMaximized = !isMaximized;
|
|
syncFrameVisibility();
|
|
}
|
|
}
|
|
|
|
function destroyFrame() {
|
|
if (!frame) {
|
|
return;
|
|
}
|
|
clearFrameTimers();
|
|
frame.style.pointerEvents = "none";
|
|
frame.style.opacity = "0";
|
|
frame.style.transform = "translate3d(0, 18px, 0) scale(0.94)";
|
|
frame.style.visibility = "hidden";
|
|
frameDestroyTimer = window.setTimeout(function () {
|
|
if (!frame) {
|
|
return;
|
|
}
|
|
if (frame.parentNode) {
|
|
frame.parentNode.removeChild(frame);
|
|
}
|
|
frame = null;
|
|
frameLoaded = false;
|
|
frameReady = false;
|
|
initSent = false;
|
|
isOpen = false;
|
|
isMaximized = false;
|
|
clearFrameTimers();
|
|
}, animationDuration);
|
|
}
|
|
|
|
function createFrame() {
|
|
if (frame) {
|
|
return frame;
|
|
}
|
|
|
|
frame = document.createElement("iframe");
|
|
frame.dataset.csAgentWidget = "frame";
|
|
frame.title = config.title || "在线客服";
|
|
frame.src = frameUrl.toString();
|
|
applyFrameLayout();
|
|
frame.style.display = "block";
|
|
frame.style.visibility = "hidden";
|
|
frame.style.pointerEvents = "none";
|
|
frame.style.opacity = "0";
|
|
frame.style.transform = "translate3d(0, 18px, 0) scale(0.96)";
|
|
frame.addEventListener("load", function () {
|
|
frameLoaded = true;
|
|
syncFrameVisibility();
|
|
});
|
|
|
|
document.body.appendChild(frame);
|
|
return frame;
|
|
}
|
|
|
|
button.addEventListener("click", function () {
|
|
if (!frame) {
|
|
createFrame();
|
|
}
|
|
isOpen = !isOpen;
|
|
syncFrameVisibility();
|
|
});
|
|
|
|
window.addEventListener("message", handleWindowMessage);
|
|
document.body.appendChild(button);
|
|
})();
|