<template>
|
<div style="position: relative;height:calc(100vh - 20px);width: 100%;display: flex;justify-content: center">
|
<!-- 聊天消息列表 -->
|
<div class="chat-messages">
|
<div
|
v-for="(message, index) in messages"
|
:key="message + index"
|
:class="['message', message.role]"
|
@mouseover="handleMouseEnter(message)"
|
>
|
<div class="avatar">
|
<img :src="getAvatar(message.role)" alt="avatar" />
|
</div>
|
<div class="content">
|
<div class="text" :ref="'msg' + index">{{ message.content }}</div>
|
<div v-if="message.role === 'assistant'">
|
<div v-show="msgIndex === message" class="msg-op-list">
|
<el-button class="copy" v-show="false"/>
|
<el-tooltip class="item" effect="dark" content="复制" placement="top">
|
<i class="el-icon-copy-document msg-op msg-copy" @click="copyText(message.content)"/>
|
</el-tooltip>
|
<el-tooltip class="item" effect="dark" content="重新生成" placement="top">
|
<i class="el-icon-refresh msg-op msg-re" @click="reAnswer"/>
|
</el-tooltip>
|
</div>
|
</div>
|
</div>
|
<div v-if="message.role === 'user'">
|
<div v-show="msgIndex === message" style="display: flex">
|
<el-tooltip class="item" effect="dark" content="复制" placement="top">
|
<i class="el-icon-copy-document msg-op msg-copy" @click="copyText(message.content)"/>
|
</el-tooltip>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<!-- 输入框 -->
|
<div class="chat-input">
|
<div style="position: relative;width: 100%;box-sizing: border-box;">
|
<el-input
|
v-model="inputMessage"
|
type="textarea"
|
resize="none"
|
:autofocus="true"
|
:autosize="true"
|
placeholder="请输入消息,按下回车发送"
|
@keyup.native.enter="sendMessage"
|
@keydown.enter.native.prevent="handleEnterKey"
|
>
|
</el-input>
|
<div class="send-op-list">
|
<div class="send-warp">
|
<div class="add-option">
|
<div :class="{'add-but': true, 'add-but-active': netSearchEnable}" @click="changeNetEnable">
|
<svg t="1743144032689" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2731" width="18" height="18">
|
<path fill="#575757" d="M909.989 343.281c-21.76-51.446-52.904-97.643-92.568-137.307-39.663-39.664-85.86-70.808-137.306-92.568-53.28-22.536-109.858-33.962-168.164-33.962S397.067 90.87 343.787 113.406c-51.446 21.76-97.643 52.904-137.307 92.568-39.664 39.664-70.808 85.86-92.568 137.307-22.536 53.28-33.962 109.858-33.962 168.164s11.426 114.884 33.962 168.164c21.76 51.445 52.904 97.643 92.568 137.306 39.664 39.664 85.86 70.809 137.307 92.568 53.28 22.535 109.858 33.962 168.164 33.962s114.884-11.427 168.164-33.962c51.445-21.76 97.643-52.904 137.306-92.568 39.664-39.663 70.809-85.86 92.568-137.306 22.535-53.28 33.962-109.858 33.962-168.164s-11.427-114.884-33.962-168.164zM543.951 376.03c17.57-0.243 35.095-0.683 52.536-1.368a1990.762 1990.762 0 0 0 91.131-5.678c10.157 34.378 16.643 71.314 19.389 110.46H543.951V376.03z m0-64.069V163.41c17.617 12.176 39.372 29.845 61.074 54.197 23.42 26.28 43.109 56.138 58.784 89.019-41.937 3.147-82.117 4.783-119.858 5.335z m-64-144.763v144.705a2038.572 2038.572 0 0 1-103.41-4.309 1929.6 1929.6 0 0 1-11.183-0.786c15.195-31.915 34.175-60.991 56.676-86.711 20.284-23.185 40.722-40.49 57.917-52.899zM370.842 371.354a2124.301 2124.301 0 0 0 109.109 4.606v103.484H322.25c2.742-39.079 9.208-75.96 19.335-110.285 9.453 0.776 19.21 1.512 29.257 2.195z m-112.731 108.09H145.342c3.916-45.757 16.208-89.778 36.139-130.276 20.257 3.578 52.836 8.759 95.407 13.641-9.902 36.699-16.182 75.682-18.777 116.635z m-0.859 64c1.975 39.144 9.712 78.242 23.101 116.799-44.093 4.976-77.797 10.319-98.621 13.992-20.08-40.64-32.458-84.841-36.39-130.792h111.91z m64.098 0h158.601v104.048c-36.37 0.602-72.818 2.13-109.109 4.601-8.41 0.573-16.612 1.182-24.609 1.821-14.322-36.523-22.659-73.543-24.883-110.47z m158.601 168.103v131.712c-16.974-15.877-36.574-35.962-56.038-59.735-18.064-22.064-33.892-44.698-47.379-67.668a2038.38 2038.38 0 0 1 103.417-4.309z m64 136.665V711.487c34.387 0.503 70.782 1.903 108.68 4.527-13.467 22.915-29.263 45.496-47.286 67.509-21.547 26.32-43.262 48.119-61.394 64.689z m52.536-199.426a2063.187 2063.187 0 0 0-52.536-1.384V543.444h163.958c-2.227 36.975-10.582 74.045-24.939 110.615a1994.972 1994.972 0 0 0-86.483-5.273z m175.521-105.342H878.56c-3.928 45.898-16.283 90.051-36.322 130.652a1822.84 1822.84 0 0 0-93.476-13.439c13.478-38.691 21.264-77.929 23.246-117.213z m-0.859-64c-2.605-41.111-8.924-80.238-18.891-117.061a1821.491 1821.491 0 0 0 90.233-13.075c19.89 40.46 32.158 84.432 36.07 130.136H771.149z m1.017-228.215a373.188 373.188 0 0 1 34.046 39.155 1772.853 1772.853 0 0 1-75.117 9.98c-19.529-46.785-45.825-88.91-78.291-125.338a423.863 423.863 0 0 0-5.386-5.927c46.185 18.263 88.573 45.955 124.748 82.13zM384.81 165.923a420.788 420.788 0 0 0-8.355 9.102c-32.552 36.525-58.904 78.775-78.449 125.71-32.357-3.484-59.48-7.244-80.226-10.469a373.386 373.386 0 0 1 33.956-39.037c38.338-38.338 83.654-67.152 133.074-85.306zM251.736 771.659a373.444 373.444 0 0 1-33.584-38.548c22.526-3.498 52.529-7.614 88.626-11.328 18.229 35.542 41.31 70.356 68.867 103.808a678.068 678.068 0 0 0 35.727 39.995c-59.757-16.875-114.524-48.814-159.636-93.927z m368.249 91.745a677.032 677.032 0 0 0 33.629-37.814c27.469-33.344 50.487-68.043 68.689-103.466a1780.315 1780.315 0 0 1 83.528 10.88 373.34 373.34 0 0 1-33.665 38.654c-43.23 43.232-95.324 74.373-152.181 91.746z" p-id="2732"></path></svg>
|
联网搜索
|
</div>
|
</div>
|
<div class="send-but-warp">
|
<div class="send-but" @click="sendMessage">
|
<svg t="1743144485359" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1261" width="36" height="36">
|
<path fill="#409eff" d="M512 85.333333c234.666667 0 426.666667 192 426.666667 426.666667s-192 426.666667-426.666667 426.666667S85.333333 746.666667 85.333333 512 277.333333 85.333333 512 85.333333z m-6.4 234.666667c-4.266667 2.133333-6.4 2.133333-12.8 8.533333l-153.6 153.6c-12.8 12.8-12.8 32 0 44.8 12.8 12.8 32 12.8 44.8 0l96-96V682.666667c0 17.066667 14.933333 32 32 32s32-14.933333 32-32V430.933333l96 96c12.8 12.8 32 12.8 44.8 0s12.8-32 0-44.8l-153.6-153.6c-6.4-6.4-8.533333-8.533333-12.8-8.533333s-8.533333-2.133333-12.8 0z" p-id="1262"></path>
|
</svg>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import ClipboardJS from 'clipboard';
|
import {sendKbMsg} from "@/api/chat";
|
import {Message} from "element-ui";
|
|
export default {
|
name: 'AiChat',
|
data() {
|
return {
|
netSearchEnable: false,
|
messages: [
|
{
|
role: 'assistant',
|
content: '你好!我是你的 AI 助手,有什么可以帮你的吗?',
|
},
|
{
|
role: 'user',
|
content: '你好!我是你的 AI 助手,有什么可以帮你的吗?',
|
},
|
],
|
inputMessage: '',
|
sendMsgForm: {
|
query: "",
|
mode: "local_kb",
|
kb_name: "samples",
|
top_k: 3,
|
score_threshold: 2,
|
history: [
|
{
|
"content": "我们来玩成语接龙,我先来,生龙活虎",
|
"role": "user"
|
},
|
{
|
"content": "虎头虎脑",
|
"role": "assistant"
|
}
|
],
|
stream: true,
|
model: "qwen:7b",
|
temperature: 0.7,
|
max_tokens: 0,
|
prompt_name: "default",
|
return_direct: false
|
},
|
msgIndex: null,
|
msgRole: null
|
};
|
},
|
methods: {
|
changeNetEnable() {
|
this.netSearchEnable = ! this.netSearchEnable
|
},
|
// 阻止回车换行
|
handleEnterKey(e) {
|
e.preventDefault(); // 阻止默认换行行为
|
},
|
handleMouseEnter(msgIndex) {
|
console.log("鼠标移入", msgIndex)
|
this.msgIndex = msgIndex
|
},
|
handleMouseLeave() {
|
console.log("鼠标移除")
|
this.msgIndex = null
|
},
|
// 重新生成
|
reAnswer() {
|
},
|
// 复制内容
|
copyText(content) {
|
const clipboard = new ClipboardJS('.copy', {
|
text: () => content
|
});
|
// 触发复制(需要一个隐藏的按钮)
|
document.querySelector('.copy').click();
|
clipboard.destroy();
|
Message.success("复制成功")
|
},
|
// 获取角色头像
|
getAvatar(role) {
|
const avatars = {
|
user: 'https://picsum.photos/200',
|
assistant: 'https://picsum.photos/200',
|
};
|
return avatars[role];
|
},
|
|
// 获取角色名称
|
getRoleName(role) {
|
const roleNames = {
|
user: '用户',
|
assistant: 'AI 助手',
|
};
|
return roleNames[role];
|
},
|
|
// 发送消息
|
async sendMessage() {
|
if (this.inputMessage.trim() === '') return;
|
|
// 添加用户消息
|
this.messages.push({
|
role: 'user',
|
content: this.inputMessage,
|
});
|
this.sendMsgForm.query = this.inputMessage
|
// 清空输入框
|
this.inputMessage = '';
|
|
sendKbMsg(this.sendMsgForm).then(res => {
|
console.log(res, "拿到回复了!!")
|
this.messages.push({
|
role: 'assistant',
|
content: '这是一条 AI 回复。',
|
});
|
})
|
},
|
},
|
};
|
</script>
|
|
<style scoped>
|
.add-but {
|
width: 90px;
|
height: 80%;
|
border: 1px solid #E4E7ED;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
border-radius: 8px;
|
background-color: #DCDFE6;
|
}
|
.add-but:hover {
|
cursor: pointer;
|
background-color: #409EFF;
|
}
|
.add-but-active {
|
background-color: #409EFF;
|
}
|
.send-but-warp {
|
flex:1;
|
display: flex;
|
justify-content: flex-end;
|
align-items: center;
|
}
|
.send-but:hover {
|
cursor: pointer;
|
}
|
.ai-chat-dialog {
|
display: flex;
|
flex-direction: column;
|
border-radius: 8px;
|
overflow: hidden;
|
}
|
|
.chat-messages {
|
padding: 16px;
|
margin-top: 14px;
|
overflow-y: auto;
|
width: 800px;
|
height: 680px;
|
}
|
|
.message {
|
display: flex;
|
margin-bottom: 18px;
|
min-height: 75px;
|
}
|
|
.message.user {
|
align-items: center;
|
flex-direction: row-reverse;
|
}
|
|
.avatar img {
|
width: 40px;
|
height: 40px;
|
border-radius: 50%;
|
}
|
|
.content {
|
max-width: 70%;
|
margin: 0 12px;
|
padding: 8px 12px;
|
border-radius: 8px;
|
background-color: #fff;
|
/*box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);*/
|
}
|
|
.message.user .content {
|
background-color: #409eff;
|
color: #fff;
|
}
|
|
.name {
|
font-size: 12px;
|
color: #666;
|
margin-bottom: 4px;
|
}
|
|
.message.user .name {
|
color: #fff;
|
}
|
|
.text {
|
font-size: 14px;
|
}
|
|
.chat-input {
|
padding: 16px;
|
background-color: #fff;
|
width: 800px;
|
display: flex;
|
flex-direction: row;
|
justify-content: center;
|
align-items: center;
|
position: absolute;
|
bottom: 50px;
|
left: 50%; /* 将 div 的左边移动到父容器的 50% 位置 */
|
transform: translateX(-50%); /* 将 div 向左移动自身宽度的 50% */
|
}
|
|
.msg-op-list {
|
margin-top: 15px;
|
}
|
.msg-copy {
|
font-size: 18px;
|
}
|
.msg-re {
|
font-size: 20px;
|
}
|
.msg-op {
|
color: gray;
|
margin-right: 5px;
|
}
|
.msg-op:hover {
|
cursor: pointer;
|
}
|
|
.send-op-list {
|
position: absolute;
|
bottom: 0px;
|
width: 100%;
|
height: 40px;
|
display: flex;
|
align-items: center;
|
user-select: none;
|
opacity: 1;
|
z-index: 2;
|
}
|
|
.send-warp {
|
display: flex;
|
width: 100%;
|
height: 100%;
|
padding: 0 15px;
|
box-sizing: border-box
|
}
|
|
.add-option {
|
flex:1;
|
display: flex;
|
justify-content: flex-start;
|
}
|
|
::v-deep(.el-textarea__inner) {
|
min-height: 100px !important;
|
max-height: 400px !important;
|
padding-bottom: 85px !important;
|
}
|
</style>
|