297 lines
11 KiB
HTML
297 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<title>DeepSeek 32B Chat</title>
|
|
<script src="js/marked.min.js"></script>
|
|
<link rel="stylesheet" href="css/main.css" />
|
|
</head>
|
|
<body>
|
|
<div id="chatBox">
|
|
<div class="messages-container"></div>
|
|
<div id="inputArea">
|
|
<input type="text" id="userInput" placeholder="输入消息..." />
|
|
<button onclick="sendMessage()" id="sendBtn">
|
|
<svg class="icon" viewBox="0 0 1057 1024" xmlns="http://www.w3.org/2000/svg" width="20" height="20">
|
|
<path
|
|
d="M891.904 825.782857L462.482286 693.613714l429.421714-495.396571-561.517714 495.469714L0.073143 561.590857 1057.133714 0.073143 891.904 825.782857zM462.482286 1024v-231.058286l132.096 65.828572-132.096 165.156571z"
|
|
fill="#ffffff"
|
|
></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 添加上传按钮 -->
|
|
<div class="upload-button-container">
|
|
<button onclick="showUploadDialog()" class="upload-btn">
|
|
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="20" height="20">
|
|
<path
|
|
d="M1024 736s-3.4048-10.24-10.24-20.5056l-150.1696-300.3392c-6.8352-10.24-20.48-20.5056-34.1248-20.5056H706.56c-13.6448 0-23.8848 10.24-23.8848 23.9104v40.96c0 13.6448 10.2144 23.9104 23.8848 23.9104h40.96c13.6448 0 27.2896 10.24 34.1248 20.48l105.8304 215.0656c6.8352 10.1888 0 20.4544-13.6704 20.4544H706.56c-13.6448 0-23.8848 10.24-23.8848 23.9104v122.9056c0 13.6448-10.24 23.9104-23.9104 23.9104H365.2352a23.3216 23.3216 0 0 1-23.8848-23.9104v-122.9056c0-13.6448-10.24-23.9104-23.9104-23.9104H146.7648c-13.6448 0-17.0752-10.24-13.6448-20.4544l109.2352-215.0656c6.8352-10.2144 20.48-20.48 34.1248-20.48h37.5552c13.6448 0 23.9104-10.24 23.9104-23.9104v-37.5552c0-13.6448-10.24-23.8848-23.9104-23.8848h-122.88c-13.6704 0-27.3152 10.2144-34.1248 20.48L10.24 715.4944c-6.8352 10.2656-10.24 20.5056-10.24 20.5056v235.4944c0 13.6448 10.24 23.9104 23.8848 23.9104h976.2048c13.6448 0 23.9104-10.24 23.9104-23.9104V736zM300.3648 292.2752h126.2848v358.4h170.6752v-358.4h133.12c13.6448 0 17.0752-6.8352 6.8352-17.0496l-211.6352-238.9504c-6.8352-10.2144-23.8848-10.2144-30.72 0l-204.8 238.9504c-6.8096 10.2144-3.4048 17.0496 10.24 17.0496z"
|
|
fill="#fff"
|
|
></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- 文件上传弹窗 -->
|
|
<div id="uploadDialog" class="upload-dialog">
|
|
<div class="upload-dialog-content">
|
|
<span class="close-btn" onclick="closeUploadDialog()">×</span>
|
|
<h2>上传文件</h2>
|
|
<div class="upload-area" id="dropZone">
|
|
<input type="file" id="fileInput" style="display: none" onchange="handleFileSelect(event)" />
|
|
<div class="upload-placeholder" onclick="document.getElementById('fileInput').click()">
|
|
<i class="fas fa-cloud-upload-alt"></i>
|
|
<p>点击或拖拽文件到此处上传</p>
|
|
<p class="supported-formats">支持的格式: PDF, DOC, DOCX</p>
|
|
</div>
|
|
</div>
|
|
<div id="uploadProgress" class="upload-progress" style="display: none">
|
|
<div class="progress-bar">
|
|
<div class="progress-fill"></div>
|
|
</div>
|
|
<span class="progress-text">0%</span>
|
|
</div>
|
|
<div id="uploadStatus" class="upload-status"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const url = "http://localhost:8899";
|
|
|
|
let currentBotMessage = null;
|
|
|
|
// 添加消息到聊天框
|
|
function addMessage(content, isUser = false) {
|
|
const messagesContainer = document.querySelector(".messages-container");
|
|
const messageDiv = document.createElement("div");
|
|
messageDiv.className = `message ${isUser ? "user-message" : "bot-message"}`;
|
|
|
|
// 创建头像元素
|
|
const avatar = document.createElement("img");
|
|
avatar.className = "avatar";
|
|
avatar.src = isUser ? "./images/user-avatar.png" : "/images/bot-avatar.png";
|
|
avatar.alt = isUser ? "User Avatar" : "Bot Avatar";
|
|
|
|
// 创建消息内容容器
|
|
const messageContent = document.createElement("div");
|
|
messageContent.className = "message-content";
|
|
|
|
if (isUser) {
|
|
messageContent.textContent = content;
|
|
} else {
|
|
messageContent.innerHTML = marked.parse(content);
|
|
}
|
|
|
|
// 组装消息元素
|
|
messageDiv.appendChild(avatar);
|
|
messageDiv.appendChild(messageContent);
|
|
|
|
messagesContainer.appendChild(messageDiv);
|
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
return messageDiv;
|
|
}
|
|
|
|
// 修改处理流式响应的部分
|
|
async function streamResponse(prompt) {
|
|
const btn = document.getElementById("sendBtn");
|
|
btn.disabled = true;
|
|
let accumulatedContent = "";
|
|
|
|
try {
|
|
const response = await fetch(url + "/knows/generate", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Accept: "text/event-stream"
|
|
},
|
|
body: JSON.stringify({
|
|
keyword: prompt
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
|
|
if (!currentBotMessage) {
|
|
// 创建完整的消息结构,包括头像
|
|
const messageDiv = document.createElement("div");
|
|
messageDiv.className = "message bot-message";
|
|
|
|
// 创建头像元素
|
|
const avatar = document.createElement("img");
|
|
avatar.className = "avatar";
|
|
avatar.src = "./images/bot-avatar.png";
|
|
avatar.alt = "Bot Avatar";
|
|
|
|
// 创建消息内容容器
|
|
const messageContent = document.createElement("div");
|
|
messageContent.className = "message-content";
|
|
|
|
// 组装消息元素
|
|
messageDiv.appendChild(avatar);
|
|
messageDiv.appendChild(messageContent);
|
|
|
|
document.querySelector(".messages-container").appendChild(messageDiv);
|
|
currentBotMessage = messageContent; // 更新 currentBotMessage 为消息内容容器
|
|
}
|
|
|
|
let thinkContent = true;
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
const chunk = decoder.decode(value);
|
|
const lines = chunk.split("\n").filter((line) => line.trim().startsWith("data: "));
|
|
|
|
for (const line of lines) {
|
|
try {
|
|
// 移除 "data: " 前缀并解析JSON
|
|
const jsonData = JSON.parse(line.substring(6));
|
|
|
|
if (jsonData.response) {
|
|
let content = jsonData.response;
|
|
// if (content.includes("\u003c/think\u003e")) {
|
|
// thinkContent = false;
|
|
// }
|
|
|
|
// if (!thinkContent) {
|
|
// accumulatedContent += content;
|
|
// currentBotMessage.innerHTML = marked.parse(accumulatedContent);
|
|
// }
|
|
|
|
accumulatedContent += content;
|
|
currentBotMessage.innerHTML = marked.parse(accumulatedContent);
|
|
}
|
|
|
|
if (jsonData.done) {
|
|
currentBotMessage = null;
|
|
}
|
|
} catch (error) {
|
|
console.error("解析数据失败:", error, "原始数据:", line);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
document.querySelector(".messages-container").scrollTop = document.querySelector(".messages-container").scrollHeight;
|
|
}
|
|
} catch (error) {
|
|
console.error("请求失败:", error);
|
|
addMessage(`[错误] ${error.message}`, false);
|
|
} finally {
|
|
btn.disabled = false;
|
|
}
|
|
}
|
|
|
|
// 发送消息
|
|
async function sendMessage() {
|
|
const input = document.getElementById("userInput");
|
|
const userMessage = input.value.trim();
|
|
|
|
if (!userMessage) return;
|
|
|
|
addMessage(userMessage, true);
|
|
input.value = "";
|
|
|
|
await streamResponse(userMessage);
|
|
}
|
|
|
|
// 回车键发送
|
|
document.getElementById("userInput").addEventListener("keypress", (e) => {
|
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
e.preventDefault();
|
|
sendMessage();
|
|
}
|
|
});
|
|
|
|
function showUploadDialog() {
|
|
document.getElementById("uploadDialog").style.display = "block";
|
|
}
|
|
|
|
function closeUploadDialog() {
|
|
document.getElementById("uploadDialog").style.display = "none";
|
|
resetUploadDialog();
|
|
}
|
|
|
|
function resetUploadDialog() {
|
|
document.getElementById("fileInput").value = "";
|
|
document.getElementById("uploadProgress").style.display = "none";
|
|
document.getElementById("uploadStatus").innerHTML = "";
|
|
document.getElementById("uploadStatus").className = "upload-status";
|
|
}
|
|
|
|
function handleFileSelect(event) {
|
|
const file = event.target.files[0];
|
|
if (file) {
|
|
uploadFile(file);
|
|
}
|
|
}
|
|
|
|
function updateProgress(percent) {
|
|
const progressBar = document.querySelector(".progress-fill");
|
|
const progressText = document.querySelector(".progress-text");
|
|
progressBar.style.width = `${percent}%`;
|
|
progressText.textContent = `${percent}%`;
|
|
}
|
|
|
|
function uploadFile(file) {
|
|
const formData = new FormData();
|
|
formData.append("file", file);
|
|
|
|
const progressDiv = document.getElementById("uploadProgress");
|
|
const statusDiv = document.getElementById("uploadStatus");
|
|
|
|
progressDiv.style.display = "block";
|
|
statusDiv.innerHTML = "正在上传...";
|
|
statusDiv.className = "upload-status";
|
|
|
|
fetch(url + "/api/file/upload", {
|
|
method: "POST",
|
|
body: formData
|
|
})
|
|
.then((response) => response.json())
|
|
.then((data) => {
|
|
statusDiv.innerHTML = data.message;
|
|
statusDiv.className = "upload-status success";
|
|
updateProgress(100);
|
|
setTimeout(() => {
|
|
closeUploadDialog();
|
|
}, 2000);
|
|
})
|
|
.catch((error) => {
|
|
statusDiv.innerHTML = "上传失败: " + error.message;
|
|
statusDiv.className = "upload-status error";
|
|
updateProgress(0);
|
|
});
|
|
}
|
|
|
|
// 添加拖拽上传支持
|
|
const dropZone = document.getElementById("dropZone");
|
|
|
|
dropZone.addEventListener("dragover", (e) => {
|
|
e.preventDefault();
|
|
dropZone.style.borderColor = "#4CAF50";
|
|
});
|
|
|
|
dropZone.addEventListener("dragleave", (e) => {
|
|
e.preventDefault();
|
|
dropZone.style.borderColor = "#ccc";
|
|
});
|
|
|
|
dropZone.addEventListener("drop", (e) => {
|
|
e.preventDefault();
|
|
dropZone.style.borderColor = "#ccc";
|
|
const file = e.dataTransfer.files[0];
|
|
if (file) {
|
|
uploadFile(file);
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|