Spaces:
Sleeping
Sleeping
| <html lang="zh"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>知微小智</title> | |
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> | |
| <style> | |
| body { | |
| font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
| margin: 0; | |
| padding: 0; | |
| background-color: #f5f8fa; | |
| color: #333; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| } | |
| .sidebar { | |
| background-color: #fff; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); | |
| padding: 20px; | |
| margin-bottom: 20px; | |
| } | |
| .chat-container { | |
| display: flex; | |
| height: calc(100vh - 200px); | |
| min-height: 500px; | |
| gap: 20px; | |
| } | |
| .chat-box { | |
| flex: 1; | |
| background-color: #fff; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| } | |
| .chat-messages { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 20px; | |
| } | |
| .message { | |
| margin-bottom: 16px; | |
| display: flex; | |
| align-items: flex-start; | |
| } | |
| .user-message { | |
| justify-content: flex-end; | |
| } | |
| .assistant-message { | |
| justify-content: flex-start; | |
| } | |
| .message-content { | |
| max-width: 80%; | |
| padding: 12px 16px; | |
| border-radius: 18px; | |
| overflow-wrap: break-word; | |
| } | |
| .user-message .message-content { | |
| background-color: #0084ff; | |
| color: white; | |
| border-bottom-right-radius: 4px; | |
| } | |
| .assistant-message .message-content { | |
| background-color: #f1f0f0; | |
| color: #333; | |
| border-bottom-left-radius: 4px; | |
| } | |
| .message-image { | |
| max-width: 100%; | |
| max-height: 300px; | |
| border-radius: 8px; | |
| margin-bottom: 8px; | |
| } | |
| .input-area { | |
| background-color: #fff; | |
| border-top: 1px solid #e6ecf0; | |
| padding: 15px; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .image-preview { | |
| display: flex; | |
| gap: 10px; | |
| margin-bottom: 10px; | |
| flex-wrap: wrap; | |
| } | |
| .image-preview img { | |
| max-width: 100px; | |
| max-height: 100px; | |
| border-radius: 4px; | |
| object-fit: cover; | |
| } | |
| .preview-container { | |
| position: relative; | |
| display: inline-block; | |
| } | |
| .remove-image { | |
| position: absolute; | |
| top: -5px; | |
| right: -5px; | |
| background-color: rgba(255, 255, 255, 0.8); | |
| border-radius: 50%; | |
| width: 20px; | |
| height: 20px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| font-size: 12px; | |
| border: 1px solid #ddd; | |
| } | |
| textarea { | |
| flex: 1; | |
| border: 1px solid #e6ecf0; | |
| border-radius: 20px; | |
| padding: 10px 15px; | |
| resize: none; | |
| height: 48px; | |
| font-size: 16px; | |
| line-height: 1.5; | |
| outline: none; | |
| } | |
| .send-button { | |
| border: none; | |
| background-color: #0084ff; | |
| color: white; | |
| border-radius: 50%; | |
| width: 40px; | |
| height: 40px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| } | |
| .send-button:disabled { | |
| background-color: #cccccc; | |
| cursor: not-allowed; | |
| } | |
| .clear-button { | |
| background-color: #f7f7f7; | |
| border: 1px solid #ddd; | |
| color: #666; | |
| padding: 8px 16px; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| margin-bottom: 15px; | |
| } | |
| .clear-button:hover { | |
| background-color: #ebebeb; | |
| } | |
| .system-prompt { | |
| width: 100%; | |
| padding: 10px; | |
| border: 1px solid #e6ecf0; | |
| border-radius: 4px; | |
| margin-bottom: 15px; | |
| min-height: 100px; | |
| resize: vertical; | |
| } | |
| .thinking { | |
| font-style: italic; | |
| color: #666; | |
| margin-bottom: 15px; | |
| padding: 10px; | |
| border-radius: 8px; | |
| background-color: #f9f9f9; | |
| display: inline-block; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1 class="mb-4">知微小智</h1> | |
| <div class="chat-container"> | |
| <div class="chat-box"> | |
| <div class="chat-messages" id="chat-messages"> | |
| <!-- 聊天消息将在这里动态添加 --> | |
| </div> | |
| <div class="input-area-container"> | |
| <div class="image-preview" id="image-preview"></div> | |
| <div class="input-area"> | |
| <textarea id="message-input" placeholder="输入消息或按Ctrl+V粘贴图片..." onkeydown="handleKeyDown(event)"></textarea> | |
| <button class="send-button" id="send-button" onclick="sendMessage()" disabled> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> | |
| <path d="M22 2L11 13" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |
| <path d="M22 2L15 22L11 13L2 9L22 2Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="sidebar" style="width: 300px;"> | |
| <h5>设置</h5> | |
| <label for="system-prompt">系统提示</label> | |
| <textarea id="system-prompt" class="system-prompt">你是一个AI度量专家助手。你可以分析文本和图像的内容。能根据用户的需求给出度量建议和洞察</textarea> | |
| <button class="clear-button" onclick="clearChat()">清除聊天记录</button> | |
| <div class="mt-4"> | |
| <p><strong>使用说明</strong></p> | |
| <ul> | |
| <li>输入文字直接提问</li> | |
| <li>使用Ctrl+V粘贴图片</li> | |
| <li>图片和文字可以一起发送</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // 全局变量 | |
| let chatHistory = []; | |
| let currentImageData = null; | |
| // 页面加载时初始化 | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // 监听粘贴事件 | |
| document.addEventListener('paste', handlePaste); | |
| // 监听输入框变化 | |
| const messageInput = document.getElementById('message-input'); | |
| messageInput.addEventListener('input', function() { | |
| document.getElementById('send-button').disabled = !messageInput.value.trim() && !currentImageData; | |
| }); | |
| }); | |
| // 处理粘贴事件 | |
| function handlePaste(event) { | |
| const items = (event.clipboardData || event.originalEvent.clipboardData).items; | |
| for (let i = 0; i < items.length; i++) { | |
| if (items[i].type.indexOf('image') !== -1) { | |
| const blob = items[i].getAsFile(); | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| // 设置当前图片数据 | |
| currentImageData = e.target.result; | |
| // 显示图片预览 | |
| const imagePreview = document.getElementById('image-preview'); | |
| imagePreview.innerHTML = ` | |
| <div class="preview-container"> | |
| <img src="${e.target.result}" alt="粘贴的图片"> | |
| <div class="remove-image" onclick="removeImage()">×</div> | |
| </div> | |
| `; | |
| // 启用发送按钮 | |
| document.getElementById('send-button').disabled = false; | |
| // 发送图片到服务器保存 | |
| saveImageToServer(currentImageData); | |
| }; | |
| reader.readAsDataURL(blob); | |
| } | |
| } | |
| } | |
| // 移除图片 | |
| function removeImage() { | |
| currentImageData = null; | |
| document.getElementById('image-preview').innerHTML = ''; | |
| // 如果消息输入框也是空的,禁用发送按钮 | |
| const messageInput = document.getElementById('message-input'); | |
| document.getElementById('send-button').disabled = !messageInput.value.trim(); | |
| } | |
| // 保存图片到服务器 | |
| function saveImageToServer(imageData) { | |
| fetch('/api/paste_image', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ image_data: imageData }) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| if (data.status === 'success') { | |
| // 成功保存,可以在这里做一些处理,比如更新图片预览的src为服务器返回的URL | |
| console.log('图片已保存到服务器:', data.image_url); | |
| } else { | |
| console.error('保存图片错误:', data.message); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('保存图片请求错误:', error); | |
| }); | |
| } | |
| // 发送消息的键盘处理 | |
| function handleKeyDown(event) { | |
| if (event.key === 'Enter' && !event.shiftKey) { | |
| event.preventDefault(); | |
| sendMessage(); | |
| } | |
| } | |
| // 发送消息 | |
| function sendMessage() { | |
| const messageInput = document.getElementById('message-input'); | |
| const message = messageInput.value.trim(); | |
| // 如果没有消息文本也没有图片,不发送 | |
| if (!message && !currentImageData) { | |
| return; | |
| } | |
| // 添加用户消息到聊天窗口 | |
| addMessageToChat('user', message, currentImageData); | |
| // 准备请求数据 | |
| const systemPrompt = document.getElementById('system-prompt').value.trim(); | |
| let history = [...chatHistory]; | |
| // 确保历史记录中有系统提示 | |
| const hasSystemPrompt = history.some(msg => msg.role === 'system'); | |
| if (!hasSystemPrompt && systemPrompt) { | |
| history.unshift({ | |
| role: 'system', | |
| content: systemPrompt | |
| }); | |
| } | |
| // 显示思考中状态 | |
| const thinkingEl = document.createElement('div'); | |
| thinkingEl.className = 'message assistant-message'; | |
| thinkingEl.innerHTML = '<div class="thinking">思考中...</div>'; | |
| document.getElementById('chat-messages').appendChild(thinkingEl); | |
| // 发送请求到后端 | |
| fetch('/api/chat', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| message: message, | |
| image: currentImageData, | |
| history: history | |
| }) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| // 移除思考中状态 | |
| document.getElementById('chat-messages').removeChild(thinkingEl); | |
| if (data.status === 'success') { | |
| // 添加助手响应到聊天窗口 | |
| addMessageToChat('assistant', data.response); | |
| } else { | |
| // 显示错误消息 | |
| addMessageToChat('assistant', `错误: ${data.message}`); | |
| } | |
| }) | |
| .catch(error => { | |
| // 移除思考中状态 | |
| document.getElementById('chat-messages').removeChild(thinkingEl); | |
| // 显示错误消息 | |
| addMessageToChat('assistant', `请求错误: ${error}`); | |
| }); | |
| // 清空输入 | |
| messageInput.value = ''; | |
| removeImage(); | |
| // 禁用发送按钮 | |
| document.getElementById('send-button').disabled = true; | |
| } | |
| // 添加消息到聊天窗口和历史记录 | |
| function addMessageToChat(role, content, image = null) { | |
| const chatMessages = document.getElementById('chat-messages'); | |
| // 创建消息元素 | |
| const messageEl = document.createElement('div'); | |
| messageEl.className = `message ${role}-message`; | |
| let messageContent = ''; | |
| // 如果有图片,添加图片 | |
| if (image) { | |
| messageContent += `<img src="${image}" alt="用户上传的图片" class="message-image"><br>`; | |
| } | |
| // 添加文本内容 | |
| if (content) { | |
| messageContent += content.replace(/\n/g, '<br>'); | |
| } | |
| messageEl.innerHTML = `<div class="message-content">${messageContent}</div>`; | |
| chatMessages.appendChild(messageEl); | |
| // 滚动到底部 | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| // 添加到历史记录 | |
| chatHistory.push({ | |
| role: role, | |
| content: content | |
| }); | |
| } | |
| // 清除聊天记录 | |
| function clearChat() { | |
| chatHistory = []; | |
| document.getElementById('chat-messages').innerHTML = ''; | |
| removeImage(); | |
| } | |
| </script> | |
| </body> | |
| </html> |