微信小程序中聊天室的构建

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

 

 

上一篇:利用eclipse开发php<转>


下一篇:pytho你基础编程:在Python的struct模块中进行数据格式转换的方法