364 lines
16 KiB
HTML
364 lines
16 KiB
HTML
|
<!DOCTYPE html>
|
||
|
<html lang="en">
|
||
|
<head>
|
||
|
<meta charset="UTF-8">
|
||
|
<title>智能GPT创作系统</title>
|
||
|
<link rel="stylesheet" href="/static/cdn/element-ui/2.15.7/theme-chalk/index.min.css">
|
||
|
<script src="/static/cdn/vue/2.6.11/vue.min.js"></script>
|
||
|
<script src="/static/cdn/element-ui/2.15.7/index.js"></script>
|
||
|
<script src="/static/cdn/jquery/3.6.0/jquery.min.js"></script>
|
||
|
<script src="/static/cdn/jquery/jquery.qrcode.min.js"></script>
|
||
|
<script src="/static/js/functions.js"></script>
|
||
|
<script src="/static/js/chat-lang.js?v=1.0.1"></script>
|
||
|
|
||
|
<link rel="stylesheet" href="/static/css/aigc.css">
|
||
|
</head>
|
||
|
<body>
|
||
|
<div id="app">
|
||
|
<template>
|
||
|
<div class="chatPannel">
|
||
|
<div class="newChat" @click="collect_id=0;msgList=[]">
|
||
|
<img src="/static/images/openailogo.svg" />新聊天</div>
|
||
|
<div class="fileList">
|
||
|
<div @click="selectCollect(item.id)" class="fileTitle" v-bind:class="{'active': item.id==collect_id}" v-for="(item,index) in collectList" :title="item.title">
|
||
|
<i class="el-icon-chat-square"></i><{truncateString(item.title)}></div>
|
||
|
</div>
|
||
|
<div class="fileTitle" @click="cleanCollect()">
|
||
|
<i class="el-icon-delete"></i>
|
||
|
<span style="margin-left: 5px">清空聊天</span>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="chatRight">
|
||
|
<div class="chatHeader">
|
||
|
模型: <{bigModel}>
|
||
|
</div>
|
||
|
<div style="display: flex;">
|
||
|
<div class="bigModelType">
|
||
|
<div @click="selectBigModel('gpt-3.5-turbo')" class="bigModelBtn " v-bind:class="{'active': bigModel=='gpt-3.5-turbo'}">GPT-3.5 Turbo</div>
|
||
|
<div @click="selectBigModel('gpt-4o')" class="bigModelBtn" v-bind:class="{'active': bigModel=='gpt-4o'}">GPT-4o</div>
|
||
|
<div @click="selectBigModel('gpt-4-turbo')" class="bigModelBtn" v-bind:class="{'active': bigModel=='gpt-4-turbo'}">GPT-4 Turbo</div>
|
||
|
<div @click="selectBigModel('gpt-4')" class="bigModelBtn" v-bind:class="{'active': bigModel=='gpt-4'}">GPT-4</div>
|
||
|
<div style="display: none" @click="selectBigModel('ERNIE-Bot-turbo')" class="bigModelBtn" v-bind:class="{'active': bigModel=='ERNIE-Bot-turbo'}">文心千帆</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="chatpdfBox">
|
||
|
<div class="chatpdfLine">
|
||
|
|
||
|
<div v-show="collect_id==0">
|
||
|
<div style="text-align: center;margin:200px 0px 80px 0px">
|
||
|
<img src="/static/images/openailogo.svg" width="50px"/>
|
||
|
</div>
|
||
|
<h1>智能GPT创作系统</h1>
|
||
|
<div class="introBlock">
|
||
|
<div class="introBlockTitle">
|
||
|
<svg stroke="currentColor" fill="none" stroke-width="1.5" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-6 w-6" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>
|
||
|
|
||
|
示例
|
||
|
</div>
|
||
|
<div class="introBlockTitle">
|
||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="round" stroke="currentColor" aria-hidden="true" class="h-6 w-6"><path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z"></path></svg>
|
||
|
技术支持
|
||
|
</div>
|
||
|
<div class="introBlockTitle">
|
||
|
<svg stroke="currentColor" fill="none" stroke-width="1.5" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-6 w-6" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
|
||
|
限制
|
||
|
</div>
|
||
|
<div class="introBlockItem" @click="askContent='请详细解释如何使用chatgpt'">
|
||
|
“请详细解释如何使用chatgpt” →
|
||
|
</div>
|
||
|
<div class="introBlockItem" @click="askContent='如何实现知识库Embedding增强GPT'">
|
||
|
如何实现知识库Embedding 增强GPT
|
||
|
</div>
|
||
|
<div class="introBlockItem" @click="askContent='如何选择智能客服系统'">
|
||
|
如何选择智能客服系统
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<div class="chatpdfRow " v-bind:class="{'chatpdfAsk': item.type=='ask'}" v-for="(item,index) in msgList">
|
||
|
<div class="chatScoll">
|
||
|
<el-avatar shape="square" class="chatAvatar" :size="35" :src="item.avatar"></el-avatar>
|
||
|
<div class="chatpdfContent" v-html="markdownHtml(item.content)"></div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
</div>
|
||
|
|
||
|
<div class="chatpdfArea">
|
||
|
<textarea @keydown.prevent.enter="sendAsk" v-model="askContent" placeholder="给AI发送消息"></textarea>
|
||
|
<svg @click="sendAsk" v-bind:class="{'active': !isSend && askContent!=''}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" class="h-4 w-4 m-1 md:m-0" stroke-width="1"><path d="M.5 1.163A1 1 0 0 1 1.97.28l12.868 6.837a1 1 0 0 1 0 1.766L1.969 15.72A1 1 0 0 1 .5 14.836V10.33a1 1 0 0 1 .816-.983L8.5 8 1.316 6.653A1 1 0 0 1 .5 5.67V1.163Z" fill="currentColor"></path></svg>
|
||
|
</div>
|
||
|
|
||
|
<el-backtop target=".chatpdfBox"></el-backtop>
|
||
|
</div>
|
||
|
</template>
|
||
|
</div>
|
||
|
</body>
|
||
|
<!-- 引入highlight.js的CSS样式 -->
|
||
|
<link rel="stylesheet" href="/static/js/highlight/styles/default.css">
|
||
|
<script src="/static/js/highlight/highlight.pack.js"></script>
|
||
|
<!-- 引入markdown-it的库 -->
|
||
|
<script src="/static/js/markdown-it.min.js"></script>
|
||
|
<style>
|
||
|
pre.hljs {
|
||
|
border-radius: 5px;
|
||
|
position: relative;
|
||
|
}
|
||
|
pre.hljs ol {
|
||
|
list-style: decimal;
|
||
|
margin: 0px;
|
||
|
margin-left: 40px;
|
||
|
padding: 0;
|
||
|
}
|
||
|
pre.hljs li {
|
||
|
list-style: decimal-leading-zero;
|
||
|
position: relative;
|
||
|
padding-left: 10px;
|
||
|
}
|
||
|
pre.hljs .line-num {
|
||
|
position: absolute;
|
||
|
left: -40px;
|
||
|
top: 0;
|
||
|
width: 40px;
|
||
|
height: 100%;
|
||
|
border-right: 1px solid #ddd;
|
||
|
}
|
||
|
pre.hljs code{
|
||
|
padding: unset;
|
||
|
color: unset;
|
||
|
}
|
||
|
</style>
|
||
|
|
||
|
<script>
|
||
|
const collectName="";
|
||
|
new Vue({
|
||
|
el: '#app',
|
||
|
delimiters:["<{","}>"],
|
||
|
data: {
|
||
|
avatar:"",
|
||
|
aiAvatar:"/static/images/openailogo.svg",
|
||
|
email:"",
|
||
|
nickname:"",
|
||
|
collect_id:0,
|
||
|
loading:null,
|
||
|
askContent:"",
|
||
|
msgList:[
|
||
|
],
|
||
|
collectList:[],
|
||
|
bigModel:"gpt-3.5-turbo",
|
||
|
markdownIt:"",
|
||
|
isSend:false,
|
||
|
},
|
||
|
methods: {
|
||
|
kefuInfo() {
|
||
|
let _this = this;
|
||
|
sendAjax("/kefu/kefuinfo","get",{},function (data) {
|
||
|
if (data.code != 200) {
|
||
|
window.location.href = "/ironMan/signInx";
|
||
|
} else {
|
||
|
let result = data.result;
|
||
|
_this.avatar=result.avator;
|
||
|
_this.email=result.email;
|
||
|
_this.nickname=result.nickname;
|
||
|
_this.getCollectList();
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
trim(str, char) {
|
||
|
if (char) {
|
||
|
str=str.replace(new RegExp('^\\'+char+'+|\\'+char+'+$', 'g'), '');
|
||
|
}
|
||
|
return str.replace(/^\s+|\s+$/g, '');
|
||
|
},
|
||
|
sendAsk(){
|
||
|
if(this.askContent=="" ||this.isSend) return;
|
||
|
let _this=this;
|
||
|
let msg={
|
||
|
'type':'ask',
|
||
|
'content':this.askContent,
|
||
|
'avatar':this.avatar
|
||
|
}
|
||
|
this.msgList.push(msg);
|
||
|
let data=JSON.stringify(this.msgList);
|
||
|
localStorage.setItem("data_"+this.collect,data);
|
||
|
this.scrollBottom();
|
||
|
this.SaveChatStream(this.askContent,'ask',function(){
|
||
|
_this.getReplyFromApi();
|
||
|
});
|
||
|
|
||
|
},
|
||
|
getReplyFromApi(){
|
||
|
let _this=this;
|
||
|
_this.isSend=true;
|
||
|
let msg={
|
||
|
'type':'answer',
|
||
|
'avatar':this.aiAvatar,
|
||
|
'content':"正在为你生成答案(开发中)...",
|
||
|
}
|
||
|
_this.msgList.push(msg);
|
||
|
let result="";
|
||
|
var xhr = new XMLHttpRequest();
|
||
|
let data={
|
||
|
content:_this.askContent,
|
||
|
model:_this.bigModel,
|
||
|
collect_id:_this.collect_id,
|
||
|
}
|
||
|
let postData=Object.keys(data)
|
||
|
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`)
|
||
|
.join("&");
|
||
|
xhr.open("POST", "/kefu/chatStream");
|
||
|
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
|
||
|
xhr.setRequestHeader("token", localStorage.getItem("token"));
|
||
|
xhr.onprogress = function (event) {
|
||
|
result=event.currentTarget.responseText;
|
||
|
_this.msgList[_this.msgList.length - 1].content =result;
|
||
|
_this.scrollBottom();
|
||
|
};
|
||
|
xhr.onreadystatechange = () => {
|
||
|
if (xhr.readyState === 4) {
|
||
|
_this.isSend=false;
|
||
|
_this.SaveChatStream(result,"answer");
|
||
|
_this.scrollBottom();
|
||
|
}
|
||
|
};
|
||
|
xhr.send(postData);
|
||
|
this.askContent="";
|
||
|
},
|
||
|
//保存消息记录
|
||
|
SaveChatStream(content,msgType,callback){
|
||
|
let _this = this;
|
||
|
let data={
|
||
|
collect_id:this.collect_id,
|
||
|
content:content,
|
||
|
kefu_avatar:this.avatar,
|
||
|
ai_avatar:this.aiAvatar,
|
||
|
msg_type:msgType
|
||
|
}
|
||
|
sendAjax("/kefu/saveChatStream","POST",data,function (ret) {
|
||
|
_this.collect_id=ret.result.collect_id;
|
||
|
let flag=false;
|
||
|
for(let i in _this.collectList){
|
||
|
if(_this.collectList[i].id==_this.collect_id){
|
||
|
flag=true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if(!flag){
|
||
|
_this.collectList.unshift({id:_this.collect_id,title:content})
|
||
|
}
|
||
|
if (callback) callback();
|
||
|
});
|
||
|
},
|
||
|
//截取字符
|
||
|
truncateString(str){
|
||
|
return truncateString(str,13);
|
||
|
},
|
||
|
//获取集合列表
|
||
|
getCollectList(){
|
||
|
let _this = this;
|
||
|
sendAjax("/kefu/chatCollects","GET",{},function (ret) {
|
||
|
_this.collectList=ret.result;
|
||
|
});
|
||
|
},
|
||
|
//选择集合
|
||
|
selectCollect(collectId){
|
||
|
let _this=this;
|
||
|
this.collect_id=collectId;
|
||
|
this.msgList=[];
|
||
|
sendAjax("/kefu/chatSessions","GET",{collect_id:collectId},function (ret) {
|
||
|
let collects=ret.result;
|
||
|
for(let i in collects){
|
||
|
let item=collects[i];
|
||
|
let element={type:item.msg_type,content:item.content,avatar:""}
|
||
|
if (item.msg_type=='ask'){
|
||
|
element.avatar=item.kefu_avatar;
|
||
|
}else{
|
||
|
element.avatar=item.ai_avatar;
|
||
|
}
|
||
|
_this.msgList.push(element);
|
||
|
}
|
||
|
_this.scrollBottom();
|
||
|
});
|
||
|
},
|
||
|
//清空集合
|
||
|
cleanCollect(){
|
||
|
let _this = this;
|
||
|
sendAjax("/kefu/cleanChatCollects","GET",{},function (ret) {
|
||
|
_this.collectList=[];
|
||
|
_this.collect_id=0;
|
||
|
});
|
||
|
},
|
||
|
//选择大模型
|
||
|
selectBigModel(name){
|
||
|
let _this=this;
|
||
|
_this.bigModel=name;
|
||
|
if(name=='ERNIE-Bot-turbo'){
|
||
|
_this.aiAvatar="/static/images/ERNIE.png";
|
||
|
}else if(name=='gpt-4'){
|
||
|
_this.aiAvatar="/static/images/gpt4.png";
|
||
|
}else {
|
||
|
_this.aiAvatar="/static/images/openailogo.svg";
|
||
|
}
|
||
|
},
|
||
|
//滚动到底部
|
||
|
scrollBottom:function(){
|
||
|
var _this=this;
|
||
|
this.$nextTick(function(){
|
||
|
var container = _this.$el.querySelector(".chatpdfBox");
|
||
|
container.scrollTop = 999999999;
|
||
|
});
|
||
|
},
|
||
|
//解析markdown
|
||
|
markdownHtml(str){
|
||
|
var result = this.markdownIt.render(str);
|
||
|
return result;
|
||
|
},
|
||
|
},
|
||
|
mounted:function(){
|
||
|
|
||
|
},
|
||
|
created: function () {
|
||
|
this.kefuInfo();
|
||
|
// 配置highlight.js
|
||
|
hljs.configure({
|
||
|
tabReplace: ' ', // 使用两个空格作为缩进
|
||
|
});
|
||
|
|
||
|
this.markdownIt=window.markdownit({
|
||
|
html: true,
|
||
|
linkify: true,
|
||
|
typographer: true,
|
||
|
highlight: function (str, lang) {
|
||
|
// 此处判断是否有添加代码语言
|
||
|
if (lang && hljs.getLanguage(lang)) {
|
||
|
try {
|
||
|
// 得到经过highlight.js之后的html代码
|
||
|
const preCode = hljs.highlight(lang, str, true).value
|
||
|
// 以换行进行分割
|
||
|
const lines = preCode.split(/\n/).slice(0, -1)
|
||
|
// 添加自定义行号
|
||
|
let html = lines.map((item, index) => {
|
||
|
return '<li><span class="line-num" data-line="' + (index + 1) + '"></span>' + item + '</li>'
|
||
|
}).join('')
|
||
|
html = '<ol>' + html + '</ol>'
|
||
|
return '<pre class="hljs"><code>' +
|
||
|
html +
|
||
|
'</code></pre>'
|
||
|
} catch (__) {}
|
||
|
}
|
||
|
// // 未添加代码语言,此处与上面同理
|
||
|
// const preCode = str;
|
||
|
// const lines = preCode.split(/\n/).slice(0, -1)
|
||
|
// let html = lines.map((item, index) => {
|
||
|
// return '<li><span class="line-num" data-line="' + (index + 1) + '"></span>' + item + '</li>'
|
||
|
// }).join('')
|
||
|
// html = '<ol>' + html + '</ol>'
|
||
|
// return '<pre class="hljs"><code>' +
|
||
|
// html +
|
||
|
// '</code></pre>'
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
})
|
||
|
</script>
|
||
|
</html>
|