开发了一个智能助手页面,需要嵌套到系统应用中,为了方便使用,开发了sdk
/**
* DeepSeek Bubble SDK
* 提供一个可拖动的气泡,点击后打开 DeepSeek 聊天界面
* @param {Object} options 配置选项
* @param {string} options.aiId 必填,AI的ID
* @param {string} options.session 必填,会话ID
* @param {string} [options.position='bottom-right'] 气泡初始位置 (top-left, top-right, bottom-left, bottom-right)
* @param {string} [options.bubbleColor='#4CAF50'] 气泡颜色
* @param {string} [options.bubbleIcon='?'] 气泡图标/文字
* @param {number} [options.bubbleSize=60] 气泡大小(px)
* @param {string} [options.url=https://localhost:8080/view/aiv1] 接口url
* @param {string} [options.title=智能助手] 抬头
*/
class DeepSeekBubble {
constructor(options) {
if (!options.aiId || !options.session) {
console.error('DeepSeekBubble: aiId and session are required parameters');
return;
}
this.options = {
position: 'bottom-right',
bubbleColor: '#4CAF50',
bubbleIcon: '?',
bubbleSize: 60,
url: "https://deepseek.dev.ihgkj.com/view/aiv1",
title: "智能助手",
...options
};
this.isOpen = false;
this.initPosition = this.calculateInitialPosition();
this.dragData = { isDragging: false, offsetX: 0, offsetY: 0 };
this.init();
}
init() {
// 创建气泡元素
this.createBubble();
// 创建iframe容器
this.createIframeContainer();
// 添加到文档
document.body.appendChild(this.bubble);
document.body.appendChild(this.iframeContainer);
// 设置初始位置
this.updateBubblePosition(this.initPosition.x, this.initPosition.y);
// 添加事件监听
this.addEventListeners();
}
createBubble() {
this.bubble = document.createElement('div');
this.bubble.style.position = 'fixed';
this.bubble.style.width = `${this.options.bubbleSize}px`;
this.bubble.style.height = `${this.options.bubbleSize}px`;
this.bubble.style.borderRadius = '50%';
this.bubble.style.backgroundColor = this.options.bubbleColor;
this.bubble.style.display = 'flex';
this.bubble.style.alignItems = 'center';
this.bubble.style.justifyContent = 'center';
this.bubble.style.color = 'white';
this.bubble.style.fontSize = '24px';
this.bubble.style.cursor = 'pointer';
this.bubble.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)';
this.bubble.style.zIndex = '9998';
this.bubble.style.userSelect = 'none';
this.bubble.style.transition = 'transform 0.2s';
this.bubble.textContent = this.options.bubbleIcon;
// 悬停效果
this.bubble.addEventListener('mouseenter', () => {
this.bubble.style.transform = 'scale(1.1)';
});
this.bubble.addEventListener('mouseleave', () => {
this.bubble.style.transform = 'scale(1)';
});
}
createIframeContainer() {
this.iframeContainer = document.createElement('div');
this.iframeContainer.style.position = 'fixed';
this.iframeContainer.style.width = '400px';
this.iframeContainer.style.height = '700px';
this.iframeContainer.style.borderRadius = '12px';
this.iframeContainer.style.boxShadow = '0 8px 16px rgba(0, 0, 0, 0.2)';
this.iframeContainer.style.backgroundColor = 'white';
this.iframeContainer.style.display = 'none';
this.iframeContainer.style.flexDirection = 'column';
this.iframeContainer.style.zIndex = '9999';
this.iframeContainer.style.overflow = 'hidden';
// 创建头部
const header = document.createElement('div');
header.style.padding = '12px 16px';
header.style.backgroundColor = '#f5f5f5';
header.style.display = 'flex';
header.style.justifyContent = 'space-between';
header.style.alignItems = 'center';
header.style.borderBottom = '1px solid #e0e0e0';
header.style.cursor = 'move';
const title = document.createElement('div');
title.textContent = `${this.options.title}`;
title.style.fontWeight = 'bold';
const closeBtn = document.createElement('div');
closeBtn.textContent = '×';
closeBtn.style.fontSize = '24px';
closeBtn.style.cursor = 'pointer';
closeBtn.addEventListener('click', () => this.toggleChat(false));
header.appendChild(title);
header.appendChild(closeBtn);
// 创建iframe
this.iframe = document.createElement('iframe');
this.iframe.style.flex = '1';
this.iframe.style.border = 'none';
this.iframe.src = `${this.options.url}?aiId=${this.options.aiId}&session=${this.options.session}`;
this.iframeContainer.appendChild(header);
this.iframeContainer.appendChild(this.iframe);
// 头部拖动功能
let isDragging = false;
let offsetX, offsetY;
header.addEventListener('mousedown', (e) => {
isDragging = true;
offsetX = e.clientX - this.iframeContainer.getBoundingClientRect().left;
offsetY = e.clientY - this.iframeContainer.getBoundingClientRect().top;
this.iframeContainer.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const x = e.clientX - offsetX;
const y = e.clientY - offsetY;
this.iframeContainer.style.left = `${x}px`;
this.iframeContainer.style.top = `${y}px`;
});
document.addEventListener('mouseup', () => {
isDragging = false;
this.iframeContainer.style.cursor = '';
});
}
addEventListeners() {
// 气泡点击事件
this.bubble.addEventListener('click', () => this.toggleChat());
// 气泡拖动功能
this.bubble.addEventListener('mousedown', (e) => {
this.dragData.isDragging = true;
this.dragData.offsetX = e.clientX - this.bubble.getBoundingClientRect().left;
this.dragData.offsetY = e.clientY - this.bubble.getBoundingClientRect().top;
this.bubble.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', (e) => {
if (!this.dragData.isDragging) return;
const x = e.clientX - this.dragData.offsetX;
const y = e.clientY - this.dragData.offsetY;
this.updateBubblePosition(x, y);
});
document.addEventListener('mouseup', () => {
this.dragData.isDragging = false;
this.bubble.style.cursor = '';
});
}
toggleChat(show) {
this.isOpen = show !== undefined ? show : !this.isOpen;
if (this.isOpen) {
// 显示聊天窗口并定位在气泡附近
const bubbleRect = this.bubble.getBoundingClientRect();
let left = bubbleRect.left;
let top = bubbleRect.top - 600 - 10; // 放在气泡上方
// 如果上方空间不足,放在下方
if (top < 10) {
top = bubbleRect.top + bubbleRect.height + 10;
}
// 确保不超出屏幕右侧
if (left + 400 > window.innerWidth) {
left = window.innerWidth - 400 - 10;
}
this.iframeContainer.style.left = `${left}px`;
this.iframeContainer.style.top = `${top}px`;
this.iframeContainer.style.display = 'flex';
} else {
this.iframeContainer.style.display = 'none';
}
}
updateBubblePosition(x, y) {
// 确保气泡不会移出视口
const maxX = window.innerWidth - this.options.bubbleSize;
const maxY = window.innerHeight - this.options.bubbleSize;
const clampedX = Math.max(0, Math.min(x, maxX));
const clampedY = Math.max(0, Math.min(y, maxY));
this.bubble.style.left = `${clampedX}px`;
this.bubble.style.top = `${clampedY}px`;
}
calculateInitialPosition() {
const size = this.options.bubbleSize;
const margin = 20;
switch (this.options.position) {
case 'top-left':
return { x: margin, y: margin };
case 'top-right':
return { x: window.innerWidth - size - margin, y: margin };
case 'bottom-left':
return { x: margin, y: window.innerHeight - size - margin };
case 'bottom-right':
default:
return { x: window.innerWidth - size - margin, y: window.innerHeight - size - margin };
}
}
destroy() {
if (this.bubble && this.bubble.parentNode) {
this.bubble.parentNode.removeChild(this.bubble);
}
if (this.iframeContainer && this.iframeContainer.parentNode) {
this.iframeContainer.parentNode.removeChild(this.iframeContainer);
}
document.removeEventListener('mousemove', this.handleDragMove);
document.removeEventListener('mouseup', this.handleDragEnd);
}
}
// 全局访问
window.DeepSeekBubble = DeepSeekBubble;
使用sdk的方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./sdk-ai.js"></script>
<script>
// 初始化气泡
const deepseekBubble = new DeepSeekBubble({
aiId: 'gjkg', // 必填参数
session: 'YOUR_SESSION', // 必填参数
position: 'bottom-right', // 可选,默认'bottom-right'
bubbleColor: '#4CAF50', // 可选,气泡颜色
bubbleIcon: '?', // 可选,气泡图标/文字
bubbleSize: 35, // 可选,气泡大小(px)
url:"http://localhost:8080/view/aiv1"
});
</script>
</body>
</html>
引用示例
<script src="./sdk-ai.min.js"></script>
<script>
// 初始化气泡
const deepseekBubble = new DeepSeekBubble({
aiId: 'gjkg', // 必填参数
session: 'YOUR_SESSION', // 必填参数
position: 'bottom-right', // 可选,默认'bottom-right'
bubbleColor: '#4CAF50', // 可选,气泡颜色
bubbleIcon: '?', // 可选,气泡图标/文字
bubbleSize: 35, // 可选,气泡大小(px)
url:"http://localhost:8080/view/aiv1"
});
</script>