PageSourceSearch

https://cdn.kuaidi100.com/js/share/cssdk/customer-service-sdk.min.js

js kuaidi100.com collected 2026-09-24 18:48:48 UTC 14,947 bytes, 12 lines download raw bytes

1/**
2 * @kuaidi100/customer-service-sdk v1.0.1
3 * 客服系统集成 SDK - 支持悬浮窗、iframe 嵌入、跨域通信、样式隔离
4 * 
5 * @author Your Company
6 * @license MIT
7 * @repository git+https://github.com/your-company/customer-service-sdk.git
8 * 
9 * Build Date: 2026-04-09T03:29:59.423Z
10 * Copyright (c) 2026 Your Company
11 */!function(){"use strict";!function(t,e){const i="kuaidics";class CustomerService{constructor(t){this.config={code:"",link:"",selector:"",targetSelector:"",position:"absolute",panel:{css:{right:"20px",bottom:"20px",width:"400px",height:"600px",zIndex:9999}},autoShow:!1,isolateHistory:!0,sandbox:"allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-popups-to-escape-sandbox allow-downloads",...t},this.isInitialized=!1,this.isVisible=!1,this.shadowRoot=null,this.container=null,this.iframe=null,this.panel=null,this.historyStack=[],this.currentHistoryIndex=-1,this.lastIframeUrl="",this.init()}init(){this.isInitialized?console.warn("[CustomerService] SDK already initialized"):this.config.code&&this.config.link?("loading"===e.readyState?e.addEventListener("DOMContentLoaded",()=>this.setup()):this.setup(),this.isInitialized=!0):console.error("[CustomerService] Missing required config: code or link")}setup(){this.createContainer(),this.createPanel(),this.createIframe(),this.bindEvents(),this.setupMessageListener(),this.config.autoShow&&this.show(),console.log("[CustomerService] SDK initialized successfully")}createContainer(){if(this.config.targetSelector&&(this.container=e.querySelector(this.config.targetSelector),this.container||console.warn(`[CustomerService] Target selector "${this.config.targetSelector}" not found, creating default container`)),this.container||(this.container=e.createElement("div"),this.container.id=`cs-container-${this.config.code}`,e.body.appendChild(this.container)),this.container.shadowRoot)this.shadowRoot=this.container.shadowRoot;else try{this.shadowRoot=this.container.attachShadow({mode:"open"})}catch(t){console.warn("[CustomerService] Shadow DOM not supported, using regular DOM"),this.shadowRoot=this.container}this.injectStyles()}injectStyles(){const t=e.createElement("style");t.textContent="\n        :host {\n          all: initial;\n        }\n\n        * {\n          box-sizing: border-box;\n          margin: 0;\n          padding: 0;\n        }\n\n        .cs-panel {\n          position: fixed;\n          background: #ffffff;\n          box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);\n          border-radius: 12px;\n          overflow: hidden;\n          transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n          opacity: 0;\n          transform: scale(0.9) translateY(20px);\n          pointer-events: none;\n          z-index: 9999;\n        }\n\n        .cs-panel.visible {\n          opacity: 1;\n          transform: scale(1) translateY(0);\n          pointer-events: all;\n        }\n\n        .cs-panel-header {\n          background: #F3F5FA;\n          color: #ffffff;\n          padding: 16px 20px;\n          display: flex;\n          justify-content: space-between;\n          align-items: center;\n          user-select: none;\n          cursor: move;\n        }\n\n        .cs-panel-title {\n          font-size: 16px;\n          font-weight: 600;\n          margin: 0;\n          color: #000;\n        }\n\n        .cs-panel-controls {\n          display: flex;\n          gap: 8px;\n        }\n\n        .cs-btn {\n          width: 28px;\n          height: 28px;\n          border: none;\n          background: transparent;\n          color: #000;\n          border-radius: 6px;\n          cursor: pointer;\n          display: flex;\n          align-items: center;\n          justify-content: center;\n          transition: all 0.2s;\n          font-size: 16px;\n          line-height: 1;\n          font-weight: 600;\n        }\n\n        .cs-btn:hover {\n          background: rgba(255, 255, 255, 0.3);\n          transform: scale(1.1);\n        }\n\n        .cs-btn:active {\n          transform: scale(0.95);\n        }\n\n        .cs-panel-body {\n          width: 100%;\n          height: calc(100% - 56px);\n          position: relative;\n        }\n\n        .cs-iframe {\n          width: 100%;\n          height: 100%;\n          border: none;\n          display: block;\n        }\n\n        .cs-loading {\n          position: absolute;\n          top: 50%;\n          left: 50%;\n          transform: translate(-50%, -50%);\n          text-align: center;\n          color: #666;\n        }\n\n        .cs-spinner {\n          width: 40px;\n          height: 40px;\n          border: 4px solid #f3f3f3;
11\n          border-top: 4px solid #667eea;\n          border-radius: 50%;\n          animation: spin 1s linear infinite;\n          margin: 0 auto 12px;\n        }\n\n        @keyframes spin {\n          0% { transform: rotate(0deg); }\n          100% { transform: rotate(360deg); }\n        }\n\n        /* 最小化状态 */\n        .cs-panel.minimized {\n          width: 60px !important;\n          height: 60px !important;\n          border-radius: 50%;\n          overflow: hidden;\n        }\n\n        .cs-panel.minimized .cs-panel-header {\n          padding: 0;\n          width: 100%;\n          height: 100%;\n          justify-content: center;\n          border-radius: 50%;\n          cursor: pointer;\n        }\n\n        .cs-panel.minimized .cs-panel-title,\n        .cs-panel.minimized .cs-btn-minimize,\n        .cs-panel.minimized .cs-btn-close,\n        .cs-panel.minimized .cs-panel-body {\n          display: none;\n        }\n\n        .cs-panel.minimized .cs-btn-maximize {\n          display: flex !important;\n          width: 100%;\n          height: 100%;\n          border-radius: 50%;\n          background: transparent;\n          font-size: 24px;\n        }\n\n        .cs-btn-maximize {\n          display: none;\n        }\n      ",this.shadowRoot.appendChild(t)}createPanel(){const t=e.createElement("div");t.className="cs-panel",Object.entries(this.config.panel.css).forEac
11h(([e,i])=>{t.style[e]=i});const i=e.createElement("div");i.className="cs-panel-header",i.innerHTML='\n        \n        <h3 class="cs-panel-title">果宝</h3>\n        <div class="cs-panel-controls">\n          <button class="cs-btn cs-btn-back" title="返回" style="opacity: 0;"><</button>\n          <button class="cs-btn cs-btn-minimize" title="最小化">−</button>\n        </div>\n        \n      ';const n=e.createElement("div");n.className="cs-panel-body",n.innerHTML='\n        <div class="cs-loading">\n          <div class="cs-spinner"></div>\n          <div>加载中...</div>\n        </div>\n      ',t.appendChild(i),t.appendChild(n),this.shadowRoot.appendChild(t),this.panel=t,this.panelHeader=i,this.panelBody=n,this.makeDraggable(i,t)}createIframe(){const t=e.createElement("iframe");t.className="cs-iframe",t.src=this.buildIframeUrl(),t.setAttribute("allowfullscreen","true"),t.setAttribute("allow","camera; microphone; clipboard-write"),this.config.isolateHistory&&(t.setAttribute("sandbox",this.config.sandbox),console.log("[CustomerService] History isolation enabled")),t.onload=()=>{const t=this.panelBody.querySelector(".cs-loading");t&&(t.style.display="none"),this.sendMessageToIframe({type:"sdk:ready",code:this.config.code}),this.startMonitoringIframeUrl()},this.panelBody.appendChild(t),this.iframe=t}buildIframeUrl(){const t=new URL(this.config.link);return t.searchParams.set("code",this.config.code),t.searchParams.set("sdk_version","1.0.0"),t.toString()}bindEvents(){if(this.config.selector){const t=e.querySelector(this.config.selector);t&&(this.triggerButton=t,t.addEventListener("click",t=>{t.preventDefault(),this.toggle()}))}const i=this.shadowRoot.querySelector(".cs-btn-back"),n=this.shadowRoot.querySelector(".cs-btn-forward"),s=this.shadowRoot.querySelector(".cs-btn-minimize"),o=this.shadowRoot.querySelector(".cs-btn-maximize"),a=this.shadowRoot.querySelector(".cs-btn-close");i&&i.addEventListener("click",()=>this.goBack()),n&&n.addEventListener("click",()=>this.goForward()),s&&s.addEventListener("click",()=>this.hide()),o&&o.addEventListener("click",()=>this.maximize()),a&&a.addEventListener("click",()=>this.hide()),this.triggerButton&&(this.repositionHandler=()=>{this.isVisible&&!this.panel.classList.contains("minimized")&&this.positionPanelRelativeToButton()},t.addEventListener("resize",this.repositionHandler),t.addEventListener("scroll",this.repositionHandler,!0))}setupMessageListener(){t.addEventListener("message",t=>{try{const e=new URL(this.config.link).origin;if(t.origin!==e)return}catch(t){return}const{type:e,data:i}=t.data;switch(e){case"iframe:ready":console.log("[CustomerService] Iframe is ready");break;case"iframe:close":this.hide();break;case"iframe:minimize":this.minimize();break;case"iframe:resize":this.resize(i.width,i.height);break;case"iframe:notification":this.handleNotification(i);break;case"iframe:historyState":this.updateHistoryState(i.canGoBack,i.canGoForward);break;default:console.log("[CustomerService] Unknown message type:",e)}})}sendMessageToIframe(e){const i=t?.location.href||"";if(e.parentPage=i,this.iframe&&this.iframe.contentWindow){const t=new URL(this.config.link).origin;this.iframe.contentWindow.postMessage(e,t)}}show(){this.triggerButton&&this.positionPanelRelativeToButton(),this.panel.classList.add("visible"),this.panel.classList.remove("minimized"),this.isVisible=!0,this.sendMessageToIframe({type:"sdk:show"})}positionPanelRelativeToButton(){if(!this.triggerButton)return;const e=this.triggerButton.getBoundingClientRect(),i=this.config.panel.css;
11if("fixed"===this.config.position)return this.panel.style.left=i.left||"auto",this.panel.style.top=i.top||"auto",this.panel.style.right=i.right||"auto",void(this.panel.style.bottom=i.bottom||"auto");const n=t=>"number"==typeof t?t:parseFloat(t)||0;let s,o;const a=n(i.width)||400,r=n(i.height)||600;if(void 0!==i.right){const t=n(i.right);s=e.right-t-e.width-a}else if(void 0!==i.left){const t=n(i.left);s=e.left+t+e.width}else s=e.right+20;if(void 0!==i.top){const t=n(i.top);o=e.top+t}else if(void 0!==i.bottom){const t=n(i.bottom);o=e.bottom+t}else o=e.top;const c=t.innerWidth,l=t.innerHeight;s+a>c&&(s=c-a-20),s<20&&(s=20),o+r>l&&(o=l-r-20),o<20&&(o=20),this.panel.style.left=s+"px",this.panel.style.top=o+"px",this.panel.style.right="auto",this.panel.style.bottom="auto"}hide(){this.panel.classList.remove("visible"),this.isVisible=!1,this.sendMessageToIframe({type:"sdk:hide"})}toggle(){this.isVisible?this.hide():this.show()}minimize(){this.panel.classList.add("minimized"),this.sendMessageToIframe({type:"sdk:minimize"})}maximize(){this.panel.classList.remove("minimized"),this.sendMessageToIframe({type:"sdk:maximize"})}resize(t,e){t&&(this.panel.style.width=t+"px"),e&&(this.panel.style.height=e+"px")}handleNotification(t){console.log("[CustomerService] Notification:",t)}startMonitoringIframeUrl(){this.urlMonitorTimer=setInterval(()=>{try{const t=this.iframe.contentWindow.location.href;t!==this.lastIframeUrl&&""!==this.lastIframeUrl&&(console.log("[CustomerService] Iframe URL changed:",t),this.handleIframeNavigation(t)),this.lastIframeUrl=t}catch(t){}},500);try{this.lastIframeUrl=this.iframe.contentWindow.location.href,this.pushHistory(this.lastIframeUrl)}catch(t){this.lastIframeUrl=this.config.link,this.pushHistory(this.lastIframeUrl)}}stopMonitoringIframeUrl(){this.urlMonitorTimer&&(clearInterval(this.urlMonitorTimer),this.urlMonitorTimer=null)}handleIframeNavigation(t){this.isNavigatingHistory||(this.historyStack=this.historyStack.slice(0,this.currentHistoryIndex+1),this.pushHistory(t)),this.isNavigatingHistory=!1,this.updateNavigationButtons()}pushHistory(t){this.historyStack.push(t),this.currentHistoryIndex=this.historyStack.length-1,this.updateNavigationButtons(),console.log("[CustomerService] History pushed:",{url:t,index:this.currentHistoryIndex,stackLength:this.historyStack.length})}goBack(){if(this.canGoBack()){this.isNavigatingHistory=!0,this.currentHistoryIndex--;const t=this.historyStack[this.currentHistoryIndex];this.iframe.src=t,this.lastIframeUrl=t,this.sendMessageToIframe({type:"sdk:goBack"}),this.updateNavigationButtons(),console.log("[CustomerService] Go back to:",t)}}goForward(){if(this.canGoForward()){this.isNavigatingHistory=!0,this.currentHistoryIndex++;const t=this.historyStack[this.currentHistoryIndex];this.iframe.src=t,this.lastIframeUrl=t,this.sendMessageToIframe({type:"sdk:goForward"}),this.updateNavigationButtons(),console.log("[CustomerService] Go forward to:",t)}}canGoBack(){return this.currentHistoryIndex>0}canGoForward(){return this.currentHistoryIndex<this.historyStack.length-1}updateNavigationButtons(){const t=this.shadowRoot.querySelector(".cs-btn-back"),e=this.shadowRoot.querySelector(".cs-btn-forward");t&&(t.style.opacity=this.canGoBack()?"1":"0"),e&&(e.style.display=this.canGoForward()?"flex":"none"),console.log("[CustomerService] Navigation buttons updated:",{canGoBack:this.canGoBack(),canGoForward:this.canGoForward(),currentIndex:this.currentHistoryIndex,stackLength:this.historyStack.length})}updateHistoryState(t,e){const i=this.shadowRoot.querySelector(".cs-btn-back"),n=this.shadowRoot.querySelector(".cs-btn-forward");i&&(i.style.display=t?"flex":"none"),n&&(n.style.display=e?"flex":"none"),console.log("[CustomerService] History state updated from iframe:",{canGoBack:t,canGoForward:e})}makeDraggable(t,i){let n,s,o,a,r=!1;t.addEventListener("mousedown",t=>{i.classList.contains("minimized")||(r=!0,o=t.clientX-i.offsetLeft,a=t.clientY-i.offsetTop,i.style.cursor="grabbing")}),e.addEventListener("mousemove",t=>{r&&(t.preventDefault(),n=t.clientX-o,s=t.clientY-a,i.style.left=n+"px",i.style.top=s+"px",i.style.right="auto",i.style.bottom="auto")}),e.addEventListener("mouseup",()=>{r&&(r=!1,i.style.cursor="default")})}destroy(){this.stopMonitoringIframeUrl(),this.repositionHandler&&(t.removeEventListener("resize",this.repositionHandler),t.removeEventListener("scroll",this.repositionHandler,!0)),this.container&&this.container.parentNode&&this.container.parentNode.removeChild(this.container),this.isInitialized=!1,console.log("[CustomerService] SDK destroyed")}}const n=new class{constructor(){this.instances={},this.queue=[]}execute(t){const e=t.code||"default";return this.instances[e]||(this.instances[e]=new CustomerService(t)),this.instances[e]}getInstance(t="default"){return this.instances[t]}
11destroyInstance(t="default"){this.instances[t]&&(this.instances[t].destroy(),delete this.instances[t])}destroyAll(){Object.keys(this.instances).forEach(t=>{this.destroyInstance(t)})}};t[i]&&t[i].d&&t[i].d.forEach(t=>{n.execute(t[0])}),t[i]=function(t){return n.execute(t)},t[i].getInstance=t=>n.getInstance(t),t[i].destroy=t=>n.destroyInstance(t),t[i].destroyAll=()=>n.destroyAll(),console.log("[CustomerServiceSDK] SDK loaded successfully")}(window,document)}();
12//# sourceMappingURL=customer-service-sdk.min.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.