主要思想使用websocket,微信小程序有提供websocket的直接方法,所有问题的难点在于后端代码的编写。

效果示意图

演示视频地址 https://www.bilibili.com/video/av57449552/

长话短说直接上代码

微信小程序端代码

<!--pages/chat/chat.wxml-->
<!--显示咨询医生--->
<view class='title'><label style='margin-left:30rpx'>您选择的咨询医生:<label style="color:#099594">{{doctorInfo.name}}</label></label>
</view><scroll-view class='chatPage' scroll-y style='height:{{scrollHeight}}px'
scroll-top="{{scrollTop}}" scroll-into-view="{{lastId}}"><block wx:for-items="{{messages}}" wx:key='id' wx:for-index="index"><view id="msg{{index}}" class='container-left' wx:if="{{item.msgType==0}}"><image class='cover' src='{{doctorInfo.url}}'></image><view class='bubble'><view class='caret-left'></view><view class='bubble1'><label style='margin-left:10px;margin-right:30rpx;'>{{item.msg}}</label></view></view></view><view id="msg{{index}}" class='container-right' wx:else><view class='bubble'><view class='bubble2'><label style='margin-left:10px;margin-right:10px;'>{{item.msg}}</label></view><view class='caret-right'></view></view><view class='cover'></view></view></block>
</scroll-view><!--输入框组件-->
<view class='messageInfo'> <image src='../../images/keyboard.png' class='icon' hidden='{{flag}}' bindtap='changeShowStatus'></image><image src='../../images/voice.png' class='icon' hidden='{{!flag}}' bindtap='changeShowStatus'></image><input class='msg-input' bindfocus='changeScrollvalue' bindinput='onInput'bindconfirm='sendMsg' value='{{_msg}}' hidden='{{flag}}'></input><button hidden='{{!flag}}' class='msg-input' bindtap='recordVoice'>按住录音</button><image src='../../images/plus.png' class='icon'></image>
</view>
/* pages/chat/chat.wxss */
page{background:white;
}.title{background: #eeeeee;width: 100%;height: 100rpx;font-size: 30rpx;display: flex;align-items: center;
}.chatPage{margin-top: 30rpx;font-size: 30rpx;
}.container-left{margin-left: 20rpx;margin-bottom: 30rpx;display: flex;
}.container-right{display: flex;position: relative;justify-content: flex-end;margin-right: 20rpx;margin-bottom: 30rpx;
}.cover{width: 50px;height: 50px;border-radius: 15%;background: #eeeeee;
}.caret-left{width: 0;height: 0;border: 5px solid transparent;border-right: 5px solid #eeeeee;
}.caret-right{width: 0;height: 0;border: 5px solid transparent;border-left: 5px solid #84cac9;
}.bubble1{background: #eeeeee;border-radius: 5px;height: 45px;display: flex;align-items: center;
}.bubble2{background: #84cac9;border-radius: 5px;height: 45px;display: flex;align-items: center;
}.bubble{display: flex;align-items: center;
}.icon{width: 50rpx;height: 50rpx;margin-left: 30rpx;margin-right: 30rpx;
}.messageInfo{width: 100%;height: 100rpx;display: flex;background: #eeeeee;align-items: center;position: absolute;bottom: 0;
}.msg-input{width: 70%;height: 75%;background: white;border-radius: 5px; font-size: 30rpx;
}

