人脸识别与大模型问答
This commit is contained in:
parent
6c7c1c7fb9
commit
460991d615
|
|
@ -550,7 +550,7 @@ export default {
|
|||
}
|
||||
</script>
|
||||
|
||||
<style vars="{{PicNum}}">
|
||||
<style lang="scss" vars="{{PicNum}}">
|
||||
@import url('./../../assets/styles/SurveillanceVideo.css');
|
||||
|
||||
#main {
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@
|
|||
<div class="main-left-list">
|
||||
<div class="list-group">
|
||||
<div
|
||||
:class="windowId === item.windowId ? 'group-item group-item-selected':'group-item group-item-noSelected'"
|
||||
v-for="(item, index) in windowList" :key="index">
|
||||
<span @click="switchWindow(item.windowId)">{{ item.windowName }}</span>
|
||||
:class="windowId == item.windowId ? 'group-item group-item-selected':'group-item group-item-noSelected'"
|
||||
v-for="(item, index) in windowList" :key="index" @mousedown="switchWindow(item.windowId)">
|
||||
<span>{{ item.windowName }}</span>
|
||||
<i
|
||||
class="el-icon-delete" @click="deleteChatWindow(item.windowId)"
|
||||
></i>
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
<div class="main-right">
|
||||
<div class="main-right-top">
|
||||
<div class="main-right-top-body">
|
||||
<div class="main-right-top-body" ref="messageContainer">
|
||||
<div class="main" v-for="(item, index) in answerList" :key="index">
|
||||
<div class="problem">
|
||||
<div class="icon">
|
||||
|
|
@ -43,8 +43,8 @@
|
|||
<div class="icon">
|
||||
<img src="../../assets/images/answer.png" alt=""/>
|
||||
</div>
|
||||
<div class="content">
|
||||
<span>{{ item.answer }}</span>
|
||||
<div class="content" v-html="item.answer">
|
||||
<!-- <span>{{ item.answer }}</span>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -67,135 +67,246 @@
|
|||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import boxHeader from '@/views/components/boxHeader.vue'
|
||||
import dialogue from '@/views/askRequest/dialogue.vue'
|
||||
import diagHistory from '@/views/askRequest/diagHistory.vue'
|
||||
import boxHeader from '@/views/components/boxHeader.vue';
|
||||
import dialogue from '@/views/askRequest/dialogue.vue';
|
||||
import diagHistory from '@/views/askRequest/diagHistory.vue';
|
||||
import {
|
||||
deleteChatWindow,
|
||||
getAllByWindowId,
|
||||
getList,
|
||||
insertChatWindow,
|
||||
insertQuestionAnswer,
|
||||
knowledgeBaseChat
|
||||
} from '@/api/askRequest/askRequest'
|
||||
import faceListShowPic from "@/views/updateFace/faceListShowPic.vue";
|
||||
insertQuestionAnswer
|
||||
} from '@/api/askRequest/askRequest';
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
|
||||
export default {
|
||||
// 注册组件
|
||||
components: {
|
||||
boxHeader,
|
||||
dialogue,
|
||||
diagHistory
|
||||
},
|
||||
|
||||
// 定义组件的数据
|
||||
data() {
|
||||
return {
|
||||
controller: '',
|
||||
windowId: '',
|
||||
windowList: [],
|
||||
answerList: [],
|
||||
inputValue: '',
|
||||
isSearch: false,
|
||||
value: '',
|
||||
diagContentTmpMain: ''
|
||||
}
|
||||
controller: new AbortController(), // 用于控制Fetch请求的中止信号
|
||||
windowId: '', // 当前窗口ID
|
||||
windowList: [], // 窗口列表
|
||||
answerList: [], // 回答列表
|
||||
inputValue: '', // 输入框的值
|
||||
isAncestor: false
|
||||
};
|
||||
},
|
||||
|
||||
// 当组件挂载时执行的逻辑
|
||||
mounted() {
|
||||
//this.getList(); // 获取窗口列表
|
||||
this.loadLastWindowId(); // 加载最近访问的窗口ID
|
||||
},
|
||||
|
||||
// 当组件创建时执行的逻辑
|
||||
created() {
|
||||
this.controller = new AbortController();
|
||||
this.getList();
|
||||
this.getList(); // 获取窗口列表
|
||||
},
|
||||
// 监听器,监听windowId的变化
|
||||
watch: {
|
||||
windowId(val) {
|
||||
if (val) {
|
||||
this.getAllByWindowId(val);
|
||||
}
|
||||
windowId(newWindowId) {
|
||||
this.getAllByWindowId(newWindowId);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 生成一个新的窗口ID
|
||||
generateNewWindowId() {
|
||||
const lastWindowId = uuidv4().replace(/-/g, "");
|
||||
this.windowId = lastWindowId
|
||||
localStorage.setItem('lastWindowId', lastWindowId);// 生成UUID并去掉所有的“-”
|
||||
},
|
||||
|
||||
// 加载最近访问的窗口ID
|
||||
loadLastWindowId() {
|
||||
const lastWindowId = localStorage.getItem('lastWindowId'); // 从本地存储中获取最近使用的窗口ID
|
||||
if (lastWindowId) {
|
||||
this.windowId = lastWindowId; // 如果存在,则设置为当前窗口ID
|
||||
} else {
|
||||
this.generateNewWindowId(); // 否则生成一个新的窗口ID
|
||||
}
|
||||
},
|
||||
|
||||
// 切换窗口时保存窗口ID
|
||||
switchWindow(windowId) {
|
||||
if (this.isAncestor) {
|
||||
this.$message.warning("问答进行中");
|
||||
return;
|
||||
}
|
||||
localStorage.setItem('lastWindowId', windowId); // 将当前窗口ID存储在本地存储中
|
||||
this.windowId = windowId; // 切换到新的窗口ID
|
||||
},
|
||||
|
||||
// 获取窗口列表
|
||||
getList() {
|
||||
getList().then(res => {
|
||||
if (res.code === 200) {
|
||||
this.windowList = res.data;
|
||||
if (res.data.length > 0) {
|
||||
this.windowId = res.data[0].windowId;
|
||||
this.windowList = res.data; // 如果请求成功,设置窗口列表
|
||||
if (this.windowList.length === 0) {
|
||||
this.generateNewWindowId();
|
||||
this.answerList = []; // 如果窗口列表为空,清空回答列表
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
switchWindow(windowId) {
|
||||
this.windowId = windowId;
|
||||
});
|
||||
},
|
||||
// 根据窗口ID获取所有聊天记录
|
||||
getAllByWindowId(windowId) {
|
||||
getAllByWindowId(windowId).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.answerList = res.data;
|
||||
this.answerList = res.data; // 如果请求成功,设置回答列表
|
||||
this.scrollToBottom(); // 滚动到聊天记录的底部
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
// 删除聊天窗口
|
||||
deleteChatWindow(windowId) {
|
||||
if (this.isAncestor) {
|
||||
this.$message.warning("问答进行中");
|
||||
return;
|
||||
}
|
||||
deleteChatWindow(windowId).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message.success("删除成功");
|
||||
this.getList();
|
||||
this.$message.success("删除成功"); // 显示删除成功的提示信息
|
||||
this.getList(); // 重新获取窗口列表
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
insertChatWindow() {
|
||||
if (!this.windowId) {
|
||||
this.$message.warning("已经是最新窗口");
|
||||
}
|
||||
this.windowId = '';
|
||||
this.answerList = [];
|
||||
},
|
||||
submitInput() {
|
||||
if (!this.windowId) {
|
||||
insertChatWindow({windowName: this.inputValue}).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.getList();
|
||||
}
|
||||
})
|
||||
}
|
||||
insertQuestionAnswer().then(res => {
|
||||
if (res.code === 200) {
|
||||
this.getList();
|
||||
}
|
||||
})
|
||||
this.resAnswer(this.inputValue)
|
||||
this.inputValue = ""; // 清空输入框
|
||||
},
|
||||
async resAnswer(question) {
|
||||
let params = {
|
||||
"query": question,
|
||||
"knowledge_base_name": "lzh",
|
||||
"top_k": 6,
|
||||
"score_threshold": 1,
|
||||
"stream": true,
|
||||
}
|
||||
knowledgeBaseChat(params).then(response => {
|
||||
const reader = response.data.getReader();
|
||||
|
||||
function read() {
|
||||
reader.read().then(({done, value}) => {
|
||||
if (done) {
|
||||
console.log('Stream complete');
|
||||
return;
|
||||
// 插入新的聊天窗口
|
||||
insertChatWindow() {
|
||||
this.generateNewWindowId();
|
||||
this.answerList = [];// 生成一个新的窗口ID
|
||||
},
|
||||
|
||||
// 提交输入内容
|
||||
submitInput() {
|
||||
if (this.answerList.length === 0) {
|
||||
// 如果当前窗口没有聊天记录,插入一个新的聊天窗口
|
||||
insertChatWindow({windowName: this.inputValue, windowId: this.windowId}).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.getList(); // 更新窗口列表
|
||||
}
|
||||
});
|
||||
}
|
||||
this.resAnswer(this.inputValue); // 处理输入内容
|
||||
this.inputValue = ""; // 清空输入框
|
||||
},
|
||||
|
||||
// 滚动到底部
|
||||
scrollToBottom() {
|
||||
this.$nextTick(() => {
|
||||
const container = this.$refs.messageContainer; // 获取消息容器的引用
|
||||
if (container) {
|
||||
container.scrollTop = container.scrollHeight; // 将容器滚动到最底部
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 处理回答逻辑
|
||||
async resAnswer(question) {
|
||||
this.isAncestor = true;
|
||||
// 创建初始回答数据
|
||||
const data = {
|
||||
answer: "正在思考。。。", // 初始回答内容
|
||||
question: question, // 提交的问题
|
||||
windowId: this.windowId, // 当前窗口ID
|
||||
};
|
||||
this.answerList.push(data); // 将初始回答数据添加到回答列表中
|
||||
this.scrollToBottom(); // 滚动到底部
|
||||
|
||||
const params = {
|
||||
query: question, // 提交给后端的查询参数
|
||||
knowledge_base_name: 'lzh', // 使用的知识库名称
|
||||
stream: true, // 是否启用流式响应
|
||||
};
|
||||
|
||||
try {
|
||||
// 创建一个Fetch请求,用于与后端进行通信
|
||||
const signal = this.controller.signal;
|
||||
const response = await fetch('/api/chat/knowledge_base_chat', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(params), // 将请求参数转换为JSON字符串
|
||||
signal: signal, // 将中止信号添加到请求中
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`); // 如果请求失败,抛出错误
|
||||
}
|
||||
|
||||
const reader = response.body.getReader(); // 获取响应体的阅读器
|
||||
const decoder = new TextDecoder(); // 创建一个TextDecoder用于将二进制数据解码为字符串
|
||||
|
||||
// 清空最后一个回答的内容,准备接收新的回答
|
||||
this.answerList[this.answerList.length - 1].answer = "";
|
||||
|
||||
while (true) {
|
||||
const {done, value} = await reader.read(); // 读取流中的数据
|
||||
if (done) break; // 如果数据读取完成,跳出循环
|
||||
|
||||
const decodedValue = decoder.decode(value); // 解码读取到的数据
|
||||
|
||||
// 分割数据,处理每个独立的消息
|
||||
const messages = decodedValue.split('}{').map((msg, index, array) => {
|
||||
if (index !== 0) msg = '{' + msg;
|
||||
if (index !== array.length - 1) msg += '}';
|
||||
return msg;
|
||||
});
|
||||
|
||||
// 解析并处理每个消息
|
||||
messages.forEach((msg) => {
|
||||
try {
|
||||
const json = JSON.parse(msg);
|
||||
if (json.answer) {
|
||||
this.answerList[this.answerList.length - 1].answer += this.escapeHtml(json.answer); // 将解析出的回答内容添加到最后一个回答中
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('JSON parse error:', e); // 如果解析出错,输出错误信息
|
||||
}
|
||||
// 处理每个数据块
|
||||
console.log(new TextDecoder("utf-8").decode(value));
|
||||
// 继续读取
|
||||
read();
|
||||
});
|
||||
}
|
||||
|
||||
read();
|
||||
}).catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
// 将最终的问答数据插入到数据库中
|
||||
insertQuestionAnswer(this.answerList[this.answerList.length - 1]).then(res => {
|
||||
this.isAncestor = false;
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fetch error:', error); // 捕获请求错误并输出
|
||||
}
|
||||
},
|
||||
escapeHtml(str) {
|
||||
// 使用正则表达式转义特殊字符
|
||||
return str.replace(/[&<>"']/g, (match) => {
|
||||
switch (match) {
|
||||
case '&':
|
||||
return '&';
|
||||
case '<':
|
||||
return '<';
|
||||
case '>':
|
||||
return '>';
|
||||
case '"':
|
||||
return '"';
|
||||
case "'":
|
||||
return ''';
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang="scss">
|
||||
.main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export default {
|
|||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang="scss">
|
||||
#main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@
|
|||
|
||||
<script>
|
||||
import {updateFace} from '@/api/updateFace/updateFace'
|
||||
import midPic from "@/views/updateFace/midPic.vue";
|
||||
import faceListShowPic from "@/views/updateFace/faceListShowPic.vue";
|
||||
|
||||
const validationRules = {
|
||||
name: [
|
||||
|
|
@ -98,6 +100,7 @@ export default {
|
|||
watch: {
|
||||
value(val) {
|
||||
this.visible = val;
|
||||
this.$emit('visible-change', val);
|
||||
if (val) {
|
||||
this.resetParameters(); // 每次打开时清除参数
|
||||
}
|
||||
|
|
@ -142,7 +145,9 @@ export default {
|
|||
});
|
||||
},
|
||||
close() {
|
||||
this.$emit('input', false);
|
||||
faceListShowPic.methods.getList();
|
||||
this.visible = false;
|
||||
this.$emit('input', this.visible);
|
||||
},
|
||||
confirm() {
|
||||
this.close();
|
||||
|
|
@ -161,7 +166,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang="scss">
|
||||
.modal-overlay {
|
||||
z-index: 1000;
|
||||
position: fixed;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div class="body-container">
|
||||
<div class="content">
|
||||
<div class="face_img" v-for="(item, index) in faceList" :key="index">
|
||||
<img :src="item.faceAddress" class="face_img" alt="">/>
|
||||
<img :src="'http://127.0.0.1:9300/'+item.faceAddress" class="face_img" alt="">/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -37,7 +37,7 @@ export default {
|
|||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang="scss">
|
||||
/* 隐藏滚动条 */
|
||||
.content::-webkit-scrollbar {
|
||||
display: none;
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
<div class="bottom-right-content">
|
||||
<img
|
||||
v-if="faceUrl"
|
||||
:src="faceUrl"
|
||||
:src="'http://127.0.0.1:9300/'+faceUrl"
|
||||
class="avatar"
|
||||
alt="Avatar"
|
||||
/>
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<addFace v-model="showModal" title="添加人脸" width="500px" @confirm="onConfirm">
|
||||
<addFace v-model="showModal" title="添加人脸" width="500px" @visible-change="handleVisibleChange">
|
||||
<p>这是弹窗的内容。</p>
|
||||
</addFace>
|
||||
-->
|
||||
|
|
@ -101,10 +101,9 @@ export default {
|
|||
},
|
||||
watch: {
|
||||
showModal(val) {
|
||||
if (!val){
|
||||
/* if (!val) {
|
||||
faceListShowPic.methods.getList();
|
||||
}
|
||||
|
||||
}*/
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -125,10 +124,9 @@ export default {
|
|||
},
|
||||
update() {
|
||||
this.showModal = true
|
||||
|
||||
},
|
||||
onConfirm() {
|
||||
|
||||
handleVisibleChange(newVal) {
|
||||
this.showModal = newVal;
|
||||
},
|
||||
selectFile() {
|
||||
this.$refs.fileInput.click();
|
||||
|
|
@ -144,7 +142,7 @@ export default {
|
|||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang="scss">
|
||||
|
||||
@keyframes moveandflash {
|
||||
0%, 100% {
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ export default {
|
|||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang="scss">
|
||||
#main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ module.exports = {
|
|||
['^' + process.env.VUE_APP_BASE_API]: ''
|
||||
}
|
||||
},
|
||||
"/dev-api/api": {
|
||||
"/api": {
|
||||
"target": "http://192.168.0.21:17861",
|
||||
//设置允许跨域——此处我经过测试发现可有可无
|
||||
"changeOrigin": true,
|
||||
|
|
|
|||
Loading…
Reference in New Issue