@ -80,6 +80,10 @@ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Noto Sans S
.code-section .st{color:#059669} /* 字符串 */
.code-section .nu{color:#2563EB} /* 数字 */
.code-section .bl{color:#DC2626} /* 必传高亮 */
.code-tabs{display:flex;gap:4px}
.code-tab{padding:3px 10px;border:1px solid #E5E7EB;border-radius:4px;background:#fff;color:#6B7280;font-size:11px;cursor:pointer;transition:all .15s;font-family:inherit}
.code-tab--active{background:#4F46E5;color:#fff;border-color:#4F46E5}
.code-tab:hover:not(.code-tab--active){background:#F3F4F6}
< / style >
< / head >
< body >
@ -188,8 +192,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Noto Sans S
< div class = "code-section" >
< div class = "code-section__header" >
< span class = "code-section__title" > 📋 接入代码< / span >
< div style = "display:flex;gap:6px;align-items:center" >
< div class = "code-tabs" >
< button class = "code-tab code-tab--active" id = "tab-snippet" onclick = "switchCodeTab('snippet',this)" > Script 片段< / button >
< button class = "code-tab" id = "tab-fullpage" onclick = "switchCodeTab('fullpage',this)" > 完整页面< / button >
< / div >
< button class = "code-section__copy" id = "btn-copy" onclick = "copyCode()" > 📋 复制代码< / button >
< / div >
< / div >
< div class = "code-section__body" >
< pre id = "code-output" > < / pre >
< / div >
@ -284,6 +294,125 @@ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Noto Sans S
function assert(c,m){if(!c)throw new Error(m);}
function assertExists(s,m){const el=document.getElementById(s)||document.querySelector(s);assert(!!el,m||s+' 不存在');return el;}
// ==================== 接入代码生成 ====================
// 当前代码展示 Tab:'snippet'(Script 片段)| 'fullpage'(完整 HTML 页面)
let codeTab = 'snippet';
// SDK 可选参数的默认值(与 config.ts parseConfig 严格对齐),用于判断是否需要在生成代码中输出
const SDK_DEFAULTS = {
title: 'AI 智能助手',
width: 380,
position: 'right-bottom',
primaryColor: '#4F46E5',
streaming: true,
locale: 'zh-CN',
showCategorySwitch: false,
};
// 生成 Script 片段代码(纯文本)
function buildCodeSnippet() {
const cfg = getCfg();
const domain = cfg.requestDomain;
// integrateId:纯数字输出为数字字面量,否则输出字符串
const iid = String(cfg.integrateId);
const iidLit = /^\d+$/.test(iid) ? iid : JSON.stringify(iid);
const params = [];
// 必传参数
params.push(' integrateId: ' + iidLit + ', // 必传:客服角色 ID(对应后端 roleId)');
params.push(' requestDomain: ' + JSON.stringify(domain) + ', // 必传:后端 API 域名');
// 可选参数:仅输出与默认值不同的项
if (cfg.userId) params.push(' userId: ' + JSON.stringify(cfg.userId) + ', // 可选:客户账号 ID(对应后端 accountId)');
if (cfg.title !== SDK_DEFAULTS.title) params.push(' title: ' + JSON.stringify(cfg.title) + ', // 弹窗标题');
if (cfg.primaryColor !== SDK_DEFAULTS.primaryColor) params.push(' primaryColor: ' + JSON.stringify(cfg.primaryColor) + ', // 主色调');
if (cfg.position !== SDK_DEFAULTS.position) params.push(' position: ' + JSON.stringify(cfg.position) + ', // 悬浮按钮位置');
if (cfg.width !== SDK_DEFAULTS.width) params.push(' width: ' + cfg.width + ', // 弹窗宽度(px)');
if (cfg.streaming !== SDK_DEFAULTS.streaming) params.push(' streaming: ' + cfg.streaming + ', // 是否启用流式输出');
if (cfg.locale !== SDK_DEFAULTS.locale) params.push(' locale: ' + JSON.stringify(cfg.locale) + ', // 界面语言');
if (cfg.showCategorySwitch !== SDK_DEFAULTS.showCategorySwitch) params.push(' showCategorySwitch: ' + cfg.showCategorySwitch + ', // 是否显示知识库切换');
// debug 强制 false(生产环境推荐),无论面板当前值
params.push(' debug: false // 调试日志,生产环境建议关闭');
const initBlock = 'ChatbotSDK.init({\n' + params.join('\n') + '\n});';
return '<!-- ChatbotSDK 智能客服 --> \n' +
'< script src = "' + domain + '/sdk/chatbot-sdk.min.js" > < \ / s c r i p t > \ n ' +
'< script > \ n ' + i n i t B l o c k + ' \ n < \ / s c r i p t > ' ;
}
// 生成完整 HTML 页面代码(纯文本)
function buildFullPage() {
const snippet = buildCodeSnippet();
const indented = snippet.split('\n').map(l => ' ' + l).join('\n');
return '<!DOCTYPE html> \n' +
'< html lang = "zh-CN" > \n' +
'< head > \n' +
' < meta charset = "UTF-8" > \n' +
' < meta name = "viewport" content = "width=device-width, initial-scale=1.0" > \n' +
' < title > AI 智能助手< / title > \n' +
'< / head > \n' +
'< body > \n' + indented + '\n< / body > \n' +
'< / html > ';
}
// 根据当前 Tab 返回纯文本代码
function getCurrentCode() {
return codeTab === 'fullpage' ? buildFullPage() : buildCodeSnippet();
}
// 语法高亮:先转义 HTML,再用单遍正则匹配 HTML 注释 / 字符串 / 行注释 / 数字 / 对象 key
function highlightCode(code) {
const esc = code.replace(/& /g, '& ').replace(/< /g, '< ').replace(/>/g, '> ');
const re = /(< !--[\s\S]*?--> )|('[^']*'|"[^"]*")|(\/\/[^\n]*)|\b(\d+)\b|\b(integrateId|requestDomain|userId|categoryId|title|primaryColor|position|width|streaming|locale|showCategorySwitch|debug|showClear|showAdminPanel|launcherIcon)(?=\s*:)/g;
return esc.replace(re, (m, htmlCmt, str, cmt, num, key) => {
if (htmlCmt) return '< span class = "cm" > ' + htmlCmt + '< / span > ';
if (str) return '< span class = "st" > ' + str + '< / span > ';
if (cmt) return '< span class = "cm" > ' + cmt + '< / span > ';
if (num) return '< span class = "nu" > ' + num + '< / span > ';
if (key) return '< span class = "kw" > ' + key + '< / span > ';
return m;
});
}
// 刷新代码预览区
function generateCode() {
const out = getEl('code-output');
if (out) out.innerHTML = highlightCode(getCurrentCode());
}
// 复制代码到剪贴板(带降级方案)
function fallbackCopy(text, cb) {
const ta = document.createElement('textarea');
ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0';
document.body.appendChild(ta); ta.select();
try { document.execCommand('copy'); cb(); } catch (e) { /* 忽略 */ }
document.body.removeChild(ta);
}
window.copyCode = function() {
const code = getCurrentCode();
const btn = getEl('btn-copy');
const done = function() {
if (btn) {
btn.textContent = '✅ 已复制';
btn.classList.add('code-section__copy--ok');
setTimeout(function() {
btn.textContent = '📋 复制代码';
btn.classList.remove('code-section__copy--ok');
}, 1500);
}
};
if (navigator.clipboard & & navigator.clipboard.writeText) {
navigator.clipboard.writeText(code).then(done).catch(function() { fallbackCopy(code, done); });
} else {
fallbackCopy(code, done);
}
};
// 切换代码 Tab
window.switchCodeTab = function(tab, btn) {
codeTab = tab;
document.querySelectorAll('.code-tab').forEach(function(t) { t.classList.remove('code-tab--active'); });
if (btn) btn.classList.add('code-tab--active');
generateCode();
};
window.doInit=function(){ChatbotSDK.destroy();ChatbotSDK.init(getCfg());const tag=getEl('tag-sdk');if(document.getElementById('csk-launcher')){tag.className='tag tag--pass';tag.textContent='✅ SDK 就绪';}};
window.doDestroy=function(){ChatbotSDK.destroy();const tag=getEl('tag-sdk');tag.className='tag tag--idle';tag.textContent='⭕ SDK 未加载';};
window.doClearHistory=function(){ChatbotSDK.clearHistory();};
@ -379,6 +508,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Noto Sans S
getEl('cfg-domain').value=window.location.origin;
setEl('footer-time',new Date().toLocaleTimeString());
if(typeof window.ChatbotSDK!=='undefined'){const tag=getEl('tag-sdk');tag.className='tag tag--pass';tag.textContent='✅ SDK 已加载';}
// 监听配置表单变化,实时刷新接入代码
['cfg-iid','cfg-domain','cfg-uid','cfg-title','cfg-color','cfg-pos','cfg-width','cfg-stream','cfg-locale','cfg-cat','cfg-debug'].forEach(function(id){
const el=getEl(id);
if(!el)return;
el.addEventListener('input', generateCode);
el.addEventListener('change', generateCode);
});
generateCode();
fetch(window.location.origin+'/ai/assistant_app/chat/sync?message=test& chatId=__probe__& roleId=1',{signal:AbortSignal.timeout(5000)}).then(r=>{const tag=getEl('tag-api');if(r.ok||r.status< 500 ) { tag . className = 'tag tag--pass' ; tag . textContent = '✅ 后端连通' ; setEl ( ' footer-info ' , ' ChatbotSDK v1 . 2 . 0 | 后端 : 在线 ✅ ' ) ; } } ) . catch ( ( ) = > {const tag=getEl('tag-api');tag.className='tag tag--fail';tag.textContent='⚠ 后端离线';});
})();
})();