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) } }) } }) }) }