后期打算在小程序中添加即时聊天的功能,但是目前这个还没有考虑好以一种什么样的形势去实现,先接入一个腾讯AI提供的免费闲聊接口。先做一个大概的页面及功能。
腾讯AI地址:
https://ai.qq.com/doc/nlpchat.shtml#基础闲聊接口
有兴趣,可以试一试,当然,这种免费且不限次数的机器人肯定不会太聪明,期望值别太高。
我这里实现的效果是,可以发文字,表情,图片以及语音,,当然如果你还想要更多的功能,就需要自己去拓展了。
最后的效果:
1597366223668013.gif其余的就不多介绍了,感兴趣的同学可以自行研究下代码:
Aichat.wxml
<!-- 顶部title及返回键 -->
<view class="index" style="height:{{globalData.CustomBar}}px;padding-top: {{globalData.StatusBar}}px;">
<!-- 如果正在输入title变正在输入中 -->
<image catchtap="fanhui" mode="widthFix" class="index-img" src="/images/leftrow.png"></image><text>{{news?'正在输入中...':openid}}</text>
</view>
<view class="chat-index" style="margin-top:{{globalData.CustomBar}}px;height:calc(100vh - {{globalData.CustomBar}}px);">
<view wx:if="{{playing}}" style="position: fixed;top: 75px;" class="chat-cbins">正在播放语音</view>
<!-- 聊天列表 -->
<scroll-view bindtouchstart="scrollstart" style="height: calc(100% - {{chatheight?chatheight:'50'}}px);{{emojis || camera?'transition:all 0.5s;':'transition:all 0.1s;'}}" scroll-with-animation="{{animation}}" class="scroll-view" scroll-y="{{true}}" scroll-into-view="{{curr}}">
<!-- 循环消息列表 -->
<view wx:for="{{newsList}}" wx:key="index" class="news {{item.openid == openid?'contrary':''}}" id="jump{{index}}">
<!-- 用户头像 -->
<view class="mews-image">
<image src="{{item.logo}}" mode='aspectFill'></image>
</view>
<!-- 用户消息,消息分四种类型,语音,表情,图片,文字 -->
<!-- 当用户标识等于系统默认用户标识的时候 -->
<view class="news-centent {{item.openid == openid?'centent-right':'centent-left'}}" >
<view class="{{item.openid == openid?'trianright':'trianleft'}}"></view>
<text selectable="{{true}}" wx:if="{{item.news_type == 'text'}}" class="{{item.openid == openid?'background_right':'background_left'}}" >{{item.news_centent}}</text>
<image class="news-images" catchtap="picture" data-src="{{item.news_centent.src}}" style="{{item.news_centent.width <= 100?'width:'+item.news_centent.width*2+'px;height:'+item.news_centent.height*2+'px':''}}{{item.news_centent.width > 100 && item.news_centent.width < 400?'width:130px;height:'+item.news_centent.height/(item.news_centent.width / 130)+'px':''}}{{item.news_centent.width > 400 && item.news_centent.width <= 600?'width:'+item.news_centent.width/2.5+'px;height:'+item.news_centent.height/2.5+'px':''}}{{item.news_centent.width > 600 && item.news_centent.width <= 800?'width:'+item.news_centent.width/4+'px;height:'+item.news_centent.height/4+'px':''}}{{item.news_centent.width > 800?'width:150px;height:'+item.news_centent.height/(item.news_centent.width / 150)+'px':''}};" wx:if="{{item.news_type == 'image'}}" src="{{item.news_centent.src}}"></image>
<view class="news-voice" wx:if="{{item.news_type == 'voice'}}" bindtap="{{cuindex == index?'suspend':'play'}}" data-voice="{{item.news_centent.voice}}" data-index="{{index}}" >
<text wx:if="{{item.openid == openid}}">{{item.news_centent.dimen < '1'?'1':item.news_centent.dimen}}"</text>
<image src="/images/{{item.openid == openid?'voice-right':'voice-left'}}.png"></image>
<text wx:if="{{item.openid != openid}}">{{item.news_centent.dimen < '1'?item.news_centent.dimen:'1'}}"</text>
</view>
</view>
</view>
</scroll-view>
<!-- 底部功能栏 -->
<view class="chat-bottom" id="chat-height">
<!-- 点击语音功能 -->
<view class="chat-centent">
<view class="chat-pression">
<image wx:if="{{!voice}}" catchtap="speak" src="/images/voice.png"></image>
<image wx:if="{{voice}}" catchtap="speak" src="/images/keyboard.png"></image>
</view>
<view class="chat-tencvi" style="{{sendout?'width:67%;':''}}">
<textarea adjust-position="{{false}}" bindfocus="getkey" bindblur="getblur" wx:if="{{!voice}}" bindinput="moninput" value="{{news}}" maxlength="-1" auto-height />
<view bindtouchstart="touchdown" bindtouchend="touchup" wx:if="{{voice}}">按住 说话</view>
</view>
<view class="chat-pression">
<image wx:if="{{!emojis}}" catchtap="emoji" src="/images/emotion.png"></image>
<image wx:if="{{emojis}}" catchtap="emoji" src="/images/keyboard.png"></image>
</view>
<view class="chat-pression" wx:if="{{!sendout}}">
<image catchtap="camerax" src="/images/plus.png"></image>
</view>
<view class="chat-news" catchtap="message" style="{{sendout?'transition:all 0.5s;width:15%;':'width:0;position: absolute;right: 0;visibility: hidden;'}}">
<view>发送</view>
</view>
</view>
<!-- 点击表情图标 -->
<view class="chat-cube" wx:if="{{emojis}}">
<view class="cube-title">所有表情</view>
<view class="cube-centent">
<view catchtap="expression" data-item="{{item}}" wx:for="{{pression}}" wx:key="index">{{item}}</view>
</view>
</view>
<!-- 点击加号图标 -->
<view class="chat-camera" wx:if="{{camera}}">
<view wx:for="{{feature}}" wx:key="index" class="camera-feature" catchtap="featch" data-index="{{index}}">
<view class="feature-src">
<image src="{{item.src}}"></image>
</view>
<view class="feature-text">{{item.name}}</view>
</view>
</view>
</view>
</view>
<!-- 加载图标 -->
<view class="animation" wx:if="{{alcur}}">
<image src="/images/recording.gif"></image>
</view>
Aichat.js
var app = getApp()
Page({
data: {
// 全局变量,控制全局显示样式
globalData:{
StatusBar:'',
Custom:'',
CustomBar:'',
},
// 消息列表(存在缓存中)
newsList:[],
// 用户唯一标识
openid:'',
// 头像
figureurl_wx:'',
// 是否显示加载图标
animation:false,
// 是否是发送声音
voice:false,
// 是否发送完成
sendout:false,
// 是否发送照片
camera:false,
// 新消息
news:'',
// 表情数组
pression:[],
// 是否是发送表情
emojis:false,
// 聊天列表高度
curra:0,
// 功能相册图标
feature:[
{ src: '/images/album.png', name: '相册' }
],
// 软键盘是否关闭
alcur:false,
// 视野(软键盘高度)
vision:'',
// 定时器对象
time:'',
// 期间
duration:'',
// 是否被正在播放录音
playing:false,
//
cuindex:'',
// ai回答
aiAnswer:'',
},
/**
* 自定义导航栏,返回上一级
*/
fanhui:function(e){
// 清除聊天缓存
wx.removeStorage({
key: 'newsList',
success: function(res) {
},
});
wx.navigateBack({
delta: 1
});
},
/**
* 监听页面底部输入框
*/
cuinbut:function(e){
let that = this
setTimeout(function () {
wx.createSelectorQuery().select('#chat-height').boundingClientRect(function (rect) {
that.setData({
// 底部输入框的高度
chatheight: parseFloat(rect.height) + parseFloat(that.data.curra),
// 当前最后一条信息
curr: 'jump' + JSON.stringify(that.data.newsList.length - 1)
})
}).exec()
}, 100);
console.log(wx.getStorageSync('newsList'));
},
/**
* 生命周期----显示页面
*/
onShow:function(e){
// 页面赋值,最新一条数据索引标识
this.setData({
curr: 'jump' + JSON.stringify(this.data.newsList.length-1)
})
},
/**
* 生命周期加载页面
*/
onLoad: function (options) {
let that = this;
that.getUseridFromStorage();
// 获取系统信息
wx.getSystemInfo({
success: e => {
that.data.globalData.StatusBar = e.statusBarHeight;
let capsule = wx.getMenuButtonBoundingClientRect();
if (capsule) {
that.data.globalData.Custom = capsule;
that.data.globalData.CustomBar = capsule.bottom + capsule.top - (e.statusBarHeight + 15);
} else {
that.data.globalData.CustomBar = e.statusBarHeight + 50;
}
}
});
that.data.figureurl_wx = options.figureurl_wx;
that.setData({
// 用户唯一标识(这里是用户名)
openid:options.openid,
// 全局变量
globalData: that.data.globalData,
// 聊天记录存储在缓存中
newsList: wx.getStorageSync('newsList'),
// 表情列表
pression: app.emoji.pression,
// 录音
vision: wx.getRecorderManager(),
});
},
/**
* 从缓存中获取用户信息
*/
getUseridFromStorage:function()
{
var self = this;
// 从缓存中获取用户id
wx.getStorage({
key: 'userinfo',
success (res) {
self.data.user_id = res.data.id;
self.data.figureurl_wx = res.data.figureurl_wx;
self.data.openid = res.data.nickname;
}
});
},
/**
* 失去焦点事件
*/
getkey:function(e){
// 这个setdata的值没有用,wxml中无应用
this.setData({
curra: e.detail.height,
emojis: false,
camera: false
})
this.cuinbut();
},
/**
* 获取焦点事件
*/
getblur:function(e){
this.setData({
curra: 0,
emojis: false,
camera: false
})
this.cuinbut();
},
/**
* 获取input输入的消息
*/
moninput:function(e){
let that = this
that.cuinbut();
this.setData({
sendout: e.detail.value?true:false,
news: e.detail.value,
})
},
/**
* 滚动条开始滚动,收起表情列表以及功能列表
*/
scrollstart:function(e){
this.setData({
emojis: false,
camera: false,
})
this.cuinbut();
},
/**
* 点击语音图标时(收起功能栏)
*/
speak:function(e){
this.setData({
voice: !this.data.voice,
emojis:false,
camera:false,
})
this.cuinbut();
},
/**
* 点击表情栏时(收起功能栏)
*/
emoji:function(e){
this.setData({
emojis: !this.data.emojis,
voice:false,
camera:false,
})
this.cuinbut();
},
/**
* 点击功能栏时,收起声音和表情
*/
camerax:function(e){
this.setData({
camera: !this.data.camera,
voice: false,
emojis: false,
})
this.cuinbut();
},
/**
* 上传图片
*/
upload:function(e){
const that = this
// 微信选择图片
wx.chooseImage({
count: 1,
success: function (res) {
// 这是临时文件路径
const src = res.tempFilePaths[0];
// 获取图片信息
wx.getImageInfo({
src,
success: function (res) {
const { width, height } = res
const newChatList = [...that.data.newsList,{
news_type: 'image',
news_centent: { src, width, height },
openid: that.data.openid,
logo:that.data.figureurl_wx
}];
// 用户消息发送完成,调用接口,等待AI回话
that.aiChat('');
that.setData({
animation: true,
newsList: newChatList,
});
// 同步设置缓存
wx.setStorageSync('newsList', newChatList);
that.cuinbut();
// 用户消息发送完成,调用接口,等待AI回话
that.aiChat();
}
})
}
})
},
/**
* 点击看大图
*/
picture:function(e){
let src = e.currentTarget.dataset.src;
wx.previewImage({
current: src,
urls: [src]
})
},
/**
* 发送消息接口
*/
message:function(e){
let that = this
const newChatList = [...that.data.newsList, {
news_type: 'text',
news_centent: that.data.news,
openid: that.data.openid,
logo:that.data.figureurl_wx
}];
// 用户消息发送完成,调用接口,等待AI回话
that.aiChat(that.data.news);
that.setData({
animation: true,
newsList: newChatList,
sendout:false,
news: '',
})
wx.setStorageSync('newsList', newChatList);
that.cuinbut();
},
/**
* 选择表情
*/
expression:function(e){
let item = e.currentTarget.dataset.item
this.setData({
sendout: true,
news: this.data.news+=item,
})
},
/**
* 点击功能图标
*/
featch:function(e){
let that = this
let index = e.currentTarget.dataset.index
if(index == 0){
that.upload();
}
},
/**
* 按住说话
*/
touchdown: function (e) {
let that = this
wx.stopVoice();
that.setData({
alcur: true,
playing: false,
cuindex: ''
})
that.data.duration = 0
that.data.time = setInterval(function () {
that.setData({
duration: that.data.duration+1
})
},1000);
// 开始录音
wx.startRecord({
success: function (res) {
const newChatList = [...that.data.newsList, {
news_type: 'voice',
news_centent: { voice: res.tempFilePath, dimen: that.data.duration},
openid: that.data.openid,
logo:that.data.figureurl_wx
}];
// 用户消息发送完成,调用接口,等待AI回话
that.aiChat('');
that.setData({
newsList: newChatList,
})
wx.setStorageSync('newsList', newChatList);
that.cuinbut();
// 用户消息发送完成,调用接口,等待AI回话
that.aiChat();
},
fail:function(e){
that.touchup();
}
});
console.log(wx.getStorageSync('newsList'));
},
/**
* 录音完成
*/
touchup: function (e) {
let that = this
wx.stopRecord()
clearInterval(that.data.time)
that.setData({
alcur:false
})
},
/**
* 播放录音
*/
play: function (e) {
console.log('123456');
let that = this
let voice = e.currentTarget.dataset.voice
var index = e.currentTarget.dataset.index
if (!that.data.playing){
that.setData({
playing:true,
cuindex:index
})
wx.playVoice({
filePath: voice,
success: function () {
that.setData({
playing: false,
cuindex:''
})
},
})
}
},
/**
* 暂停录音
*/
suspend:function(e){
wx.stopVoice();
this.setData({
playing: false,
cuindex: ''
})
},
/**
* 聊天接口
* @param str 用户发的消息
*/
aiChat:function(str)
{
var self = this;
// 请求后台接口获取文章列表
wx.request({
// 请求连接
url: 'https://guanchao.site/index/im/tencentAI',
// 请求所需要的的参数
data: {
'str':str
},
success(result){
self.data.aiAnswer = result.data.data.answer;
if(result.data.msg != 'ok')
{
self.data.aiAnswer = "你的问题太深奥了,我没有办法回答~";
}
const newChatList = [...self.data.newsList, {
news_type: 'text',
news_centent: self.data.aiAnswer,
openid: 'ai',
logo:'https://guanchao.site/uploads/atricle/5c0a66cac42cb.jpeg'
}];
self.setData({
animation: true,
newsList: newChatList,
sendout:false,
news: '',
})
wx.setStorageSync('newsList', newChatList);
self.cuinbut();
}
});
},
clicks:function(){
console.log('12345');
}
})
Aichat.json
{
"usingComponents": {},
"navigationBarTitleText": "AI聊天",
"navigationStyle": "custom"
}
Aichat.wxss
page{
background-color: #EDEDED;
}
.index{
position:fixed;
width: 100%;
float: left;
height: 64px;
padding: 20rpx 220rpx 0 0;
box-shadow: 0rpx 0rpx 0rpx;
min-height: 0px;
display: flex;align-items: center;
background-color:#fff;
z-index:9999;
border-bottom: 1px solid #f7f7f7;
top: 0;
}
.index-img{
width: 34rpx;
margin-left: 20rpx;
}
.index>text{
margin-left: 20rpx;
font-size: 28rpx;
font-weight: bold;
}
.chat-index{
/* padding-top: 30px; */
width: 100%;
float: left;
overflow: hidden;
}
.scroll-view{
width: 100%;
float: left;
overflow: hidden;
background-color: #EDEDED;
}
.contrary{
display:flex;
flex-direction:row-reverse;
}
.news{
width: 94%;
float: left;
overflow: hidden;
padding: 0 3% 0 3%;
margin-bottom: 26rpx;
}
.news:first-child{
margin-top: 60rpx
}
.mews-image{
width: 70rpx;
height: 70rpx;
float: left;
}
.mews-image>image{
width: 100%;
height: 100%;
border-radius: 10rpx;
}
.news-centent{
width: 70%;
float: left;
overflow: hidden;
position: relative;
}
.centent-right{
margin-right: 10rpx;
padding: 0 15rpx 0 0;
}
.centent-left{
margin-left: 10rpx;
padding: 0 0 0 15rpx;
}
.trianright{
width:0;
height:0;
border-top:15rpx solid transparent;
border-bottom:15rpx solid transparent;
border-left:15rpx solid #FFFFFF;
position: absolute;
right: 0;
top: 20rpx;
}
.trianleft{
width:0;
height:0;
border-top:15rpx solid transparent;
border-bottom:15rpx solid transparent;
border-right:15rpx solid #95EB69;
position: absolute;
left: 0;
top: 20rpx;
}
.centent-right>text{
float: right;
}
.centent-right>.news-images{
float: right;
}
.centent-right>.news-voice{
float: right;
}
.centent-left>text{
float: left;
}
.centent-left>.news-images{
float: left;
}
.centent-left>.news-voice{
float: left;
}
.news-centent>text{
word-wrap:break-word;
word-break:normal;
padding: 16.5rpx;
/* background-color: #95EB69; */
color: #000200;
border-radius: 8rpx;
font-size: 28rpx;
max-width: 100%;
line-height: 42rpx;
}
.news-centent>.news-images{
max-width: calc(100%);
padding: 16.5rpx;
background-color: #95EB69;
border-radius: 8rpx;
}
.news-centent>.news-voice{
height: 70rpx;
background-color: #95EB69;
border-radius: 8rpx;
padding: 0 10rpx;
display: flex;
align-items: center;
}
.centent-left>.news-voice>text{
padding: 0 30rpx 0 15rpx;
font-size: 26rpx;
font-weight: bold;
}
.centent-right>.news-voice>text{
padding: 0 15rpx 0 30rpx;
font-size: 26rpx;
font-weight: bold;
}
.centent-left>.news-voice>image{
float: left;
}
.centent-right>.news-voice>image{
float: right;
}
.news-voice>image{
width: 50rpx;
height: 42rpx;
}
.chat-bottom{
width: 100%;
float: left;
overflow: hidden;
background-color: #F7F7F7;
transition:all 0.5s;
}
.chat-centent{
width: 100%;
float: left;
overflow: hidden;
position: relative;
padding: 0 1.5%;
}
.chat-pression{
width: 7%;
height: 100rpx;
display: flex;
align-items: center;
text-align: center;
float: left;
}
.chat-pression>image{
width: 45rpx;
height: 45rpx;
margin: 0 auto;
}
.chat-tencvi{
width: 73%;
padding: 0 1%;
float: left;
display: flex;
align-items: center;
transition:all 0.5s;
}
.chat-tencvi>textarea{
background-color: #fff;
border-radius: 8rpx;
width: 100%;
padding: 3%;
font-size: 28rpx;
margin: 20rpx 0;
max-height: 180rpx;
}
.chat-tencvi>view{
width: 100%;
padding: 3%;
margin: 20rpx 0;
background-color: #fff;
border-radius: 8rpx;
font-size: 26rpx;
text-align: center;
}
.chat-news{
height: 100rpx;
float: right;
display: flex;
align-items: center;
}
.chat-news>view{
width: 80%;
background-color: #07C160;
color: #fff;
border-radius: 8rpx;
height: 60rpx;
line-height: 60rpx;
font-size:26rpx;
text-align: center;
}
.chat-cube{
width: 100%;
float: left;
overflow: hidden;
height: 400rpx;
background-color: #EDEDED;
border-top: 1px solid #ddd;
padding:2% 0 8% 0;
overflow-y: auto;
}
.cube-title{
width: 100%;
float: left;
font-size: 24rpx;
padding:0 3.5%;
position: relative;
top: 10rpx;
}
.cube-centent{
width: 100%;
float: left;
overflow: hidden;
}
.cube-centent>view{
margin-top: 30rpx;
width: 12.5%;
float: left;
text-align: center;
}
.chat-camera{
width: 100%;
float: left;
overflow: hidden;
padding-bottom: 60rpx;
background-color: #EDEDED;
border-top: 1px solid #ddd;
overflow-y: auto;
}
.camera-feature{
margin: 60rpx 0 0 5%;
width: 18.75%;
float: left;
overflow: hidden;
text-align: center;
font-size: 20rpx;
}
.feature-src{
background-color: #fff;
border-radius: 15rpx;
float: left;
width: 80rpx;
height: 80rpx;
margin: 0 calc(50% - 40rpx);
text-align: center;
}
.feature-src>image{
width: 40rpx;
height: 40rpx;
margin:20rpx;
}
.feature-text{
width: 100%;
float: left;
margin-top: 10rpx;
}
.animation{
z-index: 10;
position: fixed;
width:300rpx;
height: 300rpx;
border-radius: 50%;
margin: calc((100vh - 300rpx)/2) calc((100% - 300rpx)/2);
}
.animation>image{
width: 100%;
height: 100%;
border-radius: 50%;
}
.animation::before {
content: "";
display: block;
background-color: #ccc;
filter: blur(10rpx);
position: absolute;
width: 100%;
height: 100%;
top: 10rpx;
left: 10rpx;
z-index: -1;
opacity: 0.4;
transform-origin: 0 0;
border-radius: inherit;
transform: scale(1, 1);
}
.chat-cbins{
z-index: 10;
width: 100%;
background-color: rgba(26, 26, 26, 0.6);
color: #fff;
padding: 3%;
font-size: 22rpx;
}
.background_left{
background-color: #95EB69;
}
.background_right{
background-color: #FFFFFF;
}
代码基本上就是这些了,其实没有多少东西,当然,就这么几行代码实现出来的效果肯定是有bug的,这个不要有太高的期望。
原文链接:https://guanchao.site/index/article/articledetail.html?artid=9om9oHCpe
有好的建议,请在下方输入你的评论。
网友评论