创建一个聊天窗口
- 先创建一个Model.vue 该文件作为弹窗的内容
前端代码如下
页面部分:
<template>
<div class="model-bg" v-show="show" @mousemove="modelMove" @mouseup="cancelMove">
<div class="model-container">
<div class="model-header" @mousedown="setStartingPoint">
{{ title }}
</div>
<div class="model-main" ref="box">
<div v-for="(item,i) in list" :key="i" :class="item.id== 2 ? 'atalk' : 'btalk'">
<span>{{ item.content }}</span>
</div>
</div>
<div></div>
<input type="text" v-model="wordone" class="inputword" @keyup.enter="sendmsg">
<el-button type="primary" :disabled="isButtonDisabled" round @click="sendmsg" class="btnsend">发送</el-button>
<div class="model-footer">
<el-button round @click="cancel">关闭</el-button>
</div>
<Loading></Loading>
</div>
</div>
</template>
- 分为两块:
- 上面的 model-main 为聊天信息的内容,其中根据list的id来判断聊天消息在左侧和在右侧
- 下面的 text 为聊天的按钮
js部分
<script>
import Loading from "@/views/Loading.vue";//引用loading组件
export default {
props: {
show: {
type: Boolean,
default: false
},
title: {
type: String,
default: '智能助手'
},
},
data() {
return {
x: 0,
y: 0,
node: null,
isCanMove: false,
isButtonDisabled: false,
typingSpeed: 100, // 每个字符之间的延迟时间(毫秒)
list: [
{
id: 1,
name: '111',
content: '你好',
},
{
id: 2,
name: '222',
content: '你好👋,我是你的智能小助手',
}
],
wordone: '',
wordtow: '',
}
},
components: {
Loading,
},
mounted() {
this.node = document.querySelector('.model-container')
},
methods: {
sendmsg() {
var $this = this;
if(this.isButtonDisabled == false){
this.list.push({ id: 1, name: 'sigtuna', content: this.wordone });
this.getBotContent($this);
}else{
return;
}
this.isButtonDisabled = true;
console.log(this.wordone);
this.wordone = '';
// console.log(this.list)
var div = this.$refs.box;
setTimeout(() => {
div.scrollTop = div.scrollHeight;
}, 0)
},
//模拟逐字进行返回数据
getChatContent(text) {
let i = 0;
this.timer = setInterval(() => {
if (i < text.length) {
this.list[this.list.length - 1].content += text.charAt(i);
i++;
var div = this.$refs.box;
setTimeout(() => {
div.scrollTop = div.scrollHeight;
}, 0)
} else {
clearInterval(this.timer); // 清除定时器
this.isButtonDisabled = false;
}
}, this.typingSpeed);
},
getBotContent() {
this.bus.$emit("loading", true);
this.list.push(
{ id: 2, name: 'kanade', content: '思考中...' }
);
this.$http({
url:"/AiController/query?content="+this.wordone,
method:"post"
})
.then(res => {
console.log(res);
if(res.data.code == 200){
var data = res.data.msg;
console.log(data);
this.list[this.list.length-1].content = '';
this.getChatContent(data);
var div = this.$refs.box;
setTimeout(() => {
div.scrollTop = div.scrollHeight;
}, 0)
}
this.bus.$emit("loading", false);
}).catch(err => {
this.loading = false;
this.isButtonDisabled = false;
this.$message.error(err.message);
});
},
cancel() {
this.$emit('cancel')
},
submit() {
this.$emit('submit')
},
setStartingPoint(e) {
this.x = e.clientX - this.node.offsetLeft
this.y = e.clientY - this.node.offsetTop
this.isCanMove = true
},
modelMove(e) {
if (this.isCanMove) {
this.node.style.left = e.clientX - this.x + 'px'
this.node.style.top = e.clientY - this.y + 'px'
}
},
cancelMove() {
this.isCanMove = false
},
}
}
</script>
点击发送,向后端发送请求,然后更新消息list
css部分
<style scoped>
.model-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .5);
z-index: 10;
}
.model-container {
background: #fff;
border-radius: 10px;
position: fixed;
top: 50%;
left: 50%;
width: 40%;
height: 80%;
transform: translate(-50%, -50%);
}
.model-header {
height: 56px;
background: #409EFF;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
cursor: move;
}
.model-footer {
display: flex;
align-items: center;
justify-content: center;
height: 57px;
border-top: 1px solid #ddd;
}
.model-footer button {
width: 100px;
}
.model-main {
width: 100%;
height: 70%;
overflow: auto;
}
.main {
height: 85%;
overflow: scroll;
}
div::-webkit-scrollbar {
display: none;
}
.atalk {
margin: 10px;
}
.atalk span {
display: inline-block;
padding: 3px 10px;
border: 1px solid aliceblue;
border-radius: 15px;
padding: 5px 10px;
}
.btalk {
text-align: right;
margin: 10px;
}
.btalk span {
display: inline-block;
padding: 3px 10px;
border: 1px solid aliceblue;
border-radius: 15px;
}
.sendbox {
height: 50px;
border-bottom-left-radius: 15px;
border-bottom-right-radius: 15px;
margin-top: 5px;
margin-left: 2%;
}
.inputword {
outline: none;
width: 85%;
height: 25px;
border-radius: 15px;
text-indent: 12px;
}
.btnsend {
width: 13%;
align-items: center;
justify-content: center;
}
</style>
通过想后端发送请求,可以实现在线聊天功能,也可以通过接入AI来实现客服对话功能
Loading
<template>
<div
class="loadingBox"
v-show="loading"
style="background-color: rgba(0, 0, 0, 0.5)"
>
<div class="sun-loading"></div>
</div>
</template>
<script>
export default {
name: "loading",
data() {
return {
loading: false,
};
},
created() {
var that = this;
this.bus.$on("loading", function (data) {
that.loading = !!data;
});
},
};
</script>
<style lang="scss" scoped>
.sun-loading {
width: 45px;
height: 45px;
display: block;
animation: sunLoading 1s steps(12, end) infinite;
background: transparent
url("http://www.sucaijishi.com/uploadfile/2018/0919/20180919030731920.gif?imageMogr2/format/jpg/blur/1x0/quality/60");
background-size: 100%;
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
</style>
在main.js里面添加
Vue.prototype.bus = new Vue();
后代码如下
service层
@Service
public class ApiService {
@Value("${ai-key}")
private String apiKey;
public String query(String queryMessage) {
Constants.apiKey = apiKey;
try {
Generation gen = new Generation();
MessageManager msgManager = new MessageManager(10);
Message systemMsg = Message.builder().role(Role.SYSTEM.getValue()).content("你是Hibiki开发的智能助手,你只回答与动漫的问题,不要回答其他问题!").build();
Message userMsg = Message.builder().role(Role.USER.getValue()).content(queryMessage).build();
msgManager.add(systemMsg);
msgManager.add(userMsg);
QwenParam param = QwenParam.builder().model(Generation.Models.QWEN_TURBO).messages(msgManager.get()).resultFormat(QwenParam.ResultFormat.MESSAGE).build();
GenerationResult result = gen.call(param);
GenerationOutput output = result.getOutput();
Message message = output.getChoices().get(0).getMessage();
return message.getContent();
} catch (Exception e) {
return "智能助手现在不在线,请稍后再试~";
}
}
}
通过阿里的通义千问大模型,可以实现在线的对话机器人功能
也能自定义模型的回答类型,非常实用
所需的依赖
<!--GSON -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dashscope-sdk-java</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
</exclusions>
<version>2.9.0</version>
</dependency>
<!--ok http client-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.10.0</version>
</dependency>
其中阿里的sdk会有依赖冲突,exclusion看情况添加
在所需页面里引入弹窗
<template>
<div class="navbar">
<div :style='{ "cursor": "pointer", "margin": "0 5px", "lineHeight": "44px", "color": "#fff" }' class="logout"
@click="diaglog">智能助手</div>
<Model :show="show" @cancel="cancel" @submit="submit"></Model>
</div>
</template>
<script>
import Model from '@/views/Model.vue'
export default {
data() {
return {
show: false,
};
},
created() {
},
components: {
Model
},
mounted() {
},
methods: {
cancel() {
// 取消弹窗回调
this.show = false
},
submit() {
// 确认弹窗回调
this.show = false
},
diaglog() {
this.show = true;
},
onLogout() {
let storage = this.$storage
let router = this.$router
storage.clear()
router.replace({
name: "login"
});
},
onIndexTap() {
window.location.href = `${this.$base.indexUrl}`
},
}
};
</script>
<style lang="scss" scoped>
.navbar {}
</style>