506 lines
21 KiB
HTML
506 lines
21 KiB
HTML
{{template "header" }}
|
||
<div id="app" style="width:100%">
|
||
<template>
|
||
<el-tabs type="border-card" v-model="activeTab">
|
||
<el-tab-pane label="训练素材" name="fileList">
|
||
<el-input placeholder="搜索知识库 (基于向量相似性语义搜索)" v-model="article.search" style="width:300px;margin:0px 0px 10px 10px;"></el-input>
|
||
<el-button type="primary" @click="searchKnowledge()">搜索</el-button>
|
||
<el-table
|
||
:data="fileList"
|
||
border
|
||
style="width: 100%;margin-top: 10px">
|
||
<el-table-column
|
||
prop="id"
|
||
label="ID">
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="file_name"
|
||
label="文件名">
|
||
<template slot-scope="scope">
|
||
<a @click="activeTab='knowledgeList';fileId=scope.row.id;getAllKnowledge()"><{scope.row.file_name}></a>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="file_name"
|
||
label="文件路径">
|
||
<template slot-scope="scope">
|
||
<{getFilePath(scope.row.collect_name,scope.row.file_name)}>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="file_size"
|
||
label="字符数">
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="created_at"
|
||
label="上传时间">
|
||
<template slot-scope="scope">
|
||
<{dateFormat(scope.row.created_at)}>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="id"
|
||
label="操作">
|
||
<template slot-scope="scope">
|
||
<el-button style="margin-right: 10px" @click="activeTab='knowledgeList';fileId=scope.row.id;getAllKnowledge()" type="text">知识列表</el-button>
|
||
<el-button style="margin-right: 15px" @click="addSigleTxt.content='',addSigleTxt.title=scope.row.file_name;addSigleTxt.fileId=scope.row.id;addSigleTxt.dialog=true" type="text">添加</el-button>
|
||
<el-button style="margin-right: 10px" @click="exportToExcel(scope.row.id,scope.row.file_name)" type="text">导出</el-button>
|
||
|
||
<el-button style="margin-right: 10px" @click="delFile(scope.row.id)" type="text">删除</el-button>
|
||
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="手动录入">
|
||
<el-descriptions style="font-size: 14px;" direction="vertical" :column="1" border>
|
||
<el-descriptions-item label="输入文本">
|
||
<el-form style="font-size: 14px;width: 800px">
|
||
<el-form-item label="文本标题">
|
||
<el-input v-model="singleTxt.title"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="文本内容">
|
||
<el-input type="textarea" show-word-limit rows="10" v-model="singleTxt.content"></el-input>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button @click="addPoints" type="primary" size="small">提交</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="上传素材">
|
||
<el-alert
|
||
title="注意: 支持上传.txt .docx .pdf .xlsx文档! pdf文档不能是图片扫描生成的,读取不到数据! excel文档会按行进行分割入库"
|
||
type="info"
|
||
show-icon>
|
||
</el-alert>
|
||
|
||
|
||
<el-form style="margin:10px 0px;">
|
||
<el-upload
|
||
multiple
|
||
drag
|
||
style="margin:10px 0px;"
|
||
:action="uploadUrl"
|
||
:on-success="uploadDocSuccess"
|
||
:on-error="uploadError"
|
||
:before-upload="beforeUpload"
|
||
>
|
||
<i class="el-icon-upload"></i>
|
||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||
</el-upload>
|
||
<el-form-item style="display: none">
|
||
<el-input style="width: 800px;" v-model="learnUrl" placeholder="网页地址"></el-input>
|
||
</el-form-item>
|
||
<el-form-item style="display: none">
|
||
<el-button @click="uploadUrlPage" icon="el-icon-upload" type="primary">上传网址</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="知识列表" name="knowledgeList">
|
||
<el-input placeholder="搜索知识库 (基于向量相似性语义搜索)" v-model="article.search" style="width:300px;margin:0px 0px 10px 10px;"></el-input>
|
||
<el-button type="primary" @click="searchKnowledge()">搜索</el-button>
|
||
<el-table
|
||
:data="filePoints"
|
||
border
|
||
style="width: 100%;margin-top: 10px">
|
||
<el-table-column
|
||
prop="content"
|
||
label="内容">
|
||
<template slot-scope="scope">
|
||
<{scope.row.payload.text}>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
width="380px"
|
||
prop="content"
|
||
label="文件ID">
|
||
<template slot-scope="scope">
|
||
<{scope.row.payload.fileid}>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
width="180px"
|
||
prop="id"
|
||
label="操作">
|
||
<template slot-scope="scope">
|
||
<el-button style="float: right;margin-right: 10px" @click="delPoints(scope.row.id)" type="text">删除</el-button>
|
||
<el-button style="float: right;margin-right: 15px" @click="article.fileId=fileId;article.title=scope.row.payload.url;article.id=scope.row.id;article.content=scope.row.payload.text;article.dialog=true" type="text">编辑</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="提问日志" name="questionList">
|
||
<el-button type="primary" @click="getLogs()">刷新</el-button>
|
||
<el-table
|
||
:data="logList.list"
|
||
border
|
||
style="width: 100%;margin-top: 10px">
|
||
<el-table-column
|
||
prop="question"
|
||
width="800"
|
||
label="提问日志">
|
||
<template slot-scope="scope">
|
||
<div v-html='replace(scope.row.question,"\n","<br>")'></div>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="answer"
|
||
label="回答日志">
|
||
</el-table-column>
|
||
<el-table-column
|
||
prop="created_at"
|
||
label="时间">
|
||
</el-table-column>
|
||
</el-table>
|
||
<el-pagination
|
||
background
|
||
@current-change="getLogs"
|
||
:current-page="logList.page"
|
||
layout="prev,pager, next"
|
||
:page-size="logList.pagesize"
|
||
:total="logList.count">
|
||
</el-pagination>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
|
||
|
||
<el-dialog
|
||
title="添加知识"
|
||
:visible.sync="addSigleTxt.dialog"
|
||
width="50%"
|
||
:close-on-click-modal="false"
|
||
top="3%"
|
||
>
|
||
<el-form size="medium">
|
||
<el-form-item label="内容">
|
||
<el-input type="textarea" show-word-limit :rows="10" v-model="addSigleTxt.content"></el-input>
|
||
</el-form-item>
|
||
</el-form>
|
||
<span slot="footer" class="dialog-footer">
|
||
<el-button @click="addSigleTxt.dialog = false">取消</el-button>
|
||
<el-button type="primary" @click="saveFilePoints()">确认</el-button>
|
||
</span>
|
||
</el-dialog>
|
||
<el-dialog
|
||
title="优化修改"
|
||
:visible.sync="article.dialog"
|
||
width="50%"
|
||
:close-on-click-modal="false"
|
||
top="3%"
|
||
>
|
||
<el-form size="medium">
|
||
<el-form-item label="内容" prop="remark">
|
||
<el-input type="textarea" :rows="10" v-model="article.content"></el-input>
|
||
</el-form-item>
|
||
</el-form>
|
||
<span slot="footer" class="dialog-footer">
|
||
<el-button @click="article.dialog = false">取消</el-button>
|
||
<el-button type="primary" @click="savePoints()">确认</el-button>
|
||
</span>
|
||
</el-dialog>
|
||
</template>
|
||
</div>
|
||
<script src="/static/js/xlsx.full.min.js" type="text/javascript"></script>
|
||
<script>
|
||
new Vue({
|
||
el: '#app',
|
||
delimiters: ["<{", "}>"],
|
||
data: {
|
||
activeTab:"fileList",
|
||
fileList:[],
|
||
filePoints:[],
|
||
fileId:"",
|
||
addSigleTxt:{
|
||
dialog:false,
|
||
fileId:"",
|
||
content:"",
|
||
title:"",
|
||
},
|
||
singleTxt:{
|
||
title:"",
|
||
content:""
|
||
},
|
||
article:{
|
||
title:"",
|
||
dialog:false,
|
||
id:"",
|
||
content:"",
|
||
search:"",
|
||
},
|
||
uploadUrl:"",
|
||
learnUrl:"",
|
||
logList:{
|
||
page:1,
|
||
count:0,
|
||
pagesize:0,
|
||
list:[],
|
||
},
|
||
},
|
||
methods: {
|
||
//文件列表
|
||
getFileList(){
|
||
let _this=this;
|
||
sendAjax('/kefu/ai/fileList',"GET",{},function(data){
|
||
if(data.result){
|
||
_this.fileList=data.result;
|
||
}else{
|
||
_this.$message({
|
||
message:"请联系管理员创建集合,然后补充完整(大模型接口,大模型key,知识库集合)",
|
||
type: 'error'
|
||
});
|
||
}
|
||
})
|
||
},
|
||
//时间格式化
|
||
dateFormat(oldTime){
|
||
return dateFormat("YYYY-mm-dd HH:MM:SS",new Date(oldTime))
|
||
},
|
||
//上传文本数据
|
||
addPoints(){
|
||
let _this=this;
|
||
this.listLoading = this.$loading({
|
||
lock: true,
|
||
text: "上传中",
|
||
});
|
||
sendAjax('/kefu/ai/training',"POST",this.singleTxt,function(data){
|
||
_this.activeTab="fileList";
|
||
_this.listLoading.close();
|
||
let ret=JSON.parse(data);
|
||
if(ret.status!="ok"){
|
||
_this.$message({
|
||
message:data,
|
||
type: 'error'
|
||
});
|
||
return;
|
||
}
|
||
_this.singleTxt={
|
||
title:"",
|
||
content:""
|
||
};
|
||
_this.getFileList();
|
||
_this.$message({
|
||
message: "success",
|
||
type: 'success'
|
||
});
|
||
})
|
||
},
|
||
//编辑知识
|
||
savePoints(){
|
||
let _this=this;
|
||
this.listLoading = this.$loading({
|
||
lock: true,
|
||
text: "修改中",
|
||
});
|
||
sendAjax('/kefu/ai/training',"POST",this.article,function(data){
|
||
_this.listLoading.close();
|
||
let ret=JSON.parse(data);
|
||
if(ret.status!="ok"){
|
||
_this.$message({
|
||
message:data,
|
||
type: 'error'
|
||
});
|
||
return;
|
||
}
|
||
_this.article.dialog = false;
|
||
_this.getAllKnowledge();
|
||
_this.searchKnowledge();
|
||
_this.$message({
|
||
message: "success",
|
||
type: 'success'
|
||
});
|
||
})
|
||
},
|
||
//搜索知识
|
||
searchKnowledge(){
|
||
let _this=this;
|
||
if(!this.article.search) return;
|
||
this.listLoading = this.$loading({
|
||
lock: true,
|
||
text: "搜索中",
|
||
});
|
||
sendAjax('/kefu/ai/searchPoints',"POST",{search:this.article.search},function(data){
|
||
_this.listLoading.close();
|
||
let ret=JSON.parse(data)
|
||
if(ret.result){
|
||
_this.filePoints=ret.result;
|
||
_this.activeTab='knowledgeList'
|
||
}
|
||
})
|
||
},
|
||
//新增知识
|
||
saveFilePoints(){
|
||
let _this=this;
|
||
this.listLoading = this.$loading({
|
||
lock: true,
|
||
text: "添加中",
|
||
});
|
||
sendAjax('/kefu/ai/training',"POST",this.addSigleTxt,function(data){
|
||
_this.listLoading.close();
|
||
let ret=JSON.parse(data);
|
||
if(ret.status!="ok"){
|
||
_this.$message({
|
||
message:data,
|
||
type: 'error'
|
||
});
|
||
return;
|
||
}
|
||
_this.addSigleTxt.dialog=false;
|
||
_this.getFileList();
|
||
_this.$message({
|
||
message: "success",
|
||
type: 'success'
|
||
});
|
||
})
|
||
},
|
||
delFile(id){
|
||
let _this=this;
|
||
this.$confirm("是否删除", '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(function(){
|
||
sendAjax('/kefu/ai/delFile',"GET",{id:id},function(data){
|
||
_this.getFileList();
|
||
_this.$message({
|
||
message: "success",
|
||
type: 'success'
|
||
});
|
||
})
|
||
}).catch(function(){
|
||
});
|
||
},
|
||
//知识列表
|
||
getAllKnowledge(){
|
||
let _this=this;
|
||
let id=this.fileId;
|
||
if(id){
|
||
sendAjax('/kefu/ai/filePoints',"GET",{id:id},function(data){
|
||
let ret=JSON.parse(data)
|
||
if(ret.result){
|
||
_this.filePoints=ret.result;
|
||
}
|
||
})
|
||
}
|
||
},
|
||
//删除知识
|
||
delPoints(id){
|
||
let _this=this;
|
||
this.$confirm("是否删除", '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(function(){
|
||
sendAjax('/kefu/ai/delPoints',"GET",{id:id},function(data){
|
||
_this.getAllKnowledge();
|
||
})
|
||
}).catch(function(){
|
||
});
|
||
},
|
||
//上传文件失败
|
||
uploadDocSuccess(response, file, fileList){
|
||
this.listLoading.close();
|
||
this.activeTab="fileList";
|
||
if(response.code==200){
|
||
this.$message({
|
||
message: "上传成功",
|
||
type: 'success'
|
||
});
|
||
this.getFileList();
|
||
}else{
|
||
this.$message({
|
||
message: response.msg,
|
||
type: 'error'
|
||
});
|
||
}
|
||
},
|
||
//上传文件失败
|
||
uploadError(){
|
||
this.listLoading.close();
|
||
},
|
||
//上传之前
|
||
beforeUpload(file){
|
||
this.listLoading = this.$loading({
|
||
lock: true,
|
||
text: "上传中",
|
||
});
|
||
|
||
let ext=file.name.substring(file.name.lastIndexOf(".")+1);
|
||
if (ext != 'txt' && ext != 'docx' && ext != 'xlsx' && ext != 'pdf') {
|
||
this.$message.error('上传文件只能是 .txt .docx .xlsx .pdf 格式!');
|
||
this.listLoading.close();
|
||
return false;
|
||
}
|
||
},
|
||
//上传URL
|
||
uploadUrlPage(){
|
||
let _this=this;
|
||
this.listLoading = this.$loading({
|
||
lock: true,
|
||
text: "上传中",
|
||
});
|
||
sendAjax('/kefu/ai/uploadUrl',"POST",{url:this.learnUrl},function(data){
|
||
_this.listLoading.close();
|
||
_this.learnUrl="";
|
||
_this.getFileList();
|
||
_this.activeTab="fileList";
|
||
_this.$message({
|
||
message: "success",
|
||
type: 'success'
|
||
});
|
||
})
|
||
},
|
||
//获取文件路径
|
||
getFilePath(collectName,fileName){
|
||
if(fileName.includes(".")){
|
||
return "/static/upload/ai/"+collectName+"/"+fileName;
|
||
}else{
|
||
return "文案知识";
|
||
}
|
||
},
|
||
//导出excel
|
||
exportToExcel(id,filename) {
|
||
let _this=this;
|
||
this.listLoading = this.$loading({
|
||
lock: true,
|
||
text: "正在导出excel",
|
||
});
|
||
sendAjax('/kefu/ai/filePoints',"GET",{id:id},function(data){
|
||
let ret=JSON.parse(data)
|
||
if(ret.result){
|
||
let data=[[filename+'的主要内容']];
|
||
for(let i in ret.result){
|
||
data.push([ret.result[i].payload.text]);
|
||
}
|
||
const worksheet = XLSX.utils.aoa_to_sheet(data);
|
||
const workbook = XLSX.utils.book_new();
|
||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
||
XLSX.writeFile(workbook, filename+'.xlsx');
|
||
_this.listLoading.close();
|
||
}
|
||
})
|
||
|
||
},
|
||
//获取日志列表
|
||
getLogs(page){
|
||
var _this=this;
|
||
sendAjax("/kefu/llmLogs","get",{page:page,pagesize:20},function(result){
|
||
_this.logList=result.result;
|
||
});
|
||
},
|
||
replace(str,source,dest){
|
||
return str.replaceAll(source,dest);
|
||
},
|
||
},
|
||
mounted:function(){
|
||
|
||
},
|
||
created: function () {
|
||
this.singleTxt.title="文本-"+this.dateFormat(new Date());
|
||
this.uploadUrl=`/kefu/ai/uploadDoc?token=`+localStorage.getItem("token");
|
||
this.getFileList();
|
||
this.getLogs();
|
||
}
|
||
});
|
||
</script> |