// pages/chat/chat.js
Page({/*** 页面的初始数据*/data: {messages:[],scrollTop: 0,lastId:"msg1",_msg:'',flag: false,scrollHeight: 0},/*** 生命周期函数--监听页面加载*/onLoad: function (options) {var that = this;var doctorInfo = JSON.parse(options.doctorInfo);this.setData({doctorInfo:doctorInfo});var openId = wx.getStorageSync('user').openId+"p";var url = getApp().globalData.websocketUrl+openId+"/"+doctorInfo.openId+"d";this.setData({websocketUrl:url});wx.getSystemInfo({success: function(res) {that.setData({scrollHeight: res.windowHeight-85})},})/*** 从数据库中找到前10条*/wx.request({url: getApp().globalData.baseUrl + "getTop10Message?sender="+openId+"&receiver="+doctorInfo.openId+"d",method: "POST",success: function(res){var msg = that.data.messages;for(var i = 0;i<res.data.length;i++){msg.push(res.data[i]);}var scrollTop = (msg.length) * 100;var windowHeight = that.data.scrollHeight;if (scrollTop < windowHeight) {scrollTop = 0;}that.setData({ messages: msg, lastId: "msg" + msg.length - 1, scrollTop: scrollTop })}});wx.connectSocket({url: url,});wx.onSocketOpen(function (res) {});wx.onSocketMessage(function (res) {var obj = {msg: res.data,url:'',msgType:0};if (res != '') {var list = that.data.messages;list.push(obj);var height = list.length*100;var windowHeight = that.data.scrollHeight;if(height<windowHeight){height = 0;}that.setData({messages:list,lastId:"msg"+list.length-1,scrollTop:height})}});},/*** 生命周期函数--监听页面初次渲染完成*/onReady: function () {},/*** 生命周期函数--监听页面显示*/onShow: function () {},/*** 生命周期函数--监听页面隐藏*/onHide: function () {wx.closeSocket({})},/*** 生命周期函数--监听页面卸载*/onUnload: function () {wx.closeSocket({})},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh: function () {},/*** 页面上拉触底事件的处理函数*/onReachBottom: function () {},/*** 用户点击右上角分享*/onShareAppMessage: function () {},/*** 修改scroll值*/changeScrollvalue:function(){var length = 2;// var length = this.messages.length;var scrollTop = length*80;this.setData({scrollTop: scrollTop})},/*** 记录msg*/onInput:function(e){const value = e.detail.value;this.setData({ _msg: value });},/*** 点击键盘上的完成键发送文字*/sendMsg:function(){var that = this;var messages = this.data.messages;var msg = this.data._msg;var item = {msg:this.data._msg,url:'',msgType:1};if (msg === '') {wx.showToast({title: '不可以发送空消息',icon: 'loading',duration: 1000})return false;}messages.push(item);wx.sendSocketMessage({data: msg,});var height = messages.length*100;var windowHeight = that.data.scrollHeight;if(windowHeight>height){height = 0;}this.setData({messages: messages,_msg:'',lastId:"msg"+messages.length-1,scrollTop:height});wx.request({url: getApp().globalData.baseUrl+"saveMessage",method:"POST",data:{"sender": wx.getStorageSync('user').openId+"p","receiver": this.data.doctorInfo.openId+"d","content": msg}})},/*** 切换输入文字与输入语音两种方式*/changeShowStatus:function(){var flag = this.data.flag;this.setData({flag:!flag})},/*** 按住录音功能实现*/recordVoice:function(){}
})

后端代码如下:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;/*** @author Jingjing* @date 2019/6/13*/
@ServerEndpoint(value = "/chat/{userId}/{receiverId}")
@Component
public class ChatAnnotation {private static Logger logger = LoggerFactory.getLogger(ChatAnnotation.class);private static String userId;private static String receiverId;private Session session;private static Map<String,ChatAnnotation> links = new HashMap<>();/*** 连接建立成功调用的方法* */@OnOpenpublic void onOpen(@PathParam("userId")String userId,@PathParam("receiverId")String receiverId,Session session) {this.userId = userId;this.receiverId = receiverId;this.session = session;links.put(userId,this);logger.debug("新连接:{}",userId);}/*** 连接关闭调用的方法*/@OnClosepublic void onClose(Session session) {ChatAnnotation current = null;for (String id: links.keySet()){if(links.get(id).session.getId() == session.getId()){current = links.get(id);break;}}links.remove(current.getUserId());logger.debug("连接:{}关闭",current.getUserId());}/*** 收到客户端消息后调用的方法* @param message 客户端发送过来的消息* */@OnMessagepublic void onMessage(String message, Session session) {ChatAnnotation current = null;for (String id: links.keySet()){if(links.get(id).session.getId() == session.getId()){current = links.get(id);break;}}logger.debug("收到用户{}的消息{}",current.getUserId(),message);try{//消息需要发送给receiverId用户//首先判断此人是否加入了link
//            String id = findLink(current.getReceiverId());String id = current.getReceiverId();if(links.containsKey(id)){links.get(id).session.getBasicRemote().sendText(message);}}catch (IOException e){e.printStackTrace();}}/*** 找到接收者的id* @param receiverId* @return*/private String findLink(String receiverId){String res= null;for (String id: links.keySet()){ChatAnnotation item = links.get(id);if(item.getUserId().equals(receiverId)){res = id;break;}}return res;}/*** 发生错误时调用* */@OnErrorpublic void onError(Throwable error) {logger.debug("用户id为:{}的连接发送错误",this.userId);error.printStackTrace();}public static String getUserId() {return userId;}public static void setUserId(String userId) {ChatAnnotation.userId = userId;}public static String getReceiverId() {return receiverId;}public static void setReceiverId(String receiverId) {ChatAnnotation.receiverId = receiverId;}
}

在实践的过程中发现,想要在发送消息的时候之间往数据库里面插入代码会报错。所以额外再controller层写了一个用于保存消息的代码,比较简单这个就不贴了,由于该小程序的开发会商用所以无法将整个源码开放,代码中出现的问题欢迎大家批评与指正。

微信小程序仿微信聊天相关推荐

  1. 微信小程序仿微信SlideView组件slide-view

    微信小程序仿微信SlideView组件. 使用 1.安装 slide-view 从小程序基础库版本 2.2.1 或以上.及开发者工具 1.02.1808300 或以上开始,小程序支持使用 npm 安装 ...

  2. Java微信运动步数排序设计_微信小程序仿微信运动步数排行(交互)

    本文介绍了微信小程序仿微信运动步数排行(交互),分享给大家,也给自己留个笔记,废话不多说了,具体如下: 效果图如下: wxml: {{item.name}} {{item.steps}} wxss: ...

  3. android微信运动页面开发,微信小程序仿微信运动步数排行(交互)

    微信小程序仿微信运动步数排行(交互) 发布时间:2020-08-20 00:51:02 来源:脚本之家 阅读:101 作者:祈澈姑娘 本文介绍了微信小程序仿微信运动步数排行(交互),分享给大家,也给自 ...

  4. 微信小程序仿微信、支付宝数字密码软键盘wcKeyboard

    微信小程序自定义数字键盘|仿微信支付数字密码键盘|仿支付宝数字键盘 之前有开发过html5+js仿支付宝.微信支付自定义数字键盘,今天就在之前的插件基础上,重新开发出了小程序版本的仿微信.支付宝数字键 ...

  5. 微信小程序仿微信功能Day4

    点击列表进入用户个人信息 如图所示: 当点击了列表信息时候.跳转到用户个人信息的页面,并从js文件中获取值. 1.页面布局PersonalDetails.wxml <!-- pages/Pers ...

  6. 微信小程序仿微信漂流瓶

    看到微信里有个漂流瓶.试了一下. 这里是用leancloud做后台.涉及到语音和文字的储存,查询. 技术点: 1.微信小程序开发之录音机 音频播放 动画 (真机可用) 2.微信小程序开发之用户系统 一 ...

  7. 微信小程序仿微信聊天语音播放自定义控件

    效果如↓↓↓        假装有声音. 很郁闷,没有做到完全解耦,试了试音频播放组件<audio></audio>与API wx.createInnerAudioContex ...

  8. 微信小程序仿微信功能Day3

    仿微信通讯录功能实现 右侧一个定位锚点跳转功能. 整体是列表的数据显示. 需要图片资源的请去网盘下载 提取码:en17 1.phone.wxml <!-- pages/phone/phone.w ...

  9. Java微信运动步数排序设计_微信小程序仿微信运动步数排行-交互

    效果图如下: 图片.png wxml: {{item.name}} {{item.steps}} wxss: /* pages/leftSwiperDel/index.wxss */ view{ bo ...

最新文章

  1. 织梦动态PHP可以删除吗,DeDe织梦cms如何全站动态化,取消静态功能
  2. RedHat7/Centos7 搭建NFS服务器
  3. 程序员如果也能像C罗一样自律和勤奋,必将成为大神!
  4. 安卓 Handler使用方法
  5. Oracle语句连接查询
  6. python tkinter 弹窗_Python:tkinter-Parent获取弹出窗口的返回值
  7. 54.get set
  8. pgadmin连接服务器失败_增值税发票税控软件:连接服务器失败是否使用离线文件进行更新?...
  9. 【英语学习】【WOTD】despot 释义/词源/示例
  10. python多个函数组成_由多个激活函数组成的神经网络
  11. F1-VmwareCentOS7.x
  12. [leetcode-117]填充每个节点的下一个右侧节点指针 II
  13. Qt中实现鼠标作图并且控制每一笔粗细和颜色的方法
  14. 公用Laravel 5框架与公用库架构
  15. Problem 2122 又见LKity
  16. Flink基础系列8-Flink on yarn运行wordcount程序
  17. AUTOEXEC.BAT及CONFIG.SYS文件用法
  18. 前端 - excel导入 / 导出功能
  19. 《管理学》第五章 组织
  20. fastjson byte[]转json字符串

热门文章

  1. C#中使用SendMessage进行进程通信,可以发送字符串(转载)
  2. SQLMap的安装方法
  3. AT32F435 定时器Time1实现 实时更新时间(万年历算法)
  4. 第三方物流学习(一)
  5. 背包问题 python 背包九讲
  6. tomcat7.0安装
  7. YII 开启URL伪静态方法(yii中urlManager匹配和注意点)
  8. [Shell] 中国IP地址列表 (电信/网通/铁通/移动)
  9. Linux下gcc交叉编译工具链制作实例详细总结(附下载地址)
  10. 想十年后混得好,别选这3种贬值型工作,结局越来越差,追悔莫及