Merge pull request #2593 from zhayujie/feat-web-ui

feat: web ui channel optimization
This commit is contained in:
zhayujie
2025-05-19 11:48:34 +08:00
committed by GitHub
2 changed files with 176 additions and 135 deletions

View File

@@ -197,27 +197,54 @@
}
.user-container {
width: 100%;
margin: 0;
padding: 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: flex-end;
margin: 10px 0;
padding: 0 15px;
}
.user-container .message-container {
flex-direction: row-reverse;
text-align: right;
}
.user-container .avatar {
margin-left: 15px;
margin-right: 0;
}
.user-container .message-content {
align-items: flex-end;
}
.user-container .message {
background-color: var(--bot-msg-bg);
border-radius: 10px;
margin-bottom: 8px;
}
.bot-container .message {
background-color: var(--bot-msg-bg);
border-radius: 10px 10px 10px 0;
margin-bottom: 8px;
}
.avatar {
width: 30px;
height: 30px;
border-radius: 2px;
width: 36px;
height: 36px;
border-radius: 50%;
background-color: #e0e0e0;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
flex-shrink: 0;
margin-top: 4px;
}
.bot-avatar {
background-color: #10a37f;
color: white;
margin-top: 4px; /* 微调头像位置,使其与文本更好地对齐 */
}
.user-avatar {
@@ -226,17 +253,24 @@
}
.message-content {
display: flex;
flex-direction: column;
flex: 1;
line-height: 1.6;
padding-top: 2px;
text-align: left;
padding-top: 0; /* 移除顶部内边距 */
}
.bot-container .message-content {
align-items: flex-start;
margin-top: 0; /* 确保没有顶部外边距 */
}
.message {
width: 100%;
padding: 12px 16px;
border-radius: 10px;
margin-top: 0; /* 移除顶部外边距 */
margin-bottom: 8px;
word-wrap: break-word;
white-space: pre-wrap;
line-height: 1.3;
overflow-wrap: break-word;
}
.message p {
@@ -300,7 +334,20 @@
.timestamp {
font-size: 0.75rem;
color: var(--text-light);
margin-top: 5px;
padding-left: 2px;
}
/* 为机器人消息的时间戳添加左对齐 */
.bot-container .timestamp {
align-self: flex-start;
margin-left: 15px;
}
/* 为用户消息的时间戳添加右对齐 */
.user-container .timestamp {
align-self: flex-end;
padding-right: 2px;
padding-left: 0;
}
#input-container {
@@ -519,6 +566,8 @@
position: relative;
top: -5px;
left: -10px;
margin-top: 10px;
margin-bottom: 5px;
}
.typing-indicator span {
@@ -571,6 +620,30 @@
background-color: rgba(255, 255, 255, 0.1);
font-weight: bold;
}
/* 基础消息样式 */
.message {
border-radius: 10px;
word-wrap: break-word;
overflow-wrap: break-word;
}
/* 用户消息样式 */
.user-container .message {
background-color: var(--bot-msg-bg);
border-radius: 10px;
margin-bottom: 8px;
padding: 12px 16px;
}
/* 机器人消息样式 */
.bot-container .message {
background-color: var(--bot-msg-bg);
border-radius: 10px 10px 10px 0;
margin-bottom: 8px;
padding: 0px 16px 12px 16px;
margin-top: 0;
}
</style>
</head>
<body>
@@ -689,121 +762,10 @@
clearChat();
});
// 清空聊天记录并显示欢迎屏幕
function clearChat() {
// 清空消息区域
messagesDiv.innerHTML = '';
// 创建欢迎屏幕
const newWelcomeScreen = document.createElement('div');
newWelcomeScreen.id = 'welcome-screen';
newWelcomeScreen.innerHTML = `
<h1 id="welcome-title">AI 助手</h1>
<p id="welcome-subtitle">我可以回答问题、提供信息或者帮助您完成各种任务</p>
<div class="examples-container">
<div class="example-card">
<div class="example-title">解释复杂概念</div>
<div class="example-text">用简单的语言解释量子计算</div>
</div>
<div class="example-card">
<div class="example-title">创意写作</div>
<div class="example-text">写一个关于未来城市的短篇故事</div>
</div>
<div class="example-card">
<div class="example-title">编程帮助</div>
<div class="example-text">如何用Python写一个简单的网络爬虫</div>
</div>
</div>
`;
// 设置样式
newWelcomeScreen.style.display = 'flex';
newWelcomeScreen.style.flexDirection = 'column';
newWelcomeScreen.style.alignItems = 'center';
newWelcomeScreen.style.justifyContent = 'center';
newWelcomeScreen.style.height = '100%';
newWelcomeScreen.style.textAlign = 'center';
newWelcomeScreen.style.padding = '20px';
// 添加到DOM
messagesDiv.appendChild(newWelcomeScreen);
// 绑定示例卡片事件
newWelcomeScreen.querySelectorAll('.example-card').forEach(card => {
card.addEventListener('click', function() {
const exampleText = this.querySelector('.example-text').textContent;
input.value = exampleText;
input.dispatchEvent(new Event('input'));
input.focus();
});
});
// 清空localStorage中的消息 - 使用用户ID作为键
localStorage.setItem(`chatMessages_${userId}`, JSON.stringify([]));
// 在移动设备上关闭侧边栏
if (window.innerWidth <= 768) {
sidebar.classList.remove('active');
}
}
// 从localStorage加载消息 - 使用用户ID作为键
function loadMessagesFromLocalStorage() {
try {
return JSON.parse(localStorage.getItem(`chatMessages_${userId}`) || '[]');
} catch (error) {
console.error('Error loading messages from localStorage:', error);
return [];
}
}
// 保存消息到localStorage - 使用用户ID作为键
function saveMessageToLocalStorage(message) {
try {
const messages = loadMessagesFromLocalStorage();
messages.push(message);
localStorage.setItem(`chatMessages_${userId}`, JSON.stringify(messages));
} catch (error) {
console.error('Error saving message to localStorage:', error);
}
}
// 初始化代码
document.addEventListener('DOMContentLoaded', function() {
// 移除原始欢迎屏幕
const originalWelcomeScreen = document.getElementById('welcome-screen');
if (originalWelcomeScreen) {
originalWelcomeScreen.remove();
}
// 清空消息区域,确保不会重复显示消息
messagesDiv.innerHTML = '';
// 加载消息
const messages = loadMessagesFromLocalStorage();
if (messages.length === 0) {
// 如果没有消息,显示欢迎屏幕
clearChat();
} else {
// 显示现有消息
messages.forEach(msg => {
if (msg.role === 'user') {
// 使用不保存到localStorage的版本显示消息
displayUserMessage(msg.content, new Date(msg.timestamp));
} else if (msg.role === 'assistant') {
// 使用不保存到localStorage的版本显示消息
displayBotMessage(msg.content, new Date(msg.timestamp));
}
});
}
});
// 发送按钮点击事件
sendButton.onclick = function() {
sendButton.addEventListener('click', function() {
sendMessage();
};
});
// 输入框按键事件
input.addEventListener('keydown', function(event) {
@@ -843,6 +805,11 @@
// 添加一个等待中的机器人消息
const loadingContainer = addLoadingMessage();
// 清空输入框并重置高度 - 移到这里,确保发送后立即清空
input.value = '';
input.style.height = '52px';
sendButton.disabled = true;
// 发送到服务器并等待响应
fetch('/message', {
method: 'POST',
@@ -882,11 +849,6 @@
// 显示错误消息
addBotMessage("抱歉,发生了错误,请稍后再试。", new Date());
});
// 清空输入框并重置高度
input.value = '';
input.style.height = '52px';
sendButton.disabled = true;
}
}
@@ -1134,6 +1096,86 @@
// 初始化
input.focus();
// 清空聊天记录并显示欢迎屏幕
function clearChat() {
// 清空消息区域
messagesDiv.innerHTML = '';
// 创建欢迎屏幕
const newWelcomeScreen = document.createElement('div');
newWelcomeScreen.id = 'welcome-screen';
newWelcomeScreen.innerHTML = `
<h1 id="welcome-title">AI 助手</h1>
<p id="welcome-subtitle">我可以回答问题、提供信息或者帮助您完成各种任务</p>
<div class="examples-container">
<div class="example-card">
<div class="example-title">解释复杂概念</div>
<div class="example-text">用简单的语言解释量子计算</div>
</div>
<div class="example-card">
<div class="example-title">创意写作</div>
<div class="example-text">写一个关于未来城市的短篇故事</div>
</div>
<div class="example-card">
<div class="example-title">编程帮助</div>
<div class="example-text">如何用Python写一个简单的网络爬虫</div>
</div>
</div>
`;
// 设置样式
newWelcomeScreen.style.display = 'flex';
newWelcomeScreen.style.flexDirection = 'column';
newWelcomeScreen.style.alignItems = 'center';
newWelcomeScreen.style.justifyContent = 'center';
newWelcomeScreen.style.height = '100%';
newWelcomeScreen.style.textAlign = 'center';
newWelcomeScreen.style.padding = '20px';
// 添加到DOM
messagesDiv.appendChild(newWelcomeScreen);
// 绑定示例卡片事件
newWelcomeScreen.querySelectorAll('.example-card').forEach(card => {
card.addEventListener('click', function() {
const exampleText = this.querySelector('.example-text').textContent;
input.value = exampleText;
input.dispatchEvent(new Event('input'));
input.focus();
});
});
// 清空localStorage中的消息 - 使用用户ID作为键
localStorage.setItem(`chatMessages_${userId}`, JSON.stringify([]));
// 在移动设备上关闭侧边栏
if (window.innerWidth <= 768) {
sidebar.classList.remove('active');
}
}
// 从localStorage加载消息 - 使用用户ID作为键
function loadMessagesFromLocalStorage() {
try {
return JSON.parse(localStorage.getItem(`chatMessages_${userId}`) || '[]');
} catch (error) {
console.error('Error loading messages from localStorage:', error);
return [];
}
}
// 保存消息到localStorage - 使用用户ID作为键
function saveMessageToLocalStorage(message) {
try {
const messages = loadMessagesFromLocalStorage();
messages.push(message);
localStorage.setItem(`chatMessages_${userId}`, JSON.stringify(messages));
} catch (error) {
console.error('Error saving message to localStorage:', error);
}
}
</script>
</body>
</html>

View File

@@ -13,7 +13,6 @@ from config import conf
import os
import mimetypes # 添加这行来处理MIME类型
class WebMessage(ChatMessage):
def __init__(
self,
@@ -57,7 +56,7 @@ class WebChannel(ChatChannel):
if reply.type in self.NOT_SUPPORT_REPLYTYPE:
logger.warning(f"Web channel doesn't support {reply.type} yet")
return
# 获取用户ID
user_id = context.get("receiver", None)
if not user_id: