(function(){ if (window.__SUBAI_KF_LOADED__) return; window.__SUBAI_KF_LOADED__ = true; var script = document.currentScript || (function(){var s=document.getElementsByTagName('script');return s[s.length-1];})(); var src = script && script.src ? new URL(script.src, window.location.href) : null; var server = window.KF_SERVER || (src ? src.origin : window.location.origin); var apiKey = window.KF_API_KEY || (script && script.getAttribute('data-api-key')) || ''; var bizId = window.KF_BIZ_ID || (script && script.getAttribute('data-biz-id')) || ''; var sessionId = 'kf_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2,8); var state = {open:false, sending:false, name:'', phone:'', avatarUrl:'', position:'bottom-right'}; function cssEscape(v){ return String(v == null ? '' : v); } function make(tag, cls){ var node=document.createElement(tag); if(cls) node.className=cls; return node; } function setText(node, text){ node.textContent = text == null ? '' : String(text); } function now(){ return new Date().toLocaleTimeString('zh-CN',{hour:'2-digit',minute:'2-digit'}); } var host = document.createElement('div'); host.id = 'subai-kf-root'; document.body.appendChild(host); var root = host.attachShadow ? host.attachShadow({mode:'open'}) : host; root.innerHTML = ''+ ''+ ''; var bubble = root.querySelector('.bubble'); var panel = root.querySelector('.panel'); var messages = root.querySelector('.messages'); var input = root.querySelector('textarea'); var sendBtn = root.querySelector('.send'); var quick = root.querySelector('.quick'); var nameInput = root.querySelector('.name'); var phoneInput = root.querySelector('.phone'); var referrerInput = root.querySelector('.referrer'); function applyPosition(pos){ state.position = pos || 'bottom-right'; var left = state.position === 'bottom-left'; bubble.classList.toggle('left', left); panel.classList.toggle('left', left); } function addMessage(role, text, meta){ var row = make('div','msg '+role); if(role === 'bot'){ var avatar = make('div','avatar'); if(state.avatarUrl){ var im=document.createElement('img'); im.src=state.avatarUrl; im.alt=''; avatar.appendChild(im); } else avatar.textContent='AI'; row.appendChild(avatar); } var body = make('div','bubbleText'); setText(body,text); var stamp = make('span','time'); stamp.textContent = now() + (meta ? ' · '+meta : ''); body.appendChild(stamp); if(role === 'bot' && meta === '需要登录'){ var actions = make('div','actions'); actions.innerHTML='登录注册'; body.appendChild(actions); } row.appendChild(body); messages.appendChild(row); messages.scrollTop = messages.scrollHeight; } function setBranding(b){ if(!b || b.error) return; root.host.style.setProperty('--p', b.primary_color || '#6366f1'); root.host.style.setProperty('--a', b.accent_color || '#8b5cf6'); root.querySelector('.title strong').textContent = b.name || 'AI 客服'; state.avatarUrl = b.avatar_url || b.logo_url || ''; var logo = root.querySelector('.logo'); logo.innerHTML=''; if(b.logo_url){ var img=document.createElement('img'); img.src=b.logo_url; img.alt=''; logo.appendChild(img); bubble.innerHTML=''; var bimg=document.createElement('img'); bimg.src=b.logo_url; bimg.alt=''; bubble.appendChild(bimg); } else { logo.textContent=(b.name || 'AI').slice(0,2); bubble.innerHTML='AI'; } applyPosition(b.widget_position || 'bottom-right'); messages.innerHTML=''; addMessage('bot', b.welcome_msg || '您好!有什么可以帮您的?'); } function openPanel(force){ state.open = force == null ? !state.open : !!force; panel.classList.toggle('open', state.open); if(state.open) setTimeout(function(){input.focus();}, 30); } async function loadBranding(){ messages.innerHTML=''; addMessage('bot','您好!有什么可以帮您的?'); applyPosition(window.KF_POSITION || 'bottom-right'); if(!apiKey) return; try{ var r=await fetch(server+'/api/branding?api_key='+encodeURIComponent(apiKey)); if(r.ok) setBranding(await r.json()); }catch(e){} } async function send(text){ var q = (text || input.value).trim(); if(!q || state.sending) return; input.value=''; addMessage('user', q); var typing=make('div','typing'); typing.textContent='AI 正在输入...'; messages.appendChild(typing); messages.scrollTop=messages.scrollHeight; state.sending=true; sendBtn.disabled=true; try{ var body = {message:q, session_id:sessionId, api_key:apiKey}; if(bizId) body.biz_id=bizId; if(nameInput.value.trim()) body.name=nameInput.value.trim(); if(phoneInput.value.trim()) body.phone=phoneInput.value.trim(); if(referrerInput.value.trim()) body.referrer_code=referrerInput.value.trim(); var headers = {'Content-Type':'application/json'}; if(apiKey) headers['X-API-Key']=apiKey; var r = await fetch(server+'/api/chatbot', {method:'POST', headers:headers, body:JSON.stringify(body)}); var data = await r.json(); typing.remove(); if(!r.ok || data.error) addMessage('bot', data.message || data.error || '服务暂时不可用,请稍后再试。','错误'); else addMessage('bot', data.reply || '已收到,我们会尽快回复您。', data.source === 'login_required' ? '需要登录' : ''); }catch(e){ typing.remove(); addMessage('bot','网络连接失败,请稍后重试。','错误'); } state.sending=false; sendBtn.disabled=false; input.focus(); } bubble.addEventListener('click', function(){ openPanel(); }); root.querySelector('.close').addEventListener('click', function(){ openPanel(false); }); root.querySelector('.input').addEventListener('submit', function(e){ e.preventDefault(); send(); }); input.addEventListener('input', function(){ this.style.height='auto'; this.style.height=Math.min(this.scrollHeight,90)+'px'; }); input.addEventListener('keydown', function(e){ if(e.key === 'Enter' && !e.shiftKey){ e.preventDefault(); send(); }}); quick.addEventListener('click', function(e){ if(e.target.dataset.q) send(e.target.dataset.q); }); phoneInput.addEventListener('change', function(){ if(this.value.trim() || nameInput.value.trim()) quick.style.display='flex'; }); nameInput.addEventListener('change', function(){ if(this.value.trim() || phoneInput.value.trim()) quick.style.display='flex'; }); // 自动预填推荐码 var savedRef = localStorage.getItem('subai_referrer'); if(savedRef && referrerInput) referrerInput.value = savedRef; window.KF_OPEN = function(){ openPanel(true); }; window.KF_CLOSE = function(){ openPanel(false); }; window.KF_SEND = send; loadBranding(); })();