微信小程序聊天功能的示例代码
程序员文章站
2022-03-25 15:49:18
效果
初始化滚动条高度
var keyheight = 0;
数据格式
const chat_data=[
{
type:0,//...
效果
初始化滚动条高度
var keyheight = 0;
数据格式
const chat_data=[ { type:0,//0客服1用户 content:'欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎欢迎', headimg:'../../assets/common/images/headhortrait.jpeg',//头像 creattime:'2019-01-01',//创建时间 contenttype:'text' }, { type: 0,//0客服1用户 content: '1111111', headimg: '../../assets/common/images/headhortrait.jpeg',//头像 creattime: '2019-01-01',//创建时间 contenttype: 'text' }, { type: 1,//0客服1用户 content: '222222', headimg: '../../assets/common/images/headhortrait.jpeg',//头像 creattime: '2019-01-01',//创建时间 contenttype: 'text' }, { type: 0,//0客服1用户 content: '333333', headimg: '../../assets/common/images/headhortrait.jpeg',//头像 creattime: '2019-01-01',//创建时间 contenttype: 'text' }, { type: 1,//0客服1用户 content: '4444444', headimg: '../../assets/common/images/headhortrait.jpeg',//头像 creattime: '2019-01-01',//创建时间 contenttype: 'text', }, { type: 0,//0客服1用户 content: 'http://tmp/wxc79c66d8b0ed19a8.o6zajs6qe8l8fkq645ts4e3lokzi.pgakavhkmbq3160aa57e2bf33cb576fbabf691cd890b.durationtime=3706.aac', headimg: '../../assets/common/images/headhortrait.jpeg',//头像 creattime: '2019-01-01',//创建时间 contenttype: 'voice', duration: '3706', },{ type: 1,//0客服1用户 content: 'http://tmp/wxc79c66d8b0ed19a8.o6zajs6qe8l8fkq645ts4e3lokzi.pgakavhkmbq3160aa57e2bf33cb576fbabf691cd890b.durationtime=3706.aac', headimg: '../../assets/common/images/headhortrait.jpeg',//头像 creattime: '2019-01-01',//创建时间 contenttype: 'voice', duration:'3706' }, { type: 1,//0客服1用户 content: 'https://img.yzcdn.cn/vant/cat.jpeg', headimg: '../../assets/common/images/headhortrait.jpeg',//头像 creattime: '2019-01-01',//创建时间 contenttype: 'img' }, { type: 1,//0客服1用户 content: 'https://img.yzcdn.cn/vant/cat.jpeg', headimg: '../../assets/common/images/headhortrait.jpeg',//头像 creattime: '2019-01-01',//创建时间 contenttype: 'img' } ];
wxml对话框
<block wx:key wx:for='{{chatdata}}' wx:for-index="index"> <!-- 单个消息1 客服发出(左) --> <view wx:if='{{item.type==0}}' id='msg-{{index}}' class="contentleft" style=''> <view class="head"> <image class="headimg" src='{{item.headimg}}' mode='widthfix'></image> </view> <view class='leftmsg' wx:if="{{item.contenttype==='text'}}">{{item.content}}</view> <view class='leftmsg' wx:if="{{item.contenttype==='voice'}}" data-duration="{{item.content}}">{{item.duration}}s</view> <view class='leftmsg img' wx:if="{{item.contenttype==='img'}}"><image src="{{item.content}}" data-src="{{item.content}}" mode='widthfix' bindtap="onpreview"/></view> </view> <!-- 单个消息2 用户发出(右) --> <view wx:else id='msg-{{index}}' class="contentright"> <view class='rightmsg' wx:if="{{item.contenttype==='voice'}}" data-duration="{{item.duration}}" bindtap="playrecord" style="width:{{30+item.duration*5}}px;justify-content: flex-end;display:flex;color:#000;">{{item.duration}}"<van-icon name="../../../assets/common/icon/voice-r.png" style="padding:0 0 0 5px;" /></view> <view class='rightmsg' wx:if="{{item.contenttype==='text'}}">{{item.content}}</view> <view class='rightmsg img' wx:if="{{item.contenttype==='img'}}"><image src="{{item.content}}" data-src="{{item.content}}" mode='widthfix' bindtap="onpreview"/></view> <view> <image class="headimg" src='{{item.headimg}}' mode='widthfix'></image> </view> </view> </block>
wxml底部输入框
<view class='inputroom' style='bottom: {{inputbottom}};height: {{bottomheight}}'> <van-row class="bottomrow"> <van-col span="2"wx:if="{{show}}"> <van-icon bindtap="startrecord" class="iconfont icon" class-prefix='icon' size="40rpx" name="yuyin" ></van-icon></van-col> <van-col span="2" wx:if="{{!show}}"> <van-icon bindtap="startrecord" class="iconfont icon" class-prefix='icon' size="40rpx" name="fabiaowenzhang" ></van-icon></van-col> <van-col span="18" wx:if="{{show}}"> <input bindconfirm='sendclick'bind:input="inputvalue" adjust-position='{{false}}' value='{{inputval}}' confirm-type='send' bindfocus='focus' bindblur='blur'></input></van-col> <van-col span="18" wx:if="{{!show}}"> <view class="holdtape" bind:touchstart="starttalk" bind:touchend='stoprecord'>按住请说话</view></van-col> <van-col span="2"><van-icon class="iconfont icon" class-prefix='icon' size="40rpx" name='biaoqing' bindtap="getemoji"></van-icon></van-col> <van-col span="2"><van-uploader use-slot accept='image' bind:after-read="uploadeimg"> <van-icon class="iconfont icon" class-prefix='icon' size="40rpx" name='icon02' ></van-icon></van-uploader></van-col> </van-row> <view wx:if="{{showemoji}}" class="emoji"> <emoji bind:clickemoji="clickemoji" data-key="inputval" value="{{inputval}}" /> </view> </view> </view> <view class="recorddailog" wx:if="{{showdailog}}" > <view class="show"> <image src="../../assets/common/images/record.png"></image> <text>{{toasttitle}}</text> </view>
css
#page{ height: 90%; overflow-y: auto; } .content{ background: white; } .inputroom { width: 100vw; /* height: 16vw; */ border-top: 1px solid #cdcdcd; position: fixed; bottom: 0; display: flex; align-items: center; z-index: 20; background: white; flex-direction: column; } .bottomrow{ width: 100%; height: 16vw; display: flex; align-items: center; flex-direction: row } .bottomrow .van-row{ width: 100%; } .emoji{ height: 30vw; } input { width: 90%; height: 9.33vw; background-color: #eef4fa; border-radius: 6rpx; font-size: 28rpx; color: #444; padding: 0 3%; margin-left: 2%; } .leftmsg { font-size: 26rpx; color: #333333; line-height: 6vw; padding: 2vw 2.5vw; background-color: #eef4fa; border-radius: 10rpx; z-index: 10; } .rightmsg { font-size: 26rpx; color: white; line-height: 6vw; padding: 2vw 2.5vw; background-color: #496dff; border-radius: 10rpx; z-index: 10; } .chatframe{ background: white; height: 100% } .icon{ line-height: 8vw; } .head{ display: flex; align-items: center } .headimg{ border-radius: 50%; width: 60rpx;height: 60rpx; } .holdtape{ width: 90%; height: 9.33vw; background-color: #eef4fa; border-radius: 6rpx; padding: 0 3%; margin-left: 2%; display: flex; align-items: center; justify-content: center; } .recorddailog{ -webkit-transition-duration: 300ms; transition-duration: 300ms; z-index: 1000; position: fixed; top: 50%; left: 50%; width: -webkit-fit-content; width: fit-content; -webkit-transform: translate(-50%,-50%); transform: translate(-50%,-50%); max-width: var(--toast-max-width,70%); } .show{ width: var(--toast-default-width,90px); min-height: var(--toast-default-min-height,90px); padding: var(--toast-default-padding,16px); display: flex; -webkit-flex-direction: column; flex-direction: column; -webkit-align-items: center; align-items: center; -webkit-justify-content: center; justify-content: center; box-sizing: initial; color: var(--toast-text-color,#fff); font-size: var(--toast-font-size,14px); line-height: var(--toast-line-height,20px); white-space: pre-wrap; word-wrap: break-word; background-color: var(--toast-background-color,rgba(50,50,51,.88)); border-radius: var(--toast-border-radius,4px); } .show image{ width: 24px; height: 24px } image{ /* max-width: 88vw; max-height: 400rpx; */ width: 100% } .img{ background: none; width: 90% } .contentright{ display: flex; justify-content: flex-end; padding: 2vw 2vw 2vw 11vw;width: 86%; } .contentleft{ display: flex; padding: 2vw 11vw 2vw 2vw;width: 86%; }
js
// pages/contact/contact.js const { pagefunc } = require('../../utils/util.js'); const app = getapp(); var windowwidth = wx.getsysteminfosync().windowwidth; var windowheight = wx.getsysteminfosync().windowheight; var keyheight = 0; const { chat_data}=require("../../data/customerservice.js"); import toast from '../../components/vant/toast/toast'; const recordermanager = wx.getrecordermanager(); const inneraudiocontext = wx.createinneraudiocontext(); const db = wx.cloud.database(); /** * 初始化数据 */ /** * 计算msg总高度 */ function calscrollheight(that, keyheight) { var query = wx.createselectorquery(); query.select('.scrollmsg').boundingclientrect(function(rect) { }).exec(); } page({ /** * 页面的初始数据 */ data: { scrollheight: '100vh', inputval:"", inputbottom: 0, chatdata:[], show:true, showdailog:false, bottomheight:"18vw", senddata:{}, pagination: { pagesize: 5, currentpage: 1, total: 0, }, showemoji:false, toasttitle:"录音中...." }, /** * 生命周期函数--监听页面加载 */ onload: function (options) { // this.setdata({ // cusheadicon: app.globaldata.userinfo.avatarurl, // }); const { pagination } = this.data this.getdata({ param: chat_data, pagination }); wx.pagescrollto({ scrolltop: 1000 }) }, getdata(params) { const { chatdata } = this.data; const { param, pagination: { pagesize = 10, currentpage = 1 }, } = params; this.setdata({ pagination: { pagesize, currentpage } }); const { data, pagination } = pagefunc(param, currentpage, pagesize); data.foreach((item) => { if (item.duration) { item.duration = math.ceil(item.duration / 1000) } }); this.setdata({ 'chatdata': data.concat(chatdata) }); }, startrecord(){//开始录音 const {show}=this.data if (show){ this.setdata({ show: false, }) }else{ this.setdata({ show: true, }) } }, starttalk(e){//开始说话 this.setdata({ showdailog:true, }) const options = { duration: 60000, samplerate: 44100, numberofchannels: 1, encodebitrate: 192000, format: 'aac', framesize: 50 } recordermanager.start(options) recordermanager.onstart((res) => { }) }, stoprecord(){//停止说话 const that=this this.setdata({ showdailog:false, }) recordermanager.stop(); recordermanager.onstop((res) => { const { senddata, chatdata } = that.data; let { tempfilepath, duration, filesize} = res senddata.tempfilepathdata=res duration = math.ceil(duration / 1000) const data = { content: tempfilepath, duration , filesize, contenttype: 'voice', type: 1}; chatdata.push(data); wx.createselectorquery().select('.content').boundingclientrect(function (rect) { // 使页面滚动到底部 wx.pagescrollto({ scrolltop: rect.bottom + 5000 }) }).exec(); that.setdata({ 'tempfilepath': tempfilepath, senddata, chatdata, scrollheight: (windowheight - 0) + 'px', toview: 'msg-' + (chatdata.length - 1), inputbottom: '0px' }) }) }, playrecord(e){//播放语音 const { currenttarget: { dataset: { duration } }}=e; const { tempfilepath} = this.data inneraudiocontext.autoplay = true; inneraudiocontext.src = tempfilepath , inneraudiocontext.onplay(() => { this.setdata({ toasttitle: "播放中....", showdailog: true, }) }) inneraudiocontext.onended((res) => { this.setdata({ toasttitle: "录音中....", showdailog: false, }) }) inneraudiocontext.onerror((res) => { }); inneraudiocontext.play() }, uploadeimg(e){ const { file: { path,size:filesize} } = e.detail; const { chatdata } = this.data; const data = { content: path, filesize, contenttype: 'img', type: 1 }; chatdata.push(data); wx.createselectorquery().select('.content').boundingclientrect(function (rect) { // 使页面滚动到底部 wx.pagescrollto({ scrolltop: rect.bottom + 5000 }) }).exec() this.setdata({ chatdata, scrollheight: (windowheight - 0) + 'px', toview: 'msg-' + (chatdata.length - 1), inputbottom:'0px' }) }, getemoji(){//获取表情包 wx.createselectorquery().select('.content').boundingclientrect(function (rect) { // 使页面滚动到底部 wx.pagescrollto({ scrolltop: rect.bottom + 5000 }) }).exec() this.setdata({ showemoji:true, bottomheight:"48vw" }) }, clickemoji: function (e) {//选择表情包 const { detail: { value }, currenttarget: { dataset: { key } } } = e; this.setdata({ [key]: value }) }, onpreview(e){ const { currenttarget: { dataset: { src } } } = e; const urls = [src] wx.previewimage({ current: src, urls }) }, /** * 生命周期函数--监听页面显示 */ onshow: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onpulldownrefresh: function () { let { pagination: { currentpage } } = this.data this.getdata({ param: chat_data, pagination: { pagesize: 5, currentpage: currentpage + 1, } }); }, /** * 页面上拉触底事件的处理函数 */ onreachbottom: function () { }, /** * 获取聚焦 */ inputvalue(e){ const {detail:{value}}=e this.setdata({ inputval:value }) }, focus: function (e) { const { chatdata}=this.data keyheight = e.detail.height; wx.pagescrollto({ scrolltop: windowheight - keyheight }) this.setdata({ toview: 'msg-' + (chatdata.length - 1), inputbottom: keyheight + 'px', scrollheight: (windowheight - keyheight) + 'px', showemoji:false, bottomheight: "18vw" }) //计算msg高度 // calscrollheight(this, keyheight); }, //失去聚焦(软键盘消失) blur: function (e) { const { chatdata } = this.data this.setdata({ scrollheight: '100vh', inputbottom: 0 }) this.setdata({ toview: 'msg-' + (chatdata.length - 1) }) }, /** * 发送点击监听 */ sendclick: function (e) { const { chatdata, scrollheight}=this.data; wx.createselectorquery().select('.content').boundingclientrect(function (rect) { // 使页面滚动到底部 wx.pagescrollto({ scrolltop: rect.bottom + 5000 }) }).exec() chatdata.push({ type: 1, contenttype: 'text', content: e.detail.value, headimg: '../../assets/common/images/headhortrait.jpeg', }) this.setdata({ chatdata, inputval:'' }); }, /** * 退回上一页 */ tobackclick: function () { wx.navigateback({}) } })// pages/contact/contact.js const { pagefunc } = require('../../utils/util.js'); const app = getapp(); var windowwidth = wx.getsysteminfosync().windowwidth; var windowheight = wx.getsysteminfosync().windowheight; var keyheight = 0; const { chat_data}=require("../../data/customerservice.js"); import toast from '../../components/vant/toast/toast'; const recordermanager = wx.getrecordermanager(); const inneraudiocontext = wx.createinneraudiocontext(); const db = wx.cloud.database(); /** * 初始化数据 */ /** * 计算msg总高度 */ function calscrollheight(that, keyheight) { var query = wx.createselectorquery(); query.select('.scrollmsg').boundingclientrect(function(rect) { }).exec(); } page({ /** * 页面的初始数据 */ data: { scrollheight: '100vh', inputval:"", inputbottom: 0, chatdata:[], show:true, showdailog:false, bottomheight:"18vw", senddata:{}, pagination: { pagesize: 5, currentpage: 1, total: 0, }, showemoji:false, toasttitle:"录音中...." }, /** * 生命周期函数--监听页面加载 */ onload: function (options) { // this.setdata({ // cusheadicon: app.globaldata.userinfo.avatarurl, // }); const { pagination } = this.data this.getdata({ param: chat_data, pagination }); wx.pagescrollto({ scrolltop: 1000 }) }, getdata(params) { const { chatdata } = this.data; const { param, pagination: { pagesize = 10, currentpage = 1 }, } = params; this.setdata({ pagination: { pagesize, currentpage } }); const { data, pagination } = pagefunc(param, currentpage, pagesize); data.foreach((item) => { if (item.duration) { item.duration = math.ceil(item.duration / 1000) } }); this.setdata({ 'chatdata': data.concat(chatdata) }); }, startrecord(){//开始录音 const {show}=this.data if (show){ this.setdata({ show: false, }) }else{ this.setdata({ show: true, }) } }, starttalk(e){//开始说话 this.setdata({ showdailog:true, }) const options = { duration: 60000, samplerate: 44100, numberofchannels: 1, encodebitrate: 192000, format: 'aac', framesize: 50 } recordermanager.start(options) recordermanager.onstart((res) => { }) }, stoprecord(){//停止说话 const that=this this.setdata({ showdailog:false, }) recordermanager.stop(); recordermanager.onstop((res) => { const { senddata, chatdata } = that.data; let { tempfilepath, duration, filesize} = res senddata.tempfilepathdata=res duration = math.ceil(duration / 1000) const data = { content: tempfilepath, duration , filesize, contenttype: 'voice', type: 1}; chatdata.push(data); wx.createselectorquery().select('.content').boundingclientrect(function (rect) { // 使页面滚动到底部 wx.pagescrollto({ scrolltop: rect.bottom + 5000 }) }).exec(); that.setdata({ 'tempfilepath': tempfilepath, senddata, chatdata, scrollheight: (windowheight - 0) + 'px', toview: 'msg-' + (chatdata.length - 1), inputbottom: '0px' }) }) }, playrecord(e){//播放语音 const { currenttarget: { dataset: { duration } }}=e; const { tempfilepath} = this.data inneraudiocontext.autoplay = true; inneraudiocontext.src = tempfilepath , inneraudiocontext.onplay(() => { this.setdata({ toasttitle: "播放中....", showdailog: true, }) }) inneraudiocontext.onended((res) => { this.setdata({ toasttitle: "录音中....", showdailog: false, }) }) inneraudiocontext.onerror((res) => { }); inneraudiocontext.play() }, uploadeimg(e){ const { file: { path,size:filesize} } = e.detail; const { chatdata } = this.data; const data = { content: path, filesize, contenttype: 'img', type: 1 }; chatdata.push(data); wx.createselectorquery().select('.content').boundingclientrect(function (rect) { // 使页面滚动到底部 wx.pagescrollto({ scrolltop: rect.bottom + 5000 }) }).exec() this.setdata({ chatdata, scrollheight: (windowheight - 0) + 'px', toview: 'msg-' + (chatdata.length - 1), inputbottom:'0px' }) }, getemoji(){//获取表情包 wx.createselectorquery().select('.content').boundingclientrect(function (rect) { // 使页面滚动到底部 wx.pagescrollto({ scrolltop: rect.bottom + 5000 }) }).exec() this.setdata({ showemoji:true, bottomheight:"48vw" }) }, clickemoji: function (e) {//选择表情包 const { detail: { value }, currenttarget: { dataset: { key } } } = e; this.setdata({ [key]: value }) }, onpreview(e){ const { currenttarget: { dataset: { src } } } = e; const urls = [src] wx.previewimage({ current: src, urls }) }, /** * 生命周期函数--监听页面显示 */ onshow: function () { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onpulldownrefresh: function () { let { pagination: { currentpage } } = this.data this.getdata({ param: chat_data, pagination: { pagesize: 5, currentpage: currentpage + 1, } }); }, /** * 页面上拉触底事件的处理函数 */ onreachbottom: function () { }, /** * 获取聚焦 */ inputvalue(e){ const {detail:{value}}=e this.setdata({ inputval:value }) }, focus: function (e) { const { chatdata}=this.data keyheight = e.detail.height; wx.pagescrollto({ scrolltop: windowheight - keyheight }) this.setdata({ toview: 'msg-' + (chatdata.length - 1), inputbottom: keyheight + 'px', scrollheight: (windowheight - keyheight) + 'px', showemoji:false, bottomheight: "18vw" }) //计算msg高度 // calscrollheight(this, keyheight); }, //失去聚焦(软键盘消失) blur: function (e) { const { chatdata } = this.data this.setdata({ scrollheight: '100vh', inputbottom: 0 }) this.setdata({ toview: 'msg-' + (chatdata.length - 1) }) }, /** * 发送点击监听 */ sendclick: function (e) { const { chatdata, scrollheight}=this.data; wx.createselectorquery().select('.content').boundingclientrect(function (rect) { // 使页面滚动到底部 wx.pagescrollto({ scrolltop: rect.bottom + 5000 }) }).exec() chatdata.push({ type: 1, contenttype: 'text', content: e.detail.value, headimg: '../../assets/common/images/headhortrait.jpeg', }) this.setdata({ chatdata, inputval:'' }); }, /** * 退回上一页 */ tobackclick: function () { wx.navigateback({}) } })
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: jQuery实现鼠标移入显示蒙版效果
下一篇: js实现带搜索功能的下拉框
推荐阅读
-
微信小程序全局变量功能与用法详解
-
微信小程序使用map组件实现解析经纬度功能示例
-
微信小程序使用map组件实现获取定位城市天气或者指定城市天气数据功能
-
微信小程序map组件结合高德地图API实现wx.chooseLocation功能示例
-
微信小程序使用map组件实现路线规划功能示例
-
微信小程序登录session的使用
-
微信小程序导航栏滑动定位功能示例(实现CSS3的positionsticky效果)
-
微信小程序MUI侧滑导航菜单示例(Popup弹出式,左侧滑动,右侧不动)
-
微信小程序使用map组件实现检索(定位位置)周边的POI功能示例
-
微信小程序MUI导航栏透明渐变功能示例(通过改变opacity实现)