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>
 |