Files
k3GPT/main/x/kagent.html
2025-11-19 19:42:49 +08:00

744 lines
35 KiB
HTML
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>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>知识体</title>
<link rel="stylesheet" href="tw.k3gpt.css">
<script src="js/axios.min.js"></script>
<script src="js/marked.js"></script>
<script defer src="js/alpine.js"></script>
<script defer src="js/alp_unit.js"></script>
<style>
/* WebKit 浏览器的滚动条 */
::-webkit-scrollbar {
width: 4px; /* 滚动条宽度 */
height: 10px;
}
::-webkit-scrollbar-track {
background: #f1f1f1; /* 轨道背景色 */
}
::-webkit-scrollbar-thumb {
background: #888; /* 滚动条颜色 */
border-radius: 4px; /* 圆角 */
}
::-webkit-scrollbar-thumb:hover {
background: #555; /* 滚动条悬停时的颜色 */
}
/*WangEdit的样式*/
#editor-content-view {
border: 3px solid #ccc;
border-radius: 5px;
padding: 0 10px;
margin-top: 20px;
overflow-x: auto;
}
#editor-content-view p,
#editor-content-view li {
white-space: pre-wrap; /* 保留空格 */
}
#editor-content-view blockquote {
border-left: 8px solid #d0e5f2;
padding: 10px 10px;
margin: 10px 0;
background-color: #f1f1f1;
}
#editor-content-view code {
font-family: monospace;
background-color: #eee;
padding: 3px;
border-radius: 3px;
}
#editor-content-view pre>code {
display: block;
padding: 10px;
}
#editor-content-view table {
border-collapse: collapse;
}
#editor-content-view td,
#editor-content-view th {
border: 1px solid #ccc;
min-width: 50px;
height: 20px;
}
#editor-content-view th {
background-color: #f1f1f1;
}
#editor-content-view ul,
#editor-content-view ol {
padding-left: 20px;
}
#editor-content-view input[type="checkbox"] {
margin-right: 5px;
}
.button {
display: inline-block;
padding: 10px 15px;
margin-bottom: 10px;
margin-left: 10px;
border: none;
border-radius: 5px;
color: white;
font-size: 14px;
transition: background-color 0.3s;
width: calc(100% - 22px); /* 调整按钮宽度以适应边距 */
}
.logout-btn {
border-radius: 5px;
cursor: pointer;
padding: 5px;
width: 80px;
height: 30px;
background-color: #4f46e5;
}
.logout-btn:hover {
background-color: #0c8ab8;
}
#head {
width: 50px; /* 设置图片宽度 */
height: 50px; /* 设置图片高度 */
border-radius: 50%; /*将圆角半径设置为 50% */
/*border-radius: 5px; 圆角 */
object-fit: cover; /* 确保图片按比例缩放并覆盖容器 */
display: block;
margin: auto;
}
.fixed-top-right {
position: fixed; /* 固定定位 */
top: 20px; /* 距离顶部 10px */
right: 30px; /* 距离右侧 10px */
background-color: #9333ea; /* 背景颜色 */
color: white; /* 文字颜色 */
padding: 10px; /* 内边距 */
border-radius: 5px; /* 圆角 */
min-width: 85px;
z-index: 1000; /* 确保层级高于其他内容 */
text-align: center;
}
.content-layer {
position: relative; /* 必须设置,否则 absolute 不生效 */
border: 1px solid #ddd;
color: white;
background-color: rgb(29 78 216 / 0.9);
border-radius: 5px;
margin-bottom: 5px;
padding:5px;
}
.floating-info-btn {
position: absolute;
bottom: 5px;
right: 5px;
width: 30px;
height: 30px;
padding-left: 5px;
border-radius: 50%;
background-color: #9333ea;
color: white;
border: none;
font-size: 18px;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
opacity: 0.9;
transition: all 0.3s ease;
z-index: 25;
}
.floating-info-btn:hover {
transform: scale(1.1);
background-color: #0000ff;
}
/* 表格基础样式 */
table {
width: 100%; /* 让表格占据其容器的全宽 */
border-collapse: collapse; /* 合并边框 */
margin: 20px 0; /* 上下外边距 */
background-color: white; /* 灰*/
}
/* 表头样式 */
th {
background-color: #808080; /* 灰*/
color: white; /* 白色文字 */
font-weight: bold; /* 加粗字体 */
text-align: left; /* 左对齐文本 */
padding: 8px; /* 内边距 */
border: 1px solid #ddd; /* 边框 */
}
/* 标准单元格样式 */
td {
border: 1px solid #ddd; /* 边框 */
padding: 8px; /* 内边距 */
text-align: left; /* 左对齐文本 */
color: #828282; /* 灰色文字 */
}
/* 隔行变色 */
tr:nth-child(even) {
background-color: #f2f2f2;
}
/* 鼠标悬停时高亮行 */
tr:hover {
background-color: #ddd;
}
</style>
</head>
<body>
<!-- 用于绘制的隐藏画布 -->
<canvas id="canvas" width="100" height="100" style="display: none;"></canvas>
<div id="floating-layer" class="fixed-top-right">
<img id="head"></img>
<div id="username"></div>
</div>
<!-- 对话窗口-->
<div x-data="chat_agent" class="flex w-98 m-2 rounded-lg border shadow-lg">
<!-- 左边的 div -->
<div class="w-1/5 text-white bg-gradient-to-t from-blue-600 to-purple-600 flex flex-col h-[98vh] rounded-lg">
<h3 x-text="kagent.title" class="text-lg font-bold text-center p-2 mt-2"></h3>
<div class="flex-grow overflow-hidden overflow-y-auto p-2">
<div x-html="kagent.demo" class="font-bold text-sm mx-2"></div>
<ul>
<template x-for="(qitem, index) in qItems" :key="index">
<li class="ml-4 text-sm text-white my-2">
<span x-text="index+1"></span>.&nbsp;&nbsp;<a x-text="qitem.req" :href="'#QA'+index"></a>
</li>
</template>
</ul>
</div>
<div class="button">
<button class="logout-btn" @click="new_chat">
新建对话</button>
<button class="logout-btn" @click="logout">
退出系统</button>
</div>
</div>
<!-- 右边的 div -->
<div class="w-4/5 bg-zinc-50">
<div class="flex flex-col h-[98vh]">
<!-- 对话的展示-->
<div id="chat_display_2" class="flex-grow overflow-hidden overflow-y-auto">
<!-- 对话列表-->
<div class="px-4">
<div class="flex flex-col gap-2 mx-4">
<!-- AI's Response -->
<div class="w-full mt-2 max-w-2xl border-zinc-300 bg-zinc-100 p-2 text-left dark:border-zinc-700 dark:bg-zinc-800 rounded-xl border shadow-sm" >
<div class="flex items-center gap-2 text-neutral-900 dark:text-zinc-50">
<span class="flex size-8 items-center justify-center rounded-full bg-gradient-to-br from-blue-600 to-purple-600 text-white dark:bg-sky-600 dark:text-white">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" class="size-5">
<path d="M6 12.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M3 8.062C3 6.76 4.235 5.765 5.53 5.886a26.6 26.6 0 0 0 4.94 0C11.765 5.765 13 6.76 13 8.062v1.157a.93.93 0 0 1-.765.935c-.845.147-2.34.346-4.235.346s-3.39-.2-4.235-.346A.93.93 0 0 1 3 9.219zm4.542-.827a.25.25 0 0 0-.217.068l-.92.9a25 25 0 0 1-1.871-.183.25.25 0 0 0-.068.495c.55.076 1.232.149 2.02.193a.25.25 0 0 0 .189-.071l.754-.736.847 1.71a.25.25 0 0 0 .404.062l.932-.97a25 25 0 0 0 1.922-.188.25.25 0 0 0-.068-.495c-.538.074-1.207.145-1.98.189a.25.25 0 0 0-.166.076l-.754.785-.842-1.7a.25.25 0 0 0-.182-.135" />
<path d="M8.5 1.866a1 1 0 1 0-1 0V3h-2A4.5 4.5 0 0 0 1 7.5V8a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1v1a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v-.5A4.5 4.5 0 0 0 10.5 3h-2zM14 7.5V13a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5A3.5 3.5 0 0 1 5.5 4h5A3.5 3.5 0 0 1 14 7.5" />
</svg>
</span>
<span class="text-sm font-bold" x-text="hello">Hello.AI</span>
</div>
<p x-html="marked.parse(kagent.guide)" class="text-pretty sm:pl-10 mt-4 sm:mt-0 text-sm text-neutral-600 dark:text-zinc-200">
你好,我是一个知识机器人,我可以帮你检索知识库并回答你的问题。
请问我有什么可以帮助你的?
</p>
</div>
<template x-for="(item, index) in Items" :key="index">
<div class="flex flex-col justify-end">
<!-- User's Chat -->
<div :id="'QA'+index" class="inline-flex max-w-xl ml-auto border-zinc-300 bg-blue-700/30 p-2 text-left dark:border-zinc-700 dark:bg-zinc-800 rounded-lg border shadow-sm" >
<div class="flex items-center px-1 gap-1 text-neutral-900 dark:text-zinc-50 group cursor-pointer">
<span x-text="item.req" class="text-sm">Alice Brown</span>
<button x-on:click="copy_query(item.req)" class="rounded-full text-neutral-600/75 opacity-0 group-hover:opacity-100 group-focus:opacity-100 hover:bg-zinc-900/10 hover:text-neutral-600 focus:outline-none focus-visible:text-neutral-600 focus-visible:zinc-300 focus-visible:outline-offset-0 focus-visible:outline-sky-700 active:bg-zinc-900/5 active:-outline-offset-2 dark:text-zinc-200/75 dark:hover:bg-zinc-50/10 dark:hover:text-zinc-200 dark:focus-visible:text-zinc-200 dark:focus-visible:outline-sky-600 dark:active:bg-zinc-50/5" title="再问一次" aria-label="再问一次" >
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-4" aria-hidden="true">
<path fill-rule="evenodd" d="M13.887 3.182c.396.037.79.08 1.183.128C16.194 3.45 17 4.414 17 5.517V16.75A2.25 2.25 0 0 1 14.75 19h-9.5A2.25 2.25 0 0 1 3 16.75V5.517c0-1.103.806-2.068 1.93-2.207.393-.048.787-.09 1.183-.128A3.001 3.001 0 0 1 9 1h2c1.373 0 2.531.923 2.887 2.182ZM7.5 4A1.5 1.5 0 0 1 9 2.5h2A1.5 1.5 0 0 1 12.5 4v.5h-5V4Z" clip-rule="evenodd"/>
</svg>
</button>
</div>
</div>
<!-- AI's Response -->
<div x-show="item.rsp_show" class="mt-2 w-full border-zinc-300 bg-zinc-100 p-2 text-left dark:border-zinc-700 dark:bg-zinc-800 rounded-xl border shadow-sm" >
<div class="flex items-center gap-2 text-neutral-900 dark:text-zinc-50">
<span class="flex size-8 items-center justify-center rounded-full bg-gradient-to-br from-blue-600 to-purple-600 text-white dark:bg-sky-600 dark:text-white">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" class="size-5">
<path d="M6 12.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M3 8.062C3 6.76 4.235 5.765 5.53 5.886a26.6 26.6 0 0 0 4.94 0C11.765 5.765 13 6.76 13 8.062v1.157a.93.93 0 0 1-.765.935c-.845.147-2.34.346-4.235.346s-3.39-.2-4.235-.346A.93.93 0 0 1 3 9.219zm4.542-.827a.25.25 0 0 0-.217.068l-.92.9a25 25 0 0 1-1.871-.183.25.25 0 0 0-.068.495c.55.076 1.232.149 2.02.193a.25.25 0 0 0 .189-.071l.754-.736.847 1.71a.25.25 0 0 0 .404.062l.932-.97a25 25 0 0 0 1.922-.188.25.25 0 0 0-.068-.495c-.538.074-1.207.145-1.98.189a.25.25 0 0 0-.166.076l-.754.785-.842-1.7a.25.25 0 0 0-.182-.135" />
<path d="M8.5 1.866a1 1 0 1 0-1 0V3h-2A4.5 4.5 0 0 0 1 7.5V8a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1v1a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v-.5A4.5 4.5 0 0 0 10.5 3h-2zM14 7.5V13a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5A3.5 3.5 0 0 1 5.5 4h5A3.5 3.5 0 0 1 14 7.5" />
</svg>
</span>
<span x-text="item.rsp" style="overflow-wrap: break-word;" class="text-sm font-bold">Pengu AI</span>
</div>
<p x-bind:id="item.chat_id" :x-ref="item.chat_id" class="text-pretty sm:pl-10 mt-4 sm:mt-0 text-sm text-neutral-600 dark:text-zinc-200"></p>
</div>
</div>
</template>
</ul>
</div>
</div>
</div>
<!-- 输入框 -->
<div class="flex w-full flex-col gap-2 m-2 px-4">
<div class="relative w-full">
<label for="aiPromt" for="aiPromt" class="sr-only">ai prompt</label>
<svg x-show="kagent.atype!=2 && kagent.atype!=3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" aria-hidden="true" class="absolute left-3 top-1/2 size-4 -translate-y-1/2 fill-blue-700 dark:fill-blue-700">
<path fill-rule="evenodd" d="M5 4a.75.75 0 0 1 .738.616l.252 1.388A1.25 1.25 0 0 0 6.996 7.01l1.388.252a.75.75 0 0 1 0 1.476l-1.388.252A1.25 1.25 0 0 0 5.99 9.996l-.252 1.388a.75.75 0 0 1-1.476 0L4.01 9.996A1.25 1.25 0 0 0 3.004 8.99l-1.388-.252a.75.75 0 0 1 0-1.476l1.388-.252A1.25 1.25 0 0 0 4.01 6.004l.252-1.388A.75.75 0 0 1 5 4ZM12 1a.75.75 0 0 1 .721.544l.195.682c.118.415.443.74.858.858l.682.195a.75.75 0 0 1 0 1.442l-.682.195a1.25 1.25 0 0 0-.858.858l-.195.682a.75.75 0 0 1-1.442 0l-.195-.682a1.25 1.25 0 0 0-.858-.858l-.682-.195a.75.75 0 0 1 0-1.442l.682-.195a1.25 1.25 0 0 0 .858-.858l.195-.682A.75.75 0 0 1 12 1ZM10 11a.75.75 0 0 1 .728.568.968.968 0 0 0 .704.704.75.75 0 0 1 0 1.456.968.968 0 0 0-.704.704.75.75 0 0 1-1.456 0 .968.968 0 0 0-.704-.704.75.75 0 0 1 0-1.456.968.968 0 0 0 .704-.704A.75.75 0 0 1 10 11Z" clip-rule="evenodd" />
</svg>
<!-- 文件上传-->
<input x-ref="fileInput" @input="upload_file" type="file" multiple hidden class="w-full overflow-clip rounded border border-red-700 bg-zinc-100/50 text-sm text-red-700 file:mr-4 file:cursor-pointer file:border-none file:bg-zinc-100 file:px-4 file:py-2 file:font-medium file:text-neutral-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 disabled:cursor-not-allowed disabled:opacity-75 dark:bg-zinc-800/50 dark:file:bg-zinc-800 dark:file:text-zinc-50 dark:focus-visible:outline-sky-600" />
<svg x-show="kagent.atype==2 || kagent.atype==3" @click="$refs.fileInput.click()" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" class="absolute left-3 top-1/2 size-6 cursor-pointer -translate-y-1/2 fill-blue-700 dark:fill-sky-600">
<path fill-rule="evenodd" d="M10.5 3.75a6 6 0 0 0-5.98 6.496A5.25 5.25 0 0 0 6.75 20.25H18a4.5 4.5 0 0 0 2.206-8.423 3.75 3.75 0 0 0-4.133-4.303A6.001 6.001 0 0 0 10.5 3.75Zm2.03 5.47a.75.75 0 0 0-1.06 0l-3 3a.75.75 0 1 0 1.06 1.06l1.72-1.72v4.94a.75.75 0 0 0 1.5 0v-4.94l1.72 1.72a.75.75 0 1 0 1.06-1.06l-3-3Z" clip-rule="evenodd"/>
</svg>
<input x-show="one" x-model="query" x-on:input.debounce.100ms="query_len" x-on:keydown.enter="gen" type="text" class="w-full border-outline bg-zinc-100 border border-zinc-300 rounded px-2 py-2 pl-10 pr-24 text-sm text-neutral-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-700 disabled:cursor-not-allowed disabled:opacity-75 dark:border-zinc-700 dark:bg-zinc-800/50 dark:text-zinc-200 dark:focus-visible:outline-sky-600" value="" name="prompt" placeholder="Ask AI ..." />
<textarea id="aiPromt" x-show="!one" rows="5" x-model="query" x-on:input.debounce.100ms="query_len" type="text" class="w-full border-outline bg-zinc-100 border border-zinc-300 rounded px-2 py-2 pl-10 pr-24 text-sm text-neutral-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-700 disabled:cursor-not-allowed disabled:opacity-75 dark:border-zinc-700 dark:bg-zinc-800/50 dark:text-zinc-200 dark:focus-visible:outline-sky-600" value="" name="prompt" placeholder="Ask AI ..." ></textarea>
<button type="button" x-show="is_gen" x-on:click="gen" class="absolute right-3 top-1/2 -translate-y-1/2 cursor-pointer bg-blue-700 rounded px-2 py-1 text-xs tracking-wide text-white transition hover:opacity-75 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 active:opacity-100 active:outline-offset-0 dark:bg-sky-600 dark:text-white dark:focus-visible:outline-sky-600">提交</button>
<button type="button" x-show="!is_gen" x-on:click="stop" class="absolute right-3 top-1/2 -translate-y-1/2 cursor-pointer bg-blue-700 rounded px-2 py-1 text-xs tracking-wide text-white transition hover:opacity-75 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 active:opacity-100 active:outline-offset-0 dark:bg-sky-600 dark:text-white dark:focus-visible:outline-sky-600">停止</button>
</div>
</div>
<div class="text-pretty sm:pl-10 text-center sm:mt-0 text-sm text-neutral-500">以上内容由人工智能合成,服务编码<span x-text="kagent.aicode"></span>,如有问题请到<span x-text="kagent.url"></span>反馈或举报!</div>
</div>
</div>
</div>
<script>
const url = new URL(window.location.href);
var Agent_Kasn = url.searchParams.get('sn');
function chat_agent(){
return {
query:"",
HisOpen: false, //历史对话框
AIEditorOpen: false, // 百科创作对话框
aieditor_id:"", //百科id
eventSource: "", //对话流
hello:"Hello ", //欢迎用户,
is_gen: true, //生成模式
Info: false, // 显示上下文
fItems:[], //上下文信息
qItems:[],// 问题列表
Items:[], //对话全部内容
one:true, //一行文本输入
baike_id:0, //新增词条
ai_file:{}, // 默认的上传文件
kagent:{guide:""}, // 具体配置
Agent_Kasn:Agent_Kasn,
init(){
axios.get('/api/kagent_cfg/'+this.Agent_Kasn).then(response => {
this.kagent = response.data;
this.hello += this.kagent.username;
var username = this.kagent.username;
document.getElementById("username").innerText=username;
document.title= this.kagent.title;
if (getCookie("headimgurl")){
//图片
document.getElementById("head").src=getCookie("headimgurl").slice(1,-1);
}else{
draw_icon(username.slice(0,1));
}
}).catch(error => {
console.error('Error fetching data:', error);
throw error; // 重新抛出错误以便调用者可以处理
});
var doc = localStorage.getItem(this.Agent_Kasn);
if (doc){
this.ai_file = JSON.parse(doc);
this.push_item({chat_id:"",req:"你之前上传了文件: "+this.ai_file.name+", 可以继续对话",rsp:"",rsp_show:false});
}
},
new_chat(){
this.ai_file={};
this.qItems=[];
this.Items=[];
},
logout(){
axios.get('/api/logout?sn='+document.title).then(response => {
if (response.data.code==200){
window.location.href = "login.html?sn="+this.Agent_Kasn;
}
}).catch(error => {
console.error('Error fetching data:', error);
throw error; // 重新抛出错误以便调用者可以处理
});
},
push_item(item){
this.Items.push(item);
},
push_qitem(item){
this.qItems.push(item);
},
update_last_item(chat_id,title,ctx){
var last = this.Items[this.Items.length-1];
last.chat_id=chat_id;
last.rsp=title;
last.rsp_show=true;
last.ctx=ctx;
},
update_last_qitem(answer){
//更新最后一个问题的答案
var last = this.qItems[this.qItems.length-1];
last.rsp=answer;
},
query_len(){
if (this.query.length >45){
this.one=false;
if (this.query.length >300){
const aiPromt = document.getElementById("aiPromt");
aiPromt.rows="10";
}
if (this.query.length >1000){
const aiPromt = document.getElementById("aiPromt");
aiPromt.rows="15";
}
}
},
new0(){ //新建对话
this.qItems=[];
this.Items=[];
this.is_gen = true;
this.one = true;
},
copy_query(query){ //复制查询
this.query = query;
this.query_len()
},
copy_that(chat_id){
const messagesDiv = document.getElementById(chat_id);
let rsp = messagesDiv.innerHTML;
rsp = rsp.replace("<think>","<p>").replace("</think>","</p>");
copyToClipboard(rsp);
},
stop(){
this.eventSource.close();
this.is_gen = true;
this.one=true;
},
gen(){
this.is_gen = false;
this.push_item({chat_id:"",req:this.query,rsp:"",rsp_show:false});
var query=this.query;
if (this.query.length >100){
query = this.query.slice(0,100)+"...";
}
// 其它类型
var newItem = {
question: this.query,
kagent_sn: this.Agent_Kasn,
history: this.qItems.map(item => ({req:item.req,rsp:item.rsp})),
ai_mode: "",
};
//上传文件处理类型
if (this.kagent.atype==2 || this.kagent.atype==3 ){
newItem = {
question: this.query,
kagent_sn: this.Agent_Kasn,
file: this.ai_file.file,
base: this.ai_file.base,
path: this.ai_file.path,
history: this.qItems.map(item => ({req:item.req,rsp:item.rsp})),
ai_mode: "",
};
}
// 使用 axios 发送 POST 请求
axios.post('/api/chat_kagent', newItem)
.then(response =>{
rsp = response.data;
if (this.kagent.atype==3 || this.kagent.atype==100)
{
this.update_last_item(rsp.chat_id,`正在思考中...`,rsp.ctx);
}else{
this.update_last_item(rsp.chat_id,`搜索到${rsp.count}个相关信息,正在整理中...`,rsp.ctx);
}
this.push_qitem({chat_id:rsp.chat_id,req:query,rsp:""});
// 创建一个EventSource实例连接到服务器发送的事件流
this.eventSource = new EventSource('/api/chat_kagent/'+rsp.chat_id);
update_chat(this.eventSource,rsp.chat_id,this);
const div = document.getElementById("chat_display_2");
div.scrollTop = div.scrollHeight;
})
.catch(error => {
console.error('Error post data:', error);
throw error; // 重新抛出错误以便调用者可以处理
});
this.query="";
this.one=true;
const div = document.getElementById("chat_display_2");
div.scrollTop = div.scrollHeight;
},
del_chat(index){//删除对话列表中的对话
this.qItems.splice(index,1);
},
upload_file(){
const file = this.$refs.fileInput.files[0];
if (!file) return;
const formData = new FormData();
formData.append('file', file);
formData.append("curpath",this.kagent.files);
try {
axios.post('/api/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then( response =>{
var doc = response.data;
if (doc.errno === 1) {
alert(doc.message);
}else{
this.ai_file = doc.file;
this.push_item({chat_id:"",req:"你上传了文件: "+this.ai_file.name,rsp:"",rsp_show:false});
// 保存本地缓存
localStorage.setItem(this.Agent_Kasn, JSON.stringify(doc.file))
}
})
} catch (error) {
console.error('Error:', error);
alert('An error occurred while uploading the file.');
}
}// upload_file 上传文件
}
}
//流式更新对话
function update_chat(eventSource,chat_id,fun){
var chat_index = fun.qItems.length-1;
fun.is_gen = false;
//分块更新
var ResponseDiv = null;
var messagesDiv = document.createElement('div');
// 监听消息事件
eventSource.onmessage = function(event) {
//首次创建
if (ResponseDiv==null){
ResponseDiv = document.getElementById(chat_id);
ResponseDiv.appendChild(messagesDiv);
}
const message = JSON.parse(event.data);
//console.log(message.rsp);
if (message.rsp.indexOf("<think>")==0){
var end = message.rsp.indexOf("</think>");
if (end==-1){ //思考还未结束
var think_mk = marked.parse(message.rsp.slice(7));
messagesDiv.innerHTML = think_mk;
}else{
var think_mk = marked.parse(message.rsp.slice(7,end));
var think_all=think_mk;
var result= marked.parse(message.rsp.slice(end+8));
if (result!=""){
think_all +=`<div id=${chat_index} class="content-layer">${result}</div>`;
}
messagesDiv.innerHTML = think_all;
fun.update_last_qitem(message.rsp.slice(end+8));
}
}else if(message.rsp.indexOf("<new>")==0){
//新的一个消息,新的一个层
messagesDiv = document.createElement('div');
ResponseDiv.appendChild(messagesDiv);
}else{
//正式内容都用蓝色背景
let result =marked.parse(message.rsp);
//var think_all=`<div style="color: white; background-color: rgb(3 105 161 / 0.7); border-radius: 5px; margin-bottom: 5px; padding:5px;">${result}</div>`;
var think_all=`<div id=${chat_index} class="content-layer">${result}</div>`;
messagesDiv.innerHTML = think_all;
fun.update_last_qitem(message.rsp);
}
//自动滚动
const div = document.getElementById("chat_display_2");
div.scrollTop = div.scrollHeight;
};
// 监听错误事件
eventSource.onerror = function(error) {
fun.is_gen = true;
fun.update_last_item(rsp.chat_id,`回答结束`,rsp.ctx);
console.info(chat_id+" 回答结束");
update_create_button(fun);
//结束后滚动
const div = document.getElementById("chat_display_2");
div.scrollTop = div.scrollHeight;
if (eventSource.readyState != EventSource.CLOSED){
eventSource.close(); // 关闭连接
}
};
}
// 更新百科内容创建按扭
function update_create_button(fun){
// 获取所有具有相同 class 的 div
const layers = document.querySelectorAll('.content-layer');
layers.forEach(layer => {
// 创建悬浮按钮
const btn = document.createElement('button');
btn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5 center" aria-hidden="true">
<path d="M1 8.25a1.25 1.25 0 1 1 2.5 0v7.5a1.25 1.25 0 1 1-2.5 0v-7.5ZM11 3V1.7c0-.268.14-.526.395-.607A2 2 0 0 1 14 3c0 .995-.182 1.948-.514 2.826-.204.54.166 1.174.744 1.174h2.52c1.243 0 2.261 1.01 2.146 2.247a23.864 23.864 0 0 1-1.341 5.974C17.153 16.323 16.072 17 14.9 17h-3.192a3 3 0 0 1-1.341-.317l-2.734-1.366A3 3 0 0 0 6.292 15H5V8h.963c.685 0 1.258-.483 1.612-1.068a4.011 4.011 0 0 1 2.166-1.73c.432-.143.853-.386 1.011-.814.16-.432.248-.9.248-1.388Z"/>
</svg>`; // 可以是图标或文字
btn.classList.add('floating-info-btn'); // 添加类名便于样式控制
// 鼠标悬停效果
btn.onmouseover = () => btn.style.transform = 'scale(1.1)';
btn.onmouseout = () => btn.style.transform = 'scale(1.0)';
// // 关键:设置父容器为 relative使绝对定位生效
// layer.style.position = 'relative';
// 将按钮添加到当前 div 中
layer.appendChild(btn);
// 绑定点击事件:获取当前层内的 DOM 信息
btn.addEventListener('click', function (e) {
e.stopPropagation(); //
let chat_index = layer.id;
var rsp = layer.innerHTML;
let end = rsp.indexOf('<button class="floating-info-btn"');
rsp = rsp.slice(0,end);
// 数据对象,代表要发送的数据
const newItem = {
id: 0,
title: fun.qItems[chat_index].req.slice(0,30),
catalog: "个人空间",
html: rsp
};
// 使用 axios 发送 POST 请求
axios.post('/api/baike', newItem)
.then(response =>{
let baike_id = response.data.id;
var tip= document.createElement('span');
tip.innerText="感谢点赞,此条数据添加到个人空间!";
layer.appendChild(tip);
})
.catch(error => {
alert(error);
console.error('Error post data:', error);
throw error; // 重新抛出错误以便调用者可以处理
});
});
});
}
function getCookie(name) {
// 将所有 Cookie 按分号分割成数组
const cookies = document.cookie.split('; ');
// 遍历每个 Cookie查找目标名称
for (let cookie of cookies) {
const [cookieName, cookieValue] = cookie.split('=');
// 如果找到匹配的 Cookie 名称,返回其值
if (cookieName === name) {
return decodeURIComponent(cookieValue); // 解码 URL 编码的值
}
}
// 如果未找到,返回 null
return null;
}
function draw_icon(text){
// 获取画布和绘图上下文
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// 假设canvas和ctx已经正确定义并初始化
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
// 清空画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#FFFFFF'; // 背景颜色
ctx.fillRect(0, 0, canvas.width, canvas.height); // 填充整个画布
// 创建渐变
var gradient = ctx.createLinearGradient(0, 0, 0, canvasHeight); // 水平方向的渐变
// 给渐变添加颜色停止点color stops
gradient.addColorStop(0, '#0000FF'); // 深蓝色
gradient.addColorStop(1, '#4f46e5'); // 蓝色
// 设置字体样式
ctx.font = 'bold 65px "Microsoft YaHei"'; // 字体大小和类型
ctx.fillStyle = gradient; // 文字颜色
ctx.textAlign = 'center'; // 水平居中
ctx.textBaseline = 'middle'; // 垂直居中
// 绘制汉字
const centerX = canvas.width / 2;
const centerY = canvas.height / 2+5;
ctx.fillText(text, centerX, centerY);
// 将画布内容转换为图片
const imageData = canvas.toDataURL('image/png'); // 默认生成 PNG 格式的 Base64 图片
// 将生成的图片显示在页面上
document.getElementById('head').src = imageData;
}
</script>
</body>
</html>