You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
5.0 KiB
182 lines
5.0 KiB
/**
|
|
* ChatbotSDK 入口文件
|
|
* 单例模式,IIFE 挂载 window.ChatbotSDK
|
|
*
|
|
* 参数映射:
|
|
* integrateId → roleId(客服角色 ID)
|
|
* userId → accountId(客户账号 ID)
|
|
* chatId → 自动管理(从 /conversation/list 获取或自动生成)
|
|
*/
|
|
import { SDKConfig, ResolvedConfig, ChatbotSDKInstance } from './types';
|
|
import { parseConfig } from './config';
|
|
import { setDebug, logger } from './logger';
|
|
import { setApiConfig } from './api';
|
|
import { injectStyles, removeStyles } from './styles';
|
|
import { createLauncher, createChatWindow, enableDrag } from './dom';
|
|
import { initChat, initChatHistory, getMessages, setCategory, loadHistoryConversations } from './chat';
|
|
import { clearMessages } from './storage';
|
|
import { setLocale } from './i18n';
|
|
|
|
// ==================== 单例状态 ====================
|
|
|
|
let config: ResolvedConfig | null = null;
|
|
let isInitialized = false;
|
|
let launcherEl: HTMLElement | null = null;
|
|
let windowEl: HTMLElement | null = null;
|
|
let messagesContainer: HTMLElement | null = null;
|
|
let inputEl: HTMLTextAreaElement | null = null;
|
|
let sendBtn: HTMLElement | null = null;
|
|
let clearBtn: HTMLElement | null = null;
|
|
let categorySelect: HTMLSelectElement | null = null;
|
|
let historyPanel: HTMLElement | null = null;
|
|
let showLoadingFn: (() => HTMLElement) | null = null;
|
|
let hideLoadingFn: (() => void) | null = null;
|
|
let dragCleanup: (() => void) | null = null;
|
|
|
|
// ==================== 公开 API ====================
|
|
|
|
/** 初始化 SDK */
|
|
function init(rawConfig: SDKConfig): void {
|
|
if (isInitialized) {
|
|
logger.warn('SDK 已初始化,请先调用 destroy() 再重新初始化');
|
|
return;
|
|
}
|
|
|
|
// 1. 配置解析与校验
|
|
const parsed = parseConfig(rawConfig);
|
|
if (!parsed) return;
|
|
config = parsed;
|
|
|
|
// 2. 设置国际化语言
|
|
setLocale(config.locale);
|
|
|
|
// 3. 设置日志级别
|
|
setDebug(config.debug);
|
|
|
|
// 4. 设置 API 配置
|
|
setApiConfig(config);
|
|
|
|
// 5. 注入样式
|
|
injectStyles(config);
|
|
|
|
// 6. 创建悬浮按钮
|
|
launcherEl = createLauncher(config, toggle);
|
|
document.body.appendChild(launcherEl);
|
|
|
|
// 7. 创建聊天弹窗
|
|
const dom = createChatWindow(config);
|
|
windowEl = dom.window;
|
|
messagesContainer = dom.messagesContainer;
|
|
inputEl = dom.inputEl;
|
|
sendBtn = dom.sendBtn;
|
|
clearBtn = dom.clearBtn;
|
|
categorySelect = dom.categorySelect;
|
|
historyPanel = dom.historyPanel;
|
|
showLoadingFn = dom.showLoading;
|
|
hideLoadingFn = dom.hideLoading;
|
|
document.body.appendChild(windowEl);
|
|
|
|
// 8. 启用拖拽
|
|
const headerEl = windowEl.querySelector('.csk-header') as HTMLElement;
|
|
if (headerEl) {
|
|
dragCleanup = enableDrag(headerEl, windowEl);
|
|
}
|
|
|
|
// 9. 初始化对话模块
|
|
initChat(config, {
|
|
messagesContainer,
|
|
inputEl,
|
|
sendBtn,
|
|
clearBtn,
|
|
categorySelect,
|
|
historyPanel,
|
|
showLoading: showLoadingFn,
|
|
hideLoading: hideLoadingFn,
|
|
});
|
|
|
|
// 10. 监听知识库分类切换事件
|
|
windowEl.addEventListener('csk:categoryChange', ((e: CustomEvent) => {
|
|
setCategory(e.detail.categoryId);
|
|
}) as EventListener);
|
|
|
|
// 11. 监听会话管理面板加载事件
|
|
windowEl.addEventListener('csk:loadHistory', () => {
|
|
loadHistoryConversations();
|
|
});
|
|
|
|
isInitialized = true;
|
|
logger.lifecycleInit(config.integrateId, config.requestDomain);
|
|
|
|
// 12. 异步初始化 chatId 和对话历史(不阻塞 UI)
|
|
initChatHistory().catch(err => {
|
|
logger.warn('chatId 初始化失败,将在发送消息时重试', err);
|
|
});
|
|
}
|
|
|
|
/** 销毁 SDK 实例 */
|
|
function destroy(): void {
|
|
if (!isInitialized) return;
|
|
|
|
if (launcherEl && launcherEl.parentNode) { launcherEl.parentNode.removeChild(launcherEl); launcherEl = null; }
|
|
if (windowEl && windowEl.parentNode) { windowEl.parentNode.removeChild(windowEl); windowEl = null; }
|
|
if (dragCleanup) { dragCleanup(); dragCleanup = null; }
|
|
|
|
removeStyles();
|
|
|
|
const oldIntegrateId = config?.integrateId;
|
|
config = null;
|
|
isInitialized = false;
|
|
messagesContainer = null;
|
|
inputEl = null;
|
|
sendBtn = null;
|
|
clearBtn = null;
|
|
categorySelect = null;
|
|
historyPanel = null;
|
|
showLoadingFn = null;
|
|
hideLoadingFn = null;
|
|
|
|
logger.lifecycleDestroy(oldIntegrateId || '');
|
|
}
|
|
|
|
function open(): void {
|
|
if (!windowEl) return;
|
|
windowEl.classList.remove('csk-window--hidden');
|
|
}
|
|
|
|
function close(): void {
|
|
if (!windowEl) return;
|
|
windowEl.classList.add('csk-window--hidden');
|
|
}
|
|
|
|
function toggle(): void {
|
|
if (!windowEl) return;
|
|
if (windowEl.classList.contains('csk-window--hidden')) {
|
|
open();
|
|
setTimeout(() => { if (inputEl) inputEl.focus(); }, 100);
|
|
} else {
|
|
close();
|
|
}
|
|
}
|
|
|
|
function clearHistory(): void {
|
|
if (!config) return;
|
|
if (clearBtn) { clearBtn.click(); }
|
|
else if (confirm('确定清空所有对话记录?')) { clearMessages(config.integrateId); }
|
|
}
|
|
|
|
// ==================== 挂载到全局 ====================
|
|
|
|
const ChatbotSDK: ChatbotSDKInstance = {
|
|
init,
|
|
destroy,
|
|
open,
|
|
close,
|
|
toggle,
|
|
clearHistory,
|
|
};
|
|
|
|
if (typeof window !== 'undefined') {
|
|
(window as unknown as Record<string, unknown>).ChatbotSDK = ChatbotSDK;
|
|
}
|
|
|
|
export default ChatbotSDK;
|