html书写如下:
<!--pages/chatroomDetail/chatroomDetail.wxml-->
<view class="bgc"></view>
<view wx:if="{{loadingSocket}}">
聊天室连接中...
</view>
<!-- 可以滚动的视图 -->
<scroll-view class="chat-container" scroll-y scroll-into-view="{{'k'+list[list.length-1].id}}">
<!-- 每一条消息 -->
<view class="chat-item {{userName == item.from ? 'my-msg' : ''}}" wx:for="{{list}}" id="{{'k'+item.id}}">
<view class="avatar">
<image src="{{item.avatar}}">
</image>
</view>
<view class="msg-box">
<view class="nickname" wx:if="{{userName !== item.from}}">
{{item.nickName}}
</view>
<view wx:if="{{item.type == 1}}" class="content-box">
{{item.content}}
</view>
<view wx:elif="{{item.type==2}}" class="img-content" >
<image src="{{item.content}}" mode="widthFix">
</image>
</view>
</view>
</view>
</scroll-view>
<view class="send-msg-container">
<!-- 做简易双向绑定 -->
<input class="input" type="text" model:value="{{value}}" />
<view class="action-box">
<van-icon name="smile-o" class="icon" bind:tap="sendImg" />
<van-button class="send-btn" square type="primary" bind:tap="sendMsg">发送</van-button>
</view>
</view>
css书写如下:
/* pages/chatroomDetail/chatroomDetail.wxss */
/* pages/chat/index.wxss */
.bgc {
background-color: #E1E0E5;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
}
/* *****************消息区域***************** */
.chat-container {
padding-bottom: 110rpx;
height: 100vh;
box-sizing: border-box;
}
.chat-item{
display: flex;
padding: 20rpx 0;
}
.avatar{
width: 120rpx;
flex-shrink: 0;
display: flex;
justify-content: center;
}
.avatar image {
width: 90rpx;
height: 90rpx;
border-radius: 10rpx;
}
.msg-box{
padding: 0 10rpx;
}
.content-box{
background-color: #fff;
padding: 15rpx 20rpx;
border-radius: 5rpx;
margin-top: 10rpx;
position: relative;
max-width: 400rpx;
}
.content-box::before {
content: " ";
background-color: white;
height: 25rpx;
width: 25rpx;
position: absolute;
left: -8rpx;
top: 22rpx;
transform: rotate(45deg);
}
.img-content{
}
.img-content image {
max-width: 400rpx;
border-radius: 10rpx;
}
/* *****************消息区域***************** */
/* *****************发送消息区域***************** */
.send-msg-container {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
display: flex;
background-color: #F7F7F7;
/* background-color: hotpink; */
height: 110rpx;
align-items: center;
box-sizing: border-box;
padding: 0 10rpx;
}
.input {
background-color: #FFFFFF;
flex-grow: 1;
margin: 0 10rpx;
height: 68rpx;
border-radius: 10rpx;
padding: 0 15rpx;
box-sizing: border-box;
}
.action-box{
display: flex;
width: 200rpx;
flex-shrink: 0;
}
.action-box .icon {
font-size: 55rpx;
color: #282828;
}
.action-box .send-btn .van-button {
height: 60rpx;
margin-left: 14rpx;
}
/* *****************发送消息区域***************** */
/* *****************我发送的消息***************** */
.my-msg {
flex-direction: row-reverse;
}
view{
word-break: break-all;
}
.my-msg .content-box::before {
left: auto;
right: -8rpx;
}
/* *****************我发送的消息***************** */
js书写如下:
// pages/chatroomDetail/chatroomDetail.js
import {
uploadFile
} from "../../utils/request"
const app = getApp()
// pages/chat/index.js
Page({
/**
* 页面的初始数据
*/
data: {
loadingSocket: true, //websocket是否连接成功
value: "", //输入框的内容
list: [],//聊天室的消息
userName: ""// 当前用户的名字
},
/**
* 生命周期函数--监听页面加载
*/
// 场景1:进入聊天室 需要先向服务器查询历史消息
onl oad: function (options) {
// 首先做了是否登录判断
// 执行完第一步.then时在执行下面的程序
app.globalData.loginPromise.then(()=>{
console.log(app.globalData);
if (app.globalData.isLogin) {
// 这时已经确定登录成功了
const userInfo = app.globalData.user
// 准备数据
console.log(this)
// 在构造器中创建userName , 下次使用就直接使用this.userName
this.userName = userInfo.loginName;
// 修改data里面的值,并且将渲染层也修改点
this.setData({
userName: this.userName
})
// 随便输入
// courseId 分组id 通过这个courseId来标识不同的聊天室
this.groupId = "web16" + options.id
// nickName 昵称
this.nickName = userInfo.userName
// 头像
this.avatar = userInfo.avatar
// 建立联系完毕
// 去建立联系;
this.connectSocket();
// 监听链接打开
this.onSocketOpen()
// 接受服务器消息
this.onSocketMessage()
} else {
// 未登录
wx.reLaunch({
url: '../mine/mine',
})
}
})
},
connectSocket() {
// const url = `ws://10.9.0.51:13000?username=${this.userName}&password=123&courseId=${this.groupId}&nickName=${this.nickName}&avatar=${this.avatar}`
const url = `wss://showme2.myhope365.com/websocketChat?username=${this.userName}&password=123&courseId=${this.groupId}&nickName=${this.nickName}&avatar=${this.avatar}`
// 1.建立链接第一步建立联系
wx.connectSocket({
// 要链接的socket服务器的地址
url,
})
},
onSocketOpen() {
// 监听链接建立成功
wx.onSocketOpen((result) => {
// 当我们socket链接打开之后执行
// 需要保证的时候,我们在发送消息之前一定要先链接成功
console.log("socket链接已经打开了");
// 控制聊天室连接中...显示与否
this.setData({
loadingSocket: false
})
// 链接打开之后加载历史消息
this.getHistory()
// 添加心跳检测 心跳机制是每隔一段时间会向服务器发送一个数据包,
// 告诉服务器自己还活着,同时客户端会确认服务器端是否还活着,如果还活着的话,就会回传一个数据包给客户端来确定服务器端也还活着,
// 否则的话,有可能是网络断开连接了。需要重连~
this.intervalId = setInterval(() => {
wx.sendSocketMessage({
data: JSON.stringify({
"cmd":13, // 固定参数 与后端人员规定的
"hbbyte":"-127" // 固定参数
}),
})
}, 5000);
})
},
onSocketMessage() {
// 接受服务端的消息
// 服务端每发送一次消息 都会进入回调函数
wx.onSocketMessage((result) => {
// 服务器会返回一个字符串
const data = JSON.parse(result.data)
console.log(data)
// 针对不同类型的消息进行一些处理
// 如果comand为11的话 就证明是新消息;
if (data.command === 11) {
// 有新消息
this.data.list.push(data.data)
this.setData({
list: this.data.list
})
//
} else if (data.command === 20 && data.code === 10018) {
// 服务端返回了历史消息
this.setData({
list: data.data.groups[this.groupId]
})
}
})
},
getHistory() {
const historyBody = {
cmd: 19, // 命令 和后端规定好的规则 19为查看历史消息
type: 1, // 类型 固定值 1为查看文本消息
groupId: this.groupId, // 分组的id
userId: this.userName // 用户id(这里可以用loginName)
}
// 接受字符串类型
// 发送给服务端
wx.sendSocketMessage({
data: JSON.stringify(historyBody), //将对象转成字符串.
})
},
// 发送消息
sendSocketMsg(content, type) {
console.log(new Date().getTime())
const msgBody = {
from: this.userName, // 发送人,当前用户的用户名
createTime: new Date().getTime(), // 发送时间 getTime 为获取时间戳
cmd: 11, // 命令固定内容 11为与后端规定为发送消息
group_id: this.groupId, // 分组id。 想要发送到哪个组里
chatType: 1, // 聊天类型 固定内容
msgType: 0, // 消息类型 固定内容
content:content, // 消息内容,自己设计结构,比如你想发送图片(图片上传的接口)
nickName: this.nickName, // 用户昵称
avatar: this.avatar, // 用户头像
type// 消息类型。 你可以自己设计,发送过去是什么,返回的就是什么(1: 普通文本 2: 图片 3:点赞 4, 送花)
}
wx.sendSocketMessage({
data: JSON.stringify(msgBody),
})
},
// 点击发送按钮触发
sendMsg() {
if (!this.data.value) {
wx.showToast({
title: '请输入消息内容',
icon: "none"
})
return
}
this.sendSocketMsg(this.data.value, "1")
this.setData({
value: ""
})
},
sendImg() {
// 图片上传发送
uploadFile('https://showme2.myhope365.com/api/nos/upload/image', "file", {
'fileUseForEnum': 'DEFAULT'
}).then(res => {
this.sendSocketMsg(res.url, "2")
})
},
onUnload(){
// 进行卸载操作
wx.closeSocket({
code: 1000,
})
console.log(1)
// 清除计时器
clearInterval(this.intervalId)
}
})
在untils中写request.js,代码如下:
// 作用:封装接口请求
// 输入:
// 地址
// 方式
// 请求头
// 请求体
// ...
// 返回请求到的数据res
// 返回一个Promise,通过Promise对象拿到请求的结果
function request(options) {
// 请求拦截器
// ...
// 1. 加一些统一的参数,或者配置
if (!options.url.startsWith("https://") && !options.url.startsWith("http://")) {
options.url = "https://showme2.myhope365.com" + options.url
}
let header = {
"content-type": "application/x-www-form-urlencoded",
"cookie": wx.getStorageSync("cookie") || ""
};
if (options.header) {
header = {
...header,
...options.header
}
}
return new Promise((reslove, reject) => {
// 调用接口
wx.request({
// 默认的配置
// 加载传入的配置
...options,
header,
success(res) {
// 响应拦截器,所有接口获取数据之前,都会先执行这里
// 1. 统一的错误处理
if (res.statusCode != 200) {
wx.showToast({
title: '服务器异常,请联系管理员',
})
}
reslove(res)
},
fail(err) {
reject(err)
}
})
})
}
export function get(url, options = {}) {
return request({
url,
...options
})
}
export function post(url, data, options = {}) {
return request({
url,
data,
method: "POST",
...options
})
}
export function uploadFile(url, name = "file", formData = {}, options = {}) {
return new Promise((reslove, reject) => {
// 图片上传发送
wx.chooseImage({
success: res => {
const tempFilePaths = res.tempFilePaths
wx.uploadFile({
//仅为示例,非真实的接口地址
url,
filePath: tempFilePaths[0],
// 上传文件对应的key值,这个值在接口文档里找
name,
// 除了文件之外额外的参数
formData,
header: {
"cookie": wx.getStorageSync("cookie") || ""
},
...options,
success: res => {
// 请求成功的回调
const data = JSON.parse(res.data)
console.log(data)
reslove(data)
}
})
}
})
})
}