本地 RAG 知识库
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

/**
* 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;