Files
AI/index.html
2025-09-19 11:09:55 +08:00

1219 lines
38 KiB
HTML
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>新能源运维智能体</title>
<style>
body {
margin: 0;
font-family: "Segoe UI", "PingFang SC", Arial, sans-serif;
height: 100vh;
display: flex;
background: #f0f6fb;
color: #333;
}
/* 侧边栏 */
.sidebar {
width: 240px;
background: #ffffff;
border-right: 1px solid #e3eaf2;
display: flex;
flex-direction: column;
transition: all 0.3s ease;
box-shadow: 2px 0 5px rgba(0, 0, 0, 0.04);
z-index: 10;
}
.sidebar.collapsed {
width: 0;
overflow: hidden;
}
.sidebar-header {
padding: 16px;
padding-left: 20px;
font-size: 16px;
font-weight: bold;
color: #0057ff;
border-bottom: 1px solid #f0f4f8;
display: flex;
justify-content: flex-start;
}
.sidebar-header img {
width: 24px;
margin-right: 10px;
}
.sidebar-actions {
padding: 12px;
border-bottom: 1px solid #f0f4f8;
}
.sidebar-actions button {
width: 100%;
padding: 10px;
border: none;
border-radius: 8px;
background: #0057ff;
color: #fff;
font-size: 14px;
cursor: pointer;
transition: background 0.2s;
}
.sidebar-actions button:hover {
background: #0046cc;
}
.chat-list {
flex: 1;
overflow-y: auto;
}
.chat-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #f7f9fb;
cursor: pointer;
transition: background 0.2s;
text-align: center;
}
.chat-item:hover {
background: #f2f8fd;
}
.chat-item.active {
background: #e6f0ff;
font-weight: bold;
color: #0057ff;
}
.chat-title {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.delete-btn {
margin-left: 8px;
color: #aaa;
cursor: pointer;
font-size: 14px;
}
.delete-btn:hover {
color: #e74c3c;
}
/* 收起按钮(独立漂浮在左边) */
.toggle-btn {
position: absolute;
top: 10px;
left: 190px;
background: #0057ff;
color: #fff;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
font-size: 18px;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
z-index: 20;
transition: background 0.2s;
}
.toggle-btn:hover {
background: #0046cc;
}
/* 右侧聊天区域 */
.chat-container {
flex: 1;
display: flex;
flex-direction: column;
background: #f9fcff;
justify-content: space-between;
}
.chat-container .content {
height: 70%;
overflow-y: auto;
}
.chat-header {
height: 50px;
padding: 1px 20px;
padding-left: 22px;
background: #fff;
border-bottom: 1px solid #e3eaf2;
font-size: 16px;
font-weight: bold;
color: #0057ff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
display: flex;
justify-content: flex-start;
}
.chat-header .back {
display: none;
}
.chat-messages {
flex: 1;
padding: 20px;
overflow-y: auto;
}
.message {
max-width: 70%;
margin-bottom: 16px;
padding: 12px 16px;
border-radius: 14px;
font-size: 16px;
line-height: 1.5;
/* white-space: pre-wrap;*/
white-space: normal;
word-wrap: break-word;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
animation: fadeIn 0.3s ease;
}
.message.user {
display: block;
background: #d4e5ff;
color: #000;
align-self: flex-end;
border-bottom-right-radius: 4px;
display: block;
width: fit-content;
}
.message.assistant {
max-width: 100%;
background: #fff;
color: #333;
align-self: flex-start;
border: 1px solid #e6eaf0;
border-bottom-left-radius: 4px;
}
.message img {
max-width: 200px;
border-radius: 8px;
margin-top: 8px;
}
.chat-input {
padding: 12px;
background: #fff;
border-top: 1px solid #e3eaf2;
display: flex;
flex-direction: column;
gap: 8px;
box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.04);
}
.message.thinking {
width: auto;
/* 宽度自适应内容 */
max-width: 40%;
/* 限制最大宽度 */
}
.input-row {
display: flex;
align-items: center;
flex-direction: column;
justify-content: space-between;
gap: 8px;
}
.none {
display: none;
}
.input-row .img-row {
width: 100%;
min-width: 0px;
min-height: 0px;
box-sizing: border-box;
padding: 10px;
}
.input-row .img-box .delete-btn {
position: absolute;
right: -5px;
top: -5px;
}
.input-row .img-box {
width: 40px;
position: relative
}
.input-row img {
width: 40px;
height: 40px;
border-radius: 5px;
}
.chat-input textarea {
flex: 1;
resize: none;
padding: 10px;
border-radius: 8px;
border: 1px solid #d9e4f0;
font-size: 14px;
outline: none;
background: #f9fcff;
transition: border 0.2s;
width: 100%;
}
.chat-input textarea:focus {
/* border-color: #0057ff; */
}
.chat-input button {
padding: 10px 16px;
border: none;
border-radius: 8px;
background: #0057ff;
color: #fff;
font-size: 14px;
cursor: pointer;
transition: background 0.2s;
}
.chat-input button:hover {
background: #0046cc;
}
.options-row {
display: flex;
gap: 10px;
align-items: center;
font-size: 13px;
}
select,
input[type="file"] {
font-size: 13px;
padding: 4px 6px;
border: 1px solid #d9e4f0;
border-radius: 6px;
background: #fff;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 修改后的输入框样式 - 豆包/腾讯元宝风格 */
.chat-input {
padding: 16px;
background: #fff;
border-top: 1px solid #e8e8e8;
display: flex;
flex-direction: column;
gap: 12px;
}
.input-row {
display: flex;
align-items: flex-end;
justify-content: flex-end;
/* 底部对齐 */
gap: 8px;
border: 1px solid #e5e5e5;
border-radius: 8px;
}
.input-row .button-row {
display: flex;
flex-direction: row;
align-items: flex-end;
justify-content: space-between;
gap: 12px;
width: 100%;
margin-right: 10px;
margin-bottom: 10px;
}
.input-row .button-row>div {
cursor: pointer;
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
margin-left: 12px;
}
.chat-input textarea {
flex: 1;
resize: none;
padding: 12px 16px;
font-size: 14px;
outline: none;
background: #fff;
transition: all 0.2s;
min-height: 44px;
max-height: 200px;
box-shadow: none;
border: none !important;
box-sizing: border-box
}
.chat-input textarea:focus {
/* border-color: #0057ff; */
/* box-shadow: 0 0 0 2px rgba(0, 87, 255, 0.1); */
}
.chat-input button {
padding: 10px 16px;
height: 44px;
border: none;
border-radius: 8px;
background: #0057ff;
color: #fff;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.chat-input button:hover {
background: #0046cc;
}
/* 文件选择和模型选择样式调整 */
.options-row {
display: flex;
gap: 8px;
align-items: center;
font-size: 13px;
}
select,
input[type="file"] {
font-size: 13px;
padding: 6px 8px;
border: 1px solid #e5e5e5;
border-radius: 6px;
background: #fff;
transition: all 0.2s;
}
select:focus,
input[type="file"]:focus {
border-color: #0057ff;
outline: none;
}
#fileInput {
display: none;
}
.mobilelogo {
display: none;
}
/* 手机端适配 */
@media (max-width: 768px) {
.mobilelogo {
display: flex;
width: 28px;
height: 28px;
margin-right: 10px;
}
.sidebar {
display: none;
}
.chat-container {
width: 100%;
height: 100vh;
}
.chat-input {
/* position: fixed; */
bottom: 0;
left: 0;
right: 0;
z-index: 100;
}
.chat-messages {
padding-bottom: 125px;
/* 留出输入框空间 */
}
.chat-header .back {
display: flex;
justify-content: center;
align-items: center;
margin-right: 20px;
}
}
.fourquestion {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 10px;
margin-bottom: 20px;
}
.fourquestion>div {
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: center;
align-items: center;
gap: 10px;
}
.fourquestion .question {
width: 350px;
display: flex;
justify-content: center;
align-items: center;
font-size: 21px;
color: #999;
border: 1px solid #e5e5e5;
border-radius: 10px;
padding: 24px 10px;
cursor: pointer;
}
.fourquestion .question:hover {
background-color: #e5e5e5;
color: #0057ff;
}
.fourquestion .question img {
width: 35px;
height: 35px;
margin-right: 5px;
}
.sidequestion {
display: flex;
flex-direction: column;
flex-wrap: wrap;
width: 100%;
display: none;
}
.sidequestion>div {
width: 100%;
}
.sidequestion>div .question {
width: 80%;
height: 2px;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
color: #999;
border: 1px solid #e5e5e5;
border-radius: 10px;
padding: 15px -16px !important;
cursor: pointer;
}
</style>
</head>
<body>
<!-- 左侧侧边栏 -->
<div class="sidebar" id="sidebar">
<button class="toggle-btn" id="toggleBtn" onclick="toggleSidebar()">☰</button>
<div class="sidebar-header"><img src="./img/logo.png">会话列表</div>
<div class="sidebar-actions">
<button onclick="newChat()">+ 新建会话</button>
</div>
<div class="chat-list" id="chatList"></div>
<div class="fourquestion sidequestion">
<div>
<div class='question' data-value='分析这个场站' data-pre-value="详细分析这个光伏场站,根据图片给出详细的运维方案"><img src="./img/1.png"
alt="">分析这个场站</div>
<div class='question' data-value='详细的清洗计划' data-pre-value="如果这个光伏场站需要用无人机清洗怎么做,给出一份详细的计划"><img
src="./img/4.png" alt="">详细的清洗计划</div>
</div>
<div>
<div class='question' data-value='详细的巡检计划' data-pre-value="如果这个光伏场站需要巡检怎么做,给出一份详细的计划"><img src="./img/2.png"
alt="">详细的巡检计划</div>
<div class='question' data-value='详细的割草计划' data-pre-value="如果这个光伏场站需要割草怎么做,给出一份详细的计划"><img src="./img/3.png"
alt="">详细的割草计划</div>
</div>
</div>
</div>
<!-- 收起按钮 -->
<!-- 右侧聊天区 -->
<div class="chat-container">
<div class="chat-header">
<!-- <div class='back' onclick="handleBack()">
<svg t="1748925372585" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="4688" width="16" height="16">
<path
d="M395.21518 513.604544l323.135538-312.373427c19.052938-18.416442 19.052938-48.273447 0-66.660212-19.053961-18.416442-49.910737-18.416442-68.964698 0L291.75176 480.290811c-19.052938 18.416442-19.052938 48.273447 0 66.660212l357.633237 345.688183c9.525957 9.207709 22.01234 13.796214 34.497699 13.796214 12.485359 0 24.971741-4.588505 34.466999-13.82896 19.052938-18.416442 19.052938-48.242747 0-66.660212L395.21518 513.604544z"
fill="#999" p-id="4689"></path>
</svg>
</div> -->
<div style="
display: flex;
justify-content: flex-start;
align-items: center;
width: 100%;
">
<img src="./img/logo.png" class="mobilelogo" alt="">
新能源运维智能体<span style="font-size:14px">(MBAgent)</span>
</div>
</div>
<div class="content">
<div id="chatMessages" class="chat-messages"></div>
<div class="fourquestion fourquestionmain">
<div>
<div class='question mainauestion' data-value='分析这个场站' data-pre-value="详细分析这个光伏场站,根据图片给出详细的运维方案"><img
src="./img/1.png" alt="">分析这个场站</div>
<div class='question mainauestion' data-value='详细的清洗计划' data-pre-value="如果这个光伏场站需要用无人机清洗怎么做,给出一份详细的计划"><img
src="./img/4.png" alt="">详细的清洗计划</div>
</div>
<div>
<div class='question mainauestion' data-value='详细的巡检计划' data-pre-value="如果这个光伏场站需要巡检怎么做,给出一份详细的计划"><img
src="./img/2.png" alt="">详细的巡检计划</div>
<div class='question mainauestion' data-value='详细的割草计划' data-pre-value="如果这个光伏场站需要割草怎么做,给出一份详细的计划"><img
src="./img/3.png" alt="">详细的割草计划</div>
</div>
</div>
</div>
<div class="chat-input">
<!-- <div class="options-row">
<select id="mmModel">
<option value="hunyuan-vision">hunyuan-vision</option>
</select>
<select id="llModel">
<option value="hunyuan-api">hunyuan-api</option>
</select>
</div> -->
<div class="input-row">
<div class="img-row none">
<div class="img-box">
<img src="" alt="">
<span class="delete-btn" onclick="deleteImg()"><svg t="1757836617046" class="icon" viewBox="0 0 1024 1024"
version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16945" width="16" height="16">
<path d="M512 512m-512 0a512 512 0 1 0 1024 0 512 512 0 1 0-1024 0Z" fill="#EF4631" p-id="16946">
</path>
<path
d="M733.6 733.6c-16.7 16.7-43.7 16.6-60.2 0.1l-151-151-151 151c-16.6 16.6-43.4 16.6-60.2-0.1-16.7-16.7-16.6-43.7-0.1-60.2l151-151-151-151c-16.6-16.6-16.6-43.4 0.1-60.2 16.7-16.7 43.7-16.6 60.2-0.1l151 151 151-151c16.6-16.6 43.4-16.6 60.2 0.1 16.7 16.7 16.6 43.7 0.1 60.2l-151 151 151 151c16.6 16.6 16.7 43.4-0.1 60.2z"
fill="#FFFFFF" p-id="16947"></path>
</svg></span>
</div>
</div>
<textarea id="chatInput" rows="1" placeholder="询问任何问题..."></textarea>
<div class="button-row">
<!-- <div id="sendFileBtn">
<input type="file" id="fileInput" /><svg t="1757833755414" class="icon" viewBox="0 0 1024 1024"
version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14318" width="24" height="24">
<path
d="M576 928H192c-52.992 0-96-43.093333-96-96V192c0-52.992 43.093333-96 96-96h640c52.992 0 96 43.093333 96 96v384.064c0 17.706667-14.293333 32-32 32s-32-14.293333-32-32V192.021333c0-17.706667-14.4-32-32-32H192c-17.706667 0-32 14.378667-32 32v639.957334c0 17.706667 14.4 32 32 32h384c17.706667 0 32 14.314667 32 32 0 17.706667-14.293333 32.021333-32 32.021333zM128 693.312a32.064 32.064 0 0 1-22.613333-54.698667l159.402666-159.338666a95.786667 95.786667 0 0 1 110.72-17.984l173.589334 86.805333c12.309333 6.186667 27.093333 3.797333 36.8-5.994667l287.402666-287.445333a32.042667 32.042667 0 0 1 45.290667 45.312L631.210667 587.392a95.786667 95.786667 0 0 1-110.72 18.005333l-173.589334-86.826666a31.616 31.616 0 0 0-36.8 6.016l-159.509333 159.317333c-6.186667 6.314667-14.4 9.386667-22.592 9.386667z m320-277.376c-52.906667 0-96-43.093333-96-96s43.093333-96.021333 96-96.021333 96 43.093333 96 96-43.093333 96.021333-96 96.021333z m0-128c-17.6 0-32 14.378667-32 32 0 17.6 14.4 32 32 32s32-14.4 32-32c0-17.621333-14.4-32-32-32zM768 928c-17.706667 0-32-14.293333-32-32v-192.021333c0-17.706667 14.293333-32 32-32s32 14.293333 32 32v192c0 17.706667-14.293333 32.021333-32 32.021333z m128-128c-7.893333 0-15.701333-2.922667-21.909333-8.725333L768 691.669333l-106.090667 99.712a31.936 31.936 0 0 1-45.226666-1.408 31.957333 31.957333 0 0 1 1.408-45.205333l112.213333-105.386667A48.554667 48.554667 0 0 1 768 621.44c14.72 0 28.501333 6.613333 37.696 17.92l112.213333 105.386667A31.957333 31.957333 0 0 1 896 800z"
fill="#666666" p-id="14319"></path>
</svg>
</div> -->
<div id="sendFileBtn" style="position: relative; width: 40px; height: 40px; cursor: pointer;">
<input type="file" id="fileInput" style="position: absolute;
left: 0; top: 0; width: 100%; height: 100%;
opacity: 0; cursor: pointer;" capture="camera" />
<img src="./img/link.jpg" alt="" style="width:24px;height:24px">
<path
d="M576 928H192c-52.992 0-96-43.093333-96-96V192c0-52.992 43.093333-96 96-96h640c52.992 0 96 43.093333 96 96v384.064c0 17.706667-14.293333 32-32 32s-32-14.293333-32-32V192.021333c0-17.706667-14.4-32-32-32H192c-17.706667 0-32 14.378667-32 32v639.957334c0 17.706667 14.4 32 32 32h384c17.706667 0 32 14.314667 32 32 0 17.706667-14.293333 32.021333-32 32.021333zM128 693.312a32.064 32.064 0 0 1-22.613333-54.698667l159.402666-159.338666a95.786667 95.786667 0 0 1 110.72-17.984l173.589334 86.805333c12.309333 6.186667 27.093333 3.797333 36.8-5.994667l287.402666-287.445333a32.042667 32.042667 0 0 1 45.290667 45.312L631.210667 587.392a95.786667 95.786667 0 0 1-110.72 18.005333l-173.589334-86.826666a31.616 31.616 0 0 0-36.8 6.016l-159.509333 159.317333c-6.186667 6.314667-14.4 9.386667-22.592 9.386667z m320-277.376c-52.906667 0-96-43.093333-96-96s43.093333-96.021333 96-96.021333 96 43.093333 96 96-43.093333 96.021333-96 96.021333z m0-128c-17.6 0-32 14.378667-32 32 0 17.6 14.4 32 32 32s32-14.4 32-32c0-17.621333-14.4-32-32-32zM768 928c-17.706667 0-32-14.293333-32-32v-192.021333c0-17.706667 14.293333-32 32-32s32 14.293333 32 32v192c0 17.706667-14.293333 32.021333-32 32.021333z m128-128c-7.893333 0-15.701333-2.922667-21.909333-8.725333L768 691.669333l-106.090667 99.712a31.936 31.936 0 0 1-45.226666-1.408 31.957333 31.957333 0 0 1 1.408-45.205333l112.213333-105.386667A48.554667 48.554667 0 0 1 768 621.44c14.72 0 28.501333 6.613333 37.696 17.92l112.213333 105.386667A31.957333 31.957333 0 0 1 896 800z"
fill="#666666" p-id="14319"></path>
</svg>
</div>
<div id="sendBtn">
<img src="./img/upload.png" alt="" style="width:36px;height:36px">
</div>
</div>
</div>
</div>
</div>
</div>
<script src="./marked.min.js"></script>
<script>
const sidebar = document.getElementById("sidebar");
const chatMessages = document.getElementById("chatMessages");
const chatInput = document.getElementById("chatInput");
const fileInput = document.getElementById("fileInput");
const chatList = document.getElementById("chatList");
let sessIdList = [];
let isUpload = {};
let isPre = false;//是否点击了预设问题
// let sessIdList = JSON.parse(localStorage.getItem("sessId")) || [];
let sessId;
let activeChatId = '';
const params = new URLSearchParams(window.location.search);
console.log(params.get("userId"), 644);
const encodedUserId = params.get("userId");
// ✅ 先 URL 解码,再 Base64 解码,再 UTF-8 解码
const userId = decodeURIComponent(Number(escape(atob(encodedUserId))));
// const userId = encodedUserId;
let sendParam = {
imageBase64: '',
userId: userId,
sessionId: '',
userPrompt: ''
}
const questions = document.querySelectorAll(".question");
questions.forEach(q => {
q.addEventListener("click", () => {
if (q.classList.contains("mainauestion")) {
// q.style.display = 'none';
if (!isMobile()) {
document.querySelector('.fourquestionmain').style.display = 'none';
document.querySelector('.sidequestion').style.display = 'flex';
}
}
isPre = true;
console.log(isUpload, 88)
if (!sendParam.imageBase64 && !fileInput.files.length) {
if (!isUpload[activeChatId]) {
alert("请先上传图片");
return;
}
}
const value = q.getAttribute("data-value");
const preValue = q.getAttribute("data-pre-value");
sendParam.userPrompt = preValue;
chatInput.value = value; // ✅ 把预设问题填入输入框
chatInput.focus(); // 可选:让光标聚焦输入框
sendMessage(true);
});
});
function isMobile() {
return /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
// getHistory();
function getCookie(name) {
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
if (match) return decodeURIComponent(match[2]);
return null;
}
function handleBack() {
RPJavaFragmentInterface.navigateTo("StatusFragment");
}
function generateUniqueId16() {
// 当前时间戳(毫秒,取后几位)
const timestamp = Date.now().toString().slice(-10);
// 生成 6 位随机数
const random = Math.floor(Math.random() * 1e6).toString().padStart(6, "0");
return timestamp + random; // 一共 16 位
}
function createSession(userId, sessionId) {
try {
fetch("http://1.95.137.212:30009/session", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
userId,
sessionId,
})
}).then(res => res.json()).then(data => {
if (data.code === 0) {
console.log("会话创建成功:", data.message);
} else {
console.error("创建失败:", data.error);
}
})
} catch (err) {
console.error("请求异常:", err);
}
}
function toggleSidebar() {
sidebar.classList.toggle("collapsed");
}
function renderChatList(isNew) {
console.log(sessIdList);
chatList.innerHTML = "";
if (!sessIdList || sessIdList.length == 0) {
chatMessages.innerHTML = "";
return;
}
// if (isNew == "new") {
activeChatId = sessIdList[sessIdList.length - 1];
// } else {
// activeChatId = sessIdList[0];
// }
sendParam.sessionId = activeChatId;
getHistory();
sessIdList.forEach((item, index) => {
console.log(item, activeChatId);
const item1 = document.createElement("div");
item1.setAttribute("data-sessionid", item);
item1.className = "chat-item" + (activeChatId == item ? " active" : "");
const title = document.createElement("div");
title.className = "chat-title";
title.textContent = `新对话${index + 1}`;
item1.onclick = () => switchChat(item);
const del = document.createElement("span");
del.className = "delete-btn";
del.textContent = "✕";
del.onclick = (e) => {
e.stopPropagation();
deleteChat(item);
};
item1.appendChild(title);
item1.appendChild(del);
chatList.appendChild(item1);
})
}
// bool 是否新创建的
function renderMessages(data, bool) {
chatMessages.innerHTML = "";
if (bool || !data || !data.length) {
return;
}
data.forEach(msg => {
addMessage(msg.content, msg.role, msg.image_base64, msg.created_at);
});
}
// 内容 角色 图片 创造时间
function addMessage(content = '', role = "assistant", imgUrl = null, time = null) {
const msg = document.createElement("div");
msg.className = `message ${role}`;
if (time) {
const timeNode = document.createElement("div");
timeNode.textContent = time;
timeNode.style.fontSize = "12px";
timeNode.style.color = "#999";
timeNode.style.marginTop = "4px";
msg.appendChild(timeNode);
}
// 如果有文字内容,先创建文字节点
if (content) {
const textNode = document.createElement("div");
textNode.innerHTML = marked.parse(content);
msg.appendChild(textNode);
}
// 如果有图片,创建图片节点
if (imgUrl) {
const img = document.createElement("img");
img.src = imgUrl;
img.style.marginTop = content ? "8px" : "0"; // 如果有文字,图片加一点间距
msg.appendChild(img);
}
// 如果有时间,可以显示
chatMessages.appendChild(msg);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
function sendMessage(isPre) {
console.log("发送消息");
if (!activeChatId) {
newChat();
}
// if (sessIdList.length == 0) {
// return;
// }
ifSessionId = true;
const text = chatInput.value.trim();
if (!text && !fileInput.files.length && !sendParam.imageBase64) return;
if (text) addMessage(text, "user");
let imgUrl = '';
if (sendParam.imageBase64) {
imgUrl = sendParam.imageBase64;
if (fileInput.files[0]) {
const file = fileInput.files[0];
const url = URL.createObjectURL(file);
addMessage('', "user", url);
} else {
addMessage('', "user", sendParam.imageBase64);
sendParam.imageBase64 = '';
}
fileInput.value = "";
}
if (!isPre) {
sendParam.userPrompt = text;
} else {
}
chatInput.value = "";
getAiResponse(imgUrl);
}
const getAiResponse = async (img) => {
deleteImg();
const aiMsg = { role: "assistant", content: "", isImage: false };
const msgDiv = document.createElement("div");
msgDiv.className = "message assistant";
msgDiv.textContent = '思考中';
let dots = 0;
const interval = setInterval(() => {
dots = (dots + 1) % 4; // 0,1,2,3
msgDiv.textContent = '思考中' + '.'.repeat(dots);
}, 500);
msgDiv.className = "message assistant thinking ";
chatMessages.appendChild(msgDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
sessId = sendParam.sessionId;
sendParam.imageBase64 = img;
// sendParam.userId= encodedUserId;
sendParam.userId = decodeURIComponent(Number(escape(atob(encodedUserId))));
const res = await fetch("http://1.95.137.212:30009/analyze", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(sendParam),
});
sendParam.imageBase64 = '';
const reader = res.body.getReader();
const decoder = new TextDecoder("utf-8");
let buffer = "";
msgDiv.classList.remove("thinking");
clearInterval(interval);
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
let lines = buffer.split("\n");
buffer = lines.pop();
for (let line of lines) {
line = line.trim();
if (!line.startsWith("data:")) continue;
const data = line.slice(5).trim();
if (data === "[DONE]") {
// 更新聊天滚动条
chatMessages.scrollTop = chatMessages.scrollHeight;
return;
}
try {
const obj = JSON.parse(data);
if (obj.content) {
// 每次追加内容到 AI 消息
aiMsg.content += obj.content;
msgDiv.innerHTML = marked.parse(aiMsg.content);
// msgDiv.textContent = aiMsg.content;
chatMessages.scrollTop = chatMessages.scrollHeight;
}
} catch (err) {
console.error("JSON parse error", data);
}
}
}
};
function newChat() {
const id = generateUniqueId16();
sessIdList.push(id);
createSession(userId, id)
const chat = [{
id,
title: "新对话 " + (sessIdList.length),
messages: { role: "assistant", content: "👋 新会话已创建,请输入问题。", }
}];
activeChatId = id;
ifSessionId = true;
sessId = id;
sendParam.sessionId = sessId;
renderMessages([chat[0].messages], true);
renderChatList('new');
}
function switchChat(id) {
activeChatId = id;
sendParam.sessionId = id;
const items = document.querySelectorAll(".chat-item");
items.forEach(item => {
if (item.dataset.sessionid === id) {
item.classList.add("active");
} else {
item.classList.remove("active");
}
});
getHistory();
// renderChatList();
}
const deleteSession = (sessionId, userId) => {
try {
fetch(`http://1.95.137.212:30009/session?sessionId=${sessionId}&userId=${userId}`, {
method: "DELETE"
}).then(res => res.json()).then(data => {
if (data.code === 0) {
getSessionList();
// renderChatList();
console.log("删除成功:", data.message);
} else {
console.error("删除失败:", data.error);
}
})
} catch (err) {
console.error("请求异常:", err);
}
}
function deleteChat(id) {
// sessIdList = sessIdList.filter(item => item !== id);
// localStorage.setItem("sessId", JSON.stringify(sessIdList));
deleteSession(id, sendParam.userId)
}
// 初始化
// if (chats.length === 0) {
// newChat();
// } else {
// if (!activeChatId || !chats.find(c => c.id === activeChatId)) {
// activeChatId = chats[0].id;
// }
// renderChatList();
// renderMessages();
// }
function handleSendFile() {
console.log("选择文件");
// 触发隐藏的 input 点击
document.getElementById("fileInput").click();
}
// 监听文件选择
document.getElementById("fileInput").addEventListener("change", function (e) {
const file = e.target.files[0];
if (file) {
console.log("选择的文件:", file.name);
// 这里可以直接上传
uploadFile(file);
}
});
// 示例上传函数(fetch 到后端接口)
function uploadFile(file) {
if (file) {
console.log("上传文件:", file.name, URL.createObjectURL(file));
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = async function () {
console.log(reader.result)
sendParam.imageBase64 = reader.result;
document.querySelector(".img-row").classList.remove("none");
document.querySelector(".img-row img").src = sendParam.imageBase64;
isUpload[activeChatId] = true;
}
// document.getElementById("rawImg").src = URL.createObjectURL(file);
}
}
function deleteImg() {
document.querySelector(".img-row").classList.add("none");
sendParam.imageBase64 = '';
document.querySelector(".img-row img").src = '';
}
chatInput.addEventListener("keydown", function (e) {
if (e.key === "Enter" && !e.shiftKey) { // 按下回车且未按Shift
e.preventDefault(); // 阻止换行
sendMessage(); // 调用发送函数
}
});
document.getElementById("sendBtn").addEventListener("click", function () {
sendMessage();
});
document.getElementById("sendFileBtn").addEventListener("click", function () {
handleSendFile()
});
// getHistory();
// renderChatList("new");
// sendParam.sessionId = "7906661519458367";
// 调用获取历史记录的接口
function getHistory() {
// sendParam.userId = encodedUserId;
fetch(`http://1.95.137.212:30009/history?sessionId=${sendParam.sessionId}`, {
// fetch(`http://1.95.137.212:30008/history?sessionId=${sendParam.sessionId}`, {
method: "GET",
// headers: { "Content-Type": "application/json" },
// credentials: 'include', // 发送 cookie
// body: JSON.stringify(sendParam),
}).then(res => res.json()).then(data => {
// chats = data.messages;
// renderChatList();
renderMessages(data.messages)
})
}
function getCookie(name) {
console.log(document.cookie, 8777222)
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
return null;
}
getSessionList();
function getSessionList() {
sendParam.userId = userId;
fetch(`http://1.95.137.212:30009/sessions?userId=${sendParam.userId}`, {
// fetch(`http://1.95.137.212:30008/history?sessionId=${sendParam.sessionId}`, {
method: "GET",
// headers: { "Content-Type": "application/json" },
// credentials: 'include', // 发送 cookie
// body: JSON.stringify(sendParam),
}).then(res => res.json()).then(data => {
console.log(data, 8777)
if (data.code == 300) {
// window.location.href='http://localhost/#/login?goRemote=1 '
window.location.href = data.error;
return;
}
sessIdList = data.sessions;
renderChatList();
console.log(data, 8777)
// chats = data.messages;
// renderChatList();
// renderMessages(data.messages)
})
}
function toggleSidebar() {
sidebar.classList.toggle("collapsed");
const toggleBtn = document.getElementById("toggleBtn");
if (sidebar.classList.contains("collapsed")) {
toggleBtn.style.left = "10px"; // 收起后
} else {
toggleBtn.style.left = "190px"; // 展开后
}
}
</script>
</body>
</html>